From 193cd0fd5a6055b88a3b2860c43a647362f9e7c4 Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Fri, 23 May 2025 17:43:59 +0200 Subject: media: cedrus: Add support for additional output formats If VPU supports untiled output, it actually supports several different YUV 4:2:0 layouts, namely NV12, NV21, YUV420 and YVU420. Add support for all of them. Signed-off-by: Jernej Skrabec Reviewed-by: Paul Kocialkowski Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil [hverkuil: add 'default' case to switch to fix warning with old compiler] --- drivers/staging/media/sunxi/cedrus/cedrus_hw.c | 19 ++++++++++++++++++- drivers/staging/media/sunxi/cedrus/cedrus_video.c | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c index 32af0e96e762..444fb53878d1 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c @@ -86,9 +86,26 @@ void cedrus_dst_format_set(struct cedrus_dev *dev, switch (fmt->pixelformat) { case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: chroma_size = ALIGN(width, 16) * ALIGN(height, 16) / 2; - reg = VE_PRIMARY_OUT_FMT_NV12; + switch (fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + reg = VE_PRIMARY_OUT_FMT_NV12; + break; + case V4L2_PIX_FMT_NV21: + reg = VE_PRIMARY_OUT_FMT_NV21; + break; + case V4L2_PIX_FMT_YUV420: + reg = VE_PRIMARY_OUT_FMT_YU12; + break; + case V4L2_PIX_FMT_YVU420: + default: + reg = VE_PRIMARY_OUT_FMT_YV12; + break; + } cedrus_write(dev, VE_PRIMARY_OUT_FMT, reg); reg = chroma_size / 2; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c index 77f78266f406..9fae2c7493d0 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c @@ -64,6 +64,21 @@ static struct cedrus_format cedrus_formats[] = { .pixelformat = V4L2_PIX_FMT_NV12_32L32, .directions = CEDRUS_DECODE_DST, }, + { + .pixelformat = V4L2_PIX_FMT_NV21, + .directions = CEDRUS_DECODE_DST, + .capabilities = CEDRUS_CAPABILITY_UNTILED, + }, + { + .pixelformat = V4L2_PIX_FMT_YUV420, + .directions = CEDRUS_DECODE_DST, + .capabilities = CEDRUS_CAPABILITY_UNTILED, + }, + { + .pixelformat = V4L2_PIX_FMT_YVU420, + .directions = CEDRUS_DECODE_DST, + .capabilities = CEDRUS_CAPABILITY_UNTILED, + }, }; #define CEDRUS_FORMATS_COUNT ARRAY_SIZE(cedrus_formats) @@ -140,6 +155,9 @@ void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt) break; case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: /* 16-aligned stride. */ bytesperline = ALIGN(width, 16); -- cgit v1.2.3 From ff8c5622f9f7c644e995d013af320b59e4d61b93 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Thu, 8 May 2025 17:00:15 -0400 Subject: media: rkvdec: Restore iommu addresses on errors On errors, the rkvdec chip self resets. This can clear the addresses programmed in the iommu. This case is signaled by the RKVDEC_SOFTRESET_RDY status bit. Since the iommu framework does not have a restore functionality, and as recommended by the iommu subsystem maintainers, this patch restores the iommu programming by attaching and detaching an empty domain, which will clear and restore the default domain. Suggested-by: Detlev Casanova Tested-by: Detlev Casanova Reviewed-by: Detlev Casanova Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/rkvdec/rkvdec.c | 43 +++++++++++++++++++++++++++++------ drivers/staging/media/rkvdec/rkvdec.h | 1 + 2 files changed, 37 insertions(+), 7 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index 3367902f22de..d707088ec0dc 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -1055,24 +1056,42 @@ static void rkvdec_v4l2_cleanup(struct rkvdec_dev *rkvdec) v4l2_device_unregister(&rkvdec->v4l2_dev); } +static void rkvdec_iommu_restore(struct rkvdec_dev *rkvdec) +{ + if (rkvdec->empty_domain) { + /* + * To rewrite mapping into the attached IOMMU core, attach a new empty domain that + * will program an empty table, then detach it to restore the default domain and + * all cached mappings. + * This is safely done in this interrupt handler to make sure no memory get mapped + * through the IOMMU while the empty domain is attached. + */ + iommu_attach_device(rkvdec->empty_domain, rkvdec->dev); + iommu_detach_device(rkvdec->empty_domain, rkvdec->dev); + } +} + static irqreturn_t rkvdec_irq_handler(int irq, void *priv) { struct rkvdec_dev *rkvdec = priv; + struct rkvdec_ctx *ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev); enum vb2_buffer_state state; u32 status; status = readl(rkvdec->regs + RKVDEC_REG_INTERRUPT); - state = (status & RKVDEC_RDY_STA) ? - VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; - writel(0, rkvdec->regs + RKVDEC_REG_INTERRUPT); - if (cancel_delayed_work(&rkvdec->watchdog_work)) { - struct rkvdec_ctx *ctx; - ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev); - rkvdec_job_finish(ctx, state); + if (status & RKVDEC_RDY_STA) { + state = VB2_BUF_STATE_DONE; + } else { + state = VB2_BUF_STATE_ERROR; + if (status & RKVDEC_SOFTRESET_RDY) + rkvdec_iommu_restore(rkvdec); } + if (cancel_delayed_work(&rkvdec->watchdog_work)) + rkvdec_job_finish(ctx, state); + return IRQ_HANDLED; } @@ -1140,6 +1159,13 @@ static int rkvdec_probe(struct platform_device *pdev) return ret; } + if (iommu_get_domain_for_dev(&pdev->dev)) { + rkvdec->empty_domain = iommu_paging_domain_alloc(rkvdec->dev); + + if (!rkvdec->empty_domain) + dev_warn(rkvdec->dev, "cannot alloc new empty domain\n"); + } + vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); irq = platform_get_irq(pdev, 0); @@ -1179,6 +1205,9 @@ static void rkvdec_remove(struct platform_device *pdev) rkvdec_v4l2_cleanup(rkvdec); pm_runtime_disable(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); + + if (rkvdec->empty_domain) + iommu_domain_free(rkvdec->empty_domain); } #ifdef CONFIG_PM diff --git a/drivers/staging/media/rkvdec/rkvdec.h b/drivers/staging/media/rkvdec/rkvdec.h index 9a9f4fced7a1..f6e8bf38add3 100644 --- a/drivers/staging/media/rkvdec/rkvdec.h +++ b/drivers/staging/media/rkvdec/rkvdec.h @@ -110,6 +110,7 @@ struct rkvdec_dev { void __iomem *regs; struct mutex vdev_lock; /* serializes ioctls */ struct delayed_work watchdog_work; + struct iommu_domain *empty_domain; }; struct rkvdec_ctx { -- cgit v1.2.3 From fc5f8aec77704373ee804b5dba0e0e5029c0f180 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Thu, 27 Feb 2025 15:44:51 +0800 Subject: media: imx: fix a potential memory leak in imx_media_csc_scaler_device_init() Add video_device_release() in label 'err_m2m' to release the memory allocated by video_device_alloc() and prevent potential memory leaks. Remove the reduntant code in label 'err_m2m'. Fixes: a8ef0488cc59 ("media: imx: add csc/scaler mem2mem device") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Dan Carpenter Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/imx/imx-media-csc-scaler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/imx/imx-media-csc-scaler.c b/drivers/staging/media/imx/imx-media-csc-scaler.c index e5e08c6f79f2..19fd31cb9bb0 100644 --- a/drivers/staging/media/imx/imx-media-csc-scaler.c +++ b/drivers/staging/media/imx/imx-media-csc-scaler.c @@ -912,7 +912,7 @@ imx_media_csc_scaler_device_init(struct imx_media_dev *md) return &priv->vdev; err_m2m: - video_set_drvdata(vfd, NULL); + video_device_release(vfd); err_vfd: kfree(priv); return ERR_PTR(ret); -- cgit v1.2.3 From b7fe4c0019b12d60ece3e99eeb0ccfd5a1d103e5 Mon Sep 17 00:00:00 2001 From: Bingbu Cao Date: Thu, 29 May 2025 12:13:17 +0800 Subject: media: staging/ipu7: add Intel IPU7 PCI device driver Intel Image Processing Unit 7th Gen includes input and processing systems and the hardware presents itself as a single PCI device in system same as IPU6. The IPU7 PCI device driver basically does PCI configurations, basic hardware configuration by its buttress interfaces, loads the firmware binary, register the auxiliary device which serve for the ISYS device driver. Signed-off-by: Bingbu Cao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/ipu7/ipu7-bus.c | 158 ++ drivers/staging/media/ipu7/ipu7-bus.h | 69 + drivers/staging/media/ipu7/ipu7-buttress-regs.h | 461 ++++ drivers/staging/media/ipu7/ipu7-buttress.c | 1192 ++++++++++ drivers/staging/media/ipu7/ipu7-buttress.h | 77 + drivers/staging/media/ipu7/ipu7-platform-regs.h | 82 + drivers/staging/media/ipu7/ipu7.c | 2786 +++++++++++++++++++++++ drivers/staging/media/ipu7/ipu7.h | 242 ++ 8 files changed, 5067 insertions(+) create mode 100644 drivers/staging/media/ipu7/ipu7-bus.c create mode 100644 drivers/staging/media/ipu7/ipu7-bus.h create mode 100644 drivers/staging/media/ipu7/ipu7-buttress-regs.h create mode 100644 drivers/staging/media/ipu7/ipu7-buttress.c create mode 100644 drivers/staging/media/ipu7/ipu7-buttress.h create mode 100644 drivers/staging/media/ipu7/ipu7-platform-regs.h create mode 100644 drivers/staging/media/ipu7/ipu7.c create mode 100644 drivers/staging/media/ipu7/ipu7.h (limited to 'drivers/staging') diff --git a/drivers/staging/media/ipu7/ipu7-bus.c b/drivers/staging/media/ipu7/ipu7-bus.c new file mode 100644 index 000000000000..7da44fde002a --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-bus.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipu7.h" +#include "ipu7-bus.h" +#include "ipu7-boot.h" +#include "ipu7-dma.h" + +static int bus_pm_runtime_suspend(struct device *dev) +{ + struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); + int ret; + + ret = pm_generic_runtime_suspend(dev); + if (ret) + return ret; + + ret = ipu_buttress_powerdown(dev, adev->ctrl); + if (!ret) + return 0; + + dev_err(dev, "power down failed!\n"); + + /* Powering down failed, attempt to resume device now */ + ret = pm_generic_runtime_resume(dev); + if (!ret) + return -EBUSY; + + return -EIO; +} + +static int bus_pm_runtime_resume(struct device *dev) +{ + struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); + int ret; + + ret = ipu_buttress_powerup(dev, adev->ctrl); + if (ret) + return ret; + + ret = pm_generic_runtime_resume(dev); + if (ret) + goto out_err; + + return 0; + +out_err: + ipu_buttress_powerdown(dev, adev->ctrl); + + return -EBUSY; +} + +static struct dev_pm_domain ipu7_bus_pm_domain = { + .ops = { + .runtime_suspend = bus_pm_runtime_suspend, + .runtime_resume = bus_pm_runtime_resume, + }, +}; + +static DEFINE_MUTEX(ipu7_bus_mutex); +static void ipu7_bus_release(struct device *dev) +{ + struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); + + kfree(adev->pdata); + kfree(adev); +} + +struct ipu7_bus_device * +ipu7_bus_initialize_device(struct pci_dev *pdev, struct device *parent, + void *pdata, const struct ipu_buttress_ctrl *ctrl, + const char *name) +{ + struct auxiliary_device *auxdev; + struct ipu7_bus_device *adev; + struct ipu7_device *isp = pci_get_drvdata(pdev); + int ret; + + adev = kzalloc(sizeof(*adev), GFP_KERNEL); + if (!adev) + return ERR_PTR(-ENOMEM); + + adev->isp = isp; + adev->ctrl = ctrl; + adev->pdata = pdata; + auxdev = &adev->auxdev; + auxdev->name = name; + auxdev->id = (pci_domain_nr(pdev->bus) << 16) | + PCI_DEVID(pdev->bus->number, pdev->devfn); + + auxdev->dev.parent = parent; + auxdev->dev.release = ipu7_bus_release; + + ret = auxiliary_device_init(auxdev); + if (ret < 0) { + dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n", + ret); + kfree(adev); + return ERR_PTR(ret); + } + + dev_pm_domain_set(&auxdev->dev, &ipu7_bus_pm_domain); + + pm_runtime_forbid(&adev->auxdev.dev); + pm_runtime_enable(&adev->auxdev.dev); + + return adev; +} + +int ipu7_bus_add_device(struct ipu7_bus_device *adev) +{ + struct auxiliary_device *auxdev = &adev->auxdev; + int ret; + + ret = auxiliary_device_add(auxdev); + if (ret) { + auxiliary_device_uninit(auxdev); + return ret; + } + + mutex_lock(&ipu7_bus_mutex); + list_add(&adev->list, &adev->isp->devices); + mutex_unlock(&ipu7_bus_mutex); + + pm_runtime_allow(&auxdev->dev); + + return 0; +} + +void ipu7_bus_del_devices(struct pci_dev *pdev) +{ + struct ipu7_device *isp = pci_get_drvdata(pdev); + struct ipu7_bus_device *adev, *save; + + mutex_lock(&ipu7_bus_mutex); + + list_for_each_entry_safe(adev, save, &isp->devices, list) { + pm_runtime_disable(&adev->auxdev.dev); + list_del(&adev->list); + auxiliary_device_delete(&adev->auxdev); + auxiliary_device_uninit(&adev->auxdev); + } + + mutex_unlock(&ipu7_bus_mutex); +} diff --git a/drivers/staging/media/ipu7/ipu7-bus.h b/drivers/staging/media/ipu7/ipu7-bus.h new file mode 100644 index 000000000000..45157df16e90 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-bus.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#ifndef IPU7_BUS_H +#define IPU7_BUS_H + +#include +#include +#include +#include +#include +#include +#include + +#include "abi/ipu7_fw_boot_abi.h" + +#include "ipu7-syscom.h" + +struct pci_dev; +struct ipu_buttress_ctrl; +struct ipu7_mmu; +struct ipu7_device; + +enum ipu7_subsys { + IPU_IS = 0, + IPU_PS = 1, + IPU_SUBSYS_NUM = 2, +}; + +struct ipu7_bus_device { + struct auxiliary_device auxdev; + const struct auxiliary_driver *auxdrv; + const struct ipu7_auxdrv_data *auxdrv_data; + struct list_head list; + enum ipu7_subsys subsys; + void *pdata; + struct ipu7_mmu *mmu; + struct ipu7_device *isp; + const struct ipu_buttress_ctrl *ctrl; + u64 dma_mask; + struct sg_table fw_sgt; + u32 fw_entry; + struct ipu7_syscom_context *syscom; + struct ia_gofo_boot_config *boot_config; + dma_addr_t boot_config_dma_addr; + u32 boot_config_size; +}; + +struct ipu7_auxdrv_data { + irqreturn_t (*isr)(struct ipu7_bus_device *adev); + irqreturn_t (*isr_threaded)(struct ipu7_bus_device *adev); + bool wake_isr_thread; +}; + +#define to_ipu7_bus_device(_dev) \ + container_of(to_auxiliary_dev(_dev), struct ipu7_bus_device, auxdev) +#define auxdev_to_adev(_auxdev) \ + container_of(_auxdev, struct ipu7_bus_device, auxdev) +#define ipu7_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->auxdev.dev) + +struct ipu7_bus_device * +ipu7_bus_initialize_device(struct pci_dev *pdev, struct device *parent, + void *pdata, const struct ipu_buttress_ctrl *ctrl, + const char *name); +int ipu7_bus_add_device(struct ipu7_bus_device *adev); +void ipu7_bus_del_devices(struct pci_dev *pdev); +#endif diff --git a/drivers/staging/media/ipu7/ipu7-buttress-regs.h b/drivers/staging/media/ipu7/ipu7-buttress-regs.h new file mode 100644 index 000000000000..3eafd6a3813d --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-buttress-regs.h @@ -0,0 +1,461 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 - 2025 Intel Corporation + */ + +#ifndef IPU7_BUTTRESS_REGS_H +#define IPU7_BUTTRESS_REGS_H + +#define BUTTRESS_REG_IRQ_STATUS 0x2000 +#define BUTTRESS_REG_IRQ_STATUS_UNMASKED 0x2004 +#define BUTTRESS_REG_IRQ_ENABLE 0x2008 +#define BUTTRESS_REG_IRQ_CLEAR 0x200c +#define BUTTRESS_REG_IRQ_MASK 0x2010 +#define BUTTRESS_REG_TSC_CMD 0x2014 +#define BUTTRESS_REG_TSC_CTL 0x2018 +#define BUTTRESS_REG_TSC_LO 0x201c +#define BUTTRESS_REG_TSC_HI 0x2020 + +/* valid for PTL */ +#define BUTTRESS_REG_PB_TIMESTAMP_LO 0x2030 +#define BUTTRESS_REG_PB_TIMESTAMP_HI 0x2034 +#define BUTTRESS_REG_PB_TIMESTAMP_VALID 0x2038 + +#define BUTTRESS_REG_PS_WORKPOINT_REQ 0x2100 +#define BUTTRESS_REG_IS_WORKPOINT_REQ 0x2104 +#define BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ 0x2108 +#define BUTTRESS_REG_PS_DOMAINS_STATUS 0x2110 +#define BUTTRESS_REG_PWR_STATUS 0x2114 +#define BUTTRESS_REG_PS_WORKPOINT_REQ_SHADOW 0x2120 +#define BUTTRESS_REG_IS_WORKPOINT_REQ_SHADOW 0x2124 +#define BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ_SHADOW 0x2128 +#define BUTTRESS_REG_ISPS_WORKPOINT_DOWNLOAD 0x212c +#define BUTTRESS_REG_PG_FLOW_OVERRIDE 0x2180 +#define BUTTRESS_REG_GLOBAL_OVERRIDE_UNGATE_CTL 0x2184 +#define BUTTRESS_REG_PWR_FSM_CTL 0x2188 +#define BUTTRESS_REG_IDLE_WDT 0x218c +#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_EN 0x2190 +#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_ADDR 0x2194 +#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_DATA 0x2198 +#define BUTTRESS_REG_POWER_EN_DELAY 0x219c +#define IPU7_BUTTRESS_REG_LTR_CONTROL 0x21a0 +#define IPU7_BUTTRESS_REG_NDE_CONTROL 0x21a4 +#define IPU7_BUTTRESS_REG_INT_FRM_PUNIT 0x21a8 +#define IPU8_BUTTRESS_REG_LTR_CONTROL 0x21a4 +#define IPU8_BUTTRESS_REG_NDE_CONTROL 0x21a8 +#define IPU8_BUTTRESS_REG_INT_FRM_PUNIT 0x21ac +#define BUTTRESS_REG_SLEEP_LEVEL_CFG 0x21b0 +#define BUTTRESS_REG_SLEEP_LEVEL_STS 0x21b4 +#define BUTTRESS_REG_DVFS_FSM_STATUS 0x21b8 +#define BUTTRESS_REG_PS_PLL_ENABLE 0x21bc +#define BUTTRESS_REG_D2D_CTL 0x21d4 +#define BUTTRESS_REG_IB_CLK_CTL 0x21d8 +#define BUTTRESS_REG_IB_CRO_CLK_CTL 0x21dc +#define BUTTRESS_REG_FUNC_FUSES 0x21e0 +#define BUTTRESS_REG_ISOCH_CTL 0x21e4 +#define BUTTRESS_REG_WORKPOINT_CTL 0x21f0 +#define BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS 0x2200 +#define BUTTRESS_REG_DRV_IS_UCX_START_ADDR 0x2204 +#define BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS 0x2208 +#define BUTTRESS_REG_DRV_PS_UCX_START_ADDR 0x220c +#define BUTTRESS_REG_DRV_UCX_RESET_CFG 0x2210 + +/* configured by CSE */ +#define BUTTRESS_REG_CSE_IS_UCX_CONTROL_STATUS 0x2300 +#define BUTTRESS_REG_CSE_IS_UCX_START_ADDR 0x2304 +#define BUTTRESS_REG_CSE_PS_UCX_CONTROL_STATUS 0x2308 +#define BUTTRESS_REG_CSE_PS_UCX_START_ADDR 0x230c + +#define BUTTRESS_REG_CAMERA_MASK 0x2310 +#define BUTTRESS_REG_FW_CTL 0x2314 +#define BUTTRESS_REG_SECURITY_CTL 0x2318 +#define BUTTRESS_REG_FUNCTIONAL_FW_SETUP 0x231c +#define BUTTRESS_REG_FW_BASE 0x2320 +#define BUTTRESS_REG_FW_BASE_LIMIT 0x2324 +#define BUTTRESS_REG_FW_SCRATCH_BASE 0x2328 +#define BUTTRESS_REG_FW_SCRATCH_LIMIT 0x232c +#define BUTTRESS_REG_CSE_ACTION 0x2330 + +/* configured by SW */ +#define BUTTRESS_REG_FW_RESET_CTL 0x2334 +#define BUTTRESS_REG_FW_SOURCE_SIZE 0x2338 +#define BUTTRESS_REG_FW_SOURCE_BASE 0x233c + +#define BUTTRESS_REG_IPU_SEC_CP_LSB 0x2400 +#define BUTTRESS_REG_IPU_SEC_CP_MSB 0x2404 +#define BUTTRESS_REG_IPU_SEC_WAC_LSB 0x2408 +#define BUTTRESS_REG_IPU_SEC_WAC_MSB 0x240c +#define BUTTRESS_REG_IPU_SEC_RAC_LSB 0x2410 +#define BUTTRESS_REG_IPU_SEC_RAC_MSB 0x2414 +#define BUTTRESS_REG_IPU_DRV_CP_LSB 0x2418 +#define BUTTRESS_REG_IPU_DRV_CP_MSB 0x241c +#define BUTTRESS_REG_IPU_DRV_WAC_LSB 0x2420 +#define BUTTRESS_REG_IPU_DRV_WAC_MSB 0x2424 +#define BUTTRESS_REG_IPU_DRV_RAC_LSB 0x2428 +#define BUTTRESS_REG_IPU_DRV_RAC_MSB 0x242c +#define BUTTRESS_REG_IPU_FW_CP_LSB 0x2430 +#define BUTTRESS_REG_IPU_FW_CP_MSB 0x2434 +#define BUTTRESS_REG_IPU_FW_WAC_LSB 0x2438 +#define BUTTRESS_REG_IPU_FW_WAC_MSB 0x243c +#define BUTTRESS_REG_IPU_FW_RAC_LSB 0x2440 +#define BUTTRESS_REG_IPU_FW_RAC_MSB 0x2444 +#define BUTTRESS_REG_IPU_BIOS_SEC_CP_LSB 0x2448 +#define BUTTRESS_REG_IPU_BIOS_SEC_CP_MSB 0x244c +#define BUTTRESS_REG_IPU_BIOS_SEC_WAC_LSB 0x2450 +#define BUTTRESS_REG_IPU_BIOS_SEC_WAC_MSB 0x2454 +#define BUTTRESS_REG_IPU_BIOS_SEC_RAC_LSB 0x2458 +#define BUTTRESS_REG_IPU_BIOS_SEC_RAC_MSB 0x245c +#define BUTTRESS_REG_IPU_DFD_CP_LSB 0x2460 +#define BUTTRESS_REG_IPU_DFD_CP_MSB 0x2464 +#define BUTTRESS_REG_IPU_DFD_WAC_LSB 0x2468 +#define BUTTRESS_REG_IPU_DFD_WAC_MSB 0x246c +#define BUTTRESS_REG_IPU_DFD_RAC_LSB 0x2470 +#define BUTTRESS_REG_IPU_DFD_RAC_MSB 0x2474 +#define BUTTRESS_REG_CSE2IUDB0 0x2500 +#define BUTTRESS_REG_CSE2IUDATA0 0x2504 +#define BUTTRESS_REG_CSE2IUCSR 0x2508 +#define BUTTRESS_REG_IU2CSEDB0 0x250c +#define BUTTRESS_REG_IU2CSEDATA0 0x2510 +#define BUTTRESS_REG_IU2CSECSR 0x2514 +#define BUTTRESS_REG_CSE2IUDB0_CR_SHADOW 0x2520 +#define BUTTRESS_REG_CSE2IUDATA0_CR_SHADOW 0x2524 +#define BUTTRESS_REG_CSE2IUCSR_CR_SHADOW 0x2528 +#define BUTTRESS_REG_IU2CSEDB0_CR_SHADOW 0x252c +#define BUTTRESS_REG_DVFS_FSM_SURVIVABILITY 0x2900 +#define BUTTRESS_REG_FLOWS_FSM_SURVIVABILITY 0x2904 +#define BUTTRESS_REG_FABRICS_FSM_SURVIVABILITY 0x2908 +#define BUTTRESS_REG_PS_SUB1_PM_FSM_SURVIVABILITY 0x290c +#define BUTTRESS_REG_PS_SUB0_PM_FSM_SURVIVABILITY 0x2910 +#define BUTTRESS_REG_PS_PM_FSM_SURVIVABILITY 0x2914 +#define BUTTRESS_REG_IS_PM_FSM_SURVIVABILITY 0x2918 +#define BUTTRESS_REG_FLR_RST_FSM_SURVIVABILITY 0x291c +#define BUTTRESS_REG_FW_RST_FSM_SURVIVABILITY 0x2920 +#define BUTTRESS_REG_RESETPREP_FSM_SURVIVABILITY 0x2924 +#define BUTTRESS_REG_POWER_FSM_DOMAIN_STATUS 0x3000 +#define BUTTRESS_REG_IDLEREQ_STATUS1 0x3004 +#define BUTTRESS_REG_POWER_FSM_STATUS_IS_PS 0x3008 +#define BUTTRESS_REG_POWER_ACK_B_STATUS 0x300c +#define BUTTRESS_REG_DOMAIN_RETENTION_CTL 0x3010 +#define BUTTRESS_REG_CG_CTRL_BITS 0x3014 +#define BUTTRESS_REG_IS_IFC_STATUS0 0x3018 +#define BUTTRESS_REG_IS_IFC_STATUS1 0x301c +#define BUTTRESS_REG_PS_IFC_STATUS0 0x3020 +#define BUTTRESS_REG_PS_IFC_STATUS1 0x3024 +#define BUTTRESS_REG_BTRS_IFC_STATUS0 0x3028 +#define BUTTRESS_REG_BTRS_IFC_STATUS1 0x302c +#define BUTTRESS_REG_IPU_SKU 0x3030 +#define BUTTRESS_REG_PS_IDLEACK 0x3034 +#define BUTTRESS_REG_IS_IDLEACK 0x3038 +#define BUTTRESS_REG_SPARE_REGS_0 0x303c +#define BUTTRESS_REG_SPARE_REGS_1 0x3040 +#define BUTTRESS_REG_SPARE_REGS_2 0x3044 +#define BUTTRESS_REG_SPARE_REGS_3 0x3048 +#define BUTTRESS_REG_IUNIT_ACV 0x304c +#define BUTTRESS_REG_CHICKEN_BITS 0x3050 +#define BUTTRESS_REG_SBENDPOINT_CFG 0x3054 +#define BUTTRESS_REG_ECC_ERR_LOG 0x3058 +#define BUTTRESS_REG_POWER_FSM_STATUS 0x3070 +#define BUTTRESS_REG_RESET_FSM_STATUS 0x3074 +#define BUTTRESS_REG_IDLE_STATUS 0x3078 +#define BUTTRESS_REG_IDLEACK_STATUS 0x307c +#define BUTTRESS_REG_IPU_DEBUG 0x3080 + +#define BUTTRESS_REG_FW_BOOT_PARAMS0 0x4000 +#define BUTTRESS_REG_FW_BOOT_PARAMS1 0x4004 +#define BUTTRESS_REG_FW_BOOT_PARAMS2 0x4008 +#define BUTTRESS_REG_FW_BOOT_PARAMS3 0x400c +#define BUTTRESS_REG_FW_BOOT_PARAMS4 0x4010 +#define BUTTRESS_REG_FW_BOOT_PARAMS5 0x4014 +#define BUTTRESS_REG_FW_BOOT_PARAMS6 0x4018 +#define BUTTRESS_REG_FW_BOOT_PARAMS7 0x401c +#define BUTTRESS_REG_FW_BOOT_PARAMS8 0x4020 +#define BUTTRESS_REG_FW_BOOT_PARAMS9 0x4024 +#define BUTTRESS_REG_FW_BOOT_PARAMS10 0x4028 +#define BUTTRESS_REG_FW_BOOT_PARAMS11 0x402c +#define BUTTRESS_REG_FW_BOOT_PARAMS12 0x4030 +#define BUTTRESS_REG_FW_BOOT_PARAMS13 0x4034 +#define BUTTRESS_REG_FW_BOOT_PARAMS14 0x4038 +#define BUTTRESS_REG_FW_BOOT_PARAMS15 0x403c + +#define BUTTRESS_FW_BOOT_PARAMS_ENTRY(i) \ + (BUTTRESS_REG_FW_BOOT_PARAMS0 + ((i) * 4U)) +#define BUTTRESS_REG_FW_GP(i) (0x4040 + 0x4 * (i)) +#define BUTTRESS_REG_FPGA_SUPPORT(i) (0x40c0 + 0x4 * (i)) + +#define BUTTRESS_REG_FW_GP8 0x4060 +#define BUTTRESS_REG_FW_GP24 0x40a0 + +#define BUTTRESS_REG_GPIO_0_PADCFG_ADDR_CR 0x4100 +#define BUTTRESS_REG_GPIO_1_PADCFG_ADDR_CR 0x4104 +#define BUTTRESS_REG_GPIO_2_PADCFG_ADDR_CR 0x4108 +#define BUTTRESS_REG_GPIO_3_PADCFG_ADDR_CR 0x410c +#define BUTTRESS_REG_GPIO_4_PADCFG_ADDR_CR 0x4110 +#define BUTTRESS_REG_GPIO_5_PADCFG_ADDR_CR 0x4114 +#define BUTTRESS_REG_GPIO_6_PADCFG_ADDR_CR 0x4118 +#define BUTTRESS_REG_GPIO_7_PADCFG_ADDR_CR 0x411c +#define BUTTRESS_REG_GPIO_ENABLE 0x4140 +#define BUTTRESS_REG_GPIO_VALUE_CR 0x4144 + +#define BUTTRESS_REG_IS_MEM_CORRECTABLE_ERROR_STATUS 0x5000 +#define BUTTRESS_REG_IS_MEM_FATAL_ERROR_STATUS 0x5004 +#define BUTTRESS_REG_IS_MEM_NON_FATAL_ERROR_STATUS 0x5008 +#define BUTTRESS_REG_IS_MEM_CHECK_PASSED 0x500c +#define BUTTRESS_REG_IS_MEM_ERROR_INJECT 0x5010 +#define BUTTRESS_REG_IS_MEM_ERROR_CLEAR 0x5014 +#define BUTTRESS_REG_PS_MEM_CORRECTABLE_ERROR_STATUS 0x5040 +#define BUTTRESS_REG_PS_MEM_FATAL_ERROR_STATUS 0x5044 +#define BUTTRESS_REG_PS_MEM_NON_FATAL_ERROR_STATUS 0x5048 +#define BUTTRESS_REG_PS_MEM_CHECK_PASSED 0x504c +#define BUTTRESS_REG_PS_MEM_ERROR_INJECT 0x5050 +#define BUTTRESS_REG_PS_MEM_ERROR_CLEAR 0x5054 + +#define BUTTRESS_REG_IS_AB_REGION_MIN_ADDRESS(i) (0x6000 + 0x8 * (i)) +#define BUTTRESS_REG_IS_AB_REGION_MAX_ADDRESS(i) (0x6004 + 0x8 * (i)) +#define BUTTRESS_REG_IS_AB_VIOLATION_LOG0 0x6080 +#define BUTTRESS_REG_IS_AB_VIOLATION_LOG1 0x6084 +#define BUTTRESS_REG_PS_AB_REGION_MIN_ADDRESS(i) (0x6100 + 0x8 * (i)) +#define BUTTRESS_REG_PS_AB_REGION_MAX_ADDRESS0 (0x6104 + 0x8 * (i)) +#define BUTTRESS_REG_PS_AB_VIOLATION_LOG0 0x6180 +#define BUTTRESS_REG_PS_AB_VIOLATION_LOG1 0x6184 +#define BUTTRESS_REG_PS_DEBUG_AB_VIOLATION_LOG0 0x6200 +#define BUTTRESS_REG_PS_DEBUG_AB_VIOLATION_LOG1 0x6204 +#define BUTTRESS_REG_IS_DEBUG_AB_VIOLATION_LOG0 0x6208 +#define BUTTRESS_REG_IS_DEBUG_AB_VIOLATION_LOG1 0x620c +#define BUTTRESS_REG_IB_DVP_AB_VIOLATION_LOG0 0x6210 +#define BUTTRESS_REG_IB_DVP_AB_VIOLATION_LOG1 0x6214 +#define BUTTRESS_REG_IB_ATB2DTF_AB_VIOLATION_LOG0 0x6218 +#define BUTTRESS_REG_IB_ATB2DTF_AB_VIOLATION_LOG1 0x621c +#define BUTTRESS_REG_AB_ENABLE 0x6220 +#define BUTTRESS_REG_AB_DEFAULT_ACCESS 0x6230 + +/* Indicates CSE has received an IPU driver IPC transaction */ +#define BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE BIT(0) +/* Indicates an IPC transaction from CSE has arrived */ +#define BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING BIT(1) +/* Indicates a CSR update from CSE has arrived */ +#define BUTTRESS_IRQ_CSE_CSR_SET BIT(2) +/* Indicates an interrupt set by Punit (not in use at this time) */ +#define BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ BIT(3) +/* Indicates an SAI violation was detected on access to IB registers */ +#define BUTTRESS_IRQ_SAI_VIOLATION BIT(4) +/* Indicates a transaction to IS was not able to pass the access blocker */ +#define BUTTRESS_IRQ_IS_AB_VIOLATION BIT(5) +/* Indicates a transaction to PS was not able to pass the access blocker */ +#define BUTTRESS_IRQ_PS_AB_VIOLATION BIT(6) +/* Indicates an error response was detected by the IB config NoC */ +#define BUTTRESS_IRQ_IB_CFG_NOC_ERR_IRQ BIT(7) +/* Indicates an error response was detected by the IB data NoC */ +#define BUTTRESS_IRQ_IB_DATA_NOC_ERR_IRQ BIT(8) +/* Transaction to DVP regs was not able to pass the access blocker */ +#define BUTTRESS_IRQ_IB_DVP_AB_VIOLATION BIT(9) +/* Transaction to ATB2DTF regs was not able to pass the access blocker */ +#define BUTTRESS_IRQ_ATB2DTF_AB_VIOLATION BIT(10) +/* Transaction to IS debug regs was not able to pass the access blocker */ +#define BUTTRESS_IRQ_IS_DEBUG_AB_VIOLATION BIT(11) +/* Transaction to PS debug regs was not able to pass the access blocker */ +#define BUTTRESS_IRQ_PS_DEBUG_AB_VIOLATION BIT(12) +/* Indicates timeout occurred waiting for a response from a target */ +#define BUTTRESS_IRQ_IB_CFG_NOC_TIMEOUT_IRQ BIT(13) +/* Set when any correctable ECC error input wire to buttress is set */ +#define BUTTRESS_IRQ_ECC_CORRECTABLE BIT(14) +/* Any noncorrectable-nonfatal ECC error input wire to buttress is set */ +#define BUTTRESS_IRQ_ECC_NONCORRECTABLE_NONFATAL BIT(15) +/* Set when any noncorrectable-fatal ECC error input wire to buttress is set */ +#define BUTTRESS_IRQ_ECC_NONCORRECTABLE_FATAL BIT(16) +/* Set when timeout occurred waiting for a response from a target */ +#define BUTTRESS_IRQ_IS_CFG_NOC_TIMEOUT_IRQ BIT(17) +#define BUTTRESS_IRQ_PS_CFG_NOC_TIMEOUT_IRQ BIT(18) +#define BUTTRESS_IRQ_LB_CFG_NOC_TIMEOUT_IRQ BIT(19) +/* IS FW double exception event */ +#define BUTTRESS_IRQ_IS_UC_PFATAL_ERROR BIT(26) +/* PS FW double exception event */ +#define BUTTRESS_IRQ_PS_UC_PFATAL_ERROR BIT(27) +/* IS FW watchdog event */ +#define BUTTRESS_IRQ_IS_WATCHDOG BIT(28) +/* PS FW watchdog event */ +#define BUTTRESS_IRQ_PS_WATCHDOG BIT(29) +/* IS IRC irq out */ +#define BUTTRESS_IRQ_IS_IRQ BIT(30) +/* PS IRC irq out */ +#define BUTTRESS_IRQ_PS_IRQ BIT(31) + +/* buttress irq */ +#define BUTTRESS_PWR_STATUS_HH_STATE_IDLE 0U +#define BUTTRESS_PWR_STATUS_HH_STATE_IN_PRGS 1U +#define BUTTRESS_PWR_STATUS_HH_STATE_DONE 2U +#define BUTTRESS_PWR_STATUS_HH_STATE_ERR 3U + +#define BUTTRESS_TSC_CMD_START_TSC_SYNC BIT(0) +#define BUTTRESS_PWR_STATUS_HH_STATUS_SHIFT 11 +#define BUTTRESS_PWR_STATUS_HH_STATUS_MASK (0x3U << 11) +#define BUTTRESS_TSW_WA_SOFT_RESET BIT(8) +/* new for PTL */ +#define BUTTRESS_SEL_PB_TIMESTAMP BIT(9) +#define BUTTRESS_IRQS (BUTTRESS_IRQ_IS_IRQ | \ + BUTTRESS_IRQ_PS_IRQ | \ + BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING | \ + BUTTRESS_IRQ_CSE_CSR_SET | \ + BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE | \ + BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ) + +/* Iunit to CSE regs */ +#define BUTTRESS_IU2CSEDB0_BUSY BIT(31) +#define BUTTRESS_IU2CSEDB0_SHORT_FORMAT_SHIFT 27 +#define BUTTRESS_IU2CSEDB0_CLIENT_ID_SHIFT 10 +#define BUTTRESS_IU2CSEDB0_IPC_CLIENT_ID_VAL 2 + +#define BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD 1 +#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN 2 +#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_REPLACE 3 +#define BUTTRESS_IU2CSEDATA0_IPC_UPDATE_SECURE_TOUCH 16 + +#define BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE BIT(0) +#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE BIT(1) +#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_REPLACE_DONE BIT(2) +#define BUTTRESS_CSE2IUDATA0_IPC_UPDATE_SECURE_TOUCH_DONE BIT(4) + +#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 BIT(0) +#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 BIT(1) +#define BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE BIT(2) +#define BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ BIT(3) +#define BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID BIT(4) +#define BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ BIT(5) + +/* 0x20 == NACK, 0xf == unknown command */ +#define BUTTRESS_CSE2IUDATA0_IPC_NACK 0xf20 +#define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK 0xffff + +/* IS/PS freq control */ +#define BUTTRESS_IS_FREQ_CTL_RATIO_MASK 0xffU +#define BUTTRESS_PS_FREQ_CTL_RATIO_MASK 0xffU + +#define IPU7_IS_FREQ_MAX 450 +#define IPU7_IS_FREQ_MIN 50 +#define IPU7_PS_FREQ_MAX 750 +#define BUTTRESS_PS_FREQ_RATIO_STEP 25U +/* valid for IPU8 */ +#define BUTTRESS_IS_FREQ_RATIO_STEP 25U + +/* IS: 400mhz, PS: 500mhz */ +#define IPU7_IS_FREQ_CTL_DEFAULT_RATIO 0x1b +#define IPU7_PS_FREQ_CTL_DEFAULT_RATIO 0x14 +/* IS: 400mhz, PS: 400mhz */ +#define IPU8_IS_FREQ_CTL_DEFAULT_RATIO 0x10 +#define IPU8_PS_FREQ_CTL_DEFAULT_RATIO 0x10 + +#define IPU_FREQ_CTL_CDYN 0x80 +#define IPU_FREQ_CTL_RATIO_SHIFT 0x0 +#define IPU_FREQ_CTL_CDYN_SHIFT 0x8 + +/* buttree power status */ +#define IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT 0 +#define IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK \ + (0x3U << IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT) + +#define IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT 4 +#define IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK \ + (0x3U << IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT) + +#define IPU_BUTTRESS_PWR_STATE_DN_DONE 0x0 +#define IPU_BUTTRESS_PWR_STATE_UP_PROCESS 0x1 +#define IPU_BUTTRESS_PWR_STATE_DN_PROCESS 0x2 +#define IPU_BUTTRESS_PWR_STATE_UP_DONE 0x3 + +#define BUTTRESS_PWR_STATE_IS_PWR_SHIFT 3 +#define BUTTRESS_PWR_STATE_IS_PWR_MASK (0x3 << 3) + +#define BUTTRESS_PWR_STATE_PS_PWR_SHIFT 6 +#define BUTTRESS_PWR_STATE_PS_PWR_MASK (0x3 << 6) + +#define PS_FSM_CG BIT(3) + +#define BUTTRESS_OVERRIDE_IS_CLK BIT(1) +#define BUTTRESS_OVERRIDE_PS_CLK BIT(2) +/* ps_pll only valid for ipu8 */ +#define BUTTRESS_OWN_ACK_PS_PLL BIT(8) +#define BUTTRESS_OWN_ACK_IS_CLK BIT(9) +#define BUTTRESS_OWN_ACK_PS_CLK BIT(10) + +/* FW reset ctrl */ +#define BUTTRESS_FW_RESET_CTL_START BIT(0) +#define BUTTRESS_FW_RESET_CTL_DONE BIT(1) + +/* security */ +#define BUTTRESS_SECURITY_CTL_FW_SECURE_MODE BIT(16) +#define BUTTRESS_SECURITY_CTL_FW_SETUP_MASK GENMASK(4, 0) + +#define BUTTRESS_SECURITY_CTL_FW_SETUP_DONE BIT(0) +#define BUTTRESS_SECURITY_CTL_AUTH_DONE BIT(1) +#define BUTTRESS_SECURITY_CTL_AUTH_FAILED BIT(3) + +/* D2D */ +#define BUTTRESS_D2D_PWR_EN BIT(0) +#define BUTTRESS_D2D_PWR_ACK BIT(4) + +/* NDE */ +#define NDE_VAL_MASK GENMASK(9, 0) +#define NDE_SCALE_MASK GENMASK(12, 10) +#define NDE_VALID_MASK BIT(13) +#define NDE_RESVEC_MASK GENMASK(19, 16) +#define NDE_IN_VBLANK_DIS_MASK BIT(31) + +#define BUTTRESS_NDE_VAL_ACTIVE 48 +#define BUTTRESS_NDE_SCALE_ACTIVE 2 +#define BUTTRESS_NDE_VALID_ACTIVE 1 + +#define BUTTRESS_NDE_VAL_DEFAULT 1023 +#define BUTTRESS_NDE_SCALE_DEFAULT 2 +#define BUTTRESS_NDE_VALID_DEFAULT 0 + +/* IS and PS UCX control */ +#define UCX_CTL_RESET BIT(0) +#define UCX_CTL_RUN BIT(1) +#define UCX_CTL_WAKEUP BIT(2) +#define UCX_CTL_SPARE GENMASK(7, 3) +#define UCX_STS_PWR GENMASK(17, 16) +#define UCX_STS_SLEEPING BIT(18) + +/* offset from PHY base */ +#define PHY_CSI_CFG 0xc0 +#define PHY_CSI_RCOMP_CONTROL 0xc8 +#define PHY_CSI_BSCAN_EXCLUDE 0xd8 + +#define PHY_CPHY_DLL_OVRD(x) (0x100 + 0x100 * (x)) +#define PHY_DPHY_DLL_OVRD(x) (0x14c + 0x100 * (x)) +#define PHY_CPHY_RX_CONTROL1(x) (0x110 + 0x100 * (x)) +#define PHY_CPHY_RX_CONTROL2(x) (0x114 + 0x100 * (x)) +#define PHY_DPHY_CFG(x) (0x148 + 0x100 * (x)) +#define PHY_BB_AFE_CONFIG(x) (0x174 + 0x100 * (x)) + +/* PB registers */ +#define INTERRUPT_STATUS 0x0 +#define BTRS_LOCAL_INTERRUPT_MASK 0x4 +#define GLOBAL_INTERRUPT_MASK 0x8 +#define HM_ATS 0xc +#define ATS_ERROR_LOG1 0x10 +#define ATS_ERROR_LOG2 0x14 +#define ATS_ERROR_CLEAR 0x18 +#define CFI_0_ERROR_LOG 0x1c +#define CFI_0_ERROR_CLEAR 0x20 +#define HASH_CONFIG 0x2c +#define TLBID_HASH_ENABLE_31_0 0x30 +#define TLBID_HASH_ENABLE_63_32 0x34 +#define TLBID_HASH_ENABLE_95_64 0x38 +#define TLBID_HASH_ENABLE_127_96 0x3c +#define CFI_1_ERROR_LOGGING 0x40 +#define CFI_1_ERROR_CLEAR 0x44 +#define IMR_ERROR_LOGGING_LOW 0x48 +#define IMR_ERROR_LOGGING_HIGH 0x4c +#define IMR_ERROR_CLEAR 0x50 +#define PORT_ARBITRATION_WEIGHTS 0x54 +#define IMR_ERROR_LOGGING_CFI_1_LOW 0x58 +#define IMR_ERROR_LOGGING_CFI_1_HIGH 0x5c +#define IMR_ERROR_CLEAR_CFI_1 0x60 +#define BAR2_MISC_CONFIG 0x64 +#define RSP_ID_CONFIG_AXI2CFI_0 0x68 +#define RSP_ID_CONFIG_AXI2CFI_1 0x6c +#define PB_DRIVER_PCODE_MAILBOX_STATUS 0x70 +#define PB_DRIVER_PCODE_MAILBOX_INTERFACE 0x74 +#define PORT_ARBITRATION_WEIGHTS_ATS 0x78 + +#endif /* IPU7_BUTTRESS_REGS_H */ diff --git a/drivers/staging/media/ipu7/ipu7-buttress.c b/drivers/staging/media/ipu7/ipu7-buttress.c new file mode 100644 index 000000000000..e5707f5e300b --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-buttress.c @@ -0,0 +1,1192 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipu7.h" +#include "ipu7-bus.h" +#include "ipu7-buttress.h" +#include "ipu7-buttress-regs.h" + +#define BOOTLOADER_STATUS_OFFSET BUTTRESS_REG_FW_BOOT_PARAMS7 + +#define BOOTLOADER_MAGIC_KEY 0xb00710adU + +#define ENTRY BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 +#define EXIT BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 +#define QUERY BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE + +#define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX 10U + +#define BUTTRESS_POWER_TIMEOUT_US (200 * USEC_PER_MSEC) + +#define BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US (5 * USEC_PER_SEC) +#define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US (10 * USEC_PER_SEC) +#define BUTTRESS_CSE_FWRESET_TIMEOUT_US (100 * USEC_PER_MSEC) + +#define BUTTRESS_IPC_TX_TIMEOUT_MS MSEC_PER_SEC +#define BUTTRESS_IPC_RX_TIMEOUT_MS MSEC_PER_SEC +#define BUTTRESS_IPC_VALIDITY_TIMEOUT_US (1 * USEC_PER_SEC) +#define BUTTRESS_TSC_SYNC_TIMEOUT_US (5 * USEC_PER_MSEC) + +#define BUTTRESS_IPC_RESET_RETRY 2000U +#define BUTTRESS_CSE_IPC_RESET_RETRY 4U +#define BUTTRESS_IPC_CMD_SEND_RETRY 1U + +struct ipu7_ipc_buttress_msg { + u32 cmd; + u32 expected_resp; + bool require_resp; + u8 cmd_size; +}; + +static const u32 ipu7_adev_irq_mask[2] = { + BUTTRESS_IRQ_IS_IRQ, + BUTTRESS_IRQ_PS_IRQ +}; + +int ipu_buttress_ipc_reset(struct ipu7_device *isp, + struct ipu_buttress_ipc *ipc) +{ + unsigned int retries = BUTTRESS_IPC_RESET_RETRY; + struct ipu_buttress *b = &isp->buttress; + struct device *dev = &isp->pdev->dev; + u32 val = 0, csr_in_clr; + + if (!isp->secure_mode) { + dev_dbg(dev, "Skip IPC reset for non-secure mode\n"); + return 0; + } + + mutex_lock(&b->ipc_mutex); + + /* Clear-by-1 CSR (all bits), corresponding internal states. */ + val = readl(isp->base + ipc->csr_in); + writel(val, isp->base + ipc->csr_in); + + /* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */ + writel(ENTRY, isp->base + ipc->csr_out); + /* + * Clear-by-1 all CSR bits EXCEPT following + * bits: + * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. + * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. + * C. Possibly custom bits, depending on + * their role. + */ + csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ | + BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID | + BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY; + + do { + usleep_range(400, 500); + val = readl(isp->base + ipc->csr_in); + switch (val) { + case ENTRY | EXIT: + case ENTRY | EXIT | QUERY: + /* + * 1) Clear-by-1 CSR bits + * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, + * IPC_PEER_COMP_ACTIONS_RST_PHASE2). + * 2) Set peer CSR bit + * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. + */ + writel(ENTRY | EXIT, isp->base + ipc->csr_in); + writel(QUERY, isp->base + ipc->csr_out); + break; + case ENTRY: + case ENTRY | QUERY: + /* + * 1) Clear-by-1 CSR bits + * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, + * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE). + * 2) Set peer CSR bit + * IPC_PEER_COMP_ACTIONS_RST_PHASE1. + */ + writel(ENTRY | QUERY, isp->base + ipc->csr_in); + writel(ENTRY, isp->base + ipc->csr_out); + break; + case EXIT: + case EXIT | QUERY: + /* + * Clear-by-1 CSR bit + * IPC_PEER_COMP_ACTIONS_RST_PHASE2. + * 1) Clear incoming doorbell. + * 2) Clear-by-1 all CSR bits EXCEPT following + * bits: + * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. + * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. + * C. Possibly custom bits, depending on + * their role. + * 3) Set peer CSR bit + * IPC_PEER_COMP_ACTIONS_RST_PHASE2. + */ + writel(EXIT, isp->base + ipc->csr_in); + writel(0, isp->base + ipc->db0_in); + writel(csr_in_clr, isp->base + ipc->csr_in); + writel(EXIT, isp->base + ipc->csr_out); + + /* + * Read csr_in again to make sure if RST_PHASE2 is done. + * If csr_in is QUERY, it should be handled again. + */ + usleep_range(200, 300); + val = readl(isp->base + ipc->csr_in); + if (val & QUERY) { + dev_dbg(dev, + "RST_PHASE2 retry csr_in = %x\n", val); + break; + } + mutex_unlock(&b->ipc_mutex); + return 0; + case QUERY: + /* + * 1) Clear-by-1 CSR bit + * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. + * 2) Set peer CSR bit + * IPC_PEER_COMP_ACTIONS_RST_PHASE1 + */ + writel(QUERY, isp->base + ipc->csr_in); + writel(ENTRY, isp->base + ipc->csr_out); + break; + default: + dev_dbg_ratelimited(dev, "Unexpected CSR 0x%x\n", val); + break; + } + } while (retries--); + + mutex_unlock(&b->ipc_mutex); + dev_err(dev, "Timed out while waiting for CSE\n"); + + return -ETIMEDOUT; +} + +static void ipu_buttress_ipc_validity_close(struct ipu7_device *isp, + struct ipu_buttress_ipc *ipc) +{ + writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ, + isp->base + ipc->csr_out); +} + +static int +ipu_buttress_ipc_validity_open(struct ipu7_device *isp, + struct ipu_buttress_ipc *ipc) +{ + unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID; + void __iomem *addr; + int ret; + u32 val; + + writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ, + isp->base + ipc->csr_out); + + addr = isp->base + ipc->csr_in; + ret = readl_poll_timeout(addr, val, val & mask, 200, + BUTTRESS_IPC_VALIDITY_TIMEOUT_US); + if (ret) { + dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val); + ipu_buttress_ipc_validity_close(isp, ipc); + } + + return ret; +} + +static void ipu_buttress_ipc_recv(struct ipu7_device *isp, + struct ipu_buttress_ipc *ipc, u32 *ipc_msg) +{ + if (ipc_msg) + *ipc_msg = readl(isp->base + ipc->data0_in); + writel(0, isp->base + ipc->db0_in); +} + +static int ipu_buttress_ipc_send_msg(struct ipu7_device *isp, + struct ipu7_ipc_buttress_msg *msg) +{ + unsigned long tx_timeout_jiffies, rx_timeout_jiffies; + unsigned int retry = BUTTRESS_IPC_CMD_SEND_RETRY; + struct ipu_buttress *b = &isp->buttress; + struct ipu_buttress_ipc *ipc = &b->cse; + struct device *dev = &isp->pdev->dev; + int tout; + u32 val; + int ret; + + mutex_lock(&b->ipc_mutex); + + ret = ipu_buttress_ipc_validity_open(isp, ipc); + if (ret) { + dev_err(dev, "IPC validity open failed\n"); + goto out; + } + + tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT_MS); + rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT_MS); + +try: + reinit_completion(&ipc->send_complete); + if (msg->require_resp) + reinit_completion(&ipc->recv_complete); + + dev_dbg(dev, "IPC command: 0x%x\n", msg->cmd); + writel(msg->cmd, isp->base + ipc->data0_out); + val = BUTTRESS_IU2CSEDB0_BUSY | msg->cmd_size; + writel(val, isp->base + ipc->db0_out); + + tout = wait_for_completion_timeout(&ipc->send_complete, + tx_timeout_jiffies); + if (!tout) { + dev_err(dev, "send IPC response timeout\n"); + if (!retry--) { + ret = -ETIMEDOUT; + goto out; + } + + /* Try again if CSE is not responding on first try */ + writel(0, isp->base + ipc->db0_out); + goto try; + } + + if (!msg->require_resp) { + ret = -EIO; + goto out; + } + + tout = wait_for_completion_timeout(&ipc->recv_complete, + rx_timeout_jiffies); + if (!tout) { + dev_err(dev, "recv IPC response timeout\n"); + ret = -ETIMEDOUT; + goto out; + } + + if (ipc->nack_mask && + (ipc->recv_data & ipc->nack_mask) == ipc->nack) { + dev_err(dev, "IPC NACK for cmd 0x%x\n", msg->cmd); + ret = -EIO; + goto out; + } + + if (ipc->recv_data != msg->expected_resp) { + dev_err(dev, + "expected resp: 0x%x, IPC response: 0x%x\n", + msg->expected_resp, ipc->recv_data); + ret = -EIO; + goto out; + } + + dev_dbg(dev, "IPC commands done\n"); + +out: + ipu_buttress_ipc_validity_close(isp, ipc); + mutex_unlock(&b->ipc_mutex); + + return ret; +} + +static int ipu_buttress_ipc_send(struct ipu7_device *isp, + u32 ipc_msg, u32 size, bool require_resp, + u32 expected_resp) +{ + struct ipu7_ipc_buttress_msg msg = { + .cmd = ipc_msg, + .cmd_size = size, + .require_resp = require_resp, + .expected_resp = expected_resp, + }; + + return ipu_buttress_ipc_send_msg(isp, &msg); +} + +static irqreturn_t ipu_buttress_call_isr(struct ipu7_bus_device *adev) +{ + irqreturn_t ret = IRQ_WAKE_THREAD; + + if (!adev || !adev->auxdrv || !adev->auxdrv_data) + return IRQ_NONE; + + if (adev->auxdrv_data->isr) + ret = adev->auxdrv_data->isr(adev); + + if (ret == IRQ_WAKE_THREAD && !adev->auxdrv_data->isr_threaded) + ret = IRQ_NONE; + + return ret; +} + +irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr) +{ + struct ipu7_device *isp = isp_ptr; + struct ipu7_bus_device *adev[] = { isp->isys, isp->psys }; + struct ipu_buttress *b = &isp->buttress; + struct device *dev = &isp->pdev->dev; + irqreturn_t ret = IRQ_NONE; + u32 pb_irq, pb_local_irq; + u32 disable_irqs = 0; + u32 irq_status; + unsigned int i; + + pm_runtime_get_noresume(dev); + + pb_irq = readl(isp->pb_base + INTERRUPT_STATUS); + writel(pb_irq, isp->pb_base + INTERRUPT_STATUS); + + /* check btrs ATS, CFI and IMR errors, BIT(0) is unused for IPU */ + pb_local_irq = readl(isp->pb_base + BTRS_LOCAL_INTERRUPT_MASK); + if (pb_local_irq & ~BIT(0)) { + dev_warn(dev, "PB interrupt status 0x%x local 0x%x\n", pb_irq, + pb_local_irq); + dev_warn(dev, "Details: %x %x %x %x %x %x %x %x\n", + readl(isp->pb_base + ATS_ERROR_LOG1), + readl(isp->pb_base + ATS_ERROR_LOG2), + readl(isp->pb_base + CFI_0_ERROR_LOG), + readl(isp->pb_base + CFI_1_ERROR_LOGGING), + readl(isp->pb_base + IMR_ERROR_LOGGING_LOW), + readl(isp->pb_base + IMR_ERROR_LOGGING_HIGH), + readl(isp->pb_base + IMR_ERROR_LOGGING_CFI_1_LOW), + readl(isp->pb_base + IMR_ERROR_LOGGING_CFI_1_HIGH)); + } + + irq_status = readl(isp->base + BUTTRESS_REG_IRQ_STATUS); + if (!irq_status) { + pm_runtime_put_noidle(dev); + return IRQ_NONE; + } + + do { + writel(irq_status, isp->base + BUTTRESS_REG_IRQ_CLEAR); + + for (i = 0; i < ARRAY_SIZE(ipu7_adev_irq_mask); i++) { + irqreturn_t r = ipu_buttress_call_isr(adev[i]); + + if (!(irq_status & ipu7_adev_irq_mask[i])) + continue; + + if (r == IRQ_WAKE_THREAD) { + ret = IRQ_WAKE_THREAD; + disable_irqs |= ipu7_adev_irq_mask[i]; + } else if (ret == IRQ_NONE && r == IRQ_HANDLED) { + ret = IRQ_HANDLED; + } + } + + if (irq_status & (BUTTRESS_IRQS | BUTTRESS_IRQ_SAI_VIOLATION) && + ret == IRQ_NONE) + ret = IRQ_HANDLED; + + if (irq_status & BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING) { + dev_dbg(dev, "BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING\n"); + ipu_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data); + complete(&b->cse.recv_complete); + } + + if (irq_status & BUTTRESS_IRQ_CSE_CSR_SET) + dev_dbg(dev, "BUTTRESS_IRQ_CSE_CSR_SET\n"); + + if (irq_status & BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE) { + dev_dbg(dev, "BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE\n"); + complete(&b->cse.send_complete); + } + + if (irq_status & BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ) + dev_dbg(dev, "BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ\n"); + + if (irq_status & BUTTRESS_IRQ_SAI_VIOLATION && + ipu_buttress_get_secure_mode(isp)) + dev_err(dev, "BUTTRESS_IRQ_SAI_VIOLATION\n"); + + irq_status = readl(isp->base + BUTTRESS_REG_IRQ_STATUS); + } while (irq_status); + + if (disable_irqs) + writel(BUTTRESS_IRQS & ~disable_irqs, + isp->base + BUTTRESS_REG_IRQ_ENABLE); + + pm_runtime_put(dev); + + return ret; +} + +irqreturn_t ipu_buttress_isr_threaded(int irq, void *isp_ptr) +{ + struct ipu7_device *isp = isp_ptr; + struct ipu7_bus_device *adev[] = { isp->isys, isp->psys }; + const struct ipu7_auxdrv_data *drv_data = NULL; + irqreturn_t ret = IRQ_NONE; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ipu7_adev_irq_mask) && adev[i]; i++) { + drv_data = adev[i]->auxdrv_data; + if (!drv_data) + continue; + + if (drv_data->wake_isr_thread && + drv_data->isr_threaded(adev[i]) == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + + writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_ENABLE); + + return ret; +} + +static int isys_d2d_power(struct device *dev, bool on) +{ + struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; + int ret = 0; + u32 target = on ? BUTTRESS_D2D_PWR_ACK : 0U; + u32 val; + + dev_dbg(dev, "power %s isys d2d.\n", on ? "UP" : "DOWN"); + val = readl(isp->base + BUTTRESS_REG_D2D_CTL); + if ((val & BUTTRESS_D2D_PWR_ACK) == target) { + dev_info(dev, "d2d already in %s state.\n", + on ? "UP" : "DOWN"); + return 0; + } + + val = on ? val | BUTTRESS_D2D_PWR_EN : val & (~BUTTRESS_D2D_PWR_EN); + writel(val, isp->base + BUTTRESS_REG_D2D_CTL); + ret = readl_poll_timeout(isp->base + BUTTRESS_REG_D2D_CTL, + val, (val & BUTTRESS_D2D_PWR_ACK) == target, + 100, BUTTRESS_POWER_TIMEOUT_US); + if (ret) + dev_err(dev, "power %s d2d timeout. status: 0x%x\n", + on ? "UP" : "DOWN", val); + + return ret; +} + +static void isys_nde_control(struct device *dev, bool on) +{ + struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; + u32 val, value, scale, valid, resvec; + u32 nde_reg; + + if (on) { + value = BUTTRESS_NDE_VAL_ACTIVE; + scale = BUTTRESS_NDE_SCALE_ACTIVE; + valid = BUTTRESS_NDE_VALID_ACTIVE; + } else { + value = BUTTRESS_NDE_VAL_DEFAULT; + scale = BUTTRESS_NDE_SCALE_DEFAULT; + valid = BUTTRESS_NDE_VALID_DEFAULT; + } + + /* only set the fabrics resource ownership for ipu8 */ + nde_reg = is_ipu8(isp->hw_ver) ? IPU8_BUTTRESS_REG_NDE_CONTROL : + IPU7_BUTTRESS_REG_NDE_CONTROL; + resvec = is_ipu8(isp->hw_ver) ? 0x2 : 0xe; + val = FIELD_PREP(NDE_VAL_MASK, value) | + FIELD_PREP(NDE_SCALE_MASK, scale) | + FIELD_PREP(NDE_VALID_MASK, valid) | + FIELD_PREP(NDE_RESVEC_MASK, resvec); + + writel(val, isp->base + nde_reg); +} + +static int ipu7_buttress_powerup(struct device *dev, + const struct ipu_buttress_ctrl *ctrl) +{ + struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; + u32 val, exp_sts; + int ret = 0; + + if (!ctrl) + return 0; + + mutex_lock(&isp->buttress.power_mutex); + + exp_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; + if (ctrl->subsys_id == IPU_IS) { + ret = isys_d2d_power(dev, true); + if (ret) + goto out_power; + isys_nde_control(dev, true); + } + + /* request clock resource ownership */ + val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); + val |= ctrl->ovrd_clk; + writel(val, isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); + ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SLEEP_LEVEL_STS, + val, (val & ctrl->own_clk_ack), + 100, BUTTRESS_POWER_TIMEOUT_US); + if (ret) + dev_warn(dev, "request clk ownership timeout. status 0x%x\n", + val); + + val = ctrl->ratio << ctrl->ratio_shift | ctrl->cdyn << ctrl->cdyn_shift; + + dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, + ctrl->subsys_id == IPU_IS ? "IS" : "PS"); + writel(val, isp->base + ctrl->freq_ctl); + + ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, + val, ((val & ctrl->pwr_sts_mask) == exp_sts), + 100, BUTTRESS_POWER_TIMEOUT_US); + if (ret) { + dev_err(dev, "%s power up timeout with status: 0x%x\n", + ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); + goto out_power; + } + + dev_dbg(dev, "%s power up successfully. status: 0x%x\n", + ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); + + /* release clock resource ownership */ + val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); + val &= ~ctrl->ovrd_clk; + writel(val, isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); + +out_power: + mutex_unlock(&isp->buttress.power_mutex); + + return ret; +} + +static int ipu7_buttress_powerdown(struct device *dev, + const struct ipu_buttress_ctrl *ctrl) +{ + struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; + u32 val, exp_sts; + int ret = 0; + + if (!ctrl) + return 0; + + mutex_lock(&isp->buttress.power_mutex); + + exp_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; + val = 0x8U << ctrl->ratio_shift; + + dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, + ctrl->subsys_id == IPU_IS ? "IS" : "PS"); + writel(val, isp->base + ctrl->freq_ctl); + ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, + val, ((val & ctrl->pwr_sts_mask) == exp_sts), + 100, BUTTRESS_POWER_TIMEOUT_US); + if (ret) { + dev_err(dev, "%s power down timeout with status: 0x%x\n", + ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); + goto out_power; + } + + dev_dbg(dev, "%s power down successfully. status: 0x%x\n", + ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); +out_power: + if (ctrl->subsys_id == IPU_IS && !ret) { + isys_d2d_power(dev, false); + isys_nde_control(dev, false); + } + + mutex_unlock(&isp->buttress.power_mutex); + + return ret; +} + +static int ipu8_buttress_powerup(struct device *dev, + const struct ipu_buttress_ctrl *ctrl) +{ + struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; + u32 sleep_level_reg = BUTTRESS_REG_SLEEP_LEVEL_STS; + u32 val, exp_sts; + int ret = 0; + + if (!ctrl) + return 0; + + mutex_lock(&isp->buttress.power_mutex); + exp_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; + if (ctrl->subsys_id == IPU_IS) { + ret = isys_d2d_power(dev, true); + if (ret) + goto out_power; + isys_nde_control(dev, true); + } + + /* request ps_pll when psys freq > 400Mhz */ + if (ctrl->subsys_id == IPU_PS && ctrl->ratio > 0x10) { + writel(1, isp->base + BUTTRESS_REG_PS_PLL_ENABLE); + ret = readl_poll_timeout(isp->base + sleep_level_reg, + val, (val & ctrl->own_clk_ack), + 100, BUTTRESS_POWER_TIMEOUT_US); + if (ret) + dev_warn(dev, "ps_pll req ack timeout. status 0x%x\n", + val); + } + + val = ctrl->ratio << ctrl->ratio_shift | ctrl->cdyn << ctrl->cdyn_shift; + dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, + ctrl->subsys_id == IPU_IS ? "IS" : "PS"); + writel(val, isp->base + ctrl->freq_ctl); + ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, + val, ((val & ctrl->pwr_sts_mask) == exp_sts), + 100, BUTTRESS_POWER_TIMEOUT_US); + if (ret) { + dev_err(dev, "%s power up timeout with status: 0x%x\n", + ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); + goto out_power; + } + + dev_dbg(dev, "%s power up successfully. status: 0x%x\n", + ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); +out_power: + mutex_unlock(&isp->buttress.power_mutex); + + return ret; +} + +static int ipu8_buttress_powerdown(struct device *dev, + const struct ipu_buttress_ctrl *ctrl) +{ + struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; + u32 val, exp_sts; + int ret = 0; + + if (!ctrl) + return 0; + + mutex_lock(&isp->buttress.power_mutex); + exp_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; + + if (ctrl->subsys_id == IPU_PS) + val = 0x10U << ctrl->ratio_shift; + else + val = 0x8U << ctrl->ratio_shift; + + dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, + ctrl->subsys_id == IPU_IS ? "IS" : "PS"); + writel(val, isp->base + ctrl->freq_ctl); + ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, + val, ((val & ctrl->pwr_sts_mask) == exp_sts), + 100, BUTTRESS_POWER_TIMEOUT_US); + if (ret) { + dev_err(dev, "%s power down timeout with status: 0x%x\n", + ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); + goto out_power; + } + + dev_dbg(dev, "%s power down successfully. status: 0x%x\n", + ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); +out_power: + if (ctrl->subsys_id == IPU_IS && !ret) { + isys_d2d_power(dev, false); + isys_nde_control(dev, false); + } + + if (ctrl->subsys_id == IPU_PS) { + val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_STS); + if (val & ctrl->own_clk_ack) + writel(0, isp->base + BUTTRESS_REG_PS_PLL_ENABLE); + } + mutex_unlock(&isp->buttress.power_mutex); + + return ret; +} + +int ipu_buttress_powerup(struct device *dev, + const struct ipu_buttress_ctrl *ctrl) +{ + struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; + + if (is_ipu8(isp->hw_ver)) + return ipu8_buttress_powerup(dev, ctrl); + + return ipu7_buttress_powerup(dev, ctrl); +} + +int ipu_buttress_powerdown(struct device *dev, + const struct ipu_buttress_ctrl *ctrl) +{ + struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; + + if (is_ipu8(isp->hw_ver)) + return ipu8_buttress_powerdown(dev, ctrl); + + return ipu7_buttress_powerdown(dev, ctrl); +} + +bool ipu_buttress_get_secure_mode(struct ipu7_device *isp) +{ + u32 val; + + val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); + + return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE; +} + +bool ipu_buttress_auth_done(struct ipu7_device *isp) +{ + u32 val; + + if (!isp->secure_mode) + return true; + + val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); + val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val); + + return val == BUTTRESS_SECURITY_CTL_AUTH_DONE; +} +EXPORT_SYMBOL_NS_GPL(ipu_buttress_auth_done, "INTEL_IPU7"); + +int ipu_buttress_get_isys_freq(struct ipu7_device *isp, u32 *freq) +{ + u32 reg_val; + int ret; + + ret = pm_runtime_get_sync(&isp->isys->auxdev.dev); + if (ret < 0) { + pm_runtime_put(&isp->isys->auxdev.dev); + dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", ret); + return ret; + } + + reg_val = readl(isp->base + BUTTRESS_REG_IS_WORKPOINT_REQ); + + pm_runtime_put(&isp->isys->auxdev.dev); + + if (is_ipu8(isp->hw_ver)) + *freq = (reg_val & BUTTRESS_IS_FREQ_CTL_RATIO_MASK) * 25; + else + *freq = (reg_val & BUTTRESS_IS_FREQ_CTL_RATIO_MASK) * 50 / 3; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ipu_buttress_get_isys_freq, "INTEL_IPU7"); + +int ipu_buttress_get_psys_freq(struct ipu7_device *isp, u32 *freq) +{ + u32 reg_val; + int ret; + + ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); + if (ret < 0) { + pm_runtime_put(&isp->psys->auxdev.dev); + dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", ret); + return ret; + } + + reg_val = readl(isp->base + BUTTRESS_REG_PS_WORKPOINT_REQ); + + pm_runtime_put(&isp->psys->auxdev.dev); + + reg_val &= BUTTRESS_PS_FREQ_CTL_RATIO_MASK; + *freq = BUTTRESS_PS_FREQ_RATIO_STEP * reg_val; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ipu_buttress_get_psys_freq, "INTEL_IPU7"); + +int ipu_buttress_reset_authentication(struct ipu7_device *isp) +{ + struct device *dev = &isp->pdev->dev; + int ret; + u32 val; + + if (!isp->secure_mode) { + dev_dbg(dev, "Skip auth for non-secure mode\n"); + return 0; + } + + writel(BUTTRESS_FW_RESET_CTL_START, isp->base + + BUTTRESS_REG_FW_RESET_CTL); + + ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val, + val & BUTTRESS_FW_RESET_CTL_DONE, 500, + BUTTRESS_CSE_FWRESET_TIMEOUT_US); + if (ret) { + dev_err(dev, "Time out while resetting authentication state\n"); + return ret; + } + + dev_dbg(dev, "FW reset for authentication done\n"); + writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL); + /* leave some time for HW restore */ + usleep_range(800, 1000); + + return 0; +} + +int ipu_buttress_authenticate(struct ipu7_device *isp) +{ + struct ipu_buttress *b = &isp->buttress; + struct device *dev = &isp->pdev->dev; + u32 data, mask, done, fail; + int ret; + + if (!isp->secure_mode) { + dev_dbg(dev, "Skip auth for non-secure mode\n"); + return 0; + } + + mutex_lock(&b->auth_mutex); + + if (ipu_buttress_auth_done(isp)) { + ret = 0; + goto out_unlock; + } + + /* + * BUTTRESS_REG_FW_SOURCE_BASE needs to be set with FW CPD + * package address for secure mode. + */ + + writel(isp->cpd_fw->size, isp->base + BUTTRESS_REG_FW_SOURCE_SIZE); + writel(sg_dma_address(isp->psys->fw_sgt.sgl), + isp->base + BUTTRESS_REG_FW_SOURCE_BASE); + + /* + * Write boot_load into IU2CSEDATA0 + * Write sizeof(boot_load) | 0x2 << CLIENT_ID to + * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as + */ + dev_info(dev, "Sending BOOT_LOAD to CSE\n"); + ret = ipu_buttress_ipc_send(isp, BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD, + 1, true, + BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE); + if (ret) { + dev_err(dev, "CSE boot_load failed\n"); + goto out_unlock; + } + + mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK; + done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE; + fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED; + ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, + ((data & mask) == done || + (data & mask) == fail), 500, + BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); + if (ret) { + dev_err(dev, "CSE boot_load timeout\n"); + goto out_unlock; + } + + if ((data & mask) == fail) { + dev_err(dev, "CSE auth failed\n"); + ret = -EINVAL; + goto out_unlock; + } + + ret = readl_poll_timeout(isp->base + BOOTLOADER_STATUS_OFFSET, + data, data == BOOTLOADER_MAGIC_KEY, 500, + BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); + if (ret) { + dev_err(dev, "Unexpected magic number 0x%x\n", data); + goto out_unlock; + } + + /* + * Write authenticate_run into IU2CSEDATA0 + * Write sizeof(boot_load) | 0x2 << CLIENT_ID to + * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as + */ + dev_info(dev, "Sending AUTHENTICATE_RUN to CSE\n"); + ret = ipu_buttress_ipc_send(isp, BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN, + 1, true, + BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE); + if (ret) { + dev_err(dev, "CSE authenticate_run failed\n"); + goto out_unlock; + } + + done = BUTTRESS_SECURITY_CTL_AUTH_DONE; + ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, + ((data & mask) == done || + (data & mask) == fail), 500, + BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US); + if (ret) { + dev_err(dev, "CSE authenticate timeout\n"); + goto out_unlock; + } + + if ((data & mask) == fail) { + dev_err(dev, "CSE boot_load failed\n"); + ret = -EINVAL; + goto out_unlock; + } + + dev_info(dev, "CSE authenticate_run done\n"); + +out_unlock: + mutex_unlock(&b->auth_mutex); + + return ret; +} + +static int ipu_buttress_send_tsc_request(struct ipu7_device *isp) +{ + u32 val, mask, done; + int ret; + + mask = BUTTRESS_PWR_STATUS_HH_STATUS_MASK; + + writel(BUTTRESS_TSC_CMD_START_TSC_SYNC, + isp->base + BUTTRESS_REG_TSC_CMD); + + val = readl(isp->base + BUTTRESS_REG_PWR_STATUS); + val = FIELD_GET(mask, val); + if (val == BUTTRESS_PWR_STATUS_HH_STATE_ERR) { + dev_err(&isp->pdev->dev, "Start tsc sync failed\n"); + return -EINVAL; + } + + done = BUTTRESS_PWR_STATUS_HH_STATE_DONE; + ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, val, + FIELD_GET(mask, val) == done, 500, + BUTTRESS_TSC_SYNC_TIMEOUT_US); + if (ret) + dev_err(&isp->pdev->dev, "Start tsc sync timeout\n"); + + return ret; +} + +int ipu_buttress_start_tsc_sync(struct ipu7_device *isp) +{ + void __iomem *base = isp->base; + unsigned int i; + u32 val; + + if (is_ipu8(isp->hw_ver)) { + for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { + val = readl(base + BUTTRESS_REG_PB_TIMESTAMP_VALID); + if (val == 1) + return 0; + usleep_range(40, 50); + } + + dev_err(&isp->pdev->dev, "PB HH sync failed (valid %u)\n", val); + return -ETIMEDOUT; + } + + if (is_ipu7p5(isp->hw_ver)) { + val = readl(base + BUTTRESS_REG_TSC_CTL); + val |= BUTTRESS_SEL_PB_TIMESTAMP; + writel(val, base + BUTTRESS_REG_TSC_CTL); + + for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { + val = readl(base + BUTTRESS_REG_PB_TIMESTAMP_VALID); + if (val == 1) + return 0; + usleep_range(40, 50); + } + + dev_err(&isp->pdev->dev, "PB HH sync failed (valid %u)\n", val); + + return -ETIMEDOUT; + } + + for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { + int ret; + + ret = ipu_buttress_send_tsc_request(isp); + if (ret != -ETIMEDOUT) + return ret; + + val = readl(base + BUTTRESS_REG_TSC_CTL); + val = val | BUTTRESS_TSW_WA_SOFT_RESET; + writel(val, base + BUTTRESS_REG_TSC_CTL); + val = val & (~BUTTRESS_TSW_WA_SOFT_RESET); + writel(val, base + BUTTRESS_REG_TSC_CTL); + } + + dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n"); + + return -ETIMEDOUT; +} +EXPORT_SYMBOL_NS_GPL(ipu_buttress_start_tsc_sync, "INTEL_IPU7"); + +void ipu_buttress_tsc_read(struct ipu7_device *isp, u64 *val) +{ + unsigned long flags; + u32 tsc_hi, tsc_lo; + + local_irq_save(flags); + if (is_ipu7(isp->hw_ver)) { + tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO); + tsc_hi = readl(isp->base + BUTTRESS_REG_TSC_HI); + } else { + tsc_lo = readl(isp->base + BUTTRESS_REG_PB_TIMESTAMP_LO); + tsc_hi = readl(isp->base + BUTTRESS_REG_PB_TIMESTAMP_HI); + } + *val = (u64)tsc_hi << 32 | tsc_lo; + local_irq_restore(flags); +} +EXPORT_SYMBOL_NS_GPL(ipu_buttress_tsc_read, "INTEL_IPU7"); + +u64 ipu_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu7_device *isp) +{ + u64 ns = ticks * 10000; + + /* + * converting TSC tick count to ns is calculated by: + * Example (TSC clock frequency is 19.2MHz): + * ns = ticks * 1000 000 000 / 19.2Mhz + * = ticks * 1000 000 000 / 19200000Hz + * = ticks * 10000 / 192 ns + */ + return div_u64(ns, isp->buttress.ref_clk); +} +EXPORT_SYMBOL_NS_GPL(ipu_buttress_tsc_ticks_to_ns, "INTEL_IPU7"); + +/* trigger uc control to wakeup fw */ +void ipu_buttress_wakeup_is_uc(const struct ipu7_device *isp) +{ + u32 val; + + val = readl(isp->base + BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS); + val |= UCX_CTL_WAKEUP; + writel(val, isp->base + BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS); +} +EXPORT_SYMBOL_NS_GPL(ipu_buttress_wakeup_is_uc, "INTEL_IPU7"); + +void ipu_buttress_wakeup_ps_uc(const struct ipu7_device *isp) +{ + u32 val; + + val = readl(isp->base + BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS); + val |= UCX_CTL_WAKEUP; + writel(val, isp->base + BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS); +} +EXPORT_SYMBOL_NS_GPL(ipu_buttress_wakeup_ps_uc, "INTEL_IPU7"); + +static const struct x86_cpu_id ipu_misc_cfg_exclusion[] = { + X86_MATCH_VFM_STEPS(INTEL_PANTHERLAKE_L, 0x1, 0x1, 0), + {}, +}; + +static void ipu_buttress_setup(struct ipu7_device *isp) +{ + struct device *dev = &isp->pdev->dev; + u32 val; + + /* program PB BAR */ +#define WRXREQOP_OVRD_VAL_MASK GENMASK(22, 19) + writel(0, isp->pb_base + GLOBAL_INTERRUPT_MASK); + val = readl(isp->pb_base + BAR2_MISC_CONFIG); + if (is_ipu7(isp->hw_ver) || x86_match_cpu(ipu_misc_cfg_exclusion)) + val |= 0x100U; + else + val |= FIELD_PREP(WRXREQOP_OVRD_VAL_MASK, 0xf) | + BIT(18) | 0x100U; + + writel(val, isp->pb_base + BAR2_MISC_CONFIG); + val = readl(isp->pb_base + BAR2_MISC_CONFIG); + + if (is_ipu8(isp->hw_ver)) { + writel(BIT(13), isp->pb_base + TLBID_HASH_ENABLE_63_32); + writel(BIT(9), isp->pb_base + TLBID_HASH_ENABLE_95_64); + dev_dbg(dev, "IPU8 TLBID_HASH %x %x\n", + readl(isp->pb_base + TLBID_HASH_ENABLE_63_32), + readl(isp->pb_base + TLBID_HASH_ENABLE_95_64)); + } else if (is_ipu7p5(isp->hw_ver)) { + writel(BIT(14), isp->pb_base + TLBID_HASH_ENABLE_63_32); + writel(BIT(9), isp->pb_base + TLBID_HASH_ENABLE_95_64); + dev_dbg(dev, "IPU7P5 TLBID_HASH %x %x\n", + readl(isp->pb_base + TLBID_HASH_ENABLE_63_32), + readl(isp->pb_base + TLBID_HASH_ENABLE_95_64)); + } else { + writel(BIT(22), isp->pb_base + TLBID_HASH_ENABLE_63_32); + writel(BIT(1), isp->pb_base + TLBID_HASH_ENABLE_127_96); + dev_dbg(dev, "TLBID_HASH %x %x\n", + readl(isp->pb_base + TLBID_HASH_ENABLE_63_32), + readl(isp->pb_base + TLBID_HASH_ENABLE_127_96)); + } + + writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_CLEAR); + writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_MASK); + writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_ENABLE); + /* LNL SW workaround for PS PD hang when PS sub-domain during PD */ + writel(PS_FSM_CG, isp->base + BUTTRESS_REG_CG_CTRL_BITS); +} + +void ipu_buttress_restore(struct ipu7_device *isp) +{ + struct ipu_buttress *b = &isp->buttress; + + ipu_buttress_setup(isp); + + writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_IDLE_WDT); +} + +int ipu_buttress_init(struct ipu7_device *isp) +{ + int ret, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY; + struct ipu_buttress *b = &isp->buttress; + struct device *dev = &isp->pdev->dev; + u32 val; + + mutex_init(&b->power_mutex); + mutex_init(&b->auth_mutex); + mutex_init(&b->cons_mutex); + mutex_init(&b->ipc_mutex); + init_completion(&b->cse.send_complete); + init_completion(&b->cse.recv_complete); + + b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK; + b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK; + b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR; + b->cse.csr_out = BUTTRESS_REG_IU2CSECSR; + b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0; + b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0; + b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0; + b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0; + + isp->secure_mode = ipu_buttress_get_secure_mode(isp); + val = readl(isp->base + BUTTRESS_REG_IPU_SKU); + dev_info(dev, "IPU%u SKU %u in %s mode mask 0x%x\n", val & 0xfU, + (val >> 4) & 0x7U, isp->secure_mode ? "secure" : "non-secure", + readl(isp->base + BUTTRESS_REG_CAMERA_MASK)); + b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_IDLE_WDT); + b->ref_clk = 384; + + ipu_buttress_setup(isp); + + /* Retry couple of times in case of CSE initialization is delayed */ + do { + ret = ipu_buttress_ipc_reset(isp, &b->cse); + if (ret) { + dev_warn(dev, "IPC reset protocol failed, retrying\n"); + } else { + dev_dbg(dev, "IPC reset done\n"); + return 0; + } + } while (ipc_reset_retry--); + + dev_err(dev, "IPC reset protocol failed\n"); + + mutex_destroy(&b->power_mutex); + mutex_destroy(&b->auth_mutex); + mutex_destroy(&b->cons_mutex); + mutex_destroy(&b->ipc_mutex); + + return ret; +} + +void ipu_buttress_exit(struct ipu7_device *isp) +{ + struct ipu_buttress *b = &isp->buttress; + + writel(0, isp->base + BUTTRESS_REG_IRQ_ENABLE); + mutex_destroy(&b->power_mutex); + mutex_destroy(&b->auth_mutex); + mutex_destroy(&b->cons_mutex); + mutex_destroy(&b->ipc_mutex); +} diff --git a/drivers/staging/media/ipu7/ipu7-buttress.h b/drivers/staging/media/ipu7/ipu7-buttress.h new file mode 100644 index 000000000000..8da7dd612575 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-buttress.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#ifndef IPU7_BUTTRESS_H +#define IPU7_BUTTRESS_H + +#include +#include +#include +#include + +struct device; +struct ipu7_device; + +struct ipu_buttress_ctrl { + u32 subsys_id; + u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off; + u32 ratio; + u32 ratio_shift; + u32 cdyn; + u32 cdyn_shift; + u32 ovrd_clk; + u32 own_clk_ack; +}; + +struct ipu_buttress_ipc { + struct completion send_complete; + struct completion recv_complete; + u32 nack; + u32 nack_mask; + u32 recv_data; + u32 csr_out; + u32 csr_in; + u32 db0_in; + u32 db0_out; + u32 data0_out; + u32 data0_in; +}; + +struct ipu_buttress { + struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex; + struct ipu_buttress_ipc cse; + u32 psys_min_freq; + u32 wdt_cached_value; + u8 psys_force_ratio; + bool force_suspend; + u32 ref_clk; +}; + +int ipu_buttress_ipc_reset(struct ipu7_device *isp, + struct ipu_buttress_ipc *ipc); +int ipu_buttress_powerup(struct device *dev, + const struct ipu_buttress_ctrl *ctrl); +int ipu_buttress_powerdown(struct device *dev, + const struct ipu_buttress_ctrl *ctrl); +bool ipu_buttress_get_secure_mode(struct ipu7_device *isp); +int ipu_buttress_authenticate(struct ipu7_device *isp); +int ipu_buttress_reset_authentication(struct ipu7_device *isp); +bool ipu_buttress_auth_done(struct ipu7_device *isp); +int ipu_buttress_get_isys_freq(struct ipu7_device *isp, u32 *freq); +int ipu_buttress_get_psys_freq(struct ipu7_device *isp, u32 *freq); +int ipu_buttress_start_tsc_sync(struct ipu7_device *isp); +void ipu_buttress_tsc_read(struct ipu7_device *isp, u64 *val); +u64 ipu_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu7_device *isp); + +irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr); +irqreturn_t ipu_buttress_isr_threaded(int irq, void *isp_ptr); +int ipu_buttress_init(struct ipu7_device *isp); +void ipu_buttress_exit(struct ipu7_device *isp); +void ipu_buttress_csi_port_config(struct ipu7_device *isp, + u32 legacy, u32 combo); +void ipu_buttress_restore(struct ipu7_device *isp); +void ipu_buttress_wakeup_is_uc(const struct ipu7_device *isp); +void ipu_buttress_wakeup_ps_uc(const struct ipu7_device *isp); +#endif /* IPU7_BUTTRESS_H */ diff --git a/drivers/staging/media/ipu7/ipu7-platform-regs.h b/drivers/staging/media/ipu7/ipu7-platform-regs.h new file mode 100644 index 000000000000..eeadc886a8cf --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-platform-regs.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2018 - 2025 Intel Corporation + */ + +#ifndef IPU7_PLATFORM_REGS_H +#define IPU7_PLATFORM_REGS_H + +#define IS_BASE 0x230000 +#define IS_UC_CTRL_BASE (IS_BASE + 0x0) + +#define PS_BASE 0x130000 +#define PS_UC_CTRL_BASE (PS_BASE + 0x0) + +/* + * bit 0: IRQ from FW, + * bit 1, 2 and 3: IRQ from HW + */ +#define TO_SW_IRQ_MASK 0xf +#define TO_SW_IRQ_FW BIT(0) + +#define FW_CODE_BASE 0x0 +#define FW_DATA_BASE 0x4 +#define PRINTF_EN_THROUGH_TRACE 0x3004 +#define PRINTF_EN_DIRECTLY_TO_DDR 0x3008 +#define PRINTF_DDR_BASE_ADDR 0x300c +#define PRINTF_DDR_SIZE 0x3010 +#define PRINTF_DDR_NEXT_ADDR 0x3014 +#define PRINTF_STATUS 0x3018 +#define PRINTF_AXI_CNTL 0x301c +#define PRINTF_MSG_LENGTH 0x3020 +#define TO_SW_IRQ_CNTL_EDGE 0x4000 +#define TO_SW_IRQ_CNTL_MASK_N 0x4004 +#define TO_SW_IRQ_CNTL_STATUS 0x4008 +#define TO_SW_IRQ_CNTL_CLEAR 0x400c +#define TO_SW_IRQ_CNTL_ENABLE 0x4010 +#define TO_SW_IRQ_CNTL_LEVEL_NOT_PULSE 0x4014 +#define ERR_IRQ_CNTL_EDGE 0x4018 +#define ERR_IRQ_CNTL_MASK_N 0x401c +#define ERR_IRQ_CNTL_STATUS 0x4020 +#define ERR_IRQ_CNTL_CLEAR 0x4024 +#define ERR_IRQ_CNTL_ENABLE 0x4028 +#define ERR_IRQ_CNTL_LEVEL_NOT_PULSE 0x402c +#define LOCAL_DMEM_BASE_ADDR 0x1300000 + +/* + * IS_UC_TO_SW irqs + * bit 0: IRQ from local FW + * bit 1~3: IRQ from HW + */ +#define IS_UC_TO_SW_IRQ_MASK 0xf + +#define IPU_ISYS_SPC_OFFSET 0x210000 +#define IPU7_PSYS_SPC_OFFSET 0x118000 +#define IPU_ISYS_DMEM_OFFSET 0x200000 +#define IPU_PSYS_DMEM_OFFSET 0x100000 + +#define IPU7_ISYS_CSI_PORT_NUM 4 + +/* IRQ-related registers in PSYS */ +#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_EDGE 0x134000 +#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_MASK 0x134004 +#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_STATUS 0x134008 +#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR 0x13400c +#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE 0x134010 +#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_LEVEL_NOT_PULSE 0x134014 +#define IRQ_FROM_LOCAL_FW BIT(0) + +/* + * psys subdomains power request regs + */ +enum ipu7_device_buttress_psys_domain_pos { + IPU_PSYS_SUBDOMAIN_LB = 0, + IPU_PSYS_SUBDOMAIN_BB = 1, +}; + +#define IPU7_PSYS_DOMAIN_POWER_MASK (BIT(IPU_PSYS_SUBDOMAIN_LB) | \ + BIT(IPU_PSYS_SUBDOMAIN_BB)) +#define IPU8_PSYS_DOMAIN_POWER_MASK BIT(IPU_PSYS_SUBDOMAIN_LB) +#define IPU_PSYS_DOMAIN_POWER_IN_PROGRESS BIT(31) + +#endif /* IPU7_PLATFORM_REGS_H */ diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c new file mode 100644 index 000000000000..b3c4789fa5c8 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7.c @@ -0,0 +1,2786 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "abi/ipu7_fw_common_abi.h" + +#include "ipu7.h" +#include "ipu7-bus.h" +#include "ipu7-buttress.h" +#include "ipu7-buttress-regs.h" +#include "ipu7-cpd.h" +#include "ipu7-dma.h" +#include "ipu7-isys-csi2-regs.h" +#include "ipu7-mmu.h" +#include "ipu7-platform-regs.h" + +#define IPU_PCI_BAR 0 +#define IPU_PCI_PBBAR 4 + +static const unsigned int ipu7_csi_offsets[] = { + IPU_CSI_PORT_A_ADDR_OFFSET, + IPU_CSI_PORT_B_ADDR_OFFSET, + IPU_CSI_PORT_C_ADDR_OFFSET, + IPU_CSI_PORT_D_ADDR_OFFSET, +}; + +static struct ipu_isys_internal_pdata ipu7p5_isys_ipdata = { + .csi2 = { + .gpreg = IS_IO_CSI2_GPREGS_BASE, + }, + .hw_variant = { + .offset = IPU_UNIFIED_OFFSET, + .nr_mmus = IPU7P5_IS_MMU_NUM, + .mmu_hw = { + { + .name = "IS_FW_RD", + .offset = IPU7P5_IS_MMU_FW_RD_OFFSET, + .zlx_offset = IPU7P5_IS_ZLX_UC_RD_OFFSET, + .uao_offset = IPU7P5_IS_UAO_UC_RD_OFFSET, + .info_bits = 0x20005101, + .refill = 0x00002726, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG, + .l2_block = IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM, + .nr_l2streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM, + .l1_block_sz = { + 0x0, 0x8, 0xa, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, + }, + .zlx_nr = IPU7P5_IS_ZLX_UC_RD_NUM, + .zlx_axi_pool = { + 0x00000f30, + }, + .zlx_en = { + 0, 1, 0, 0 + }, + .zlx_conf = { + 0x0, + }, + .uao_p_num = IPU7P5_IS_UAO_UC_RD_PLANENUM, + .uao_p2tlb = { + 0x00000049, + 0x0000004c, + 0x0000004d, + 0x00000000, + }, + }, + { + .name = "IS_FW_WR", + .offset = IPU7P5_IS_MMU_FW_WR_OFFSET, + .zlx_offset = IPU7P5_IS_ZLX_UC_WR_OFFSET, + .uao_offset = IPU7P5_IS_UAO_UC_WR_OFFSET, + .info_bits = 0x20005001, + .refill = 0x00002524, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG, + .l2_block = IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM, + .nr_l2streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM, + .l1_block_sz = { + 0x0, 0x8, 0xa, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, + }, + .zlx_nr = IPU7P5_IS_ZLX_UC_WR_NUM, + .zlx_axi_pool = { + 0x00000f20, + }, + .zlx_en = { + 0, 1, 1, 0, + }, + .zlx_conf = { + 0x0, + 0x00010101, + 0x00010101, + 0x0, + }, + .uao_p_num = IPU7P5_IS_UAO_UC_WR_PLANENUM, + .uao_p2tlb = { + 0x00000049, + 0x0000004a, + 0x0000004b, + 0x00000000, + }, + }, + { + .name = "IS_DATA_WR_ISOC", + .offset = IPU7P5_IS_MMU_M0_OFFSET, + .zlx_offset = IPU7P5_IS_ZLX_M0_OFFSET, + .uao_offset = IPU7P5_IS_UAO_M0_WR_OFFSET, + .info_bits = 0x20004e01, + .refill = 0x00002120, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG, + .l2_block = IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_IS_MMU_M0_STREAM_NUM, + .nr_l2streams = IPU7P5_IS_MMU_M0_STREAM_NUM, + .l1_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x0000001a, + 0x0000001c, + 0x0000001e, + }, + .l2_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x0000001a, + 0x0000001c, + 0x0000001e, + }, + .zlx_nr = IPU7P5_IS_ZLX_M0_NUM, + .zlx_axi_pool = { + 0x00000f10, + }, + .zlx_en = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + }, + .zlx_conf = { + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + }, + .uao_p_num = IPU7P5_IS_UAO_M0_WR_PLANENUM, + .uao_p2tlb = { + 0x00000041, + 0x00000042, + 0x00000043, + 0x00000044, + 0x00000041, + 0x00000042, + 0x00000043, + 0x00000044, + 0x00000041, + 0x00000042, + 0x00000043, + 0x00000044, + 0x00000041, + 0x00000042, + 0x00000043, + 0x00000044, + }, + }, + { + .name = "IS_DATA_WR_SNOOP", + .offset = IPU7P5_IS_MMU_M1_OFFSET, + .zlx_offset = IPU7P5_IS_ZLX_M1_OFFSET, + .uao_offset = IPU7P5_IS_UAO_M1_WR_OFFSET, + .info_bits = 0x20004f01, + .refill = 0x00002322, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG, + .l2_block = IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_IS_MMU_M1_STREAM_NUM, + .nr_l2streams = IPU7P5_IS_MMU_M1_STREAM_NUM, + .l1_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x0000001a, + 0x0000001c, + 0x0000001e, + }, + .l2_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x0000001a, + 0x0000001c, + 0x0000001e, + }, + .zlx_nr = IPU7P5_IS_ZLX_M1_NUM, + .zlx_axi_pool = { + 0x00000f20, + }, + .zlx_en = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + }, + .zlx_conf = { + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + }, + .uao_p_num = IPU7P5_IS_UAO_M1_WR_PLANENUM, + .uao_p2tlb = { + 0x00000045, + 0x00000046, + 0x00000047, + 0x00000048, + 0x00000045, + 0x00000046, + 0x00000047, + 0x00000048, + 0x00000045, + 0x00000046, + 0x00000047, + 0x00000048, + 0x00000045, + 0x00000046, + 0x00000047, + 0x00000048, + }, + }, + }, + .cdc_fifos = 3, + .cdc_fifo_threshold = {6, 8, 2}, + .dmem_offset = IPU_ISYS_DMEM_OFFSET, + .spc_offset = IPU_ISYS_SPC_OFFSET, + }, + .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, +}; + +static struct ipu_psys_internal_pdata ipu7p5_psys_ipdata = { + .hw_variant = { + .offset = IPU_UNIFIED_OFFSET, + .nr_mmus = IPU7P5_PS_MMU_NUM, + .mmu_hw = { + { + .name = "PS_FW_RD", + .offset = IPU7P5_PS_MMU_FW_RD_OFFSET, + .zlx_offset = IPU7P5_PS_ZLX_FW_RD_OFFSET, + .uao_offset = IPU7P5_PS_UAO_FW_RD_OFFSET, + .info_bits = 0x20004001, + .refill = 0x00002726, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG, + .l2_block = IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM, + .nr_l2streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM, + .l1_block_sz = { + 0x00000000, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000d, + 0x0000000f, + 0x00000011, + 0x00000012, + 0x00000013, + 0x00000014, + 0x00000016, + 0x00000018, + 0x00000019, + 0x0000001a, + 0x0000001a, + 0x0000001a, + }, + .l2_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x0000001a, + 0x0000001c, + 0x0000001e, + }, + .zlx_nr = IPU7P5_PS_ZLX_FW_RD_NUM, + .zlx_axi_pool = { + 0x00000f30, + }, + .zlx_en = { + 0, 1, 0, 0, 1, 1, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, + }, + .zlx_conf = { + 0x00000000, + 0x00010101, + 0x00000000, + 0x00000000, + 0x00010101, + 0x00010101, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00010101, + 0x00010101, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + }, + .uao_p_num = IPU7P5_PS_UAO_FW_RD_PLANENUM, + .uao_p2tlb = { + 0x0000002e, + 0x00000035, + 0x00000036, + 0x00000031, + 0x00000037, + 0x00000038, + 0x00000039, + 0x00000032, + 0x00000033, + 0x0000003a, + 0x0000003b, + 0x0000003c, + 0x00000034, + 0x0, + 0x0, + 0x0, + }, + }, + { + .name = "PS_FW_WR", + .offset = IPU7P5_PS_MMU_FW_WR_OFFSET, + .zlx_offset = IPU7P5_PS_ZLX_FW_WR_OFFSET, + .uao_offset = IPU7P5_PS_UAO_FW_WR_OFFSET, + .info_bits = 0x20003e01, + .refill = 0x00002322, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG, + .l2_block = IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM, + .nr_l2streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM, + .l1_block_sz = { + 0x00000000, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000d, + 0x0000000e, + 0x0000000f, + 0x00000010, + 0x00000010, + 0x00000010, + }, + .l2_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + }, + .zlx_nr = IPU7P5_PS_ZLX_FW_WR_NUM, + .zlx_axi_pool = { + 0x00000f20, + }, + .zlx_en = { + 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, + }, + .zlx_conf = { + 0x00000000, + 0x00010101, + 0x00010101, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + }, + .uao_p_num = IPU7P5_PS_UAO_FW_WR_PLANENUM, + .uao_p2tlb = { + 0x0000002e, + 0x0000002f, + 0x00000030, + 0x00000031, + 0x00000032, + 0x00000033, + 0x00000034, + 0x0, + 0x0, + 0x0, + }, + }, + { + .name = "PS_DATA_RD", + .offset = IPU7P5_PS_MMU_SRT_RD_OFFSET, + .zlx_offset = IPU7P5_PS_ZLX_DATA_RD_OFFSET, + .uao_offset = IPU7P5_PS_UAO_SRT_RD_OFFSET, + .info_bits = 0x20003f01, + .refill = 0x00002524, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG, + .l2_block = IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM, + .nr_l2streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM, + .l1_block_sz = { + 0x00000000, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000b, + 0x0000000d, + 0x0000000f, + 0x00000013, + 0x00000017, + 0x00000019, + 0x0000001b, + 0x0000001d, + 0x0000001f, + 0x0000002b, + 0x00000033, + 0x0000003f, + 0x00000047, + 0x00000049, + 0x0000004b, + 0x0000004c, + 0x0000004d, + 0x0000004e, + }, + .l2_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x0000001a, + 0x0000001c, + 0x0000001e, + 0x00000020, + 0x00000022, + 0x00000024, + 0x00000026, + 0x00000028, + 0x0000002a, + }, + .zlx_nr = IPU7P5_PS_ZLX_DATA_RD_NUM, + .zlx_axi_pool = { + 0x00000f30, + }, + .zlx_en = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 0, 0, + }, + .zlx_conf = { + 0x00030303, + 0x00010101, + 0x00010101, + 0x00030202, + 0x00010101, + 0x00010101, + 0x00030303, + 0x00030303, + 0x00010101, + 0x00030800, + 0x00030500, + 0x00020101, + 0x00042000, + 0x00031000, + 0x00042000, + 0x00031000, + 0x00020400, + 0x00010101, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + }, + .uao_p_num = IPU7P5_PS_UAO_SRT_RD_PLANENUM, + .uao_p2tlb = { + 0x0000001c, + 0x0000001d, + 0x0000001e, + 0x0000001f, + 0x00000020, + 0x00000021, + 0x00000022, + 0x00000023, + 0x00000024, + 0x00000025, + 0x00000026, + 0x00000027, + 0x00000028, + 0x00000029, + 0x0000002a, + 0x0000002b, + 0x0000002c, + 0x0000002d, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + }, + }, + { + .name = "PS_DATA_WR", + .offset = IPU7P5_PS_MMU_SRT_WR_OFFSET, + .zlx_offset = IPU7P5_PS_ZLX_DATA_WR_OFFSET, + .uao_offset = IPU7P5_PS_UAO_SRT_WR_OFFSET, + .info_bits = 0x20003d01, + .refill = 0x00002120, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG, + .l2_block = IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG, + .nr_l1streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM, + .nr_l2streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM, + .l1_block_sz = { + 0x00000000, + 0x00000002, + 0x00000006, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x0000001a, + 0x0000001c, + 0x0000001e, + 0x00000020, + 0x00000022, + 0x00000024, + 0x00000028, + 0x0000002a, + 0x00000036, + 0x0000003e, + 0x00000040, + 0x00000042, + 0x0000004e, + 0x00000056, + 0x0000005c, + 0x00000068, + 0x00000070, + 0x00000076, + 0x00000077, + 0x00000078, + 0x00000079, + }, + .l2_block_sz = { + 0x00000000, + 0x00000002, + 0x00000006, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x0000001a, + 0x0000001c, + 0x0000001e, + 0x00000020, + 0x00000022, + 0x00000024, + 0x00000028, + 0x0000002a, + 0x00000036, + 0x0000003e, + 0x00000040, + 0x00000042, + 0x0000004e, + 0x00000056, + 0x0000005c, + 0x00000068, + 0x00000070, + 0x00000076, + 0x00000077, + 0x00000078, + 0x00000079, + }, + .zlx_nr = IPU7P5_PS_ZLX_DATA_WR_NUM, + .zlx_axi_pool = { + 0x00000f50, + }, + .zlx_en = { + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, + }, + .zlx_conf = { + 0x00010102, + 0x00030103, + 0x00030103, + 0x00010101, + 0x00010101, + 0x00030101, + 0x00010101, + 0x38010101, + 0x00000000, + 0x00000000, + 0x38010101, + 0x38010101, + 0x38010101, + 0x38010101, + 0x38010101, + 0x38010101, + 0x00030303, + 0x00010101, + 0x00042000, + 0x00031000, + 0x00010101, + 0x00010101, + 0x00042000, + 0x00031000, + 0x00031000, + 0x00042000, + 0x00031000, + 0x00031000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + }, + .uao_p_num = IPU7P5_PS_UAO_SRT_WR_PLANENUM, + .uao_p2tlb = { + 0x00000000, + 0x00000001, + 0x00000002, + 0x00000003, + 0x00000004, + 0x00000005, + 0x00000006, + 0x00000007, + 0x00000008, + 0x00000009, + 0x0000000a, + 0x0000000b, + 0x0000000c, + 0x0000000d, + 0x0000000e, + 0x0000000f, + 0x00000010, + 0x00000011, + 0x00000012, + 0x00000013, + 0x00000014, + 0x00000015, + 0x00000016, + 0x00000017, + 0x00000018, + 0x00000019, + 0x0000001a, + 0x0000001b, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + }, + }, + }, + .dmem_offset = IPU_PSYS_DMEM_OFFSET, + }, +}; + +static struct ipu_isys_internal_pdata ipu7_isys_ipdata = { + .csi2 = { + .gpreg = IS_IO_CSI2_GPREGS_BASE, + }, + .hw_variant = { + .offset = IPU_UNIFIED_OFFSET, + .nr_mmus = IPU7_IS_MMU_NUM, + .mmu_hw = { + { + .name = "IS_FW_RD", + .offset = IPU7_IS_MMU_FW_RD_OFFSET, + .zlx_offset = IPU7_IS_ZLX_UC_RD_OFFSET, + .uao_offset = IPU7_IS_UAO_UC_RD_OFFSET, + .info_bits = 0x20006701, + .refill = 0x00002726, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG, + .l2_block = IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_IS_MMU_FW_RD_STREAM_NUM, + .nr_l2streams = IPU7_IS_MMU_FW_RD_STREAM_NUM, + .l1_block_sz = { + 0x0, 0x8, 0xa, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, + }, + .zlx_nr = IPU7_IS_ZLX_UC_RD_NUM, + .zlx_axi_pool = { + 0x00000f30, + }, + .zlx_en = { + 0, 0, 0, 0 + }, + .zlx_conf = { + 0x0, 0x0, 0x0, 0x0, + }, + .uao_p_num = IPU7_IS_UAO_UC_RD_PLANENUM, + .uao_p2tlb = { + 0x00000061, + 0x00000064, + 0x00000065, + }, + }, + { + .name = "IS_FW_WR", + .offset = IPU7_IS_MMU_FW_WR_OFFSET, + .zlx_offset = IPU7_IS_ZLX_UC_WR_OFFSET, + .uao_offset = IPU7_IS_UAO_UC_WR_OFFSET, + .info_bits = 0x20006801, + .refill = 0x00002524, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG, + .l2_block = IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_IS_MMU_FW_WR_STREAM_NUM, + .nr_l2streams = IPU7_IS_MMU_FW_WR_STREAM_NUM, + .l1_block_sz = { + 0x0, 0x8, 0xa, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, + }, + .zlx_nr = IPU7_IS_ZLX_UC_WR_NUM, + .zlx_axi_pool = { + 0x00000f20, + }, + .zlx_en = { + 0, 1, 1, 0, + }, + .zlx_conf = { + 0x0, + 0x00010101, + 0x00010101, + }, + .uao_p_num = IPU7_IS_UAO_UC_WR_PLANENUM, + .uao_p2tlb = { + 0x00000061, + 0x00000062, + 0x00000063, + }, + }, + { + .name = "IS_DATA_WR_ISOC", + .offset = IPU7_IS_MMU_M0_OFFSET, + .zlx_offset = IPU7_IS_ZLX_M0_OFFSET, + .uao_offset = IPU7_IS_UAO_M0_WR_OFFSET, + .info_bits = 0x20006601, + .refill = 0x00002120, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_IS_MMU_M0_L1_BLOCKNR_REG, + .l2_block = IPU7_IS_MMU_M0_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_IS_MMU_M0_STREAM_NUM, + .nr_l2streams = IPU7_IS_MMU_M0_STREAM_NUM, + .l1_block_sz = { + 0x0, 0x3, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, + }, + .zlx_nr = IPU7_IS_ZLX_M0_NUM, + .zlx_axi_pool = { + 0x00000f10, + }, + .zlx_en = { + 1, 1, 1, 1, 1, 1, 1, 1, + }, + .zlx_conf = { + 0x00010103, + 0x00010103, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + }, + .uao_p_num = IPU7_IS_UAO_M0_WR_PLANENUM, + .uao_p2tlb = { + 0x00000049, + 0x0000004a, + 0x0000004b, + 0x0000004c, + 0x0000004d, + 0x0000004e, + 0x0000004f, + 0x00000050, + }, + }, + { + .name = "IS_DATA_WR_SNOOP", + .offset = IPU7_IS_MMU_M1_OFFSET, + .zlx_offset = IPU7_IS_ZLX_M1_OFFSET, + .uao_offset = IPU7_IS_UAO_M1_WR_OFFSET, + .info_bits = 0x20006901, + .refill = 0x00002322, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_IS_MMU_M1_L1_BLOCKNR_REG, + .l2_block = IPU7_IS_MMU_M1_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_IS_MMU_M1_STREAM_NUM, + .nr_l2streams = IPU7_IS_MMU_M1_STREAM_NUM, + .l1_block_sz = { + 0x0, 0x3, 0x6, 0x9, 0xc, + 0xe, 0x10, 0x12, 0x14, 0x16, + 0x18, 0x1a, 0x1c, 0x1e, 0x20, + 0x22, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, 0x6, 0x8, + 0xa, 0xc, 0xe, 0x10, 0x12, + 0x14, 0x16, 0x18, 0x1a, 0x1c, + 0x1e, + }, + .zlx_nr = IPU7_IS_ZLX_M1_NUM, + .zlx_axi_pool = { + 0x00000f20, + }, + .zlx_en = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + }, + .zlx_conf = { + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010103, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00010101, + }, + .uao_p_num = IPU7_IS_UAO_M1_WR_PLANENUM, + .uao_p2tlb = { + 0x00000051, + 0x00000052, + 0x00000053, + 0x00000054, + 0x00000055, + 0x00000056, + 0x00000057, + 0x00000058, + 0x00000059, + 0x0000005a, + 0x0000005b, + 0x0000005c, + 0x0000005d, + 0x0000005e, + 0x0000005f, + 0x00000060, + }, + }, + }, + .cdc_fifos = 3, + .cdc_fifo_threshold = {6, 8, 2}, + .dmem_offset = IPU_ISYS_DMEM_OFFSET, + .spc_offset = IPU_ISYS_SPC_OFFSET, + }, + .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, +}; + +static struct ipu_psys_internal_pdata ipu7_psys_ipdata = { + .hw_variant = { + .offset = IPU_UNIFIED_OFFSET, + .nr_mmus = IPU7_PS_MMU_NUM, + .mmu_hw = { + { + .name = "PS_FW_RD", + .offset = IPU7_PS_MMU_FW_RD_OFFSET, + .zlx_offset = IPU7_PS_ZLX_FW_RD_OFFSET, + .uao_offset = IPU7_PS_UAO_FW_RD_OFFSET, + .info_bits = 0x20004801, + .refill = 0x00002726, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG, + .l2_block = IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_PS_MMU_FW_RD_STREAM_NUM, + .nr_l2streams = IPU7_PS_MMU_FW_RD_STREAM_NUM, + .l1_block_sz = { + 0, 0x8, 0xa, 0xc, 0xd, + 0xf, 0x11, 0x12, 0x13, 0x14, + 0x16, 0x18, 0x19, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, 0x6, 0x8, + 0xa, 0xc, 0xe, 0x10, 0x12, + 0x14, 0x16, 0x18, 0x1a, 0x1c, + 0x1e, 0x20, 0x22, 0x24, 0x26, + }, + .zlx_nr = IPU7_PS_ZLX_FW_RD_NUM, + .zlx_axi_pool = { + 0x00000f30, + }, + .zlx_en = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + .zlx_conf = { + 0x0, + }, + .uao_p_num = IPU7_PS_UAO_FW_RD_PLANENUM, + .uao_p2tlb = { + 0x00000036, + 0x0000003d, + 0x0000003e, + 0x00000039, + 0x0000003f, + 0x00000040, + 0x00000041, + 0x0000003a, + 0x0000003b, + 0x00000042, + 0x00000043, + 0x00000044, + 0x0000003c, + }, + }, + { + .name = "PS_FW_WR", + .offset = IPU7_PS_MMU_FW_WR_OFFSET, + .zlx_offset = IPU7_PS_ZLX_FW_WR_OFFSET, + .uao_offset = IPU7_PS_UAO_FW_WR_OFFSET, + .info_bits = 0x20004601, + .refill = 0x00002322, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG, + .l2_block = IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_PS_MMU_FW_WR_STREAM_NUM, + .nr_l2streams = IPU7_PS_MMU_FW_WR_STREAM_NUM, + .l1_block_sz = { + 0, 0x8, 0xa, 0xc, 0xd, + 0xe, 0xf, 0x10, 0x10, 0x10, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, 0x6, 0x8, + 0xa, 0xc, 0xe, 0x10, 0x12, + }, + .zlx_nr = IPU7_PS_ZLX_FW_WR_NUM, + .zlx_axi_pool = { + 0x00000f20, + }, + .zlx_en = { + 0, 1, 1, 0, 0, 0, 0, 0, + 0, 0, + }, + .zlx_conf = { + 0x0, + 0x00010101, + 0x00010101, + }, + .uao_p_num = IPU7_PS_UAO_FW_WR_PLANENUM, + .uao_p2tlb = { + 0x00000036, + 0x00000037, + 0x00000038, + 0x00000039, + 0x0000003a, + 0x0000003b, + 0x0000003c, + }, + }, + { + .name = "PS_DATA_RD", + .offset = IPU7_PS_MMU_SRT_RD_OFFSET, + .zlx_offset = IPU7_PS_ZLX_DATA_RD_OFFSET, + .uao_offset = IPU7_PS_UAO_SRT_RD_OFFSET, + .info_bits = 0x20004701, + .refill = 0x00002120, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG, + .l2_block = IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM, + .nr_l2streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM, + .l1_block_sz = { + 0x0, 0x4, 0x6, 0x8, 0xb, + 0xd, 0xf, 0x11, 0x13, 0x15, + 0x17, 0x23, 0x2b, 0x37, 0x3f, + 0x41, 0x43, 0x44, 0x45, 0x46, + 0x47, 0x48, 0x49, 0x4a, 0x4b, + 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0x53, 0x55, 0x57, + 0x59, 0x5b, 0x5d, 0x5f, 0x61, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, 0x6, 0x8, + 0xa, 0xc, 0xe, 0x10, 0x12, + 0x14, 0x16, 0x18, 0x1a, 0x1c, + 0x1e, 0x20, 0x22, 0x24, 0x26, + 0x28, 0x2a, 0x2c, 0x2e, 0x30, + 0x32, 0x34, 0x36, 0x38, 0x3a, + 0x3c, 0x3e, 0x40, 0x42, 0x44, + 0x46, 0x48, 0x4a, 0x4c, 0x4e, + }, + .zlx_nr = IPU7_PS_ZLX_DATA_RD_NUM, + .zlx_axi_pool = { + 0x00000f30, + }, + .zlx_en = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + .zlx_conf = { + 0x00030303, + 0x00010101, + 0x00010101, + 0x00030202, + 0x00010101, + 0x00010101, + 0x00010101, + 0x00030800, + 0x00030500, + 0x00020101, + 0x00042000, + 0x00031000, + 0x00042000, + 0x00031000, + 0x00020400, + 0x00010101, + }, + .uao_p_num = IPU7_PS_UAO_SRT_RD_PLANENUM, + .uao_p2tlb = { + 0x00000022, + 0x00000023, + 0x00000024, + 0x00000025, + 0x00000026, + 0x00000027, + 0x00000028, + 0x00000029, + 0x0000002a, + 0x0000002b, + 0x0000002c, + 0x0000002d, + 0x0000002e, + 0x0000002f, + 0x00000030, + 0x00000031, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0000001e, + 0x0000001f, + 0x00000020, + 0x00000021, + 0x00000032, + 0x00000033, + 0x00000034, + 0x00000035, + }, + }, + { + .name = "PS_DATA_WR", + .offset = IPU7_PS_MMU_SRT_WR_OFFSET, + .zlx_offset = IPU7_PS_ZLX_DATA_WR_OFFSET, + .uao_offset = IPU7_PS_UAO_SRT_WR_OFFSET, + .info_bits = 0x20004501, + .refill = 0x00002120, + .collapse_en_bitmap = 0x0, + .l1_block = IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG, + .l2_block = IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG, + .nr_l1streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM, + .nr_l2streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM, + .l1_block_sz = { + 0x0, 0x2, 0x6, 0xa, 0xc, + 0xe, 0x10, 0x12, 0x14, 0x16, + 0x18, 0x1a, 0x1c, 0x1e, 0x20, + 0x22, 0x24, 0x26, 0x32, 0x3a, + 0x3c, 0x3e, 0x4a, 0x52, 0x58, + 0x64, 0x6c, 0x72, 0x7e, 0x86, + 0x8c, 0x8d, 0x8e, 0x8f, 0x90, + 0x91, 0x92, 0x94, 0x96, 0x98, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, 0x6, 0x8, + 0xa, 0xc, 0xe, 0x10, 0x12, + 0x14, 0x16, 0x18, 0x1a, 0x1c, + 0x1e, 0x20, 0x22, 0x24, 0x26, + 0x28, 0x2a, 0x2c, 0x2e, 0x30, + 0x32, 0x34, 0x36, 0x38, 0x3a, + 0x3c, 0x3e, 0x40, 0x42, 0x44, + 0x46, 0x48, 0x4a, 0x4c, 0x4e, + }, + .zlx_nr = IPU7_PS_ZLX_DATA_WR_NUM, + .zlx_axi_pool = { + 0x00000f50, + }, + .zlx_en = { + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 0, + }, + .zlx_conf = { + 0x00010102, + 0x00030103, + 0x00030103, + 0x00010101, + 0x00010101, + 0x00030101, + 0x00010101, + 0x38010101, + 0x0, + 0x0, + 0x38010101, + 0x38010101, + 0x38010101, + 0x38010101, + 0x38010101, + 0x38010101, + 0x00010101, + 0x00042000, + 0x00031000, + 0x00010101, + 0x00010101, + 0x00042000, + 0x00031000, + 0x00031000, + 0x00042000, + 0x00031000, + 0x00031000, + 0x00042000, + 0x00031000, + 0x00031000, + 0x0, + 0x0, + }, + .uao_p_num = IPU7_PS_UAO_SRT_WR_PLANENUM, + .uao_p2tlb = { + 0x00000000, + 0x00000001, + 0x00000002, + 0x00000003, + 0x00000004, + 0x00000005, + 0x00000006, + 0x00000007, + 0x00000008, + 0x00000009, + 0x0000000a, + 0x0000000b, + 0x0000000c, + 0x0000000d, + 0x0000000e, + 0x0000000f, + 0x00000010, + 0x00000011, + 0x00000012, + 0x00000013, + 0x00000014, + 0x00000015, + 0x00000016, + 0x00000017, + 0x00000018, + 0x00000019, + 0x0000001a, + 0x0000001b, + 0x0000001c, + 0x0000001d, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0000001e, + 0x0000001f, + 0x00000020, + 0x00000021, + }, + }, + }, + .dmem_offset = IPU_PSYS_DMEM_OFFSET, + }, +}; + +static struct ipu_isys_internal_pdata ipu8_isys_ipdata = { + .csi2 = { + .gpreg = IPU8_IS_IO_CSI2_GPREGS_BASE, + }, + .hw_variant = { + .offset = IPU_UNIFIED_OFFSET, + .nr_mmus = IPU8_IS_MMU_NUM, + .mmu_hw = { + { + .name = "IS_FW_RD", + .offset = IPU8_IS_MMU_FW_RD_OFFSET, + .zlx_offset = IPU8_IS_ZLX_UC_RD_OFFSET, + .uao_offset = IPU8_IS_UAO_UC_RD_OFFSET, + .info_bits = 0x20005101, + .refill = 0x00002726, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU8_IS_MMU_FW_RD_L1_BLOCKNR_REG, + .l2_block = IPU8_IS_MMU_FW_RD_L2_BLOCKNR_REG, + .nr_l1streams = IPU8_IS_MMU_FW_RD_STREAM_NUM, + .nr_l2streams = IPU8_IS_MMU_FW_RD_STREAM_NUM, + .l1_block_sz = { + 0x0, 0x8, 0xa, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, + }, + .zlx_nr = IPU8_IS_ZLX_UC_RD_NUM, + .zlx_axi_pool = { + 0x00000f30, + }, + .zlx_en = { + 0, 1, 0, 0 + }, + .zlx_conf = { + 0, 2, 0, 0 + }, + .uao_p_num = IPU8_IS_UAO_UC_RD_PLANENUM, + .uao_p2tlb = { + 0x00000049, + 0x0000004c, + 0x0000004d, + 0x00000000, + }, + }, + { + .name = "IS_FW_WR", + .offset = IPU8_IS_MMU_FW_WR_OFFSET, + .zlx_offset = IPU8_IS_ZLX_UC_WR_OFFSET, + .uao_offset = IPU8_IS_UAO_UC_WR_OFFSET, + .info_bits = 0x20005001, + .refill = 0x00002524, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU8_IS_MMU_FW_WR_L1_BLOCKNR_REG, + .l2_block = IPU8_IS_MMU_FW_WR_L2_BLOCKNR_REG, + .nr_l1streams = IPU8_IS_MMU_FW_WR_STREAM_NUM, + .nr_l2streams = IPU8_IS_MMU_FW_WR_STREAM_NUM, + .l1_block_sz = { + 0x0, 0x8, 0xa, + }, + .l2_block_sz = { + 0x0, 0x2, 0x4, + }, + .zlx_nr = IPU8_IS_ZLX_UC_WR_NUM, + .zlx_axi_pool = { + 0x00000f20, + }, + .zlx_en = { + 0, 1, 1, 0, + }, + .zlx_conf = { + 0x0, + 0x2, + 0x2, + 0x0, + }, + .uao_p_num = IPU8_IS_UAO_UC_WR_PLANENUM, + .uao_p2tlb = { + 0x00000049, + 0x0000004a, + 0x0000004b, + 0x00000000, + }, + }, + { + .name = "IS_DATA_WR_ISOC", + .offset = IPU8_IS_MMU_M0_OFFSET, + .zlx_offset = IPU8_IS_ZLX_M0_OFFSET, + .uao_offset = IPU8_IS_UAO_M0_WR_OFFSET, + .info_bits = 0x20004e01, + .refill = 0x00002120, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU8_IS_MMU_M0_L1_BLOCKNR_REG, + .l2_block = IPU8_IS_MMU_M0_L2_BLOCKNR_REG, + .nr_l1streams = IPU8_IS_MMU_M0_STREAM_NUM, + .nr_l2streams = IPU8_IS_MMU_M0_STREAM_NUM, + .l1_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x0000001a, + 0x0000001c, + 0x0000001e, + }, + .l2_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x0000001a, + 0x0000001c, + 0x0000001e, + }, + .zlx_nr = IPU8_IS_ZLX_M0_NUM, + .zlx_axi_pool = { + 0x00000f10, + }, + .zlx_en = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + }, + .zlx_conf = { + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + }, + .uao_p_num = IPU8_IS_UAO_M0_WR_PLANENUM, + .uao_p2tlb = { + 0x0000003b, + 0x0000003c, + 0x0000003d, + 0x0000003e, + 0x0000003b, + 0x0000003c, + 0x0000003d, + 0x0000003e, + 0x0000003b, + 0x0000003c, + 0x0000003d, + 0x0000003e, + 0x0000003b, + 0x0000003c, + 0x0000003d, + 0x0000003e, + }, + }, + { + .name = "IS_DATA_WR_SNOOP", + .offset = IPU8_IS_MMU_M1_OFFSET, + .zlx_offset = IPU8_IS_ZLX_M1_OFFSET, + .uao_offset = IPU8_IS_UAO_M1_WR_OFFSET, + .info_bits = 0x20004f01, + .refill = 0x00002322, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU8_IS_MMU_M1_L1_BLOCKNR_REG, + .l2_block = IPU8_IS_MMU_M1_L2_BLOCKNR_REG, + .nr_l1streams = IPU8_IS_MMU_M1_STREAM_NUM, + .nr_l2streams = IPU8_IS_MMU_M1_STREAM_NUM, + .l1_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x0000001a, + 0x0000001c, + 0x0000001e, + }, + .l2_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x0000001a, + 0x0000001c, + 0x0000001e, + }, + .zlx_nr = IPU8_IS_ZLX_M1_NUM, + .zlx_axi_pool = { + 0x00000f20, + }, + .zlx_en = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + }, + .zlx_conf = { + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + }, + .uao_p_num = IPU8_IS_UAO_M1_WR_PLANENUM, + .uao_p2tlb = { + 0x0000003f, + 0x00000040, + 0x00000041, + 0x00000042, + 0x0000003f, + 0x00000040, + 0x00000041, + 0x00000042, + 0x0000003f, + 0x00000040, + 0x00000041, + 0x00000042, + 0x0000003f, + 0x00000040, + 0x00000041, + 0x00000042, + }, + }, + { + .name = "IS_UPIPE", + .offset = IPU8_IS_MMU_UPIPE_OFFSET, + .zlx_offset = IPU8_IS_ZLX_UPIPE_OFFSET, + .uao_offset = IPU8_IS_UAO_UPIPE_OFFSET, + .info_bits = 0x20005201, + .refill = 0x00002928, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU8_IS_MMU_UPIPE_L1_BLOCKNR_REG, + .l2_block = IPU8_IS_MMU_UPIPE_L2_BLOCKNR_REG, + .nr_l1streams = IPU8_IS_MMU_UPIPE_STREAM_NUM, + .nr_l2streams = IPU8_IS_MMU_UPIPE_STREAM_NUM, + .l1_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + }, + .l2_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + }, + .zlx_nr = IPU8_IS_ZLX_UPIPE_NUM, + .zlx_axi_pool = { + 0x00000f20, + }, + .zlx_en = { + 1, 1, 1, 1, 1, 1, + }, + .zlx_conf = { + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + 0x3, + }, + .uao_p_num = IPU8_IS_UAO_UPIPE_PLANENUM, + .uao_p2tlb = { + 0x00000043, + 0x00000044, + 0x00000045, + 0x00000046, + 0x00000047, + 0x00000048, + }, + }, + }, + .cdc_fifos = 3, + .cdc_fifo_threshold = {6, 8, 2}, + .dmem_offset = IPU_ISYS_DMEM_OFFSET, + .spc_offset = IPU_ISYS_SPC_OFFSET, + }, + .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, +}; + +static struct ipu_psys_internal_pdata ipu8_psys_ipdata = { + .hw_variant = { + .offset = IPU_UNIFIED_OFFSET, + .nr_mmus = IPU8_PS_MMU_NUM, + .mmu_hw = { + { + .name = "PS_FW_RD", + .offset = IPU8_PS_MMU_FW_RD_OFFSET, + .zlx_offset = IPU8_PS_ZLX_FW_RD_OFFSET, + .uao_offset = IPU8_PS_UAO_FW_RD_OFFSET, + .info_bits = 0x20003a01, + .refill = 0x00002726, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU8_PS_MMU_FW_RD_L1_BLOCKNR_REG, + .l2_block = IPU8_PS_MMU_FW_RD_L2_BLOCKNR_REG, + .nr_l1streams = IPU8_PS_MMU_FW_RD_STREAM_NUM, + .nr_l2streams = IPU8_PS_MMU_FW_RD_STREAM_NUM, + .l1_block_sz = { + 0x00000000, + 0x00000008, + 0x0000000a, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x00000018, + 0x00000018, + 0x00000018, + }, + .l2_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + }, + .zlx_nr = IPU8_PS_ZLX_FW_RD_NUM, + .zlx_axi_pool = { + 0x00000f30, + }, + .zlx_en = { + 0, 1, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, + }, + .zlx_conf = { + 0x0, + 0x2, + 0x0, + 0x0, + 0x2, + 0x2, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + }, + .uao_p_num = IPU8_PS_UAO_FW_RD_PLANENUM, + .uao_p2tlb = { + 0x0000002d, + 0x00000032, + 0x00000033, + 0x00000030, + 0x00000034, + 0x00000035, + 0x00000036, + 0x00000031, + 0x0, + 0x0, + 0x0, + 0x0, + }, + }, + { + .name = "PS_FW_WR", + .offset = IPU8_PS_MMU_FW_WR_OFFSET, + .zlx_offset = IPU8_PS_ZLX_FW_WR_OFFSET, + .uao_offset = IPU8_PS_UAO_FW_WR_OFFSET, + .info_bits = 0x20003901, + .refill = 0x00002524, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU8_PS_MMU_FW_WR_L1_BLOCKNR_REG, + .l2_block = IPU8_PS_MMU_FW_WR_L2_BLOCKNR_REG, + .nr_l1streams = IPU8_PS_MMU_FW_WR_STREAM_NUM, + .nr_l2streams = IPU8_PS_MMU_FW_WR_STREAM_NUM, + .l1_block_sz = { + 0x00000000, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000010, + 0x00000010, + }, + .l2_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + }, + .zlx_nr = IPU8_PS_ZLX_FW_WR_NUM, + .zlx_axi_pool = { + 0x00000f20, + }, + .zlx_en = { + 0, 1, 1, 0, 0, 0, 0, 0, + }, + .zlx_conf = { + 0x0, 0x2, 0x2, 0x0, + 0x0, 0x0, 0x0, 0x0, + }, + .uao_p_num = IPU8_PS_UAO_FW_WR_PLANENUM, + .uao_p2tlb = { + 0x0000002d, + 0x0000002e, + 0x0000002f, + 0x00000030, + 0x00000031, + 0x0, + 0x0, + 0x0, + }, + }, + { + .name = "PS_DATA_RD", + .offset = IPU8_PS_MMU_SRT_RD_OFFSET, + .zlx_offset = IPU8_PS_ZLX_DATA_RD_OFFSET, + .uao_offset = IPU8_PS_UAO_SRT_RD_OFFSET, + .info_bits = 0x20003801, + .refill = 0x00002322, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU8_PS_MMU_SRT_RD_L1_BLOCKNR_REG, + .l2_block = IPU8_PS_MMU_SRT_RD_L2_BLOCKNR_REG, + .nr_l1streams = IPU8_PS_MMU_SRT_RD_STREAM_NUM, + .nr_l2streams = IPU8_PS_MMU_SRT_RD_STREAM_NUM, + .l1_block_sz = { + 0x00000000, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000014, + 0x00000018, + 0x0000001c, + 0x0000001e, + 0x00000022, + 0x00000024, + 0x00000026, + 0x00000028, + 0x0000002a, + 0x0000002c, + 0x0000002e, + 0x00000030, + 0x00000032, + 0x00000036, + 0x0000003a, + 0x0000003c, + 0x0000003c, + 0x0000003c, + 0x0000003c, + }, + .l2_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x0000001a, + 0x0000001c, + 0x0000001e, + 0x00000020, + 0x00000022, + 0x00000024, + 0x00000026, + 0x00000028, + 0x0000002a, + 0x0000002c, + 0x0000002e, + 0x00000030, + 0x00000032, + }, + .zlx_nr = IPU8_PS_ZLX_DATA_RD_NUM, + .zlx_axi_pool = { + 0x00000f30, + }, + .zlx_en = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, + }, + .zlx_conf = { + 0x6, 0x3, 0x3, 0x6, + 0x2, 0x2, 0x6, 0x6, + 0x6, 0x3, 0x6, 0x3, + 0x3, 0x2, 0x2, 0x2, + 0x2, 0x2, 0x2, 0x6, + 0x6, 0x3, 0x0, 0x0, + 0x0, 0x0, + }, + .uao_p_num = IPU8_PS_UAO_SRT_RD_PLANENUM, + .uao_p2tlb = { + 0x00000017, + 0x00000018, + 0x00000019, + 0x0000001a, + 0x0000001b, + 0x0000001c, + 0x0000001d, + 0x0000001e, + 0x0000001f, + 0x00000020, + 0x00000021, + 0x00000022, + 0x00000023, + 0x00000024, + 0x00000025, + 0x00000026, + 0x00000027, + 0x00000028, + 0x00000029, + 0x0000002a, + 0x0000002b, + 0x0000002c, + 0x0, + 0x0, + 0x0, + 0x0, + }, + }, + { + .name = "PS_DATA_WR", + .offset = IPU8_PS_MMU_SRT_WR_OFFSET, + .zlx_offset = IPU8_PS_ZLX_DATA_WR_OFFSET, + .uao_offset = IPU8_PS_UAO_SRT_WR_OFFSET, + .info_bits = 0x20003701, + .refill = 0x00002120, + .collapse_en_bitmap = 0x1, + .at_sp_arb_cfg = 0x1, + .l1_block = IPU8_PS_MMU_SRT_WR_L1_BLOCKNR_REG, + .l2_block = IPU8_PS_MMU_SRT_WR_L2_BLOCKNR_REG, + .nr_l1streams = IPU8_PS_MMU_SRT_WR_STREAM_NUM, + .nr_l2streams = IPU8_PS_MMU_SRT_WR_STREAM_NUM, + .l1_block_sz = { + 0x00000000, + 0x00000002, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x0000001c, + 0x0000001e, + 0x00000022, + 0x00000024, + 0x00000028, + 0x0000002a, + 0x0000002e, + 0x00000030, + 0x00000032, + 0x00000036, + 0x00000038, + 0x0000003a, + 0x0000003a, + 0x0000003a, + }, + .l2_block_sz = { + 0x00000000, + 0x00000002, + 0x00000004, + 0x00000006, + 0x00000008, + 0x0000000a, + 0x0000000c, + 0x0000000e, + 0x00000010, + 0x00000012, + 0x00000014, + 0x00000016, + 0x00000018, + 0x0000001a, + 0x0000001c, + 0x0000001e, + 0x00000020, + 0x00000022, + 0x00000024, + 0x00000026, + 0x00000028, + 0x0000002a, + 0x0000002c, + 0x0000002e, + 0x00000030, + 0x00000032, + }, + .zlx_nr = IPU8_PS_ZLX_DATA_WR_NUM, + .zlx_axi_pool = { + 0x00000f50, + }, + .zlx_en = { + 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, + }, + .zlx_conf = { + 0x3, + 0x6, + 0x38000002, + 0x38000000, + 0x3, + 0x38000002, + 0x38000002, + 0x38000002, + 0x38000002, + 0x38000002, + 0x38000002, + 0x6, + 0x3, + 0x6, + 0x3, + 0x6, + 0x3, + 0x6, + 0x3, + 0x3, + 0x6, + 0x3, + 0x3, + 0x0, + 0x0, + 0x0, + }, + .uao_p_num = IPU8_PS_UAO_SRT_WR_PLANENUM, + .uao_p2tlb = { + 0x00000000, + 0x00000001, + 0x00000002, + 0x00000003, + 0x00000004, + 0x00000005, + 0x00000006, + 0x00000007, + 0x00000008, + 0x00000009, + 0x0000000a, + 0x0000000b, + 0x0000000c, + 0x0000000d, + 0x0000000e, + 0x0000000f, + 0x00000010, + 0x00000011, + 0x00000012, + 0x00000013, + 0x00000014, + 0x00000015, + 0x00000016, + 0x00000000, + 0x00000000, + 0x00000000, + }, + }, + }, + .dmem_offset = IPU_PSYS_DMEM_OFFSET, + }, +}; + +static const struct ipu_buttress_ctrl ipu7_isys_buttress_ctrl = { + .subsys_id = IPU_IS, + .ratio = IPU7_IS_FREQ_CTL_DEFAULT_RATIO, + .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, + .cdyn = IPU_FREQ_CTL_CDYN, + .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, + .freq_ctl = BUTTRESS_REG_IS_WORKPOINT_REQ, + .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT, + .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK, + .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, + .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, + .ovrd_clk = BUTTRESS_OVERRIDE_IS_CLK, + .own_clk_ack = BUTTRESS_OWN_ACK_IS_CLK, +}; + +static const struct ipu_buttress_ctrl ipu7_psys_buttress_ctrl = { + .subsys_id = IPU_PS, + .ratio = IPU7_PS_FREQ_CTL_DEFAULT_RATIO, + .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, + .cdyn = IPU_FREQ_CTL_CDYN, + .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, + .freq_ctl = BUTTRESS_REG_PS_WORKPOINT_REQ, + .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT, + .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK, + .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, + .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, + .ovrd_clk = BUTTRESS_OVERRIDE_PS_CLK, + .own_clk_ack = BUTTRESS_OWN_ACK_PS_CLK, +}; + +static const struct ipu_buttress_ctrl ipu8_isys_buttress_ctrl = { + .subsys_id = IPU_IS, + .ratio = IPU8_IS_FREQ_CTL_DEFAULT_RATIO, + .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, + .cdyn = IPU_FREQ_CTL_CDYN, + .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, + .freq_ctl = BUTTRESS_REG_IS_WORKPOINT_REQ, + .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT, + .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK, + .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, + .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, +}; + +static const struct ipu_buttress_ctrl ipu8_psys_buttress_ctrl = { + .subsys_id = IPU_PS, + .ratio = IPU8_PS_FREQ_CTL_DEFAULT_RATIO, + .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, + .cdyn = IPU_FREQ_CTL_CDYN, + .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, + .freq_ctl = BUTTRESS_REG_PS_WORKPOINT_REQ, + .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT, + .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK, + .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, + .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, + .own_clk_ack = BUTTRESS_OWN_ACK_PS_PLL, +}; + +void ipu_internal_pdata_init(struct ipu_isys_internal_pdata *isys_ipdata, + struct ipu_psys_internal_pdata *psys_ipdata) +{ + isys_ipdata->csi2.nports = ARRAY_SIZE(ipu7_csi_offsets); + isys_ipdata->csi2.offsets = ipu7_csi_offsets; + isys_ipdata->num_parallel_streams = IPU7_ISYS_NUM_STREAMS; + psys_ipdata->hw_variant.spc_offset = IPU7_PSYS_SPC_OFFSET; +} + +static int ipu7_isys_check_fwnode_graph(struct fwnode_handle *fwnode) +{ + struct fwnode_handle *endpoint; + + if (IS_ERR_OR_NULL(fwnode)) + return -EINVAL; + + endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (endpoint) { + fwnode_handle_put(endpoint); + return 0; + } + + return ipu7_isys_check_fwnode_graph(fwnode->secondary); +} + +static struct ipu7_bus_device * +ipu7_isys_init(struct pci_dev *pdev, struct device *parent, + const struct ipu_buttress_ctrl *ctrl, void __iomem *base, + const struct ipu_isys_internal_pdata *ipdata, + unsigned int nr) +{ + struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); + struct ipu7_bus_device *isys_adev; + struct device *dev = &pdev->dev; + struct ipu7_isys_pdata *pdata; + int ret; + + ret = ipu7_isys_check_fwnode_graph(fwnode); + if (ret) { + if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary)) { + dev_err(dev, + "fwnode graph has no endpoints connection\n"); + return ERR_PTR(-EINVAL); + } + + ret = ipu_bridge_init(dev, ipu_bridge_parse_ssdb); + if (ret) { + dev_err_probe(dev, ret, "IPU bridge init failed\n"); + return ERR_PTR(ret); + } + } + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->base = base; + pdata->ipdata = ipdata; + + isys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl, + IPU_ISYS_NAME); + if (IS_ERR(isys_adev)) { + dev_err_probe(dev, PTR_ERR(isys_adev), + "ipu7_bus_initialize_device isys failed\n"); + kfree(pdata); + return ERR_CAST(isys_adev); + } + + isys_adev->mmu = ipu7_mmu_init(dev, base, ISYS_MMID, + &ipdata->hw_variant); + if (IS_ERR(isys_adev->mmu)) { + dev_err_probe(dev, PTR_ERR(isys_adev->mmu), + "ipu7_mmu_init(isys_adev->mmu) failed\n"); + put_device(&isys_adev->auxdev.dev); + kfree(pdata); + return ERR_CAST(isys_adev->mmu); + } + + isys_adev->mmu->dev = &isys_adev->auxdev.dev; + isys_adev->subsys = IPU_IS; + + ret = ipu7_bus_add_device(isys_adev); + if (ret) { + kfree(pdata); + return ERR_PTR(ret); + } + + return isys_adev; +} + +static struct ipu7_bus_device * +ipu7_psys_init(struct pci_dev *pdev, struct device *parent, + const struct ipu_buttress_ctrl *ctrl, void __iomem *base, + const struct ipu_psys_internal_pdata *ipdata, unsigned int nr) +{ + struct ipu7_bus_device *psys_adev; + struct ipu7_psys_pdata *pdata; + int ret; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->base = base; + pdata->ipdata = ipdata; + + psys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl, + IPU_PSYS_NAME); + if (IS_ERR(psys_adev)) { + dev_err_probe(&pdev->dev, PTR_ERR(psys_adev), + "ipu7_bus_initialize_device psys failed\n"); + kfree(pdata); + return ERR_CAST(psys_adev); + } + + psys_adev->mmu = ipu7_mmu_init(&pdev->dev, base, PSYS_MMID, + &ipdata->hw_variant); + if (IS_ERR(psys_adev->mmu)) { + dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), + "ipu7_mmu_init(psys_adev->mmu) failed\n"); + put_device(&psys_adev->auxdev.dev); + kfree(pdata); + return ERR_CAST(psys_adev->mmu); + } + + psys_adev->mmu->dev = &psys_adev->auxdev.dev; + psys_adev->subsys = IPU_PS; + + ret = ipu7_bus_add_device(psys_adev); + if (ret) { + kfree(pdata); + return ERR_PTR(ret); + } + + return psys_adev; +} + +static struct ia_gofo_msg_log_info_ts fw_error_log[IPU_SUBSYS_NUM]; +void ipu7_dump_fw_error_log(const struct ipu7_bus_device *adev) +{ + void __iomem *reg = adev->isp->base + ((adev->subsys == IPU_IS) ? + BUTTRESS_REG_FW_GP24 : + BUTTRESS_REG_FW_GP8); + + memcpy_fromio(&fw_error_log[adev->subsys], reg, + sizeof(fw_error_log[adev->subsys])); +} +EXPORT_SYMBOL_NS_GPL(ipu7_dump_fw_error_log, "INTEL_IPU7"); + +static int ipu7_pci_config_setup(struct pci_dev *dev) +{ + u16 pci_command; + int ret; + + pci_read_config_word(dev, PCI_COMMAND, &pci_command); + pci_command |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + pci_write_config_word(dev, PCI_COMMAND, pci_command); + + ret = pci_enable_msi(dev); + if (ret) + dev_err(&dev->dev, "Failed to enable msi (%d)\n", ret); + + return ret; +} + +static int ipu7_map_fw_code_region(struct ipu7_bus_device *sys, + void *data, size_t size) +{ + struct device *dev = &sys->auxdev.dev; + struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); + struct sg_table *sgt = &sys->fw_sgt; + struct ipu7_device *isp = adev->isp; + struct pci_dev *pdev = isp->pdev; + unsigned long n_pages, i; + unsigned long attr = 0; + struct page **pages; + int ret; + + n_pages = PFN_UP(size); + + pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL); + if (!pages) + return -ENOMEM; + + for (i = 0; i < n_pages; i++) { + struct page *p = vmalloc_to_page(data); + + if (!p) { + ret = -ENODEV; + goto out; + } + + pages[i] = p; + data += PAGE_SIZE; + } + + ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, size, + GFP_KERNEL); + if (ret) { + ret = -ENOMEM; + goto out; + } + + if (!isp->secure_mode) + attr |= DMA_ATTR_RESERVE_REGION; + + ret = dma_map_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); + if (ret < 0) { + dev_err(dev, "map fw code[%lu pages %u nents] failed\n", + n_pages, sgt->nents); + ret = -ENOMEM; + sg_free_table(sgt); + goto out; + } + + ret = ipu7_dma_map_sgtable(sys, sgt, DMA_BIDIRECTIONAL, attr); + if (ret) { + dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); + sg_free_table(sgt); + goto out; + } + + ipu7_dma_sync_sgtable(sys, sgt); + + dev_dbg(dev, "fw code region mapped at 0x%pad entries %d\n", + &sgt->sgl->dma_address, sgt->nents); + +out: + kfree(pages); + + return ret; +} + +static void ipu7_unmap_fw_code_region(struct ipu7_bus_device *sys) +{ + struct pci_dev *pdev = sys->isp->pdev; + struct sg_table *sgt = &sys->fw_sgt; + + ipu7_dma_unmap_sgtable(sys, sgt, DMA_BIDIRECTIONAL, 0); + dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); + sg_free_table(sgt); +} + +static int ipu7_init_fw_code_region_by_sys(struct ipu7_bus_device *sys, + const char *sys_name) +{ + struct device *dev = &sys->auxdev.dev; + struct ipu7_device *isp = sys->isp; + int ret; + + /* Copy FW binaries to specific location. */ + ret = ipu7_cpd_copy_binary(isp->cpd_fw->data, sys_name, + isp->fw_code_region, &sys->fw_entry); + if (ret) { + dev_err(dev, "%s binary not found.\n", sys_name); + return ret; + } + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "Failed to get runtime PM\n"); + return ret; + } + + ret = ipu7_mmu_hw_init(sys->mmu); + if (ret) { + dev_err(dev, "Failed to set mmu hw\n"); + pm_runtime_put(dev); + return ret; + } + + /* Map code region. */ + ret = ipu7_map_fw_code_region(sys, isp->fw_code_region, + IPU_FW_CODE_REGION_SIZE); + if (ret) + dev_err(dev, "Failed to map fw code region for %s.\n", + sys_name); + + ipu7_mmu_hw_cleanup(sys->mmu); + pm_runtime_put(dev); + + return ret; +} + +static int ipu7_init_fw_code_region(struct ipu7_device *isp) +{ + int ret; + + /* + * Allocate and map memory for FW execution. + * Not required in secure mode, in which FW runs in IMR. + */ + isp->fw_code_region = vmalloc(IPU_FW_CODE_REGION_SIZE); + if (!isp->fw_code_region) + return -ENOMEM; + + ret = ipu7_init_fw_code_region_by_sys(isp->isys, "isys"); + if (ret) + goto fail_init; + + ret = ipu7_init_fw_code_region_by_sys(isp->psys, "psys"); + if (ret) + goto fail_init; + + return 0; + +fail_init: + vfree(isp->fw_code_region); + + return ret; +} + +static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct ipu_buttress_ctrl *isys_ctrl = NULL, *psys_ctrl = NULL; + struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); + const struct ipu_buttress_ctrl *isys_buttress_ctrl; + const struct ipu_buttress_ctrl *psys_buttress_ctrl; + struct ipu_isys_internal_pdata *isys_ipdata; + struct ipu_psys_internal_pdata *psys_ipdata; + unsigned int dma_mask = IPU_DMA_MASK; + struct device *dev = &pdev->dev; + void __iomem *isys_base = NULL; + void __iomem *psys_base = NULL; + phys_addr_t phys, pb_phys; + struct ipu7_device *isp; + u32 is_es; + int ret; + + if (!fwnode || fwnode_property_read_u32(fwnode, "is_es", &is_es)) + is_es = 0; + + isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL); + if (!isp) + return -ENOMEM; + + dev_set_name(dev, "intel-ipu7"); + isp->pdev = pdev; + INIT_LIST_HEAD(&isp->devices); + + ret = pcim_enable_device(pdev); + if (ret) + return dev_err_probe(dev, ret, "Enable PCI device failed\n"); + + dev_info(dev, "Device 0x%x (rev: 0x%x)\n", + pdev->device, pdev->revision); + + phys = pci_resource_start(pdev, IPU_PCI_BAR); + pb_phys = pci_resource_start(pdev, IPU_PCI_PBBAR); + dev_info(dev, "IPU7 PCI BAR0 base %pap BAR2 base %pap\n", + &phys, &pb_phys); + + isp->base = pcim_iomap_region(pdev, IPU_PCI_BAR, IPU_NAME); + if (IS_ERR(isp->base)) + return dev_err_probe(dev, PTR_ERR(isp->base), + "Failed to I/O memory remapping bar %u\n", + IPU_PCI_BAR); + + isp->pb_base = pcim_iomap_region(pdev, IPU_PCI_PBBAR, IPU_NAME); + if (IS_ERR(isp->pb_base)) + return dev_err_probe(dev, PTR_ERR(isp->pb_base), + "Failed to I/O memory remapping bar %u\n", + IPU_PCI_PBBAR); + + dev_info(dev, "IPU7 PCI BAR0 mapped at %p\n BAR2 mapped at %p\n", + isp->base, isp->pb_base); + + pci_set_drvdata(pdev, isp); + pci_set_master(pdev); + + switch (id->device) { + case IPU7_PCI_ID: + isp->hw_ver = IPU_VER_7; + isp->cpd_fw_name = IPU7_FIRMWARE_NAME; + isys_ipdata = &ipu7_isys_ipdata; + psys_ipdata = &ipu7_psys_ipdata; + isys_buttress_ctrl = &ipu7_isys_buttress_ctrl; + psys_buttress_ctrl = &ipu7_psys_buttress_ctrl; + break; + case IPU7P5_PCI_ID: + isp->hw_ver = IPU_VER_7P5; + isp->cpd_fw_name = IPU7P5_FIRMWARE_NAME; + isys_ipdata = &ipu7p5_isys_ipdata; + psys_ipdata = &ipu7p5_psys_ipdata; + isys_buttress_ctrl = &ipu7_isys_buttress_ctrl; + psys_buttress_ctrl = &ipu7_psys_buttress_ctrl; + break; + case IPU8_PCI_ID: + isp->hw_ver = IPU_VER_8; + isp->cpd_fw_name = IPU8_FIRMWARE_NAME; + isys_ipdata = &ipu8_isys_ipdata; + psys_ipdata = &ipu8_psys_ipdata; + isys_buttress_ctrl = &ipu8_isys_buttress_ctrl; + psys_buttress_ctrl = &ipu8_psys_buttress_ctrl; + break; + default: + WARN(1, "Unsupported IPU device"); + return -ENODEV; + } + + ipu_internal_pdata_init(isys_ipdata, psys_ipdata); + + isys_base = isp->base + isys_ipdata->hw_variant.offset; + psys_base = isp->base + psys_ipdata->hw_variant.offset; + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_mask)); + if (ret) + return dev_err_probe(dev, ret, "Failed to set DMA mask\n"); + + dma_set_max_seg_size(dev, UINT_MAX); + + ret = ipu7_pci_config_setup(pdev); + if (ret) + return ret; + + ret = ipu_buttress_init(isp); + if (ret) + return ret; + + dev_info(dev, "firmware cpd file: %s\n", isp->cpd_fw_name); + + ret = request_firmware(&isp->cpd_fw, isp->cpd_fw_name, dev); + if (ret) { + dev_err_probe(dev, ret, + "Requesting signed firmware %s failed\n", + isp->cpd_fw_name); + goto buttress_exit; + } + + ret = ipu7_cpd_validate_cpd_file(isp, isp->cpd_fw->data, + isp->cpd_fw->size); + if (ret) { + dev_err_probe(dev, ret, "Failed to validate cpd\n"); + goto out_ipu_bus_del_devices; + } + + isys_ctrl = devm_kmemdup(dev, isys_buttress_ctrl, + sizeof(*isys_buttress_ctrl), GFP_KERNEL); + if (!isys_ctrl) { + ret = -ENOMEM; + goto out_ipu_bus_del_devices; + } + + isp->isys = ipu7_isys_init(pdev, dev, isys_ctrl, isys_base, + isys_ipdata, 0); + if (IS_ERR(isp->isys)) { + ret = PTR_ERR(isp->isys); + goto out_ipu_bus_del_devices; + } + + psys_ctrl = devm_kmemdup(dev, psys_buttress_ctrl, + sizeof(*psys_buttress_ctrl), GFP_KERNEL); + if (!psys_ctrl) { + ret = -ENOMEM; + goto out_ipu_bus_del_devices; + } + + isp->psys = ipu7_psys_init(pdev, &isp->isys->auxdev.dev, + psys_ctrl, psys_base, + psys_ipdata, 0); + if (IS_ERR(isp->psys)) { + ret = PTR_ERR(isp->psys); + goto out_ipu_bus_del_devices; + } + + ret = devm_request_threaded_irq(dev, pdev->irq, + ipu_buttress_isr, + ipu_buttress_isr_threaded, + IRQF_SHARED, IPU_NAME, isp); + if (ret) + goto out_ipu_bus_del_devices; + + if (!isp->secure_mode) { + ret = ipu7_init_fw_code_region(isp); + if (ret) + goto out_ipu_bus_del_devices; + } else { + ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); + if (ret < 0) { + dev_err(&isp->psys->auxdev.dev, + "Failed to get runtime PM\n"); + goto out_ipu_bus_del_devices; + } + + ret = ipu7_mmu_hw_init(isp->psys->mmu); + if (ret) { + dev_err_probe(&isp->pdev->dev, ret, + "Failed to init MMU hardware\n"); + goto out_ipu_bus_del_devices; + } + + ret = ipu7_map_fw_code_region(isp->psys, + (void *)isp->cpd_fw->data, + isp->cpd_fw->size); + if (ret) { + dev_err_probe(&isp->pdev->dev, ret, + "failed to map fw image\n"); + goto out_ipu_bus_del_devices; + } + + ret = ipu_buttress_authenticate(isp); + if (ret) { + dev_err_probe(&isp->pdev->dev, ret, + "FW authentication failed\n"); + goto out_ipu_bus_del_devices; + } + + ipu7_mmu_hw_cleanup(isp->psys->mmu); + pm_runtime_put(&isp->psys->auxdev.dev); + } + + pm_runtime_put_noidle(dev); + pm_runtime_allow(dev); + + isp->ipu7_bus_ready_to_probe = true; + + return 0; + +out_ipu_bus_del_devices: + if (!IS_ERR_OR_NULL(isp->isys) && isp->isys->fw_sgt.nents) + ipu7_unmap_fw_code_region(isp->isys); + if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) + ipu7_unmap_fw_code_region(isp->psys); + if (!IS_ERR_OR_NULL(isp->psys) && !IS_ERR_OR_NULL(isp->psys->mmu)) + ipu7_mmu_cleanup(isp->psys->mmu); + if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu)) + ipu7_mmu_cleanup(isp->isys->mmu); + if (!IS_ERR_OR_NULL(isp->psys)) + pm_runtime_put(&isp->psys->auxdev.dev); + ipu7_bus_del_devices(pdev); + release_firmware(isp->cpd_fw); +buttress_exit: + ipu_buttress_exit(isp); + + return ret; +} + +static void ipu7_pci_remove(struct pci_dev *pdev) +{ + struct ipu7_device *isp = pci_get_drvdata(pdev); + + if (!IS_ERR_OR_NULL(isp->isys) && isp->isys->fw_sgt.nents) + ipu7_unmap_fw_code_region(isp->isys); + if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) + ipu7_unmap_fw_code_region(isp->psys); + + if (!IS_ERR_OR_NULL(isp->fw_code_region)) + vfree(isp->fw_code_region); + + ipu7_bus_del_devices(pdev); + + pm_runtime_forbid(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + + ipu_buttress_exit(isp); + + release_firmware(isp->cpd_fw); + + ipu7_mmu_cleanup(isp->psys->mmu); + ipu7_mmu_cleanup(isp->isys->mmu); +} + +static void ipu7_pci_reset_prepare(struct pci_dev *pdev) +{ + struct ipu7_device *isp = pci_get_drvdata(pdev); + + dev_warn(&pdev->dev, "FLR prepare\n"); + pm_runtime_forbid(&isp->pdev->dev); +} + +static void ipu7_pci_reset_done(struct pci_dev *pdev) +{ + struct ipu7_device *isp = pci_get_drvdata(pdev); + + ipu_buttress_restore(isp); + if (isp->secure_mode) + ipu_buttress_reset_authentication(isp); + + isp->ipc_reinit = true; + pm_runtime_allow(&isp->pdev->dev); + + dev_warn(&pdev->dev, "FLR completed\n"); +} + +/* + * PCI base driver code requires driver to provide these to enable + * PCI device level PM state transitions (D0<->D3) + */ +static int ipu7_suspend(struct device *dev) +{ + return 0; +} + +static int ipu7_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct ipu7_device *isp = pci_get_drvdata(pdev); + struct ipu_buttress *b = &isp->buttress; + int ret; + + isp->secure_mode = ipu_buttress_get_secure_mode(isp); + dev_info(dev, "IPU7 in %s mode\n", + isp->secure_mode ? "secure" : "non-secure"); + + ipu_buttress_restore(isp); + + ret = ipu_buttress_ipc_reset(isp, &b->cse); + if (ret) + dev_err(dev, "IPC reset protocol failed!\n"); + + ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); + if (ret < 0) { + dev_err(dev, "Failed to get runtime PM\n"); + return 0; + } + + ret = ipu_buttress_authenticate(isp); + if (ret) + dev_err(dev, "FW authentication failed(%d)\n", ret); + + pm_runtime_put(&isp->psys->auxdev.dev); + + return 0; +} + +static int ipu7_runtime_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct ipu7_device *isp = pci_get_drvdata(pdev); + int ret; + + ipu_buttress_restore(isp); + + if (isp->ipc_reinit) { + struct ipu_buttress *b = &isp->buttress; + + isp->ipc_reinit = false; + ret = ipu_buttress_ipc_reset(isp, &b->cse); + if (ret) + dev_err(dev, "IPC reset protocol failed!\n"); + } + + return 0; +} + +static const struct dev_pm_ops ipu7_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(&ipu7_suspend, &ipu7_resume) + SET_RUNTIME_PM_OPS(&ipu7_suspend, /* Same as in suspend flow */ + &ipu7_runtime_resume, + NULL) +}; + +static const struct pci_device_id ipu7_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7_PCI_ID)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7P5_PCI_ID)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU8_PCI_ID)}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, ipu7_pci_tbl); + +static const struct pci_error_handlers pci_err_handlers = { + .reset_prepare = ipu7_pci_reset_prepare, + .reset_done = ipu7_pci_reset_done, +}; + +static struct pci_driver ipu7_pci_driver = { + .name = IPU_NAME, + .id_table = ipu7_pci_tbl, + .probe = ipu7_pci_probe, + .remove = ipu7_pci_remove, + .driver = { + .pm = &ipu7_pm_ops, + }, + .err_handler = &pci_err_handlers, +}; + +module_pci_driver(ipu7_pci_driver); + +MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); +MODULE_AUTHOR("Bingbu Cao "); +MODULE_AUTHOR("Tianshu Qiu "); +MODULE_AUTHOR("Qingwu Zhang "); +MODULE_AUTHOR("Intel"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel ipu7 pci driver"); diff --git a/drivers/staging/media/ipu7/ipu7.h b/drivers/staging/media/ipu7/ipu7.h new file mode 100644 index 000000000000..ac8ac0689468 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7.h @@ -0,0 +1,242 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#ifndef IPU7_H +#define IPU7_H + +#include +#include +#include + +#include "ipu7-buttress.h" + +struct ipu7_bus_device; +struct pci_dev; +struct firmware; + +#define IPU_NAME "intel-ipu7" +#define IPU_MEDIA_DEV_MODEL_NAME "ipu7" + +#define IPU7_FIRMWARE_NAME "intel/ipu/ipu7_fw.bin" +#define IPU7P5_FIRMWARE_NAME "intel/ipu/ipu7ptl_fw.bin" +#define IPU8_FIRMWARE_NAME "intel/ipu/ipu8_fw.bin" + +#define IPU7_ISYS_NUM_STREAMS 12 + +#define IPU7_PCI_ID 0x645d +#define IPU7P5_PCI_ID 0xb05d +#define IPU8_PCI_ID 0xd719 + +#define FW_LOG_BUF_SIZE (2 * 1024 * 1024) + +enum ipu_version { + IPU_VER_INVALID = 0, + IPU_VER_7 = 1, + IPU_VER_7P5 = 2, + IPU_VER_8 = 3, +}; + +static inline bool is_ipu7p5(u8 hw_ver) +{ + return hw_ver == IPU_VER_7P5; +} + +static inline bool is_ipu7(u8 hw_ver) +{ + return hw_ver == IPU_VER_7; +} + +static inline bool is_ipu8(u8 hw_ver) +{ + return hw_ver == IPU_VER_8; +} + +#define IPU_UNIFIED_OFFSET 0 + +/* + * ISYS DMA can overshoot. For higher resolutions over allocation is one line + * but it must be at minimum 1024 bytes. Value could be different in + * different versions / generations thus provide it via platform data. + */ +#define IPU_ISYS_OVERALLOC_MIN 1024 + +#define IPU_FW_CODE_REGION_SIZE 0x1000000 /* 16MB */ +#define IPU_FW_CODE_REGION_START 0x4000000 /* 64MB */ +#define IPU_FW_CODE_REGION_END (IPU_FW_CODE_REGION_START + \ + IPU_FW_CODE_REGION_SIZE) /* 80MB */ + +struct ipu7_device { + struct pci_dev *pdev; + struct list_head devices; + struct ipu7_bus_device *isys; + struct ipu7_bus_device *psys; + struct ipu_buttress buttress; + + const struct firmware *cpd_fw; + const char *cpd_fw_name; + /* Only for non-secure mode. */ + void *fw_code_region; + + void __iomem *base; + void __iomem *pb_base; + u8 hw_ver; + bool ipc_reinit; + bool secure_mode; + bool ipu7_bus_ready_to_probe; +}; + +#define IPU_DMA_MASK 39 +#define IPU_LIB_CALL_TIMEOUT_MS 2000 +#define IPU_PSYS_CMD_TIMEOUT_MS 2000 +#define IPU_PSYS_OPEN_CLOSE_TIMEOUT_US 50 +#define IPU_PSYS_OPEN_CLOSE_RETRY (10000 / IPU_PSYS_OPEN_CLOSE_TIMEOUT_US) + +#define IPU_ISYS_NAME "isys" +#define IPU_PSYS_NAME "psys" + +#define IPU_MMU_ADDR_BITS 32 +/* FW is accessible within the first 2 GiB only in non-secure mode. */ +#define IPU_MMU_ADDR_BITS_NON_SECURE 31 + +#define IPU7_IS_MMU_NUM 4U +#define IPU7_PS_MMU_NUM 4U +#define IPU7P5_IS_MMU_NUM 4U +#define IPU7P5_PS_MMU_NUM 4U +#define IPU8_IS_MMU_NUM 5U +#define IPU8_PS_MMU_NUM 4U +#define IPU_MMU_MAX_NUM 5U /* max(IS, PS) */ +#define IPU_MMU_MAX_TLB_L1_STREAMS 40U +#define IPU_MMU_MAX_TLB_L2_STREAMS 40U +#define IPU_ZLX_MAX_NUM 32U +#define IPU_ZLX_POOL_NUM 8U +#define IPU_UAO_PLANE_MAX_NUM 64U + +/* + * To maximize the IOSF utlization, IPU need to send requests in bursts. + * At the DMA interface with the buttress, there are CDC FIFOs with burst + * collection capability. CDC FIFO burst collectors have a configurable + * threshold and is configured based on the outcome of performance measurements. + * + * isys has 3 ports with IOSF interface for VC0, VC1 and VC2 + * psys has 4 ports with IOSF interface for VC0, VC1w, VC1r and VC2 + * + * Threshold values are pre-defined and are arrived at after performance + * evaluations on a type of IPU + */ +#define IPU_MAX_VC_IOSF_PORTS 4 + +/* + * IPU must configure correct arbitration mechanism related to the IOSF VC + * requests. There are two options per VC0 and VC1 - > 0 means rearbitrate on + * stall and 1 means stall until the request is completed. + */ +#define IPU_BTRS_ARB_MODE_TYPE_REARB 0 +#define IPU_BTRS_ARB_MODE_TYPE_STALL 1 + +/* Currently chosen arbitration mechanism for VC0 */ +#define IPU_BTRS_ARB_STALL_MODE_VC0 IPU_BTRS_ARB_MODE_TYPE_REARB + +/* Currently chosen arbitration mechanism for VC1 */ +#define IPU_BTRS_ARB_STALL_MODE_VC1 IPU_BTRS_ARB_MODE_TYPE_REARB + +/* One L2 entry maps 1024 L1 entries and one L1 entry per page */ +#define IPU_MMUV2_L2_RANGE (1024 * PAGE_SIZE) +/* Max L2 blocks per stream */ +#define IPU_MMUV2_MAX_L2_BLOCKS 2 +/* Max L1 blocks per stream */ +#define IPU_MMUV2_MAX_L1_BLOCKS 16 +#define IPU_MMUV2_TRASH_RANGE (IPU_MMUV2_L2_RANGE * \ + IPU_MMUV2_MAX_L2_BLOCKS) +/* Entries per L1 block */ +#define MMUV2_ENTRIES_PER_L1_BLOCK 16 +#define MMUV2_TRASH_L1_BLOCK_OFFSET (MMUV2_ENTRIES_PER_L1_BLOCK * PAGE_SIZE) +#define MMUV2_TRASH_L2_BLOCK_OFFSET IPU_MMUV2_L2_RANGE + +struct ipu7_mmu_hw { + char name[32]; + + void __iomem *base; + void __iomem *zlx_base; + void __iomem *uao_base; + + u32 offset; + u32 zlx_offset; + u32 uao_offset; + + u32 info_bits; + u32 refill; + u32 collapse_en_bitmap; + u32 at_sp_arb_cfg; + + u32 l1_block; + u32 l2_block; + + u8 nr_l1streams; + u8 nr_l2streams; + u32 l1_block_sz[IPU_MMU_MAX_TLB_L1_STREAMS]; + u32 l2_block_sz[IPU_MMU_MAX_TLB_L2_STREAMS]; + + u8 zlx_nr; + u32 zlx_axi_pool[IPU_ZLX_POOL_NUM]; + u32 zlx_en[IPU_ZLX_MAX_NUM]; + u32 zlx_conf[IPU_ZLX_MAX_NUM]; + + u32 uao_p_num; + u32 uao_p2tlb[IPU_UAO_PLANE_MAX_NUM]; +}; + +struct ipu7_mmu_pdata { + u32 nr_mmus; + struct ipu7_mmu_hw mmu_hw[IPU_MMU_MAX_NUM]; + int mmid; +}; + +struct ipu7_isys_csi2_pdata { + void __iomem *base; +}; + +struct ipu7_isys_internal_csi2_pdata { + u32 nports; + u32 const *offsets; + u32 gpreg; +}; + +struct ipu7_hw_variants { + unsigned long offset; + u32 nr_mmus; + struct ipu7_mmu_hw mmu_hw[IPU_MMU_MAX_NUM]; + u8 cdc_fifos; + u8 cdc_fifo_threshold[IPU_MAX_VC_IOSF_PORTS]; + u32 dmem_offset; + u32 spc_offset; /* SPC offset from psys base */ +}; + +struct ipu_isys_internal_pdata { + struct ipu7_isys_internal_csi2_pdata csi2; + struct ipu7_hw_variants hw_variant; + u32 num_parallel_streams; + u32 isys_dma_overshoot; +}; + +struct ipu7_isys_pdata { + void __iomem *base; + const struct ipu_isys_internal_pdata *ipdata; +}; + +struct ipu_psys_internal_pdata { + struct ipu7_hw_variants hw_variant; +}; + +struct ipu7_psys_pdata { + void __iomem *base; + const struct ipu_psys_internal_pdata *ipdata; +}; + +int request_cpd_fw(const struct firmware **firmware_p, const char *name, + struct device *device); +void ipu_internal_pdata_init(struct ipu_isys_internal_pdata *isys_ipdata, + struct ipu_psys_internal_pdata *psys_ipdata); +void ipu7_dump_fw_error_log(const struct ipu7_bus_device *adev); +#endif /* IPU7_H */ -- cgit v1.2.3 From 71d81c25683a7cace187cdeeb232b51bb72a0d04 Mon Sep 17 00:00:00 2001 From: Bingbu Cao Date: Thu, 29 May 2025 12:13:18 +0800 Subject: media: staging/ipu7: add IPU7 DMA APIs and MMU mapping The Intel IPU7 has internal microprocessor which runs the firmware The microprocessor accesses system DRAM by its internal 32-bit virtual address space. Driver should setup the MMU table and expose the DMA APIs for memory buffer mapping. This patch adds the IPU MMU and a DMA mapping implementation to setup the internal MMU hardware. Signed-off-by: Bingbu Cao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/ipu7/ipu7-dma.c | 477 +++++++++++++++++++ drivers/staging/media/ipu7/ipu7-dma.h | 46 ++ drivers/staging/media/ipu7/ipu7-mmu.c | 853 ++++++++++++++++++++++++++++++++++ drivers/staging/media/ipu7/ipu7-mmu.h | 414 +++++++++++++++++ 4 files changed, 1790 insertions(+) create mode 100644 drivers/staging/media/ipu7/ipu7-dma.c create mode 100644 drivers/staging/media/ipu7/ipu7-dma.h create mode 100644 drivers/staging/media/ipu7/ipu7-mmu.c create mode 100644 drivers/staging/media/ipu7/ipu7-mmu.h (limited to 'drivers/staging') diff --git a/drivers/staging/media/ipu7/ipu7-dma.c b/drivers/staging/media/ipu7/ipu7-dma.c new file mode 100644 index 000000000000..a118b41b2f34 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-dma.c @@ -0,0 +1,477 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipu7.h" +#include "ipu7-bus.h" +#include "ipu7-dma.h" +#include "ipu7-mmu.h" + +struct vm_info { + struct list_head list; + struct page **pages; + dma_addr_t ipu7_iova; + void *vaddr; + unsigned long size; +}; + +static struct vm_info *get_vm_info(struct ipu7_mmu *mmu, dma_addr_t iova) +{ + struct vm_info *info, *save; + + list_for_each_entry_safe(info, save, &mmu->vma_list, list) { + if (iova >= info->ipu7_iova && + iova < (info->ipu7_iova + info->size)) + return info; + } + + return NULL; +} + +static void __clear_buffer(struct page *page, size_t size, unsigned long attrs) +{ + void *ptr; + + if (!page) + return; + /* + * Ensure that the allocated pages are zeroed, and that any data + * lurking in the kernel direct-mapped region is invalidated. + */ + ptr = page_address(page); + memset(ptr, 0, size); + if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) + clflush_cache_range(ptr, size); +} + +static struct page **__alloc_buffer(size_t size, gfp_t gfp, unsigned long attrs) +{ + unsigned int count = PHYS_PFN(size); + unsigned int array_size = count * sizeof(struct page *); + struct page **pages; + int i = 0; + + pages = kvzalloc(array_size, GFP_KERNEL); + if (!pages) + return NULL; + + gfp |= __GFP_NOWARN; + + while (count) { + int j, order = __fls(count); + + pages[i] = alloc_pages(gfp, order); + while (!pages[i] && order) + pages[i] = alloc_pages(gfp, --order); + if (!pages[i]) + goto error; + + if (order) { + split_page(pages[i], order); + j = 1U << order; + while (j--) + pages[i + j] = pages[i] + j; + } + + __clear_buffer(pages[i], PAGE_SIZE << order, attrs); + i += 1U << order; + count -= 1U << order; + } + + return pages; +error: + while (i--) + if (pages[i]) + __free_pages(pages[i], 0); + kvfree(pages); + return NULL; +} + +static void __free_buffer(struct page **pages, size_t size, unsigned long attrs) +{ + unsigned int count = PHYS_PFN(size); + unsigned int i; + + for (i = 0; i < count && pages[i]; i++) { + __clear_buffer(pages[i], PAGE_SIZE, attrs); + __free_pages(pages[i], 0); + } + + kvfree(pages); +} + +void ipu7_dma_sync_single(struct ipu7_bus_device *sys, dma_addr_t dma_handle, + size_t size) +{ + void *vaddr; + u32 offset; + struct vm_info *info; + struct ipu7_mmu *mmu = sys->mmu; + + info = get_vm_info(mmu, dma_handle); + if (WARN_ON(!info)) + return; + + offset = dma_handle - info->ipu7_iova; + if (WARN_ON(size > (info->size - offset))) + return; + + vaddr = info->vaddr + offset; + clflush_cache_range(vaddr, size); +} +EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_single, "INTEL_IPU7"); + +void ipu7_dma_sync_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, + unsigned int nents) +{ + struct scatterlist *sg; + int i; + + for_each_sg(sglist, sg, nents, i) + clflush_cache_range(sg_virt(sg), sg->length); +} +EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_sg, "INTEL_IPU7"); + +void ipu7_dma_sync_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt) +{ + ipu7_dma_sync_sg(sys, sgt->sgl, sgt->orig_nents); +} +EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_sgtable, "INTEL_IPU7"); + +void *ipu7_dma_alloc(struct ipu7_bus_device *sys, size_t size, + dma_addr_t *dma_handle, gfp_t gfp, + unsigned long attrs) +{ + struct device *dev = &sys->auxdev.dev; + struct pci_dev *pdev = sys->isp->pdev; + dma_addr_t pci_dma_addr, ipu7_iova; + struct ipu7_mmu *mmu = sys->mmu; + struct vm_info *info; + unsigned long count; + struct page **pages; + struct iova *iova; + unsigned int i; + int ret; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return NULL; + + size = PAGE_ALIGN(size); + count = PHYS_PFN(size); + + iova = alloc_iova(&mmu->dmap->iovad, count, + PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); + if (!iova) + goto out_kfree; + + pages = __alloc_buffer(size, gfp, attrs); + if (!pages) + goto out_free_iova; + + dev_dbg(dev, "dma_alloc: size %zu iova low pfn %lu, high pfn %lu\n", + size, iova->pfn_lo, iova->pfn_hi); + for (i = 0; iova->pfn_lo + i <= iova->pfn_hi; i++) { + pci_dma_addr = dma_map_page_attrs(&pdev->dev, pages[i], 0, + PAGE_SIZE, DMA_BIDIRECTIONAL, + attrs); + dev_dbg(dev, "dma_alloc: mapped pci_dma_addr %pad\n", + &pci_dma_addr); + if (dma_mapping_error(&pdev->dev, pci_dma_addr)) { + dev_err(dev, "pci_dma_mapping for page[%d] failed", i); + goto out_unmap; + } + + ret = ipu7_mmu_map(mmu->dmap->mmu_info, + PFN_PHYS(iova->pfn_lo + i), pci_dma_addr, + PAGE_SIZE); + if (ret) { + dev_err(dev, "ipu7_mmu_map for pci_dma[%d] %pad failed", + i, &pci_dma_addr); + dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, + PAGE_SIZE, DMA_BIDIRECTIONAL, + attrs); + goto out_unmap; + } + } + + info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL); + if (!info->vaddr) + goto out_unmap; + + *dma_handle = PFN_PHYS(iova->pfn_lo); + + info->pages = pages; + info->ipu7_iova = *dma_handle; + info->size = size; + list_add(&info->list, &mmu->vma_list); + + return info->vaddr; + +out_unmap: + while (i--) { + ipu7_iova = PFN_PHYS(iova->pfn_lo + i); + pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info, + ipu7_iova); + dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE, + DMA_BIDIRECTIONAL, attrs); + + ipu7_mmu_unmap(mmu->dmap->mmu_info, ipu7_iova, PAGE_SIZE); + } + + __free_buffer(pages, size, attrs); + +out_free_iova: + __free_iova(&mmu->dmap->iovad, iova); +out_kfree: + kfree(info); + + return NULL; +} +EXPORT_SYMBOL_NS_GPL(ipu7_dma_alloc, "INTEL_IPU7"); + +void ipu7_dma_free(struct ipu7_bus_device *sys, size_t size, void *vaddr, + dma_addr_t dma_handle, unsigned long attrs) +{ + struct ipu7_mmu *mmu = sys->mmu; + struct pci_dev *pdev = sys->isp->pdev; + struct iova *iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(dma_handle)); + dma_addr_t pci_dma_addr, ipu7_iova; + struct vm_info *info; + struct page **pages; + unsigned int i; + + if (WARN_ON(!iova)) + return; + + info = get_vm_info(mmu, dma_handle); + if (WARN_ON(!info)) + return; + + if (WARN_ON(!info->vaddr)) + return; + + if (WARN_ON(!info->pages)) + return; + + list_del(&info->list); + + size = PAGE_ALIGN(size); + + pages = info->pages; + + vunmap(vaddr); + + for (i = 0; i < PHYS_PFN(size); i++) { + ipu7_iova = PFN_PHYS(iova->pfn_lo + i); + pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info, + ipu7_iova); + dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE, + DMA_BIDIRECTIONAL, attrs); + } + + ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), + PFN_PHYS(iova_size(iova))); + + __free_buffer(pages, size, attrs); + + mmu->tlb_invalidate(mmu); + + __free_iova(&mmu->dmap->iovad, iova); + + kfree(info); +} +EXPORT_SYMBOL_NS_GPL(ipu7_dma_free, "INTEL_IPU7"); + +int ipu7_dma_mmap(struct ipu7_bus_device *sys, struct vm_area_struct *vma, + void *addr, dma_addr_t iova, size_t size, + unsigned long attrs) +{ + struct ipu7_mmu *mmu = sys->mmu; + size_t count = PFN_UP(size); + struct vm_info *info; + size_t i; + int ret; + + info = get_vm_info(mmu, iova); + if (!info) + return -EFAULT; + + if (!info->vaddr) + return -EFAULT; + + if (vma->vm_start & ~PAGE_MASK) + return -EINVAL; + + if (size > info->size) + return -EFAULT; + + for (i = 0; i < count; i++) { + ret = vm_insert_page(vma, vma->vm_start + PFN_PHYS(i), + info->pages[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +void ipu7_dma_unmap_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, + int nents, enum dma_data_direction dir, + unsigned long attrs) +{ + struct device *dev = &sys->auxdev.dev; + struct ipu7_mmu *mmu = sys->mmu; + struct iova *iova = find_iova(&mmu->dmap->iovad, + PHYS_PFN(sg_dma_address(sglist))); + struct scatterlist *sg; + dma_addr_t pci_dma_addr; + unsigned int i; + + if (!nents) + return; + + if (WARN_ON(!iova)) + return; + + /* + * Before IPU7 mmu unmap, return the pci dma address back to sg + * assume the nents is less than orig_nents as the least granule + * is 1 SZ_4K page + */ + dev_dbg(dev, "trying to unmap concatenated %u ents\n", nents); + for_each_sg(sglist, sg, nents, i) { + dev_dbg(dev, "unmap sg[%d] %pad size %u\n", i, + &sg_dma_address(sg), sg_dma_len(sg)); + pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info, + sg_dma_address(sg)); + dev_dbg(dev, "return pci_dma_addr %pad back to sg[%d]\n", + &pci_dma_addr, i); + sg_dma_address(sg) = pci_dma_addr; + } + + dev_dbg(dev, "ipu7_mmu_unmap low pfn %lu high pfn %lu\n", + iova->pfn_lo, iova->pfn_hi); + ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), + PFN_PHYS(iova_size(iova))); + + mmu->tlb_invalidate(mmu); + __free_iova(&mmu->dmap->iovad, iova); +} +EXPORT_SYMBOL_NS_GPL(ipu7_dma_unmap_sg, "INTEL_IPU7"); + +int ipu7_dma_map_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, + int nents, enum dma_data_direction dir, + unsigned long attrs) +{ + struct device *dev = &sys->auxdev.dev; + struct ipu7_mmu *mmu = sys->mmu; + struct scatterlist *sg; + struct iova *iova; + size_t npages = 0; + unsigned long iova_addr; + int i; + + for_each_sg(sglist, sg, nents, i) { + if (sg->offset) { + dev_err(dev, "Unsupported non-zero sg[%d].offset %x\n", + i, sg->offset); + return -EFAULT; + } + } + + for_each_sg(sglist, sg, nents, i) + npages += PFN_UP(sg_dma_len(sg)); + + dev_dbg(dev, "dmamap trying to map %d ents %zu pages\n", + nents, npages); + + if (attrs & DMA_ATTR_RESERVE_REGION) { + /* + * Reserve iova with size aligned to IPU_FW_CODE_REGION_SIZE. + * Only apply for non-secure mode. + */ + unsigned long lo, hi; + + lo = iova_pfn(&mmu->dmap->iovad, IPU_FW_CODE_REGION_START); + hi = iova_pfn(&mmu->dmap->iovad, IPU_FW_CODE_REGION_END) - 1U; + iova = reserve_iova(&mmu->dmap->iovad, lo, hi); + if (!iova) { + dev_err(dev, "Reserve iova[%lx:%lx] failed.\n", lo, hi); + return -ENOMEM; + } + dev_dbg(dev, "iova[%lx:%lx] reserved for FW code.\n", lo, hi); + } else { + iova = alloc_iova(&mmu->dmap->iovad, npages, + PHYS_PFN(mmu->dmap->mmu_info->aperture_end), + 0); + if (!iova) + return 0; + } + + dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo, + iova->pfn_hi); + + iova_addr = iova->pfn_lo; + for_each_sg(sglist, sg, nents, i) { + phys_addr_t iova_pa; + int ret; + + iova_pa = PFN_PHYS(iova_addr); + dev_dbg(dev, "mapping entry %d: iova %pap phy %pap size %d\n", + i, &iova_pa, &sg_dma_address(sg), sg_dma_len(sg)); + + ret = ipu7_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr), + sg_dma_address(sg), + PAGE_ALIGN(sg_dma_len(sg))); + if (ret) + goto out_fail; + + sg_dma_address(sg) = PFN_PHYS(iova_addr); + + iova_addr += PFN_UP(sg_dma_len(sg)); + } + + dev_dbg(dev, "dmamap %d ents %zu pages mapped\n", nents, npages); + + return nents; + +out_fail: + ipu7_dma_unmap_sg(sys, sglist, i, dir, attrs); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ipu7_dma_map_sg, "INTEL_IPU7"); + +int ipu7_dma_map_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, + enum dma_data_direction dir, unsigned long attrs) +{ + int nents; + + nents = ipu7_dma_map_sg(sys, sgt->sgl, sgt->nents, dir, attrs); + if (nents < 0) + return nents; + + sgt->nents = nents; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ipu7_dma_map_sgtable, "INTEL_IPU7"); + +void ipu7_dma_unmap_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, + enum dma_data_direction dir, unsigned long attrs) +{ + ipu7_dma_unmap_sg(sys, sgt->sgl, sgt->nents, dir, attrs); +} +EXPORT_SYMBOL_NS_GPL(ipu7_dma_unmap_sgtable, "INTEL_IPU7"); diff --git a/drivers/staging/media/ipu7/ipu7-dma.h b/drivers/staging/media/ipu7/ipu7-dma.h new file mode 100644 index 000000000000..fe789af5e664 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-dma.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2013--2025 Intel Corporation */ + +#ifndef IPU7_DMA_H +#define IPU7_DMA_H + +#include +#include +#include +#include +#include + +#include "ipu7-bus.h" + +#define DMA_ATTR_RESERVE_REGION BIT(31) +struct ipu7_mmu_info; + +struct ipu7_dma_mapping { + struct ipu7_mmu_info *mmu_info; + struct iova_domain iovad; +}; + +void ipu7_dma_sync_single(struct ipu7_bus_device *sys, dma_addr_t dma_handle, + size_t size); +void ipu7_dma_sync_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, + unsigned int nents); +void ipu7_dma_sync_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt); +void *ipu7_dma_alloc(struct ipu7_bus_device *sys, size_t size, + dma_addr_t *dma_handle, gfp_t gfp, + unsigned long attrs); +void ipu7_dma_free(struct ipu7_bus_device *sys, size_t size, void *vaddr, + dma_addr_t dma_handle, unsigned long attrs); +int ipu7_dma_mmap(struct ipu7_bus_device *sys, struct vm_area_struct *vma, + void *addr, dma_addr_t iova, size_t size, + unsigned long attrs); +int ipu7_dma_map_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, + int nents, enum dma_data_direction dir, + unsigned long attrs); +void ipu7_dma_unmap_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, + int nents, enum dma_data_direction dir, + unsigned long attrs); +int ipu7_dma_map_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, + enum dma_data_direction dir, unsigned long attrs); +void ipu7_dma_unmap_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, + enum dma_data_direction dir, unsigned long attrs); +#endif /* IPU7_DMA_H */ diff --git a/drivers/staging/media/ipu7/ipu7-mmu.c b/drivers/staging/media/ipu7/ipu7-mmu.c new file mode 100644 index 000000000000..ded1986eb8ba --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-mmu.c @@ -0,0 +1,853 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipu7.h" +#include "ipu7-dma.h" +#include "ipu7-mmu.h" +#include "ipu7-platform-regs.h" + +#define ISP_PAGE_SHIFT 12 +#define ISP_PAGE_SIZE BIT(ISP_PAGE_SHIFT) +#define ISP_PAGE_MASK (~(ISP_PAGE_SIZE - 1U)) + +#define ISP_L1PT_SHIFT 22 +#define ISP_L1PT_MASK (~((1U << ISP_L1PT_SHIFT) - 1)) + +#define ISP_L2PT_SHIFT 12 +#define ISP_L2PT_MASK (~(ISP_L1PT_MASK | (~(ISP_PAGE_MASK)))) + +#define ISP_L1PT_PTES 1024U +#define ISP_L2PT_PTES 1024U + +#define ISP_PADDR_SHIFT 12 + +#define REG_L1_PHYS 0x0004 /* 27-bit pfn */ +#define REG_INFO 0x0008 + +#define TBL_PHYS_ADDR(a) ((phys_addr_t)(a) << ISP_PADDR_SHIFT) + +#define MMU_TLB_INVALIDATE_TIMEOUT 2000 + +static __maybe_unused void mmu_irq_handler(struct ipu7_mmu *mmu) +{ + unsigned int i; + u32 irq_cause; + + for (i = 0; i < mmu->nr_mmus; i++) { + irq_cause = readl(mmu->mmu_hw[i].base + MMU_REG_IRQ_CAUSE); + pr_info("mmu %s irq_cause = 0x%x", mmu->mmu_hw[i].name, + irq_cause); + writel(0x1ffff, mmu->mmu_hw[i].base + MMU_REG_IRQ_CLEAR); + } +} + +static void tlb_invalidate(struct ipu7_mmu *mmu) +{ + unsigned long flags; + unsigned int i; + int ret; + u32 val; + + spin_lock_irqsave(&mmu->ready_lock, flags); + if (!mmu->ready) { + spin_unlock_irqrestore(&mmu->ready_lock, flags); + return; + } + + for (i = 0; i < mmu->nr_mmus; i++) { + writel(0xffffffffU, mmu->mmu_hw[i].base + + MMU_REG_INVALIDATE_0); + + /* Need check with HW, use l1streams or l2streams */ + if (mmu->mmu_hw[i].nr_l2streams > 32) + writel(0xffffffffU, mmu->mmu_hw[i].base + + MMU_REG_INVALIDATE_1); + + /* + * The TLB invalidation is a "single cycle" (IOMMU clock cycles) + * When the actual MMIO write reaches the IPU TLB Invalidate + * register, wmb() will force the TLB invalidate out if the CPU + * attempts to update the IOMMU page table (or sooner). + */ + wmb(); + + /* wait invalidation done */ + ret = readl_poll_timeout_atomic(mmu->mmu_hw[i].base + + MMU_REG_INVALIDATION_STATUS, + val, !(val & 0x1U), 500, + MMU_TLB_INVALIDATE_TIMEOUT); + if (ret) + dev_err(mmu->dev, "MMU[%u] TLB invalidate failed\n", i); + } + + spin_unlock_irqrestore(&mmu->ready_lock, flags); +} + +static dma_addr_t map_single(struct ipu7_mmu_info *mmu_info, void *ptr) +{ + dma_addr_t dma; + + dma = dma_map_single(mmu_info->dev, ptr, PAGE_SIZE, DMA_BIDIRECTIONAL); + if (dma_mapping_error(mmu_info->dev, dma)) + return 0; + + return dma; +} + +static int get_dummy_page(struct ipu7_mmu_info *mmu_info) +{ + void *pt = (void *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); + dma_addr_t dma; + + if (!pt) + return -ENOMEM; + + dev_dbg(mmu_info->dev, "dummy_page: get_zeroed_page() == %p\n", pt); + + dma = map_single(mmu_info, pt); + if (!dma) { + dev_err(mmu_info->dev, "Failed to map dummy page\n"); + goto err_free_page; + } + + mmu_info->dummy_page = pt; + mmu_info->dummy_page_pteval = dma >> ISP_PAGE_SHIFT; + + return 0; + +err_free_page: + free_page((unsigned long)pt); + return -ENOMEM; +} + +static void free_dummy_page(struct ipu7_mmu_info *mmu_info) +{ + dma_unmap_single(mmu_info->dev, + TBL_PHYS_ADDR(mmu_info->dummy_page_pteval), + PAGE_SIZE, DMA_BIDIRECTIONAL); + free_page((unsigned long)mmu_info->dummy_page); +} + +static int alloc_dummy_l2_pt(struct ipu7_mmu_info *mmu_info) +{ + u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); + dma_addr_t dma; + unsigned int i; + + if (!pt) + return -ENOMEM; + + dev_dbg(mmu_info->dev, "dummy_l2: get_zeroed_page() = %p\n", pt); + + dma = map_single(mmu_info, pt); + if (!dma) { + dev_err(mmu_info->dev, "Failed to map l2pt page\n"); + goto err_free_page; + } + + for (i = 0; i < ISP_L2PT_PTES; i++) + pt[i] = mmu_info->dummy_page_pteval; + + mmu_info->dummy_l2_pt = pt; + mmu_info->dummy_l2_pteval = dma >> ISP_PAGE_SHIFT; + + return 0; + +err_free_page: + free_page((unsigned long)pt); + return -ENOMEM; +} + +static void free_dummy_l2_pt(struct ipu7_mmu_info *mmu_info) +{ + dma_unmap_single(mmu_info->dev, + TBL_PHYS_ADDR(mmu_info->dummy_l2_pteval), + PAGE_SIZE, DMA_BIDIRECTIONAL); + free_page((unsigned long)mmu_info->dummy_l2_pt); +} + +static u32 *alloc_l1_pt(struct ipu7_mmu_info *mmu_info) +{ + u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); + dma_addr_t dma; + unsigned int i; + + if (!pt) + return NULL; + + dev_dbg(mmu_info->dev, "alloc_l1: get_zeroed_page() = %p\n", pt); + + for (i = 0; i < ISP_L1PT_PTES; i++) + pt[i] = mmu_info->dummy_l2_pteval; + + dma = map_single(mmu_info, pt); + if (!dma) { + dev_err(mmu_info->dev, "Failed to map l1pt page\n"); + goto err_free_page; + } + + mmu_info->l1_pt_dma = dma >> ISP_PADDR_SHIFT; + dev_dbg(mmu_info->dev, "l1 pt %p mapped at %pad\n", pt, &dma); + + return pt; + +err_free_page: + free_page((unsigned long)pt); + return NULL; +} + +static u32 *alloc_l2_pt(struct ipu7_mmu_info *mmu_info) +{ + u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); + unsigned int i; + + if (!pt) + return NULL; + + dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt); + + for (i = 0; i < ISP_L1PT_PTES; i++) + pt[i] = mmu_info->dummy_page_pteval; + + return pt; +} + +static void l2_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova, + phys_addr_t dummy, size_t size) +{ + unsigned int l2_entries; + unsigned int l2_idx; + unsigned long flags; + u32 l1_idx; + u32 *l2_pt; + + spin_lock_irqsave(&mmu_info->lock, flags); + for (l1_idx = iova >> ISP_L1PT_SHIFT; + size > 0U && l1_idx < ISP_L1PT_PTES; l1_idx++) { + dev_dbg(mmu_info->dev, + "unmapping l2 pgtable (l1 index %u (iova 0x%8.8lx))\n", + l1_idx, iova); + + if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) { + dev_err(mmu_info->dev, + "unmap not mapped iova 0x%8.8lx l1 index %u\n", + iova, l1_idx); + continue; + } + l2_pt = mmu_info->l2_pts[l1_idx]; + + l2_entries = 0; + for (l2_idx = (iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; + size > 0U && l2_idx < ISP_L2PT_PTES; l2_idx++) { + phys_addr_t pteval = TBL_PHYS_ADDR(l2_pt[l2_idx]); + + dev_dbg(mmu_info->dev, + "unmap l2 index %u with pteval 0x%p\n", + l2_idx, &pteval); + l2_pt[l2_idx] = mmu_info->dummy_page_pteval; + + iova += ISP_PAGE_SIZE; + size -= ISP_PAGE_SIZE; + + l2_entries++; + } + + WARN_ON_ONCE(!l2_entries); + clflush_cache_range(&l2_pt[l2_idx - l2_entries], + sizeof(l2_pt[0]) * l2_entries); + } + + WARN_ON_ONCE(size); + spin_unlock_irqrestore(&mmu_info->lock, flags); +} + +static int l2_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, + phys_addr_t paddr, size_t size) +{ + struct device *dev = mmu_info->dev; + unsigned int l2_entries; + u32 *l2_pt, *l2_virt; + unsigned int l2_idx; + unsigned long flags; + size_t mapped = 0; + dma_addr_t dma; + u32 l1_entry; + u32 l1_idx; + int err = 0; + + spin_lock_irqsave(&mmu_info->lock, flags); + + paddr = ALIGN(paddr, ISP_PAGE_SIZE); + for (l1_idx = iova >> ISP_L1PT_SHIFT; + size && l1_idx < ISP_L1PT_PTES; l1_idx++) { + dev_dbg(dev, + "mapping l2 page table for l1 index %u (iova %8.8x)\n", + l1_idx, (u32)iova); + + l1_entry = mmu_info->l1_pt[l1_idx]; + if (l1_entry == mmu_info->dummy_l2_pteval) { + l2_virt = mmu_info->l2_pts[l1_idx]; + if (likely(!l2_virt)) { + l2_virt = alloc_l2_pt(mmu_info); + if (!l2_virt) { + err = -ENOMEM; + goto error; + } + } + + dma = map_single(mmu_info, l2_virt); + if (!dma) { + dev_err(dev, "Failed to map l2pt page\n"); + free_page((unsigned long)l2_virt); + err = -EINVAL; + goto error; + } + + l1_entry = dma >> ISP_PADDR_SHIFT; + + dev_dbg(dev, "page for l1_idx %u %p allocated\n", + l1_idx, l2_virt); + mmu_info->l1_pt[l1_idx] = l1_entry; + mmu_info->l2_pts[l1_idx] = l2_virt; + + clflush_cache_range(&mmu_info->l1_pt[l1_idx], + sizeof(mmu_info->l1_pt[l1_idx])); + } + + l2_pt = mmu_info->l2_pts[l1_idx]; + l2_entries = 0; + + for (l2_idx = (iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; + size && l2_idx < ISP_L2PT_PTES; l2_idx++) { + l2_pt[l2_idx] = paddr >> ISP_PADDR_SHIFT; + + dev_dbg(dev, "l2 index %u mapped as 0x%8.8x\n", l2_idx, + l2_pt[l2_idx]); + + iova += ISP_PAGE_SIZE; + paddr += ISP_PAGE_SIZE; + mapped += ISP_PAGE_SIZE; + size -= ISP_PAGE_SIZE; + + l2_entries++; + } + + WARN_ON_ONCE(!l2_entries); + clflush_cache_range(&l2_pt[l2_idx - l2_entries], + sizeof(l2_pt[0]) * l2_entries); + } + + spin_unlock_irqrestore(&mmu_info->lock, flags); + + return 0; + +error: + spin_unlock_irqrestore(&mmu_info->lock, flags); + /* unroll mapping in case something went wrong */ + if (mapped) + l2_unmap(mmu_info, iova - mapped, paddr - mapped, mapped); + + return err; +} + +static int __ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, + phys_addr_t paddr, size_t size) +{ + u32 iova_start = round_down(iova, ISP_PAGE_SIZE); + u32 iova_end = ALIGN(iova + size, ISP_PAGE_SIZE); + + dev_dbg(mmu_info->dev, + "mapping iova 0x%8.8x--0x%8.8x, size %zu at paddr %pap\n", + iova_start, iova_end, size, &paddr); + + return l2_map(mmu_info, iova_start, paddr, size); +} + +static void __ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, + unsigned long iova, size_t size) +{ + l2_unmap(mmu_info, iova, 0, size); +} + +static int allocate_trash_buffer(struct ipu7_mmu *mmu) +{ + unsigned int n_pages = PFN_UP(IPU_MMUV2_TRASH_RANGE); + unsigned long iova_addr; + struct iova *iova; + unsigned int i; + dma_addr_t dma; + int ret; + + /* Allocate 8MB in iova range */ + iova = alloc_iova(&mmu->dmap->iovad, n_pages, + PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); + if (!iova) { + dev_err(mmu->dev, "cannot allocate iova range for trash\n"); + return -ENOMEM; + } + + dma = dma_map_page(mmu->dmap->mmu_info->dev, mmu->trash_page, 0, + PAGE_SIZE, DMA_BIDIRECTIONAL); + if (dma_mapping_error(mmu->dmap->mmu_info->dev, dma)) { + dev_err(mmu->dmap->mmu_info->dev, "Failed to map trash page\n"); + ret = -ENOMEM; + goto out_free_iova; + } + + mmu->pci_trash_page = dma; + + /* + * Map the 8MB iova address range to the same physical trash page + * mmu->trash_page which is already reserved at the probe + */ + iova_addr = iova->pfn_lo; + for (i = 0; i < n_pages; i++) { + ret = ipu7_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr), + mmu->pci_trash_page, PAGE_SIZE); + if (ret) { + dev_err(mmu->dev, + "mapping trash buffer range failed\n"); + goto out_unmap; + } + + iova_addr++; + } + + mmu->iova_trash_page = PFN_PHYS(iova->pfn_lo); + dev_dbg(mmu->dev, "iova trash buffer for MMUID: %d is %u\n", + mmu->mmid, (unsigned int)mmu->iova_trash_page); + return 0; + +out_unmap: + ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), + PFN_PHYS(iova_size(iova))); + dma_unmap_page(mmu->dmap->mmu_info->dev, mmu->pci_trash_page, + PAGE_SIZE, DMA_BIDIRECTIONAL); +out_free_iova: + __free_iova(&mmu->dmap->iovad, iova); + return ret; +} + +static void __mmu_at_init(struct ipu7_mmu *mmu) +{ + struct ipu7_mmu_info *mmu_info; + unsigned int i; + + mmu_info = mmu->dmap->mmu_info; + for (i = 0; i < mmu->nr_mmus; i++) { + struct ipu7_mmu_hw *mmu_hw = &mmu->mmu_hw[i]; + unsigned int j; + + /* Write page table address per MMU */ + writel((phys_addr_t)mmu_info->l1_pt_dma, + mmu_hw->base + MMU_REG_PAGE_TABLE_BASE_ADDR); + dev_dbg(mmu->dev, "mmu %s base was set as %x\n", mmu_hw->name, + readl(mmu_hw->base + MMU_REG_PAGE_TABLE_BASE_ADDR)); + + /* Set info bits and axi_refill per MMU */ + writel(mmu_hw->info_bits, + mmu_hw->base + MMU_REG_USER_INFO_BITS); + writel(mmu_hw->refill, mmu_hw->base + MMU_REG_AXI_REFILL_IF_ID); + writel(mmu_hw->collapse_en_bitmap, + mmu_hw->base + MMU_REG_COLLAPSE_ENABLE_BITMAP); + + dev_dbg(mmu->dev, "mmu %s info_bits was set as %x\n", + mmu_hw->name, + readl(mmu_hw->base + MMU_REG_USER_INFO_BITS)); + + if (mmu_hw->at_sp_arb_cfg) + writel(mmu_hw->at_sp_arb_cfg, + mmu_hw->base + MMU_REG_AT_SP_ARB_CFG); + + /* default irq configuration */ + writel(0x3ff, mmu_hw->base + MMU_REG_IRQ_MASK); + writel(0x3ff, mmu_hw->base + MMU_REG_IRQ_ENABLE); + + /* Configure MMU TLB stream configuration for L1/L2 */ + for (j = 0; j < mmu_hw->nr_l1streams; j++) { + writel(mmu_hw->l1_block_sz[j], mmu_hw->base + + mmu_hw->l1_block + 4U * j); + } + + for (j = 0; j < mmu_hw->nr_l2streams; j++) { + writel(mmu_hw->l2_block_sz[j], mmu_hw->base + + mmu_hw->l2_block + 4U * j); + } + + for (j = 0; j < mmu_hw->uao_p_num; j++) { + if (!mmu_hw->uao_p2tlb[j]) + continue; + writel(mmu_hw->uao_p2tlb[j], mmu_hw->uao_base + 4U * j); + } + } +} + +static void __mmu_zlx_init(struct ipu7_mmu *mmu) +{ + unsigned int i; + + dev_dbg(mmu->dev, "mmu zlx init\n"); + + for (i = 0; i < mmu->nr_mmus; i++) { + struct ipu7_mmu_hw *mmu_hw = &mmu->mmu_hw[i]; + unsigned int j; + + dev_dbg(mmu->dev, "mmu %s zlx init\n", mmu_hw->name); + for (j = 0; j < IPU_ZLX_POOL_NUM; j++) { + if (!mmu_hw->zlx_axi_pool[j]) + continue; + writel(mmu_hw->zlx_axi_pool[j], + mmu_hw->zlx_base + ZLX_REG_AXI_POOL + j * 0x4U); + } + + for (j = 0; j < mmu_hw->zlx_nr; j++) { + if (!mmu_hw->zlx_conf[j]) + continue; + + writel(mmu_hw->zlx_conf[j], + mmu_hw->zlx_base + ZLX_REG_CONF + j * 0x8U); + } + + for (j = 0; j < mmu_hw->zlx_nr; j++) { + if (!mmu_hw->zlx_en[j]) + continue; + + writel(mmu_hw->zlx_en[j], + mmu_hw->zlx_base + ZLX_REG_EN + j * 0x8U); + } + } +} + +int ipu7_mmu_hw_init(struct ipu7_mmu *mmu) +{ + unsigned long flags; + + dev_dbg(mmu->dev, "IPU mmu hardware init\n"); + + /* Initialise the each MMU and ZLX */ + __mmu_at_init(mmu); + __mmu_zlx_init(mmu); + + if (!mmu->trash_page) { + int ret; + + mmu->trash_page = alloc_page(GFP_KERNEL); + if (!mmu->trash_page) { + dev_err(mmu->dev, "insufficient memory for trash buffer\n"); + return -ENOMEM; + } + + ret = allocate_trash_buffer(mmu); + if (ret) { + __free_page(mmu->trash_page); + mmu->trash_page = NULL; + dev_err(mmu->dev, "trash buffer allocation failed\n"); + return ret; + } + } + + spin_lock_irqsave(&mmu->ready_lock, flags); + mmu->ready = true; + spin_unlock_irqrestore(&mmu->ready_lock, flags); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ipu7_mmu_hw_init, "INTEL_IPU7"); + +static struct ipu7_mmu_info *ipu7_mmu_alloc(struct ipu7_device *isp) +{ + struct ipu7_mmu_info *mmu_info; + int ret; + + mmu_info = kzalloc(sizeof(*mmu_info), GFP_KERNEL); + if (!mmu_info) + return NULL; + + if (isp->secure_mode) { + mmu_info->aperture_start = IPU_FW_CODE_REGION_END; + mmu_info->aperture_end = + (dma_addr_t)DMA_BIT_MASK(IPU_MMU_ADDR_BITS); + } else { + mmu_info->aperture_start = IPU_FW_CODE_REGION_START; + mmu_info->aperture_end = + (dma_addr_t)DMA_BIT_MASK(IPU_MMU_ADDR_BITS_NON_SECURE); + } + + mmu_info->pgsize_bitmap = SZ_4K; + mmu_info->dev = &isp->pdev->dev; + + ret = get_dummy_page(mmu_info); + if (ret) + goto err_free_info; + + ret = alloc_dummy_l2_pt(mmu_info); + if (ret) + goto err_free_dummy_page; + + mmu_info->l2_pts = vzalloc(ISP_L2PT_PTES * sizeof(*mmu_info->l2_pts)); + if (!mmu_info->l2_pts) + goto err_free_dummy_l2_pt; + + /* + * We always map the L1 page table (a single page as well as + * the L2 page tables). + */ + mmu_info->l1_pt = alloc_l1_pt(mmu_info); + if (!mmu_info->l1_pt) + goto err_free_l2_pts; + + spin_lock_init(&mmu_info->lock); + + dev_dbg(mmu_info->dev, "domain initialised\n"); + + return mmu_info; + +err_free_l2_pts: + vfree(mmu_info->l2_pts); +err_free_dummy_l2_pt: + free_dummy_l2_pt(mmu_info); +err_free_dummy_page: + free_dummy_page(mmu_info); +err_free_info: + kfree(mmu_info); + + return NULL; +} + +void ipu7_mmu_hw_cleanup(struct ipu7_mmu *mmu) +{ + unsigned long flags; + + spin_lock_irqsave(&mmu->ready_lock, flags); + mmu->ready = false; + spin_unlock_irqrestore(&mmu->ready_lock, flags); +} +EXPORT_SYMBOL_NS_GPL(ipu7_mmu_hw_cleanup, "INTEL_IPU7"); + +static struct ipu7_dma_mapping *alloc_dma_mapping(struct ipu7_device *isp) +{ + struct ipu7_dma_mapping *dmap; + unsigned long base_pfn; + + dmap = kzalloc(sizeof(*dmap), GFP_KERNEL); + if (!dmap) + return NULL; + + dmap->mmu_info = ipu7_mmu_alloc(isp); + if (!dmap->mmu_info) { + kfree(dmap); + return NULL; + } + + /* 0~64M is forbidden for uctile controller */ + base_pfn = max_t(unsigned long, 1, + PFN_DOWN(dmap->mmu_info->aperture_start)); + init_iova_domain(&dmap->iovad, SZ_4K, base_pfn); + dmap->mmu_info->dmap = dmap; + + dev_dbg(&isp->pdev->dev, "alloc mapping\n"); + + iova_cache_get(); + + return dmap; +} + +phys_addr_t ipu7_mmu_iova_to_phys(struct ipu7_mmu_info *mmu_info, + dma_addr_t iova) +{ + phys_addr_t phy_addr; + unsigned long flags; + u32 *l2_pt; + + spin_lock_irqsave(&mmu_info->lock, flags); + l2_pt = mmu_info->l2_pts[iova >> ISP_L1PT_SHIFT]; + phy_addr = (phys_addr_t)l2_pt[(iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT]; + phy_addr <<= ISP_PAGE_SHIFT; + spin_unlock_irqrestore(&mmu_info->lock, flags); + + return phy_addr; +} + +void ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova, + size_t size) +{ + unsigned int min_pagesz; + + dev_dbg(mmu_info->dev, "unmapping iova 0x%lx size 0x%zx\n", iova, size); + + /* find out the minimum page size supported */ + min_pagesz = 1U << __ffs(mmu_info->pgsize_bitmap); + + /* + * The virtual address and the size of the mapping must be + * aligned (at least) to the size of the smallest page supported + * by the hardware + */ + if (!IS_ALIGNED(iova | size, min_pagesz)) { + dev_err(mmu_info->dev, + "unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n", + iova, size, min_pagesz); + return; + } + + __ipu7_mmu_unmap(mmu_info, iova, size); +} + +int ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, + phys_addr_t paddr, size_t size) +{ + unsigned int min_pagesz; + + if (mmu_info->pgsize_bitmap == 0UL) + return -ENODEV; + + /* find out the minimum page size supported */ + min_pagesz = 1U << __ffs(mmu_info->pgsize_bitmap); + + /* + * both the virtual address and the physical one, as well as + * the size of the mapping, must be aligned (at least) to the + * size of the smallest page supported by the hardware + */ + if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) { + dev_err(mmu_info->dev, + "unaligned: iova %lx pa %pa size %zx min_pagesz %x\n", + iova, &paddr, size, min_pagesz); + return -EINVAL; + } + + dev_dbg(mmu_info->dev, "map: iova 0x%lx pa %pa size 0x%zx\n", + iova, &paddr, size); + + return __ipu7_mmu_map(mmu_info, iova, paddr, size); +} + +static void ipu7_mmu_destroy(struct ipu7_mmu *mmu) +{ + struct ipu7_dma_mapping *dmap = mmu->dmap; + struct ipu7_mmu_info *mmu_info = dmap->mmu_info; + struct iova *iova; + u32 l1_idx; + + if (mmu->iova_trash_page) { + iova = find_iova(&dmap->iovad, PHYS_PFN(mmu->iova_trash_page)); + if (iova) { + /* unmap and free the trash buffer iova */ + ipu7_mmu_unmap(mmu_info, PFN_PHYS(iova->pfn_lo), + PFN_PHYS(iova_size(iova))); + __free_iova(&dmap->iovad, iova); + } else { + dev_err(mmu->dev, "trash buffer iova not found.\n"); + } + + mmu->iova_trash_page = 0; + dma_unmap_page(mmu_info->dev, mmu->pci_trash_page, + PAGE_SIZE, DMA_BIDIRECTIONAL); + mmu->pci_trash_page = 0; + __free_page(mmu->trash_page); + } + + for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) { + if (mmu_info->l1_pt[l1_idx] != mmu_info->dummy_l2_pteval) { + dma_unmap_single(mmu_info->dev, + TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]), + PAGE_SIZE, DMA_BIDIRECTIONAL); + free_page((unsigned long)mmu_info->l2_pts[l1_idx]); + } + } + + vfree(mmu_info->l2_pts); + free_dummy_page(mmu_info); + dma_unmap_single(mmu_info->dev, TBL_PHYS_ADDR(mmu_info->l1_pt_dma), + PAGE_SIZE, DMA_BIDIRECTIONAL); + free_page((unsigned long)mmu_info->dummy_l2_pt); + free_page((unsigned long)mmu_info->l1_pt); + kfree(mmu_info); +} + +struct ipu7_mmu *ipu7_mmu_init(struct device *dev, + void __iomem *base, int mmid, + const struct ipu7_hw_variants *hw) +{ + struct ipu7_device *isp = pci_get_drvdata(to_pci_dev(dev)); + struct ipu7_mmu_pdata *pdata; + struct ipu7_mmu *mmu; + unsigned int i; + + if (hw->nr_mmus > IPU_MMU_MAX_NUM) + return ERR_PTR(-EINVAL); + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < hw->nr_mmus; i++) { + struct ipu7_mmu_hw *pdata_mmu = &pdata->mmu_hw[i]; + const struct ipu7_mmu_hw *src_mmu = &hw->mmu_hw[i]; + + if (src_mmu->nr_l1streams > IPU_MMU_MAX_TLB_L1_STREAMS || + src_mmu->nr_l2streams > IPU_MMU_MAX_TLB_L2_STREAMS) + return ERR_PTR(-EINVAL); + + *pdata_mmu = *src_mmu; + pdata_mmu->base = base + src_mmu->offset; + pdata_mmu->zlx_base = base + src_mmu->zlx_offset; + pdata_mmu->uao_base = base + src_mmu->uao_offset; + } + + mmu = devm_kzalloc(dev, sizeof(*mmu), GFP_KERNEL); + if (!mmu) + return ERR_PTR(-ENOMEM); + + mmu->mmid = mmid; + mmu->mmu_hw = pdata->mmu_hw; + mmu->nr_mmus = hw->nr_mmus; + mmu->tlb_invalidate = tlb_invalidate; + mmu->ready = false; + INIT_LIST_HEAD(&mmu->vma_list); + spin_lock_init(&mmu->ready_lock); + + mmu->dmap = alloc_dma_mapping(isp); + if (!mmu->dmap) { + dev_err(dev, "can't alloc dma mapping\n"); + return ERR_PTR(-ENOMEM); + } + + return mmu; +} + +void ipu7_mmu_cleanup(struct ipu7_mmu *mmu) +{ + struct ipu7_dma_mapping *dmap = mmu->dmap; + + ipu7_mmu_destroy(mmu); + mmu->dmap = NULL; + iova_cache_put(); + put_iova_domain(&dmap->iovad); + kfree(dmap); +} diff --git a/drivers/staging/media/ipu7/ipu7-mmu.h b/drivers/staging/media/ipu7/ipu7-mmu.h new file mode 100644 index 000000000000..d85bb8ffc711 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-mmu.h @@ -0,0 +1,414 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#ifndef IPU7_MMU_H +#define IPU7_MMU_H + +#include +#include +#include +#include + +struct device; +struct page; +struct ipu7_hw_variants; +struct ipu7_mmu; +struct ipu7_mmu_info; + +#define ISYS_MMID 0x1 +#define PSYS_MMID 0x0 + +/* IPU7 for LNL */ +/* IS MMU Cmd RD */ +#define IPU7_IS_MMU_FW_RD_OFFSET 0x274000 +#define IPU7_IS_MMU_FW_RD_STREAM_NUM 3 +#define IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 +#define IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 + +/* IS MMU Cmd WR */ +#define IPU7_IS_MMU_FW_WR_OFFSET 0x275000 +#define IPU7_IS_MMU_FW_WR_STREAM_NUM 3 +#define IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 +#define IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 + +/* IS MMU Data WR Snoop */ +#define IPU7_IS_MMU_M0_OFFSET 0x276000 +#define IPU7_IS_MMU_M0_STREAM_NUM 8 +#define IPU7_IS_MMU_M0_L1_BLOCKNR_REG 0x54 +#define IPU7_IS_MMU_M0_L2_BLOCKNR_REG 0x74 + +/* IS MMU Data WR ISOC */ +#define IPU7_IS_MMU_M1_OFFSET 0x277000 +#define IPU7_IS_MMU_M1_STREAM_NUM 16 +#define IPU7_IS_MMU_M1_L1_BLOCKNR_REG 0x54 +#define IPU7_IS_MMU_M1_L2_BLOCKNR_REG 0x94 + +/* PS MMU FW RD */ +#define IPU7_PS_MMU_FW_RD_OFFSET 0x148000 +#define IPU7_PS_MMU_FW_RD_STREAM_NUM 20 +#define IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 +#define IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG 0xa4 + +/* PS MMU FW WR */ +#define IPU7_PS_MMU_FW_WR_OFFSET 0x149000 +#define IPU7_PS_MMU_FW_WR_STREAM_NUM 10 +#define IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 +#define IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c + +/* PS MMU FW Data RD VC0 */ +#define IPU7_PS_MMU_SRT_RD_OFFSET 0x14a000 +#define IPU7_PS_MMU_SRT_RD_STREAM_NUM 40 +#define IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 +#define IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xf4 + +/* PS MMU FW Data WR VC0 */ +#define IPU7_PS_MMU_SRT_WR_OFFSET 0x14b000 +#define IPU7_PS_MMU_SRT_WR_STREAM_NUM 40 +#define IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 +#define IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xf4 + +/* IS UAO UC RD */ +#define IPU7_IS_UAO_UC_RD_OFFSET 0x27c000 +#define IPU7_IS_UAO_UC_RD_PLANENUM 4 + +/* IS UAO UC WR */ +#define IPU7_IS_UAO_UC_WR_OFFSET 0x27d000 +#define IPU7_IS_UAO_UC_WR_PLANENUM 4 + +/* IS UAO M0 WR */ +#define IPU7_IS_UAO_M0_WR_OFFSET 0x27e000 +#define IPU7_IS_UAO_M0_WR_PLANENUM 8 + +/* IS UAO M1 WR */ +#define IPU7_IS_UAO_M1_WR_OFFSET 0x27f000 +#define IPU7_IS_UAO_M1_WR_PLANENUM 16 + +/* PS UAO FW RD */ +#define IPU7_PS_UAO_FW_RD_OFFSET 0x156000 +#define IPU7_PS_UAO_FW_RD_PLANENUM 20 + +/* PS UAO FW WR */ +#define IPU7_PS_UAO_FW_WR_OFFSET 0x157000 +#define IPU7_PS_UAO_FW_WR_PLANENUM 16 + +/* PS UAO SRT RD */ +#define IPU7_PS_UAO_SRT_RD_OFFSET 0x154000 +#define IPU7_PS_UAO_SRT_RD_PLANENUM 40 + +/* PS UAO SRT WR */ +#define IPU7_PS_UAO_SRT_WR_OFFSET 0x155000 +#define IPU7_PS_UAO_SRT_WR_PLANENUM 40 + +#define IPU7_IS_ZLX_UC_RD_OFFSET 0x278000 +#define IPU7_IS_ZLX_UC_WR_OFFSET 0x279000 +#define IPU7_IS_ZLX_M0_OFFSET 0x27a000 +#define IPU7_IS_ZLX_M1_OFFSET 0x27b000 +#define IPU7_IS_ZLX_UC_RD_NUM 4 +#define IPU7_IS_ZLX_UC_WR_NUM 4 +#define IPU7_IS_ZLX_M0_NUM 8 +#define IPU7_IS_ZLX_M1_NUM 16 + +#define IPU7_PS_ZLX_DATA_RD_OFFSET 0x14e000 +#define IPU7_PS_ZLX_DATA_WR_OFFSET 0x14f000 +#define IPU7_PS_ZLX_FW_RD_OFFSET 0x150000 +#define IPU7_PS_ZLX_FW_WR_OFFSET 0x151000 +#define IPU7_PS_ZLX_DATA_RD_NUM 32 +#define IPU7_PS_ZLX_DATA_WR_NUM 32 +#define IPU7_PS_ZLX_FW_RD_NUM 16 +#define IPU7_PS_ZLX_FW_WR_NUM 10 + +/* IPU7P5 for PTL */ +/* IS MMU Cmd RD */ +#define IPU7P5_IS_MMU_FW_RD_OFFSET 0x274000 +#define IPU7P5_IS_MMU_FW_RD_STREAM_NUM 3 +#define IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 +#define IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 + +/* IS MMU Cmd WR */ +#define IPU7P5_IS_MMU_FW_WR_OFFSET 0x275000 +#define IPU7P5_IS_MMU_FW_WR_STREAM_NUM 3 +#define IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 +#define IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 + +/* IS MMU Data WR Snoop */ +#define IPU7P5_IS_MMU_M0_OFFSET 0x276000 +#define IPU7P5_IS_MMU_M0_STREAM_NUM 16 +#define IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG 0x54 +#define IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG 0x94 + +/* IS MMU Data WR ISOC */ +#define IPU7P5_IS_MMU_M1_OFFSET 0x277000 +#define IPU7P5_IS_MMU_M1_STREAM_NUM 16 +#define IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG 0x54 +#define IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG 0x94 + +/* PS MMU FW RD */ +#define IPU7P5_PS_MMU_FW_RD_OFFSET 0x148000 +#define IPU7P5_PS_MMU_FW_RD_STREAM_NUM 16 +#define IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 +#define IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG 0x94 + +/* PS MMU FW WR */ +#define IPU7P5_PS_MMU_FW_WR_OFFSET 0x149000 +#define IPU7P5_PS_MMU_FW_WR_STREAM_NUM 10 +#define IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 +#define IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c + +/* PS MMU FW Data RD VC0 */ +#define IPU7P5_PS_MMU_SRT_RD_OFFSET 0x14a000 +#define IPU7P5_PS_MMU_SRT_RD_STREAM_NUM 22 +#define IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 +#define IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xac + +/* PS MMU FW Data WR VC0 */ +#define IPU7P5_PS_MMU_SRT_WR_OFFSET 0x14b000 +#define IPU7P5_PS_MMU_SRT_WR_STREAM_NUM 32 +#define IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 +#define IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xd4 + +/* IS UAO UC RD */ +#define IPU7P5_IS_UAO_UC_RD_OFFSET 0x27c000 +#define IPU7P5_IS_UAO_UC_RD_PLANENUM 4 + +/* IS UAO UC WR */ +#define IPU7P5_IS_UAO_UC_WR_OFFSET 0x27d000 +#define IPU7P5_IS_UAO_UC_WR_PLANENUM 4 + +/* IS UAO M0 WR */ +#define IPU7P5_IS_UAO_M0_WR_OFFSET 0x27e000 +#define IPU7P5_IS_UAO_M0_WR_PLANENUM 16 + +/* IS UAO M1 WR */ +#define IPU7P5_IS_UAO_M1_WR_OFFSET 0x27f000 +#define IPU7P5_IS_UAO_M1_WR_PLANENUM 16 + +/* PS UAO FW RD */ +#define IPU7P5_PS_UAO_FW_RD_OFFSET 0x156000 +#define IPU7P5_PS_UAO_FW_RD_PLANENUM 16 + +/* PS UAO FW WR */ +#define IPU7P5_PS_UAO_FW_WR_OFFSET 0x157000 +#define IPU7P5_PS_UAO_FW_WR_PLANENUM 10 + +/* PS UAO SRT RD */ +#define IPU7P5_PS_UAO_SRT_RD_OFFSET 0x154000 +#define IPU7P5_PS_UAO_SRT_RD_PLANENUM 22 + +/* PS UAO SRT WR */ +#define IPU7P5_PS_UAO_SRT_WR_OFFSET 0x155000 +#define IPU7P5_PS_UAO_SRT_WR_PLANENUM 32 + +#define IPU7P5_IS_ZLX_UC_RD_OFFSET 0x278000 +#define IPU7P5_IS_ZLX_UC_WR_OFFSET 0x279000 +#define IPU7P5_IS_ZLX_M0_OFFSET 0x27a000 +#define IPU7P5_IS_ZLX_M1_OFFSET 0x27b000 +#define IPU7P5_IS_ZLX_UC_RD_NUM 4 +#define IPU7P5_IS_ZLX_UC_WR_NUM 4 +#define IPU7P5_IS_ZLX_M0_NUM 16 +#define IPU7P5_IS_ZLX_M1_NUM 16 + +#define IPU7P5_PS_ZLX_DATA_RD_OFFSET 0x14e000 +#define IPU7P5_PS_ZLX_DATA_WR_OFFSET 0x14f000 +#define IPU7P5_PS_ZLX_FW_RD_OFFSET 0x150000 +#define IPU7P5_PS_ZLX_FW_WR_OFFSET 0x151000 +#define IPU7P5_PS_ZLX_DATA_RD_NUM 22 +#define IPU7P5_PS_ZLX_DATA_WR_NUM 32 +#define IPU7P5_PS_ZLX_FW_RD_NUM 16 +#define IPU7P5_PS_ZLX_FW_WR_NUM 10 + +/* IS MMU Cmd RD */ +#define IPU8_IS_MMU_FW_RD_OFFSET 0x270000 +#define IPU8_IS_MMU_FW_RD_STREAM_NUM 3 +#define IPU8_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 +#define IPU8_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 + +/* IS MMU Cmd WR */ +#define IPU8_IS_MMU_FW_WR_OFFSET 0x271000 +#define IPU8_IS_MMU_FW_WR_STREAM_NUM 3 +#define IPU8_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 +#define IPU8_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 + +/* IS MMU Data WR Snoop */ +#define IPU8_IS_MMU_M0_OFFSET 0x272000 +#define IPU8_IS_MMU_M0_STREAM_NUM 16 +#define IPU8_IS_MMU_M0_L1_BLOCKNR_REG 0x54 +#define IPU8_IS_MMU_M0_L2_BLOCKNR_REG 0x94 + +/* IS MMU Data WR ISOC */ +#define IPU8_IS_MMU_M1_OFFSET 0x273000 +#define IPU8_IS_MMU_M1_STREAM_NUM 16 +#define IPU8_IS_MMU_M1_L1_BLOCKNR_REG 0x54 +#define IPU8_IS_MMU_M1_L2_BLOCKNR_REG 0x94 + +/* IS MMU UPIPE ISOC */ +#define IPU8_IS_MMU_UPIPE_OFFSET 0x274000 +#define IPU8_IS_MMU_UPIPE_STREAM_NUM 6 +#define IPU8_IS_MMU_UPIPE_L1_BLOCKNR_REG 0x54 +#define IPU8_IS_MMU_UPIPE_L2_BLOCKNR_REG 0x6c + +/* PS MMU FW RD */ +#define IPU8_PS_MMU_FW_RD_OFFSET 0x148000 +#define IPU8_PS_MMU_FW_RD_STREAM_NUM 12 +#define IPU8_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 +#define IPU8_PS_MMU_FW_RD_L2_BLOCKNR_REG 0x84 + +/* PS MMU FW WR */ +#define IPU8_PS_MMU_FW_WR_OFFSET 0x149000 +#define IPU8_PS_MMU_FW_WR_STREAM_NUM 8 +#define IPU8_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 +#define IPU8_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x74 + +/* PS MMU FW Data RD VC0 */ +#define IPU8_PS_MMU_SRT_RD_OFFSET 0x14a000 +#define IPU8_PS_MMU_SRT_RD_STREAM_NUM 26 +#define IPU8_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 +#define IPU8_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xbc + +/* PS MMU FW Data WR VC0 */ +#define IPU8_PS_MMU_SRT_WR_OFFSET 0x14b000 +#define IPU8_PS_MMU_SRT_WR_STREAM_NUM 26 +#define IPU8_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 +#define IPU8_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xbc + +/* IS UAO UC RD */ +#define IPU8_IS_UAO_UC_RD_OFFSET 0x27a000 +#define IPU8_IS_UAO_UC_RD_PLANENUM 4 + +/* IS UAO UC WR */ +#define IPU8_IS_UAO_UC_WR_OFFSET 0x27b000 +#define IPU8_IS_UAO_UC_WR_PLANENUM 4 + +/* IS UAO M0 WR */ +#define IPU8_IS_UAO_M0_WR_OFFSET 0x27c000 +#define IPU8_IS_UAO_M0_WR_PLANENUM 16 + +/* IS UAO M1 WR */ +#define IPU8_IS_UAO_M1_WR_OFFSET 0x27d000 +#define IPU8_IS_UAO_M1_WR_PLANENUM 16 + +/* IS UAO UPIPE */ +#define IPU8_IS_UAO_UPIPE_OFFSET 0x27e000 +#define IPU8_IS_UAO_UPIPE_PLANENUM 6 + +/* PS UAO FW RD */ +#define IPU8_PS_UAO_FW_RD_OFFSET 0x156000 +#define IPU8_PS_UAO_FW_RD_PLANENUM 12 + +/* PS UAO FW WR */ +#define IPU8_PS_UAO_FW_WR_OFFSET 0x157000 +#define IPU8_PS_UAO_FW_WR_PLANENUM 8 + +/* PS UAO SRT RD */ +#define IPU8_PS_UAO_SRT_RD_OFFSET 0x154000 +#define IPU8_PS_UAO_SRT_RD_PLANENUM 26 + +/* PS UAO SRT WR */ +#define IPU8_PS_UAO_SRT_WR_OFFSET 0x155000 +#define IPU8_PS_UAO_SRT_WR_PLANENUM 26 + +#define IPU8_IS_ZLX_UC_RD_OFFSET 0x275000 +#define IPU8_IS_ZLX_UC_WR_OFFSET 0x276000 +#define IPU8_IS_ZLX_M0_OFFSET 0x277000 +#define IPU8_IS_ZLX_M1_OFFSET 0x278000 +#define IPU8_IS_ZLX_UPIPE_OFFSET 0x279000 +#define IPU8_IS_ZLX_UC_RD_NUM 4 +#define IPU8_IS_ZLX_UC_WR_NUM 4 +#define IPU8_IS_ZLX_M0_NUM 16 +#define IPU8_IS_ZLX_M1_NUM 16 +#define IPU8_IS_ZLX_UPIPE_NUM 6 + +#define IPU8_PS_ZLX_DATA_RD_OFFSET 0x14e000 +#define IPU8_PS_ZLX_DATA_WR_OFFSET 0x14f000 +#define IPU8_PS_ZLX_FW_RD_OFFSET 0x150000 +#define IPU8_PS_ZLX_FW_WR_OFFSET 0x151000 +#define IPU8_PS_ZLX_DATA_RD_NUM 26 +#define IPU8_PS_ZLX_DATA_WR_NUM 26 +#define IPU8_PS_ZLX_FW_RD_NUM 12 +#define IPU8_PS_ZLX_FW_WR_NUM 8 + +#define MMU_REG_INVALIDATE_0 0x00 +#define MMU_REG_INVALIDATE_1 0x04 +#define MMU_REG_PAGE_TABLE_BASE_ADDR 0x08 +#define MMU_REG_USER_INFO_BITS 0x0c +#define MMU_REG_AXI_REFILL_IF_ID 0x10 +#define MMU_REG_PW_EN_BITMAP 0x14 +#define MMU_REG_COLLAPSE_ENABLE_BITMAP 0x18 +#define MMU_REG_GENERAL_REG 0x1c +#define MMU_REG_AT_SP_ARB_CFG 0x20 +#define MMU_REG_INVALIDATION_STATUS 0x24 +#define MMU_REG_IRQ_LEVEL_NO_PULSE 0x28 +#define MMU_REG_IRQ_MASK 0x2c +#define MMU_REG_IRQ_ENABLE 0x30 +#define MMU_REG_IRQ_EDGE 0x34 +#define MMU_REG_IRQ_CLEAR 0x38 +#define MMU_REG_IRQ_CAUSE 0x3c +#define MMU_REG_CG_CTRL_BITS 0x40 +#define MMU_REG_RD_FIFOS_STATUS 0x44 +#define MMU_REG_WR_FIFOS_STATUS 0x48 +#define MMU_REG_COMMON_FIFOS_STATUS 0x4c +#define MMU_REG_FSM_STATUS 0x50 + +#define ZLX_REG_AXI_POOL 0x0 +#define ZLX_REG_EN 0x20 +#define ZLX_REG_CONF 0x24 +#define ZLX_REG_CG_CTRL 0x900 +#define ZLX_REG_FORCE_BYPASS 0x904 + +struct ipu7_mmu_info { + struct device *dev; + + u32 *l1_pt; + u32 l1_pt_dma; + u32 **l2_pts; + + u32 *dummy_l2_pt; + u32 dummy_l2_pteval; + void *dummy_page; + u32 dummy_page_pteval; + + dma_addr_t aperture_start; + dma_addr_t aperture_end; + unsigned long pgsize_bitmap; + + spinlock_t lock; /* Serialize access to users */ + struct ipu7_dma_mapping *dmap; +}; + +struct ipu7_mmu { + struct list_head node; + + struct ipu7_mmu_hw *mmu_hw; + unsigned int nr_mmus; + unsigned int mmid; + + phys_addr_t pgtbl; + struct device *dev; + + struct ipu7_dma_mapping *dmap; + struct list_head vma_list; + + struct page *trash_page; + dma_addr_t pci_trash_page; /* IOVA from PCI DMA services (parent) */ + dma_addr_t iova_trash_page; /* IOVA for IPU child nodes to use */ + + bool ready; + spinlock_t ready_lock; /* Serialize access to bool ready */ + + void (*tlb_invalidate)(struct ipu7_mmu *mmu); +}; + +struct ipu7_mmu *ipu7_mmu_init(struct device *dev, + void __iomem *base, int mmid, + const struct ipu7_hw_variants *hw); +void ipu7_mmu_cleanup(struct ipu7_mmu *mmu); +int ipu7_mmu_hw_init(struct ipu7_mmu *mmu); +void ipu7_mmu_hw_cleanup(struct ipu7_mmu *mmu); +int ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, + phys_addr_t paddr, size_t size); +void ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova, + size_t size); +phys_addr_t ipu7_mmu_iova_to_phys(struct ipu7_mmu_info *mmu_info, + dma_addr_t iova); +#endif -- cgit v1.2.3 From cc5de519299d35fe34905303186abd8cac252163 Mon Sep 17 00:00:00 2001 From: Bingbu Cao Date: Thu, 29 May 2025 12:13:19 +0800 Subject: media: staging/ipu7: add firmware parse, syscom interface and boot IPU7 irmware is generated and released as signed Code Partition Directory (CPD) format file which is aligned with the SPI flash code partition definition. The driver parses the CPD based on the layout. Syscom is an inter-process(or) communication mechanism between an IPU and host. Syscom uses message queues for message exchange between IPU and host. Each message queue has its consumer and producer, host queue messages to firmware as the producer and then firmware to dequeue the messages as consumer and vice versa. IPU and host use shared registers or memory to reside the read and write indices which are updated by consumer and producer. IPU7 firmware defined its boot sequence, driver setup the boot configuration and program the boot parameters to start and run the firmware. Signed-off-by: Bingbu Cao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/ipu7/ipu7-boot.c | 430 +++++++++++++++++++++++++++++++ drivers/staging/media/ipu7/ipu7-boot.h | 25 ++ drivers/staging/media/ipu7/ipu7-cpd.c | 276 ++++++++++++++++++++ drivers/staging/media/ipu7/ipu7-cpd.h | 16 ++ drivers/staging/media/ipu7/ipu7-syscom.c | 78 ++++++ drivers/staging/media/ipu7/ipu7-syscom.h | 35 +++ 6 files changed, 860 insertions(+) create mode 100644 drivers/staging/media/ipu7/ipu7-boot.c create mode 100644 drivers/staging/media/ipu7/ipu7-boot.h create mode 100644 drivers/staging/media/ipu7/ipu7-cpd.c create mode 100644 drivers/staging/media/ipu7/ipu7-cpd.h create mode 100644 drivers/staging/media/ipu7/ipu7-syscom.c create mode 100644 drivers/staging/media/ipu7/ipu7-syscom.h (limited to 'drivers/staging') diff --git a/drivers/staging/media/ipu7/ipu7-boot.c b/drivers/staging/media/ipu7/ipu7-boot.c new file mode 100644 index 000000000000..d7901ff78b38 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-boot.c @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 - 2025 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "abi/ipu7_fw_boot_abi.h" + +#include "ipu7.h" +#include "ipu7-boot.h" +#include "ipu7-bus.h" +#include "ipu7-buttress-regs.h" +#include "ipu7-dma.h" +#include "ipu7-platform-regs.h" +#include "ipu7-syscom.h" + +#define IPU_FW_START_STOP_TIMEOUT 2000 +#define IPU_BOOT_CELL_RESET_TIMEOUT (2 * USEC_PER_SEC) +#define BOOT_STATE_IS_CRITICAL(s) IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(s) +#define BOOT_STATE_IS_READY(s) ((s) == IA_GOFO_FW_BOOT_STATE_READY) +#define BOOT_STATE_IS_INACTIVE(s) ((s) == IA_GOFO_FW_BOOT_STATE_INACTIVE) + +struct ipu7_boot_context { + u32 base; + u32 dmem_address; + u32 status_ctrl_reg; + u32 fw_start_address_reg; + u32 fw_code_base_reg; +}; + +static const struct ipu7_boot_context contexts[IPU_SUBSYS_NUM] = { + { + /* ISYS */ + .dmem_address = IPU_ISYS_DMEM_OFFSET, + .status_ctrl_reg = BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS, + .fw_start_address_reg = BUTTRESS_REG_DRV_IS_UCX_START_ADDR, + .fw_code_base_reg = IS_UC_CTRL_BASE + }, + { + /* PSYS */ + .dmem_address = IPU_PSYS_DMEM_OFFSET, + .status_ctrl_reg = BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS, + .fw_start_address_reg = BUTTRESS_REG_DRV_PS_UCX_START_ADDR, + .fw_code_base_reg = PS_UC_CTRL_BASE + } +}; + +static u32 get_fw_boot_reg_addr(const struct ipu7_bus_device *adev, + enum ia_gofo_buttress_reg_id reg) +{ + u32 base = (adev->subsys == IPU_IS) ? 0U : (u32)IA_GOFO_FW_BOOT_ID_MAX; + + return BUTTRESS_FW_BOOT_PARAMS_ENTRY(base + (u32)reg); +} + +static void write_fw_boot_param(const struct ipu7_bus_device *adev, + enum ia_gofo_buttress_reg_id reg, + u32 val) +{ + void __iomem *base = adev->isp->base; + + dev_dbg(&adev->auxdev.dev, + "write boot param reg: %d addr: %x val: 0x%x\n", + reg, get_fw_boot_reg_addr(adev, reg), val); + writel(val, base + get_fw_boot_reg_addr(adev, reg)); +} + +static u32 read_fw_boot_param(const struct ipu7_bus_device *adev, + enum ia_gofo_buttress_reg_id reg) +{ + void __iomem *base = adev->isp->base; + + return readl(base + get_fw_boot_reg_addr(adev, reg)); +} + +static int ipu7_boot_cell_reset(const struct ipu7_bus_device *adev) +{ + const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; + const struct device *dev = &adev->auxdev.dev; + u32 ucx_ctrl_status = ctx->status_ctrl_reg; + u32 timeout = IPU_BOOT_CELL_RESET_TIMEOUT; + void __iomem *base = adev->isp->base; + u32 val, val2; + int ret; + + dev_dbg(dev, "cell enter reset...\n"); + val = readl(base + ucx_ctrl_status); + dev_dbg(dev, "cell_ctrl_reg addr = 0x%x, val = 0x%x\n", + ucx_ctrl_status, val); + + dev_dbg(dev, "force cell reset...\n"); + val |= UCX_CTL_RESET; + val &= ~UCX_CTL_RUN; + + dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", + ucx_ctrl_status, val); + writel(val, base + ucx_ctrl_status); + + ret = readl_poll_timeout(base + ucx_ctrl_status, val2, + (val2 & 0x3U) == (val & 0x3U), 100, timeout); + if (ret) { + dev_err(dev, "cell enter reset timeout. status: 0x%x\n", val2); + return -ETIMEDOUT; + } + + dev_dbg(dev, "cell exit reset...\n"); + val = readl(base + ucx_ctrl_status); + WARN((!(val & UCX_CTL_RESET) || val & UCX_CTL_RUN), + "cell status 0x%x", val); + + val &= ~(UCX_CTL_RESET | UCX_CTL_RUN); + dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", + ucx_ctrl_status, val); + writel(val, base + ucx_ctrl_status); + + ret = readl_poll_timeout(base + ucx_ctrl_status, val2, + (val2 & 0x3U) == (val & 0x3U), 100, timeout); + if (ret) { + dev_err(dev, "cell exit reset timeout. status: 0x%x\n", val2); + return -ETIMEDOUT; + } + + return 0; +} + +static void ipu7_boot_cell_start(const struct ipu7_bus_device *adev) +{ + const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; + void __iomem *base = adev->isp->base; + const struct device *dev = &adev->auxdev.dev; + u32 val; + + dev_dbg(dev, "starting cell...\n"); + val = readl(base + ctx->status_ctrl_reg); + WARN_ON(val & (UCX_CTL_RESET | UCX_CTL_RUN)); + + val &= ~UCX_CTL_RESET; + val |= UCX_CTL_RUN; + dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", + ctx->status_ctrl_reg, val); + writel(val, base + ctx->status_ctrl_reg); +} + +static void ipu7_boot_cell_stop(const struct ipu7_bus_device *adev) +{ + const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; + void __iomem *base = adev->isp->base; + const struct device *dev = &adev->auxdev.dev; + u32 val; + + dev_dbg(dev, "stopping cell...\n"); + + val = readl(base + ctx->status_ctrl_reg); + val &= ~UCX_CTL_RUN; + dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", + ctx->status_ctrl_reg, val); + writel(val, base + ctx->status_ctrl_reg); + + /* Wait for uC transactions complete */ + usleep_range(10, 20); + + val = readl(base + ctx->status_ctrl_reg); + val |= UCX_CTL_RESET; + dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", + ctx->status_ctrl_reg, val); + writel(val, base + ctx->status_ctrl_reg); +} + +static int ipu7_boot_cell_init(const struct ipu7_bus_device *adev) +{ + const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; + void __iomem *base = adev->isp->base; + + dev_dbg(&adev->auxdev.dev, "write fw_start_address_reg(0x%x) to 0x%x\n", + ctx->fw_start_address_reg, adev->fw_entry); + writel(adev->fw_entry, base + ctx->fw_start_address_reg); + + return ipu7_boot_cell_reset(adev); +} + +static void init_boot_config(struct ia_gofo_boot_config *boot_config, + u32 length, u8 major) +{ + /* syscom version, new syscom2 version */ + boot_config->length = length; + boot_config->config_version.major = 1U; + boot_config->config_version.minor = 0U; + boot_config->config_version.subminor = 0U; + boot_config->config_version.patch = 0U; + + /* msg version for task interface */ + boot_config->client_version_support.num_versions = 1U; + boot_config->client_version_support.versions[0].major = major; + boot_config->client_version_support.versions[0].minor = 0U; + boot_config->client_version_support.versions[0].subminor = 0U; + boot_config->client_version_support.versions[0].patch = 0U; +} + +int ipu7_boot_init_boot_config(struct ipu7_bus_device *adev, + struct syscom_queue_config *qconfigs, + int num_queues, u32 uc_freq, + dma_addr_t subsys_config, u8 major) +{ + u32 total_queue_size_aligned = 0; + struct ipu7_syscom_context *syscom = adev->syscom; + struct ia_gofo_boot_config *boot_config; + struct syscom_queue_params_config *cfgs; + struct device *dev = &adev->auxdev.dev; + struct syscom_config_s *syscfg; + dma_addr_t queue_mem_dma_ptr; + void *queue_mem_ptr; + unsigned int i; + + dev_dbg(dev, "boot config queues_nr: %d freq: %u sys_conf: 0x%pad\n", + num_queues, uc_freq, &subsys_config); + /* Allocate boot config. */ + adev->boot_config_size = + sizeof(*cfgs) * num_queues + sizeof(*boot_config); + adev->boot_config = ipu7_dma_alloc(adev, adev->boot_config_size, + &adev->boot_config_dma_addr, + GFP_KERNEL, 0); + if (!adev->boot_config) { + dev_err(dev, "Failed to allocate boot config.\n"); + return -ENOMEM; + } + + boot_config = adev->boot_config; + memset(boot_config, 0, sizeof(struct ia_gofo_boot_config)); + init_boot_config(boot_config, adev->boot_config_size, major); + boot_config->subsys_config = subsys_config; + + boot_config->uc_tile_frequency = uc_freq; + boot_config->uc_tile_frequency_units = + IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_MHZ; + boot_config->syscom_context_config.max_output_queues = + syscom->num_output_queues; + boot_config->syscom_context_config.max_input_queues = + syscom->num_input_queues; + + ipu7_dma_sync_single(adev, adev->boot_config_dma_addr, + adev->boot_config_size); + + for (i = 0; i < num_queues; i++) { + u32 queue_size = qconfigs[i].max_capacity * + qconfigs[i].token_size_in_bytes; + + queue_size = ALIGN(queue_size, 64U); + total_queue_size_aligned += queue_size; + qconfigs[i].queue_size = queue_size; + } + + /* Allocate queue memory */ + syscom->queue_mem = ipu7_dma_alloc(adev, total_queue_size_aligned, + &syscom->queue_mem_dma_addr, + GFP_KERNEL, 0); + if (!syscom->queue_mem) { + dev_err(dev, "Failed to allocate queue memory.\n"); + return -ENOMEM; + } + syscom->queue_mem_size = total_queue_size_aligned; + + syscfg = &boot_config->syscom_context_config; + cfgs = ipu7_syscom_get_queue_config(syscfg); + queue_mem_ptr = syscom->queue_mem; + queue_mem_dma_ptr = syscom->queue_mem_dma_addr; + for (i = 0; i < num_queues; i++) { + cfgs[i].token_array_mem = queue_mem_dma_ptr; + cfgs[i].max_capacity = qconfigs[i].max_capacity; + cfgs[i].token_size_in_bytes = qconfigs[i].token_size_in_bytes; + qconfigs[i].token_array_mem = queue_mem_ptr; + queue_mem_dma_ptr += qconfigs[i].queue_size; + queue_mem_ptr += qconfigs[i].queue_size; + } + + ipu7_dma_sync_single(adev, syscom->queue_mem_dma_addr, + total_queue_size_aligned); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ipu7_boot_init_boot_config, "INTEL_IPU7"); + +void ipu7_boot_release_boot_config(struct ipu7_bus_device *adev) +{ + struct ipu7_syscom_context *syscom = adev->syscom; + + if (syscom->queue_mem) { + ipu7_dma_free(adev, syscom->queue_mem_size, + syscom->queue_mem, + syscom->queue_mem_dma_addr, 0); + syscom->queue_mem = NULL; + syscom->queue_mem_dma_addr = 0; + } + + if (adev->boot_config) { + ipu7_dma_free(adev, adev->boot_config_size, + adev->boot_config, + adev->boot_config_dma_addr, 0); + adev->boot_config = NULL; + adev->boot_config_dma_addr = 0; + } +} +EXPORT_SYMBOL_NS_GPL(ipu7_boot_release_boot_config, "INTEL_IPU7"); + +int ipu7_boot_start_fw(const struct ipu7_bus_device *adev) +{ + const struct device *dev = &adev->auxdev.dev; + u32 timeout = IPU_FW_START_STOP_TIMEOUT; + void __iomem *base = adev->isp->base; + u32 boot_state, last_boot_state; + u32 indices_addr, msg_ver, id; + int ret; + + ret = ipu7_boot_cell_init(adev); + if (ret) + return ret; + + dev_dbg(dev, "start booting fw...\n"); + /* store "uninit" state to syscom/boot state reg */ + write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID, + IA_GOFO_FW_BOOT_STATE_UNINIT); + /* + * Set registers to zero + * (not strictly required, but recommended for diagnostics) + */ + write_fw_boot_param(adev, + IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID, 0); + write_fw_boot_param(adev, IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID, 0); + /* store firmware configuration address */ + write_fw_boot_param(adev, IA_GOFO_FW_BOOT_CONFIG_ID, + adev->boot_config_dma_addr); + + /* Kick uC, then wait for boot complete */ + ipu7_boot_cell_start(adev); + + last_boot_state = IA_GOFO_FW_BOOT_STATE_UNINIT; + while (timeout--) { + boot_state = read_fw_boot_param(adev, + IA_GOFO_FW_BOOT_STATE_ID); + if (boot_state != last_boot_state) { + dev_dbg(dev, "boot state changed from 0x%x to 0x%x\n", + last_boot_state, boot_state); + last_boot_state = boot_state; + } + if (BOOT_STATE_IS_CRITICAL(boot_state) || + BOOT_STATE_IS_READY(boot_state)) + break; + usleep_range(1000, 1200); + } + + if (BOOT_STATE_IS_CRITICAL(boot_state)) { + ipu7_dump_fw_error_log(adev); + dev_err(dev, "critical boot state error 0x%x\n", boot_state); + return -EINVAL; + } else if (!BOOT_STATE_IS_READY(boot_state)) { + dev_err(dev, "fw boot timeout. state: 0x%x\n", boot_state); + return -ETIMEDOUT; + } + dev_dbg(dev, "fw boot done.\n"); + + /* Get FW syscom queue indices addr */ + id = IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID; + indices_addr = read_fw_boot_param(adev, id); + adev->syscom->queue_indices = base + indices_addr; + dev_dbg(dev, "fw queue indices offset is 0x%x\n", indices_addr); + + /* Get message version. */ + msg_ver = read_fw_boot_param(adev, + IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID); + dev_dbg(dev, "ipu message version is 0x%08x\n", msg_ver); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ipu7_boot_start_fw, "INTEL_IPU7"); + +int ipu7_boot_stop_fw(const struct ipu7_bus_device *adev) +{ + const struct device *dev = &adev->auxdev.dev; + u32 timeout = IPU_FW_START_STOP_TIMEOUT; + u32 boot_state; + + boot_state = read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID); + if (BOOT_STATE_IS_CRITICAL(boot_state) || + !BOOT_STATE_IS_READY(boot_state)) { + dev_err(dev, "fw not ready for shutdown, state 0x%x\n", + boot_state); + return -EBUSY; + } + + /* Issue shutdown to start shutdown process */ + dev_dbg(dev, "stopping fw...\n"); + write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID, + IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD); + while (timeout--) { + boot_state = read_fw_boot_param(adev, + IA_GOFO_FW_BOOT_STATE_ID); + if (BOOT_STATE_IS_CRITICAL(boot_state) || + BOOT_STATE_IS_INACTIVE(boot_state)) + break; + usleep_range(1000, 1200); + } + + if (BOOT_STATE_IS_CRITICAL(boot_state)) { + ipu7_dump_fw_error_log(adev); + dev_err(dev, "critical boot state error 0x%x\n", boot_state); + return -EINVAL; + } else if (!BOOT_STATE_IS_INACTIVE(boot_state)) { + dev_err(dev, "stop fw timeout. state: 0x%x\n", boot_state); + return -ETIMEDOUT; + } + + ipu7_boot_cell_stop(adev); + dev_dbg(dev, "stop fw done.\n"); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ipu7_boot_stop_fw, "INTEL_IPU7"); + +u32 ipu7_boot_get_boot_state(const struct ipu7_bus_device *adev) +{ + return read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID); +} +EXPORT_SYMBOL_NS_GPL(ipu7_boot_get_boot_state, "INTEL_IPU7"); diff --git a/drivers/staging/media/ipu7/ipu7-boot.h b/drivers/staging/media/ipu7/ipu7-boot.h new file mode 100644 index 000000000000..5600be849931 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-boot.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2022 - 2025 Intel Corporation + */ + +#ifndef IPU7_BOOT_H +#define IPU7_BOOT_H + +#include + +struct ipu7_bus_device; +struct syscom_queue_config; + +#define FW_QUEUE_CONFIG_SIZE(num_queues) \ + (sizeof(struct syscom_queue_config) * (num_queues)) + +int ipu7_boot_init_boot_config(struct ipu7_bus_device *adev, + struct syscom_queue_config *qconfigs, + int num_queues, u32 uc_freq, + dma_addr_t subsys_config, u8 major); +void ipu7_boot_release_boot_config(struct ipu7_bus_device *adev); +int ipu7_boot_start_fw(const struct ipu7_bus_device *adev); +int ipu7_boot_stop_fw(const struct ipu7_bus_device *adev); +u32 ipu7_boot_get_boot_state(const struct ipu7_bus_device *adev); +#endif diff --git a/drivers/staging/media/ipu7/ipu7-cpd.c b/drivers/staging/media/ipu7/ipu7-cpd.c new file mode 100644 index 000000000000..4f49fb57eae4 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-cpd.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2015 - 2025 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ipu7.h" +#include "ipu7-cpd.h" + +/* $CPD */ +#define CPD_HDR_MARK 0x44504324 + +/* Maximum size is 4K DWORDs or 16KB */ +#define MAX_MANIFEST_SIZE (SZ_4K * sizeof(u32)) + +#define CPD_MANIFEST_IDX 0 +#define CPD_BINARY_START_IDX 1U +#define CPD_METADATA_START_IDX 2U +#define CPD_BINARY_NUM 2U /* ISYS + PSYS */ +/* + * Entries include: + * 1 manifest entry. + * 1 metadata entry for each sub system(ISYS and PSYS). + * 1 binary entry for each sub system(ISYS and PSYS). + */ +#define CPD_ENTRY_NUM (CPD_BINARY_NUM * 2U + 1U) + +#define CPD_METADATA_ATTR 0xa +#define CPD_METADATA_IPL 0x1c +#define ONLINE_METADATA_SIZE 128U +#define ONLINE_METADATA_LINES 6U + +struct ipu7_cpd_hdr { + u32 hdr_mark; + u32 ent_cnt; + u8 hdr_ver; + u8 ent_ver; + u8 hdr_len; + u8 rsvd; + u8 partition_name[4]; + u32 crc32; +} __packed; + +struct ipu7_cpd_ent { + u8 name[12]; + u32 offset; + u32 len; + u8 rsvd[4]; +} __packed; + +struct ipu7_cpd_metadata_hdr { + u32 type; + u32 len; +} __packed; + +struct ipu7_cpd_metadata_attr { + struct ipu7_cpd_metadata_hdr hdr; + u8 compression_type; + u8 encryption_type; + u8 rsvd[2]; + u32 uncompressed_size; + u32 compressed_size; + u32 module_id; + u8 hash[48]; +} __packed; + +struct ipu7_cpd_metadata_ipl { + struct ipu7_cpd_metadata_hdr hdr; + u32 param[4]; + u8 rsvd[8]; +} __packed; + +struct ipu7_cpd_metadata { + struct ipu7_cpd_metadata_attr attr; + struct ipu7_cpd_metadata_ipl ipl; +} __packed; + +static inline struct ipu7_cpd_ent *ipu7_cpd_get_entry(const void *cpd, int idx) +{ + const struct ipu7_cpd_hdr *cpd_hdr = cpd; + + return ((struct ipu7_cpd_ent *)((u8 *)cpd + cpd_hdr->hdr_len)) + idx; +} + +#define ipu7_cpd_get_manifest(cpd) ipu7_cpd_get_entry(cpd, 0) + +static struct ipu7_cpd_metadata *ipu7_cpd_get_metadata(const void *cpd, int idx) +{ + struct ipu7_cpd_ent *cpd_ent = + ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2); + + return (struct ipu7_cpd_metadata *)((u8 *)cpd + cpd_ent->offset); +} + +static int ipu7_cpd_validate_cpd(struct ipu7_device *isp, + const void *cpd, unsigned long data_size) +{ + const struct ipu7_cpd_hdr *cpd_hdr = cpd; + struct device *dev = &isp->pdev->dev; + struct ipu7_cpd_ent *ent; + unsigned int i; + u8 len; + + len = cpd_hdr->hdr_len; + + /* Ensure cpd hdr is within moduledata */ + if (data_size < len) { + dev_err(dev, "Invalid CPD moduledata size\n"); + return -EINVAL; + } + + /* Check for CPD file marker */ + if (cpd_hdr->hdr_mark != CPD_HDR_MARK) { + dev_err(dev, "Invalid CPD header marker\n"); + return -EINVAL; + } + + /* Sanity check for CPD entry header */ + if (cpd_hdr->ent_cnt != CPD_ENTRY_NUM) { + dev_err(dev, "Invalid CPD entry number %d\n", + cpd_hdr->ent_cnt); + return -EINVAL; + } + if ((data_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) { + dev_err(dev, "Invalid CPD entry headers\n"); + return -EINVAL; + } + + /* Ensure that all entries are within moduledata */ + ent = (struct ipu7_cpd_ent *)(((u8 *)cpd_hdr) + len); + for (i = 0; i < cpd_hdr->ent_cnt; i++) { + if (data_size < ent->offset || + data_size - ent->offset < ent->len) { + dev_err(dev, "Invalid CPD entry %d\n", i); + return -EINVAL; + } + ent++; + } + + return 0; +} + +static int ipu7_cpd_validate_metadata(struct ipu7_device *isp, + const void *cpd, int idx) +{ + const struct ipu7_cpd_ent *cpd_ent = + ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2); + const struct ipu7_cpd_metadata *metadata = + ipu7_cpd_get_metadata(cpd, idx); + struct device *dev = &isp->pdev->dev; + + /* Sanity check for metadata size */ + if (cpd_ent->len != sizeof(struct ipu7_cpd_metadata)) { + dev_err(dev, "Invalid metadata size\n"); + return -EINVAL; + } + + /* Validate type and length of metadata sections */ + if (metadata->attr.hdr.type != CPD_METADATA_ATTR) { + dev_err(dev, "Invalid metadata attr type (%d)\n", + metadata->attr.hdr.type); + return -EINVAL; + } + if (metadata->attr.hdr.len != sizeof(struct ipu7_cpd_metadata_attr)) { + dev_err(dev, "Invalid metadata attr size (%d)\n", + metadata->attr.hdr.len); + return -EINVAL; + } + if (metadata->ipl.hdr.type != CPD_METADATA_IPL) { + dev_err(dev, "Invalid metadata ipl type (%d)\n", + metadata->ipl.hdr.type); + return -EINVAL; + } + if (metadata->ipl.hdr.len != sizeof(struct ipu7_cpd_metadata_ipl)) { + dev_err(dev, "Invalid metadata ipl size (%d)\n", + metadata->ipl.hdr.len); + return -EINVAL; + } + + return 0; +} + +int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp, const void *cpd_file, + unsigned long cpd_file_size) +{ + struct device *dev = &isp->pdev->dev; + struct ipu7_cpd_ent *ent; + unsigned int i; + int ret; + char *buf; + + ret = ipu7_cpd_validate_cpd(isp, cpd_file, cpd_file_size); + if (ret) { + dev_err(dev, "Invalid CPD in file\n"); + return -EINVAL; + } + + /* Sanity check for manifest size */ + ent = ipu7_cpd_get_manifest(cpd_file); + if (ent->len > MAX_MANIFEST_SIZE) { + dev_err(dev, "Invalid manifest size\n"); + return -EINVAL; + } + + /* Validate metadata */ + for (i = 0; i < CPD_BINARY_NUM; i++) { + ret = ipu7_cpd_validate_metadata(isp, cpd_file, i); + if (ret) { + dev_err(dev, "Invalid metadata%d\n", i); + return ret; + } + } + + /* Get fw binary version. */ + buf = kmalloc(ONLINE_METADATA_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + for (i = 0; i < CPD_BINARY_NUM; i++) { + char *lines[ONLINE_METADATA_LINES]; + char *info = buf; + unsigned int l; + + ent = ipu7_cpd_get_entry(cpd_file, + CPD_BINARY_START_IDX + i * 2U); + memcpy(info, (u8 *)cpd_file + ent->offset + ent->len - + ONLINE_METADATA_SIZE, ONLINE_METADATA_SIZE); + for (l = 0; l < ONLINE_METADATA_LINES; l++) { + lines[l] = strsep((char **)&info, "\n"); + if (!lines[l]) + break; + } + if (l < ONLINE_METADATA_LINES) { + dev_err(dev, "Failed to parse fw binary%d info.\n", i); + continue; + } + dev_info(dev, "FW binary%d info:\n", i); + dev_info(dev, "Name: %s\n", lines[1]); + dev_info(dev, "Version: %s\n", lines[2]); + dev_info(dev, "Timestamp: %s\n", lines[3]); + dev_info(dev, "Commit: %s\n", lines[4]); + } + kfree(buf); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(ipu7_cpd_validate_cpd_file, "INTEL_IPU7"); + +int ipu7_cpd_copy_binary(const void *cpd, const char *name, + void *code_region, u32 *entry) +{ + unsigned int i; + + for (i = 0; i < CPD_BINARY_NUM; i++) { + const struct ipu7_cpd_ent *binary = + ipu7_cpd_get_entry(cpd, CPD_BINARY_START_IDX + i * 2U); + const struct ipu7_cpd_metadata *metadata = + ipu7_cpd_get_metadata(cpd, i); + + if (!strncmp(binary->name, name, sizeof(binary->name))) { + memcpy(code_region + metadata->ipl.param[0], + cpd + binary->offset, binary->len); + *entry = metadata->ipl.param[2]; + return 0; + } + } + + return -ENOENT; +} +EXPORT_SYMBOL_NS_GPL(ipu7_cpd_copy_binary, "INTEL_IPU7"); diff --git a/drivers/staging/media/ipu7/ipu7-cpd.h b/drivers/staging/media/ipu7/ipu7-cpd.h new file mode 100644 index 000000000000..b4178848c6b9 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-cpd.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2015 - 2025 Intel Corporation + */ + +#ifndef IPU7_CPD_H +#define IPU7_CPD_H + +struct ipu7_device; + +int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp, + const void *cpd_file, + unsigned long cpd_file_size); +int ipu7_cpd_copy_binary(const void *cpd, const char *name, + void *code_region, u32 *entry); +#endif /* IPU7_CPD_H */ diff --git a/drivers/staging/media/ipu7/ipu7-syscom.c b/drivers/staging/media/ipu7/ipu7-syscom.c new file mode 100644 index 000000000000..3f9f9c5c3cce --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-syscom.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#include +#include + +#include "abi/ipu7_fw_syscom_abi.h" + +#include "ipu7.h" +#include "ipu7-syscom.h" + +static void __iomem *ipu7_syscom_get_indices(struct ipu7_syscom_context *ctx, + u32 q) +{ + return ctx->queue_indices + (q * sizeof(struct syscom_queue_indices_s)); +} + +void *ipu7_syscom_get_token(struct ipu7_syscom_context *ctx, int q) +{ + struct syscom_queue_config *queue_params = &ctx->queue_configs[q]; + void __iomem *queue_indices = ipu7_syscom_get_indices(ctx, q); + u32 write_index = readl(queue_indices + + offsetof(struct syscom_queue_indices_s, + write_index)); + u32 read_index = readl(queue_indices + + offsetof(struct syscom_queue_indices_s, + read_index)); + void *token = NULL; + + if (q < ctx->num_output_queues) { + /* Output queue */ + bool empty = (write_index == read_index); + + if (!empty) + token = queue_params->token_array_mem + + read_index * + queue_params->token_size_in_bytes; + } else { + /* Input queue */ + bool full = (read_index == ((write_index + 1U) % + (u32)queue_params->max_capacity)); + + if (!full) + token = queue_params->token_array_mem + + write_index * queue_params->token_size_in_bytes; + } + return token; +} +EXPORT_SYMBOL_NS_GPL(ipu7_syscom_get_token, "INTEL_IPU7"); + +void ipu7_syscom_put_token(struct ipu7_syscom_context *ctx, int q) +{ + struct syscom_queue_config *queue_params = &ctx->queue_configs[q]; + void __iomem *queue_indices = ipu7_syscom_get_indices(ctx, q); + u32 offset, index; + + if (q < ctx->num_output_queues) + /* Output queue */ + offset = offsetof(struct syscom_queue_indices_s, read_index); + + else + /* Input queue */ + offset = offsetof(struct syscom_queue_indices_s, write_index); + + index = readl(queue_indices + offset); + writel((index + 1U) % queue_params->max_capacity, + queue_indices + offset); +} +EXPORT_SYMBOL_NS_GPL(ipu7_syscom_put_token, "INTEL_IPU7"); + +struct syscom_queue_params_config * +ipu7_syscom_get_queue_config(struct syscom_config_s *config) +{ + return (struct syscom_queue_params_config *)(&config[1]); +} +EXPORT_SYMBOL_NS_GPL(ipu7_syscom_get_queue_config, "INTEL_IPU7"); diff --git a/drivers/staging/media/ipu7/ipu7-syscom.h b/drivers/staging/media/ipu7/ipu7-syscom.h new file mode 100644 index 000000000000..e1fbe3b7914e --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-syscom.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#ifndef IPU7_SYSCOM_H +#define IPU7_SYSCOM_H + +#include + +struct syscom_config_s; +struct syscom_queue_params_config; + +struct syscom_queue_config { + void *token_array_mem; + u32 queue_size; + u16 token_size_in_bytes; + u16 max_capacity; +}; + +struct ipu7_syscom_context { + u16 num_input_queues; + u16 num_output_queues; + struct syscom_queue_config *queue_configs; + void __iomem *queue_indices; + dma_addr_t queue_mem_dma_addr; + void *queue_mem; + u32 queue_mem_size; +}; + +void ipu7_syscom_put_token(struct ipu7_syscom_context *ctx, int q); +void *ipu7_syscom_get_token(struct ipu7_syscom_context *ctx, int q); +struct syscom_queue_params_config * +ipu7_syscom_get_queue_config(struct syscom_config_s *config); +#endif -- cgit v1.2.3 From 2788a049ba59fb2f90cd0d8658a8a01ce9b2797b Mon Sep 17 00:00:00 2001 From: Bingbu Cao Date: Thu, 29 May 2025 12:13:20 +0800 Subject: media: staging/ipu7: add IPU7 firmware ABI headers IPU7 firmware defines the ABIs between firmware and software, this patch adds a series of ABI headers. Signed-off-by: Bingbu Cao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h | 163 ++++++++ .../staging/media/ipu7/abi/ipu7_fw_common_abi.h | 175 ++++++++ .../staging/media/ipu7/abi/ipu7_fw_config_abi.h | 19 + .../media/ipu7/abi/ipu7_fw_insys_config_abi.h | 19 + drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h | 412 ++++++++++++++++++ drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h | 465 +++++++++++++++++++++ .../media/ipu7/abi/ipu7_fw_psys_config_abi.h | 24 ++ .../staging/media/ipu7/abi/ipu7_fw_syscom_abi.h | 49 +++ 8 files changed, 1326 insertions(+) create mode 100644 drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h create mode 100644 drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h create mode 100644 drivers/staging/media/ipu7/abi/ipu7_fw_config_abi.h create mode 100644 drivers/staging/media/ipu7/abi/ipu7_fw_insys_config_abi.h create mode 100644 drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h create mode 100644 drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h create mode 100644 drivers/staging/media/ipu7/abi/ipu7_fw_psys_config_abi.h create mode 100644 drivers/staging/media/ipu7/abi/ipu7_fw_syscom_abi.h (limited to 'drivers/staging') diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h new file mode 100644 index 000000000000..a1519c4fe661 --- /dev/null +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 - 2025 Intel Corporation + */ + +#ifndef IPU7_FW_BOOT_ABI_H +#define IPU7_FW_BOOT_ABI_H + +#include "ipu7_fw_common_abi.h" +#include "ipu7_fw_syscom_abi.h" + +#define IA_GOFO_FWLOG_SEVERITY_CRIT (0U) +#define IA_GOFO_FWLOG_SEVERITY_ERROR (1U) +#define IA_GOFO_FWLOG_SEVERITY_WARNING (2U) +#define IA_GOFO_FWLOG_SEVERITY_INFO (3U) +#define IA_GOFO_FWLOG_SEVERITY_DEBUG (4U) +#define IA_GOFO_FWLOG_SEVERITY_VERBOSE (5U) +#define IA_GOFO_FWLOG_MAX_LOGGER_SOURCES (64U) + +#define LOGGER_CONFIG_CHANNEL_ENABLE_HWPRINTF_BITMASK BIT(0) +#define LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK BIT(1) +#define LOGGER_CONFIG_CHANNEL_ENABLE_ALL_BITMASK \ + (LOGGER_CONFIG_CHANNEL_ENABLE_HWPRINTF_BITMASK | \ + LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK) + +struct ia_gofo_logger_config { + u8 use_source_severity; + u8 source_severity[IA_GOFO_FWLOG_MAX_LOGGER_SOURCES]; + u8 use_channels_enable_bitmask; + u8 channels_enable_bitmask; + u8 padding[1]; + ia_gofo_addr_t hw_printf_buffer_base_addr; + u32 hw_printf_buffer_size_bytes; +}; + +#pragma pack(push, 1) + +#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP \ + ((u32)IA_GOFO_FW_BOOT_ID_MAX) +#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_IS_OFFSET (0U) +#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_PS_OFFSET \ + ((IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_IS_OFFSET) + \ + (u32)(IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP)) +#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_PRIMARY_OFFSET (0U) +#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET (0x3000U / 4U) +#define IA_GOFO_HKR_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ + (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 2U) +#define IA_GOFO_HKR_HIF_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ + (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP) +#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX \ + (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 4U) +#define IA_GOFO_HKR_IPU_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX \ + (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 4U) + +#define IA_GOFO_BOOT_RESERVED_SIZE (58U) +#define IA_GOFO_BOOT_SECONDARY_RESERVED_SIZE (IA_GOFO_BOOT_RESERVED_SIZE) +#define IA_GOFO_BOOT_SECONDARY_RESERVED_FIELDS \ + (sizeof(ia_gofo_addr_t) + sizeof(ia_gofo_addr_t) + sizeof(u32)) + +enum ia_gofo_buttress_reg_id { + IA_GOFO_FW_BOOT_CONFIG_ID = 0, + IA_GOFO_FW_BOOT_STATE_ID = 1, + IA_GOFO_FW_BOOT_RESERVED1_ID = IA_GOFO_FW_BOOT_STATE_ID, + IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID = 2, + IA_GOFO_FW_BOOT_UNTRUSTED_ADDR_MIN_ID = 3, + IA_GOFO_FW_BOOT_RESERVED0_ID = IA_GOFO_FW_BOOT_UNTRUSTED_ADDR_MIN_ID, + IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID = 4, + IA_GOFO_FW_BOOT_ID_MAX +}; + +enum ia_gofo_boot_uc_tile_frequency_units { + IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_MHZ = 0, + IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_HZ = 1, + IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_N +}; + +#define IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(boot_state) \ + (0xdead0000U == ((boot_state) & 0xffff0000U)) + +struct ia_gofo_boot_config { + u32 length; + struct ia_gofo_version_s config_version; + struct ia_gofo_msg_version_list client_version_support; + ia_gofo_addr_t pkg_dir; + ia_gofo_addr_t subsys_config; + u32 uc_tile_frequency; + u16 checksum; + u8 uc_tile_frequency_units; + u8 padding[1]; + u32 reserved[IA_GOFO_BOOT_RESERVED_SIZE]; + struct syscom_config_s syscom_context_config; +}; + +struct ia_gofo_secondary_boot_config { + u32 length; + struct ia_gofo_version_s config_version; + struct ia_gofo_msg_version_list client_version_support; + u8 reserved1[IA_GOFO_BOOT_SECONDARY_RESERVED_FIELDS]; + u16 checksum; + u8 padding[2]; + u32 reserved2[IA_GOFO_BOOT_SECONDARY_RESERVED_SIZE]; + struct syscom_config_s syscom_context_config; +}; + +#pragma pack(pop) + +#define IA_GOFO_WDT_TIMEOUT_ERR 0xdead0401U +#define IA_GOFO_MEM_FATAL_DME_ERR 0xdead0801U +#define IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR 0xdead0802U +#define IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR 0xdead0803U +#define IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR 0xdead0804U +#define IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR 0xdead0805U +#define IA_GOFO_DOUBLE_EXCEPTION_ERR 0xdead0806U +#define IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR 0xdead1000U +#define IA_GOFO_BIST_DATA_INTEGRITY_FAILURE 0xdead1010U + +enum ia_gofo_boot_state { + IA_GOFO_FW_BOOT_STATE_SECONDARY_BOOT_CONFIG_READY = 0x57a7b000U, + IA_GOFO_FW_BOOT_STATE_UNINIT = 0x57a7e000U, + IA_GOFO_FW_BOOT_STATE_STARTING_0 = 0x57a7d000U, + IA_GOFO_FW_BOOT_STATE_CACHE_INIT_DONE = 0x57a7d010U, + IA_GOFO_FW_BOOT_STATE_MEM_INIT_DONE = 0x57a7d020U, + IA_GOFO_FW_BOOT_STATE_STACK_INIT_DONE = 0x57a7d030U, + IA_GOFO_FW_BOOT_STATE_EARLY_BOOT_DONE = 0x57a7d100U, + IA_GOFO_FW_BOOT_STATE_BOOT_CONFIG_START = 0x57a7d200U, + IA_GOFO_FW_BOOT_STATE_QUEUE_INIT_DONE = 0x57a7d300U, + IA_GOFO_FW_BOOT_STATE_READY = 0x57a7e100U, + IA_GOFO_FW_BOOT_STATE_CRIT_UNSPECIFIED = 0xdead0001U, + IA_GOFO_FW_BOOT_STATE_CRIT_CFG_PTR = 0xdead0101U, + IA_GOFO_FW_BOOT_STATE_CRIT_CFG_VERSION = 0xdead0201U, + IA_GOFO_FW_BOOT_STATE_CRIT_MSG_VERSION = 0xdead0301U, + IA_GOFO_FW_BOOT_STATE_CRIT_WDT_TIMEOUT = IA_GOFO_WDT_TIMEOUT_ERR, + IA_GOFO_FW_BOOT_STATE_WRONG_DATA_SECTION_UNPACKING = 0xdead0501U, + IA_GOFO_FW_BOOT_STATE_WRONG_RO_DATA_SECTION_UNPACKING = 0xdead0601U, + IA_GOFO_FW_BOOT_STATE_INVALID_UNTRUSTED_ADDR_MIN = 0xdead0701U, + IA_GOFO_FW_BOOT_STATE_CRIT_MEM_FATAL_DME = IA_GOFO_MEM_FATAL_DME_ERR, + IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_LOCAL = + IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR, + IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_DIRTY = + IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR, + IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_DTAG = + IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR, + IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_CACHE = + IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR, + IA_GOFO_FW_BOOT_STATE_CRIT_DOUBLE_EXCEPTION = + IA_GOFO_DOUBLE_EXCEPTION_ERR, + IA_GOFO_FW_BOOT_STATE_CRIT_BIST_DMEM_FAULT_DETECTION_ERR = + IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR, + IA_GOFO_FW_BOOT_STATE_CRIT_DATA_INTEGRITY_FAILURE = 0xdead1010U, + IA_GOFO_FW_BOOT_STATE_CRIT_STACK_CHK_FAILURE = 0xdead1011U, + IA_GOFO_FW_BOOT_STATE_CRIT_SYSCOM_CONTEXT_INTEGRITY_FAILURE = + 0xdead1012U, + IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013U, + IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014U, + IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015U, + IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001U, + IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200U, + IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300U, + IA_GOFO_FW_BOOT_HW_CMD_ACK_TIMEOUT = 0x57a7e400U, + IA_GOFO_FW_BOOT_SYSTEM_CYCLES_ERROR = 0x57a7e500U +}; + +#endif diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h new file mode 100644 index 000000000000..7bb6fac585a3 --- /dev/null +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 - 2025 Intel Corporation + */ + +#ifndef IPU7_FW_COMMOM_ABI_H +#define IPU7_FW_COMMOM_ABI_H + +#include + +#pragma pack(push, 1) +typedef u32 ia_gofo_addr_t; + +#define IA_GOFO_ADDR_NULL (0U) + +struct ia_gofo_version_s { + u8 patch; + u8 subminor; + u8 minor; + u8 major; +}; + +#define IA_GOFO_MSG_VERSION_INIT(major_val, minor_val, subminor_val, patch_val)\ + {.major = (major_val), .minor = (minor_val), .subminor = \ + (subminor_val), .patch = (patch_val)} + +#define IA_GOFO_MSG_VERSION_LIST_MAX_ENTRIES (3U) +#define IA_GOFO_MSG_RESERVED_SIZE (3U) + +struct ia_gofo_msg_version_list { + u8 num_versions; + u8 reserved[IA_GOFO_MSG_RESERVED_SIZE]; + struct ia_gofo_version_s versions[IA_GOFO_MSG_VERSION_LIST_MAX_ENTRIES]; +}; + +#pragma pack(pop) + +#define TLV_TYPE_PADDING (0U) + +#pragma pack(push, 1) + +#define IA_GOFO_ABI_BITS_PER_BYTE (8U) + +struct ia_gofo_tlv_header { + u16 tlv_type; + u16 tlv_len32; +}; + +struct ia_gofo_tlv_list { + u16 num_elems; + u16 head_offset; +}; + +#define TLV_ITEM_ALIGNMENT ((u32)sizeof(u32)) +#define TLV_MSG_ALIGNMENT ((u32)sizeof(u64)) +#define TLV_LIST_ALIGNMENT TLV_ITEM_ALIGNMENT +#pragma pack(pop) + +#define IA_GOFO_MODULO(dividend, divisor) ((dividend) % (divisor)) + +#define IA_GOFO_MSG_ERR_MAX_DETAILS (4U) +#define IA_GOFO_MSG_ERR_OK (0U) +#define IA_GOFO_MSG_ERR_UNSPECIFED (0xffffffffU) +#define IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED (0U) +#define IA_GOFO_MSG_ERR_IS_OK(err) (IA_GOFO_MSG_ERR_OK == (err).err_code) + +#pragma pack(push, 1) +struct ia_gofo_msg_err { + u32 err_group; + u32 err_code; + u32 err_detail[IA_GOFO_MSG_ERR_MAX_DETAILS]; +}; + +#pragma pack(pop) + +#define IA_GOFO_MSG_ERR_GROUP_APP_EXT_START (16U) +#define IA_GOFO_MSG_ERR_GROUP_MAX (31U) +#define IA_GOFO_MSG_ERR_GROUP_INTERNAL_START (IA_GOFO_MSG_ERR_GROUP_MAX + 1U) +#define IA_GOFO_MSG_ERR_GROUP_RESERVED IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED +#define IA_GOFO_MSG_ERR_GROUP_GENERAL 1 + +enum ia_gofo_msg_err_general { + IA_GOFO_MSG_ERR_GENERAL_OK = IA_GOFO_MSG_ERR_OK, + IA_GOFO_MSG_ERR_GENERAL_MSG_TOO_SMALL = 1, + IA_GOFO_MSG_ERR_GENERAL_MSG_TOO_LARGE = 2, + IA_GOFO_MSG_ERR_GENERAL_DEVICE_STATE = 3, + IA_GOFO_MSG_ERR_GENERAL_ALIGNMENT = 4, + IA_GOFO_MSG_ERR_GENERAL_INDIRECT_REF_PTR_INVALID = 5, + IA_GOFO_MSG_ERR_GENERAL_INVALID_MSG_TYPE = 6, + IA_GOFO_MSG_ERR_GENERAL_SYSCOM_FAIL = 7, + IA_GOFO_MSG_ERR_GENERAL_N +}; + +#pragma pack(push, 1) +#define IA_GOFO_MSG_TYPE_RESERVED 0 +#define IA_GOFO_MSG_TYPE_INDIRECT 1 +#define IA_GOFO_MSG_TYPE_LOG 2 +#define IA_GOFO_MSG_TYPE_GENERAL_ERR 3 + +struct ia_gofo_msg_header { + struct ia_gofo_tlv_header tlv_header; + struct ia_gofo_tlv_list msg_options; + u64 user_token; +}; + +struct ia_gofo_msg_header_ack { + struct ia_gofo_msg_header header; + struct ia_gofo_msg_err err; + +}; + +struct ia_gofo_msg_general_err { + struct ia_gofo_msg_header_ack header; +}; + +#pragma pack(pop) + +#pragma pack(push, 1) +enum ia_gofo_msg_link_streaming_mode { + IA_GOFO_MSG_LINK_STREAMING_MODE_SOFF = 0, + IA_GOFO_MSG_LINK_STREAMING_MODE_DOFF = 1, + IA_GOFO_MSG_LINK_STREAMING_MODE_BCLM = 2, + IA_GOFO_MSG_LINK_STREAMING_MODE_BCSM_FIX = 3, + IA_GOFO_MSG_LINK_STREAMING_MODE_N +}; + +enum ia_gofo_soc_pbk_instance_id { + IA_GOFO_SOC_PBK_ID0 = 0, + IA_GOFO_SOC_PBK_ID1 = 1, + IA_GOFO_SOC_PBK_ID_N +}; + +#define IA_GOFO_MSG_LINK_PBK_MAX_SLOTS (2U) + +struct ia_gofo_msg_indirect { + struct ia_gofo_msg_header header; + struct ia_gofo_tlv_header ref_header; + ia_gofo_addr_t ref_msg_ptr; +}; + +#pragma pack(pop) + +#pragma pack(push, 1) +#define IA_GOFO_MSG_LOG_MAX_PARAMS (4U) +#define IA_GOFO_MSG_LOG_DOC_FMT_ID_MIN (0U) + +#define IA_GOFO_MSG_LOG_DOC_FMT_ID_MAX (4095U) +#define IA_GOFO_MSG_LOG_FMT_ID_INVALID (0xfffffffU) + +struct ia_gofo_msg_log_info { + u16 log_counter; + u8 msg_parameter_types; + /* [0:0] is_out_of_order, [1:3] logger_channel, [4:7] reserved */ + u8 logger_opts; + u32 fmt_id; + u32 params[IA_GOFO_MSG_LOG_MAX_PARAMS]; +}; + +struct ia_gofo_msg_log_info_ts { + u64 msg_ts; + struct ia_gofo_msg_log_info log_info; +}; + +struct ia_gofo_msg_log { + struct ia_gofo_msg_header header; + struct ia_gofo_msg_log_info_ts log_info_ts; +}; + +#pragma pack(pop) + +#define IA_GOFO_MSG_ABI_OUT_ACK_QUEUE_ID (0U) +#define IA_GOFO_MSG_ABI_OUT_LOG_QUEUE_ID (1U) +#define IA_GOFO_MSG_ABI_IN_DEV_QUEUE_ID (2U) + +#endif diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_config_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_config_abi.h new file mode 100644 index 000000000000..c3f62aaedd86 --- /dev/null +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_config_abi.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2021 - 2025 Intel Corporation + */ + +#ifndef IPU7_FW_CONFIG_ABI_H +#define IPU7_FW_CONFIG_ABI_H + +#include + +#define IPU_CONFIG_ABI_WDT_TIMER_DISABLED 0U +#define IPU_CONFIG_ABI_CMD_TIMER_DISABLED 0U + +struct ipu7_wdt_abi { + u32 wdt_timer1_us; + u32 wdt_timer2_us; +}; + +#endif diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_insys_config_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_insys_config_abi.h new file mode 100644 index 000000000000..f161a605c500 --- /dev/null +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_insys_config_abi.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2021 - 2025 Intel Corporation + */ + +#ifndef IPU7_FW_INSYS_CONFIG_ABI_H +#define IPU7_FW_INSYS_CONFIG_ABI_H + +#include "ipu7_fw_boot_abi.h" +#include "ipu7_fw_config_abi.h" +#include "ipu7_fw_isys_abi.h" + +struct ipu7_insys_config { + u32 timeout_val_ms; + struct ia_gofo_logger_config logger_config; + struct ipu7_wdt_abi wdt_config; +}; + +#endif diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h new file mode 100644 index 000000000000..c42d0b7a2627 --- /dev/null +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h @@ -0,0 +1,412 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 - 2025 Intel Corporation + */ + +#ifndef IPU7_FW_ISYS_ABI_H +#define IPU7_FW_ISYS_ABI_H + +#include "ipu7_fw_common_abi.h" +#include "ipu7_fw_isys_abi.h" + +#define IPU_INSYS_MAX_OUTPUT_QUEUES (3U) +#define IPU_INSYS_STREAM_ID_MAX (16U) + +#define IPU_INSYS_MAX_INPUT_QUEUES (IPU_INSYS_STREAM_ID_MAX + 1U) +#define IPU_INSYS_OUTPUT_FIRST_QUEUE (0U) +#define IPU_INSYS_OUTPUT_LAST_QUEUE (IPU_INSYS_MAX_OUTPUT_QUEUES - 1U) +#define IPU_INSYS_OUTPUT_MSG_QUEUE (IPU_INSYS_OUTPUT_FIRST_QUEUE) +#define IPU_INSYS_OUTPUT_LOG_QUEUE (IPU_INSYS_OUTPUT_FIRST_QUEUE + 1U) +#define IPU_INSYS_OUTPUT_RESERVED_QUEUE (IPU_INSYS_OUTPUT_LAST_QUEUE) +#define IPU_INSYS_INPUT_FIRST_QUEUE (IPU_INSYS_MAX_OUTPUT_QUEUES) +#define IPU_INSYS_INPUT_LAST_QUEUE \ + (IPU_INSYS_INPUT_FIRST_QUEUE + IPU_INSYS_MAX_INPUT_QUEUES - 1U) +#define IPU_INSYS_INPUT_DEV_QUEUE (IPU_INSYS_INPUT_FIRST_QUEUE) +#define IPU_INSYS_INPUT_MSG_QUEUE (IPU_INSYS_INPUT_FIRST_QUEUE + 1U) +#define IPU_INSYS_INPUT_MSG_MAX_QUEUE (IPU_INSYS_MAX_INPUT_QUEUES - 1U) + +#define MAX_OPINS_FOR_SINGLE_IPINS (3U) +#define DEV_SEND_QUEUE_SIZE (IPU_INSYS_STREAM_ID_MAX) + +#define PIN_PLANES_MAX (4U) + +#define INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES_INPUT \ + INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES + +typedef u64 ipu7_insys_return_token; + +enum ipu7_insys_resp_type { + IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE = 0, + IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK = 1, + IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK = 2, + IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK = 3, + IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK = 4, + IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK = 5, + IPU_INSYS_RESP_TYPE_PIN_DATA_READY = 6, + IPU_INSYS_RESP_TYPE_FRAME_SOF = 7, + IPU_INSYS_RESP_TYPE_FRAME_EOF = 8, + IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE = 9, + IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE = 10, + IPU_INSYS_RESP_TYPE_PWM_IRQ = 11, + N_IPU_INSYS_RESP_TYPE +}; + +enum ipu7_insys_send_type { + IPU_INSYS_SEND_TYPE_STREAM_OPEN = 0, + IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE = 1, + IPU_INSYS_SEND_TYPE_STREAM_CAPTURE = 2, + IPU_INSYS_SEND_TYPE_STREAM_ABORT = 3, + IPU_INSYS_SEND_TYPE_STREAM_FLUSH = 4, + IPU_INSYS_SEND_TYPE_STREAM_CLOSE = 5, + N_IPU_INSYS_SEND_TYPE +}; + +enum ipu7_insys_mipi_vc { + IPU_INSYS_MIPI_VC_0 = 0, + IPU_INSYS_MIPI_VC_1 = 1, + IPU_INSYS_MIPI_VC_2 = 2, + IPU_INSYS_MIPI_VC_3 = 3, + IPU_INSYS_MIPI_VC_4 = 4, + IPU_INSYS_MIPI_VC_5 = 5, + IPU_INSYS_MIPI_VC_6 = 6, + IPU_INSYS_MIPI_VC_7 = 7, + IPU_INSYS_MIPI_VC_8 = 8, + IPU_INSYS_MIPI_VC_9 = 9, + IPU_INSYS_MIPI_VC_10 = 10, + IPU_INSYS_MIPI_VC_11 = 11, + IPU_INSYS_MIPI_VC_12 = 12, + IPU_INSYS_MIPI_VC_13 = 13, + IPU_INSYS_MIPI_VC_14 = 14, + IPU_INSYS_MIPI_VC_15 = 15, + N_IPU_INSYS_MIPI_VC +}; + +enum ipu7_insys_mipi_port { + IPU_INSYS_MIPI_PORT_0 = 0, + IPU_INSYS_MIPI_PORT_1 = 1, + IPU_INSYS_MIPI_PORT_2 = 2, + IPU_INSYS_MIPI_PORT_3 = 3, + IPU_INSYS_MIPI_PORT_4 = 4, + IPU_INSYS_MIPI_PORT_5 = 5, + NA_IPU_INSYS_MIPI_PORT +}; + +enum ipu7_insys_frame_format_type { + IPU_INSYS_FRAME_FORMAT_NV11 = 0, + IPU_INSYS_FRAME_FORMAT_NV12 = 1, + IPU_INSYS_FRAME_FORMAT_NV12_16 = 2, + IPU_INSYS_FRAME_FORMAT_NV12_TILEY = 3, + IPU_INSYS_FRAME_FORMAT_NV16 = 4, + IPU_INSYS_FRAME_FORMAT_NV21 = 5, + IPU_INSYS_FRAME_FORMAT_NV61 = 6, + IPU_INSYS_FRAME_FORMAT_YV12 = 7, + IPU_INSYS_FRAME_FORMAT_YV16 = 8, + IPU_INSYS_FRAME_FORMAT_YUV420 = 9, + IPU_INSYS_FRAME_FORMAT_YUV420_10 = 10, + IPU_INSYS_FRAME_FORMAT_YUV420_12 = 11, + IPU_INSYS_FRAME_FORMAT_YUV420_14 = 12, + IPU_INSYS_FRAME_FORMAT_YUV420_16 = 13, + IPU_INSYS_FRAME_FORMAT_YUV422 = 14, + IPU_INSYS_FRAME_FORMAT_YUV422_16 = 15, + IPU_INSYS_FRAME_FORMAT_UYVY = 16, + IPU_INSYS_FRAME_FORMAT_YUYV = 17, + IPU_INSYS_FRAME_FORMAT_YUV444 = 18, + IPU_INSYS_FRAME_FORMAT_YUV_LINE = 19, + IPU_INSYS_FRAME_FORMAT_RAW8 = 20, + IPU_INSYS_FRAME_FORMAT_RAW10 = 21, + IPU_INSYS_FRAME_FORMAT_RAW12 = 22, + IPU_INSYS_FRAME_FORMAT_RAW14 = 23, + IPU_INSYS_FRAME_FORMAT_RAW16 = 24, + IPU_INSYS_FRAME_FORMAT_RGB565 = 25, + IPU_INSYS_FRAME_FORMAT_PLANAR_RGB888 = 26, + IPU_INSYS_FRAME_FORMAT_RGBA888 = 27, + IPU_INSYS_FRAME_FORMAT_QPLANE6 = 28, + IPU_INSYS_FRAME_FORMAT_BINARY_8 = 29, + IPU_INSYS_FRAME_FORMAT_Y_8 = 30, + IPU_INSYS_FRAME_FORMAT_ARGB888 = 31, + IPU_INSYS_FRAME_FORMAT_BGRA888 = 32, + IPU_INSYS_FRAME_FORMAT_ABGR888 = 33, + N_IPU_INSYS_FRAME_FORMAT +}; + +#define IPU_INSYS_FRAME_FORMAT_RAW (IPU_INSYS_FRAME_FORMAT_RAW16) +#define N_IPU_INSYS_MIPI_DATA_TYPE 0x40 + +enum ipu7_insys_mipi_dt_rename_mode { + IPU_INSYS_MIPI_DT_NO_RENAME = 0, + IPU_INSYS_MIPI_DT_RENAMED_MODE = 1, + N_IPU_INSYS_MIPI_DT_MODE +}; + +#define IPU_INSYS_SEND_MSG_ENABLED 1U +#define IPU_INSYS_SEND_MSG_DISABLED 0U + +#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF BIT(0) +#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF BIT(1) +#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF BIT(2) +#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF BIT(3) +#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED BIT(4) +#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF_DISCARDED BIT(5) +#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED BIT(6) +#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF_DISCARDED BIT(7) +#define IPU_INSYS_STREAM_SYNC_MSG_ENABLE_MSG_SEND_RESP ( \ + IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF | \ + IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF | \ + IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED | \ + IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF_DISCARDED) +#define IPU_INSYS_STREAM_SYNC_MSG_ENABLE_MSG_SEND_IRQ ( \ + IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF | \ + IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF | \ + IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED | \ + IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF_DISCARDED) + +#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE BIT(0) +#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE BIT(1) +#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK BIT(2) +#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK BIT(3) +#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK BIT(4) +#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK BIT(5) +#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK BIT(6) +#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK BIT(7) +#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK BIT(8) +#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK BIT(9) +#define IPU_INSYS_STREAM_ENABLE_MSG_SEND_RESP ( \ + IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE | \ + IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK | \ + IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK | \ + IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK | \ + IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK) +#define IPU_INSYS_STREAM_ENABLE_MSG_SEND_IRQ ( \ + IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE | \ + IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK | \ + IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK | \ + IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK | \ + IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK) + +#define IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK BIT(0) +#define IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK BIT(1) +#define IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE BIT(2) +#define IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE BIT(3) +#define IPU_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY BIT(4) +#define IPU_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY BIT(5) +#define IPU_INSYS_FRAME_ENABLE_MSG_SEND_RESP ( \ + IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK | \ + IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE | \ + IPU_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY) +#define IPU_INSYS_FRAME_ENABLE_MSG_SEND_IRQ ( \ + IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK | \ + IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE | \ + IPU_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY) + +enum ipu7_insys_output_link_dest { + IPU_INSYS_OUTPUT_LINK_DEST_MEM = 0, + IPU_INSYS_OUTPUT_LINK_DEST_PSYS = 1, + IPU_INSYS_OUTPUT_LINK_DEST_IPU_EXTERNAL = 2 +}; + +enum ipu7_insys_dpcm_type { + IPU_INSYS_DPCM_TYPE_DISABLED = 0, + IPU_INSYS_DPCM_TYPE_10_8_10 = 1, + IPU_INSYS_DPCM_TYPE_12_8_12 = 2, + IPU_INSYS_DPCM_TYPE_12_10_12 = 3, + N_IPU_INSYS_DPCM_TYPE +}; + +enum ipu7_insys_dpcm_predictor { + IPU_INSYS_DPCM_PREDICTOR_1 = 0, + IPU_INSYS_DPCM_PREDICTOR_2 = 1, + N_IPU_INSYS_DPCM_PREDICTOR +}; + +enum ipu7_insys_send_queue_token_flag { + IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE = 0, + IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_FLUSH_FORCE = 1 +}; + +#pragma pack(push, 1) +struct ipu7_insys_resolution { + u32 width; + u32 height; +}; + +struct ipu7_insys_capture_output_pin_payload { + u64 user_token; + ia_gofo_addr_t addr; + u8 pad[4]; +}; + +struct ipu7_insys_output_link { + u32 buffer_lines; + u16 foreign_key; + u16 granularity_pointer_update; + u8 msg_link_streaming_mode; + u8 pbk_id; + u8 pbk_slot_id; + u8 dest; + u8 use_sw_managed; + u8 is_snoop; + u8 pad[2]; +}; + +struct ipu7_insys_output_cropping { + u16 line_top; + u16 line_bottom; +}; + +struct ipu7_insys_output_dpcm { + u8 enable; + u8 type; + u8 predictor; + u8 pad; +}; + +struct ipu7_insys_output_pin { + struct ipu7_insys_output_link link; + struct ipu7_insys_output_cropping crop; + struct ipu7_insys_output_dpcm dpcm; + u32 stride; + u16 ft; + u8 send_irq; + u8 input_pin_id; + u8 early_ack_en; + u8 pad[3]; +}; + +struct ipu7_insys_input_pin { + struct ipu7_insys_resolution input_res; + u16 sync_msg_map; + u8 dt; + u8 disable_mipi_unpacking; + u8 dt_rename_mode; + u8 mapped_dt; + u8 pad[2]; +}; + +struct ipu7_insys_stream_cfg { + struct ipu7_insys_input_pin input_pins[4]; + struct ipu7_insys_output_pin output_pins[4]; + u16 stream_msg_map; + u8 port_id; + u8 vc; + u8 nof_input_pins; + u8 nof_output_pins; + u8 pad[2]; +}; + +struct ipu7_insys_buffset { + struct ipu7_insys_capture_output_pin_payload output_pins[4]; + u8 capture_msg_map; + u8 frame_id; + u8 skip_frame; + u8 pad[5]; +}; + +struct ipu7_insys_resp { + u64 buf_id; + struct ipu7_insys_capture_output_pin_payload pin; + struct ia_gofo_msg_err error_info; + u32 timestamp[2]; + u8 type; + u8 msg_link_streaming_mode; + u8 stream_id; + u8 pin_id; + u8 frame_id; + u8 skip_frame; + u8 pad[2]; +}; + +struct ipu7_insys_resp_queue_token { + struct ipu7_insys_resp resp_info; +}; + +struct ipu7_insys_send_queue_token { + u64 buf_handle; + ia_gofo_addr_t addr; + u16 stream_id; + u8 send_type; + u8 flag; +}; + +#pragma pack(pop) + +enum insys_msg_err_stream { + INSYS_MSG_ERR_STREAM_OK = IA_GOFO_MSG_ERR_OK, + INSYS_MSG_ERR_STREAM_STREAM_ID = 1, + INSYS_MSG_ERR_STREAM_MAX_OPINS = 2, + INSYS_MSG_ERR_STREAM_MAX_IPINS = 3, + INSYS_MSG_ERR_STREAM_STREAM_MESSAGES_MAP = 4, + INSYS_MSG_ERR_STREAM_SYNC_MESSAGES_MAP = 5, + INSYS_MSG_ERR_STREAM_SENSOR_TYPE = 6, + INSYS_MSG_ERR_STREAM_FOREIGN_KEY = 7, + INSYS_MSG_ERR_STREAM_STREAMING_MODE = 8, + INSYS_MSG_ERR_STREAM_DPCM_EN = 9, + INSYS_MSG_ERR_STREAM_DPCM_TYPE = 10, + INSYS_MSG_ERR_STREAM_DPCM_PREDICTOR = 11, + INSYS_MSG_ERR_STREAM_GRANULARITY_POINTER_UPDATE = 12, + INSYS_MSG_ERR_STREAM_MPF_LUT_ENTRY_RESOURCES_BUSY = 13, + INSYS_MSG_ERR_STREAM_MPF_DEV_ID = 14, + INSYS_MSG_ERR_STREAM_BUFFER_LINES = 15, + INSYS_MSG_ERR_STREAM_IPIN_ID = 16, + INSYS_MSG_ERR_STREAM_DATA_TYPE = 17, + INSYS_MSG_ERR_STREAM_STREAMING_PROTOCOL_STATE = 18, + INSYS_MSG_ERR_STREAM_SYSCOM_FLUSH = 19, + INSYS_MSG_ERR_STREAM_MIPI_VC = 20, + INSYS_MSG_ERR_STREAM_STREAM_SRC = 21, + INSYS_MSG_ERR_STREAM_PBK_ID = 22, + INSYS_MSG_ERR_STREAM_CMD_QUEUE_DEALLOCATE = 23, + INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES = 24, + INSYS_MSG_ERR_STREAM_IPIN_CONFIGURATION = 25, + INSYS_MSG_ERR_STREAM_INVALID_STATE = 26, + INSYS_MSG_ERR_STREAM_SW_MANAGED = 27, + INSYS_MSG_ERR_STREAM_PBK_SLOT_ID = 28, + INSYS_MSG_ERR_STREAM_FLUSH_TIMEOUT = 29, + INSYS_MSG_ERR_STREAM_IPIN_WIDTH = 30, + INSYS_MSG_ERR_STREAM_IPIN_HEIGHT = 31, + INSYS_MSG_ERR_STREAM_OUTPUT_PIN_EARLY_ACK_EN = 32, + INSYS_MSG_ERR_STREAM_INCONSISTENT_PARAMS = 33, + INSYS_MSG_ERR_STREAM_PLANE_COUNT = 34, + INSYS_MSG_ERR_STREAM_FRAME_FORMAT_TYPE = 35, + INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES_OUTPUT = 36, + INSYS_MSG_ERR_STREAM_WIDTH_OUTPUT_SIZE = 37, + INSYS_MSG_ERR_STREAM_CLOSED = 38, + INSYS_MSG_ERR_STREAM_N +}; + +enum insys_msg_err_capture { + INSYS_MSG_ERR_CAPTURE_OK = IA_GOFO_MSG_ERR_OK, + INSYS_MSG_ERR_CAPTURE_STREAM_ID = 1, + INSYS_MSG_ERR_CAPTURE_PAYLOAD_PTR = 2, + INSYS_MSG_ERR_CAPTURE_MEM_SLOT = 3, + INSYS_MSG_ERR_CAPTURE_STREAMING_MODE = 4, + INSYS_MSG_ERR_CAPTURE_AVAILABLE_CMD_SLOT = 5, + INSYS_MSG_ERR_CAPTURE_CONSUMED_CMD_SLOT = 6, + INSYS_MSG_ERR_CAPTURE_CMD_SLOT_PAYLOAD_PTR = 7, + INSYS_MSG_ERR_CAPTURE_CMD_PREPARE = 8, + INSYS_MSG_ERR_CAPTURE_OUTPUT_PIN = 9, + INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP = 10, + INSYS_MSG_ERR_CAPTURE_FRAME_MESSAGES_MAP = 11, + INSYS_MSG_ERR_CAPTURE_TIMEOUT = 12, + INSYS_MSG_ERR_CAPTURE_INVALID_STREAM_STATE = 13, + INSYS_MSG_ERR_CAPTURE_HW_ERR_MULTIBIT_PH_ERROR_DETECTED = 14, + INSYS_MSG_ERR_CAPTURE_HW_ERR_PAYLOAD_CRC_ERROR = 15, + INSYS_MSG_ERR_CAPTURE_HW_ERR_INPUT_DATA_LOSS_ELASTIC_FIFO_OVFL = 16, + INSYS_MSG_ERR_CAPTURE_HW_ERR_PIXEL_BUFFER_OVERFLOW = 17, + INSYS_MSG_ERR_CAPTURE_HW_ERR_BAD_FRAME_DIM = 18, + INSYS_MSG_ERR_CAPTURE_HW_ERR_PHY_SYNC_ERR = 19, + INSYS_MSG_ERR_CAPTURE_HW_ERR_SECURE_TOUCH = 20, + INSYS_MSG_ERR_CAPTURE_HW_ERR_MASTER_SLAVE_SYNC_ERR = 21, + INSYS_MSG_ERR_CAPTURE_FRAME_SKIP_ERR = 22, + INSYS_MSG_ERR_CAPTURE_FE_INPUT_FIFO_OVERFLOW_ERR = 23, + INSYS_MSG_ERR_CAPTURE_CMD_SUBMIT_TO_HW = 24, + INSYS_MSG_ERR_CAPTURE_N +}; + +enum insys_msg_err_groups { + INSYS_MSG_ERR_GROUP_RESERVED = IA_GOFO_MSG_ERR_GROUP_RESERVED, + INSYS_MSG_ERR_GROUP_GENERAL = IA_GOFO_MSG_ERR_GROUP_GENERAL, + INSYS_MSG_ERR_GROUP_STREAM = 2, + INSYS_MSG_ERR_GROUP_CAPTURE = 3, + INSYS_MSG_ERR_GROUP_N, +}; + +#endif diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h new file mode 100644 index 000000000000..8a78dd0936df --- /dev/null +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h @@ -0,0 +1,465 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 - 2025 Intel Corporation + */ + +#ifndef IPU7_FW_MSG_ABI_H +#define IPU7_FW_MSG_ABI_H + +#include "ipu7_fw_common_abi.h" + +#pragma pack(push, 1) +enum ipu7_msg_type { + IPU_MSG_TYPE_RESERVED = IA_GOFO_MSG_TYPE_RESERVED, + IPU_MSG_TYPE_INDIRECT = IA_GOFO_MSG_TYPE_INDIRECT, + IPU_MSG_TYPE_DEV_LOG = IA_GOFO_MSG_TYPE_LOG, + IPU_MSG_TYPE_GENERAL_ERR = IA_GOFO_MSG_TYPE_GENERAL_ERR, + IPU_MSG_TYPE_DEV_OPEN = 4, + IPU_MSG_TYPE_DEV_OPEN_ACK = 5, + IPU_MSG_TYPE_GRAPH_OPEN = 6, + IPU_MSG_TYPE_GRAPH_OPEN_ACK = 7, + IPU_MSG_TYPE_TASK_REQ = 8, + IPU_MSG_TYPE_TASK_DONE = 9, + IPU_MSG_TYPE_GRAPH_CLOSE = 10, + IPU_MSG_TYPE_GRAPH_CLOSE_ACK = 11, + IPU_MSG_TYPE_DEV_CLOSE = 12, + IPU_MSG_TYPE_DEV_CLOSE_ACK = 13, + IPU_MSG_TYPE_TERM_EVENT = 14, + IPU_MSG_TYPE_N, +}; + +#define IPU_MSG_MAX_NODE_TERMS (64U) +#define IPU_MSG_MAX_FRAGS (7U) + +enum ipu7_msg_node_type { + IPU_MSG_NODE_TYPE_PAD = 0, + IPU_MSG_NODE_TYPE_BASE, + IPU_MSG_NODE_TYPE_N +}; + +#define IPU_MSG_NODE_MAX_DEVICES (128U) +#define DEB_NUM_UINT32 (IPU_MSG_NODE_MAX_DEVICES / (sizeof(u32) * 8U)) + +typedef u32 ipu7_msg_teb_t[2]; +typedef u32 ipu7_msg_deb_t[DEB_NUM_UINT32]; + +#define IPU_MSG_NODE_MAX_ROUTE_ENABLES (128U) +#define RBM_NUM_UINT32 (IPU_MSG_NODE_MAX_ROUTE_ENABLES / (sizeof(u32) * 8U)) + +typedef u32 ipu7_msg_rbm_t[RBM_NUM_UINT32]; + +enum ipu7_msg_node_profile_type { + IPU_MSG_NODE_PROFILE_TYPE_PAD = 0, + IPU_MSG_NODE_PROFILE_TYPE_BASE, + IPU_MSG_NODE_PROFILE_TYPE_CB, + IPU_MSG_NODE_PROFILE_TYPE_N +}; + +struct ipu7_msg_node_profile { + struct ia_gofo_tlv_header tlv_header; + ipu7_msg_teb_t teb; +}; + +struct ipu7_msg_cb_profile { + struct ipu7_msg_node_profile profile_base; + ipu7_msg_deb_t deb; + ipu7_msg_rbm_t rbm; + ipu7_msg_rbm_t reb; +}; + +#define IPU_MSG_NODE_MAX_PROFILES (2U) +#define IPU_MSG_NODE_DEF_PROFILE_IDX (0U) +#define IPU_MSG_NODE_RSRC_ID_EXT_IP (0xffU) + +#define IPU_MSG_NODE_DONT_CARE_TEB_HI (0xffffffffU) +#define IPU_MSG_NODE_DONT_CARE_TEB_LO (0xffffffffU) +#define IPU_MSG_NODE_RSRC_ID_IS (0xfeU) + +struct ipu7_msg_node { + struct ia_gofo_tlv_header tlv_header; + u8 node_rsrc_id; + u8 node_ctx_id; + u8 num_frags; + u8 reserved[1]; + struct ia_gofo_tlv_list profiles_list; + struct ia_gofo_tlv_list terms_list; + struct ia_gofo_tlv_list node_options; +}; + +enum ipu7_msg_node_option_types { + IPU_MSG_NODE_OPTION_TYPES_PADDING = 0, + IPU_MSG_NODE_OPTION_TYPES_N +}; + +#pragma pack(pop) + +#pragma pack(push, 1) + +enum ipu7_msg_link_type { + IPU_MSG_LINK_TYPE_PAD = 0, + IPU_MSG_LINK_TYPE_GENERIC = 1, + IPU_MSG_LINK_TYPE_N +}; + +enum ipu7_msg_link_option_types { + IPU_MSG_LINK_OPTION_TYPES_PADDING = 0, + IPU_MSG_LINK_OPTION_TYPES_CMPRS = 1, + IPU_MSG_LINK_OPTION_TYPES_N +}; + +enum ipu7_msg_link_cmprs_option_bit_depth { + IPU_MSG_LINK_CMPRS_OPTION_8BPP = 0, + IPU_MSG_LINK_CMPRS_OPTION_10BPP = 1, + IPU_MSG_LINK_CMPRS_OPTION_12BPP = 2, +}; + +#define IPU_MSG_LINK_CMPRS_SPACE_SAVING_DENOM (128U) +#define IPU_MSG_LINK_CMPRS_LOSSY_CFG_PAYLOAD_SIZE (5U) +#define IPU_MSG_LINK_CMPRS_SPACE_SAVING_NUM_MAX \ + (IPU_MSG_LINK_CMPRS_SPACE_SAVING_DENOM - 1U) + +struct ipu7_msg_link_cmprs_plane_desc { + u8 plane_enable; + u8 cmprs_enable; + u8 encoder_plane_id; + u8 decoder_plane_id; + u8 cmprs_is_lossy; + u8 cmprs_is_footprint; + u8 bit_depth; + u8 space_saving_numerator; + u32 pixels_offset; + u32 ts_offset; + u32 tile_row_to_tile_row_stride; + u32 rows_of_tiles; + u32 lossy_cfg[IPU_MSG_LINK_CMPRS_LOSSY_CFG_PAYLOAD_SIZE]; +}; + +#define IPU_MSG_LINK_CMPRS_MAX_PLANES (2U) +#define IPU_MSG_LINK_CMPRS_NO_ALIGN_INTERVAL (0U) +#define IPU_MSG_LINK_CMPRS_MIN_ALIGN_INTERVAL (16U) +#define IPU_MSG_LINK_CMPRS_MAX_ALIGN_INTERVAL (1024U) +struct ipu7_msg_link_cmprs_option { + struct ia_gofo_tlv_header header; + u32 cmprs_buf_size; + u16 align_interval; + u8 reserved[2]; + struct ipu7_msg_link_cmprs_plane_desc plane_descs[2]; +}; + +struct ipu7_msg_link_ep { + u8 node_ctx_id; + u8 term_id; +}; + +struct ipu7_msg_link_ep_pair { + struct ipu7_msg_link_ep ep_src; + struct ipu7_msg_link_ep ep_dst; +}; + +#define IPU_MSG_LINK_FOREIGN_KEY_NONE (65535U) +#define IPU_MSG_LINK_FOREIGN_KEY_MAX (64U) +#define IPU_MSG_LINK_PBK_ID_DONT_CARE (255U) +#define IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE (255U) +#define IPU_MSG_LINK_TERM_ID_DONT_CARE (0xffU) + +struct ipu7_msg_link { + struct ia_gofo_tlv_header tlv_header; + struct ipu7_msg_link_ep_pair endpoints; + u16 foreign_key; + u8 streaming_mode; + u8 pbk_id; + u8 pbk_slot_id; + u8 delayed_link; + u8 reserved[2]; + struct ia_gofo_tlv_list link_options; +}; + +#pragma pack(pop) + +enum ipu7_msg_dev_state { + IPU_MSG_DEV_STATE_CLOSED = 0, + IPU_MSG_DEV_STATE_OPEN_WAIT = 1, + IPU_MSG_DEV_STATE_OPEN = 2, + IPU_MSG_DEV_STATE_CLOSE_WAIT = 3, + IPU_MSG_DEV_STATE_N +}; + +enum ipu7_msg_graph_state { + IPU_MSG_GRAPH_STATE_CLOSED = 0, + IPU_MSG_GRAPH_STATE_OPEN_WAIT = 1, + IPU_MSG_GRAPH_STATE_OPEN = 2, + IPU_MSG_GRAPH_STATE_CLOSE_WAIT = 3, + IPU_MSG_GRAPH_STATE_N +}; + +enum ipu7_msg_task_state { + IPU_MSG_TASK_STATE_DONE = 0, + IPU_MSG_TASK_STATE_WAIT_DONE = 1, + IPU_MSG_TASK_STATE_N +}; + +enum ipu7_msg_err_groups { + IPU_MSG_ERR_GROUP_RESERVED = IA_GOFO_MSG_ERR_GROUP_RESERVED, + IPU_MSG_ERR_GROUP_GENERAL = IA_GOFO_MSG_ERR_GROUP_GENERAL, + IPU_MSG_ERR_GROUP_DEVICE = 2, + IPU_MSG_ERR_GROUP_GRAPH = 3, + IPU_MSG_ERR_GROUP_TASK = 4, + IPU_MSG_ERR_GROUP_N, +}; + +#pragma pack(push, 1) +struct ipu7_msg_task { + struct ia_gofo_msg_header header; + u8 graph_id; + u8 profile_idx; + u8 node_ctx_id; + u8 frame_id; + u8 frag_id; + u8 req_done_msg; + u8 req_done_irq; + u8 reserved[1]; + ipu7_msg_teb_t payload_reuse_bm; + ia_gofo_addr_t term_buffers[IPU_MSG_MAX_NODE_TERMS]; +}; + +struct ipu7_msg_task_done { + struct ia_gofo_msg_header_ack header; + u8 graph_id; + u8 frame_id; + u8 node_ctx_id; + u8 profile_idx; + u8 frag_id; + u8 reserved[3]; +}; + +enum ipu7_msg_err_task { + IPU_MSG_ERR_TASK_OK = IA_GOFO_MSG_ERR_OK, + IPU_MSG_ERR_TASK_GRAPH_ID = 1, + IPU_MSG_ERR_TASK_NODE_CTX_ID = 2, + IPU_MSG_ERR_TASK_PROFILE_IDX = 3, + IPU_MSG_ERR_TASK_CTX_MEMORY_TASK = 4, + IPU_MSG_ERR_TASK_TERM_PAYLOAD_PTR = 5, + IPU_MSG_ERR_TASK_FRAME_ID = 6, + IPU_MSG_ERR_TASK_FRAG_ID = 7, + IPU_MSG_ERR_TASK_EXEC_EXT = 8, + IPU_MSG_ERR_TASK_EXEC_SBX = 9, + IPU_MSG_ERR_TASK_EXEC_INT = 10, + IPU_MSG_ERR_TASK_EXEC_UNKNOWN = 11, + IPU_MSG_ERR_TASK_PRE_EXEC = 12, + IPU_MSG_ERR_TASK_N +}; + +#pragma pack(pop) + +#pragma pack(push, 1) +enum ipu7_msg_term_type { + IPU_MSG_TERM_TYPE_PAD = 0, + IPU_MSG_TERM_TYPE_BASE, + IPU_MSG_TERM_TYPE_N, +}; + +#define IPU_MSG_TERM_EVENT_TYPE_NONE 0U +#define IPU_MSG_TERM_EVENT_TYPE_PROGRESS 1U +#define IPU_MSG_TERM_EVENT_TYPE_N (IPU_MSG_TERM_EVENT_TYPE_PROGRESS + 1U) + +struct ipu7_msg_term { + struct ia_gofo_tlv_header tlv_header; + u8 term_id; + u8 event_req_bm; + u8 reserved[2]; + u32 payload_size; + struct ia_gofo_tlv_list term_options; +}; + +enum ipu7_msg_term_option_types { + IPU_MSG_TERM_OPTION_TYPES_PADDING = 0, + IPU_MSG_TERM_OPTION_TYPES_N +}; + +struct ipu7_msg_term_event { + struct ia_gofo_msg_header header; + u8 graph_id; + u8 frame_id; + u8 node_ctx_id; + u8 profile_idx; + u8 frag_id; + u8 term_id; + u8 event_type; + u8 reserved[1]; + u64 event_ts; +}; + +#pragma pack(pop) + +#pragma pack(push, 1) +#define IPU_MSG_DEVICE_SEND_MSG_ENABLED 1U +#define IPU_MSG_DEVICE_SEND_MSG_DISABLED 0U + +#define IPU_MSG_DEVICE_OPEN_SEND_RESP BIT(0) +#define IPU_MSG_DEVICE_OPEN_SEND_IRQ BIT(1) + +#define IPU_MSG_DEVICE_CLOSE_SEND_RESP BIT(0) +#define IPU_MSG_DEVICE_CLOSE_SEND_IRQ BIT(1) + +struct ipu7_msg_dev_open { + struct ia_gofo_msg_header header; + u32 max_graphs; + u8 dev_msg_map; + u8 enable_power_gating; + u8 reserved[2]; +}; + +struct ipu7_msg_dev_open_ack { + struct ia_gofo_msg_header_ack header; +}; + +struct ipu7_msg_dev_close { + struct ia_gofo_msg_header header; + u8 dev_msg_map; + u8 reserved[7]; +}; + +struct ipu7_msg_dev_close_ack { + struct ia_gofo_msg_header_ack header; +}; + +enum ipu7_msg_err_device { + IPU_MSG_ERR_DEVICE_OK = IA_GOFO_MSG_ERR_OK, + IPU_MSG_ERR_DEVICE_MAX_GRAPHS = 1, + IPU_MSG_ERR_DEVICE_MSG_MAP = 2, + IPU_MSG_ERR_DEVICE_N +}; + +#pragma pack(pop) + +#pragma pack(push, 1) +#define IPU_MSG_GRAPH_ID_UNKNOWN (0xffU) +#define IPU_MSG_GRAPH_SEND_MSG_ENABLED 1U +#define IPU_MSG_GRAPH_SEND_MSG_DISABLED 0U + +#define IPU_MSG_GRAPH_OPEN_SEND_RESP BIT(0) +#define IPU_MSG_GRAPH_OPEN_SEND_IRQ BIT(1) + +#define IPU_MSG_GRAPH_CLOSE_SEND_RESP BIT(0) +#define IPU_MSG_GRAPH_CLOSE_SEND_IRQ BIT(1) + +struct ipu7_msg_graph_open { + struct ia_gofo_msg_header header; + struct ia_gofo_tlv_list nodes; + struct ia_gofo_tlv_list links; + u8 graph_id; + u8 graph_msg_map; + u8 reserved[6]; +}; + +enum ipu7_msg_graph_ack_option_types { + IPU_MSG_GRAPH_ACK_OPTION_TYPES_PADDING = 0, + IPU_MSG_GRAPH_ACK_TASK_Q_INFO, + IPU_MSG_GRAPH_ACK_OPTION_TYPES_N +}; + +struct ipu7_msg_graph_open_ack_task_q_info { + struct ia_gofo_tlv_header header; + u8 node_ctx_id; + u8 q_id; + u8 reserved[2]; +}; + +struct ipu7_msg_graph_open_ack { + struct ia_gofo_msg_header_ack header; + u8 graph_id; + u8 reserved[7]; +}; + +struct ipu7_msg_graph_close { + struct ia_gofo_msg_header header; + u8 graph_id; + u8 graph_msg_map; + u8 reserved[6]; +}; + +struct ipu7_msg_graph_close_ack { + struct ia_gofo_msg_header_ack header; + u8 graph_id; + u8 reserved[7]; +}; + +enum ipu7_msg_err_graph { + IPU_MSG_ERR_GRAPH_OK = IA_GOFO_MSG_ERR_OK, + IPU_MSG_ERR_GRAPH_GRAPH_STATE = 1, + IPU_MSG_ERR_GRAPH_MAX_GRAPHS = 2, + IPU_MSG_ERR_GRAPH_GRAPH_ID = 3, + IPU_MSG_ERR_GRAPH_NODE_CTX_ID = 4, + IPU_MSG_ERR_GRAPH_NODE_RSRC_ID = 5, + IPU_MSG_ERR_GRAPH_PROFILE_IDX = 6, + IPU_MSG_ERR_GRAPH_TERM_ID = 7, + IPU_MSG_ERR_GRAPH_TERM_PAYLOAD_SIZE = 8, + IPU_MSG_ERR_GRAPH_LINK_NODE_CTX_ID = 9, + IPU_MSG_ERR_GRAPH_LINK_TERM_ID = 10, + IPU_MSG_ERR_GRAPH_PROFILE_TYPE = 11, + IPU_MSG_ERR_GRAPH_NUM_FRAGS = 12, + IPU_MSG_ERR_GRAPH_QUEUE_ID_USAGE = 13, + IPU_MSG_ERR_GRAPH_QUEUE_OPEN = 14, + IPU_MSG_ERR_GRAPH_QUEUE_CLOSE = 15, + IPU_MSG_ERR_GRAPH_QUEUE_ID_TASK_REQ_MISMATCH = 16, + IPU_MSG_ERR_GRAPH_CTX_MEMORY_FGRAPH = 17, + IPU_MSG_ERR_GRAPH_CTX_MEMORY_NODE = 18, + IPU_MSG_ERR_GRAPH_CTX_MEMORY_NODE_PROFILE = 19, + IPU_MSG_ERR_GRAPH_CTX_MEMORY_TERM = 20, + IPU_MSG_ERR_GRAPH_CTX_MEMORY_LINK = 21, + IPU_MSG_ERR_GRAPH_CTX_MSG_MAP = 22, + IPU_MSG_ERR_GRAPH_CTX_FOREIGN_KEY = 23, + IPU_MSG_ERR_GRAPH_CTX_STREAMING_MODE = 24, + IPU_MSG_ERR_GRAPH_CTX_PBK_RSRC = 25, + IPU_MSG_ERR_GRAPH_UNSUPPORTED_EVENT_TYPE = 26, + IPU_MSG_ERR_GRAPH_TOO_MANY_EVENTS = 27, + IPU_MSG_ERR_GRAPH_CTX_MEMORY_CMPRS = 28, + IPU_MSG_ERR_GRAPH_CTX_CMPRS_ALIGN_INTERVAL = 29, + IPU_MSG_ERR_GRAPH_CTX_CMPRS_PLANE_ID = 30, + IPU_MSG_ERR_GRAPH_CTX_CMPRS_UNSUPPORTED_MODE = 31, + IPU_MSG_ERR_GRAPH_CTX_CMPRS_BIT_DEPTH = 32, + IPU_MSG_ERR_GRAPH_CTX_CMPRS_STRIDE_ALIGNMENT = 33, + IPU_MSG_ERR_GRAPH_CTX_CMPRS_SUB_BUFFER_ALIGNMENT = 34, + IPU_MSG_ERR_GRAPH_CTX_CMPRS_LAYOUT_ORDER = 35, + IPU_MSG_ERR_GRAPH_CTX_CMPRS_LAYOUT_OVERLAP = 36, + IPU_MSG_ERR_GRAPH_CTX_CMPRS_BUFFER_TOO_SMALL = 37, + IPU_MSG_ERR_GRAPH_CTX_DELAYED_LINK = 38, + IPU_MSG_ERR_GRAPH_N +}; + +#pragma pack(pop) + +#define FWPS_MSG_ABI_MAX_INPUT_QUEUES (60U) +#define FWPS_MSG_ABI_MAX_OUTPUT_QUEUES (2U) +#define FWPS_MSG_ABI_MAX_QUEUES \ + (FWPS_MSG_ABI_MAX_OUTPUT_QUEUES + FWPS_MSG_ABI_MAX_INPUT_QUEUES) + +#define FWPS_MSG_ABI_OUT_ACK_QUEUE_ID (IA_GOFO_MSG_ABI_OUT_ACK_QUEUE_ID) +#define FWPS_MSG_ABI_OUT_LOG_QUEUE_ID (IA_GOFO_MSG_ABI_OUT_LOG_QUEUE_ID) +#if (FWPS_MSG_ABI_OUT_LOG_QUEUE_ID >= FWPS_MSG_ABI_MAX_OUTPUT_QUEUES) +#error "Maximum output queues configuration is too small to fit ACK and LOG \ +queues" +#endif +#define FWPS_MSG_ABI_IN_DEV_QUEUE_ID (IA_GOFO_MSG_ABI_IN_DEV_QUEUE_ID) +#define FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID (3U) +#define FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID \ + (FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID + 1U) + +#if (FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID >= FWPS_MSG_ABI_MAX_INPUT_QUEUES) +#error "Maximum queues configuration is too small to fit minimum number of \ +useful queues" +#endif + +#define FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID (FWPS_MSG_ABI_MAX_QUEUES - 1U) +#define FWPS_MSG_ABI_IN_MAX_TASK_QUEUES \ + (FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID - \ + FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID + 1U) +#define FWPS_MSG_ABI_OUT_FIRST_QUEUE_ID (FWPS_MSG_ABI_OUT_ACK_QUEUE_ID) +#define FWPS_MSG_ABI_OUT_LAST_QUEUE_ID (FWPS_MSG_ABI_MAX_OUTPUT_QUEUES - 1U) +#define FWPS_MSG_ABI_IN_FIRST_QUEUE_ID (FWPS_MSG_ABI_IN_DEV_QUEUE_ID) +#define FWPS_MSG_ABI_IN_LAST_QUEUE_ID (FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID) + +#define FWPS_MSG_HOST2FW_MAX_SIZE (2U * 1024U) +#define FWPS_MSG_FW2HOST_MAX_SIZE (256U) + +#endif diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_psys_config_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_psys_config_abi.h new file mode 100644 index 000000000000..0af04c8c6a88 --- /dev/null +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_psys_config_abi.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 - 2025 Intel Corporation + */ + +#ifndef IPU7_PSYS_CONFIG_ABI_H_INCLUDED__ +#define IPU7_PSYS_CONFIG_ABI_H_INCLUDED__ + +#include + +#include "ipu7_fw_boot_abi.h" +#include "ipu7_fw_config_abi.h" + +struct ipu7_psys_config { + u32 use_debug_manifest; + u32 timeout_val_ms; + u32 compression_support_enabled; + struct ia_gofo_logger_config logger_config; + struct ipu7_wdt_abi wdt_config; + u8 ipu_psys_debug_bitmask; + u8 padding[3]; +}; + +#endif diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_syscom_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_syscom_abi.h new file mode 100644 index 000000000000..bfa5258d5b97 --- /dev/null +++ b/drivers/staging/media/ipu7/abi/ipu7_fw_syscom_abi.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 - 2025 Intel Corporation + */ + +#ifndef IPU7_FW_SYSCOM_ABI_H +#define IPU7_FW_SYSCOM_ABI_H + +#include + +#include "ipu7_fw_common_abi.h" + +#pragma pack(push, 1) +#define SYSCOM_QUEUE_MIN_CAPACITY 2U + +struct syscom_queue_params_config { + ia_gofo_addr_t token_array_mem; + u16 token_size_in_bytes; + u16 max_capacity; +}; + +struct syscom_config_s { + u16 max_output_queues; + u16 max_input_queues; +}; + +#pragma pack(pop) + +static inline struct syscom_queue_params_config * +syscom_config_get_queue_configs(struct syscom_config_s *config) +{ + return (struct syscom_queue_params_config *)(&config[1]); +} + +static inline const struct syscom_queue_params_config * +syscom_config_get_queue_configs_const(const struct syscom_config_s *config) +{ + return (const struct syscom_queue_params_config *)(&config[1]); +} + +#pragma pack(push, 1) +struct syscom_queue_indices_s { + u32 read_index; + u32 write_index; +}; + +#pragma pack(pop) + +#endif -- cgit v1.2.3 From a516d36bdc3d8373f904af57c95e76d6f921cf1c Mon Sep 17 00:00:00 2001 From: Bingbu Cao Date: Thu, 29 May 2025 12:13:21 +0800 Subject: media: staging/ipu7: add IPU7 input system device driver The main input system driver mainly cover the basic hardware setup, v4l2 devices registration, firmware stream interfaces and interrupt handling. Input system CSI2 receiver is exposed as a v4l2 sub-device. Each CSI2 sub-device represent one single CSI2 hardware port which be linked with external sub-device such camera sensor by linked with ISYS CSI2's sink pad. The CSI2 source pad is linked to the sink pad of video capture device. Register V4L2 video device and setup the VB2 queues to support video capture. Video streaming callback will trigger the input system driver to construct a input system stream configuration for firmware based on data type and stream ID and then queue buffers to firmware to do capture. IPU7 CSI-2 D-PHY hardware is a Synopsys DWC MIPI CSI2 Rx IP, the driver program the DPHY to receive MIPI data from camera sensors. Signed-off-by: Bingbu Cao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/ipu7/ipu7-fw-isys.c | 301 ++++++ drivers/staging/media/ipu7/ipu7-fw-isys.h | 39 + drivers/staging/media/ipu7/ipu7-isys-csi-phy.c | 1034 +++++++++++++++++++ drivers/staging/media/ipu7/ipu7-isys-csi-phy.h | 16 + drivers/staging/media/ipu7/ipu7-isys-csi2-regs.h | 1197 ++++++++++++++++++++++ drivers/staging/media/ipu7/ipu7-isys-csi2.c | 543 ++++++++++ drivers/staging/media/ipu7/ipu7-isys-csi2.h | 64 ++ drivers/staging/media/ipu7/ipu7-isys-queue.c | 829 +++++++++++++++ drivers/staging/media/ipu7/ipu7-isys-queue.h | 72 ++ drivers/staging/media/ipu7/ipu7-isys-subdev.c | 348 +++++++ drivers/staging/media/ipu7/ipu7-isys-subdev.h | 53 + drivers/staging/media/ipu7/ipu7-isys-video.c | 1112 ++++++++++++++++++++ drivers/staging/media/ipu7/ipu7-isys-video.h | 117 +++ drivers/staging/media/ipu7/ipu7-isys.c | 1166 +++++++++++++++++++++ drivers/staging/media/ipu7/ipu7-isys.h | 140 +++ drivers/staging/media/ipu7/ipu7.c | 6 +- 16 files changed, 7033 insertions(+), 4 deletions(-) create mode 100644 drivers/staging/media/ipu7/ipu7-fw-isys.c create mode 100644 drivers/staging/media/ipu7/ipu7-fw-isys.h create mode 100644 drivers/staging/media/ipu7/ipu7-isys-csi-phy.c create mode 100644 drivers/staging/media/ipu7/ipu7-isys-csi-phy.h create mode 100644 drivers/staging/media/ipu7/ipu7-isys-csi2-regs.h create mode 100644 drivers/staging/media/ipu7/ipu7-isys-csi2.c create mode 100644 drivers/staging/media/ipu7/ipu7-isys-csi2.h create mode 100644 drivers/staging/media/ipu7/ipu7-isys-queue.c create mode 100644 drivers/staging/media/ipu7/ipu7-isys-queue.h create mode 100644 drivers/staging/media/ipu7/ipu7-isys-subdev.c create mode 100644 drivers/staging/media/ipu7/ipu7-isys-subdev.h create mode 100644 drivers/staging/media/ipu7/ipu7-isys-video.c create mode 100644 drivers/staging/media/ipu7/ipu7-isys-video.h create mode 100644 drivers/staging/media/ipu7/ipu7-isys.c create mode 100644 drivers/staging/media/ipu7/ipu7-isys.h (limited to 'drivers/staging') diff --git a/drivers/staging/media/ipu7/ipu7-fw-isys.c b/drivers/staging/media/ipu7/ipu7-fw-isys.c new file mode 100644 index 000000000000..e4b9c364572b --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-fw-isys.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include + +#include "abi/ipu7_fw_insys_config_abi.h" +#include "abi/ipu7_fw_isys_abi.h" + +#include "ipu7.h" +#include "ipu7-boot.h" +#include "ipu7-bus.h" +#include "ipu7-dma.h" +#include "ipu7-fw-isys.h" +#include "ipu7-isys.h" +#include "ipu7-platform-regs.h" +#include "ipu7-syscom.h" + +static const char * const send_msg_types[N_IPU_INSYS_SEND_TYPE] = { + "STREAM_OPEN", + "STREAM_START_AND_CAPTURE", + "STREAM_CAPTURE", + "STREAM_ABORT", + "STREAM_FLUSH", + "STREAM_CLOSE" +}; + +int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys, + const unsigned int stream_handle, + void *cpu_mapped_buf, + dma_addr_t dma_mapped_buf, + size_t size, u16 send_type) +{ + struct ipu7_syscom_context *ctx = isys->adev->syscom; + struct device *dev = &isys->adev->auxdev.dev; + struct ipu7_insys_send_queue_token *token; + + if (send_type >= N_IPU_INSYS_SEND_TYPE) + return -EINVAL; + + dev_dbg(dev, "send_token: %s\n", send_msg_types[send_type]); + + /* + * Time to flush cache in case we have some payload. Not all messages + * have that + */ + if (cpu_mapped_buf) + clflush_cache_range(cpu_mapped_buf, size); + + token = ipu7_syscom_get_token(ctx, stream_handle + + IPU_INSYS_INPUT_MSG_QUEUE); + if (!token) + return -EBUSY; + + token->addr = dma_mapped_buf; + token->buf_handle = (unsigned long)cpu_mapped_buf; + token->send_type = send_type; + token->stream_id = stream_handle; + token->flag = IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE; + + ipu7_syscom_put_token(ctx, stream_handle + IPU_INSYS_INPUT_MSG_QUEUE); + /* now wakeup FW */ + ipu_buttress_wakeup_is_uc(isys->adev->isp); + + return 0; +} + +int ipu7_fw_isys_simple_cmd(struct ipu7_isys *isys, + const unsigned int stream_handle, u16 send_type) +{ + return ipu7_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0, + send_type); +} + +int ipu7_fw_isys_init(struct ipu7_isys *isys) +{ + struct syscom_queue_config *queue_configs; + struct ipu7_bus_device *adev = isys->adev; + struct device *dev = &adev->auxdev.dev; + struct ipu7_insys_config *isys_config; + struct ipu7_syscom_context *syscom; + dma_addr_t isys_config_dma_addr; + unsigned int i, num_queues; + u32 freq; + u8 major; + int ret; + + /* Allocate and init syscom context. */ + syscom = devm_kzalloc(dev, sizeof(struct ipu7_syscom_context), + GFP_KERNEL); + if (!syscom) + return -ENOMEM; + + adev->syscom = syscom; + syscom->num_input_queues = IPU_INSYS_MAX_INPUT_QUEUES; + syscom->num_output_queues = IPU_INSYS_MAX_OUTPUT_QUEUES; + num_queues = syscom->num_input_queues + syscom->num_output_queues; + queue_configs = devm_kzalloc(dev, FW_QUEUE_CONFIG_SIZE(num_queues), + GFP_KERNEL); + if (!queue_configs) { + ipu7_fw_isys_release(isys); + return -ENOMEM; + } + syscom->queue_configs = queue_configs; + queue_configs[IPU_INSYS_OUTPUT_MSG_QUEUE].max_capacity = + IPU_ISYS_SIZE_RECV_QUEUE; + queue_configs[IPU_INSYS_OUTPUT_MSG_QUEUE].token_size_in_bytes = + sizeof(struct ipu7_insys_resp); + queue_configs[IPU_INSYS_OUTPUT_LOG_QUEUE].max_capacity = + IPU_ISYS_SIZE_LOG_QUEUE; + queue_configs[IPU_INSYS_OUTPUT_LOG_QUEUE].token_size_in_bytes = + sizeof(struct ipu7_insys_resp); + queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].max_capacity = 0; + queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].token_size_in_bytes = 0; + + queue_configs[IPU_INSYS_INPUT_DEV_QUEUE].max_capacity = + IPU_ISYS_MAX_STREAMS; + queue_configs[IPU_INSYS_INPUT_DEV_QUEUE].token_size_in_bytes = + sizeof(struct ipu7_insys_send_queue_token); + + for (i = IPU_INSYS_INPUT_MSG_QUEUE; i < num_queues; i++) { + queue_configs[i].max_capacity = IPU_ISYS_SIZE_SEND_QUEUE; + queue_configs[i].token_size_in_bytes = + sizeof(struct ipu7_insys_send_queue_token); + } + + /* Allocate ISYS subsys config. */ + isys_config = ipu7_dma_alloc(adev, sizeof(struct ipu7_insys_config), + &isys_config_dma_addr, GFP_KERNEL, 0); + if (!isys_config) { + dev_err(dev, "Failed to allocate isys subsys config.\n"); + ipu7_fw_isys_release(isys); + return -ENOMEM; + } + isys->subsys_config = isys_config; + isys->subsys_config_dma_addr = isys_config_dma_addr; + memset(isys_config, 0, sizeof(struct ipu7_insys_config)); + isys_config->logger_config.use_source_severity = 0; + isys_config->logger_config.use_channels_enable_bitmask = 1; + isys_config->logger_config.channels_enable_bitmask = + LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK; + isys_config->logger_config.hw_printf_buffer_base_addr = 0U; + isys_config->logger_config.hw_printf_buffer_size_bytes = 0U; + isys_config->wdt_config.wdt_timer1_us = 0; + isys_config->wdt_config.wdt_timer2_us = 0; + ret = ipu_buttress_get_isys_freq(adev->isp, &freq); + if (ret) { + dev_err(dev, "Failed to get ISYS frequency.\n"); + ipu7_fw_isys_release(isys); + return ret; + } + + ipu7_dma_sync_single(adev, isys_config_dma_addr, + sizeof(struct ipu7_insys_config)); + + major = is_ipu8(adev->isp->hw_ver) ? 2U : 1U; + ret = ipu7_boot_init_boot_config(adev, queue_configs, num_queues, + freq, isys_config_dma_addr, major); + if (ret) + ipu7_fw_isys_release(isys); + + return ret; +} + +void ipu7_fw_isys_release(struct ipu7_isys *isys) +{ + struct ipu7_bus_device *adev = isys->adev; + + ipu7_boot_release_boot_config(adev); + if (isys->subsys_config) { + ipu7_dma_free(adev, + sizeof(struct ipu7_insys_config), + isys->subsys_config, + isys->subsys_config_dma_addr, 0); + isys->subsys_config = NULL; + isys->subsys_config_dma_addr = 0; + } +} + +int ipu7_fw_isys_open(struct ipu7_isys *isys) +{ + return ipu7_boot_start_fw(isys->adev); +} + +int ipu7_fw_isys_close(struct ipu7_isys *isys) +{ + return ipu7_boot_stop_fw(isys->adev); +} + +struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys) +{ + return (struct ipu7_insys_resp *) + ipu7_syscom_get_token(isys->adev->syscom, + IPU_INSYS_OUTPUT_MSG_QUEUE); +} + +void ipu7_fw_isys_put_resp(struct ipu7_isys *isys) +{ + ipu7_syscom_put_token(isys->adev->syscom, IPU_INSYS_OUTPUT_MSG_QUEUE); +} + +void ipu7_fw_isys_dump_stream_cfg(struct device *dev, + struct ipu7_insys_stream_cfg *cfg) +{ + unsigned int i; + + dev_dbg(dev, "---------------------------\n"); + dev_dbg(dev, "IPU_FW_ISYS_STREAM_CFG_DATA\n"); + + dev_dbg(dev, ".port id %d\n", cfg->port_id); + dev_dbg(dev, ".vc %d\n", cfg->vc); + dev_dbg(dev, ".nof_input_pins = %d\n", cfg->nof_input_pins); + dev_dbg(dev, ".nof_output_pins = %d\n", cfg->nof_output_pins); + dev_dbg(dev, ".stream_msg_map = 0x%x\n", cfg->stream_msg_map); + + for (i = 0; i < cfg->nof_input_pins; i++) { + dev_dbg(dev, ".input_pin[%d]:\n", i); + dev_dbg(dev, "\t.dt = 0x%0x\n", + cfg->input_pins[i].dt); + dev_dbg(dev, "\t.disable_mipi_unpacking = %d\n", + cfg->input_pins[i].disable_mipi_unpacking); + dev_dbg(dev, "\t.dt_rename_mode = %d\n", + cfg->input_pins[i].dt_rename_mode); + dev_dbg(dev, "\t.mapped_dt = 0x%0x\n", + cfg->input_pins[i].mapped_dt); + dev_dbg(dev, "\t.input_res = %d x %d\n", + cfg->input_pins[i].input_res.width, + cfg->input_pins[i].input_res.height); + dev_dbg(dev, "\t.sync_msg_map = 0x%x\n", + cfg->input_pins[i].sync_msg_map); + } + + for (i = 0; i < cfg->nof_output_pins; i++) { + dev_dbg(dev, ".output_pin[%d]:\n", i); + dev_dbg(dev, "\t.input_pin_id = %d\n", + cfg->output_pins[i].input_pin_id); + dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride); + dev_dbg(dev, "\t.send_irq = %d\n", + cfg->output_pins[i].send_irq); + dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft); + + dev_dbg(dev, "\t.link.buffer_lines = %d\n", + cfg->output_pins[i].link.buffer_lines); + dev_dbg(dev, "\t.link.foreign_key = %d\n", + cfg->output_pins[i].link.foreign_key); + dev_dbg(dev, "\t.link.granularity_pointer_update = %d\n", + cfg->output_pins[i].link.granularity_pointer_update); + dev_dbg(dev, "\t.link.msg_link_streaming_mode = %d\n", + cfg->output_pins[i].link.msg_link_streaming_mode); + dev_dbg(dev, "\t.link.pbk_id = %d\n", + cfg->output_pins[i].link.pbk_id); + dev_dbg(dev, "\t.link.pbk_slot_id = %d\n", + cfg->output_pins[i].link.pbk_slot_id); + dev_dbg(dev, "\t.link.dest = %d\n", + cfg->output_pins[i].link.dest); + dev_dbg(dev, "\t.link.use_sw_managed = %d\n", + cfg->output_pins[i].link.use_sw_managed); + dev_dbg(dev, "\t.link.is_snoop = %d\n", + cfg->output_pins[i].link.is_snoop); + + dev_dbg(dev, "\t.crop.line_top = %d\n", + cfg->output_pins[i].crop.line_top); + dev_dbg(dev, "\t.crop.line_bottom = %d\n", + cfg->output_pins[i].crop.line_bottom); + + dev_dbg(dev, "\t.dpcm_enable = %d\n", + cfg->output_pins[i].dpcm.enable); + dev_dbg(dev, "\t.dpcm.type = %d\n", + cfg->output_pins[i].dpcm.type); + dev_dbg(dev, "\t.dpcm.predictor = %d\n", + cfg->output_pins[i].dpcm.predictor); + } + dev_dbg(dev, "---------------------------\n"); +} + +void ipu7_fw_isys_dump_frame_buff_set(struct device *dev, + struct ipu7_insys_buffset *buf, + unsigned int outputs) +{ + unsigned int i; + + dev_dbg(dev, "--------------------------\n"); + dev_dbg(dev, "IPU_ISYS_BUFF_SET\n"); + dev_dbg(dev, ".capture_msg_map = %d\n", buf->capture_msg_map); + dev_dbg(dev, ".frame_id = %d\n", buf->frame_id); + dev_dbg(dev, ".skip_frame = %d\n", buf->skip_frame); + + for (i = 0; i < outputs; i++) { + dev_dbg(dev, ".output_pin[%d]:\n", i); + dev_dbg(dev, "\t.user_token = %llx\n", + buf->output_pins[i].user_token); + dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr); + } + dev_dbg(dev, "---------------------------\n"); +} diff --git a/drivers/staging/media/ipu7/ipu7-fw-isys.h b/drivers/staging/media/ipu7/ipu7-fw-isys.h new file mode 100644 index 000000000000..b556feda6b08 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-fw-isys.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#ifndef IPU7_FW_ISYS_H +#define IPU7_FW_ISYS_H + +#include + +#include "abi/ipu7_fw_isys_abi.h" + +struct device; +struct ipu7_insys_buffset; +struct ipu7_insys_stream_cfg; +struct ipu7_isys; + +/* From here on type defines not coming from the ISYSAPI interface */ + +int ipu7_fw_isys_init(struct ipu7_isys *isys); +void ipu7_fw_isys_release(struct ipu7_isys *isys); +int ipu7_fw_isys_open(struct ipu7_isys *isys); +int ipu7_fw_isys_close(struct ipu7_isys *isys); + +void ipu7_fw_isys_dump_stream_cfg(struct device *dev, + struct ipu7_insys_stream_cfg *cfg); +void ipu7_fw_isys_dump_frame_buff_set(struct device *dev, + struct ipu7_insys_buffset *buf, + unsigned int outputs); +int ipu7_fw_isys_simple_cmd(struct ipu7_isys *isys, + const unsigned int stream_handle, u16 send_type); +int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys, + const unsigned int stream_handle, + void *cpu_mapped_buf, + dma_addr_t dma_mapped_buf, + size_t size, u16 send_type); +struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys); +void ipu7_fw_isys_put_resp(struct ipu7_isys *isys); +#endif diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c new file mode 100644 index 000000000000..4407750c7f40 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c @@ -0,0 +1,1034 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ipu7.h" +#include "ipu7-bus.h" +#include "ipu7-buttress.h" +#include "ipu7-isys.h" +#include "ipu7-isys-csi2.h" +#include "ipu7-isys-csi2-regs.h" +#include "ipu7-platform-regs.h" +#include "ipu7-isys-csi-phy.h" + +#define PORT_A 0U +#define PORT_B 1U +#define PORT_C 2U +#define PORT_D 3U + +#define N_DATA_IDS 8U +static DECLARE_BITMAP(data_ids, N_DATA_IDS); + +struct ddlcal_counter_ref_s { + u16 min_mbps; + u16 max_mbps; + + u16 ddlcal_counter_ref; +}; + +struct ddlcal_params { + u16 min_mbps; + u16 max_mbps; + u16 oa_lanex_hsrx_cdphy_sel_fast; + u16 ddlcal_max_phase; + u16 phase_bound; + u16 ddlcal_dll_fbk; + u16 ddlcal_ddl_coarse_bank; + u16 fjump_deskew; + u16 min_eye_opening_deskew; +}; + +struct i_thssettle_params { + u16 min_mbps; + u16 max_mbps; + u16 i_thssettle; +}; + + /* lane2 for 4l3t, lane1 for 2l2t */ +struct oa_lane_clk_div_params { + u16 min_mbps; + u16 max_mbps; + u16 oa_lane_hsrx_hs_clk_div; +}; + +struct cdr_fbk_cap_prog_params { + u16 min_mbps; + u16 max_mbps; + u16 val; +}; + +static const struct ddlcal_counter_ref_s table0[] = { + { 1500, 1999, 118 }, + { 2000, 2499, 157 }, + { 2500, 3499, 196 }, + { 3500, 4499, 274 }, + { 4500, 4500, 352 }, + { } +}; + +static const struct ddlcal_params table1[] = { + { 1500, 1587, 0, 143, 167, 17, 3, 4, 29 }, + { 1588, 1687, 0, 135, 167, 15, 3, 4, 27 }, + { 1688, 1799, 0, 127, 135, 15, 2, 4, 26 }, + { 1800, 1928, 0, 119, 135, 13, 2, 3, 24 }, + { 1929, 2076, 0, 111, 135, 13, 2, 3, 23 }, + { 2077, 2249, 0, 103, 135, 11, 2, 3, 21 }, + { 2250, 2454, 0, 95, 103, 11, 1, 3, 19 }, + { 2455, 2699, 0, 87, 103, 9, 1, 3, 18 }, + { 2700, 2999, 0, 79, 103, 9, 1, 2, 16 }, + { 3000, 3229, 0, 71, 71, 7, 1, 2, 15 }, + { 3230, 3599, 1, 87, 103, 9, 1, 3, 18 }, + { 3600, 3999, 1, 79, 103, 9, 1, 2, 16 }, + { 4000, 4499, 1, 71, 103, 7, 1, 2, 15 }, + { 4500, 4500, 1, 63, 71, 7, 0, 2, 13 }, + { } +}; + +static const struct i_thssettle_params table2[] = { + { 80, 124, 24 }, + { 125, 249, 20 }, + { 250, 499, 16 }, + { 500, 749, 14 }, + { 750, 1499, 13 }, + { 1500, 4500, 12 }, + { } +}; + +static const struct oa_lane_clk_div_params table6[] = { + { 80, 159, 0x1 }, + { 160, 319, 0x2 }, + { 320, 639, 0x3 }, + { 640, 1279, 0x4 }, + { 1280, 2560, 0x5 }, + { 2561, 4500, 0x6 }, + { } +}; + +static const struct cdr_fbk_cap_prog_params table7[] = { + { 80, 919, 0 }, + { 920, 1029, 1 }, + { 1030, 1169, 2 }, + { 1170, 1349, 3 }, + { 1350, 1589, 4 }, + { 1590, 1949, 5 }, + { 1950, 2499, 6 }, + { } +}; + +static void dwc_phy_write(struct ipu7_isys *isys, u32 id, u32 addr, u16 data) +{ + void __iomem *isys_base = isys->pdata->base; + void __iomem *base = isys_base + IS_IO_CDPHY_BASE(id); + + dev_dbg(&isys->adev->auxdev.dev, "phy write: reg 0x%zx = data 0x%04x", + base + addr - isys_base, data); + writew(data, base + addr); +} + +static u16 dwc_phy_read(struct ipu7_isys *isys, u32 id, u32 addr) +{ + void __iomem *isys_base = isys->pdata->base; + void __iomem *base = isys_base + IS_IO_CDPHY_BASE(id); + u16 data; + + data = readw(base + addr); + dev_dbg(&isys->adev->auxdev.dev, "phy read: reg 0x%zx = data 0x%04x", + base + addr - isys_base, data); + + return data; +} + +static void dwc_csi_write(struct ipu7_isys *isys, u32 id, u32 addr, u32 data) +{ + void __iomem *isys_base = isys->pdata->base; + void __iomem *base = isys_base + IS_IO_CSI2_HOST_BASE(id); + struct device *dev = &isys->adev->auxdev.dev; + + dev_dbg(dev, "csi write: reg 0x%zx = data 0x%08x", + base + addr - isys_base, data); + writel(data, base + addr); + dev_dbg(dev, "csi read: reg 0x%zx = data 0x%08x", + base + addr - isys_base, readl(base + addr)); +} + +static void gpreg_write(struct ipu7_isys *isys, u32 id, u32 addr, u32 data) +{ + void __iomem *isys_base = isys->pdata->base; + u32 gpreg = isys->pdata->ipdata->csi2.gpreg; + void __iomem *base = isys_base + gpreg + 0x1000 * id; + struct device *dev = &isys->adev->auxdev.dev; + + dev_dbg(dev, "gpreg write: reg 0x%zx = data 0x%08x", + base + addr - isys_base, data); + writel(data, base + addr); + dev_dbg(dev, "gpreg read: reg 0x%zx = data 0x%08x", + base + addr - isys_base, readl(base + addr)); +} + +static u32 dwc_csi_read(struct ipu7_isys *isys, u32 id, u32 addr) +{ + void __iomem *isys_base = isys->pdata->base; + void __iomem *base = isys_base + IS_IO_CSI2_HOST_BASE(id); + u32 data; + + data = readl(base + addr); + dev_dbg(&isys->adev->auxdev.dev, "csi read: reg 0x%zx = data 0x%x", + base + addr - isys_base, data); + + return data; +} + +static void dwc_phy_write_mask(struct ipu7_isys *isys, u32 id, u32 addr, + u16 val, u8 lo, u8 hi) +{ + u32 temp, mask; + + WARN_ON(lo > hi); + WARN_ON(hi > 15); + + mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi)); + temp = dwc_phy_read(isys, id, addr); + temp &= ~mask; + temp |= (val << lo) & mask; + dwc_phy_write(isys, id, addr, temp); +} + +static void dwc_csi_write_mask(struct ipu7_isys *isys, u32 id, u32 addr, + u32 val, u8 hi, u8 lo) +{ + u32 temp, mask; + + WARN_ON(lo > hi); + + mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi)); + temp = dwc_csi_read(isys, id, addr); + temp &= ~mask; + temp |= (val << lo) & mask; + dwc_csi_write(isys, id, addr, temp); +} + +static void ipu7_isys_csi_ctrl_cfg(struct ipu7_isys_csi2 *csi2) +{ + struct ipu7_isys *isys = csi2->isys; + struct device *dev = &isys->adev->auxdev.dev; + u32 id, lanes, phy_mode; + u32 val; + + id = csi2->port; + lanes = csi2->nlanes; + phy_mode = csi2->phy_mode; + dev_dbg(dev, "csi-%d controller init with %u lanes, phy mode %u", + id, lanes, phy_mode); + + val = dwc_csi_read(isys, id, VERSION); + dev_dbg(dev, "csi-%d controller version = 0x%x", id, val); + + /* num of active data lanes */ + dwc_csi_write(isys, id, N_LANES, lanes - 1); + dwc_csi_write(isys, id, CDPHY_MODE, phy_mode); + dwc_csi_write(isys, id, VC_EXTENSION, 0); + + /* only mask PHY_FATAL and PKT_FATAL interrupts */ + dwc_csi_write(isys, id, INT_MSK_PHY_FATAL, 0xff); + dwc_csi_write(isys, id, INT_MSK_PKT_FATAL, 0x3); + dwc_csi_write(isys, id, INT_MSK_PHY, 0x0); + dwc_csi_write(isys, id, INT_MSK_LINE, 0x0); + dwc_csi_write(isys, id, INT_MSK_BNDRY_FRAME_FATAL, 0x0); + dwc_csi_write(isys, id, INT_MSK_SEQ_FRAME_FATAL, 0x0); + dwc_csi_write(isys, id, INT_MSK_CRC_FRAME_FATAL, 0x0); + dwc_csi_write(isys, id, INT_MSK_PLD_CRC_FATAL, 0x0); + dwc_csi_write(isys, id, INT_MSK_DATA_ID, 0x0); + dwc_csi_write(isys, id, INT_MSK_ECC_CORRECTED, 0x0); +} + +static void ipu7_isys_csi_phy_reset(struct ipu7_isys *isys, u32 id) +{ + dwc_csi_write(isys, id, PHY_SHUTDOWNZ, 0); + dwc_csi_write(isys, id, DPHY_RSTZ, 0); + dwc_csi_write(isys, id, CSI2_RESETN, 0); + gpreg_write(isys, id, PHY_RESET, 0); + gpreg_write(isys, id, PHY_SHUTDOWN, 0); +} + +/* 8 Data ID monitors, each Data ID is composed by pair of VC and data type */ +static int __dids_config(struct ipu7_isys_csi2 *csi2, u32 id, u8 vc, u8 dt) +{ + struct ipu7_isys *isys = csi2->isys; + u32 reg, n; + u8 lo, hi; + int ret; + + dev_dbg(&isys->adev->auxdev.dev, "config CSI-%u with vc:%u dt:0x%02x\n", + id, vc, dt); + + dwc_csi_write(isys, id, VC_EXTENSION, 0x0); + n = find_first_zero_bit(data_ids, N_DATA_IDS); + if (n == N_DATA_IDS) + return -ENOSPC; + + ret = test_and_set_bit(n, data_ids); + if (ret) + return -EBUSY; + + reg = n < 4 ? DATA_IDS_VC_1 : DATA_IDS_VC_2; + lo = (n % 4) * 8; + hi = lo + 4; + dwc_csi_write_mask(isys, id, reg, vc & GENMASK(4, 0), hi, lo); + + reg = n < 4 ? DATA_IDS_1 : DATA_IDS_2; + lo = (n % 4) * 8; + hi = lo + 5; + dwc_csi_write_mask(isys, id, reg, dt & GENMASK(5, 0), hi, lo); + + return 0; +} + +static int ipu7_isys_csi_ctrl_dids_config(struct ipu7_isys_csi2 *csi2, u32 id) +{ + struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; + struct device *dev = &csi2->isys->adev->auxdev.dev; + struct v4l2_mbus_frame_desc desc; + struct v4l2_subdev *ext_sd; + struct media_pad *pad; + unsigned int i; + int ret; + + pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); + if (IS_ERR(pad)) { + dev_warn(dev, "can't get remote source pad of %s (%ld)\n", + csi2->asd.sd.name, PTR_ERR(pad)); + return PTR_ERR(pad); + } + + ext_sd = media_entity_to_v4l2_subdev(pad->entity); + if (WARN(!ext_sd, "Failed to get subdev for entity %s\n", + pad->entity->name)) + return -ENODEV; + + ret = v4l2_subdev_call(ext_sd, pad, get_frame_desc, pad->index, &desc); + if (ret) + return ret; + + if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + dev_warn(dev, "Unsupported frame descriptor type\n"); + return -EINVAL; + } + + for (i = 0; i < desc.num_entries; i++) { + desc_entry = &desc.entry[i]; + if (desc_entry->bus.csi2.vc < IPU7_NR_OF_CSI2_VC) { + ret = __dids_config(csi2, id, desc_entry->bus.csi2.vc, + desc_entry->bus.csi2.dt); + if (ret) + return ret; + } + } + + return 0; +} + +#define CDPHY_TIMEOUT 5000000U +static int ipu7_isys_phy_ready(struct ipu7_isys *isys, u32 id) +{ + void __iomem *isys_base = isys->pdata->base; + u32 gpreg_offset = isys->pdata->ipdata->csi2.gpreg; + void __iomem *gpreg = isys_base + gpreg_offset + 0x1000 * id; + struct device *dev = &isys->adev->auxdev.dev; + unsigned int i; + u32 phy_ready; + u32 reg, rext; + int ret; + + dev_dbg(dev, "waiting phy ready...\n"); + ret = readl_poll_timeout(gpreg + PHY_READY, phy_ready, + phy_ready & BIT(0) && phy_ready != ~0U, + 100, CDPHY_TIMEOUT); + dev_dbg(dev, "phy %u ready = 0x%08x\n", id, readl(gpreg + PHY_READY)); + dev_dbg(dev, "csi %u PHY_RX = 0x%08x\n", id, + dwc_csi_read(isys, id, PHY_RX)); + dev_dbg(dev, "csi %u PHY_STOPSTATE = 0x%08x\n", id, + dwc_csi_read(isys, id, PHY_STOPSTATE)); + dev_dbg(dev, "csi %u PHY_CAL = 0x%08x\n", id, + dwc_csi_read(isys, id, PHY_CAL)); + for (i = 0; i < 4U; i++) { + reg = CORE_DIG_DLANE_0_R_HS_RX_0 + (i * 0x400U); + dev_dbg(dev, "phy %u DLANE%u skewcal = 0x%04x\n", + id, i, dwc_phy_read(isys, id, reg)); + } + dev_dbg(dev, "phy %u DDLCAL = 0x%04x\n", id, + dwc_phy_read(isys, id, PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5)); + dev_dbg(dev, "phy %u TERMCAL = 0x%04x\n", id, + dwc_phy_read(isys, id, PPI_R_TERMCAL_DEBUG_0)); + dev_dbg(dev, "phy %u LPDCOCAL = 0x%04x\n", id, + dwc_phy_read(isys, id, PPI_R_LPDCOCAL_DEBUG_RB)); + dev_dbg(dev, "phy %u HSDCOCAL = 0x%04x\n", id, + dwc_phy_read(isys, id, PPI_R_HSDCOCAL_DEBUG_RB)); + dev_dbg(dev, "phy %u LPDCOCAL_VT = 0x%04x\n", id, + dwc_phy_read(isys, id, PPI_R_LPDCOCAL_DEBUG_VT)); + + if (!ret) { + if (id) { + dev_dbg(dev, "ignore phy %u rext\n", id); + return 0; + } + + rext = dwc_phy_read(isys, id, + CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15) & 0xfU; + dev_dbg(dev, "phy %u rext value = %u\n", id, rext); + isys->phy_rext_cal = (rext ? rext : 5); + + return 0; + } + + dev_err(dev, "wait phy ready timeout!\n"); + + return ret; +} + +static int lookup_table1(u64 mbps) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(table1); i++) { + if (mbps >= table1[i].min_mbps && mbps <= table1[i].max_mbps) + return i; + } + + return -ENXIO; +} + +static const u16 deskew_fine_mem[] = { + 0x0404, 0x040c, 0x0414, 0x041c, + 0x0423, 0x0429, 0x0430, 0x043a, + 0x0445, 0x044a, 0x0450, 0x045a, + 0x0465, 0x0469, 0x0472, 0x047a, + 0x0485, 0x0489, 0x0490, 0x049a, + 0x04a4, 0x04ac, 0x04b4, 0x04bc, + 0x04c4, 0x04cc, 0x04d4, 0x04dc, + 0x04e4, 0x04ec, 0x04f4, 0x04fc, + 0x0504, 0x050c, 0x0514, 0x051c, + 0x0523, 0x0529, 0x0530, 0x053a, + 0x0545, 0x054a, 0x0550, 0x055a, + 0x0565, 0x0569, 0x0572, 0x057a, + 0x0585, 0x0589, 0x0590, 0x059a, + 0x05a4, 0x05ac, 0x05b4, 0x05bc, + 0x05c4, 0x05cc, 0x05d4, 0x05dc, + 0x05e4, 0x05ec, 0x05f4, 0x05fc, + 0x0604, 0x060c, 0x0614, 0x061c, + 0x0623, 0x0629, 0x0632, 0x063a, + 0x0645, 0x064a, 0x0650, 0x065a, + 0x0665, 0x0669, 0x0672, 0x067a, + 0x0685, 0x0689, 0x0690, 0x069a, + 0x06a4, 0x06ac, 0x06b4, 0x06bc, + 0x06c4, 0x06cc, 0x06d4, 0x06dc, + 0x06e4, 0x06ec, 0x06f4, 0x06fc, + 0x0704, 0x070c, 0x0714, 0x071c, + 0x0723, 0x072a, 0x0730, 0x073a, + 0x0745, 0x074a, 0x0750, 0x075a, + 0x0765, 0x0769, 0x0772, 0x077a, + 0x0785, 0x0789, 0x0790, 0x079a, + 0x07a4, 0x07ac, 0x07b4, 0x07bc, + 0x07c4, 0x07cc, 0x07d4, 0x07dc, + 0x07e4, 0x07ec, 0x07f4, 0x07fc, +}; + +static void ipu7_isys_dphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + bool aggregation, u64 mbps) +{ + u16 hsrxval0 = 0; + u16 hsrxval1 = 0; + u16 hsrxval2 = 0; + int index; + u16 reg; + u16 val; + u32 i; + + dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, 0, 0, 9); + if (mbps > 1500) + dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, + 40, 0, 7); + else + dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, + 104, 0, 7); + + dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 80, 0, 7); + dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_0, 191, 0, 9); + dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_7, 34, 7, 12); + dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_1, 38, 8, 15); + dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 4, 12, 15); + dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 2, 10, 11); + dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 1, 8, 8); + dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 38, 0, 7); + dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 1, 9, 9); + dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_4, 10, 0, 9); + dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_6, 20, 0, 9); + dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_7, 19, 0, 6); + + for (i = 0; i < ARRAY_SIZE(table0); i++) { + if (mbps >= table0[i].min_mbps && mbps <= table0[i].max_mbps) { + dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_3, + table0[i].ddlcal_counter_ref, + 0, 9); + break; + } + } + + index = lookup_table1(mbps); + if (index >= 0) { + dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_1, + table1[index].phase_bound, 0, 7); + dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_5, + table1[index].ddlcal_dll_fbk, 4, 9); + dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_5, + table1[index].ddlcal_ddl_coarse_bank, 0, 3); + + reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8; + val = table1[index].oa_lanex_hsrx_cdphy_sel_fast; + for (i = 0; i < lanes + 1; i++) + dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, + 12, 12); + } + + reg = CORE_DIG_DLANE_0_RW_LP_0; + for (i = 0; i < lanes; i++) + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11); + + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2, + 0, 0, 0); + if (!is_ipu7(isys->adev->isp->hw_ver) || + id == PORT_B || id == PORT_C) { + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, + 1, 0, 0); + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, + 0, 0, 0); + } else { + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, + 0, 0, 0); + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, + 1, 0, 0); + } + + if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2, + 0, 0, 0); + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2, + 0, 0, 0); + } + + dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_6, 1, 0, 2); + dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_6, 1, 3, 5); + + reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12; + val = (mbps > 1500) ? 0 : 1; + for (i = 0; i < lanes + 1; i++) { + dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); + dwc_phy_write_mask(isys, id, reg + (i * 0x400), !val, 3, 3); + } + + reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13; + val = (mbps > 1500) ? 0 : 1; + for (i = 0; i < lanes + 1; i++) { + dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); + dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 3, 3); + } + + if (!is_ipu7(isys->adev->isp->hw_ver) || id == PORT_B || id == PORT_C) + reg = CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9; + else + reg = CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9; + + for (i = 0; i < ARRAY_SIZE(table6); i++) { + if (mbps >= table6[i].min_mbps && mbps <= table6[i].max_mbps) { + dwc_phy_write_mask(isys, id, reg, + table6[i].oa_lane_hsrx_hs_clk_div, + 5, 7); + break; + } + } + + if (aggregation) { + dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_0, 1, + 1, 1); + + reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15; + dwc_phy_write_mask(isys, id, reg, 3, 3, 4); + + val = (id == PORT_A) ? 3 : 0; + reg = CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15; + dwc_phy_write_mask(isys, id, reg, val, 3, 4); + + reg = CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15; + dwc_phy_write_mask(isys, id, reg, 3, 3, 4); + } + + dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_HS_RX_0, 28, 0, 7); + dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_HS_RX_7, 6, 0, 7); + + reg = CORE_DIG_DLANE_0_RW_HS_RX_0; + for (i = 0; i < ARRAY_SIZE(table2); i++) { + if (mbps >= table2[i].min_mbps && mbps <= table2[i].max_mbps) { + u8 j; + + for (j = 0; j < lanes; j++) + dwc_phy_write_mask(isys, id, reg + (j * 0x400), + table2[i].i_thssettle, + 8, 15); + break; + } + } + + /* deskew */ + for (i = 0; i < lanes; i++) { + reg = CORE_DIG_DLANE_0_RW_CFG_1; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), + ((mbps > 1500) ? 0x1 : 0x2), 2, 3); + + reg = CORE_DIG_DLANE_0_RW_HS_RX_2; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), + ((mbps > 2500) ? 0 : 1), 15, 15); + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 13, 13); + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 7, 9, 12); + + reg = CORE_DIG_DLANE_0_RW_LP_0; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 12, 15); + + reg = CORE_DIG_DLANE_0_RW_LP_2; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 0); + + reg = CORE_DIG_DLANE_0_RW_HS_RX_1; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 16, 0, 7); + + reg = CORE_DIG_DLANE_0_RW_HS_RX_3; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 2); + index = lookup_table1(mbps); + if (index >= 0) { + val = table1[index].fjump_deskew; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, + 3, 8); + } + + reg = CORE_DIG_DLANE_0_RW_HS_RX_4; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 150, 0, 15); + + reg = CORE_DIG_DLANE_0_RW_HS_RX_5; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 7); + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 8, 15); + + reg = CORE_DIG_DLANE_0_RW_HS_RX_6; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 7); + index = lookup_table1(mbps); + if (index >= 0) { + val = table1[index].min_eye_opening_deskew; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, + 8, 15); + } + reg = CORE_DIG_DLANE_0_RW_HS_RX_7; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 13, 13); + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 15, 15); + + reg = CORE_DIG_DLANE_0_RW_HS_RX_9; + index = lookup_table1(mbps); + if (index >= 0) { + val = table1[index].ddlcal_max_phase; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), + val, 0, 7); + } + } + + dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_LP_0, 1, 12, 15); + dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_LP_2, 0, 0, 0); + + for (i = 0; i < ARRAY_SIZE(deskew_fine_mem); i++) + dwc_phy_write_mask(isys, id, CORE_DIG_COMMON_RW_DESKEW_FINE_MEM, + deskew_fine_mem[i], 0, 15); + + if (mbps > 1500) { + hsrxval0 = 4; + hsrxval2 = 3; + } + + if (mbps > 2500) + hsrxval1 = 2; + + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9, + hsrxval0, 0, 2); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9, + hsrxval0, 0, 2); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9, + hsrxval0, 0, 2); + if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9, + hsrxval0, 0, 2); + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9, + hsrxval0, 0, 2); + } + + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9, + hsrxval1, 3, 4); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9, + hsrxval1, 3, 4); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9, + hsrxval1, 3, 4); + if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9, + hsrxval1, 3, 4); + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9, + hsrxval1, 3, 4); + } + + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15, + hsrxval2, 0, 2); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15, + hsrxval2, 0, 2); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15, + hsrxval2, 0, 2); + if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15, + hsrxval2, 0, 2); + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15, + hsrxval2, 0, 2); + } + + /* force and override rext */ + if (isys->phy_rext_cal && id) { + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8, + isys->phy_rext_cal, 0, 3); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, + 1, 11, 11); + } +} + +static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + bool aggregation, u64 mbps) +{ + u8 trios = 2; + u16 coarse_target; + u16 deass_thresh; + u16 delay_thresh; + u16 reset_thresh; + u16 cap_prog = 6U; + u16 reg; + u16 val; + u32 i; + u32 r; + + if (is_ipu7p5(isys->adev->isp->hw_ver)) + val = 0x15; + else + val = 0x155; + + if (is_ipu7(isys->adev->isp->hw_ver)) + trios = 3; + + dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, val, 0, 9); + dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, 104, 0, 7); + dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 16, 0, 7); + + reg = CORE_DIG_CLANE_0_RW_LP_0; + for (i = 0; i < trios; i++) + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11); + + val = (mbps > 900U) ? 1U : 0U; + for (i = 0; i < trios; i++) { + reg = CORE_DIG_CLANE_0_RW_HS_RX_0; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 0, 0); + dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); + + reg = CORE_DIG_CLANE_0_RW_HS_RX_1; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15); + + reg = CORE_DIG_CLANE_0_RW_HS_RX_5; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15); + + reg = CORE_DIG_CLANE_0_RW_HS_RX_6; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 10, 0, 15); + } + + /* + * Below 900Msps, always use the same value. + * The formula is suitable for data rate 80-3500Msps. + * Timebase (us) = 1, DIV = 32, TDDL (UI) = 0.5 + */ + if (mbps >= 80U) + coarse_target = DIV_ROUND_UP_ULL(mbps, 16) - 1; + else + coarse_target = 56; + + for (i = 0; i < trios; i++) { + reg = CORE_DIG_CLANE_0_RW_HS_RX_2 + i * 0x400; + dwc_phy_write_mask(isys, id, reg, coarse_target, 0, 15); + } + + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2, 1, 0, 0); + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, 0, 0, 0); + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, 1, 0, 0); + + if (!is_ipu7p5(isys->adev->isp->hw_ver) && lanes == 4) { + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2, + 1, 0, 0); + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2, + 0, 0, 0); + } + + for (i = 0; i < trios; i++) { + reg = CORE_DIG_RW_TRIO0_0 + i * 0x400; + dwc_phy_write_mask(isys, id, reg, 1, 6, 8); + dwc_phy_write_mask(isys, id, reg, 1, 3, 5); + dwc_phy_write_mask(isys, id, reg, 2, 0, 2); + } + + deass_thresh = (u16)div_u64_rem(7 * 1000 * 6, mbps * 5U, &r) + 1; + if (r != 0) + deass_thresh++; + + reg = CORE_DIG_RW_TRIO0_2; + for (i = 0; i < trios; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, + deass_thresh, 0, 7); + + delay_thresh = + ((224U - (9U * 7U)) * 1000U) / (5U * mbps) - 7U; + + if (delay_thresh < 1) + delay_thresh = 1; + + reg = CORE_DIG_RW_TRIO0_1; + for (i = 0; i < trios; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, + delay_thresh, 0, 15); + + reset_thresh = (u16)div_u64_rem(2U * 5U * mbps, 7U * 1000U, &r); + if (!r) + reset_thresh--; + + if (reset_thresh < 1) + reset_thresh = 1; + + reg = CORE_DIG_RW_TRIO0_0; + for (i = 0; i < trios; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, + reset_thresh, 9, 11); + + reg = CORE_DIG_CLANE_0_RW_LP_0; + for (i = 0; i < trios; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, 1, 12, 15); + + reg = CORE_DIG_CLANE_0_RW_LP_2; + for (i = 0; i < trios; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, 0, 0, 0); + + reg = CORE_DIG_CLANE_0_RW_HS_RX_0; + for (i = 0; i < trios; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, 12, 2, 6); + + for (i = 0; i < ARRAY_SIZE(table7); i++) { + if (mbps >= table7[i].min_mbps && mbps <= table7[i].max_mbps) { + cap_prog = table7[i].val; + break; + } + } + + for (i = 0; i < (lanes + 1); i++) { + reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 + 0x400 * i; + dwc_phy_write_mask(isys, id, reg, 4U, 0, 2); + dwc_phy_write_mask(isys, id, reg, 0U, 3, 4); + + reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i; + dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12); + } +} + +static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + bool aggregation) +{ + struct device *dev = &isys->adev->auxdev.dev; + u32 phy_mode; + s64 link_freq; + u64 mbps; + + if (aggregation) + link_freq = ipu7_isys_csi2_get_link_freq(&isys->csi2[0]); + else + link_freq = ipu7_isys_csi2_get_link_freq(&isys->csi2[id]); + + if (link_freq < 0) { + dev_err(dev, "get link freq failed (%lld)\n", link_freq); + return link_freq; + } + + mbps = div_u64(link_freq, 500000); + dev_dbg(dev, "config phy %u with lanes %u aggregation %d mbps %lld\n", + id, lanes, aggregation, mbps); + + dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_10, 48, 0, 7); + dwc_phy_write_mask(isys, id, CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2, + 1, 12, 13); + dwc_phy_write_mask(isys, id, CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0, + 63, 2, 7); + dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_STARTUP_1_1, + 563, 0, 11); + dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_2, 5, 0, 7); + /* bypass the RCAL state (bit6) */ + if (aggregation && id != PORT_A) + dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_2, 0x45, + 0, 7); + + dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_6, 39, 0, 7); + dwc_phy_write_mask(isys, id, PPI_CALIBCTRL_RW_COMMON_BG_0, 500, 0, 8); + dwc_phy_write_mask(isys, id, PPI_RW_TERMCAL_CFG_0, 38, 0, 6); + dwc_phy_write_mask(isys, id, PPI_RW_OFFSETCAL_CFG_0, 7, 0, 4); + dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TIMEBASE, 153, 0, 9); + dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_NREF, 800, 0, 10); + dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_NREF_RANGE, 27, 0, 4); + dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TWAIT_CONFIG, 47, 0, 8); + dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TWAIT_CONFIG, 127, 9, 15); + dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 47, 7, 15); + dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 27, 2, 6); + dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 3, 0, 1); + dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_COARSE_CFG, 1, 0, 1); + dwc_phy_write_mask(isys, id, PPI_RW_COMMON_CFG, 3, 0, 1); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0, + 0, 10, 10); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1, + 1, 10, 10); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1, + 0, 15, 15); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3, + 3, 8, 9); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0, + 0, 15, 15); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6, + 7, 12, 14); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, + 0, 8, 10); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5, + 0, 8, 8); + + if (aggregation) + phy_mode = isys->csi2[0].phy_mode; + else + phy_mode = isys->csi2[id].phy_mode; + + if (phy_mode == PHY_MODE_DPHY) { + ipu7_isys_dphy_config(isys, id, lanes, aggregation, mbps); + } else if (phy_mode == PHY_MODE_CPHY) { + ipu7_isys_cphy_config(isys, id, lanes, aggregation, mbps); + } else { + dev_err(dev, "unsupported phy mode %d!\n", + isys->csi2[id].phy_mode); + } + + return 0; +} + +int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2) +{ + struct ipu7_isys *isys = csi2->isys; + u32 lanes = csi2->nlanes; + bool aggregation = false; + u32 id = csi2->port; + int ret; + + /* lanes remapping for aggregation (port AB) mode */ + if (!is_ipu7(isys->adev->isp->hw_ver) && lanes > 2 && id == PORT_A) { + aggregation = true; + lanes = 2; + } + + ipu7_isys_csi_phy_reset(isys, id); + gpreg_write(isys, id, PHY_CLK_LANE_CONTROL, 0x1); + gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0x2); + gpreg_write(isys, id, PHY_LANE_CONTROL_EN, (1U << lanes) - 1U); + gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0xf); + gpreg_write(isys, id, PHY_MODE, csi2->phy_mode); + + /* config PORT_B if aggregation mode */ + if (aggregation) { + ipu7_isys_csi_phy_reset(isys, PORT_B); + gpreg_write(isys, PORT_B, PHY_CLK_LANE_CONTROL, 0x0); + gpreg_write(isys, PORT_B, PHY_LANE_CONTROL_EN, 0x3); + gpreg_write(isys, PORT_B, PHY_CLK_LANE_FORCE_CONTROL, 0x2); + gpreg_write(isys, PORT_B, PHY_LANE_FORCE_CONTROL, 0xf); + gpreg_write(isys, PORT_B, PHY_MODE, csi2->phy_mode); + } + + ipu7_isys_csi_ctrl_cfg(csi2); + ipu7_isys_csi_ctrl_dids_config(csi2, id); + + ret = ipu7_isys_phy_config(isys, id, lanes, aggregation); + if (ret < 0) + return ret; + + gpreg_write(isys, id, PHY_RESET, 1); + gpreg_write(isys, id, PHY_SHUTDOWN, 1); + dwc_csi_write(isys, id, DPHY_RSTZ, 1); + dwc_csi_write(isys, id, PHY_SHUTDOWNZ, 1); + dwc_csi_write(isys, id, CSI2_RESETN, 1); + + ret = ipu7_isys_phy_ready(isys, id); + if (ret < 0) + return ret; + + gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0); + gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0); + + /* config PORT_B if aggregation mode */ + if (aggregation) { + ret = ipu7_isys_phy_config(isys, PORT_B, 2, aggregation); + if (ret < 0) + return ret; + + gpreg_write(isys, PORT_B, PHY_RESET, 1); + gpreg_write(isys, PORT_B, PHY_SHUTDOWN, 1); + dwc_csi_write(isys, PORT_B, DPHY_RSTZ, 1); + dwc_csi_write(isys, PORT_B, PHY_SHUTDOWNZ, 1); + dwc_csi_write(isys, PORT_B, CSI2_RESETN, 1); + ret = ipu7_isys_phy_ready(isys, PORT_B); + if (ret < 0) + return ret; + + gpreg_write(isys, PORT_B, PHY_LANE_FORCE_CONTROL, 0); + gpreg_write(isys, PORT_B, PHY_CLK_LANE_FORCE_CONTROL, 0); + } + + return 0; +} + +void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2) +{ + struct ipu7_isys *isys = csi2->isys; + + ipu7_isys_csi_phy_reset(isys, csi2->port); + if (!is_ipu7(isys->adev->isp->hw_ver) && + csi2->nlanes > 2U && csi2->port == PORT_A) + ipu7_isys_csi_phy_reset(isys, PORT_B); +} diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.h b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.h new file mode 100644 index 000000000000..dfdcb61540c4 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#ifndef IPU7_ISYS_CSI_PHY_H +#define IPU7_ISYS_CSI_PHY_H + +struct ipu7_isys; + +#define PHY_MODE_DPHY 0U +#define PHY_MODE_CPHY 1U + +int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2); +void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2); +#endif diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi2-regs.h b/drivers/staging/media/ipu7/ipu7-isys-csi2-regs.h new file mode 100644 index 000000000000..aad52c44a005 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-isys-csi2-regs.h @@ -0,0 +1,1197 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 - 2025 Intel Corporation + */ + +#ifndef IPU7_ISYS_CSI2_REG_H +#define IPU7_ISYS_CSI2_REG_H + +/* IS main regs base */ +#define IS_MAIN_BASE 0x240000 +#define IS_MAIN_S2B_BASE (IS_MAIN_BASE + 0x22000) +#define IS_MAIN_B2O_BASE (IS_MAIN_BASE + 0x26000) +#define IS_MAIN_ISD_M0_BASE (IS_MAIN_BASE + 0x2b000) +#define IS_MAIN_ISD_M1_BASE (IS_MAIN_BASE + 0x2b100) +#define IS_MAIN_ISD_INT_BASE (IS_MAIN_BASE + 0x2b200) +#define IS_MAIN_GDA_BASE (IS_MAIN_BASE + 0x32000) +#define IS_MAIN_GPREGS_MAIN_BASE (IS_MAIN_BASE + 0x32500) +#define IS_MAIN_IRQ_CTRL_BASE (IS_MAIN_BASE + 0x32700) +#define IS_MAIN_PWM_CTRL_BASE (IS_MAIN_BASE + 0x32b00) + +#define S2B_IRQ_COMMON_0_CTL_STATUS (IS_MAIN_S2B_BASE + 0x1c) +#define S2B_IRQ_COMMON_0_CTL_CLEAR (IS_MAIN_S2B_BASE + 0x20) +#define S2B_IRQ_COMMON_0_CTL_ENABLE (IS_MAIN_S2B_BASE + 0x24) +#define S2B_IID_IRQ_CTL_STATUS(iid) (IS_MAIN_S2B_BASE + 0x94 + \ + 0x100 * (iid)) + +#define B2O_IRQ_COMMON_0_CTL_STATUS (IS_MAIN_B2O_BASE + 0x30) +#define B2O_IRQ_COMMON_0_CTL_CLEAR (IS_MAIN_B2O_BASE + 0x34) +#define B2O_IRQ_COMMON_0_CTL_ENABLE (IS_MAIN_B2O_BASE + 0x38) +#define B2O_IID_IRQ_CTL_STATUS(oid) (IS_MAIN_B2O_BASE + 0x3dc + \ + 0x200 * (oid)) + +#define ISD_M0_IRQ_CTL_STATUS (IS_MAIN_ISD_M0_BASE + 0x1c) +#define ISD_M0_IRQ_CTL_CLEAR (IS_MAIN_ISD_M0_BASE + 0x20) +#define ISD_M0_IRQ_CTL_ENABLE (IS_MAIN_ISD_M0_BASE + 0x24) + +#define ISD_M1_IRQ_CTL_STATUS (IS_MAIN_ISD_M1_BASE + 0x1c) +#define ISD_M1_IRQ_CTL_CLEAR (IS_MAIN_ISD_M1_BASE + 0x20) +#define ISD_M1_IRQ_CTL_ENABLE (IS_MAIN_ISD_M1_BASE + 0x24) + +#define ISD_INT_IRQ_CTL_STATUS (IS_MAIN_ISD_INT_BASE + 0x1c) +#define ISD_INT_IRQ_CTL_CLEAR (IS_MAIN_ISD_INT_BASE + 0x20) +#define ISD_INT_IRQ_CTL_ENABLE (IS_MAIN_ISD_INT_BASE + 0x24) + +#define GDA_IRQ_CTL_STATUS (IS_MAIN_GDA_BASE + 0x1c) +#define GDA_IRQ_CTL_CLEAR (IS_MAIN_GDA_BASE + 0x20) +#define GDA_IRQ_CTL_ENABLE (IS_MAIN_GDA_BASE + 0x24) + +#define IS_MAIN_IRQ_CTL_EDGE IS_MAIN_IRQ_CTRL_BASE +#define IS_MAIN_IRQ_CTL_MASK (IS_MAIN_IRQ_CTRL_BASE + 0x4) +#define IS_MAIN_IRQ_CTL_STATUS (IS_MAIN_IRQ_CTRL_BASE + 0x8) +#define IS_MAIN_IRQ_CTL_CLEAR (IS_MAIN_IRQ_CTRL_BASE + 0xc) +#define IS_MAIN_IRQ_CTL_ENABLE (IS_MAIN_IRQ_CTRL_BASE + 0x10) +#define IS_MAIN_IRQ_CTL_LEVEL_NOT_PULSE (IS_MAIN_IRQ_CTRL_BASE + 0x14) + +/* IS IO regs base */ +#define IS_PHY_NUM 4U +#define IS_IO_BASE 0x280000 + +/* dwc csi cdphy registers */ +#define IS_IO_CDPHY_BASE(i) (IS_IO_BASE + 0x10000 * (i)) +#define PPI_STARTUP_RW_COMMON_DPHY_0 0x1800 +#define PPI_STARTUP_RW_COMMON_DPHY_1 0x1802 +#define PPI_STARTUP_RW_COMMON_DPHY_2 0x1804 +#define PPI_STARTUP_RW_COMMON_DPHY_3 0x1806 +#define PPI_STARTUP_RW_COMMON_DPHY_4 0x1808 +#define PPI_STARTUP_RW_COMMON_DPHY_5 0x180a +#define PPI_STARTUP_RW_COMMON_DPHY_6 0x180c +#define PPI_STARTUP_RW_COMMON_DPHY_7 0x180e +#define PPI_STARTUP_RW_COMMON_DPHY_8 0x1810 +#define PPI_STARTUP_RW_COMMON_DPHY_9 0x1812 +#define PPI_STARTUP_RW_COMMON_DPHY_A 0x1814 +#define PPI_STARTUP_RW_COMMON_DPHY_10 0x1820 +#define PPI_STARTUP_RW_COMMON_STARTUP_1_1 0x1822 +#define PPI_STARTUP_RW_COMMON_STARTUP_1_2 0x1824 +#define PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_0 0x1840 +#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_1 0x1842 +#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_2 0x1844 +#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_3 0x1846 +#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_4 0x1848 +#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5 0x184a +#define PPI_CALIBCTRL_RW_COMMON_BG_0 0x184c +#define PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_7 0x184e +#define PPI_CALIBCTRL_RW_ADC_CFG_0 0x1850 +#define PPI_CALIBCTRL_RW_ADC_CFG_1 0x1852 +#define PPI_CALIBCTRL_R_ADC_DEBUG 0x1854 +#define PPI_RW_LPDCOCAL_TOP_OVERRIDE 0x1c00 +#define PPI_RW_LPDCOCAL_TIMEBASE 0x1c02 +#define PPI_RW_LPDCOCAL_NREF 0x1c04 +#define PPI_RW_LPDCOCAL_NREF_RANGE 0x1c06 +#define PPI_RW_LPDCOCAL_NREF_TRIGGER_MAN 0x1c08 +#define PPI_RW_LPDCOCAL_TWAIT_CONFIG 0x1c0a +#define PPI_RW_LPDCOCAL_VT_CONFIG 0x1c0c +#define PPI_R_LPDCOCAL_DEBUG_RB 0x1c0e +#define PPI_RW_LPDCOCAL_COARSE_CFG 0x1c10 +#define PPI_R_LPDCOCAL_DEBUG_COARSE_RB 0x1c12 +#define PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_0_RB 0x1c14 +#define PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_1_RB 0x1c16 +#define PPI_R_LPDCOCAL_DEBUG_COARSE_FWORD_RB 0x1c18 +#define PPI_R_LPDCOCAL_DEBUG_MEASURE_CURR_ERROR 0x1c1a +#define PPI_R_LPDCOCAL_DEBUG_MEASURE_LAST_ERROR 0x1c1c +#define PPI_R_LPDCOCAL_DEBUG_VT 0x1c1e +#define PPI_RW_LB_TIMEBASE_CONFIG 0x1c20 +#define PPI_RW_LB_STARTCMU_CONFIG 0x1c22 +#define PPI_R_LBPULSE_COUNTER_RB 0x1c24 +#define PPI_R_LB_START_CMU_RB 0x1c26 +#define PPI_RW_LB_DPHY_BURST_START 0x1c28 +#define PPI_RW_LB_CPHY_BURST_START 0x1c2a +#define PPI_RW_DDLCAL_CFG_0 0x1c40 +#define PPI_RW_DDLCAL_CFG_1 0x1c42 +#define PPI_RW_DDLCAL_CFG_2 0x1c44 +#define PPI_RW_DDLCAL_CFG_3 0x1c46 +#define PPI_RW_DDLCAL_CFG_4 0x1c48 +#define PPI_RW_DDLCAL_CFG_5 0x1c4a +#define PPI_RW_DDLCAL_CFG_6 0x1c4c +#define PPI_RW_DDLCAL_CFG_7 0x1c4e +#define PPI_R_DDLCAL_DEBUG_0 0x1c50 +#define PPI_R_DDLCAL_DEBUG_1 0x1c52 +#define PPI_RW_PARITY_TEST 0x1c60 +#define PPI_RW_STARTUP_OVR_0 0x1c62 +#define PPI_RW_STARTUP_STATE_OVR_1 0x1c64 +#define PPI_RW_DTB_SELECTOR 0x1c66 +#define PPI_RW_DPHY_CLK_SPARE 0x1c6a +#define PPI_RW_COMMON_CFG 0x1c6c +#define PPI_RW_TERMCAL_CFG_0 0x1c80 +#define PPI_R_TERMCAL_DEBUG_0 0x1c82 +#define PPI_RW_TERMCAL_CTRL_0 0x1c84 +#define PPI_RW_OFFSETCAL_CFG_0 0x1ca0 +#define PPI_R_OFFSETCAL_DEBUG_LANE0 0x1ca2 +#define PPI_R_OFFSETCAL_DEBUG_LANE1 0x1ca4 +#define PPI_R_OFFSETCAL_DEBUG_LANE2 0x1ca6 +#define PPI_R_OFFSETCAL_DEBUG_LANE3 0x1ca8 +#define PPI_R_OFFSETCAL_DEBUG_LANE4 0x1caa +#define PPI_RW_HSDCOCAL_CFG_O 0x1d00 +#define PPI_RW_HSDCOCAL_CFG_1 0x1d02 +#define PPI_RW_HSDCOCAL_CFG_2 0x1d04 +#define PPI_RW_HSDCOCAL_CFG_3 0x1d06 +#define PPI_RW_HSDCOCAL_CFG_4 0x1d08 +#define PPI_RW_HSDCOCAL_CFG_5 0x1d0a +#define PPI_RW_HSDCOCAL_CFG_6 0x1d0c +#define PPI_RW_HSDCOCAL_CFG_7 0x1d0e +#define PPI_RW_HSDCOCAL_CFG_8 0x1d10 +#define PPI_R_HSDCOCAL_DEBUG_RB 0x1d12 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_0 0x2000 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_1 0x2002 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_2 0x2004 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_3 0x2006 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_4 0x2008 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_5 0x200a +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_6 0x200c +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_7 0x200e +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_8 0x2010 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_9 0x2012 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_10 0x2014 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_11 0x2016 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_12 0x2018 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_13 0x201a +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_14 0x201c +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_15 0x201e +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_0 0x2020 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_1 0x2022 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_2 0x2024 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_3 0x2026 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_4 0x2028 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_5 0x202a +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_6 0x202c +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_7 0x202e +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_8 0x2030 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_9 0x2032 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_10 0x2034 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_11 0x2036 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_12 0x2038 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_13 0x203a +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_14 0x203c +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_15 0x203e +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_0 0x2040 +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_1 0x2042 +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2 0x2044 +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_3 0x2046 +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_4 0x2048 +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_5 0x204a +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_6 0x204c +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 0x204e +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8 0x2050 +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 0x2052 +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_10 0x2054 +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_11 0x2056 +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12 0x2058 +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13 0x205a +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_14 0x205c +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15 0x205e +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_0 0x2060 +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_1 0x2062 +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_2 0x2064 +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_3 0x2066 +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_4 0x2068 +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_5 0x206a +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_6 0x206c +#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_7 0x206e +#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_8 0x2070 +#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_9 0x2072 +#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_10 0x2074 +#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_11 0x2076 +#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_12 0x2078 +#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_13 0x207a +#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_14 0x207c +#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_15 0x207e +#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_0 0x2080 +#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_1 0x2082 +#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_2 0x2084 +#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_3 0x2086 +#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_4 0x2088 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_0 0x20a0 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_1 0x20a2 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_2 0x20a4 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_3 0x20a6 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_4 0x20a8 +#define CORE_DIG_RW_TRIO0_0 0x2100 +#define CORE_DIG_RW_TRIO0_1 0x2102 +#define CORE_DIG_RW_TRIO0_2 0x2104 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_0 0x2400 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_1 0x2402 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_2 0x2404 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_3 0x2406 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_4 0x2408 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_5 0x240a +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_6 0x240c +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_7 0x240e +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_8 0x2410 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_9 0x2412 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_10 0x2414 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_11 0x2416 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_12 0x2418 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_13 0x241a +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_14 0x241c +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_15 0x241e +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_0 0x2420 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_1 0x2422 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_2 0x2424 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_3 0x2426 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_4 0x2428 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_5 0x242a +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_6 0x242c +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_7 0x242e +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_8 0x2430 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_9 0x2432 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_10 0x2434 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_11 0x2436 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_12 0x2438 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_13 0x243a +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_14 0x243c +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_15 0x243e +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_0 0x2440 +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_1 0x2442 +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2 0x2444 +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_3 0x2446 +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_4 0x2448 +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_5 0x244a +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_6 0x244c +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_7 0x244e +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_8 0x2450 +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9 0x2452 +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_10 0x2454 +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_11 0x2456 +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_12 0x2458 +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_13 0x245a +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_14 0x245c +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15 0x245e +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_0 0x2460 +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_1 0x2462 +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_2 0x2464 +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_3 0x2466 +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_4 0x2468 +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_5 0x246a +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_6 0x246c +#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_7 0x246e +#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_8 0x2470 +#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_9 0x2472 +#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_10 0x2474 +#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_11 0x2476 +#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_12 0x2478 +#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_13 0x247a +#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_14 0x247c +#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_15 0x247e +#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_0 0x2480 +#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_1 0x2482 +#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_2 0x2484 +#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_3 0x2486 +#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_4 0x2488 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_0 0x24a0 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_1 0x24a2 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_2 0x24a4 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_3 0x24a6 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_4 0x24a8 +#define CORE_DIG_RW_TRIO1_0 0x2500 +#define CORE_DIG_RW_TRIO1_1 0x2502 +#define CORE_DIG_RW_TRIO1_2 0x2504 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_0 0x2800 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_1 0x2802 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_2 0x2804 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_3 0x2806 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_4 0x2808 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_5 0x280a +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_6 0x280c +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_7 0x280e +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_8 0x2810 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_9 0x2812 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_10 0x2814 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_11 0x2816 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_12 0x2818 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_13 0x281a +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_14 0x281c +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_15 0x281e +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_0 0x2820 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_1 0x2822 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_2 0x2824 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_3 0x2826 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_4 0x2828 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_5 0x282a +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_6 0x282c +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_7 0x282e +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_8 0x2830 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_9 0x2832 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_10 0x2834 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_11 0x2836 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_12 0x2838 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_13 0x283a +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_14 0x283c +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_15 0x283e +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_0 0x2840 +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_1 0x2842 +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2 0x2844 +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_3 0x2846 +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_4 0x2848 +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_5 0x284a +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_6 0x284c +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_7 0x284e +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_8 0x2850 +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9 0x2852 +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_10 0x2854 +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_11 0x2856 +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_12 0x2858 +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_13 0x285a +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_14 0x285c +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15 0x285e +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_0 0x2860 +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_1 0x2862 +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_2 0x2864 +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_3 0x2866 +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_4 0x2868 +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_5 0x286a +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_6 0x286c +#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_7 0x286e +#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_8 0x2870 +#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_9 0x2872 +#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_10 0x2874 +#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_11 0x2876 +#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_12 0x2878 +#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_13 0x287a +#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_14 0x287c +#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_15 0x287e +#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_0 0x2880 +#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_1 0x2882 +#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_2 0x2884 +#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_3 0x2886 +#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_4 0x2888 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_0 0x28a0 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_1 0x28a2 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_2 0x28a4 +#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_3 0x28a6 +#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_4 0x28a8 +#define CORE_DIG_RW_TRIO2_0 0x2900 +#define CORE_DIG_RW_TRIO2_1 0x2902 +#define CORE_DIG_RW_TRIO2_2 0x2904 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_0 0x2c00 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_1 0x2c02 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_2 0x2c04 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_3 0x2c06 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_4 0x2c08 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_5 0x2c0a +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_6 0x2c0c +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_7 0x2c0e +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_8 0x2c10 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_9 0x2c12 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_10 0x2c14 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_11 0x2c16 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_12 0x2c18 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_13 0x2c1a +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_14 0x2c1c +#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_15 0x2c1e +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_0 0x2c40 +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_1 0x2c42 +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2 0x2c44 +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_3 0x2c46 +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_4 0x2c48 +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_5 0x2c4a +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_6 0x2c4c +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_7 0x2c4e +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_8 0x2c50 +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9 0x2c52 +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_10 0x2c54 +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_11 0x2c56 +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_12 0x2c58 +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_13 0x2c5a +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_14 0x2c5c +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15 0x2c5e +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_0 0x2c60 +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_1 0x2c62 +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_2 0x2c64 +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_3 0x2c66 +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_4 0x2c68 +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_5 0x2c6a +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_6 0x2c6c +#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_7 0x2c6e +#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_8 0x2c70 +#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_9 0x2c72 +#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_10 0x2c74 +#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_11 0x2c76 +#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_12 0x2c78 +#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_13 0x2c7a +#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_14 0x2c7c +#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_15 0x2c7e +#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_0 0x2c80 +#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_1 0x2c82 +#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_2 0x2c84 +#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_3 0x2c86 +#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_4 0x2c88 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_0 0x3040 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_1 0x3042 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2 0x3044 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_3 0x3046 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_4 0x3048 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_5 0x304a +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_6 0x304c +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_7 0x304e +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_8 0x3050 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9 0x3052 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_10 0x3054 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_11 0x3056 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_12 0x3058 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_13 0x305a +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_14 0x305c +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15 0x305e +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_0 0x3060 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_1 0x3062 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_2 0x3064 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_3 0x3066 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_4 0x3068 +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_5 0x306a +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_6 0x306c +#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_7 0x306e +#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_8 0x3070 +#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_9 0x3072 +#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_10 0x3074 +#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_11 0x3076 +#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_12 0x3078 +#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_13 0x307a +#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_14 0x307c +#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_15 0x307e +#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_0 0x3080 +#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_1 0x3082 +#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_2 0x3084 +#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_3 0x3086 +#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_4 0x3088 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_0 0x3400 +#define CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_1 0x3402 +#define CORE_DIG_IOCTRL_R_DPHY_PPI_CLK_OVR_0_2 0x3404 +#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_0 0x3800 +#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_1 0x3802 +#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_2 0x3804 +#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_3 0x3806 +#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_4 0x3808 +#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_5 0x380a +#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_6 0x380c +#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_7 0x380e +#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_8 0x3810 +#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_9 0x3812 +#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_10 0x3814 +#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_11 0x3816 +#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_12 0x3818 +#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_13 0x381a +#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_14 0x381c +#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_15 0x381e +#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_0 0x3820 +#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_1 0x3822 +#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_2 0x3824 +#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_3 0x3826 +#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_4 0x3828 +#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0 0x3840 +#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1 0x3842 +#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_2 0x3844 +#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3 0x3846 +#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_4 0x3848 +#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5 0x384a +#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6 0x384c +#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7 0x384e +#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8 0x3850 +#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_9 0x3852 +#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_10 0x3854 +#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_11 0x3856 +#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_12 0x3858 +#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_13 0x385a +#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_14 0x385c +#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15 0x385e +#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_3_0 0x3860 +#define CORE_DIG_RW_COMMON_0 0x3880 +#define CORE_DIG_RW_COMMON_1 0x3882 +#define CORE_DIG_RW_COMMON_2 0x3884 +#define CORE_DIG_RW_COMMON_3 0x3886 +#define CORE_DIG_RW_COMMON_4 0x3888 +#define CORE_DIG_RW_COMMON_5 0x388a +#define CORE_DIG_RW_COMMON_6 0x388c +#define CORE_DIG_RW_COMMON_7 0x388e +#define CORE_DIG_RW_COMMON_8 0x3890 +#define CORE_DIG_RW_COMMON_9 0x3892 +#define CORE_DIG_RW_COMMON_10 0x3894 +#define CORE_DIG_RW_COMMON_11 0x3896 +#define CORE_DIG_RW_COMMON_12 0x3898 +#define CORE_DIG_RW_COMMON_13 0x389a +#define CORE_DIG_RW_COMMON_14 0x389c +#define CORE_DIG_RW_COMMON_15 0x389e +#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0 0x39e0 +#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_1 0x39e2 +#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2 0x39e4 +#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_3 0x39e6 +#define CORE_DIG_COMMON_RW_DESKEW_FINE_MEM 0x3fe0 +#define CORE_DIG_COMMON_R_DESKEW_FINE_MEM 0x3fe2 +#define PPI_RW_DPHY_LANE0_LBERT_0 0x4000 +#define PPI_RW_DPHY_LANE0_LBERT_1 0x4002 +#define PPI_R_DPHY_LANE0_LBERT_0 0x4004 +#define PPI_R_DPHY_LANE0_LBERT_1 0x4006 +#define PPI_RW_DPHY_LANE0_SPARE 0x4008 +#define PPI_RW_DPHY_LANE1_LBERT_0 0x4400 +#define PPI_RW_DPHY_LANE1_LBERT_1 0x4402 +#define PPI_R_DPHY_LANE1_LBERT_0 0x4404 +#define PPI_R_DPHY_LANE1_LBERT_1 0x4406 +#define PPI_RW_DPHY_LANE1_SPARE 0x4408 +#define PPI_RW_DPHY_LANE2_LBERT_0 0x4800 +#define PPI_RW_DPHY_LANE2_LBERT_1 0x4802 +#define PPI_R_DPHY_LANE2_LBERT_0 0x4804 +#define PPI_R_DPHY_LANE2_LBERT_1 0x4806 +#define PPI_RW_DPHY_LANE2_SPARE 0x4808 +#define PPI_RW_DPHY_LANE3_LBERT_0 0x4c00 +#define PPI_RW_DPHY_LANE3_LBERT_1 0x4c02 +#define PPI_R_DPHY_LANE3_LBERT_0 0x4c04 +#define PPI_R_DPHY_LANE3_LBERT_1 0x4c06 +#define PPI_RW_DPHY_LANE3_SPARE 0x4c08 +#define CORE_DIG_DLANE_0_RW_CFG_0 0x6000 +#define CORE_DIG_DLANE_0_RW_CFG_1 0x6002 +#define CORE_DIG_DLANE_0_RW_CFG_2 0x6004 +#define CORE_DIG_DLANE_0_RW_LP_0 0x6080 +#define CORE_DIG_DLANE_0_RW_LP_1 0x6082 +#define CORE_DIG_DLANE_0_RW_LP_2 0x6084 +#define CORE_DIG_DLANE_0_R_LP_0 0x60a0 +#define CORE_DIG_DLANE_0_R_LP_1 0x60a2 +#define CORE_DIG_DLANE_0_R_HS_TX_0 0x60e0 +#define CORE_DIG_DLANE_0_RW_HS_RX_0 0x6100 +#define CORE_DIG_DLANE_0_RW_HS_RX_1 0x6102 +#define CORE_DIG_DLANE_0_RW_HS_RX_2 0x6104 +#define CORE_DIG_DLANE_0_RW_HS_RX_3 0x6106 +#define CORE_DIG_DLANE_0_RW_HS_RX_4 0x6108 +#define CORE_DIG_DLANE_0_RW_HS_RX_5 0x610a +#define CORE_DIG_DLANE_0_RW_HS_RX_6 0x610c +#define CORE_DIG_DLANE_0_RW_HS_RX_7 0x610e +#define CORE_DIG_DLANE_0_RW_HS_RX_8 0x6110 +#define CORE_DIG_DLANE_0_RW_HS_RX_9 0x6112 +#define CORE_DIG_DLANE_0_R_HS_RX_0 0x6120 +#define CORE_DIG_DLANE_0_R_HS_RX_1 0x6122 +#define CORE_DIG_DLANE_0_R_HS_RX_2 0x6124 +#define CORE_DIG_DLANE_0_R_HS_RX_3 0x6126 +#define CORE_DIG_DLANE_0_R_HS_RX_4 0x6128 +#define CORE_DIG_DLANE_0_RW_HS_TX_0 0x6200 +#define CORE_DIG_DLANE_0_RW_HS_TX_1 0x6202 +#define CORE_DIG_DLANE_0_RW_HS_TX_2 0x6204 +#define CORE_DIG_DLANE_0_RW_HS_TX_3 0x6206 +#define CORE_DIG_DLANE_0_RW_HS_TX_4 0x6208 +#define CORE_DIG_DLANE_0_RW_HS_TX_5 0x620a +#define CORE_DIG_DLANE_0_RW_HS_TX_6 0x620c +#define CORE_DIG_DLANE_0_RW_HS_TX_7 0x620e +#define CORE_DIG_DLANE_0_RW_HS_TX_8 0x6210 +#define CORE_DIG_DLANE_0_RW_HS_TX_9 0x6212 +#define CORE_DIG_DLANE_0_RW_HS_TX_10 0x6214 +#define CORE_DIG_DLANE_0_RW_HS_TX_11 0x6216 +#define CORE_DIG_DLANE_0_RW_HS_TX_12 0x6218 +#define CORE_DIG_DLANE_1_RW_CFG_0 0x6400 +#define CORE_DIG_DLANE_1_RW_CFG_1 0x6402 +#define CORE_DIG_DLANE_1_RW_CFG_2 0x6404 +#define CORE_DIG_DLANE_1_RW_LP_0 0x6480 +#define CORE_DIG_DLANE_1_RW_LP_1 0x6482 +#define CORE_DIG_DLANE_1_RW_LP_2 0x6484 +#define CORE_DIG_DLANE_1_R_LP_0 0x64a0 +#define CORE_DIG_DLANE_1_R_LP_1 0x64a2 +#define CORE_DIG_DLANE_1_R_HS_TX_0 0x64e0 +#define CORE_DIG_DLANE_1_RW_HS_RX_0 0x6500 +#define CORE_DIG_DLANE_1_RW_HS_RX_1 0x6502 +#define CORE_DIG_DLANE_1_RW_HS_RX_2 0x6504 +#define CORE_DIG_DLANE_1_RW_HS_RX_3 0x6506 +#define CORE_DIG_DLANE_1_RW_HS_RX_4 0x6508 +#define CORE_DIG_DLANE_1_RW_HS_RX_5 0x650a +#define CORE_DIG_DLANE_1_RW_HS_RX_6 0x650c +#define CORE_DIG_DLANE_1_RW_HS_RX_7 0x650e +#define CORE_DIG_DLANE_1_RW_HS_RX_8 0x6510 +#define CORE_DIG_DLANE_1_RW_HS_RX_9 0x6512 +#define CORE_DIG_DLANE_1_R_HS_RX_0 0x6520 +#define CORE_DIG_DLANE_1_R_HS_RX_1 0x6522 +#define CORE_DIG_DLANE_1_R_HS_RX_2 0x6524 +#define CORE_DIG_DLANE_1_R_HS_RX_3 0x6526 +#define CORE_DIG_DLANE_1_R_HS_RX_4 0x6528 +#define CORE_DIG_DLANE_1_RW_HS_TX_0 0x6600 +#define CORE_DIG_DLANE_1_RW_HS_TX_1 0x6602 +#define CORE_DIG_DLANE_1_RW_HS_TX_2 0x6604 +#define CORE_DIG_DLANE_1_RW_HS_TX_3 0x6606 +#define CORE_DIG_DLANE_1_RW_HS_TX_4 0x6608 +#define CORE_DIG_DLANE_1_RW_HS_TX_5 0x660a +#define CORE_DIG_DLANE_1_RW_HS_TX_6 0x660c +#define CORE_DIG_DLANE_1_RW_HS_TX_7 0x660e +#define CORE_DIG_DLANE_1_RW_HS_TX_8 0x6610 +#define CORE_DIG_DLANE_1_RW_HS_TX_9 0x6612 +#define CORE_DIG_DLANE_1_RW_HS_TX_10 0x6614 +#define CORE_DIG_DLANE_1_RW_HS_TX_11 0x6616 +#define CORE_DIG_DLANE_1_RW_HS_TX_12 0x6618 +#define CORE_DIG_DLANE_2_RW_CFG_0 0x6800 +#define CORE_DIG_DLANE_2_RW_CFG_1 0x6802 +#define CORE_DIG_DLANE_2_RW_CFG_2 0x6804 +#define CORE_DIG_DLANE_2_RW_LP_0 0x6880 +#define CORE_DIG_DLANE_2_RW_LP_1 0x6882 +#define CORE_DIG_DLANE_2_RW_LP_2 0x6884 +#define CORE_DIG_DLANE_2_R_LP_0 0x68a0 +#define CORE_DIG_DLANE_2_R_LP_1 0x68a2 +#define CORE_DIG_DLANE_2_R_HS_TX_0 0x68e0 +#define CORE_DIG_DLANE_2_RW_HS_RX_0 0x6900 +#define CORE_DIG_DLANE_2_RW_HS_RX_1 0x6902 +#define CORE_DIG_DLANE_2_RW_HS_RX_2 0x6904 +#define CORE_DIG_DLANE_2_RW_HS_RX_3 0x6906 +#define CORE_DIG_DLANE_2_RW_HS_RX_4 0x6908 +#define CORE_DIG_DLANE_2_RW_HS_RX_5 0x690a +#define CORE_DIG_DLANE_2_RW_HS_RX_6 0x690c +#define CORE_DIG_DLANE_2_RW_HS_RX_7 0x690e +#define CORE_DIG_DLANE_2_RW_HS_RX_8 0x6910 +#define CORE_DIG_DLANE_2_RW_HS_RX_9 0x6912 +#define CORE_DIG_DLANE_2_R_HS_RX_0 0x6920 +#define CORE_DIG_DLANE_2_R_HS_RX_1 0x6922 +#define CORE_DIG_DLANE_2_R_HS_RX_2 0x6924 +#define CORE_DIG_DLANE_2_R_HS_RX_3 0x6926 +#define CORE_DIG_DLANE_2_R_HS_RX_4 0x6928 +#define CORE_DIG_DLANE_2_RW_HS_TX_0 0x6a00 +#define CORE_DIG_DLANE_2_RW_HS_TX_1 0x6a02 +#define CORE_DIG_DLANE_2_RW_HS_TX_2 0x6a04 +#define CORE_DIG_DLANE_2_RW_HS_TX_3 0x6a06 +#define CORE_DIG_DLANE_2_RW_HS_TX_4 0x6a08 +#define CORE_DIG_DLANE_2_RW_HS_TX_5 0x6a0a +#define CORE_DIG_DLANE_2_RW_HS_TX_6 0x6a0c +#define CORE_DIG_DLANE_2_RW_HS_TX_7 0x6a0e +#define CORE_DIG_DLANE_2_RW_HS_TX_8 0x6a10 +#define CORE_DIG_DLANE_2_RW_HS_TX_9 0x6a12 +#define CORE_DIG_DLANE_2_RW_HS_TX_10 0x6a14 +#define CORE_DIG_DLANE_2_RW_HS_TX_11 0x6a16 +#define CORE_DIG_DLANE_2_RW_HS_TX_12 0x6a18 +#define CORE_DIG_DLANE_3_RW_CFG_0 0x6c00 +#define CORE_DIG_DLANE_3_RW_CFG_1 0x6c02 +#define CORE_DIG_DLANE_3_RW_CFG_2 0x6c04 +#define CORE_DIG_DLANE_3_RW_LP_0 0x6c80 +#define CORE_DIG_DLANE_3_RW_LP_1 0x6c82 +#define CORE_DIG_DLANE_3_RW_LP_2 0x6c84 +#define CORE_DIG_DLANE_3_R_LP_0 0x6ca0 +#define CORE_DIG_DLANE_3_R_LP_1 0x6ca2 +#define CORE_DIG_DLANE_3_R_HS_TX_0 0x6ce0 +#define CORE_DIG_DLANE_3_RW_HS_RX_0 0x6d00 +#define CORE_DIG_DLANE_3_RW_HS_RX_1 0x6d02 +#define CORE_DIG_DLANE_3_RW_HS_RX_2 0x6d04 +#define CORE_DIG_DLANE_3_RW_HS_RX_3 0x6d06 +#define CORE_DIG_DLANE_3_RW_HS_RX_4 0x6d08 +#define CORE_DIG_DLANE_3_RW_HS_RX_5 0x6d0a +#define CORE_DIG_DLANE_3_RW_HS_RX_6 0x6d0c +#define CORE_DIG_DLANE_3_RW_HS_RX_7 0x6d0e +#define CORE_DIG_DLANE_3_RW_HS_RX_8 0x6d10 +#define CORE_DIG_DLANE_3_RW_HS_RX_9 0x6d12 +#define CORE_DIG_DLANE_3_R_HS_RX_0 0x6d20 +#define CORE_DIG_DLANE_3_R_HS_RX_1 0x6d22 +#define CORE_DIG_DLANE_3_R_HS_RX_2 0x6d24 +#define CORE_DIG_DLANE_3_R_HS_RX_3 0x6d26 +#define CORE_DIG_DLANE_3_R_HS_RX_4 0x6d28 +#define CORE_DIG_DLANE_3_RW_HS_TX_0 0x6e00 +#define CORE_DIG_DLANE_3_RW_HS_TX_1 0x6e02 +#define CORE_DIG_DLANE_3_RW_HS_TX_2 0x6e04 +#define CORE_DIG_DLANE_3_RW_HS_TX_3 0x6e06 +#define CORE_DIG_DLANE_3_RW_HS_TX_4 0x6e08 +#define CORE_DIG_DLANE_3_RW_HS_TX_5 0x6e0a +#define CORE_DIG_DLANE_3_RW_HS_TX_6 0x6e0c +#define CORE_DIG_DLANE_3_RW_HS_TX_7 0x6e0e +#define CORE_DIG_DLANE_3_RW_HS_TX_8 0x6e10 +#define CORE_DIG_DLANE_3_RW_HS_TX_9 0x6e12 +#define CORE_DIG_DLANE_3_RW_HS_TX_10 0x6e14 +#define CORE_DIG_DLANE_3_RW_HS_TX_11 0x6e16 +#define CORE_DIG_DLANE_3_RW_HS_TX_12 0x6e18 +#define CORE_DIG_DLANE_CLK_RW_CFG_0 0x7000 +#define CORE_DIG_DLANE_CLK_RW_CFG_1 0x7002 +#define CORE_DIG_DLANE_CLK_RW_CFG_2 0x7004 +#define CORE_DIG_DLANE_CLK_RW_LP_0 0x7080 +#define CORE_DIG_DLANE_CLK_RW_LP_1 0x7082 +#define CORE_DIG_DLANE_CLK_RW_LP_2 0x7084 +#define CORE_DIG_DLANE_CLK_R_LP_0 0x70a0 +#define CORE_DIG_DLANE_CLK_R_LP_1 0x70a2 +#define CORE_DIG_DLANE_CLK_R_HS_TX_0 0x70e0 +#define CORE_DIG_DLANE_CLK_RW_HS_RX_0 0x7100 +#define CORE_DIG_DLANE_CLK_RW_HS_RX_1 0x7102 +#define CORE_DIG_DLANE_CLK_RW_HS_RX_2 0x7104 +#define CORE_DIG_DLANE_CLK_RW_HS_RX_3 0x7106 +#define CORE_DIG_DLANE_CLK_RW_HS_RX_4 0x7108 +#define CORE_DIG_DLANE_CLK_RW_HS_RX_5 0x710a +#define CORE_DIG_DLANE_CLK_RW_HS_RX_6 0x710c +#define CORE_DIG_DLANE_CLK_RW_HS_RX_7 0x710e +#define CORE_DIG_DLANE_CLK_RW_HS_RX_8 0x7110 +#define CORE_DIG_DLANE_CLK_RW_HS_RX_9 0x7112 +#define CORE_DIG_DLANE_CLK_R_HS_RX_0 0x7120 +#define CORE_DIG_DLANE_CLK_R_HS_RX_1 0x7122 +#define CORE_DIG_DLANE_CLK_R_HS_RX_2 0x7124 +#define CORE_DIG_DLANE_CLK_R_HS_RX_3 0x7126 +#define CORE_DIG_DLANE_CLK_R_HS_RX_4 0x7128 +#define CORE_DIG_DLANE_CLK_RW_HS_TX_0 0x7200 +#define CORE_DIG_DLANE_CLK_RW_HS_TX_1 0x7202 +#define CORE_DIG_DLANE_CLK_RW_HS_TX_2 0x7204 +#define CORE_DIG_DLANE_CLK_RW_HS_TX_3 0x7206 +#define CORE_DIG_DLANE_CLK_RW_HS_TX_4 0x7208 +#define CORE_DIG_DLANE_CLK_RW_HS_TX_5 0x720a +#define CORE_DIG_DLANE_CLK_RW_HS_TX_6 0x720c +#define CORE_DIG_DLANE_CLK_RW_HS_TX_7 0x720e +#define CORE_DIG_DLANE_CLK_RW_HS_TX_8 0x7210 +#define CORE_DIG_DLANE_CLK_RW_HS_TX_9 0x7212 +#define CORE_DIG_DLANE_CLK_RW_HS_TX_10 0x7214 +#define CORE_DIG_DLANE_CLK_RW_HS_TX_11 0x7216 +#define CORE_DIG_DLANE_CLK_RW_HS_TX_12 0x7218 +#define PPI_RW_CPHY_TRIO0_LBERT_0 0x8000 +#define PPI_RW_CPHY_TRIO0_LBERT_1 0x8002 +#define PPI_R_CPHY_TRIO0_LBERT_0 0x8004 +#define PPI_R_CPHY_TRIO0_LBERT_1 0x8006 +#define PPI_RW_CPHY_TRIO0_SPARE 0x8008 +#define PPI_RW_CPHY_TRIO1_LBERT_0 0x8400 +#define PPI_RW_CPHY_TRIO1_LBERT_1 0x8402 +#define PPI_R_CPHY_TRIO1_LBERT_0 0x8404 +#define PPI_R_CPHY_TRIO1_LBERT_1 0x8406 +#define PPI_RW_CPHY_TRIO1_SPARE 0x8408 +#define PPI_RW_CPHY_TRIO2_LBERT_0 0x8800 +#define PPI_RW_CPHY_TRIO2_LBERT_1 0x8802 +#define PPI_R_CPHY_TRIO2_LBERT_0 0x8804 +#define PPI_R_CPHY_TRIO2_LBERT_1 0x8806 +#define PPI_RW_CPHY_TRIO2_SPARE 0x8808 +#define CORE_DIG_CLANE_0_RW_CFG_0 0xa000 +#define CORE_DIG_CLANE_0_RW_CFG_2 0xa004 +#define CORE_DIG_CLANE_0_RW_LP_0 0xa080 +#define CORE_DIG_CLANE_0_RW_LP_1 0xa082 +#define CORE_DIG_CLANE_0_RW_LP_2 0xa084 +#define CORE_DIG_CLANE_0_R_LP_0 0xa0a0 +#define CORE_DIG_CLANE_0_R_LP_1 0xa0a2 +#define CORE_DIG_CLANE_0_RW_HS_RX_0 0xa100 +#define CORE_DIG_CLANE_0_RW_HS_RX_1 0xa102 +#define CORE_DIG_CLANE_0_RW_HS_RX_2 0xa104 +#define CORE_DIG_CLANE_0_RW_HS_RX_3 0xa106 +#define CORE_DIG_CLANE_0_RW_HS_RX_4 0xa108 +#define CORE_DIG_CLANE_0_RW_HS_RX_5 0xa10a +#define CORE_DIG_CLANE_0_RW_HS_RX_6 0xa10c +#define CORE_DIG_CLANE_0_R_RX_0 0xa120 +#define CORE_DIG_CLANE_0_R_RX_1 0xa122 +#define CORE_DIG_CLANE_0_R_TX_0 0xa124 +#define CORE_DIG_CLANE_0_R_RX_2 0xa126 +#define CORE_DIG_CLANE_0_R_RX_3 0xa128 +#define CORE_DIG_CLANE_0_RW_HS_TX_0 0xa200 +#define CORE_DIG_CLANE_0_RW_HS_TX_1 0xa202 +#define CORE_DIG_CLANE_0_RW_HS_TX_2 0xa204 +#define CORE_DIG_CLANE_0_RW_HS_TX_3 0xa206 +#define CORE_DIG_CLANE_0_RW_HS_TX_4 0xa208 +#define CORE_DIG_CLANE_0_RW_HS_TX_5 0xa20a +#define CORE_DIG_CLANE_0_RW_HS_TX_6 0xa20c +#define CORE_DIG_CLANE_0_RW_HS_TX_7 0xa20e +#define CORE_DIG_CLANE_0_RW_HS_TX_8 0xa210 +#define CORE_DIG_CLANE_0_RW_HS_TX_9 0xa212 +#define CORE_DIG_CLANE_0_RW_HS_TX_10 0xa214 +#define CORE_DIG_CLANE_0_RW_HS_TX_11 0xa216 +#define CORE_DIG_CLANE_0_RW_HS_TX_12 0xa218 +#define CORE_DIG_CLANE_0_RW_HS_TX_13 0xa21a +#define CORE_DIG_CLANE_1_RW_CFG_0 0xa400 +#define CORE_DIG_CLANE_1_RW_CFG_2 0xa404 +#define CORE_DIG_CLANE_1_RW_LP_0 0xa480 +#define CORE_DIG_CLANE_1_RW_LP_1 0xa482 +#define CORE_DIG_CLANE_1_RW_LP_2 0xa484 +#define CORE_DIG_CLANE_1_R_LP_0 0xa4a0 +#define CORE_DIG_CLANE_1_R_LP_1 0xa4a2 +#define CORE_DIG_CLANE_1_RW_HS_RX_0 0xa500 +#define CORE_DIG_CLANE_1_RW_HS_RX_1 0xa502 +#define CORE_DIG_CLANE_1_RW_HS_RX_2 0xa504 +#define CORE_DIG_CLANE_1_RW_HS_RX_3 0xa506 +#define CORE_DIG_CLANE_1_RW_HS_RX_4 0xa508 +#define CORE_DIG_CLANE_1_RW_HS_RX_5 0xa50a +#define CORE_DIG_CLANE_1_RW_HS_RX_6 0xa50c +#define CORE_DIG_CLANE_1_R_RX_0 0xa520 +#define CORE_DIG_CLANE_1_R_RX_1 0xa522 +#define CORE_DIG_CLANE_1_R_TX_0 0xa524 +#define CORE_DIG_CLANE_1_R_RX_2 0xa526 +#define CORE_DIG_CLANE_1_R_RX_3 0xa528 +#define CORE_DIG_CLANE_1_RW_HS_TX_0 0xa600 +#define CORE_DIG_CLANE_1_RW_HS_TX_1 0xa602 +#define CORE_DIG_CLANE_1_RW_HS_TX_2 0xa604 +#define CORE_DIG_CLANE_1_RW_HS_TX_3 0xa606 +#define CORE_DIG_CLANE_1_RW_HS_TX_4 0xa608 +#define CORE_DIG_CLANE_1_RW_HS_TX_5 0xa60a +#define CORE_DIG_CLANE_1_RW_HS_TX_6 0xa60c +#define CORE_DIG_CLANE_1_RW_HS_TX_7 0xa60e +#define CORE_DIG_CLANE_1_RW_HS_TX_8 0xa610 +#define CORE_DIG_CLANE_1_RW_HS_TX_9 0xa612 +#define CORE_DIG_CLANE_1_RW_HS_TX_10 0xa614 +#define CORE_DIG_CLANE_1_RW_HS_TX_11 0xa616 +#define CORE_DIG_CLANE_1_RW_HS_TX_12 0xa618 +#define CORE_DIG_CLANE_1_RW_HS_TX_13 0xa61a +#define CORE_DIG_CLANE_2_RW_CFG_0 0xa800 +#define CORE_DIG_CLANE_2_RW_CFG_2 0xa804 +#define CORE_DIG_CLANE_2_RW_LP_0 0xa880 +#define CORE_DIG_CLANE_2_RW_LP_1 0xa882 +#define CORE_DIG_CLANE_2_RW_LP_2 0xa884 +#define CORE_DIG_CLANE_2_R_LP_0 0xa8a0 +#define CORE_DIG_CLANE_2_R_LP_1 0xa8a2 +#define CORE_DIG_CLANE_2_RW_HS_RX_0 0xa900 +#define CORE_DIG_CLANE_2_RW_HS_RX_1 0xa902 +#define CORE_DIG_CLANE_2_RW_HS_RX_2 0xa904 +#define CORE_DIG_CLANE_2_RW_HS_RX_3 0xa906 +#define CORE_DIG_CLANE_2_RW_HS_RX_4 0xa908 +#define CORE_DIG_CLANE_2_RW_HS_RX_5 0xa90a +#define CORE_DIG_CLANE_2_RW_HS_RX_6 0xa90c +#define CORE_DIG_CLANE_2_R_RX_0 0xa920 +#define CORE_DIG_CLANE_2_R_RX_1 0xa922 +#define CORE_DIG_CLANE_2_R_TX_0 0xa924 +#define CORE_DIG_CLANE_2_R_RX_2 0xa926 +#define CORE_DIG_CLANE_2_R_RX_3 0xa928 +#define CORE_DIG_CLANE_2_RW_HS_TX_0 0xaa00 +#define CORE_DIG_CLANE_2_RW_HS_TX_1 0xaa02 +#define CORE_DIG_CLANE_2_RW_HS_TX_2 0xaa04 +#define CORE_DIG_CLANE_2_RW_HS_TX_3 0xaa06 +#define CORE_DIG_CLANE_2_RW_HS_TX_4 0xaa08 +#define CORE_DIG_CLANE_2_RW_HS_TX_5 0xaa0a +#define CORE_DIG_CLANE_2_RW_HS_TX_6 0xaa0c +#define CORE_DIG_CLANE_2_RW_HS_TX_7 0xaa0e +#define CORE_DIG_CLANE_2_RW_HS_TX_8 0xaa10 +#define CORE_DIG_CLANE_2_RW_HS_TX_9 0xaa12 +#define CORE_DIG_CLANE_2_RW_HS_TX_10 0xaa14 +#define CORE_DIG_CLANE_2_RW_HS_TX_11 0xaa16 +#define CORE_DIG_CLANE_2_RW_HS_TX_12 0xaa18 +#define CORE_DIG_CLANE_2_RW_HS_TX_13 0xaa1a + +/* dwc csi host controller registers */ +#define IS_IO_CSI2_HOST_BASE(i) (IS_IO_BASE + 0x40000 + \ + 0x2000 * (i)) +#define VERSION 0x0 +#define N_LANES 0x4 +#define CSI2_RESETN 0x8 +#define INT_ST_MAIN 0xc +#define DATA_IDS_1 0x10 +#define DATA_IDS_2 0x14 +#define CDPHY_MODE 0x1c +#define DATA_IDS_VC_1 0x30 +#define DATA_IDS_VC_2 0x34 +#define PHY_SHUTDOWNZ 0x40 +#define DPHY_RSTZ 0x44 +#define PHY_RX 0x48 +#define PHY_STOPSTATE 0x4c +#define PHY_TEST_CTRL0 0x50 +#define PHY_TEST_CTRL1 0x54 +#define PPI_PG_PATTERN_VRES 0x60 +#define PPI_PG_PATTERN_HRES 0x64 +#define PPI_PG_CONFIG 0x68 +#define PPI_PG_ENABLE 0x6c +#define PPI_PG_STATUS 0x70 +#define VC_EXTENSION 0xc8 +#define PHY_CAL 0xcc +#define INT_ST_PHY_FATAL 0xe0 +#define INT_MSK_PHY_FATAL 0xe4 +#define INT_FORCE_PHY_FATAL 0xe8 +#define INT_ST_PKT_FATAL 0xf0 +#define INT_MSK_PKT_FATAL 0xf4 +#define INT_FORCE_PKT_FATAL 0xf8 +#define INT_ST_PHY 0x110 +#define INT_MSK_PHY 0x114 +#define INT_FORCE_PHY 0x118 +#define INT_ST_LINE 0x130 +#define INT_MSK_LINE 0x134 +#define INT_FORCE_LINE 0x138 +#define INT_ST_BNDRY_FRAME_FATAL 0x280 +#define INT_MSK_BNDRY_FRAME_FATAL 0x284 +#define INT_FORCE_BNDRY_FRAME_FATAL 0x288 +#define INT_ST_SEQ_FRAME_FATAL 0x290 +#define INT_MSK_SEQ_FRAME_FATAL 0x294 +#define INT_FORCE_SEQ_FRAME_FATAL 0x298 +#define INT_ST_CRC_FRAME_FATAL 0x2a0 +#define INT_MSK_CRC_FRAME_FATAL 0x2a4 +#define INT_FORCE_CRC_FRAME_FATAL 0x2a8 +#define INT_ST_PLD_CRC_FATAL 0x2b0 +#define INT_MSK_PLD_CRC_FATAL 0x2b4 +#define INT_FORCE_PLD_CRC_FATAL 0x2b8 +#define INT_ST_DATA_ID 0x2c0 +#define INT_MSK_DATA_ID 0x2c4 +#define INT_FORCE_DATA_ID 0x2c8 +#define INT_ST_ECC_CORRECTED 0x2d0 +#define INT_MSK_ECC_CORRECTED 0x2d4 +#define INT_FORCE_ECC_CORRECTED 0x2d8 +#define SCRAMBLING 0x300 +#define SCRAMBLING_SEED1 0x304 +#define SCRAMBLING_SEED2 0x308 +#define SCRAMBLING_SEED3 0x30c +#define SCRAMBLING_SEED4 0x310 +#define SCRAMBLING 0x300 + +#define IS_IO_CSI2_ADPL_PORT_BASE(i) (IS_IO_BASE + 0x40800 + \ + 0x2000 * (i)) +#define CSI2_ADPL_INPUT_MODE 0x0 +#define CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN 0x4 +#define CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_ADDR 0x8 +#define CSI2_ADPL_CSI_RX_ERR_IRQ_STATUS 0xc +#define CSI2_ADPL_IRQ_CTL_COMMON_STATUS 0xa4 +#define CSI2_ADPL_IRQ_CTL_COMMON_CLEAR 0xa8 +#define CSI2_ADPL_IRQ_CTL_COMMON_ENABLE 0xac +#define CSI2_ADPL_IRQ_CTL_FS_STATUS 0xbc +#define CSI2_ADPL_IRQ_CTL_FS_CLEAR 0xc0 +#define CSI2_ADPL_IRQ_CTL_FS_ENABLE 0xc4 +#define CSI2_ADPL_IRQ_CTL_FE_STATUS 0xc8 +#define CSI2_ADPL_IRQ_CTL_FE_CLEAR 0xcc +#define CSI2_ADPL_IRQ_CTL_FE_ENABLE 0xd0 + +/* software control the legacy csi irq */ +#define IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(i) (IS_IO_BASE + 0x40c00 + \ + 0x2000 * (i)) +#define IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(i) (IS_IO_BASE + 0x40d00 + \ + 0x2000 * (i)) +#define IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE (IS_IO_BASE + 0x49000) +#define IS_IO_CSI2_IRQ_CTRL_BASE (IS_IO_BASE + 0x4e100) + +#define IRQ_CTL_EDGE 0x0 +#define IRQ_CTL_MASK 0x4 +#define IRQ_CTL_STATUS 0x8 +#define IRQ_CTL_CLEAR 0xc +#define IRQ_CTL_ENABLE 0x10 +/* FE irq for PTL */ +#define IRQ1_CTL_MASK 0x14 +#define IRQ1_CTL_STATUS 0x18 +#define IRQ1_CTL_CLEAR 0x1c +#define IRQ1_CTL_ENABLE 0x20 + +/* software to set the clock gate to use the port or mgc */ +#define IS_IO_GPREGS_BASE (IS_IO_BASE + 0x49200) +#define SRST_PORT_ARB 0x0 +#define SRST_MGC 0x4 +#define SRST_WIDTH_CONV 0x8 +#define SRST_CSI_IRQ 0xc +#define SRST_CSI_LEGACY_IRQ 0x10 +#define CLK_EN_TXCLKESC 0x14 +#define CLK_DIV_FACTOR_IS_CLK 0x18 +#define CLK_DIV_FACTOR_APB_CLK 0x1c +#define CSI_PORT_CLK_GATE 0x20 +#define CSI_PORTAB_AGGREGATION 0x24 +#define MGC_CLK_GATE 0x2c +#define CG_CTRL_BITS 0x3c +#define SPARE_RW 0xf8 +#define SPARE_RO 0xfc + +#define IS_IO_CSI2_MPF_PORT_BASE(i) (IS_IO_BASE + 0x53000 + \ + 0x1000 * (i)) +#define MPF_16_IRQ_CNTRL_STATUS 0x238 +#define MPF_16_IRQ_CNTRL_CLEAR 0x23c +#define MPF_16_IRQ_CNTRL_ENABLE 0x240 + +/* software config the phy */ +#define IS_IO_CSI2_GPREGS_BASE (IS_IO_BASE + 0x53400) +#define IPU8_IS_IO_CSI2_GPREGS_BASE (IS_IO_BASE + 0x40e00) +#define CSI_ADAPT_LAYER_SRST 0x0 +#define MPF_SRST_RST 0x4 +#define CSI_ERR_IRQ_CTRL_SRST 0x8 +#define CSI_SYNC_RC_SRST 0xc +#define CSI_CG_CTRL_BITS 0x10 +#define SOC_CSI2HOST_SELECT 0x14 +#define PHY_RESET 0x18 +#define PHY_SHUTDOWN 0x1c +#define PHY_MODE 0x20 +#define PHY_READY 0x24 +#define PHY_CLK_LANE_FORCE_CONTROL 0x28 +#define PHY_CLK_LANE_CONTROL 0x2c +#define PHY_CLK_LANE_STATUS 0x30 +#define PHY_LANE_RX_ESC_REQ 0x34 +#define PHY_LANE_RX_ESC_DATA 0x38 +#define PHY_LANE_TURNDISABLE 0x3c +#define PHY_LANE_DIRECTION 0x40 +#define PHY_LANE_FORCE_CONTROL 0x44 +#define PHY_LANE_CONTROL_EN 0x48 +#define PHY_LANE_CONTROL_DATAWIDTH 0x4c +#define PHY_LANE_STATUS 0x50 +#define PHY_LANE_ERR 0x54 +#define PHY_LANE_RXALP 0x58 +#define PHY_LANE_RXALP_NIBBLE 0x5c +#define PHY_PARITY_ERROR 0x60 +#define PHY_DEBUG_REGS_CLK_GATE_EN 0x64 +#define SPARE_RW 0xf8 +#define SPARE_RO 0xfc + +/* software not touch */ +#define PORT_ARB_BASE (IS_IO_BASE + 0x4e000) +#define PORT_ARB_IRQ_CTL_STATUS 0x4 +#define PORT_ARB_IRQ_CTL_CLEAR 0x8 +#define PORT_ARB_IRQ_CTL_ENABLE 0xc + +#define MGC_PPC 4U +#define MGC_DTYPE_RAW(i) (((i) - 8) / 2) +#define IS_IO_MGC_BASE (IS_IO_BASE + 0x48000) +#define MGC_KICK 0x0 +#define MGC_ASYNC_STOP 0x4 +#define MGC_PORT_OFFSET 0x100 +#define MGC_CSI_PORT_MAP(i) (0x8 + (i) * 0x4) +#define MGC_MG_PORT(i) (IS_IO_MGC_BASE + \ + (i) * MGC_PORT_OFFSET) +/* per mgc instance */ +#define MGC_MG_CSI_ADAPT_LAYER_TYPE 0x28 +#define MGC_MG_MODE 0x2c +#define MGC_MG_INIT_COUNTER 0x30 +#define MGC_MG_MIPI_VC 0x34 +#define MGC_MG_MIPI_DTYPES 0x38 +#define MGC_MG_MULTI_DTYPES_MODE 0x3c +#define MGC_MG_NOF_FRAMES 0x40 +#define MGC_MG_FRAME_DIM 0x44 +#define MGC_MG_HBLANK 0x48 +#define MGC_MG_VBLANK 0x4c +#define MGC_MG_TPG_MODE 0x50 +#define MGC_MG_TPG_R0 0x54 +#define MGC_MG_TPG_G0 0x58 +#define MGC_MG_TPG_B0 0x5c +#define MGC_MG_TPG_R1 0x60 +#define MGC_MG_TPG_G1 0x64 +#define MGC_MG_TPG_B1 0x68 +#define MGC_MG_TPG_FACTORS 0x6c +#define MGC_MG_TPG_MASKS 0x70 +#define MGC_MG_TPG_XY_MASK 0x74 +#define MGC_MG_TPG_TILE_DIM 0x78 +#define MGC_MG_PRBS_LFSR_INIT_0 0x7c +#define MGC_MG_PRBS_LFSR_INIT_1 0x80 +#define MGC_MG_SYNC_STOP_POINT 0x84 +#define MGC_MG_SYNC_STOP_POINT_LOC 0x88 +#define MGC_MG_ERR_INJECT 0x8c +#define MGC_MG_ERR_LOCATION 0x90 +#define MGC_MG_DTO_SPEED_CTRL_EN 0x94 +#define MGC_MG_DTO_SPEED_CTRL_INCR_VAL 0x98 +#define MGC_MG_HOR_LOC_STTS 0x9c +#define MGC_MG_VER_LOC_STTS 0xa0 +#define MGC_MG_FRAME_NUM_STTS 0xa4 +#define MGC_MG_BUSY_STTS 0xa8 +#define MGC_MG_STOPPED_STTS 0xac +/* tile width and height in pixels for Chess board and Color palette */ +#define MGC_TPG_TILE_WIDTH 64U +#define MGC_TPG_TILE_HEIGHT 64U + +#define IPU_CSI_PORT_A_ADDR_OFFSET 0x0 +#define IPU_CSI_PORT_B_ADDR_OFFSET 0x0 +#define IPU_CSI_PORT_C_ADDR_OFFSET 0x0 +#define IPU_CSI_PORT_D_ADDR_OFFSET 0x0 + +/* + * 0 - CSI RX Port 0 interrupt; + * 1 - MPF Port 0 interrupt; + * 2 - CSI RX Port 1 interrupt; + * 3 - MPF Port 1 interrupt; + * 4 - CSI RX Port 2 interrupt; + * 5 - MPF Port 2 interrupt; + * 6 - CSI RX Port 3 interrupt; + * 7 - MPF Port 3 interrupt; + * 8 - Port ARB FIFO 0 overflow; + * 9 - Port ARB FIFO 1 overflow; + * 10 - Port ARB FIFO 2 overflow; + * 11 - Port ARB FIFO 3 overflow; + * 12 - isys_cfgnoc_err_probe_intl; + * 13-15 - reserved + */ +#define IPU7_CSI_IS_IO_IRQ_MASK 0xffff + +/* Adapter layer irq */ +#define IPU7_CSI_ADPL_IRQ_MASK 0xffff + +/* sw irq from legacy irq control + * legacy irq status + * IPU7 + * 0 - CSI Port 0 error interrupt + * 1 - CSI Port 0 sync interrupt + * 2 - CSI Port 1 error interrupt + * 3 - CSI Port 1 sync interrupt + * 4 - CSI Port 2 error interrupt + * 5 - CSI Port 2 sync interrupt + * 6 - CSI Port 3 error interrupt + * 7 - CSI Port 3 sync interrupt + * IPU7P5 + * 0 - CSI Port 0 error interrupt + * 1 - CSI Port 0 fs interrupt + * 2 - CSI Port 0 fe interrupt + * 3 - CSI Port 1 error interrupt + * 4 - CSI Port 1 fs interrupt + * 5 - CSI Port 1 fe interrupt + * 6 - CSI Port 2 error interrupt + * 7 - CSI Port 2 fs interrupt + * 8 - CSI Port 2 fe interrupt + */ +#define IPU7_CSI_RX_LEGACY_IRQ_MASK 0x1ff + +/* legacy error status per port + * 0 - Error handler FIFO full; + * 1 - Reserved Short Packet encoding detected; + * 2 - Reserved Long Packet encoding detected; + * 3 - Received packet is too short (fewer data words than specified in header); + * 4 - Received packet is too long (more data words than specified in header); + * 5 - Short packet discarded due to errors; + * 6 - Long packet discarded due to errors; + * 7 - CSI Combo Rx interrupt; + * 8 - IDI CDC FIFO overflow; remaining bits are reserved and tied to 0; + */ +#define IPU7_CSI_RX_ERROR_IRQ_MASK 0xfff + +/* + * 0 - VC0 frame start received + * 1 - VC0 frame end received + * 2 - VC1 frame start received + * 3 - VC1 frame end received + * 4 - VC2 frame start received + * 5 - VC2 frame end received + * 6 - VC3 frame start received + * 7 - VC3 frame end received + * 8 - VC4 frame start received + * 9 - VC4 frame end received + * 10 - VC5 frame start received + * 11 - VC5 frame end received + * 12 - VC6 frame start received + * 13 - VC6 frame end received + * 14 - VC7 frame start received + * 15 - VC7 frame end received + * 16 - VC8 frame start received + * 17 - VC8 frame end received + * 18 - VC9 frame start received + * 19 - VC9 frame end received + * 20 - VC10 frame start received + * 21 - VC10 frame end received + * 22 - VC11 frame start received + * 23 - VC11 frame end received + * 24 - VC12 frame start received + * 25 - VC12 frame end received + * 26 - VC13 frame start received + * 27 - VC13 frame end received + * 28 - VC14 frame start received + * 29 - VC14 frame end received + * 30 - VC15 frame start received + * 31 - VC15 frame end received + */ + +#define IPU7_CSI_RX_SYNC_IRQ_MASK 0x0 +#define IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK 0x0 + +#define CSI_RX_NUM_ERRORS_IN_IRQ 12U +#define CSI_RX_NUM_SYNC_IN_IRQ 32U + +enum CSI_FE_MODE_TYPE { + CSI_FE_DPHY_MODE = 0, + CSI_FE_CPHY_MODE = 1, +}; + +enum CSI_FE_INPUT_MODE { + CSI_SENSOR_INPUT = 0, + CSI_MIPIGEN_INPUT = 1, +}; + +enum MGC_CSI_ADPL_TYPE { + MGC_MAPPED_2_LANES = 0, + MGC_MAPPED_4_LANES = 1, +}; + +enum CSI2HOST_SELECTION { + CSI2HOST_SEL_SOC = 0, + CSI2HOST_SEL_CSI2HOST = 1, +}; + +#define IPU7_ISYS_LEGACY_IRQ_CSI2(port) (0x3 << (port)) +#define IPU7P5_ISYS_LEGACY_IRQ_CSI2(port) (0x7 << (port)) + +/* ---------------------------------------------------------------- */ +#define CSI_REG_BASE 0x220000 +#define CSI_REG_BASE_PORT(id) ((id) * 0x1000) + +/* CSI Port General Purpose Registers */ +#define CSI_REG_PORT_GPREG_SRST 0x0 +#define CSI_REG_PORT_GPREG_CSI2_SLV_REG_SRST 0x4 +#define CSI_REG_PORT_GPREG_CSI2_PORT_CONTROL 0x8 + +#define CSI_RX_NUM_IRQ 32U + +#define IPU7_CSI_RX_SYNC_FS_VC 0x55555555 +#define IPU7_CSI_RX_SYNC_FE_VC 0xaaaaaaaa +#define IPU7P5_CSI_RX_SYNC_FS_VC 0xffff +#define IPU7P5_CSI_RX_SYNC_FE_VC 0xffff + +#endif /* IPU7_ISYS_CSI2_REG_H */ diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi2.c b/drivers/staging/media/ipu7/ipu7-isys-csi2.c new file mode 100644 index 000000000000..9c16ae9a0e5b --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-isys-csi2.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ipu7.h" +#include "ipu7-bus.h" +#include "ipu7-isys.h" +#include "ipu7-isys-csi2.h" +#include "ipu7-isys-csi2-regs.h" +#include "ipu7-isys-csi-phy.h" + +static const u32 csi2_supported_codes[] = { + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_RGB565_1X16, + MEDIA_BUS_FMT_RGB888_1X24, + MEDIA_BUS_FMT_UYVY8_1X16, + MEDIA_BUS_FMT_YUYV8_1X16, + MEDIA_BUS_FMT_YUYV10_1X20, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + 0, +}; + +s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2) +{ + struct media_pad *src_pad; + + src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); + if (IS_ERR(src_pad)) { + dev_err(&csi2->isys->adev->auxdev.dev, + "can't get source pad of %s (%ld)\n", + csi2->asd.sd.name, PTR_ERR(src_pad)); + return PTR_ERR(src_pad); + } + + return v4l2_get_link_freq(src_pad, 0, 0); +} + +static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); + struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd); + struct device *dev = &csi2->isys->adev->auxdev.dev; + + dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n", + sub->type, sub->id); + + switch (sub->type) { + case V4L2_EVENT_FRAME_SYNC: + return v4l2_event_subscribe(fh, sub, 10, NULL); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + +static const struct v4l2_subdev_core_ops csi2_sd_core_ops = { + .subscribe_event = csi2_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static void csi2_irq_enable(struct ipu7_isys_csi2 *csi2) +{ + struct ipu7_device *isp = csi2->isys->adev->isp; + unsigned int offset, mask; + + /* enable CSI2 legacy error irq */ + offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); + mask = IPU7_CSI_RX_ERROR_IRQ_MASK; + writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); + writel(mask, csi2->base + offset + IRQ_CTL_MASK); + writel(mask, csi2->base + offset + IRQ_CTL_ENABLE); + + /* enable CSI2 legacy sync irq */ + offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); + mask = IPU7_CSI_RX_SYNC_IRQ_MASK; + writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); + writel(mask, csi2->base + offset + IRQ_CTL_MASK); + writel(mask, csi2->base + offset + IRQ_CTL_ENABLE); + + mask = IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK; + if (!is_ipu7(isp->hw_ver)) { + writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR); + writel(mask, csi2->base + offset + IRQ1_CTL_MASK); + writel(mask, csi2->base + offset + IRQ1_CTL_ENABLE); + } +} + +static void csi2_irq_disable(struct ipu7_isys_csi2 *csi2) +{ + struct ipu7_device *isp = csi2->isys->adev->isp; + unsigned int offset, mask; + + /* disable CSI2 legacy error irq */ + offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); + mask = IPU7_CSI_RX_ERROR_IRQ_MASK; + writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); + writel(0, csi2->base + offset + IRQ_CTL_MASK); + writel(0, csi2->base + offset + IRQ_CTL_ENABLE); + + /* disable CSI2 legacy sync irq */ + offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); + mask = IPU7_CSI_RX_SYNC_IRQ_MASK; + writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); + writel(0, csi2->base + offset + IRQ_CTL_MASK); + writel(0, csi2->base + offset + IRQ_CTL_ENABLE); + + if (!is_ipu7(isp->hw_ver)) { + writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR); + writel(0, csi2->base + offset + IRQ1_CTL_MASK); + writel(0, csi2->base + offset + IRQ1_CTL_ENABLE); + } +} + +static void ipu7_isys_csi2_disable_stream(struct ipu7_isys_csi2 *csi2) +{ + struct ipu7_isys *isys = csi2->isys; + void __iomem *isys_base = isys->pdata->base; + + ipu7_isys_csi_phy_powerdown(csi2); + + writel(0x4, isys_base + IS_IO_GPREGS_BASE + CLK_DIV_FACTOR_APB_CLK); + csi2_irq_disable(csi2); +} + +static int ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 *csi2) +{ + struct ipu7_isys *isys = csi2->isys; + struct device *dev = &isys->adev->auxdev.dev; + void __iomem *isys_base = isys->pdata->base; + unsigned int port, nlanes, offset; + int ret; + + port = csi2->port; + nlanes = csi2->nlanes; + + offset = IS_IO_GPREGS_BASE; + writel(0x2, isys_base + offset + CLK_DIV_FACTOR_APB_CLK); + dev_dbg(dev, "port %u CLK_GATE = 0x%04x DIV_FACTOR_APB_CLK=0x%04x\n", + port, readl(isys_base + offset + CSI_PORT_CLK_GATE), + readl(isys_base + offset + CLK_DIV_FACTOR_APB_CLK)); + if (port == 0U && nlanes == 4U && !is_ipu7(isys->adev->isp->hw_ver)) { + dev_dbg(dev, "CSI port %u in aggregation mode\n", port); + writel(0x1, isys_base + offset + CSI_PORTAB_AGGREGATION); + } + + /* input is coming from CSI receiver (sensor) */ + offset = IS_IO_CSI2_ADPL_PORT_BASE(port); + writel(CSI_SENSOR_INPUT, isys_base + offset + CSI2_ADPL_INPUT_MODE); + writel(1, isys_base + offset + CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN); + + ret = ipu7_isys_csi_phy_powerup(csi2); + if (ret) { + dev_err(dev, "CSI-%d PHY power up failed %d\n", port, ret); + return ret; + } + + csi2_irq_enable(csi2); + + return 0; +} + +static int ipu7_isys_csi2_set_sel(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); + struct device *dev = &asd->isys->adev->auxdev.dev; + struct v4l2_mbus_framefmt *sink_ffmt; + struct v4l2_mbus_framefmt *src_ffmt; + struct v4l2_rect *crop; + + if (sel->pad == IPU7_CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state, + sel->pad, + sel->stream); + if (!sink_ffmt) + return -EINVAL; + + src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream); + if (!src_ffmt) + return -EINVAL; + + crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); + if (!crop) + return -EINVAL; + + /* Only vertical cropping is supported */ + sel->r.left = 0; + sel->r.width = sink_ffmt->width; + /* Non-bayer formats can't be single line cropped */ + if (!ipu7_isys_is_bayer_format(sink_ffmt->code)) + sel->r.top &= ~1U; + sel->r.height = clamp(sel->r.height & ~1U, IPU_ISYS_MIN_HEIGHT, + sink_ffmt->height - sel->r.top); + *crop = sel->r; + + /* update source pad format */ + src_ffmt->width = sel->r.width; + src_ffmt->height = sel->r.height; + if (ipu7_isys_is_bayer_format(sink_ffmt->code)) + src_ffmt->code = ipu7_isys_convert_bayer_order(sink_ffmt->code, + sel->r.left, + sel->r.top); + dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n", + sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height, + src_ffmt->code); + + return 0; +} + +static int ipu7_isys_csi2_get_sel(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct v4l2_mbus_framefmt *sink_ffmt; + struct v4l2_rect *crop; + int ret = 0; + + if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK) + return -EINVAL; + + sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state, + sel->pad, + sel->stream); + if (!sink_ffmt) + return -EINVAL; + + crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); + if (!crop) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = sink_ffmt->width; + sel->r.height = sink_ffmt->height; + break; + case V4L2_SEL_TGT_CROP: + sel->r = *crop; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/* + * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set + * of streams. + */ +#define CSI2_SUBDEV_MAX_STREAM_ID 63 + +static int ipu7_isys_csi2_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); + struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd); + struct v4l2_subdev *r_sd; + struct media_pad *rp; + u32 sink_pad, sink_stream; + int ret, i; + + if (!csi2->stream_count) { + dev_dbg(&csi2->isys->adev->auxdev.dev, + "stream on CSI2-%u with %u lanes\n", csi2->port, + csi2->nlanes); + ret = ipu7_isys_csi2_enable_stream(csi2); + if (ret) + return ret; + } + + for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) { + if (streams_mask & BIT_ULL(i)) + break; + } + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i, + &sink_pad, &sink_stream); + if (ret) + return ret; + + rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]); + r_sd = media_entity_to_v4l2_subdev(rp->entity); + + ret = v4l2_subdev_enable_streams(r_sd, rp->index, + BIT_ULL(sink_stream)); + if (!ret) { + csi2->stream_count++; + return 0; + } + + if (!csi2->stream_count) + ipu7_isys_csi2_disable_stream(csi2); + + return ret; +} + +static int ipu7_isys_csi2_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); + struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd); + struct v4l2_subdev *r_sd; + struct media_pad *rp; + u32 sink_pad, sink_stream; + int ret, i; + + for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) { + if (streams_mask & BIT_ULL(i)) + break; + } + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i, + &sink_pad, &sink_stream); + if (ret) + return ret; + + rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]); + r_sd = media_entity_to_v4l2_subdev(rp->entity); + + v4l2_subdev_disable_streams(r_sd, rp->index, BIT_ULL(sink_stream)); + + if (--csi2->stream_count) + return 0; + + dev_dbg(&csi2->isys->adev->auxdev.dev, + "stream off CSI2-%u with %u lanes\n", csi2->port, csi2->nlanes); + + ipu7_isys_csi2_disable_stream(csi2); + + return 0; +} + +static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = { + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = ipu7_isys_subdev_set_fmt, + .get_selection = ipu7_isys_csi2_get_sel, + .set_selection = ipu7_isys_csi2_set_sel, + .enum_mbus_code = ipu7_isys_subdev_enum_mbus_code, + .enable_streams = ipu7_isys_csi2_enable_streams, + .disable_streams = ipu7_isys_csi2_disable_streams, + .set_routing = ipu7_isys_subdev_set_routing, +}; + +static const struct v4l2_subdev_ops csi2_sd_ops = { + .core = &csi2_sd_core_ops, + .pad = &csi2_sd_pad_ops, +}; + +static const struct media_entity_operations csi2_entity_ops = { + .link_validate = v4l2_subdev_link_validate, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, +}; + +void ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 *csi2) +{ + if (!csi2->isys) + return; + + v4l2_device_unregister_subdev(&csi2->asd.sd); + v4l2_subdev_cleanup(&csi2->asd.sd); + ipu7_isys_subdev_cleanup(&csi2->asd); + csi2->isys = NULL; +} + +int ipu7_isys_csi2_init(struct ipu7_isys_csi2 *csi2, + struct ipu7_isys *isys, + void __iomem *base, unsigned int index) +{ + struct device *dev = &isys->adev->auxdev.dev; + int ret; + + csi2->isys = isys; + csi2->base = base; + csi2->port = index; + + if (!is_ipu7(isys->adev->isp->hw_ver)) + csi2->legacy_irq_mask = 0x7U << (index * 3U); + else + csi2->legacy_irq_mask = 0x3U << (index * 2U); + + dev_dbg(dev, "csi-%d legacy irq mask = 0x%x\n", index, + csi2->legacy_irq_mask); + + csi2->asd.sd.entity.ops = &csi2_entity_ops; + csi2->asd.isys = isys; + + ret = ipu7_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0, + IPU7_NR_OF_CSI2_SINK_PADS, + IPU7_NR_OF_CSI2_SRC_PADS); + if (ret) + return ret; + + csi2->asd.source = (int)index; + csi2->asd.supported_codes = csi2_supported_codes; + snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name), + IPU_ISYS_ENTITY_PREFIX " CSI2 %u", index); + v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd); + + ret = v4l2_subdev_init_finalize(&csi2->asd.sd); + if (ret) { + dev_err(dev, "failed to init v4l2 subdev (%d)\n", ret); + goto isys_subdev_cleanup; + } + + ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd); + if (ret) { + dev_err(dev, "failed to register v4l2 subdev (%d)\n", ret); + goto v4l2_subdev_cleanup; + } + + return 0; + +v4l2_subdev_cleanup: + v4l2_subdev_cleanup(&csi2->asd.sd); +isys_subdev_cleanup: + ipu7_isys_subdev_cleanup(&csi2->asd); + + return ret; +} + +void ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream *stream) +{ + struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd); + struct device *dev = &stream->isys->adev->auxdev.dev; + struct video_device *vdev = csi2->asd.sd.devnode; + struct v4l2_event ev = { + .type = V4L2_EVENT_FRAME_SYNC, + }; + + ev.id = stream->vc; + ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence); + v4l2_event_queue(vdev, &ev); + + dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n", + csi2->port, ev.u.frame_sync.frame_sequence, stream->vc); +} + +void ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream *stream) +{ + struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd); + struct device *dev = &stream->isys->adev->auxdev.dev; + u32 frame_sequence = atomic_read(&stream->sequence); + + dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n", + csi2->port, frame_sequence); +} + +int ipu7_isys_csi2_get_remote_desc(u32 source_stream, + struct ipu7_isys_csi2 *csi2, + struct media_entity *source_entity, + struct v4l2_mbus_frame_desc_entry *entry, + int *nr_queues) +{ + struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; + struct device *dev = &csi2->isys->adev->auxdev.dev; + struct v4l2_mbus_frame_desc desc; + struct v4l2_subdev *source; + struct media_pad *pad; + unsigned int i; + int ret; + + source = media_entity_to_v4l2_subdev(source_entity); + if (!source) + return -EPIPE; + + pad = media_pad_remote_pad_first(&csi2->asd.pad[IPU7_CSI2_PAD_SINK]); + if (!pad) + return -EPIPE; + + ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc); + if (ret) + return ret; + + if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + dev_err(dev, "Unsupported frame descriptor type\n"); + return -EINVAL; + } + + for (i = 0; i < desc.num_entries; i++) { + if (source_stream == desc.entry[i].stream) { + desc_entry = &desc.entry[i]; + break; + } + } + + if (!desc_entry) { + dev_err(dev, "Failed to find stream %u from remote subdev\n", + source_stream); + return -EINVAL; + } + + if (desc_entry->bus.csi2.vc >= IPU7_NR_OF_CSI2_VC) { + dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc); + return -EINVAL; + } + + *entry = *desc_entry; + + for (i = 0; i < desc.num_entries; i++) { + if (desc_entry->bus.csi2.vc == desc.entry[i].bus.csi2.vc) + (*nr_queues)++; + } + + return 0; +} diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi2.h b/drivers/staging/media/ipu7/ipu7-isys-csi2.h new file mode 100644 index 000000000000..6c23b80f92a2 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-isys-csi2.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#ifndef IPU7_ISYS_CSI2_H +#define IPU7_ISYS_CSI2_H + +#include +#include + +#include "ipu7-isys-subdev.h" +#include "ipu7-isys-video.h" + +struct ipu7_isys; +struct ipu7_isys_csi2_pdata; +struct ipu7_isys_stream; + +#define IPU7_NR_OF_CSI2_VC 16U +#define INVALID_VC_ID -1 +#define IPU7_NR_OF_CSI2_SINK_PADS 1U +#define IPU7_CSI2_PAD_SINK 0U +#define IPU7_NR_OF_CSI2_SRC_PADS 8U +#define IPU7_CSI2_PAD_SRC 1U +#define IPU7_NR_OF_CSI2_PADS (IPU7_NR_OF_CSI2_SINK_PADS + \ + IPU7_NR_OF_CSI2_SRC_PADS) + +/* + * struct ipu7_isys_csi2 + * + * @nlanes: number of lanes in the receiver + */ +struct ipu7_isys_csi2 { + struct ipu7_isys_subdev asd; + struct ipu7_isys_csi2_pdata *pdata; + struct ipu7_isys *isys; + struct ipu7_isys_video av[IPU7_NR_OF_CSI2_SRC_PADS]; + + void __iomem *base; + u32 receiver_errors; + u32 legacy_irq_mask; + unsigned int nlanes; + unsigned int port; + unsigned int phy_mode; + unsigned int stream_count; +}; + +#define ipu7_isys_subdev_to_csi2(__sd) \ + container_of(__sd, struct ipu7_isys_csi2, asd) + +#define to_ipu7_isys_csi2(__asd) container_of(__asd, struct ipu7_isys_csi2, asd) + +s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2); +int ipu7_isys_csi2_init(struct ipu7_isys_csi2 *csi2, struct ipu7_isys *isys, + void __iomem *base, unsigned int index); +void ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 *csi2); +void ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream *stream); +void ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream *stream); +int ipu7_isys_csi2_get_remote_desc(u32 source_stream, + struct ipu7_isys_csi2 *csi2, + struct media_entity *source_entity, + struct v4l2_mbus_frame_desc_entry *entry, + int *nr_queues); +#endif /* IPU7_ISYS_CSI2_H */ diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.c b/drivers/staging/media/ipu7/ipu7-isys-queue.c new file mode 100644 index 000000000000..7046c29141f8 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-isys-queue.c @@ -0,0 +1,829 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "abi/ipu7_fw_isys_abi.h" + +#include "ipu7-bus.h" +#include "ipu7-dma.h" +#include "ipu7-fw-isys.h" +#include "ipu7-isys.h" +#include "ipu7-isys-csi2-regs.h" +#include "ipu7-isys-video.h" +#include "ipu7-platform-regs.h" + +#define IPU_MAX_FRAME_COUNTER (U8_MAX + 1) + +static int ipu7_isys_buf_init(struct vb2_buffer *vb) +{ + struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); + struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); + struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); + struct ipu7_isys_video_buffer *ivb = + vb2_buffer_to_ipu7_isys_video_buffer(vvb); + int ret; + + ret = ipu7_dma_map_sgtable(isys->adev, sg, DMA_TO_DEVICE, 0); + if (ret) + return ret; + + ivb->dma_addr = sg_dma_address(sg->sgl); + + return 0; +} + +static void ipu7_isys_buf_cleanup(struct vb2_buffer *vb) +{ + struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); + struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); + struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); + struct ipu7_isys_video_buffer *ivb = + vb2_buffer_to_ipu7_isys_video_buffer(vvb); + + ivb->dma_addr = 0; + ipu7_dma_unmap_sgtable(isys->adev, sg, DMA_TO_DEVICE, 0); +} + +static int ipu7_isys_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); + struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); + struct device *dev = &av->isys->adev->auxdev.dev; + u32 size = av->pix_fmt.sizeimage; + + /* num_planes == 0: we're being called through VIDIOC_REQBUFS */ + if (!*num_planes) { + sizes[0] = size; + } else if (sizes[0] < size) { + dev_dbg(dev, "%s: queue setup: size %u < %u\n", + av->vdev.name, sizes[0], size); + return -EINVAL; + } + + *num_planes = 1; + + return 0; +} + +static int ipu7_isys_buf_prepare(struct vb2_buffer *vb) +{ + struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); + struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); + struct device *dev = &av->isys->adev->auxdev.dev; + u32 bytesperline = av->pix_fmt.bytesperline; + u32 height = av->pix_fmt.height; + + dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n", + av->vdev.name, av->pix_fmt.sizeimage, vb2_plane_size(vb, 0)); + + if (av->pix_fmt.sizeimage > vb2_plane_size(vb, 0)) + return -EINVAL; + + dev_dbg(dev, "buffer: %s: bytesperline %u, height %u\n", + av->vdev.name, bytesperline, height); + vb2_set_plane_payload(vb, 0, bytesperline * height); + + return 0; +} + +/* + * Queue a buffer list back to incoming or active queues. The buffers + * are removed from the buffer list. + */ +void ipu7_isys_buffer_list_queue(struct ipu7_isys_buffer_list *bl, + unsigned long op_flags, + enum vb2_buffer_state state) +{ + struct ipu7_isys_buffer *ib, *ib_safe; + unsigned long flags; + bool first = true; + + if (!bl) + return; + + WARN_ON_ONCE(!bl->nbufs); + WARN_ON_ONCE(op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE && + op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING); + + list_for_each_entry_safe(ib, ib_safe, &bl->head, head) { + struct ipu7_isys_video *av; + + struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); + struct ipu7_isys_queue *aq = + vb2_queue_to_isys_queue(vb->vb2_queue); + + av = ipu7_isys_queue_to_video(aq); + spin_lock_irqsave(&aq->lock, flags); + list_del(&ib->head); + if (op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE) + list_add(&ib->head, &aq->active); + else if (op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING) + list_add_tail(&ib->head, &aq->incoming); + spin_unlock_irqrestore(&aq->lock, flags); + + if (op_flags & IPU_ISYS_BUFFER_LIST_FL_SET_STATE) + vb2_buffer_done(vb, state); + + if (first) { + dev_dbg(&av->isys->adev->auxdev.dev, + "queue buf list %p flags %lx, s %d, %d bufs\n", + bl, op_flags, state, bl->nbufs); + first = false; + } + + bl->nbufs--; + } + + WARN_ON(bl->nbufs); +} + +/* + * flush_firmware_streamon_fail() - Flush in cases where requests may + * have been queued to firmware and the *firmware streamon fails for a + * reason or another. + */ +static void flush_firmware_streamon_fail(struct ipu7_isys_stream *stream) +{ + struct ipu7_isys_queue *aq; + unsigned long flags; + + lockdep_assert_held(&stream->mutex); + + list_for_each_entry(aq, &stream->queues, node) { + struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); + struct device *dev = &av->isys->adev->auxdev.dev; + struct ipu7_isys_buffer *ib, *ib_safe; + + spin_lock_irqsave(&aq->lock, flags); + list_for_each_entry_safe(ib, ib_safe, &aq->active, head) { + struct vb2_buffer *vb = + ipu7_isys_buffer_to_vb2_buffer(ib); + + list_del(&ib->head); + if (av->streaming) { + dev_dbg(dev, + "%s: queue buffer %u back to incoming\n", + av->vdev.name, vb->index); + /* Queue already streaming, return to driver. */ + list_add(&ib->head, &aq->incoming); + continue; + } + /* Queue not yet streaming, return to user. */ + dev_dbg(dev, "%s: return %u back to videobuf2\n", + av->vdev.name, vb->index); + vb2_buffer_done(ipu7_isys_buffer_to_vb2_buffer(ib), + VB2_BUF_STATE_QUEUED); + } + spin_unlock_irqrestore(&aq->lock, flags); + } +} + +/* + * Attempt obtaining a buffer list from the incoming queues, a list of buffers + * that contains one entry from each video buffer queue. If a buffer can't be + * obtained from every queue, the buffers are returned back to the queue. + */ +static int buffer_list_get(struct ipu7_isys_stream *stream, + struct ipu7_isys_buffer_list *bl) +{ + unsigned long buf_flag = IPU_ISYS_BUFFER_LIST_FL_INCOMING; + struct device *dev = &stream->isys->adev->auxdev.dev; + struct ipu7_isys_queue *aq; + unsigned long flags; + + bl->nbufs = 0; + INIT_LIST_HEAD(&bl->head); + + list_for_each_entry(aq, &stream->queues, node) { + struct ipu7_isys_buffer *ib; + + spin_lock_irqsave(&aq->lock, flags); + if (list_empty(&aq->incoming)) { + spin_unlock_irqrestore(&aq->lock, flags); + if (!list_empty(&bl->head)) + ipu7_isys_buffer_list_queue(bl, buf_flag, 0); + return -ENODATA; + } + + ib = list_last_entry(&aq->incoming, + struct ipu7_isys_buffer, head); + + dev_dbg(dev, "buffer: %s: buffer %u\n", + ipu7_isys_queue_to_video(aq)->vdev.name, + ipu7_isys_buffer_to_vb2_buffer(ib)->index); + list_del(&ib->head); + list_add(&ib->head, &bl->head); + spin_unlock_irqrestore(&aq->lock, flags); + + bl->nbufs++; + } + + dev_dbg(dev, "get buffer list %p, %u buffers\n", bl, bl->nbufs); + + return 0; +} + +static void ipu7_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb, + struct ipu7_insys_buffset *set) +{ + struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); + struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); + struct ipu7_isys_video_buffer *ivb = + vb2_buffer_to_ipu7_isys_video_buffer(vvb); + + set->output_pins[aq->fw_output].addr = ivb->dma_addr; + set->output_pins[aq->fw_output].user_token = (uintptr_t)set; +} + +/* + * Convert a buffer list to a isys fw ABI framebuffer set. The + * buffer list is not modified. + */ +#define IPU_ISYS_FRAME_NUM_THRESHOLD (30) +void ipu7_isys_buffer_to_fw_frame_buff(struct ipu7_insys_buffset *set, + struct ipu7_isys_stream *stream, + struct ipu7_isys_buffer_list *bl) +{ + struct ipu7_isys_buffer *ib; + u32 buf_id; + + WARN_ON(!bl->nbufs); + + set->skip_frame = 0; + set->capture_msg_map = IPU_INSYS_FRAME_ENABLE_MSG_SEND_RESP | + IPU_INSYS_FRAME_ENABLE_MSG_SEND_IRQ; + + buf_id = atomic_fetch_inc(&stream->buf_id); + set->frame_id = buf_id % IPU_MAX_FRAME_COUNTER; + + list_for_each_entry(ib, &bl->head, head) { + struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); + + ipu7_isys_buf_to_fw_frame_buf_pin(vb, set); + } +} + +/* Start streaming for real. The buffer list must be available. */ +static int ipu7_isys_stream_start(struct ipu7_isys_video *av, + struct ipu7_isys_buffer_list *bl, bool error) +{ + struct ipu7_isys_stream *stream = av->stream; + struct device *dev = &stream->isys->adev->auxdev.dev; + struct ipu7_isys_buffer_list __bl; + int ret; + + mutex_lock(&stream->isys->stream_mutex); + + ret = ipu7_isys_video_set_streaming(av, 1, bl); + mutex_unlock(&stream->isys->stream_mutex); + if (ret) + goto out_requeue; + + stream->streaming = 1; + + bl = &__bl; + + do { + struct ipu7_insys_buffset *buf = NULL; + struct isys_fw_msgs *msg; + enum ipu7_insys_send_type send_type = + IPU_INSYS_SEND_TYPE_STREAM_CAPTURE; + + ret = buffer_list_get(stream, bl); + if (ret < 0) + break; + + msg = ipu7_get_fw_msg_buf(stream); + if (!msg) + return -ENOMEM; + + buf = &msg->fw_msg.frame; + + ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl); + + ipu7_fw_isys_dump_frame_buff_set(dev, buf, + stream->nr_output_pins); + + ipu7_isys_buffer_list_queue(bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, + 0); + + ret = ipu7_fw_isys_complex_cmd(stream->isys, + stream->stream_handle, buf, + msg->dma_addr, sizeof(*buf), + send_type); + } while (!WARN_ON(ret)); + + return 0; + +out_requeue: + if (bl && bl->nbufs) + ipu7_isys_buffer_list_queue(bl, + IPU_ISYS_BUFFER_LIST_FL_INCOMING | + (error ? + IPU_ISYS_BUFFER_LIST_FL_SET_STATE : + 0), error ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_QUEUED); + flush_firmware_streamon_fail(stream); + + return ret; +} + +static void buf_queue(struct vb2_buffer *vb) +{ + struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); + struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); + struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); + struct ipu7_isys_video_buffer *ivb = + vb2_buffer_to_ipu7_isys_video_buffer(vvb); + struct media_pipeline *media_pipe = + media_entity_pipeline(&av->vdev.entity); + struct device *dev = &av->isys->adev->auxdev.dev; + struct ipu7_isys_stream *stream = av->stream; + struct ipu7_isys_buffer *ib = &ivb->ib; + struct ipu7_insys_buffset *buf = NULL; + struct ipu7_isys_buffer_list bl; + struct isys_fw_msgs *msg; + unsigned long flags; + dma_addr_t dma; + int ret; + + dev_dbg(dev, "queue buffer %u for %s\n", vb->index, av->vdev.name); + + dma = ivb->dma_addr; + dev_dbg(dev, "iova: iova %pad\n", &dma); + + spin_lock_irqsave(&aq->lock, flags); + list_add(&ib->head, &aq->incoming); + spin_unlock_irqrestore(&aq->lock, flags); + + if (!media_pipe || !vb->vb2_queue->start_streaming_called) { + dev_dbg(dev, "media pipeline is not ready for %s\n", + av->vdev.name); + return; + } + + mutex_lock(&stream->mutex); + + if (stream->nr_streaming != stream->nr_queues) { + dev_dbg(dev, "not streaming yet, adding to incoming\n"); + goto out; + } + + /* + * We just put one buffer to the incoming list of this queue + * (above). Let's see whether all queues in the pipeline would + * have a buffer. + */ + ret = buffer_list_get(stream, &bl); + if (ret < 0) { + dev_dbg(dev, "No buffers available\n"); + goto out; + } + + msg = ipu7_get_fw_msg_buf(stream); + if (!msg) { + ret = -ENOMEM; + goto out; + } + + buf = &msg->fw_msg.frame; + + ipu7_isys_buffer_to_fw_frame_buff(buf, stream, &bl); + + ipu7_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins); + + if (!stream->streaming) { + ret = ipu7_isys_stream_start(av, &bl, true); + if (ret) + dev_err(dev, "stream start failed.\n"); + goto out; + } + + /* + * We must queue the buffers in the buffer list to the + * appropriate video buffer queues BEFORE passing them to the + * firmware since we could get a buffer event back before we + * have queued them ourselves to the active queue. + */ + ipu7_isys_buffer_list_queue(&bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0); + + ret = ipu7_fw_isys_complex_cmd(stream->isys, stream->stream_handle, + buf, msg->dma_addr, sizeof(*buf), + IPU_INSYS_SEND_TYPE_STREAM_CAPTURE); + if (ret < 0) + dev_err(dev, "send stream capture failed\n"); + +out: + mutex_unlock(&stream->mutex); +} + +static int ipu7_isys_link_fmt_validate(struct ipu7_isys_queue *aq) +{ + struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); + struct device *dev = &av->isys->adev->auxdev.dev; + struct media_pad *remote_pad = + media_pad_remote_pad_first(av->vdev.entity.pads); + struct v4l2_mbus_framefmt format; + struct v4l2_subdev *sd; + u32 r_stream, code; + int ret; + + if (!remote_pad) + return -ENOTCONN; + + sd = media_entity_to_v4l2_subdev(remote_pad->entity); + r_stream = ipu7_isys_get_src_stream_by_src_pad(sd, remote_pad->index); + + ret = ipu7_isys_get_stream_pad_fmt(sd, remote_pad->index, r_stream, + &format); + if (ret) { + dev_dbg(dev, "failed to get %s: pad %d, stream:%d format\n", + sd->entity.name, remote_pad->index, r_stream); + return ret; + } + + if (format.width != av->pix_fmt.width || + format.height != av->pix_fmt.height) { + dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n", + av->pix_fmt.width, av->pix_fmt.height, format.width, + format.height); + return -EINVAL; + } + + code = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat)->code; + if (format.code != code) { + dev_dbg(dev, "wrong mbus code 0x%8.8x (0x%8.8x expected)\n", + code, format.code); + return -EINVAL; + } + + return 0; +} + +static void return_buffers(struct ipu7_isys_queue *aq, + enum vb2_buffer_state state) +{ + struct ipu7_isys_buffer *ib; + struct vb2_buffer *vb; + unsigned long flags; + + spin_lock_irqsave(&aq->lock, flags); + /* + * Something went wrong (FW crash / HW hang / not all buffers + * returned from isys) if there are still buffers queued in active + * queue. We have to clean up places a bit. + */ + while (!list_empty(&aq->active)) { + ib = list_last_entry(&aq->active, struct ipu7_isys_buffer, + head); + vb = ipu7_isys_buffer_to_vb2_buffer(ib); + + list_del(&ib->head); + spin_unlock_irqrestore(&aq->lock, flags); + + vb2_buffer_done(vb, state); + + spin_lock_irqsave(&aq->lock, flags); + } + + while (!list_empty(&aq->incoming)) { + ib = list_last_entry(&aq->incoming, struct ipu7_isys_buffer, + head); + vb = ipu7_isys_buffer_to_vb2_buffer(ib); + list_del(&ib->head); + spin_unlock_irqrestore(&aq->lock, flags); + + vb2_buffer_done(vb, state); + + spin_lock_irqsave(&aq->lock, flags); + } + + spin_unlock_irqrestore(&aq->lock, flags); +} + +static void ipu7_isys_stream_cleanup(struct ipu7_isys_video *av) +{ + video_device_pipeline_stop(&av->vdev); + ipu7_isys_put_stream(av->stream); + av->stream = NULL; +} + +static int start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); + struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); + struct device *dev = &av->isys->adev->auxdev.dev; + const struct ipu7_isys_pixelformat *pfmt = + ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); + struct ipu7_isys_buffer_list __bl, *bl = NULL; + struct ipu7_isys_stream *stream; + struct media_entity *source_entity = NULL; + int nr_queues, ret; + + dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n", + av->vdev.name, av->pix_fmt.width, av->pix_fmt.height, + pfmt->css_pixelformat); + + ret = ipu7_isys_setup_video(av, &source_entity, &nr_queues); + if (ret < 0) { + dev_dbg(dev, "failed to setup video\n"); + goto out_return_buffers; + } + + ret = ipu7_isys_link_fmt_validate(aq); + if (ret) { + dev_dbg(dev, + "%s: link format validation failed (%d)\n", + av->vdev.name, ret); + goto out_pipeline_stop; + } + + stream = av->stream; + mutex_lock(&stream->mutex); + if (!stream->nr_streaming) { + ret = ipu7_isys_video_prepare_stream(av, source_entity, + nr_queues); + if (ret) { + mutex_unlock(&stream->mutex); + goto out_pipeline_stop; + } + } + + stream->nr_streaming++; + dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming, + stream->nr_queues); + + list_add(&aq->node, &stream->queues); + + if (stream->nr_streaming != stream->nr_queues) + goto out; + + bl = &__bl; + ret = buffer_list_get(stream, bl); + if (ret < 0) { + dev_warn(dev, "no buffer available, DRIVER BUG?\n"); + goto out; + } + + ret = ipu7_isys_fw_open(av->isys); + if (ret) + goto out_stream_start; + + ipu7_isys_setup_hw(av->isys); + + ret = ipu7_isys_stream_start(av, bl, false); + if (ret) + goto out_isys_fw_close; + +out: + mutex_unlock(&stream->mutex); + + return 0; + +out_isys_fw_close: + ipu7_isys_fw_close(av->isys); + +out_stream_start: + list_del(&aq->node); + stream->nr_streaming--; + mutex_unlock(&stream->mutex); + +out_pipeline_stop: + ipu7_isys_stream_cleanup(av); + +out_return_buffers: + return_buffers(aq, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void stop_streaming(struct vb2_queue *q) +{ + struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); + struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); + struct ipu7_isys_stream *stream = av->stream; + + mutex_lock(&stream->mutex); + mutex_lock(&av->isys->stream_mutex); + if (stream->nr_streaming == stream->nr_queues && stream->streaming) + ipu7_isys_video_set_streaming(av, 0, NULL); + mutex_unlock(&av->isys->stream_mutex); + + stream->nr_streaming--; + list_del(&aq->node); + stream->streaming = 0; + + mutex_unlock(&stream->mutex); + + ipu7_isys_stream_cleanup(av); + + return_buffers(aq, VB2_BUF_STATE_ERROR); + + ipu7_isys_fw_close(av->isys); +} + +static unsigned int +get_sof_sequence_by_timestamp(struct ipu7_isys_stream *stream, u64 time) +{ + struct ipu7_isys *isys = stream->isys; + struct device *dev = &isys->adev->auxdev.dev; + unsigned int i; + + /* + * The timestamp is invalid as no TSC in some FPGA platform, + * so get the sequence from pipeline directly in this case. + */ + if (time == 0) + return atomic_read(&stream->sequence) - 1; + + for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++) + if (time == stream->seq[i].timestamp) { + dev_dbg(dev, "SOF: using seq nr %u for ts %llu\n", + stream->seq[i].sequence, time); + return stream->seq[i].sequence; + } + + dev_dbg(dev, "SOF: looking for %llu\n", time); + for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++) + dev_dbg(dev, "SOF: sequence %u, timestamp value %llu\n", + stream->seq[i].sequence, stream->seq[i].timestamp); + dev_dbg(dev, "SOF sequence number not found\n"); + + return atomic_read(&stream->sequence) - 1; +} + +static u64 get_sof_ns_delta(struct ipu7_isys_video *av, u64 time) +{ + struct ipu7_bus_device *adev = av->isys->adev; + struct ipu7_device *isp = adev->isp; + u64 delta, tsc_now; + + ipu_buttress_tsc_read(isp, &tsc_now); + if (!tsc_now) + return 0; + + delta = tsc_now - time; + + return ipu_buttress_tsc_ticks_to_ns(delta, isp); +} + +static void ipu7_isys_buf_calc_sequence_time(struct ipu7_isys_buffer *ib, + u64 time) +{ + struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); + struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); + struct device *dev = &av->isys->adev->auxdev.dev; + struct ipu7_isys_stream *stream = av->stream; + u64 ns; + u32 sequence; + + ns = ktime_get_ns() - get_sof_ns_delta(av, time); + sequence = get_sof_sequence_by_timestamp(stream, time); + + vbuf->vb2_buf.timestamp = ns; + vbuf->sequence = sequence; + + dev_dbg(dev, "buf: %s: buffer done, CPU-timestamp:%lld, sequence:%d\n", + av->vdev.name, ktime_get_ns(), sequence); + dev_dbg(dev, "index:%d, vbuf timestamp:%lld\n", vb->index, + vbuf->vb2_buf.timestamp); +} + +static void ipu7_isys_queue_buf_done(struct ipu7_isys_buffer *ib) +{ + struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); + + if (atomic_read(&ib->str2mmio_flag)) { + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + /* + * Operation on buffer is ended with error and will be reported + * to the userspace when it is de-queued + */ + atomic_set(&ib->str2mmio_flag, 0); + } else { + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + } +} + +void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream, + struct ipu7_insys_resp *info) +{ + struct ipu7_isys_queue *aq = stream->output_pins[info->pin_id].aq; + u64 time = ((u64)info->timestamp[1] << 32 | info->timestamp[0]); + struct ipu7_isys *isys = stream->isys; + struct device *dev = &isys->adev->auxdev.dev; + struct ipu7_isys_buffer *ib; + struct vb2_buffer *vb; + unsigned long flags; + bool first = true; + struct vb2_v4l2_buffer *buf; + + dev_dbg(dev, "buffer: %s: received buffer %8.8x %d\n", + ipu7_isys_queue_to_video(aq)->vdev.name, info->pin.addr, + info->frame_id); + + spin_lock_irqsave(&aq->lock, flags); + if (list_empty(&aq->active)) { + spin_unlock_irqrestore(&aq->lock, flags); + dev_err(dev, "active queue empty\n"); + return; + } + + list_for_each_entry_reverse(ib, &aq->active, head) { + struct ipu7_isys_video_buffer *ivb; + struct vb2_v4l2_buffer *vvb; + dma_addr_t addr; + + vb = ipu7_isys_buffer_to_vb2_buffer(ib); + vvb = to_vb2_v4l2_buffer(vb); + ivb = vb2_buffer_to_ipu7_isys_video_buffer(vvb); + addr = ivb->dma_addr; + + if (info->pin.addr != addr) { + if (first) + dev_err(dev, "Unexpected buffer address %pad\n", + &addr); + + first = false; + continue; + } + + dev_dbg(dev, "buffer: found buffer %pad\n", &addr); + + buf = to_vb2_v4l2_buffer(vb); + buf->field = V4L2_FIELD_NONE; + + list_del(&ib->head); + spin_unlock_irqrestore(&aq->lock, flags); + + ipu7_isys_buf_calc_sequence_time(ib, time); + + ipu7_isys_queue_buf_done(ib); + + return; + } + + dev_err(dev, "Failed to find a matching video buffer\n"); + + spin_unlock_irqrestore(&aq->lock, flags); +} + +static const struct vb2_ops ipu7_isys_queue_ops = { + .queue_setup = ipu7_isys_queue_setup, + .buf_init = ipu7_isys_buf_init, + .buf_prepare = ipu7_isys_buf_prepare, + .buf_cleanup = ipu7_isys_buf_cleanup, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, + .buf_queue = buf_queue, +}; + +int ipu7_isys_queue_init(struct ipu7_isys_queue *aq) +{ + struct ipu7_isys *isys = ipu7_isys_queue_to_video(aq)->isys; + struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); + struct ipu7_bus_device *adev = isys->adev; + int ret; + + if (!aq->vbq.io_modes) + aq->vbq.io_modes = VB2_MMAP | VB2_DMABUF; + + aq->vbq.drv_priv = isys; + aq->vbq.ops = &ipu7_isys_queue_ops; + aq->vbq.lock = &av->mutex; + aq->vbq.mem_ops = &vb2_dma_sg_memops; + aq->vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + aq->vbq.min_queued_buffers = 1; + aq->vbq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + + ret = vb2_queue_init(&aq->vbq); + if (ret) + return ret; + + aq->dev = &adev->auxdev.dev; + aq->vbq.dev = &adev->isp->pdev->dev; + spin_lock_init(&aq->lock); + INIT_LIST_HEAD(&aq->active); + INIT_LIST_HEAD(&aq->incoming); + + return 0; +} diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.h b/drivers/staging/media/ipu7/ipu7-isys-queue.h new file mode 100644 index 000000000000..0cb08a38f756 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-isys-queue.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#ifndef IPU7_ISYS_QUEUE_H +#define IPU7_ISYS_QUEUE_H + +#include +#include +#include +#include + +#include + +struct device; +struct ipu7_isys_stream; +struct ipu7_insys_resp; +struct ipu7_insys_buffset; + +struct ipu7_isys_queue { + struct vb2_queue vbq; + struct list_head node; + struct device *dev; + spinlock_t lock; + struct list_head active; + struct list_head incoming; + unsigned int fw_output; +}; + +struct ipu7_isys_buffer { + struct list_head head; + atomic_t str2mmio_flag; +}; + +struct ipu7_isys_video_buffer { + struct vb2_v4l2_buffer vb_v4l2; + struct ipu7_isys_buffer ib; + dma_addr_t dma_addr; +}; + +#define IPU_ISYS_BUFFER_LIST_FL_INCOMING BIT(0) +#define IPU_ISYS_BUFFER_LIST_FL_ACTIVE BIT(1) +#define IPU_ISYS_BUFFER_LIST_FL_SET_STATE BIT(2) + +struct ipu7_isys_buffer_list { + struct list_head head; + unsigned int nbufs; +}; + +#define vb2_queue_to_isys_queue(__vb2) \ + container_of(__vb2, struct ipu7_isys_queue, vbq) + +#define ipu7_isys_to_isys_video_buffer(__ib) \ + container_of(__ib, struct ipu7_isys_video_buffer, ib) + +#define vb2_buffer_to_ipu7_isys_video_buffer(__vvb) \ + container_of(__vvb, struct ipu7_isys_video_buffer, vb_v4l2) + +#define ipu7_isys_buffer_to_vb2_buffer(__ib) \ + (&ipu7_isys_to_isys_video_buffer(__ib)->vb_v4l2.vb2_buf) + +void ipu7_isys_buffer_list_queue(struct ipu7_isys_buffer_list *bl, + unsigned long op_flags, + enum vb2_buffer_state state); +void ipu7_isys_buffer_to_fw_frame_buff(struct ipu7_insys_buffset *set, + struct ipu7_isys_stream *stream, + struct ipu7_isys_buffer_list *bl); +void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream, + struct ipu7_insys_resp *info); +int ipu7_isys_queue_init(struct ipu7_isys_queue *aq); +#endif /* IPU7_ISYS_QUEUE_H */ diff --git a/drivers/staging/media/ipu7/ipu7-isys-subdev.c b/drivers/staging/media/ipu7/ipu7-isys-subdev.c new file mode 100644 index 000000000000..98b6ef6a2f21 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-isys-subdev.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "ipu7-bus.h" +#include "ipu7-isys.h" +#include "ipu7-isys-subdev.h" + +unsigned int ipu7_isys_mbus_code_to_mipi(u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_RGB565_1X16: + return MIPI_CSI2_DT_RGB565; + case MEDIA_BUS_FMT_RGB888_1X24: + return MIPI_CSI2_DT_RGB888; + case MEDIA_BUS_FMT_YUYV10_1X20: + return MIPI_CSI2_DT_YUV422_10B; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + return MIPI_CSI2_DT_YUV422_8B; + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + return MIPI_CSI2_DT_RAW12; + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + return MIPI_CSI2_DT_RAW10; + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + return MIPI_CSI2_DT_RAW8; + default: + WARN_ON(1); + return 0xff; + } +} + +bool ipu7_isys_is_bayer_format(u32 code) +{ + switch (ipu7_isys_mbus_code_to_mipi(code)) { + case MIPI_CSI2_DT_RAW8: + case MIPI_CSI2_DT_RAW10: + case MIPI_CSI2_DT_RAW12: + case MIPI_CSI2_DT_RAW14: + case MIPI_CSI2_DT_RAW16: + case MIPI_CSI2_DT_RAW20: + case MIPI_CSI2_DT_RAW24: + case MIPI_CSI2_DT_RAW28: + return true; + default: + return false; + } +} + +u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y) +{ + static const u32 code_map[] = { + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SBGGR12_1X12, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(code_map); i++) + if (code_map[i] == code) + break; + + if (WARN_ON(i == ARRAY_SIZE(code_map))) + return code; + + return code_map[i ^ ((((u32)y & 1U) << 1U) | ((u32)x & 1U))]; +} + +int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); + u32 code = asd->supported_codes[0]; + struct v4l2_mbus_framefmt *fmt; + u32 other_pad, other_stream; + struct v4l2_rect *crop; + unsigned int i; + int ret; + + /* No transcoding, source and sink formats must match. */ + if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) && + sd->entity.num_pads > 1) + return v4l2_subdev_get_fmt(sd, state, format); + + format->format.width = clamp(format->format.width, IPU_ISYS_MIN_WIDTH, + IPU_ISYS_MAX_WIDTH); + format->format.height = clamp(format->format.height, + IPU_ISYS_MIN_HEIGHT, + IPU_ISYS_MAX_HEIGHT); + + for (i = 0; asd->supported_codes[i]; i++) { + if (asd->supported_codes[i] == format->format.code) { + code = asd->supported_codes[i]; + break; + } + } + format->format.code = code; + format->format.field = V4L2_FIELD_NONE; + + /* Store the format and propagate it to the source pad. */ + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK)) + return 0; + + /* propagate format to following source pad */ + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + ret = v4l2_subdev_routing_find_opposite_end(&state->routing, + format->pad, + format->stream, + &other_pad, + &other_stream); + if (ret) + return -EINVAL; + + crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream); + /* reset crop */ + crop->left = 0; + crop->top = 0; + crop->width = fmt->width; + crop->height = fmt->height; + + return 0; +} + +int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); + const u32 *supported_codes = asd->supported_codes; + u32 index; + + for (index = 0; supported_codes[index]; index++) { + if (index == code->index) { + code->code = supported_codes[index]; + return 0; + } + } + + return -EINVAL; +} + +static int subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + static const struct v4l2_mbus_framefmt fmt = { + .width = 4096, + .height = 3072, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .field = V4L2_FIELD_NONE, + }; + int ret; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt); +} + +int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, + struct v4l2_mbus_framefmt *format) +{ + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *fmt; + + if (!sd || !format) + return -EINVAL; + + state = v4l2_subdev_lock_and_get_active_state(sd); + fmt = v4l2_subdev_state_get_format(state, pad, stream); + if (fmt) + *format = *fmt; + v4l2_subdev_unlock_state(state); + + return fmt ? 0 : -EINVAL; +} + +u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad) +{ + struct v4l2_subdev_state *state; + struct v4l2_subdev_route *routes; + u32 source_stream = 0; + unsigned int i; + + state = v4l2_subdev_lock_and_get_active_state(sd); + if (!state) + return 0; + + routes = state->routing.routes; + for (i = 0; i < state->routing.num_routes; i++) { + if (routes[i].source_pad == pad) { + source_stream = routes[i].source_stream; + break; + } + } + + v4l2_subdev_unlock_state(state); + + return source_stream; +} + +static int ipu7_isys_subdev_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route route = { + .sink_pad = 0, + .sink_stream = 0, + .source_pad = 1, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }; + struct v4l2_subdev_krouting routing = { + .num_routes = 1, + .routes = &route, + }; + + return subdev_set_routing(sd, state, &routing); +} + +int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + return subdev_set_routing(sd, state, routing); +} + +static const struct v4l2_subdev_internal_ops ipu7_isys_subdev_internal_ops = { + .init_state = ipu7_isys_subdev_init_state, +}; + +int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd, + const struct v4l2_subdev_ops *ops, + unsigned int nr_ctrls, + unsigned int num_sink_pads, + unsigned int num_source_pads) +{ + unsigned int num_pads = num_sink_pads + num_source_pads; + unsigned int i; + int ret; + + v4l2_subdev_init(&asd->sd, ops); + + asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS | + V4L2_SUBDEV_FL_STREAMS; + asd->sd.owner = THIS_MODULE; + asd->sd.dev = &asd->isys->adev->auxdev.dev; + asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + asd->sd.internal_ops = &ipu7_isys_subdev_internal_ops; + + asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads, + sizeof(*asd->pad), GFP_KERNEL); + if (!asd->pad) + return -ENOMEM; + + for (i = 0; i < num_sink_pads; i++) + asd->pad[i].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; + + for (i = num_sink_pads; i < num_pads; i++) + asd->pad[i].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad); + if (ret) { + pr_err("isys subdev init failed %d.\n", ret); + return ret; + } + + if (asd->ctrl_init) { + ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls); + if (ret) + goto out_media_entity_cleanup; + + asd->ctrl_init(&asd->sd); + if (asd->ctrl_handler.error) { + ret = asd->ctrl_handler.error; + goto out_v4l2_ctrl_handler_free; + } + + asd->sd.ctrl_handler = &asd->ctrl_handler; + } + + asd->source = -1; + + return 0; + +out_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(&asd->ctrl_handler); + +out_media_entity_cleanup: + media_entity_cleanup(&asd->sd.entity); + + return ret; +} + +void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd) +{ + media_entity_cleanup(&asd->sd.entity); + v4l2_ctrl_handler_free(&asd->ctrl_handler); +} diff --git a/drivers/staging/media/ipu7/ipu7-isys-subdev.h b/drivers/staging/media/ipu7/ipu7-isys-subdev.h new file mode 100644 index 000000000000..1057ec39ae39 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-isys-subdev.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#ifndef IPU7_ISYS_SUBDEV_H +#define IPU7_ISYS_SUBDEV_H + +#include + +#include +#include +#include + +struct ipu7_isys; + +struct ipu7_isys_subdev { + struct v4l2_subdev sd; + struct ipu7_isys *isys; + u32 const *supported_codes; + struct media_pad *pad; + struct v4l2_ctrl_handler ctrl_handler; + void (*ctrl_init)(struct v4l2_subdev *sd); + int source; /* SSI stream source; -1 if unset */ +}; + +#define to_ipu7_isys_subdev(__sd) \ + container_of(__sd, struct ipu7_isys_subdev, sd) +unsigned int ipu7_isys_mbus_code_to_mipi(u32 code); +bool ipu7_isys_is_bayer_format(u32 code); +u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y); + +int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format); +int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum + *code); +u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad); +int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, + struct v4l2_mbus_framefmt *format); +int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing); +int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd, + const struct v4l2_subdev_ops *ops, + unsigned int nr_ctrls, + unsigned int num_sink_pads, + unsigned int num_source_pads); +void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd); +#endif /* IPU7_ISYS_SUBDEV_H */ diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.c b/drivers/staging/media/ipu7/ipu7-isys-video.c new file mode 100644 index 000000000000..8756da3a8fb0 --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-isys-video.c @@ -0,0 +1,1112 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "abi/ipu7_fw_isys_abi.h" + +#include "ipu7.h" +#include "ipu7-bus.h" +#include "ipu7-buttress-regs.h" +#include "ipu7-fw-isys.h" +#include "ipu7-isys.h" +#include "ipu7-isys-video.h" +#include "ipu7-platform-regs.h" + +const struct ipu7_isys_pixelformat ipu7_isys_pfmts[] = { + {V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12, + IPU_INSYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12, + IPU_INSYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12, + IPU_INSYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12, + IPU_INSYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10, + IPU_INSYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10, + IPU_INSYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10, + IPU_INSYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10, + IPU_INSYS_FRAME_FORMAT_RAW16}, + {V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8, + IPU_INSYS_FRAME_FORMAT_RAW8}, + {V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8, + IPU_INSYS_FRAME_FORMAT_RAW8}, + {V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8, + IPU_INSYS_FRAME_FORMAT_RAW8}, + {V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8, + IPU_INSYS_FRAME_FORMAT_RAW8}, + {V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12, + IPU_INSYS_FRAME_FORMAT_RAW12}, + {V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12, + IPU_INSYS_FRAME_FORMAT_RAW12}, + {V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12, + IPU_INSYS_FRAME_FORMAT_RAW12}, + {V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12, + IPU_INSYS_FRAME_FORMAT_RAW12}, + {V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10, + IPU_INSYS_FRAME_FORMAT_RAW10}, + {V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10, + IPU_INSYS_FRAME_FORMAT_RAW10}, + {V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10, + IPU_INSYS_FRAME_FORMAT_RAW10}, + {V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10, + IPU_INSYS_FRAME_FORMAT_RAW10}, + {V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16, + IPU_INSYS_FRAME_FORMAT_UYVY}, + {V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16, + IPU_INSYS_FRAME_FORMAT_YUYV}, + {V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16, + IPU_INSYS_FRAME_FORMAT_RGB565}, + {V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24, + IPU_INSYS_FRAME_FORMAT_RGBA888}, +}; + +static int video_open(struct file *file) +{ + return v4l2_fh_open(file); +} + +const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { + const struct ipu7_isys_pixelformat *pfmt = &ipu7_isys_pfmts[i]; + + if (pfmt->pixelformat == pixelformat) + return pfmt; + } + + return &ipu7_isys_pfmts[0]; +} + +static int ipu7_isys_vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct ipu7_isys_video *av = video_drvdata(file); + + strscpy(cap->driver, IPU_ISYS_NAME, sizeof(cap->driver)); + strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card)); + + return 0; +} + +static int ipu7_isys_vidioc_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + unsigned int i, num_found; + + for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + continue; + + if (f->mbus_code && f->mbus_code != ipu7_isys_pfmts[i].code) + continue; + + if (num_found < f->index) { + num_found++; + continue; + } + + f->flags = 0; + f->pixelformat = ipu7_isys_pfmts[i].pixelformat; + + return 0; + } + + return -EINVAL; +} + +static int ipu7_isys_vidioc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + unsigned int i; + + if (fsize->index > 0) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { + if (fsize->pixel_format != ipu7_isys_pfmts[i].pixelformat) + continue; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = IPU_ISYS_MIN_WIDTH; + fsize->stepwise.max_width = IPU_ISYS_MAX_WIDTH; + fsize->stepwise.min_height = IPU_ISYS_MIN_HEIGHT; + fsize->stepwise.max_height = IPU_ISYS_MAX_HEIGHT; + fsize->stepwise.step_width = 2; + fsize->stepwise.step_height = 2; + + return 0; + } + + return -EINVAL; +} + +static int ipu7_isys_vidioc_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct ipu7_isys_video *av = video_drvdata(file); + + f->fmt.pix = av->pix_fmt; + + return 0; +} + +static void ipu7_isys_try_fmt_cap(struct ipu7_isys_video *av, u32 type, + u32 *format, u32 *width, u32 *height, + u32 *bytesperline, u32 *sizeimage) +{ + const struct ipu7_isys_pixelformat *pfmt = + ipu7_isys_get_isys_format(*format); + + *format = pfmt->pixelformat; + *width = clamp(*width, IPU_ISYS_MIN_WIDTH, IPU_ISYS_MAX_WIDTH); + *height = clamp(*height, IPU_ISYS_MIN_HEIGHT, IPU_ISYS_MAX_HEIGHT); + + if (pfmt->bpp != pfmt->bpp_packed) + *bytesperline = *width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE); + else + *bytesperline = DIV_ROUND_UP(*width * pfmt->bpp, BITS_PER_BYTE); + + *bytesperline = ALIGN(*bytesperline, 64U); + + /* + * (height + 1) * bytesperline due to a hardware issue: the DMA unit + * is a power of two, and a line should be transferred as few units + * as possible. The result is that up to line length more data than + * the image size may be transferred to memory after the image. + * Another limitation is the GDA allocation unit size. For low + * resolution it gives a bigger number. Use larger one to avoid + * memory corruption. + */ + *sizeimage = *bytesperline * *height + + max(*bytesperline, + av->isys->pdata->ipdata->isys_dma_overshoot); +} + +static void __ipu_isys_vidioc_try_fmt_vid_cap(struct ipu7_isys_video *av, + struct v4l2_format *f) +{ + ipu7_isys_try_fmt_cap(av, f->type, &f->fmt.pix.pixelformat, + &f->fmt.pix.width, &f->fmt.pix.height, + &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage); + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; + f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT; + f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int ipu7_isys_vidioc_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct ipu7_isys_video *av = video_drvdata(file); + + if (vb2_is_busy(&av->aq.vbq)) + return -EBUSY; + + __ipu_isys_vidioc_try_fmt_vid_cap(av, f); + + return 0; +} + +static int ipu7_isys_vidioc_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct ipu7_isys_video *av = video_drvdata(file); + + ipu7_isys_vidioc_try_fmt_vid_cap(file, fh, f); + av->pix_fmt = f->fmt.pix; + + return 0; +} + +static int ipu7_isys_vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct ipu7_isys_video *av = video_drvdata(file); + int ret; + + av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type); + av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type); + + ret = vb2_queue_change_type(&av->aq.vbq, p->type); + if (ret) + return ret; + + return vb2_ioctl_reqbufs(file, priv, p); +} + +static int ipu7_isys_vidioc_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *p) +{ + struct ipu7_isys_video *av = video_drvdata(file); + int ret; + + av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type); + av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type); + + ret = vb2_queue_change_type(&av->aq.vbq, p->format.type); + if (ret) + return ret; + + return vb2_ioctl_create_bufs(file, priv, p); +} + +static int link_validate(struct media_link *link) +{ + struct ipu7_isys_video *av = + container_of(link->sink, struct ipu7_isys_video, pad); + struct device *dev = &av->isys->adev->auxdev.dev; + struct v4l2_subdev_state *s_state; + struct v4l2_mbus_framefmt *s_fmt; + struct v4l2_subdev *s_sd; + struct media_pad *s_pad; + u32 s_stream, code; + int ret = -EPIPE; + + if (!link->source->entity) + return ret; + + s_sd = media_entity_to_v4l2_subdev(link->source->entity); + s_state = v4l2_subdev_get_unlocked_active_state(s_sd); + if (!s_state) + return ret; + + dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n", + link->source->entity->name, link->source->index, + link->sink->entity->name); + + s_pad = media_pad_remote_pad_first(&av->pad); + s_stream = ipu7_isys_get_src_stream_by_src_pad(s_sd, s_pad->index); + + v4l2_subdev_lock_state(s_state); + + s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream); + if (!s_fmt) { + dev_err(dev, "failed to get source pad format\n"); + goto unlock; + } + + code = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat)->code; + + if (s_fmt->width != av->pix_fmt.width || + s_fmt->height != av->pix_fmt.height || s_fmt->code != code) { + dev_dbg(dev, "format mismatch %dx%d,%x != %dx%d,%x\n", + s_fmt->width, s_fmt->height, s_fmt->code, + av->pix_fmt.width, av->pix_fmt.height, code); + goto unlock; + } + + v4l2_subdev_unlock_state(s_state); + + return 0; +unlock: + v4l2_subdev_unlock_state(s_state); + + return ret; +} + +static void get_stream_opened(struct ipu7_isys_video *av) +{ + unsigned long flags; + + spin_lock_irqsave(&av->isys->streams_lock, flags); + av->isys->stream_opened++; + spin_unlock_irqrestore(&av->isys->streams_lock, flags); +} + +static void put_stream_opened(struct ipu7_isys_video *av) +{ + unsigned long flags; + + spin_lock_irqsave(&av->isys->streams_lock, flags); + av->isys->stream_opened--; + spin_unlock_irqrestore(&av->isys->streams_lock, flags); +} + +static int ipu7_isys_fw_pin_cfg(struct ipu7_isys_video *av, + struct ipu7_insys_stream_cfg *cfg) +{ + struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad); + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity); + struct ipu7_isys_stream *stream = av->stream; + const struct ipu7_isys_pixelformat *pfmt = + ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); + struct ipu7_insys_output_pin *output_pin; + struct ipu7_insys_input_pin *input_pin; + int input_pins = cfg->nof_input_pins++; + struct ipu7_isys_queue *aq = &av->aq; + struct ipu7_isys *isys = av->isys; + struct device *dev = &isys->adev->auxdev.dev; + struct v4l2_mbus_framefmt fmt; + int output_pins; + u32 src_stream; + int ret; + + src_stream = ipu7_isys_get_src_stream_by_src_pad(sd, src_pad->index); + ret = ipu7_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream, + &fmt); + if (ret < 0) { + dev_err(dev, "can't get stream format (%d)\n", ret); + return ret; + } + + input_pin = &cfg->input_pins[input_pins]; + input_pin->input_res.width = fmt.width; + input_pin->input_res.height = fmt.height; + input_pin->dt = av->dt; + input_pin->disable_mipi_unpacking = 0; + pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); + if (pfmt->bpp == pfmt->bpp_packed && pfmt->bpp % BITS_PER_BYTE) + input_pin->disable_mipi_unpacking = 1; + input_pin->mapped_dt = N_IPU_INSYS_MIPI_DATA_TYPE; + input_pin->dt_rename_mode = IPU_INSYS_MIPI_DT_NO_RENAME; + /* if enable polling isys interrupt, the follow values maybe set */ + input_pin->sync_msg_map = IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF | + IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED | + IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF | + IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED; + + output_pins = cfg->nof_output_pins++; + aq->fw_output = output_pins; + stream->output_pins[output_pins].pin_ready = ipu7_isys_queue_buf_ready; + stream->output_pins[output_pins].aq = aq; + + output_pin = &cfg->output_pins[output_pins]; + /* output pin msg link */ + output_pin->link.buffer_lines = 0; + output_pin->link.foreign_key = IPU_MSG_LINK_FOREIGN_KEY_NONE; + output_pin->link.granularity_pointer_update = 0; + output_pin->link.msg_link_streaming_mode = + IA_GOFO_MSG_LINK_STREAMING_MODE_SOFF; + + output_pin->link.pbk_id = IPU_MSG_LINK_PBK_ID_DONT_CARE; + output_pin->link.pbk_slot_id = IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE; + output_pin->link.dest = IPU_INSYS_OUTPUT_LINK_DEST_MEM; + output_pin->link.use_sw_managed = 1; + /* TODO: set the snoop bit for metadata capture */ + output_pin->link.is_snoop = 0; + + /* output pin crop */ + output_pin->crop.line_top = 0; + output_pin->crop.line_bottom = 0; + + /* output de-compression */ + output_pin->dpcm.enable = 0; + + /* frame format type */ + pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); + output_pin->ft = (u16)pfmt->css_pixelformat; + + /* stride in bytes */ + output_pin->stride = av->pix_fmt.bytesperline; + output_pin->send_irq = 1; + output_pin->early_ack_en = 0; + + /* input pin id */ + output_pin->input_pin_id = input_pins; + + return 0; +} + +/* Create stream and start it using the CSS FW ABI. */ +static int start_stream_firmware(struct ipu7_isys_video *av, + struct ipu7_isys_buffer_list *bl) +{ + struct device *dev = &av->isys->adev->auxdev.dev; + struct ipu7_isys_stream *stream = av->stream; + struct ipu7_insys_stream_cfg *stream_cfg; + struct ipu7_insys_buffset *buf = NULL; + struct isys_fw_msgs *msg = NULL; + struct ipu7_isys_queue *aq; + int ret, retout, tout; + u16 send_type; + + if (WARN_ON(!bl)) + return -EIO; + + msg = ipu7_get_fw_msg_buf(stream); + if (!msg) + return -ENOMEM; + + stream_cfg = &msg->fw_msg.stream; + stream_cfg->port_id = stream->stream_source; + stream_cfg->vc = stream->vc; + stream_cfg->stream_msg_map = IPU_INSYS_STREAM_ENABLE_MSG_SEND_RESP | + IPU_INSYS_STREAM_ENABLE_MSG_SEND_IRQ; + + list_for_each_entry(aq, &stream->queues, node) { + struct ipu7_isys_video *__av = ipu7_isys_queue_to_video(aq); + + ret = ipu7_isys_fw_pin_cfg(__av, stream_cfg); + if (ret < 0) { + ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); + return ret; + } + } + + ipu7_fw_isys_dump_stream_cfg(dev, stream_cfg); + + stream->nr_output_pins = stream_cfg->nof_output_pins; + + reinit_completion(&stream->stream_open_completion); + + ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle, + stream_cfg, msg->dma_addr, + sizeof(*stream_cfg), + IPU_INSYS_SEND_TYPE_STREAM_OPEN); + if (ret < 0) { + dev_err(dev, "can't open stream (%d)\n", ret); + ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); + return ret; + } + + get_stream_opened(av); + + tout = wait_for_completion_timeout(&stream->stream_open_completion, + FW_CALL_TIMEOUT_JIFFIES); + + ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); + + if (!tout) { + dev_err(dev, "stream open time out\n"); + ret = -ETIMEDOUT; + goto out_put_stream_opened; + } + if (stream->error) { + dev_err(dev, "stream open error: %d\n", stream->error); + ret = -EIO; + goto out_put_stream_opened; + } + dev_dbg(dev, "start stream: open complete\n"); + + msg = ipu7_get_fw_msg_buf(stream); + if (!msg) { + ret = -ENOMEM; + goto out_put_stream_opened; + } + buf = &msg->fw_msg.frame; + + ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl); + ipu7_isys_buffer_list_queue(bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0); + + reinit_completion(&stream->stream_start_completion); + + send_type = IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE; + ipu7_fw_isys_dump_frame_buff_set(dev, buf, + stream_cfg->nof_output_pins); + ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle, buf, + msg->dma_addr, sizeof(*buf), + send_type); + if (ret < 0) { + dev_err(dev, "can't start streaming (%d)\n", ret); + goto out_stream_close; + } + + tout = wait_for_completion_timeout(&stream->stream_start_completion, + FW_CALL_TIMEOUT_JIFFIES); + if (!tout) { + dev_err(dev, "stream start time out\n"); + ret = -ETIMEDOUT; + goto out_stream_close; + } + if (stream->error) { + dev_err(dev, "stream start error: %d\n", stream->error); + ret = -EIO; + goto out_stream_close; + } + dev_dbg(dev, "start stream: complete\n"); + + return 0; + +out_stream_close: + reinit_completion(&stream->stream_close_completion); + + retout = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, + IPU_INSYS_SEND_TYPE_STREAM_CLOSE); + if (retout < 0) { + dev_dbg(dev, "can't close stream (%d)\n", retout); + goto out_put_stream_opened; + } + + tout = wait_for_completion_timeout(&stream->stream_close_completion, + FW_CALL_TIMEOUT_JIFFIES); + if (!tout) + dev_err(dev, "stream close time out with error %d\n", + stream->error); + else + dev_dbg(dev, "stream close complete\n"); + +out_put_stream_opened: + put_stream_opened(av); + + return ret; +} + +static void stop_streaming_firmware(struct ipu7_isys_video *av) +{ + struct device *dev = &av->isys->adev->auxdev.dev; + struct ipu7_isys_stream *stream = av->stream; + int ret, tout; + + reinit_completion(&stream->stream_stop_completion); + + ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, + IPU_INSYS_SEND_TYPE_STREAM_FLUSH); + if (ret < 0) { + dev_err(dev, "can't stop stream (%d)\n", ret); + return; + } + + tout = wait_for_completion_timeout(&stream->stream_stop_completion, + FW_CALL_TIMEOUT_JIFFIES); + if (!tout) + dev_warn(dev, "stream stop time out\n"); + else if (stream->error) + dev_warn(dev, "stream stop error: %d\n", stream->error); + else + dev_dbg(dev, "stop stream: complete\n"); +} + +static void close_streaming_firmware(struct ipu7_isys_video *av) +{ + struct device *dev = &av->isys->adev->auxdev.dev; + struct ipu7_isys_stream *stream = av->stream; + int ret, tout; + + reinit_completion(&stream->stream_close_completion); + + ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, + IPU_INSYS_SEND_TYPE_STREAM_CLOSE); + if (ret < 0) { + dev_err(dev, "can't close stream (%d)\n", ret); + return; + } + + tout = wait_for_completion_timeout(&stream->stream_close_completion, + FW_CALL_TIMEOUT_JIFFIES); + if (!tout) + dev_warn(dev, "stream close time out\n"); + else if (stream->error) + dev_warn(dev, "stream close error: %d\n", stream->error); + else + dev_dbg(dev, "close stream: complete\n"); + + put_stream_opened(av); +} + +int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av, + struct media_entity *source_entity, + int nr_queues) +{ + struct ipu7_isys_stream *stream = av->stream; + struct ipu7_isys_csi2 *csi2; + + if (WARN_ON(stream->nr_streaming)) + return -EINVAL; + + stream->nr_queues = nr_queues; + atomic_set(&stream->sequence, 0); + atomic_set(&stream->buf_id, 0); + + stream->seq_index = 0; + memset(stream->seq, 0, sizeof(stream->seq)); + + if (WARN_ON(!list_empty(&stream->queues))) + return -EINVAL; + + stream->stream_source = stream->asd->source; + + csi2 = ipu7_isys_subdev_to_csi2(stream->asd); + csi2->receiver_errors = 0; + stream->source_entity = source_entity; + + dev_dbg(&av->isys->adev->auxdev.dev, + "prepare stream: external entity %s\n", + stream->source_entity->name); + + return 0; +} + +void ipu7_isys_put_stream(struct ipu7_isys_stream *stream) +{ + unsigned long flags; + struct device *dev; + unsigned int i; + + if (!stream) { + pr_err("ipu7-isys: no available stream\n"); + return; + } + + dev = &stream->isys->adev->auxdev.dev; + + spin_lock_irqsave(&stream->isys->streams_lock, flags); + for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { + if (&stream->isys->streams[i] == stream) { + if (stream->isys->streams_ref_count[i] > 0) + stream->isys->streams_ref_count[i]--; + else + dev_warn(dev, "invalid stream %d\n", i); + + break; + } + } + spin_unlock_irqrestore(&stream->isys->streams_lock, flags); +} + +static struct ipu7_isys_stream * +ipu7_isys_get_stream(struct ipu7_isys_video *av, struct ipu7_isys_subdev *asd) +{ + struct ipu7_isys_stream *stream = NULL; + struct ipu7_isys *isys = av->isys; + unsigned long flags; + unsigned int i; + u8 vc = av->vc; + + if (!isys) + return NULL; + + spin_lock_irqsave(&isys->streams_lock, flags); + for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { + if (isys->streams_ref_count[i] && isys->streams[i].vc == vc && + isys->streams[i].asd == asd) { + isys->streams_ref_count[i]++; + stream = &isys->streams[i]; + break; + } + } + + if (!stream) { + for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { + if (!isys->streams_ref_count[i]) { + isys->streams_ref_count[i]++; + stream = &isys->streams[i]; + stream->vc = vc; + stream->asd = asd; + break; + } + } + } + spin_unlock_irqrestore(&isys->streams_lock, flags); + + return stream; +} + +struct ipu7_isys_stream * +ipu7_isys_query_stream_by_handle(struct ipu7_isys *isys, u8 stream_handle) +{ + unsigned long flags; + struct ipu7_isys_stream *stream = NULL; + + if (!isys) + return NULL; + + if (stream_handle >= IPU_ISYS_MAX_STREAMS) { + dev_err(&isys->adev->auxdev.dev, + "stream_handle %d is invalid\n", stream_handle); + return NULL; + } + + spin_lock_irqsave(&isys->streams_lock, flags); + if (isys->streams_ref_count[stream_handle] > 0) { + isys->streams_ref_count[stream_handle]++; + stream = &isys->streams[stream_handle]; + } + spin_unlock_irqrestore(&isys->streams_lock, flags); + + return stream; +} + +struct ipu7_isys_stream * +ipu7_isys_query_stream_by_source(struct ipu7_isys *isys, int source, u8 vc) +{ + struct ipu7_isys_stream *stream = NULL; + unsigned long flags; + unsigned int i; + + if (!isys) + return NULL; + + if (source < 0) { + dev_err(&isys->adev->auxdev.dev, + "query stream with invalid port number\n"); + return NULL; + } + + spin_lock_irqsave(&isys->streams_lock, flags); + for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { + if (!isys->streams_ref_count[i]) + continue; + + if (isys->streams[i].stream_source == source && + isys->streams[i].vc == vc) { + stream = &isys->streams[i]; + isys->streams_ref_count[i]++; + break; + } + } + spin_unlock_irqrestore(&isys->streams_lock, flags); + + return stream; +} + +static u32 get_remote_pad_stream(struct media_pad *r_pad) +{ + struct v4l2_subdev_state *state; + struct v4l2_subdev *sd; + u32 stream_id = 0; + unsigned int i; + + sd = media_entity_to_v4l2_subdev(r_pad->entity); + state = v4l2_subdev_lock_and_get_active_state(sd); + if (!state) + return 0; + + for (i = 0; i < state->stream_configs.num_configs; i++) { + struct v4l2_subdev_stream_config *cfg = + &state->stream_configs.configs[i]; + if (cfg->pad == r_pad->index) { + stream_id = cfg->stream; + break; + } + } + + v4l2_subdev_unlock_state(state); + + return stream_id; +} + +int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state, + struct ipu7_isys_buffer_list *bl) +{ + struct ipu7_isys_stream *stream = av->stream; + struct device *dev = &av->isys->adev->auxdev.dev; + struct media_pad *r_pad; + struct v4l2_subdev *sd; + u32 r_stream; + int ret = 0; + + dev_dbg(dev, "set stream: %d\n", state); + + if (WARN(!stream->source_entity, "No source entity for stream\n")) + return -ENODEV; + + sd = &stream->asd->sd; + r_pad = media_pad_remote_pad_first(&av->pad); + r_stream = get_remote_pad_stream(r_pad); + if (!state) { + stop_streaming_firmware(av); + + /* stop sub-device which connects with video */ + dev_dbg(dev, "disable streams %s pad:%d mask:0x%llx\n", + sd->name, r_pad->index, BIT_ULL(r_stream)); + ret = v4l2_subdev_disable_streams(sd, r_pad->index, + BIT_ULL(r_stream)); + if (ret) { + dev_err(dev, "disable streams %s failed with %d\n", + sd->name, ret); + return ret; + } + + close_streaming_firmware(av); + } else { + ret = start_stream_firmware(av, bl); + if (ret) { + dev_err(dev, "start stream of firmware failed\n"); + return ret; + } + + /* start sub-device which connects with video */ + dev_dbg(dev, "enable streams %s pad: %d mask:0x%llx\n", + sd->name, r_pad->index, BIT_ULL(r_stream)); + ret = v4l2_subdev_enable_streams(sd, r_pad->index, + BIT_ULL(r_stream)); + if (ret) { + dev_err(dev, "enable streams %s failed with %d\n", + sd->name, ret); + goto out_media_entity_stop_streaming_firmware; + } + } + + av->streaming = state; + + return 0; + +out_media_entity_stop_streaming_firmware: + stop_streaming_firmware(av); + + return ret; +} + +static const struct v4l2_ioctl_ops ipu7_v4l2_ioctl_ops = { + .vidioc_querycap = ipu7_isys_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = ipu7_isys_vidioc_enum_fmt, + .vidioc_enum_framesizes = ipu7_isys_vidioc_enum_framesizes, + .vidioc_g_fmt_vid_cap = ipu7_isys_vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = ipu7_isys_vidioc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = ipu7_isys_vidioc_try_fmt_vid_cap, + .vidioc_reqbufs = ipu7_isys_vidioc_reqbufs, + .vidioc_create_bufs = ipu7_isys_vidioc_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_expbuf = vb2_ioctl_expbuf, +}; + +static const struct media_entity_operations entity_ops = { + .link_validate = link_validate, +}; + +static const struct v4l2_file_operations isys_fops = { + .owner = THIS_MODULE, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, + .open = video_open, + .release = vb2_fop_release, +}; + +int ipu7_isys_fw_open(struct ipu7_isys *isys) +{ + struct ipu7_bus_device *adev = isys->adev; + int ret; + + ret = pm_runtime_resume_and_get(&adev->auxdev.dev); + if (ret < 0) + return ret; + + mutex_lock(&isys->mutex); + + if (isys->ref_count++) + goto unlock; + + /* + * Buffers could have been left to wrong queue at last closure. + * Move them now back to empty buffer queue. + */ + ipu7_cleanup_fw_msg_bufs(isys); + + ret = ipu7_fw_isys_open(isys); + if (ret < 0) + goto out; + +unlock: + mutex_unlock(&isys->mutex); + + return 0; +out: + isys->ref_count--; + mutex_unlock(&isys->mutex); + pm_runtime_put(&adev->auxdev.dev); + + return ret; +} + +void ipu7_isys_fw_close(struct ipu7_isys *isys) +{ + mutex_lock(&isys->mutex); + + isys->ref_count--; + + if (!isys->ref_count) + ipu7_fw_isys_close(isys); + + mutex_unlock(&isys->mutex); +} + +int ipu7_isys_setup_video(struct ipu7_isys_video *av, + struct media_entity **source_entity, int *nr_queues) +{ + const struct ipu7_isys_pixelformat *pfmt = + ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); + struct device *dev = &av->isys->adev->auxdev.dev; + struct media_pad *source_pad, *remote_pad; + struct v4l2_mbus_frame_desc_entry entry; + struct v4l2_subdev_route *route = NULL; + struct v4l2_subdev_route *r; + struct v4l2_subdev_state *state; + struct ipu7_isys_subdev *asd; + struct v4l2_subdev *remote_sd; + struct media_pipeline *pipeline; + int ret = -EINVAL; + + *nr_queues = 0; + + remote_pad = media_pad_remote_pad_unique(&av->pad); + if (IS_ERR(remote_pad)) { + dev_dbg(dev, "failed to get remote pad\n"); + return PTR_ERR(remote_pad); + } + + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + asd = to_ipu7_isys_subdev(remote_sd); + + source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]); + if (!source_pad) { + dev_dbg(dev, "No external source entity\n"); + return -ENODEV; + } + + *source_entity = source_pad->entity; + + state = v4l2_subdev_lock_and_get_active_state(remote_sd); + for_each_active_route(&state->routing, r) { + if (r->source_pad == remote_pad->index) + route = r; + } + + if (!route) { + v4l2_subdev_unlock_state(state); + dev_dbg(dev, "Failed to find route\n"); + return -ENODEV; + } + + v4l2_subdev_unlock_state(state); + + ret = ipu7_isys_csi2_get_remote_desc(route->sink_stream, + to_ipu7_isys_csi2(asd), + *source_entity, &entry, + nr_queues); + if (ret == -ENOIOCTLCMD) { + av->vc = 0; + av->dt = ipu7_isys_mbus_code_to_mipi(pfmt->code); + if (av->dt == 0xff) + return -EINVAL; + *nr_queues = 1; + } else if (*nr_queues && !ret) { + dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n", + entry.stream, entry.length, entry.bus.csi2.vc, + entry.bus.csi2.dt); + + av->vc = entry.bus.csi2.vc; + av->dt = entry.bus.csi2.dt; + } else { + dev_err(dev, "failed to get remote frame desc\n"); + return ret; + } + + pipeline = media_entity_pipeline(&av->vdev.entity); + if (!pipeline) + ret = video_device_pipeline_alloc_start(&av->vdev); + else + ret = video_device_pipeline_start(&av->vdev, pipeline); + if (ret < 0) { + dev_dbg(dev, "media pipeline start failed\n"); + return ret; + } + + av->stream = ipu7_isys_get_stream(av, asd); + if (!av->stream) { + video_device_pipeline_stop(&av->vdev); + dev_err(dev, "no available stream for firmware\n"); + return -EINVAL; + } + + return 0; +} + +/* + * Do everything that's needed to initialise things related to video + * buffer queue, video node, and the related media entity. The caller + * is expected to assign isys field and set the name of the video + * device. + */ +int ipu7_isys_video_init(struct ipu7_isys_video *av) +{ + struct v4l2_format format = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix = { + .width = 1920, + .height = 1080, + }, + }; + int ret; + + mutex_init(&av->mutex); + av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC | + V4L2_CAP_VIDEO_CAPTURE; + av->vdev.vfl_dir = VFL_DIR_RX; + + ret = ipu7_isys_queue_init(&av->aq); + if (ret) + goto out_mutex_destroy; + + av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; + ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad); + if (ret) + goto out_vb2_queue_cleanup; + + av->vdev.entity.ops = &entity_ops; + av->vdev.release = video_device_release_empty; + av->vdev.fops = &isys_fops; + av->vdev.v4l2_dev = &av->isys->v4l2_dev; + av->vdev.dev_parent = &av->isys->adev->isp->pdev->dev; + av->vdev.ioctl_ops = &ipu7_v4l2_ioctl_ops; + av->vdev.queue = &av->aq.vbq; + av->vdev.lock = &av->mutex; + + __ipu_isys_vidioc_try_fmt_vid_cap(av, &format); + av->pix_fmt = format.fmt.pix; + + set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags); + video_set_drvdata(&av->vdev, av); + + ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1); + if (ret) + goto out_media_entity_cleanup; + + return ret; + +out_media_entity_cleanup: + vb2_video_unregister_device(&av->vdev); + media_entity_cleanup(&av->vdev.entity); + +out_vb2_queue_cleanup: + vb2_queue_release(&av->aq.vbq); + +out_mutex_destroy: + mutex_destroy(&av->mutex); + + return ret; +} + +void ipu7_isys_video_cleanup(struct ipu7_isys_video *av) +{ + vb2_video_unregister_device(&av->vdev); + media_entity_cleanup(&av->vdev.entity); + mutex_destroy(&av->mutex); +} diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.h b/drivers/staging/media/ipu7/ipu7-isys-video.h new file mode 100644 index 000000000000..1ac1787fabef --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-isys-video.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#ifndef IPU7_ISYS_VIDEO_H +#define IPU7_ISYS_VIDEO_H + +#include +#include +#include +#include +#include + +#include +#include + +#include "ipu7-isys-queue.h" + +#define IPU_INSYS_OUTPUT_PINS 11U +#define IPU_ISYS_MAX_PARALLEL_SOF 2U + +struct file; +struct ipu7_isys; +struct ipu7_isys_csi2; +struct ipu7_insys_stream_cfg; +struct ipu7_isys_subdev; + +struct ipu7_isys_pixelformat { + u32 pixelformat; + u32 bpp; + u32 bpp_packed; + u32 code; + u32 css_pixelformat; +}; + +struct sequence_info { + unsigned int sequence; + u64 timestamp; +}; + +struct output_pin_data { + void (*pin_ready)(struct ipu7_isys_stream *stream, + struct ipu7_insys_resp *info); + struct ipu7_isys_queue *aq; +}; + +/* + * Align with firmware stream. Each stream represents a CSI virtual channel. + * May map to multiple video devices + */ +struct ipu7_isys_stream { + struct mutex mutex; + struct media_entity *source_entity; + atomic_t sequence; + atomic_t buf_id; + unsigned int seq_index; + struct sequence_info seq[IPU_ISYS_MAX_PARALLEL_SOF]; + int stream_source; + int stream_handle; + unsigned int nr_output_pins; + struct ipu7_isys_subdev *asd; + + int nr_queues; /* Number of capture queues */ + int nr_streaming; + int streaming; + struct list_head queues; + struct completion stream_open_completion; + struct completion stream_close_completion; + struct completion stream_start_completion; + struct completion stream_stop_completion; + struct ipu7_isys *isys; + + struct output_pin_data output_pins[IPU_INSYS_OUTPUT_PINS]; + int error; + u8 vc; +}; + +struct ipu7_isys_video { + struct ipu7_isys_queue aq; + /* Serialise access to other fields in the struct. */ + struct mutex mutex; + struct media_pad pad; + struct video_device vdev; + struct v4l2_pix_format pix_fmt; + struct ipu7_isys *isys; + struct ipu7_isys_csi2 *csi2; + struct ipu7_isys_stream *stream; + unsigned int streaming; + u8 vc; + u8 dt; +}; + +#define ipu7_isys_queue_to_video(__aq) \ + container_of(__aq, struct ipu7_isys_video, aq) + +extern const struct ipu7_isys_pixelformat ipu7_isys_pfmts[]; + +const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat); +int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av, + struct media_entity *source_entity, + int nr_queues); +int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state, + struct ipu7_isys_buffer_list *bl); +int ipu7_isys_fw_open(struct ipu7_isys *isys); +void ipu7_isys_fw_close(struct ipu7_isys *isys); +int ipu7_isys_setup_video(struct ipu7_isys_video *av, + struct media_entity **source_entity, int *nr_queues); +int ipu7_isys_video_init(struct ipu7_isys_video *av); +void ipu7_isys_video_cleanup(struct ipu7_isys_video *av); +void ipu7_isys_put_stream(struct ipu7_isys_stream *stream); +struct ipu7_isys_stream * +ipu7_isys_query_stream_by_handle(struct ipu7_isys *isys, + u8 stream_handle); +struct ipu7_isys_stream * +ipu7_isys_query_stream_by_source(struct ipu7_isys *isys, int source, u8 vc); +#endif /* IPU7_ISYS_VIDEO_H */ diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c new file mode 100644 index 000000000000..cb2f49f3e0fa --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-isys.c @@ -0,0 +1,1166 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "abi/ipu7_fw_isys_abi.h" + +#include "ipu7-bus.h" +#include "ipu7-buttress-regs.h" +#include "ipu7-cpd.h" +#include "ipu7-dma.h" +#include "ipu7-fw-isys.h" +#include "ipu7-mmu.h" +#include "ipu7-isys.h" +#include "ipu7-isys-csi2.h" +#include "ipu7-isys-csi-phy.h" +#include "ipu7-isys-csi2-regs.h" +#include "ipu7-isys-video.h" +#include "ipu7-platform-regs.h" + +#define ISYS_PM_QOS_VALUE 300 + +static int +isys_complete_ext_device_registration(struct ipu7_isys *isys, + struct v4l2_subdev *sd, + struct ipu7_isys_csi2_config *csi2) +{ + struct device *dev = &isys->adev->auxdev.dev; + unsigned int i; + int ret; + + v4l2_set_subdev_hostdata(sd, csi2); + + for (i = 0; i < sd->entity.num_pads; i++) { + if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) + break; + } + + if (i == sd->entity.num_pads) { + dev_warn(dev, "no source pad in external entity\n"); + ret = -ENOENT; + goto skip_unregister_subdev; + } + + ret = media_create_pad_link(&sd->entity, i, + &isys->csi2[csi2->port].asd.sd.entity, + 0, MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_warn(dev, "can't create link\n"); + goto skip_unregister_subdev; + } + + isys->csi2[csi2->port].nlanes = csi2->nlanes; + if (csi2->bus_type == V4L2_MBUS_CSI2_DPHY) + isys->csi2[csi2->port].phy_mode = PHY_MODE_DPHY; + else + isys->csi2[csi2->port].phy_mode = PHY_MODE_CPHY; + + return 0; + +skip_unregister_subdev: + v4l2_device_unregister_subdev(sd); + return ret; +} + +static void isys_stream_init(struct ipu7_isys *isys) +{ + unsigned int i; + + for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { + mutex_init(&isys->streams[i].mutex); + init_completion(&isys->streams[i].stream_open_completion); + init_completion(&isys->streams[i].stream_close_completion); + init_completion(&isys->streams[i].stream_start_completion); + init_completion(&isys->streams[i].stream_stop_completion); + INIT_LIST_HEAD(&isys->streams[i].queues); + isys->streams[i].isys = isys; + isys->streams[i].stream_handle = i; + isys->streams[i].vc = INVALID_VC_ID; + } +} + +static int isys_fw_log_init(struct ipu7_isys *isys) +{ + struct device *dev = &isys->adev->auxdev.dev; + struct isys_fw_log *fw_log; + void *log_buf; + + if (isys->fw_log) + return 0; + + fw_log = devm_kzalloc(dev, sizeof(*fw_log), GFP_KERNEL); + if (!fw_log) + return -ENOMEM; + + mutex_init(&fw_log->mutex); + + log_buf = devm_kzalloc(dev, FW_LOG_BUF_SIZE, GFP_KERNEL); + if (!log_buf) + return -ENOMEM; + + fw_log->head = log_buf; + fw_log->addr = log_buf; + fw_log->count = 0; + fw_log->size = 0; + + isys->fw_log = fw_log; + + return 0; +} + +/* The .bound() notifier callback when a match is found */ +static int isys_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asc) +{ + struct ipu7_isys *isys = container_of(notifier, + struct ipu7_isys, notifier); + struct sensor_async_sd *s_asd = + container_of(asc, struct sensor_async_sd, asc); + struct device *dev = &isys->adev->auxdev.dev; + int ret; + + ret = ipu_bridge_instantiate_vcm(sd->dev); + if (ret) { + dev_err(dev, "instantiate vcm failed\n"); + return ret; + } + + dev_info(dev, "bind %s nlanes is %d port is %d\n", + sd->name, s_asd->csi2.nlanes, s_asd->csi2.port); + isys_complete_ext_device_registration(isys, sd, &s_asd->csi2); + + return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); +} + +static int isys_notifier_complete(struct v4l2_async_notifier *notifier) +{ + struct ipu7_isys *isys = container_of(notifier, + struct ipu7_isys, notifier); + + dev_info(&isys->adev->auxdev.dev, + "All sensor registration completed.\n"); + + return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); +} + +static const struct v4l2_async_notifier_operations isys_async_ops = { + .bound = isys_notifier_bound, + .complete = isys_notifier_complete, +}; + +static int isys_notifier_init(struct ipu7_isys *isys) +{ + const struct ipu7_isys_internal_csi2_pdata *csi2 = + &isys->pdata->ipdata->csi2; + struct ipu7_device *isp = isys->adev->isp; + struct device *dev = &isp->pdev->dev; + unsigned int i; + int ret; + + v4l2_async_nf_init(&isys->notifier, &isys->v4l2_dev); + + for (i = 0; i < csi2->nports; i++) { + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_UNKNOWN + }; + struct sensor_async_sd *s_asd; + struct fwnode_handle *ep; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) + continue; + + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + if (ret) + goto err_parse; + + if (vep.bus_type != V4L2_MBUS_CSI2_DPHY && + vep.bus_type != V4L2_MBUS_CSI2_CPHY) { + ret = -EINVAL; + dev_err(dev, "unsupported bus type %d!\n", + vep.bus_type); + goto err_parse; + } + + s_asd = v4l2_async_nf_add_fwnode_remote(&isys->notifier, ep, + struct + sensor_async_sd); + if (IS_ERR(s_asd)) { + ret = PTR_ERR(s_asd); + goto err_parse; + } + + s_asd->csi2.port = vep.base.port; + s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes; + s_asd->csi2.bus_type = vep.bus_type; + + fwnode_handle_put(ep); + + continue; + +err_parse: + fwnode_handle_put(ep); + return ret; + } + + if (list_empty(&isys->notifier.waiting_list)) { + /* isys probe could continue with async subdevs missing */ + dev_warn(dev, "no subdev found in graph\n"); + return 0; + } + + isys->notifier.ops = &isys_async_ops; + ret = v4l2_async_nf_register(&isys->notifier); + if (ret) { + dev_err(dev, "failed to register async notifier(%d)\n", ret); + v4l2_async_nf_cleanup(&isys->notifier); + } + + return ret; +} + +static void isys_notifier_cleanup(struct ipu7_isys *isys) +{ + v4l2_async_nf_unregister(&isys->notifier); + v4l2_async_nf_cleanup(&isys->notifier); +} + +static void isys_unregister_video_devices(struct ipu7_isys *isys) +{ + const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = + &isys->pdata->ipdata->csi2; + unsigned int i, j; + + for (i = 0; i < csi2_pdata->nports; i++) + for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) + ipu7_isys_video_cleanup(&isys->csi2[i].av[j]); +} + +static int isys_register_video_devices(struct ipu7_isys *isys) +{ + const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = + &isys->pdata->ipdata->csi2; + unsigned int i, j; + int ret; + + for (i = 0; i < csi2_pdata->nports; i++) { + for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { + struct ipu7_isys_video *av = &isys->csi2[i].av[j]; + + snprintf(av->vdev.name, sizeof(av->vdev.name), + IPU_ISYS_ENTITY_PREFIX " ISYS Capture %u", + i * IPU7_NR_OF_CSI2_SRC_PADS + j); + av->isys = isys; + av->aq.vbq.buf_struct_size = + sizeof(struct ipu7_isys_video_buffer); + + ret = ipu7_isys_video_init(av); + if (ret) + goto fail; + } + } + + return 0; + +fail: + i = i + 1U; + while (i--) { + while (j--) + ipu7_isys_video_cleanup(&isys->csi2[i].av[j]); + j = IPU7_NR_OF_CSI2_SRC_PADS; + } + + return ret; +} + +static void isys_csi2_unregister_subdevices(struct ipu7_isys *isys) +{ + const struct ipu7_isys_internal_csi2_pdata *csi2 = + &isys->pdata->ipdata->csi2; + unsigned int i; + + for (i = 0; i < csi2->nports; i++) + ipu7_isys_csi2_cleanup(&isys->csi2[i]); +} + +static int isys_csi2_register_subdevices(struct ipu7_isys *isys) +{ + const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = + &isys->pdata->ipdata->csi2; + unsigned int i; + int ret; + + for (i = 0; i < csi2_pdata->nports; i++) { + ret = ipu7_isys_csi2_init(&isys->csi2[i], isys, + isys->pdata->base + + csi2_pdata->offsets[i], i); + if (ret) + goto fail; + } + + isys->isr_csi2_mask = IPU7_CSI_RX_LEGACY_IRQ_MASK; + + return 0; + +fail: + while (i--) + ipu7_isys_csi2_cleanup(&isys->csi2[i]); + + return ret; +} + +static int isys_csi2_create_media_links(struct ipu7_isys *isys) +{ + const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = + &isys->pdata->ipdata->csi2; + struct device *dev = &isys->adev->auxdev.dev; + struct media_entity *sd; + unsigned int i, j; + int ret; + + for (i = 0; i < csi2_pdata->nports; i++) { + sd = &isys->csi2[i].asd.sd.entity; + + for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { + struct ipu7_isys_video *av = &isys->csi2[i].av[j]; + + ret = media_create_pad_link(sd, IPU7_CSI2_PAD_SRC + j, + &av->vdev.entity, 0, 0); + if (ret) { + dev_err(dev, "CSI2 can't create link\n"); + return ret; + } + + av->csi2 = &isys->csi2[i]; + } + } + + return 0; +} + +static int isys_register_devices(struct ipu7_isys *isys) +{ + struct device *dev = &isys->adev->auxdev.dev; + struct pci_dev *pdev = isys->adev->isp->pdev; + int ret; + + media_device_pci_init(&isys->media_dev, + pdev, IPU_MEDIA_DEV_MODEL_NAME); + + strscpy(isys->v4l2_dev.name, isys->media_dev.model, + sizeof(isys->v4l2_dev.name)); + + ret = media_device_register(&isys->media_dev); + if (ret < 0) + goto out_media_device_unregister; + + isys->v4l2_dev.mdev = &isys->media_dev; + isys->v4l2_dev.ctrl_handler = NULL; + + ret = v4l2_device_register(dev, &isys->v4l2_dev); + if (ret < 0) + goto out_media_device_unregister; + + ret = isys_register_video_devices(isys); + if (ret) + goto out_v4l2_device_unregister; + + ret = isys_csi2_register_subdevices(isys); + if (ret) + goto out_video_unregister_device; + + ret = isys_csi2_create_media_links(isys); + if (ret) + goto out_csi2_unregister_subdevices; + + ret = isys_notifier_init(isys); + if (ret) + goto out_csi2_unregister_subdevices; + + return 0; + +out_csi2_unregister_subdevices: + isys_csi2_unregister_subdevices(isys); + +out_video_unregister_device: + isys_unregister_video_devices(isys); + +out_v4l2_device_unregister: + v4l2_device_unregister(&isys->v4l2_dev); + +out_media_device_unregister: + media_device_unregister(&isys->media_dev); + media_device_cleanup(&isys->media_dev); + + dev_err(dev, "failed to register isys devices\n"); + + return ret; +} + +static void isys_unregister_devices(struct ipu7_isys *isys) +{ + isys_unregister_video_devices(isys); + isys_csi2_unregister_subdevices(isys); + v4l2_device_unregister(&isys->v4l2_dev); + media_device_unregister(&isys->media_dev); + media_device_cleanup(&isys->media_dev); +} + +static void enable_csi2_legacy_irq(struct ipu7_isys *isys, bool enable) +{ + u32 offset = IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE; + void __iomem *base = isys->pdata->base; + u32 mask = isys->isr_csi2_mask; + + if (!enable) { + writel(mask, base + offset + IRQ_CTL_CLEAR); + writel(0, base + offset + IRQ_CTL_ENABLE); + return; + } + + writel(mask, base + offset + IRQ_CTL_EDGE); + writel(mask, base + offset + IRQ_CTL_CLEAR); + writel(mask, base + offset + IRQ_CTL_MASK); + writel(mask, base + offset + IRQ_CTL_ENABLE); +} + +static void enable_to_sw_irq(struct ipu7_isys *isys, bool enable) +{ + void __iomem *base = isys->pdata->base; + u32 mask = IS_UC_TO_SW_IRQ_MASK; + u32 offset = IS_UC_CTRL_BASE; + + if (!enable) { + writel(0, base + offset + TO_SW_IRQ_CNTL_ENABLE); + return; + } + + writel(mask, base + offset + TO_SW_IRQ_CNTL_CLEAR); + writel(mask, base + offset + TO_SW_IRQ_CNTL_MASK_N); + writel(mask, base + offset + TO_SW_IRQ_CNTL_ENABLE); +} + +void ipu7_isys_setup_hw(struct ipu7_isys *isys) +{ + u32 offset; + void __iomem *base = isys->pdata->base; + + /* soft reset */ + offset = IS_IO_GPREGS_BASE; + + writel(0x0, base + offset + CLK_EN_TXCLKESC); + /* Update if ISYS freq updated (0: 400/1, 1:400/2, 63:400/64) */ + writel(0x0, base + offset + CLK_DIV_FACTOR_IS_CLK); + /* correct the initial printf configuration */ + writel(0x200, base + IS_UC_CTRL_BASE + PRINTF_AXI_CNTL); + + enable_to_sw_irq(isys, 1); + enable_csi2_legacy_irq(isys, 1); +} + +static void isys_cleanup_hw(struct ipu7_isys *isys) +{ + enable_csi2_legacy_irq(isys, 0); + enable_to_sw_irq(isys, 0); +} + +static int isys_runtime_pm_resume(struct device *dev) +{ + struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); + struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); + struct ipu7_device *isp = adev->isp; + unsigned long flags; + int ret; + + if (!isys) + return 0; + + ret = ipu7_mmu_hw_init(adev->mmu); + if (ret) + return ret; + + cpu_latency_qos_update_request(&isys->pm_qos, ISYS_PM_QOS_VALUE); + + ret = ipu_buttress_start_tsc_sync(isp); + if (ret) + return ret; + + spin_lock_irqsave(&isys->power_lock, flags); + isys->power = 1; + spin_unlock_irqrestore(&isys->power_lock, flags); + + return 0; +} + +static int isys_runtime_pm_suspend(struct device *dev) +{ + struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); + struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); + unsigned long flags; + + if (!isys) + return 0; + + isys_cleanup_hw(isys); + + spin_lock_irqsave(&isys->power_lock, flags); + isys->power = 0; + spin_unlock_irqrestore(&isys->power_lock, flags); + + cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); + + ipu7_mmu_hw_cleanup(adev->mmu); + + return 0; +} + +static int isys_suspend(struct device *dev) +{ + struct ipu7_isys *isys = dev_get_drvdata(dev); + + /* If stream is open, refuse to suspend */ + if (isys->stream_opened) + return -EBUSY; + + return 0; +} + +static int isys_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops isys_pm_ops = { + .runtime_suspend = isys_runtime_pm_suspend, + .runtime_resume = isys_runtime_pm_resume, + .suspend = isys_suspend, + .resume = isys_resume, +}; + +static void isys_remove(struct auxiliary_device *auxdev) +{ + struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); + struct isys_fw_msgs *fwmsg, *safe; + struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); + + for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) + mutex_destroy(&isys->streams[i].mutex); + + list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) + ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), + fwmsg, fwmsg->dma_addr, 0); + + list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) + ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), + fwmsg, fwmsg->dma_addr, 0); + + isys_notifier_cleanup(isys); + isys_unregister_devices(isys); + + cpu_latency_qos_remove_request(&isys->pm_qos); + + mutex_destroy(&isys->stream_mutex); + mutex_destroy(&isys->mutex); +} + +static int alloc_fw_msg_bufs(struct ipu7_isys *isys, int amount) +{ + struct ipu7_bus_device *adev = isys->adev; + struct isys_fw_msgs *addr; + dma_addr_t dma_addr; + unsigned long flags; + unsigned int i; + + for (i = 0; i < amount; i++) { + addr = ipu7_dma_alloc(adev, sizeof(struct isys_fw_msgs), + &dma_addr, GFP_KERNEL, 0); + if (!addr) + break; + addr->dma_addr = dma_addr; + + spin_lock_irqsave(&isys->listlock, flags); + list_add(&addr->head, &isys->framebuflist); + spin_unlock_irqrestore(&isys->listlock, flags); + } + + if (i == amount) + return 0; + + spin_lock_irqsave(&isys->listlock, flags); + while (!list_empty(&isys->framebuflist)) { + addr = list_first_entry(&isys->framebuflist, + struct isys_fw_msgs, head); + list_del(&addr->head); + spin_unlock_irqrestore(&isys->listlock, flags); + ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), + addr, addr->dma_addr, 0); + spin_lock_irqsave(&isys->listlock, flags); + } + spin_unlock_irqrestore(&isys->listlock, flags); + + return -ENOMEM; +} + +struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream) +{ + struct device *dev = &stream->isys->adev->auxdev.dev; + struct ipu7_isys *isys = stream->isys; + struct isys_fw_msgs *msg; + unsigned long flags; + int ret; + + spin_lock_irqsave(&isys->listlock, flags); + if (list_empty(&isys->framebuflist)) { + spin_unlock_irqrestore(&isys->listlock, flags); + dev_dbg(dev, "Frame buffer list empty\n"); + + ret = alloc_fw_msg_bufs(isys, 5); + if (ret < 0) + return NULL; + + spin_lock_irqsave(&isys->listlock, flags); + if (list_empty(&isys->framebuflist)) { + spin_unlock_irqrestore(&isys->listlock, flags); + dev_err(dev, "Frame list empty\n"); + return NULL; + } + } + msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head); + list_move(&msg->head, &isys->framebuflist_fw); + spin_unlock_irqrestore(&isys->listlock, flags); + memset(&msg->fw_msg, 0, sizeof(msg->fw_msg)); + + return msg; +} + +void ipu7_cleanup_fw_msg_bufs(struct ipu7_isys *isys) +{ + struct isys_fw_msgs *fwmsg, *fwmsg0; + unsigned long flags; + + spin_lock_irqsave(&isys->listlock, flags); + list_for_each_entry_safe(fwmsg, fwmsg0, &isys->framebuflist_fw, head) + list_move(&fwmsg->head, &isys->framebuflist); + spin_unlock_irqrestore(&isys->listlock, flags); +} + +void ipu7_put_fw_msg_buf(struct ipu7_isys *isys, uintptr_t data) +{ + struct isys_fw_msgs *msg; + void *ptr = (void *)data; + unsigned long flags; + + if (WARN_ON_ONCE(!ptr)) + return; + + spin_lock_irqsave(&isys->listlock, flags); + msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy); + list_move(&msg->head, &isys->framebuflist); + spin_unlock_irqrestore(&isys->listlock, flags); +} + +static int isys_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *auxdev_id) +{ + const struct ipu7_isys_internal_csi2_pdata *csi2_pdata; + struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); + struct ipu7_device *isp = adev->isp; + struct ipu7_isys *isys; + int ret = 0; + + if (!isp->ipu7_bus_ready_to_probe) + return -EPROBE_DEFER; + + isys = devm_kzalloc(&auxdev->dev, sizeof(*isys), GFP_KERNEL); + if (!isys) + return -ENOMEM; + + ret = pm_runtime_resume_and_get(&auxdev->dev); + if (ret < 0) + return ret; + + adev->auxdrv_data = + (const struct ipu7_auxdrv_data *)auxdev_id->driver_data; + adev->auxdrv = to_auxiliary_drv(auxdev->dev.driver); + isys->adev = adev; + isys->pdata = adev->pdata; + + INIT_LIST_HEAD(&isys->requests); + csi2_pdata = &isys->pdata->ipdata->csi2; + + isys->csi2 = devm_kcalloc(&auxdev->dev, csi2_pdata->nports, + sizeof(*isys->csi2), GFP_KERNEL); + if (!isys->csi2) { + ret = -ENOMEM; + goto out_runtime_put; + } + + ret = ipu7_mmu_hw_init(adev->mmu); + if (ret) + goto out_runtime_put; + + spin_lock_init(&isys->streams_lock); + spin_lock_init(&isys->power_lock); + isys->power = 0; + + mutex_init(&isys->mutex); + mutex_init(&isys->stream_mutex); + + spin_lock_init(&isys->listlock); + INIT_LIST_HEAD(&isys->framebuflist); + INIT_LIST_HEAD(&isys->framebuflist_fw); + + dev_set_drvdata(&auxdev->dev, isys); + + isys->icache_prefetch = 0; + isys->phy_rext_cal = 0; + + isys_stream_init(isys); + + cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); + ret = alloc_fw_msg_bufs(isys, 20); + if (ret < 0) + goto out_cleanup_isys; + + ret = ipu7_fw_isys_init(isys); + if (ret) + goto out_cleanup_isys; + + ret = isys_register_devices(isys); + if (ret) + goto out_cleanup_fw; + + ret = isys_fw_log_init(isys); + if (ret) + goto out_cleanup; + + ipu7_mmu_hw_cleanup(adev->mmu); + pm_runtime_put(&auxdev->dev); + + return 0; + +out_cleanup: + isys_unregister_devices(isys); +out_cleanup_fw: + ipu7_fw_isys_release(isys); +out_cleanup_isys: + cpu_latency_qos_remove_request(&isys->pm_qos); + + for (unsigned int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) + mutex_destroy(&isys->streams[i].mutex); + + mutex_destroy(&isys->mutex); + mutex_destroy(&isys->stream_mutex); + + ipu7_mmu_hw_cleanup(adev->mmu); + +out_runtime_put: + pm_runtime_put(&auxdev->dev); + + return ret; +} + +struct ipu7_csi2_error { + const char *error_string; + bool is_info_only; +}; + +/* + * Strings corresponding to CSI-2 receiver errors are here. + * Corresponding macros are defined in the header file. + */ +static const struct ipu7_csi2_error dphy_rx_errors[] = { + { "Error handler FIFO full", false }, + { "Reserved Short Packet encoding detected", true }, + { "Reserved Long Packet encoding detected", true }, + { "Received packet is too short", false}, + { "Received packet is too long", false}, + { "Short packet discarded due to errors", false }, + { "Long packet discarded due to errors", false }, + { "CSI Combo Rx interrupt", false }, + { "IDI CDC FIFO overflow(remaining bits are reserved as 0)", false }, + { "Received NULL packet", true }, + { "Received blanking packet", true }, + { "Tie to 0", true }, + { } +}; + +static void ipu7_isys_register_errors(struct ipu7_isys_csi2 *csi2) +{ + u32 offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); + u32 status = readl(csi2->base + offset + IRQ_CTL_STATUS); + u32 mask = IPU7_CSI_RX_ERROR_IRQ_MASK; + + if (!status) + return; + + dev_dbg(&csi2->isys->adev->auxdev.dev, "csi2-%u error status 0x%08x\n", + csi2->port, status); + + writel(status & mask, csi2->base + offset + IRQ_CTL_CLEAR); + csi2->receiver_errors |= status & mask; +} + +static void ipu7_isys_csi2_error(struct ipu7_isys_csi2 *csi2) +{ + struct ipu7_csi2_error const *errors; + unsigned int i; + u32 status; + + /* Register errors once more in case of error interrupts are disabled */ + ipu7_isys_register_errors(csi2); + status = csi2->receiver_errors; + csi2->receiver_errors = 0; + errors = dphy_rx_errors; + + for (i = 0; i < CSI_RX_NUM_ERRORS_IN_IRQ; i++) { + if (status & BIT(i)) + dev_err_ratelimited(&csi2->isys->adev->auxdev.dev, + "csi2-%i error: %s\n", + csi2->port, + errors[i].error_string); + } +} + +struct resp_to_msg { + enum ipu7_insys_resp_type type; + const char *msg; +}; + +static const struct resp_to_msg is_fw_msg[] = { + {IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE, + "IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE"}, + {IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, + "IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK"}, + {IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK, + "IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK"}, + {IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK, + "IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK"}, + {IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK, + "IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK"}, + {IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK, + "IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK"}, + {IPU_INSYS_RESP_TYPE_PIN_DATA_READY, + "IPU_INSYS_RESP_TYPE_PIN_DATA_READY"}, + {IPU_INSYS_RESP_TYPE_FRAME_SOF, "IPU_INSYS_RESP_TYPE_FRAME_SOF"}, + {IPU_INSYS_RESP_TYPE_FRAME_EOF, "IPU_INSYS_RESP_TYPE_FRAME_EOF"}, + {IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, + "IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE"}, + {IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE, + "IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE"}, + {N_IPU_INSYS_RESP_TYPE, "N_IPU_INSYS_RESP_TYPE"}, +}; + +int isys_isr_one(struct ipu7_bus_device *adev) +{ + struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); + struct ipu7_isys_stream *stream = NULL; + struct device *dev = &adev->auxdev.dev; + struct ipu7_isys_csi2 *csi2 = NULL; + struct ia_gofo_msg_err err_info; + struct ipu7_insys_resp *resp; + u64 ts; + + if (!isys->adev->syscom) + return 1; + + resp = ipu7_fw_isys_get_resp(isys); + if (!resp) + return 1; + if (resp->type >= N_IPU_INSYS_RESP_TYPE) { + dev_err(dev, "Unknown response type %u stream %u\n", + resp->type, resp->stream_id); + ipu7_fw_isys_put_resp(isys); + return 1; + } + + err_info = resp->error_info; + ts = ((u64)resp->timestamp[1] << 32) | resp->timestamp[0]; + if (err_info.err_group == INSYS_MSG_ERR_GROUP_CAPTURE && + err_info.err_code == INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP) { + /* receive a sp w/o command, firmware drop it */ + dev_dbg(dev, "FRAME DROP: %02u %s stream %u\n", + resp->type, is_fw_msg[resp->type].msg, + resp->stream_id); + dev_dbg(dev, "\tpin %u buf_id %llx frame %u\n", + resp->pin_id, resp->buf_id, resp->frame_id); + dev_dbg(dev, "\terror group %u code %u details [%u %u]\n", + err_info.err_group, err_info.err_code, + err_info.err_detail[0], err_info.err_detail[1]); + } else if (!IA_GOFO_MSG_ERR_IS_OK(err_info)) { + dev_err(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n", + resp->type, is_fw_msg[resp->type].msg, resp->stream_id, + resp->pin_id, resp->buf_id, resp->frame_id); + dev_err(dev, "\terror group %u code %u details [%u %u]\n", + err_info.err_group, err_info.err_code, + err_info.err_detail[0], err_info.err_detail[1]); + } else { + dev_dbg(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n", + resp->type, is_fw_msg[resp->type].msg, resp->stream_id, + resp->pin_id, resp->buf_id, resp->frame_id); + dev_dbg(dev, "\tts %llu\n", ts); + } + + if (resp->stream_id >= IPU_ISYS_MAX_STREAMS) { + dev_err(dev, "bad stream handle %u\n", + resp->stream_id); + goto leave; + } + + stream = ipu7_isys_query_stream_by_handle(isys, resp->stream_id); + if (!stream) { + dev_err(dev, "stream of stream_handle %u is unused\n", + resp->stream_id); + goto leave; + } + + stream->error = err_info.err_code; + + if (stream->asd) + csi2 = ipu7_isys_subdev_to_csi2(stream->asd); + + switch (resp->type) { + case IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE: + complete(&stream->stream_open_completion); + break; + case IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK: + complete(&stream->stream_close_completion); + break; + case IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: + complete(&stream->stream_start_completion); + break; + case IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK: + complete(&stream->stream_stop_completion); + break; + case IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK: + complete(&stream->stream_stop_completion); + break; + case IPU_INSYS_RESP_TYPE_PIN_DATA_READY: + /* + * firmware only release the capture msg until software + * get pin_data_ready event + */ + ipu7_put_fw_msg_buf(ipu7_bus_get_drvdata(adev), resp->buf_id); + if (resp->pin_id < IPU_INSYS_OUTPUT_PINS && + stream->output_pins[resp->pin_id].pin_ready) + stream->output_pins[resp->pin_id].pin_ready(stream, + resp); + else + dev_err(dev, "No handler for pin %u ready\n", + resp->pin_id); + if (csi2) + ipu7_isys_csi2_error(csi2); + + break; + case IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK: + break; + case IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE: + case IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE: + break; + case IPU_INSYS_RESP_TYPE_FRAME_SOF: + if (csi2) + ipu7_isys_csi2_sof_event_by_stream(stream); + + stream->seq[stream->seq_index].sequence = + atomic_read(&stream->sequence) - 1U; + stream->seq[stream->seq_index].timestamp = ts; + dev_dbg(dev, + "SOF: stream %u frame %u (index %u), ts 0x%16.16llx\n", + resp->stream_id, resp->frame_id, + stream->seq[stream->seq_index].sequence, ts); + stream->seq_index = (stream->seq_index + 1U) + % IPU_ISYS_MAX_PARALLEL_SOF; + break; + case IPU_INSYS_RESP_TYPE_FRAME_EOF: + if (csi2) + ipu7_isys_csi2_eof_event_by_stream(stream); + + dev_dbg(dev, "eof: stream %d(index %u) ts 0x%16.16llx\n", + resp->stream_id, + stream->seq[stream->seq_index].sequence, ts); + break; + default: + dev_err(dev, "Unknown response type %u stream %u\n", + resp->type, resp->stream_id); + break; + } + + ipu7_isys_put_stream(stream); +leave: + ipu7_fw_isys_put_resp(isys); + + return 0; +} + +static void ipu7_isys_csi2_isr(struct ipu7_isys_csi2 *csi2) +{ + struct device *dev = &csi2->isys->adev->auxdev.dev; + struct ipu7_device *isp = csi2->isys->adev->isp; + struct ipu7_isys_stream *s; + u32 sync, offset; + u32 fe = 0; + u8 vc; + + ipu7_isys_register_errors(csi2); + + offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); + sync = readl(csi2->base + offset + IRQ_CTL_STATUS); + writel(sync, csi2->base + offset + IRQ_CTL_CLEAR); + dev_dbg(dev, "csi2-%u sync status 0x%08x\n", csi2->port, sync); + + if (!is_ipu7(isp->hw_ver)) { + fe = readl(csi2->base + offset + IRQ1_CTL_STATUS); + writel(fe, csi2->base + offset + IRQ1_CTL_CLEAR); + dev_dbg(dev, "csi2-%u FE status 0x%08x\n", csi2->port, fe); + } + + for (vc = 0; vc < IPU7_NR_OF_CSI2_VC && (sync || fe); vc++) { + s = ipu7_isys_query_stream_by_source(csi2->isys, + csi2->asd.source, vc); + if (!s) + continue; + + if (!is_ipu7(isp->hw_ver)) { + if (sync & IPU7P5_CSI_RX_SYNC_FS_VC & (1U << vc)) + ipu7_isys_csi2_sof_event_by_stream(s); + + if (fe & IPU7P5_CSI_RX_SYNC_FE_VC & (1U << vc)) + ipu7_isys_csi2_eof_event_by_stream(s); + } else { + if (sync & IPU7_CSI_RX_SYNC_FS_VC & (1U << (vc * 2))) + ipu7_isys_csi2_sof_event_by_stream(s); + + if (sync & IPU7_CSI_RX_SYNC_FE_VC & (2U << (vc * 2))) + ipu7_isys_csi2_eof_event_by_stream(s); + } + } +} + +static irqreturn_t isys_isr(struct ipu7_bus_device *adev) +{ + struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); + u32 status_csi, status_sw, csi_offset, sw_offset; + struct device *dev = &isys->adev->auxdev.dev; + void __iomem *base = isys->pdata->base; + + spin_lock(&isys->power_lock); + if (!isys->power) { + spin_unlock(&isys->power_lock); + return IRQ_NONE; + } + + csi_offset = IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE; + sw_offset = IS_BASE; + + status_csi = readl(base + csi_offset + IRQ_CTL_STATUS); + status_sw = readl(base + sw_offset + TO_SW_IRQ_CNTL_STATUS); + if (!status_csi && !status_sw) { + spin_unlock(&isys->power_lock); + return IRQ_NONE; + } + + if (status_csi) + dev_dbg(dev, "status csi 0x%08x\n", status_csi); + if (status_sw) + dev_dbg(dev, "status to_sw 0x%08x\n", status_sw); + + do { + writel(status_sw, base + sw_offset + TO_SW_IRQ_CNTL_CLEAR); + writel(status_csi, base + csi_offset + IRQ_CTL_CLEAR); + + if (isys->isr_csi2_mask & status_csi) { + unsigned int i; + + for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) { + /* irq from not enabled port */ + if (!isys->csi2[i].base) + continue; + if (status_csi & isys->csi2[i].legacy_irq_mask) + ipu7_isys_csi2_isr(&isys->csi2[i]); + } + } + + if (!isys_isr_one(adev)) + status_sw = TO_SW_IRQ_FW; + else + status_sw = 0; + + status_csi = readl(base + csi_offset + IRQ_CTL_STATUS); + status_sw |= readl(base + sw_offset + TO_SW_IRQ_CNTL_STATUS); + } while ((status_csi & isys->isr_csi2_mask) || + (status_sw & TO_SW_IRQ_FW)); + + writel(TO_SW_IRQ_MASK, base + sw_offset + TO_SW_IRQ_CNTL_MASK_N); + + spin_unlock(&isys->power_lock); + + return IRQ_HANDLED; +} + +static const struct ipu7_auxdrv_data ipu7_isys_auxdrv_data = { + .isr = isys_isr, + .isr_threaded = NULL, + .wake_isr_thread = false, +}; + +static const struct auxiliary_device_id ipu7_isys_id_table[] = { + { + .name = "intel_ipu7.isys", + .driver_data = (kernel_ulong_t)&ipu7_isys_auxdrv_data, + }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, ipu7_isys_id_table); + +static struct auxiliary_driver isys_driver = { + .name = IPU_ISYS_NAME, + .probe = isys_probe, + .remove = isys_remove, + .id_table = ipu7_isys_id_table, + .driver = { + .pm = &isys_pm_ops, + }, +}; + +module_auxiliary_driver(isys_driver); + +MODULE_AUTHOR("Bingbu Cao "); +MODULE_AUTHOR("Tianshu Qiu "); +MODULE_AUTHOR("Qingwu Zhang "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel ipu7 input system driver"); +MODULE_IMPORT_NS("INTEL_IPU7"); +MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h new file mode 100644 index 000000000000..ef1ab1b42f6c --- /dev/null +++ b/drivers/staging/media/ipu7/ipu7-isys.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 - 2025 Intel Corporation + */ + +#ifndef IPU7_ISYS_H +#define IPU7_ISYS_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "abi/ipu7_fw_msg_abi.h" +#include "abi/ipu7_fw_isys_abi.h" + +#include "ipu7.h" +#include "ipu7-isys-csi2.h" +#include "ipu7-isys-video.h" + +#define IPU_ISYS_ENTITY_PREFIX "Intel IPU7" + +/* FW support max 16 streams */ +#define IPU_ISYS_MAX_STREAMS 16U + +/* + * Current message queue configuration. These must be big enough + * so that they never gets full. Queues are located in system memory + */ +#define IPU_ISYS_SIZE_RECV_QUEUE 40U +#define IPU_ISYS_SIZE_LOG_QUEUE 256U +#define IPU_ISYS_SIZE_SEND_QUEUE 40U +#define IPU_ISYS_NUM_RECV_QUEUE 1U + +#define IPU_ISYS_MIN_WIDTH 2U +#define IPU_ISYS_MIN_HEIGHT 2U +#define IPU_ISYS_MAX_WIDTH 8160U +#define IPU_ISYS_MAX_HEIGHT 8190U + +#define FW_CALL_TIMEOUT_JIFFIES \ + msecs_to_jiffies(IPU_LIB_CALL_TIMEOUT_MS) + +struct isys_fw_log { + struct mutex mutex; /* protect whole struct */ + void *head; + void *addr; + u32 count; /* running counter of log */ + u32 size; /* actual size of log content, in bits */ +}; + +/* + * struct ipu7_isys + * + * @media_dev: Media device + * @v4l2_dev: V4L2 device + * @adev: ISYS bus device + * @power: Is ISYS powered on or not? + * @isr_bits: Which bits does the ISR handle? + * @power_lock: Serialise access to power (power state in general) + * @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers + * @streams_lock: serialise access to streams + * @streams: streams per firmware stream ID + * @syscom: fw communication layer context + * @ref_count: total number of callers fw open + * @mutex: serialise access isys video open/release related operations + * @stream_mutex: serialise stream start and stop, queueing requests + * @pdata: platform data pointer + * @csi2: CSI-2 receivers + */ +struct ipu7_isys { + struct media_device media_dev; + struct v4l2_device v4l2_dev; + struct ipu7_bus_device *adev; + + int power; + spinlock_t power_lock; /* Serialise access to power */ + u32 isr_csi2_mask; + u32 csi2_rx_ctrl_cached; + spinlock_t streams_lock; + struct ipu7_isys_stream streams[IPU_ISYS_MAX_STREAMS]; + int streams_ref_count[IPU_ISYS_MAX_STREAMS]; + u32 phy_rext_cal; + bool icache_prefetch; + bool csi2_cse_ipc_not_supported; + unsigned int ref_count; + unsigned int stream_opened; + + struct mutex mutex; /* Serialise isys video open/release related */ + struct mutex stream_mutex; /* Stream start, stop, queueing reqs */ + + struct ipu7_isys_pdata *pdata; + + struct ipu7_isys_csi2 *csi2; + struct isys_fw_log *fw_log; + + struct list_head requests; + struct pm_qos_request pm_qos; + spinlock_t listlock; /* Protect framebuflist */ + struct list_head framebuflist; + struct list_head framebuflist_fw; + struct v4l2_async_notifier notifier; + + struct ipu7_insys_config *subsys_config; + dma_addr_t subsys_config_dma_addr; +}; + +struct isys_fw_msgs { + union { + u64 dummy; + struct ipu7_insys_buffset frame; + struct ipu7_insys_stream_cfg stream; + } fw_msg; + struct list_head head; + dma_addr_t dma_addr; +}; + +struct ipu7_isys_csi2_config { + unsigned int nlanes; + unsigned int port; + enum v4l2_mbus_type bus_type; +}; + +struct sensor_async_sd { + struct v4l2_async_connection asc; + struct ipu7_isys_csi2_config csi2; +}; + +struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream); +void ipu7_put_fw_msg_buf(struct ipu7_isys *isys, uintptr_t data); +void ipu7_cleanup_fw_msg_bufs(struct ipu7_isys *isys); +int isys_isr_one(struct ipu7_bus_device *adev); +void ipu7_isys_setup_hw(struct ipu7_isys *isys); +#endif /* IPU7_ISYS_H */ diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c index b3c4789fa5c8..4a70d3527cb7 100644 --- a/drivers/staging/media/ipu7/ipu7.c +++ b/drivers/staging/media/ipu7/ipu7.c @@ -2745,10 +2745,8 @@ static int ipu7_runtime_resume(struct device *dev) } static const struct dev_pm_ops ipu7_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(&ipu7_suspend, &ipu7_resume) - SET_RUNTIME_PM_OPS(&ipu7_suspend, /* Same as in suspend flow */ - &ipu7_runtime_resume, - NULL) + SYSTEM_SLEEP_PM_OPS(&ipu7_suspend, &ipu7_resume) + RUNTIME_PM_OPS(&ipu7_suspend, &ipu7_runtime_resume, NULL) }; static const struct pci_device_id ipu7_pci_tbl[] = { -- cgit v1.2.3 From c5ddd03bc5c4ac92703060001d8912cacb3c2765 Mon Sep 17 00:00:00 2001 From: Bingbu Cao Date: Thu, 29 May 2025 12:13:23 +0800 Subject: media: staging/ipu7: add Makefile, Kconfig and to-do file for IPU7 Add Kconfig and Makefile for IPU7 driver and also update the Makefile to build the IPU7 driver. Also add a to-do file to list the TODOs. Signed-off-by: Bingbu Cao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/Kconfig | 2 ++ drivers/staging/media/Makefile | 1 + drivers/staging/media/ipu7/Kconfig | 18 ++++++++++++++++++ drivers/staging/media/ipu7/Makefile | 23 +++++++++++++++++++++++ drivers/staging/media/ipu7/TODO | 28 ++++++++++++++++++++++++++++ 5 files changed, 72 insertions(+) create mode 100644 drivers/staging/media/ipu7/Kconfig create mode 100644 drivers/staging/media/ipu7/Makefile create mode 100644 drivers/staging/media/ipu7/TODO (limited to 'drivers/staging') diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index b44214854399..043ffbc472fb 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -28,6 +28,8 @@ source "drivers/staging/media/imx/Kconfig" source "drivers/staging/media/ipu3/Kconfig" +source "drivers/staging/media/ipu7/Kconfig" + source "drivers/staging/media/max96712/Kconfig" source "drivers/staging/media/meson/vdec/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index ad4e9619a9e0..177dfe95289b 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -9,4 +9,5 @@ obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive/ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ +obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ obj-$(CONFIG_DVB_AV7110) += av7110/ diff --git a/drivers/staging/media/ipu7/Kconfig b/drivers/staging/media/ipu7/Kconfig new file mode 100644 index 000000000000..5b800e9b09c4 --- /dev/null +++ b/drivers/staging/media/ipu7/Kconfig @@ -0,0 +1,18 @@ +config VIDEO_INTEL_IPU7 + tristate "Intel IPU7 driver" + depends on ACPI || COMPILE_TEST + depends on VIDEO_DEV + depends on X86 && HAS_DMA + depends on IPU_BRIDGE || !IPU_BRIDGE + select AUXILIARY_BUS + select IOMMU_IOVA + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + select VIDEOBUF2_DMA_SG + select V4L2_FWNODE + help + This is the 7th Gen Intel Image Processing Unit, found in Intel SoCs + and used for capturing images and video from camera sensors. + + To compile this driver, say Y here! It contains 2 modules - + intel_ipu7 and intel_ipu7_isys. diff --git a/drivers/staging/media/ipu7/Makefile b/drivers/staging/media/ipu7/Makefile new file mode 100644 index 000000000000..6d2aec219e65 --- /dev/null +++ b/drivers/staging/media/ipu7/Makefile @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2017 - 2025 Intel Corporation. + +intel-ipu7-objs += ipu7.o \ + ipu7-bus.o \ + ipu7-dma.o \ + ipu7-mmu.o \ + ipu7-buttress.o \ + ipu7-cpd.o \ + ipu7-syscom.o \ + ipu7-boot.o + +obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7.o + +intel-ipu7-isys-objs += ipu7-isys.o \ + ipu7-isys-csi2.o \ + ipu7-isys-csi-phy.o \ + ipu7-fw-isys.o \ + ipu7-isys-video.o \ + ipu7-isys-queue.o \ + ipu7-isys-subdev.o + +obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-isys.o diff --git a/drivers/staging/media/ipu7/TODO b/drivers/staging/media/ipu7/TODO new file mode 100644 index 000000000000..7fbc37059adf --- /dev/null +++ b/drivers/staging/media/ipu7/TODO @@ -0,0 +1,28 @@ +This is a list of things that need to be done to get this driver out of the +staging directory. + +- ABI headers cleanup + Cleanup the firmware ABI headers + +- Add metadata capture support + The IPU7 hardware should support metadata capture, but it is not + fully verified with IPU7 firmware ABI so far, need to add the metadata + capture support. + +- Refine CSI2 PHY code + Refine the ipu7-isys-csi2-phy.c, move the hardware specific variant + into structure, clarify and explain the PHY registers to make it more + readable. + +- Work with the common IPU module + Sakari commented much of the driver code is the same than the IPU6 driver. + 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. -- cgit v1.2.3 From a8598c7de1bcd94461ca54c972efa9b4ea501fb9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 3 Jul 2025 17:53:19 +0300 Subject: media: ipu7: Drop IPU8 PCI ID for now This driver is intended for IPU7 at the moment so drop the IPU8 PCI ID. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/ipu7/ipu7.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c index 4a70d3527cb7..1b4f01db13ca 100644 --- a/drivers/staging/media/ipu7/ipu7.c +++ b/drivers/staging/media/ipu7/ipu7.c @@ -2752,7 +2752,6 @@ static const struct dev_pm_ops ipu7_pm_ops = { static const struct pci_device_id ipu7_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7_PCI_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7P5_PCI_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU8_PCI_ID)}, {0,} }; MODULE_DEVICE_TABLE(pci, ipu7_pci_tbl); -- cgit v1.2.3 From 0a345630c980b6a274d9d4b1e9fd833bfc1a6959 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Jul 2025 11:20:30 +0200 Subject: media: atomisp: Move atomisp_stop_streaming() above atomisp_start_streaming() Move atomisp_stop_streaming() above atomisp_start_streaming(), this is a preparation patch for making atomisp_start_streaming() properly cleanup if starting the sensor stream fails. No functional change, only moving a block of code up. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250505210008.152659-2-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp_ioctl.c | 148 +++++++++++----------- 1 file changed, 74 insertions(+), 74 deletions(-) (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 97d99bed1560..705f104a2147 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c +++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c @@ -856,6 +856,80 @@ static void atomisp_dma_burst_len_cfg(struct atomisp_sub_device *asd) atomisp_css2_hw_store_32(DMA_BURST_SIZE_REG, 0x00); } +void atomisp_stop_streaming(struct vb2_queue *vq) +{ + struct atomisp_video_pipe *pipe = vq_to_pipe(vq); + struct atomisp_sub_device *asd = pipe->asd; + struct atomisp_device *isp = asd->isp; + struct pci_dev *pdev = to_pci_dev(isp->dev); + unsigned long flags; + int ret; + + dev_dbg(isp->dev, "Stop stream\n"); + + mutex_lock(&isp->mutex); + /* + * There is no guarantee that the buffers queued to / owned by the ISP + * will properly be returned to the queue when stopping. Set a flag to + * avoid new buffers getting queued and then wait for all the current + * buffers to finish. + */ + pipe->stopping = true; + mutex_unlock(&isp->mutex); + /* wait max 1 second */ + ret = wait_event_timeout(pipe->vb_queue.done_wq, + atomisp_buffers_in_css(pipe) == 0, HZ); + mutex_lock(&isp->mutex); + pipe->stopping = false; + if (ret == 0) + dev_warn(isp->dev, "Warning timeout waiting for CSS to return buffers\n"); + + spin_lock_irqsave(&isp->lock, flags); + asd->streaming = false; + spin_unlock_irqrestore(&isp->lock, flags); + + atomisp_clear_css_buffer_counters(asd); + atomisp_css_irq_enable(isp, IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF, false); + + atomisp_css_stop(asd, false); + + atomisp_flush_video_pipe(pipe, VB2_BUF_STATE_ERROR, true); + + atomisp_subdev_cleanup_pending_events(asd); + + ret = v4l2_subdev_call(isp->inputs[asd->input_curr].csi_remote_source, + video, s_stream, 0); + if (ret) + dev_warn(isp->dev, "Stopping sensor stream failed: %d\n", ret); + + /* Disable the CSI interface on ANN B0/K0 */ + if (isp->media_dev.hw_revision >= ((ATOMISP_HW_REVISION_ISP2401 << + ATOMISP_HW_REVISION_SHIFT) | ATOMISP_HW_STEPPING_B0)) { + pci_write_config_word(pdev, MRFLD_PCI_CSI_CONTROL, + isp->saved_regs.csi_control & ~MRFLD_PCI_CSI_CONTROL_CSI_READY); + } + + if (atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_LOW, false)) + dev_warn(isp->dev, "DFS failed.\n"); + + /* + * ISP work around, need to reset ISP to allow next stream on to work. + * Streams have already been destroyed by atomisp_css_stop(). + * Disable PUNIT/ISP acknowledge/handshake - SRSE=3 and then reset. + */ + pci_write_config_dword(pdev, PCI_I_CONTROL, + isp->saved_regs.i_control | MRFLD_PCI_I_CONTROL_SRSE_RESET_MASK); + atomisp_reset(isp); + + /* Streams were destroyed by atomisp_css_stop(), recreate them. */ + ret = atomisp_create_pipes_stream(&isp->asd); + if (ret) + dev_warn(isp->dev, "Recreating streams failed: %d\n", ret); + + media_pipeline_stop(&asd->video_out.vdev.entity.pads[0]); + mutex_unlock(&isp->mutex); +} + int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count) { struct atomisp_video_pipe *pipe = vq_to_pipe(vq); @@ -961,80 +1035,6 @@ out_unlock: return ret; } -void atomisp_stop_streaming(struct vb2_queue *vq) -{ - struct atomisp_video_pipe *pipe = vq_to_pipe(vq); - struct atomisp_sub_device *asd = pipe->asd; - struct atomisp_device *isp = asd->isp; - struct pci_dev *pdev = to_pci_dev(isp->dev); - unsigned long flags; - int ret; - - dev_dbg(isp->dev, "Stop stream\n"); - - mutex_lock(&isp->mutex); - /* - * There is no guarantee that the buffers queued to / owned by the ISP - * will properly be returned to the queue when stopping. Set a flag to - * avoid new buffers getting queued and then wait for all the current - * buffers to finish. - */ - pipe->stopping = true; - mutex_unlock(&isp->mutex); - /* wait max 1 second */ - ret = wait_event_timeout(pipe->vb_queue.done_wq, - atomisp_buffers_in_css(pipe) == 0, HZ); - mutex_lock(&isp->mutex); - pipe->stopping = false; - if (ret == 0) - dev_warn(isp->dev, "Warning timeout waiting for CSS to return buffers\n"); - - spin_lock_irqsave(&isp->lock, flags); - asd->streaming = false; - spin_unlock_irqrestore(&isp->lock, flags); - - atomisp_clear_css_buffer_counters(asd); - atomisp_css_irq_enable(isp, IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF, false); - - atomisp_css_stop(asd, false); - - atomisp_flush_video_pipe(pipe, VB2_BUF_STATE_ERROR, true); - - atomisp_subdev_cleanup_pending_events(asd); - - ret = v4l2_subdev_call(isp->inputs[asd->input_curr].csi_remote_source, - video, s_stream, 0); - if (ret) - dev_warn(isp->dev, "Stopping sensor stream failed: %d\n", ret); - - /* Disable the CSI interface on ANN B0/K0 */ - if (isp->media_dev.hw_revision >= ((ATOMISP_HW_REVISION_ISP2401 << - ATOMISP_HW_REVISION_SHIFT) | ATOMISP_HW_STEPPING_B0)) { - pci_write_config_word(pdev, MRFLD_PCI_CSI_CONTROL, - isp->saved_regs.csi_control & ~MRFLD_PCI_CSI_CONTROL_CSI_READY); - } - - if (atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_LOW, false)) - dev_warn(isp->dev, "DFS failed.\n"); - - /* - * ISP work around, need to reset ISP to allow next stream on to work. - * Streams have already been destroyed by atomisp_css_stop(). - * Disable PUNIT/ISP acknowledge/handshake - SRSE=3 and then reset. - */ - pci_write_config_dword(pdev, PCI_I_CONTROL, - isp->saved_regs.i_control | MRFLD_PCI_I_CONTROL_SRSE_RESET_MASK); - atomisp_reset(isp); - - /* Streams were destroyed by atomisp_css_stop(), recreate them. */ - ret = atomisp_create_pipes_stream(&isp->asd); - if (ret) - dev_warn(isp->dev, "Recreating streams failed: %d\n", ret); - - media_pipeline_stop(&asd->video_out.vdev.entity.pads[0]); - mutex_unlock(&isp->mutex); -} - /* * To get the current value of a control. * applications initialize the id field of a struct v4l2_control and -- cgit v1.2.3 From 41dd1f2fac325bdc39318665a91040097b2cd26c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Jul 2025 11:20:46 +0200 Subject: media: atomisp: Properly stop the ISP stream on sensor streamon errors When v4l2_subdev_call(sensor, s_stream, 1) fails atomisp_start_streaming() was not properly returning the buffer ownership back to the videobuf2-core code, resulting in: [ 1318.153447] ------------[ cut here ]------------ [ 1318.153499] WARNING: CPU: 0 PID: 4856 at drivers/media/common/videobuf2/videobuf2-core.c:1803 vb2_start_streaming+0xcb/0x160 [videobuf2_common] ... [ 1318.154551] Call Trace: [ 1318.154560] [ 1318.154571] ? __warn.cold+0xb7/0x14a [ 1318.154591] ? vb2_start_streaming+0xcb/0x160 [videobuf2_common] [ 1318.154617] ? report_bug+0xe0/0x180 [ 1318.154640] ? handle_bug+0x5e/0xa0 [ 1318.154652] ? exc_invalid_op+0x14/0x70 [ 1318.154665] ? asm_exc_invalid_op+0x16/0x20 [ 1318.154697] ? vb2_start_streaming+0xcb/0x160 [videobuf2_common] [ 1318.154723] ? vb2_start_streaming+0x70/0x160 [videobuf2_common] [ 1318.154748] vb2_core_streamon+0xa2/0x100 [videobuf2_common] The sensor streamon call is the last thing that atomisp_start_streaming() does and it was failing to undo all of the previous steps in general. Refactor atomisp_stop_streaming() into an atomisp_stop_stream() helper and call that on sensor streamon failure to properly clean things up. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250505210008.152659-3-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp_ioctl.c | 38 +++++++++++++---------- 1 file changed, 22 insertions(+), 16 deletions(-) (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 705f104a2147..491af67cc7a8 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c +++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c @@ -856,18 +856,15 @@ static void atomisp_dma_burst_len_cfg(struct atomisp_sub_device *asd) atomisp_css2_hw_store_32(DMA_BURST_SIZE_REG, 0x00); } -void atomisp_stop_streaming(struct vb2_queue *vq) +static void atomisp_stop_stream(struct atomisp_video_pipe *pipe, + bool stop_sensor, enum vb2_buffer_state state) { - struct atomisp_video_pipe *pipe = vq_to_pipe(vq); struct atomisp_sub_device *asd = pipe->asd; struct atomisp_device *isp = asd->isp; struct pci_dev *pdev = to_pci_dev(isp->dev); unsigned long flags; int ret; - dev_dbg(isp->dev, "Stop stream\n"); - - mutex_lock(&isp->mutex); /* * There is no guarantee that the buffers queued to / owned by the ISP * will properly be returned to the queue when stopping. Set a flag to @@ -893,14 +890,16 @@ void atomisp_stop_streaming(struct vb2_queue *vq) atomisp_css_stop(asd, false); - atomisp_flush_video_pipe(pipe, VB2_BUF_STATE_ERROR, true); + atomisp_flush_video_pipe(pipe, state, true); atomisp_subdev_cleanup_pending_events(asd); - ret = v4l2_subdev_call(isp->inputs[asd->input_curr].csi_remote_source, - video, s_stream, 0); - if (ret) - dev_warn(isp->dev, "Stopping sensor stream failed: %d\n", ret); + if (stop_sensor) { + ret = v4l2_subdev_call(isp->inputs[asd->input_curr].csi_remote_source, + video, s_stream, 0); + if (ret) + dev_warn(isp->dev, "Stopping sensor stream failed: %d\n", ret); + } /* Disable the CSI interface on ANN B0/K0 */ if (isp->media_dev.hw_revision >= ((ATOMISP_HW_REVISION_ISP2401 << @@ -927,7 +926,6 @@ void atomisp_stop_streaming(struct vb2_queue *vq) dev_warn(isp->dev, "Recreating streams failed: %d\n", ret); media_pipeline_stop(&asd->video_out.vdev.entity.pads[0]); - mutex_unlock(&isp->mutex); } int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -1023,11 +1021,7 @@ int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count) video, s_stream, 1); if (ret) { dev_err(isp->dev, "Starting sensor stream failed: %d\n", ret); - spin_lock_irqsave(&isp->lock, irqflags); - asd->streaming = false; - spin_unlock_irqrestore(&isp->lock, irqflags); - ret = -EINVAL; - goto out_unlock; + atomisp_stop_stream(pipe, false, VB2_BUF_STATE_QUEUED); } out_unlock: @@ -1035,6 +1029,18 @@ out_unlock: return ret; } +void atomisp_stop_streaming(struct vb2_queue *vq) +{ + struct atomisp_video_pipe *pipe = vq_to_pipe(vq); + struct atomisp_device *isp = pipe->asd->isp; + + dev_dbg(isp->dev, "Stop stream\n"); + + mutex_lock(&isp->mutex); + atomisp_stop_stream(pipe, true, VB2_BUF_STATE_ERROR); + mutex_unlock(&isp->mutex); +} + /* * To get the current value of a control. * applications initialize the id field of a struct v4l2_control and -- cgit v1.2.3 From 2194e6066ae092864cc5a92400826c9758f75a57 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Jul 2025 11:20:56 +0200 Subject: media: atomisp: Stop pipeline on atomisp_css_start() failure atomisp_start_streaming() starts the media pipeline before calling atomisp_css_start(). On atomisp_css_start() failures stop the pipeline before returning the error. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250505210008.152659-4-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp_ioctl.c | 1 + 1 file changed, 1 insertion(+) (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 491af67cc7a8..fecba2588e38 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c +++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c @@ -982,6 +982,7 @@ int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count) ret = atomisp_css_start(asd); if (ret) { atomisp_flush_video_pipe(pipe, VB2_BUF_STATE_QUEUED, true); + media_pipeline_stop(&asd->video_out.vdev.entity.pads[0]); goto out_unlock; } -- cgit v1.2.3 From f01d091c3e2b198ac0485fb83e420a0ba8d070bd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Jul 2025 11:21:08 +0200 Subject: media: atomisp: Always free MIPI / CSI-receiver buffers from ia_css_uninit() The atomisp interrupt handling will free the MIPI / CSI-receiver buffers when processing a frame-completion event if the stop_requested flag is set, but only in the ISP2400 / BYT, not in the ISP2401 / CHT case. There are 2 problems with this: 1. Since this is only done in the BYT case the "mipi frames are not freed." warning always triggers on CHT devices. 2. There are 2 stop_requested flags, ia_css_pipe.stop_requested and ia_css_pipeline.stop_requested. The ISR checks the ia_css_pipe flag, but atomisp_css_stop() sets the ia_css_pipeline.stop_requested flag. So even on BYT freeing the buffers from the ISR never happens. This likely is a good thing since the buffers get freed on the first frame completion event and there might be multiple frames queued up. Fix things by completely dropping the freeing of the MIPI buffers from the ISR as well as the stop_requested flag always freeing the buffers from ia_css_uninit(). Also drop the warning since this now always is expected behavior. Note that ia_css_uninit() get called whenever streaming is stopped through atomisp_stop_stream() calling atomisp_reset() so the buffers are still freed whenever streaming is stopped. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250505210008.152659-5-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/ia_css_pipe.h | 2 -- .../runtime/pipeline/interface/ia_css_pipeline.h | 1 - .../atomisp/pci/runtime/pipeline/src/pipeline.c | 2 -- drivers/staging/media/atomisp/pci/sh_css.c | 27 ---------------------- drivers/staging/media/atomisp/pci/sh_css_mipi.c | 11 --------- drivers/staging/media/atomisp/pci/sh_css_mipi.h | 2 -- 6 files changed, 45 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/pci/ia_css_pipe.h b/drivers/staging/media/atomisp/pci/ia_css_pipe.h index c97d2ae356fd..77072694eb42 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_pipe.h +++ b/drivers/staging/media/atomisp/pci/ia_css_pipe.h @@ -102,8 +102,6 @@ struct ia_css_yuvpp_settings { struct osys_object; struct ia_css_pipe { - /* TODO: Remove stop_requested and use stop_requested in the pipeline */ - bool stop_requested; struct ia_css_pipe_config config; struct ia_css_pipe_extra_config extra_config; struct ia_css_pipe_info info; diff --git a/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h b/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h index 316eaa2070ea..8b7cbf31a1a2 100644 --- a/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h +++ b/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h @@ -34,7 +34,6 @@ struct ia_css_pipeline_stage { struct ia_css_pipeline { enum ia_css_pipe_id pipe_id; u8 pipe_num; - bool stop_requested; struct ia_css_pipeline_stage *stages; struct ia_css_pipeline_stage *current_stage; unsigned int num_stages; diff --git a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c index aabebe61ec77..cb8d652227a7 100644 --- a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c +++ b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c @@ -198,7 +198,6 @@ int ia_css_pipeline_request_stop(struct ia_css_pipeline *pipeline) ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_pipeline_request_stop() enter: pipeline=%p\n", pipeline); - pipeline->stop_requested = true; /* Send stop event to the sp*/ /* This needs improvement, stop on all the pipes available @@ -663,7 +662,6 @@ static void pipeline_init_defaults( pipeline->pipe_id = pipe_id; pipeline->stages = NULL; - pipeline->stop_requested = false; pipeline->current_stage = NULL; memcpy(&pipeline->in_frame, &ia_css_default_frame, diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index 5a8e8e67aa13..73bd87f43a8c 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -2002,10 +2002,6 @@ ia_css_uninit(void) sh_css_params_free_default_gdc_lut(); - /* TODO: JB: implement decent check and handling of freeing mipi frames */ - if (!mipi_is_free()) - dev_warn(atomisp_dev, "mipi frames are not freed.\n"); - /* cleanup generic data */ sh_css_params_uninit(); ia_css_refcount_uninit(); @@ -3743,23 +3739,6 @@ ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe, case IA_CSS_BUFFER_TYPE_INPUT_FRAME: case IA_CSS_BUFFER_TYPE_OUTPUT_FRAME: case IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME: - if (pipe && pipe->stop_requested) { - if (!IS_ISP2401) { - /* - * free mipi frames only for old input - * system for 2401 it is done in - * ia_css_stream_destroy call - */ - return_err = free_mipi_frames(pipe); - if (return_err) { - IA_CSS_LOG("free_mipi_frames() failed"); - IA_CSS_LEAVE_ERR(return_err); - return return_err; - } - } - pipe->stop_requested = false; - } - fallthrough; case IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME: case IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME: frame = (struct ia_css_frame *)HOST_ADDRESS(ddr_buffer.kernel_ptr); @@ -4095,8 +4074,6 @@ sh_css_pipe_start(struct ia_css_stream *stream) return err; } - pipe->stop_requested = false; - switch (pipe_id) { case IA_CSS_PIPE_ID_PREVIEW: err = preview_start(pipe); @@ -4120,19 +4097,15 @@ sh_css_pipe_start(struct ia_css_stream *stream) for (i = 1; i < stream->num_pipes && 0 == err ; i++) { switch (stream->pipes[i]->mode) { case IA_CSS_PIPE_ID_PREVIEW: - stream->pipes[i]->stop_requested = false; err = preview_start(stream->pipes[i]); break; case IA_CSS_PIPE_ID_VIDEO: - stream->pipes[i]->stop_requested = false; err = video_start(stream->pipes[i]); break; case IA_CSS_PIPE_ID_CAPTURE: - stream->pipes[i]->stop_requested = false; err = capture_start(stream->pipes[i]); break; case IA_CSS_PIPE_ID_YUVPP: - stream->pipes[i]->stop_requested = false; err = yuvpp_start(stream->pipes[i]); break; default: diff --git a/drivers/staging/media/atomisp/pci/sh_css_mipi.c b/drivers/staging/media/atomisp/pci/sh_css_mipi.c index 42f14ed853e1..971b685cdb58 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_mipi.c +++ b/drivers/staging/media/atomisp/pci/sh_css_mipi.c @@ -185,17 +185,6 @@ mipi_init(void) ref_count_mipi_allocation[i] = 0; } -bool mipi_is_free(void) -{ - unsigned int i; - - for (i = 0; i < N_CSI_PORTS; i++) - if (ref_count_mipi_allocation[i]) - return false; - - return true; -} - /* * @brief Calculate the required MIPI buffer sizes. * Based on the stream configuration, calculate the diff --git a/drivers/staging/media/atomisp/pci/sh_css_mipi.h b/drivers/staging/media/atomisp/pci/sh_css_mipi.h index 6f7389f44baa..b3887ee3c75a 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_mipi.h +++ b/drivers/staging/media/atomisp/pci/sh_css_mipi.h @@ -14,8 +14,6 @@ void mipi_init(void); -bool mipi_is_free(void); - int allocate_mipi_frames(struct ia_css_pipe *pipe, struct ia_css_stream_info *info); -- cgit v1.2.3 From 0193cce58b196603427e925683b54f2f64ef031c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Jul 2025 11:21:34 +0200 Subject: media: atomisp: Fix "stop stream timeout." error Commit c7194b21809e ("media: atomisp: On streamoff wait for buffers owned by the CSS to be given back") added draining of the CSS buffer queue to the beginning of atomisp_stop_stream(). But it turns out that when telling the CSS to stop streaming it needs at least 1 buffer queued, because the CSS firmware waits for a frame to be completed before stopping and without buffers it cannot complete a frame. At the end of atomisp_stop_stream() it is always safe to return buffer ownership to the videobuf2-core. Either atomisp_css_stop() has successfully stopped the stream; or the atomisp_reset() later on which power-cycles the ISP will definitely have stopped the stream. Drop the draining of the CSS buffer queue to fix the "stop stream timeout." error and move the atomisp_flush_video_pipe() call after atomisp_reset(), passing false for the warn_on_css_frames flag since some buffers still being marked as owned by the CSS expected on stream off. Also increase the timeout in destroy_stream(), since this waits for the last frame to be completed this can take longer then 40 ms. When e.g. using a framerate of 15 fps, this could take 66ms, make the timeout 200 ms to be on the safe side. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250505210008.152659-6-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/atomisp/pci/atomisp_compat_css20.c | 2 +- drivers/staging/media/atomisp/pci/atomisp_fops.c | 5 +---- drivers/staging/media/atomisp/pci/atomisp_ioctl.c | 20 ++------------------ drivers/staging/media/atomisp/pci/atomisp_subdev.h | 3 --- 4 files changed, 4 insertions(+), 26 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c index bc97fa2c374c..be5f37f4a6fd 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c @@ -387,7 +387,7 @@ static int __destroy_stream(struct atomisp_sub_device *asd, } if (stream_env->stream_state == CSS_STREAM_STARTED) { - timeout = jiffies + msecs_to_jiffies(40); + timeout = jiffies + msecs_to_jiffies(200); while (1) { if (ia_css_stream_has_stopped(stream_env->stream)) break; diff --git a/drivers/staging/media/atomisp/pci/atomisp_fops.c b/drivers/staging/media/atomisp/pci/atomisp_fops.c index 57da7ddb1503..c7aef066f209 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_fops.c +++ b/drivers/staging/media/atomisp/pci/atomisp_fops.c @@ -234,9 +234,6 @@ static int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd, if (WARN_ON(css_pipe_id >= IA_CSS_PIPE_ID_NUM)) return -EINVAL; - if (pipe->stopping) - return -EINVAL; - space = ATOMISP_CSS_Q_DEPTH - atomisp_buffers_in_css(pipe); while (space--) { struct ia_css_frame *frame; @@ -367,7 +364,7 @@ static void atomisp_buf_queue(struct vb2_buffer *vb) mutex_lock(&asd->isp->mutex); ret = atomisp_pipe_check(pipe, false); - if (ret || pipe->stopping) { + if (ret) { spin_lock_irqsave(&pipe->irq_lock, irqflags); atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR); spin_unlock_irqrestore(&pipe->irq_lock, irqflags); diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c index fecba2588e38..bb8b2f2213b0 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c +++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c @@ -865,22 +865,6 @@ static void atomisp_stop_stream(struct atomisp_video_pipe *pipe, unsigned long flags; int ret; - /* - * There is no guarantee that the buffers queued to / owned by the ISP - * will properly be returned to the queue when stopping. Set a flag to - * avoid new buffers getting queued and then wait for all the current - * buffers to finish. - */ - pipe->stopping = true; - mutex_unlock(&isp->mutex); - /* wait max 1 second */ - ret = wait_event_timeout(pipe->vb_queue.done_wq, - atomisp_buffers_in_css(pipe) == 0, HZ); - mutex_lock(&isp->mutex); - pipe->stopping = false; - if (ret == 0) - dev_warn(isp->dev, "Warning timeout waiting for CSS to return buffers\n"); - spin_lock_irqsave(&isp->lock, flags); asd->streaming = false; spin_unlock_irqrestore(&isp->lock, flags); @@ -890,8 +874,6 @@ static void atomisp_stop_stream(struct atomisp_video_pipe *pipe, atomisp_css_stop(asd, false); - atomisp_flush_video_pipe(pipe, state, true); - atomisp_subdev_cleanup_pending_events(asd); if (stop_sensor) { @@ -920,6 +902,8 @@ static void atomisp_stop_stream(struct atomisp_video_pipe *pipe, isp->saved_regs.i_control | MRFLD_PCI_I_CONTROL_SRSE_RESET_MASK); atomisp_reset(isp); + atomisp_flush_video_pipe(pipe, state, false); + /* Streams were destroyed by atomisp_css_stop(), recreate them. */ ret = atomisp_create_pipes_stream(&isp->asd); if (ret) diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.h b/drivers/staging/media/atomisp/pci/atomisp_subdev.h index bd1a198cda30..e1d0168cb91d 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.h +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.h @@ -57,9 +57,6 @@ struct atomisp_video_pipe { /* Filled through atomisp_get_css_frame_info() on queue setup */ struct ia_css_frame_info frame_info; - /* Set from streamoff to disallow queuing further buffers in CSS */ - bool stopping; - /* * irq_lock is used to protect video buffer state change operations and * also to make activeq and capq operations atomic. -- cgit v1.2.3 From cf101966f4e283b3748c7b04f3a58026da176095 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Jul 2025 11:24:02 +0200 Subject: media: atomisp: Switch to int3472 driver sensor GPIO mapping code Replace the duplicate code for calling the special Intel camera sensor GPIO type _DSM (79234640-9e10-4fea-a5c1-b5aa8b19756f) and mapping GPIOs to the sensor with a call to int3472_discrete_parse_crs() from the int3472 driver. Besides avoiding code duplication the int3472 version of the code also supports more features, like mapping the powerdown GPIO to a regulator on the mt9m114 which is necessary to make the camera on the Asus T100TA work. Signed-off-by: Hans de Goede Acked-by: Sakari Ailus Link: https://lore.kernel.org/r/20250507184737.154747-7-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/Kconfig | 1 + drivers/staging/media/atomisp/pci/atomisp_csi2.h | 17 -- .../media/atomisp/pci/atomisp_csi2_bridge.c | 233 +++------------------ drivers/staging/media/atomisp/pci/atomisp_v4l2.c | 1 + 4 files changed, 27 insertions(+), 225 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/Kconfig b/drivers/staging/media/atomisp/Kconfig index 1265fc58a232..fbe507ae6ec6 100644 --- a/drivers/staging/media/atomisp/Kconfig +++ b/drivers/staging/media/atomisp/Kconfig @@ -12,6 +12,7 @@ menuconfig INTEL_ATOMISP config VIDEO_ATOMISP tristate "Intel Atom Image Signal Processor Driver" depends on VIDEO_DEV && INTEL_ATOMISP + depends on INTEL_SKL_INT3472 depends on IPU_BRIDGE depends on MEDIA_PCI_SUPPORT depends on PMIC_OPREGION diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.h b/drivers/staging/media/atomisp/pci/atomisp_csi2.h index bb998c82a438..ec762f8fb922 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_csi2.h +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.h @@ -19,28 +19,11 @@ #define CSI2_PAD_SOURCE 1 #define CSI2_PADS_NUM 2 -#define CSI2_MAX_ACPI_GPIOS 2u - -struct acpi_device; struct v4l2_device; struct atomisp_device; struct atomisp_sub_device; -struct atomisp_csi2_acpi_gpio_map { - struct acpi_gpio_params params[CSI2_MAX_ACPI_GPIOS]; - struct acpi_gpio_mapping mapping[CSI2_MAX_ACPI_GPIOS + 1]; -}; - -struct atomisp_csi2_acpi_gpio_parsing_data { - struct acpi_device *adev; - struct atomisp_csi2_acpi_gpio_map *map; - u32 settings[CSI2_MAX_ACPI_GPIOS]; - unsigned int settings_count; - unsigned int res_count; - unsigned int map_count; -}; - struct atomisp_mipi_csi2_device { struct v4l2_subdev subdev; struct media_pad pads[CSI2_PADS_NUM]; diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c index 6abda358a72f..2a90f86e515f 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -24,39 +25,6 @@ #define PMC_CLK_RATE_19_2MHZ 19200000 -/* - * 79234640-9e10-4fea-a5c1-b5aa8b19756f - * This _DSM GUID returns information about the GPIO lines mapped to a sensor. - * Function number 1 returns a count of the GPIO lines that are mapped. - * Subsequent functions return 32 bit ints encoding information about the GPIO. - */ -static const guid_t intel_sensor_gpio_info_guid = - GUID_INIT(0x79234640, 0x9e10, 0x4fea, - 0xa5, 0xc1, 0xb5, 0xaa, 0x8b, 0x19, 0x75, 0x6f); - -#define INTEL_GPIO_DSM_TYPE_SHIFT 0 -#define INTEL_GPIO_DSM_TYPE_MASK GENMASK(7, 0) -#define INTEL_GPIO_DSM_PIN_SHIFT 8 -#define INTEL_GPIO_DSM_PIN_MASK GENMASK(15, 8) -#define INTEL_GPIO_DSM_SENSOR_ON_VAL_SHIFT 24 -#define INTEL_GPIO_DSM_SENSOR_ON_VAL_MASK GENMASK(31, 24) - -#define INTEL_GPIO_DSM_TYPE(x) \ - (((x) & INTEL_GPIO_DSM_TYPE_MASK) >> INTEL_GPIO_DSM_TYPE_SHIFT) -#define INTEL_GPIO_DSM_PIN(x) \ - (((x) & INTEL_GPIO_DSM_PIN_MASK) >> INTEL_GPIO_DSM_PIN_SHIFT) -#define INTEL_GPIO_DSM_SENSOR_ON_VAL(x) \ - (((x) & INTEL_GPIO_DSM_SENSOR_ON_VAL_MASK) >> INTEL_GPIO_DSM_SENSOR_ON_VAL_SHIFT) - -/* - * 822ace8f-2814-4174-a56b-5f029fe079ee - * This _DSM GUID returns a string from the sensor device, which acts as a - * module identifier. - */ -static const guid_t intel_sensor_module_guid = - GUID_INIT(0x822ace8f, 0x2814, 0x4174, - 0xa5, 0x6b, 0x5f, 0x02, 0x9f, 0xe0, 0x79, 0xee); - /* * dc2f6c4f-045b-4f1d-97b9-882a6860a4be * This _DSM GUID returns a package with n*2 strings, with each set of 2 strings @@ -323,196 +291,45 @@ static int atomisp_csi2_get_port(struct acpi_device *adev, int clock_num) return gmin_cfg_get_int(adev, "CsiPort", port); } -/* Note this always returns 1 to continue looping so that res_count is accurate */ -static int atomisp_csi2_handle_acpi_gpio_res(struct acpi_resource *ares, void *_data) -{ - struct atomisp_csi2_acpi_gpio_parsing_data *data = _data; - struct acpi_resource_gpio *agpio; - const char *name; - bool active_low; - unsigned int i; - u32 settings = 0; - u16 pin; - - if (!acpi_gpio_get_io_resource(ares, &agpio)) - return 1; /* Not a GPIO, continue the loop */ - - data->res_count++; - - pin = agpio->pin_table[0]; - for (i = 0; i < data->settings_count; i++) { - if (INTEL_GPIO_DSM_PIN(data->settings[i]) == pin) { - settings = data->settings[i]; - break; - } - } - - if (i == data->settings_count) { - acpi_handle_warn(data->adev->handle, - "%s: Could not find DSM GPIO settings for pin %u\n", - dev_name(&data->adev->dev), pin); - return 1; - } - - switch (INTEL_GPIO_DSM_TYPE(settings)) { - case 0: - name = "reset-gpios"; - break; - case 1: - name = "powerdown-gpios"; - break; - default: - acpi_handle_warn(data->adev->handle, "%s: Unknown GPIO type 0x%02lx for pin %u\n", - dev_name(&data->adev->dev), - INTEL_GPIO_DSM_TYPE(settings), pin); - return 1; - } - - /* - * Both reset and power-down need to be logical false when the sensor - * is on (sensor should not be in reset and not be powered-down). So - * when the sensor-on-value (which is the physical pin value) is high, - * then the signal is active-low. - */ - active_low = INTEL_GPIO_DSM_SENSOR_ON_VAL(settings); - - i = data->map_count; - if (i == CSI2_MAX_ACPI_GPIOS) - return 1; - - /* res_count is already incremented */ - data->map->params[i].crs_entry_index = data->res_count - 1; - data->map->params[i].active_low = active_low; - data->map->mapping[i].name = name; - data->map->mapping[i].data = &data->map->params[i]; - data->map->mapping[i].size = 1; - data->map_count++; - - acpi_handle_info(data->adev->handle, "%s: %s crs %d %s pin %u active-%s\n", - dev_name(&data->adev->dev), name, - data->res_count - 1, agpio->resource_source.string_ptr, - pin, active_low ? "low" : "high"); - - return 1; -} - /* - * Helper function to create an ACPI GPIO lookup table for sensor reset and - * powerdown signals on Intel Bay Trail (BYT) and Cherry Trail (CHT) devices, - * including setting the correct polarity for the GPIO. - * - * This uses the "79234640-9e10-4fea-a5c1-b5aa8b19756f" DSM method directly - * on the sensor device's ACPI node. This is different from later Intel - * hardware which has a separate INT3472 acpi_device with this info. - * - * This function must be called before creating the sw-noded describing - * the fwnode graph endpoint. And sensor drivers used on these devices - * must return -EPROBE_DEFER when there is no endpoint description yet. - * Together this guarantees that the GPIO lookups are in place before - * the sensor driver tries to get GPIOs with gpiod_get(). + * Alloc and fill an int3472_discrete_device struct so that we can re-use + * the INT3472 sensor GPIO mapping code. * - * Note this code uses the same DSM GUID as the int3472_gpio_guid in - * the INT3472 discrete.c code and there is some overlap, but there are - * enough differences that it is difficult to share the code. + * This gets called from ipu_bridge_init() which runs only once per boot, + * the created faux int3472 device is leaked on purpose to keep the created + * GPIO mappings around permanently. */ static int atomisp_csi2_add_gpio_mappings(struct acpi_device *adev) { - struct atomisp_csi2_acpi_gpio_parsing_data data = { }; - LIST_HEAD(resource_list); - union acpi_object *obj; - unsigned int i, j; + struct int3472_discrete_device *int3472; int ret; - obj = acpi_evaluate_dsm_typed(adev->handle, &intel_sensor_module_guid, - 0x00, 1, NULL, ACPI_TYPE_STRING); - if (obj) { - acpi_handle_info(adev->handle, "%s: Sensor module id: '%s'\n", - dev_name(&adev->dev), obj->string.pointer); - ACPI_FREE(obj); - } + /* Max num GPIOs we've seen plus a terminator */ + int3472 = kzalloc(struct_size(int3472, gpios.table, INT3472_MAX_SENSOR_GPIOS + 1), + GFP_KERNEL); + if (!int3472) + return -ENOMEM; /* - * First get the GPIO-settings count and then get count GPIO-settings - * values. Note the order of these may differ from the order in which - * the GPIOs are listed on the ACPI resources! So we first store them all - * and then enumerate the ACPI resources and match them up by pin number. + * On atomisp the _DSM to get the GPIO type must be made on the sensor + * adev, rather than on a separate INT3472 adev. */ - obj = acpi_evaluate_dsm_typed(adev->handle, - &intel_sensor_gpio_info_guid, 0x00, 1, - NULL, ACPI_TYPE_INTEGER); - if (!obj) { - acpi_handle_err(adev->handle, "%s: No _DSM entry for GPIO pin count\n", - dev_name(&adev->dev)); - return -EIO; - } - - data.settings_count = obj->integer.value; - ACPI_FREE(obj); + int3472->adev = adev; + int3472->dev = &adev->dev; + int3472->sensor = adev; - if (data.settings_count > CSI2_MAX_ACPI_GPIOS) { - acpi_handle_err(adev->handle, "%s: Too many GPIOs %u > %u\n", - dev_name(&adev->dev), data.settings_count, - CSI2_MAX_ACPI_GPIOS); - return -EOVERFLOW; + int3472->sensor_name = kasprintf(GFP_KERNEL, I2C_DEV_NAME_FORMAT, acpi_dev_name(adev)); + if (!int3472->sensor_name) { + kfree(int3472); + return -ENOMEM; } - for (i = 0; i < data.settings_count; i++) { - /* - * i + 2 because the index of this _DSM function is 1-based - * and the first function is just a count. - */ - obj = acpi_evaluate_dsm_typed(adev->handle, - &intel_sensor_gpio_info_guid, - 0x00, i + 2, - NULL, ACPI_TYPE_INTEGER); - if (!obj) { - acpi_handle_err(adev->handle, "%s: No _DSM entry for pin %u\n", - dev_name(&adev->dev), i); - return -EIO; - } - - data.settings[i] = obj->integer.value; - ACPI_FREE(obj); + ret = int3472_discrete_parse_crs(int3472); + if (ret) { + int3472_discrete_cleanup(int3472); + kfree(int3472); } - /* Since we match up by pin-number the pin-numbers must be unique */ - for (i = 0; i < data.settings_count; i++) { - for (j = i + 1; j < data.settings_count; j++) { - if (INTEL_GPIO_DSM_PIN(data.settings[i]) != - INTEL_GPIO_DSM_PIN(data.settings[j])) - continue; - - acpi_handle_err(adev->handle, "%s: Duplicate pin number %lu\n", - dev_name(&adev->dev), - INTEL_GPIO_DSM_PIN(data.settings[i])); - return -EIO; - } - } - - data.map = kzalloc(sizeof(*data.map), GFP_KERNEL); - if (!data.map) - return -ENOMEM; - - /* Now parse the ACPI resources and build the lookup table */ - data.adev = adev; - ret = acpi_dev_get_resources(adev, &resource_list, - atomisp_csi2_handle_acpi_gpio_res, &data); - if (ret < 0) - return ret; - - acpi_dev_free_resource_list(&resource_list); - - if (data.map_count != data.settings_count || - data.res_count != data.settings_count) - acpi_handle_warn(adev->handle, "%s: ACPI GPIO resources vs DSM GPIO-info count mismatch (dsm: %d res: %d map %d\n", - dev_name(&adev->dev), data.settings_count, - data.res_count, data.map_count); - - ret = acpi_dev_add_driver_gpios(adev, data.map->mapping); - if (ret) - acpi_handle_err(adev->handle, "%s: Error adding driver GPIOs: %d\n", - dev_name(&adev->dev), ret); - return ret; } diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index 0bd0bfded4af..3fdaa4b7bbaf 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -1513,3 +1513,4 @@ MODULE_AUTHOR("Xiaolin Zhang "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Intel ATOM Platform ISP Driver"); MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); +MODULE_IMPORT_NS("INTEL_INT3472_DISCRETE"); -- cgit v1.2.3 From ed3bc38ff5cbc431f9b155610046f8f404437bb5 Mon Sep 17 00:00:00 2001 From: Pablo Date: Sat, 3 May 2025 17:00:30 -0300 Subject: media: atomisp: Fix ia_css_vf.host.c coding style Fix a coding style: "ERROR: that open brace { should be on the previous line" issue reported in ia_css_vf.host.c:94. Signed-off-by: Pablo Link: https://lore.kernel.org/r/20250503200030.5982-1-pablo@pablo.ct.ws Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c index 127f12ba2214..3c675063c4a7 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c @@ -91,8 +91,7 @@ configure_kernel( unsigned int vf_log_ds = 0; /* First compute value */ - if (vf_info) - { + if (vf_info) { err = sh_css_vf_downscale_log2(out_info, vf_info, &vf_log_ds); if (err) return err; -- cgit v1.2.3 From e3b95e64d508d0e869d3fb7bdef6559f976bbdef Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Jul 2025 21:17:29 +0200 Subject: media: atomisp: gc0310: Rename "dev" function variable to "sensor" Many functions on the gc0310 driver use a function local variable called "dev" but these variable's type is not "struct device *" type as one would expect based on the name. Instead they point to the gc0310 driver data struct. Rename these variables to sensor to make their purpose more clear. Signed-off-by: Hans de Goede Reviewed-by: Kieran Bingham Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-2-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 112 ++++++++++----------- 1 file changed, 56 insertions(+), 56 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index d35394f1ddbb..cc74e90a1457 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -282,16 +282,16 @@ static int gc0310_write_reg_array(struct i2c_client *client, return 0; } -static int gc0310_exposure_set(struct gc0310_device *dev, u32 exp) +static int gc0310_exposure_set(struct gc0310_device *sensor, u32 exp) { - struct i2c_client *client = v4l2_get_subdevdata(&dev->sd); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); return i2c_smbus_write_word_swapped(client, GC0310_AEC_PK_EXPO_H, exp); } -static int gc0310_gain_set(struct gc0310_device *dev, u32 gain) +static int gc0310_gain_set(struct gc0310_device *sensor, u32 gain) { - struct i2c_client *client = v4l2_get_subdevdata(&dev->sd); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); u8 again, dgain; int ret; @@ -317,27 +317,27 @@ static int gc0310_gain_set(struct gc0310_device *dev, u32 gain) static int gc0310_s_ctrl(struct v4l2_ctrl *ctrl) { - struct gc0310_device *dev = + struct gc0310_device *sensor = container_of(ctrl->handler, struct gc0310_device, ctrls.handler); int ret; /* Only apply changes to the controls if the device is powered up */ - if (!pm_runtime_get_if_in_use(dev->sd.dev)) + if (!pm_runtime_get_if_in_use(sensor->sd.dev)) return 0; switch (ctrl->id) { case V4L2_CID_EXPOSURE: - ret = gc0310_exposure_set(dev, ctrl->val); + ret = gc0310_exposure_set(sensor, ctrl->val); break; case V4L2_CID_GAIN: - ret = gc0310_gain_set(dev, ctrl->val); + ret = gc0310_gain_set(sensor, ctrl->val); break; default: ret = -EINVAL; break; } - pm_runtime_put(dev->sd.dev); + pm_runtime_put(sensor->sd.dev); return ret; } @@ -346,14 +346,14 @@ static const struct v4l2_ctrl_ops ctrl_ops = { }; static struct v4l2_mbus_framefmt * -gc0310_get_pad_format(struct gc0310_device *dev, +gc0310_get_pad_format(struct gc0310_device *sensor, struct v4l2_subdev_state *state, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) return v4l2_subdev_state_get_format(state, pad); - return &dev->mode.fmt; + return &sensor->mode.fmt; } /* The GC0310 currently only supports 1 fixed fmt */ @@ -370,10 +370,10 @@ static int gc0310_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { - struct gc0310_device *dev = to_gc0310_sensor(sd); + struct gc0310_device *sensor = to_gc0310_sensor(sd); struct v4l2_mbus_framefmt *fmt; - fmt = gc0310_get_pad_format(dev, sd_state, format->pad, format->which); + fmt = gc0310_get_pad_format(sensor, sd_state, format->pad, format->which); gc0310_fill_format(fmt); format->format = *fmt; @@ -384,10 +384,10 @@ static int gc0310_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) { - struct gc0310_device *dev = to_gc0310_sensor(sd); + struct gc0310_device *sensor = to_gc0310_sensor(sd); struct v4l2_mbus_framefmt *fmt; - fmt = gc0310_get_pad_format(dev, sd_state, format->pad, format->which); + fmt = gc0310_get_pad_format(sensor, sd_state, format->pad, format->which); format->format = *fmt; return 0; } @@ -424,12 +424,12 @@ static int gc0310_detect(struct i2c_client *client) static int gc0310_s_stream(struct v4l2_subdev *sd, int enable) { - struct gc0310_device *dev = to_gc0310_sensor(sd); + struct gc0310_device *sensor = to_gc0310_sensor(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; dev_dbg(&client->dev, "%s S enable=%d\n", __func__, enable); - mutex_lock(&dev->input_lock); + mutex_lock(&sensor->input_lock); if (enable) { ret = pm_runtime_get_sync(&client->dev); @@ -449,7 +449,7 @@ static int gc0310_s_stream(struct v4l2_subdev *sd, int enable) goto error_power_down; /* restore value of all ctrls */ - ret = __v4l2_ctrl_handler_setup(&dev->ctrls.handler); + ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler); if (ret) goto error_power_down; @@ -475,14 +475,14 @@ static int gc0310_s_stream(struct v4l2_subdev *sd, int enable) if (!enable) pm_runtime_put(&client->dev); - dev->is_streaming = enable; - mutex_unlock(&dev->input_lock); + sensor->is_streaming = enable; + mutex_unlock(&sensor->input_lock); return 0; error_power_down: pm_runtime_put(&client->dev); - dev->is_streaming = false; - mutex_unlock(&dev->input_lock); + sensor->is_streaming = false; + mutex_unlock(&sensor->input_lock); return ret; } @@ -559,21 +559,21 @@ static const struct v4l2_subdev_ops gc0310_ops = { .sensor = &gc0310_sensor_ops, }; -static int gc0310_init_controls(struct gc0310_device *dev) +static int gc0310_init_controls(struct gc0310_device *sensor) { - struct v4l2_ctrl_handler *hdl = &dev->ctrls.handler; + struct v4l2_ctrl_handler *hdl = &sensor->ctrls.handler; v4l2_ctrl_handler_init(hdl, 2); /* Use the same lock for controls as for everything else */ - hdl->lock = &dev->input_lock; - dev->sd.ctrl_handler = hdl; + hdl->lock = &sensor->input_lock; + sensor->sd.ctrl_handler = hdl; - dev->ctrls.exposure = + sensor->ctrls.exposure = v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_EXPOSURE, 0, 4095, 1, 1023); /* 32 steps at base gain 1 + 64 half steps at base gain 2 */ - dev->ctrls.gain = + sensor->ctrls.gain = v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_GAIN, 0, 95, 1, 31); return hdl->error; @@ -582,21 +582,21 @@ static int gc0310_init_controls(struct gc0310_device *dev) static void gc0310_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct gc0310_device *dev = to_gc0310_sensor(sd); + struct gc0310_device *sensor = to_gc0310_sensor(sd); dev_dbg(&client->dev, "gc0310_remove...\n"); v4l2_async_unregister_subdev(sd); - media_entity_cleanup(&dev->sd.entity); - v4l2_ctrl_handler_free(&dev->ctrls.handler); - mutex_destroy(&dev->input_lock); + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + mutex_destroy(&sensor->input_lock); pm_runtime_disable(&client->dev); } static int gc0310_probe(struct i2c_client *client) { struct fwnode_handle *ep_fwnode; - struct gc0310_device *dev; + struct gc0310_device *sensor; int ret; /* @@ -609,25 +609,25 @@ static int gc0310_probe(struct i2c_client *client) fwnode_handle_put(ep_fwnode); - dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) + sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) return -ENOMEM; - dev->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(dev->reset)) { - return dev_err_probe(&client->dev, PTR_ERR(dev->reset), + sensor->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset)) { + return dev_err_probe(&client->dev, PTR_ERR(sensor->reset), "getting reset GPIO\n"); } - dev->powerdown = devm_gpiod_get(&client->dev, "powerdown", GPIOD_OUT_HIGH); - if (IS_ERR(dev->powerdown)) { - return dev_err_probe(&client->dev, PTR_ERR(dev->powerdown), + sensor->powerdown = devm_gpiod_get(&client->dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(sensor->powerdown)) { + return dev_err_probe(&client->dev, PTR_ERR(sensor->powerdown), "getting powerdown GPIO\n"); } - mutex_init(&dev->input_lock); - v4l2_i2c_subdev_init(&dev->sd, client, &gc0310_ops); - gc0310_fill_format(&dev->mode.fmt); + mutex_init(&sensor->input_lock); + v4l2_i2c_subdev_init(&sensor->sd, client, &gc0310_ops); + gc0310_fill_format(&sensor->mode.fmt); pm_runtime_set_suspended(&client->dev); pm_runtime_enable(&client->dev); @@ -640,23 +640,23 @@ static int gc0310_probe(struct i2c_client *client) return ret; } - dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - dev->pad.flags = MEDIA_PAD_FL_SOURCE; - dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - ret = gc0310_init_controls(dev); + ret = gc0310_init_controls(sensor); if (ret) { gc0310_remove(client); return ret; } - ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); + ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); if (ret) { gc0310_remove(client); return ret; } - ret = v4l2_async_register_subdev_sensor(&dev->sd); + ret = v4l2_async_register_subdev_sensor(&sensor->sd); if (ret) { gc0310_remove(client); return ret; @@ -668,22 +668,22 @@ static int gc0310_probe(struct i2c_client *client) static int gc0310_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct gc0310_device *gc0310_dev = to_gc0310_sensor(sd); + struct gc0310_device *sensor = to_gc0310_sensor(sd); - gpiod_set_value_cansleep(gc0310_dev->powerdown, 1); - gpiod_set_value_cansleep(gc0310_dev->reset, 1); + gpiod_set_value_cansleep(sensor->powerdown, 1); + gpiod_set_value_cansleep(sensor->reset, 1); return 0; } static int gc0310_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct gc0310_device *gc0310_dev = to_gc0310_sensor(sd); + struct gc0310_device *sensor = to_gc0310_sensor(sd); usleep_range(10000, 15000); - gpiod_set_value_cansleep(gc0310_dev->reset, 0); + gpiod_set_value_cansleep(sensor->reset, 0); usleep_range(10000, 15000); - gpiod_set_value_cansleep(gc0310_dev->powerdown, 0); + gpiod_set_value_cansleep(sensor->powerdown, 0); return 0; } -- cgit v1.2.3 From 214b24b4cdb2decd61d2f79116fe4d4ad9a7dd4c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Jul 2025 21:18:15 +0200 Subject: media: atomisp: gc0310: Drop unused GC0310_FOCAL_LENGTH_NUM define Drop the unused GC0310_FOCAL_LENGTH_NUM define, the focal-length is a property of the sensor-module, not of the raw sensor itself. Signed-off-by: Hans de Goede Reviewed-by: Kieran Bingham Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-3-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index cc74e90a1457..f253b4b494f2 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -25,8 +25,6 @@ #define GC0310_FPS 30 #define GC0310_SKIP_FRAMES 3 -#define GC0310_FOCAL_LENGTH_NUM 278 /* 2.78mm */ - #define GC0310_ID 0xa310 #define GC0310_RESET_RELATED 0xFE -- cgit v1.2.3 From 58eec766d28a59542cbc898e75fbe5a893a27174 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Jul 2025 21:18:27 +0200 Subject: media: atomisp: gc0310: Modify vblank value to run at 30 fps Currently the sensor is running 30.9 fps, increase vblank to have it actually run at 30.0 fps. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-4-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index f253b4b494f2..6b11f0ff6088 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -141,7 +141,7 @@ static const struct gc0310_reg gc0310_reset_register[] = { { 0x05, 0x00 }, { 0x06, 0xb2 }, /* 0x0a //HB */ { 0x07, 0x00 }, - { 0x08, 0x0c }, /* 0x89 //VB */ + { 0x08, 0x1b }, /* 0x89 //VB */ { 0x09, 0x00 }, /* row start */ { 0x0a, 0x00 }, { 0x0b, 0x00 }, /* col start */ -- cgit v1.2.3 From 2c3a35f27db887c0eb1944ab516e670da0765b42 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Jul 2025 21:20:16 +0200 Subject: media: atomisp: gc0310: Switch to CCI register access helpers Switch the GC0310 driver over to the CCI register access helpers. While at it also add a _REG prefix to all register address defines to make clear they are register addresses and group register value defines together with the address definition. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-5-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/Kconfig | 1 + drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 143 ++++++++------------- 2 files changed, 55 insertions(+), 89 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/Kconfig b/drivers/staging/media/atomisp/i2c/Kconfig index 4f46182da58b..ef2094c22347 100644 --- a/drivers/staging/media/atomisp/i2c/Kconfig +++ b/drivers/staging/media/atomisp/i2c/Kconfig @@ -31,6 +31,7 @@ config VIDEO_ATOMISP_GC0310 tristate "GC0310 sensor support" depends on ACPI depends on I2C && VIDEO_DEV + select V4L2_CCI_I2C help This is a Video4Linux2 sensor-level driver for the Galaxycore GC0310 0.3MP sensor. diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index 6b11f0ff6088..91f95db94283 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -13,9 +13,11 @@ #include #include #include +#include #include #include +#include #include #include @@ -27,41 +29,32 @@ #define GC0310_ID 0xa310 -#define GC0310_RESET_RELATED 0xFE +#define GC0310_RESET_RELATED_REG CCI_REG8(0xfe) #define GC0310_REGISTER_PAGE_0 0x0 #define GC0310_REGISTER_PAGE_3 0x3 /* * GC0310 System control registers */ -#define GC0310_SW_STREAM 0x10 - -#define GC0310_SC_CMMN_CHIP_ID_H 0xf0 -#define GC0310_SC_CMMN_CHIP_ID_L 0xf1 - -#define GC0310_AEC_PK_EXPO_H 0x03 -#define GC0310_AEC_PK_EXPO_L 0x04 -#define GC0310_AGC_ADJ 0x48 -#define GC0310_DGC_ADJ 0x71 -#define GC0310_GROUP_ACCESS 0x3208 - -#define GC0310_H_CROP_START_H 0x09 -#define GC0310_H_CROP_START_L 0x0A -#define GC0310_V_CROP_START_H 0x0B -#define GC0310_V_CROP_START_L 0x0C -#define GC0310_H_OUTSIZE_H 0x0F -#define GC0310_H_OUTSIZE_L 0x10 -#define GC0310_V_OUTSIZE_H 0x0D -#define GC0310_V_OUTSIZE_L 0x0E -#define GC0310_H_BLANKING_H 0x05 -#define GC0310_H_BLANKING_L 0x06 -#define GC0310_V_BLANKING_H 0x07 -#define GC0310_V_BLANKING_L 0x08 -#define GC0310_SH_DELAY 0x11 +#define GC0310_SW_STREAM_REG CCI_REG8(0x10) #define GC0310_START_STREAMING 0x94 /* 8-bit enable */ #define GC0310_STOP_STREAMING 0x0 /* 8-bit disable */ +#define GC0310_SC_CMMN_CHIP_ID_REG CCI_REG16(0xf0) + +#define GC0310_AEC_PK_EXPO_REG CCI_REG16(0x03) +#define GC0310_AGC_ADJ_REG CCI_REG8(0x48) +#define GC0310_DGC_ADJ_REG CCI_REG8(0x71) + +#define GC0310_H_CROP_START_REG CCI_REG16(0x09) +#define GC0310_V_CROP_START_REG CCI_REG16(0x0b) +#define GC0310_H_OUTSIZE_REG CCI_REG16(0x0f) +#define GC0310_V_OUTSIZE_REG CCI_REG16(0x0d) +#define GC0310_H_BLANKING_REG CCI_REG16(0x05) +#define GC0310_V_BLANKING_REG CCI_REG16(0x07) +#define GC0310_SH_DELAY_REG CCI_REG8(0x11) + #define to_gc0310_sensor(x) container_of(x, struct gc0310_device, sd) struct gc0310_device { @@ -71,6 +64,7 @@ struct gc0310_device { struct mutex input_lock; bool is_streaming; + struct regmap *regmap; struct gpio_desc *reset; struct gpio_desc *powerdown; @@ -90,7 +84,7 @@ struct gc0310_reg { u8 val; }; -static const struct gc0310_reg gc0310_reset_register[] = { +static const struct reg_sequence gc0310_reset_register[] = { /* System registers */ { 0xfe, 0xf0 }, { 0xfe, 0xf0 }, @@ -233,7 +227,7 @@ static const struct gc0310_reg gc0310_reset_register[] = { { 0xfe, 0x00 }, }; -static const struct gc0310_reg gc0310_VGA_30fps[] = { +static const struct reg_sequence gc0310_VGA_30fps[] = { { 0xfe, 0x00 }, { 0x0d, 0x01 }, /* height */ { 0x0e, 0xf2 }, /* 0xf7 //height */ @@ -257,41 +251,10 @@ static const struct gc0310_reg gc0310_VGA_30fps[] = { { 0xfe, 0x00 }, }; -/* - * gc0310_write_reg_array - Initializes a list of GC0310 registers - * @client: i2c driver client structure - * @reglist: list of registers to be written - * @count: number of register, value pairs in the list - */ -static int gc0310_write_reg_array(struct i2c_client *client, - const struct gc0310_reg *reglist, int count) -{ - int i, err; - - for (i = 0; i < count; i++) { - err = i2c_smbus_write_byte_data(client, reglist[i].reg, reglist[i].val); - if (err) { - dev_err(&client->dev, "write error: wrote 0x%x to offset 0x%x error %d", - reglist[i].val, reglist[i].reg, err); - return err; - } - } - - return 0; -} - -static int gc0310_exposure_set(struct gc0310_device *sensor, u32 exp) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); - - return i2c_smbus_write_word_swapped(client, GC0310_AEC_PK_EXPO_H, exp); -} - static int gc0310_gain_set(struct gc0310_device *sensor, u32 gain) { - struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); u8 again, dgain; - int ret; + int ret = 0; /* Taken from original driver, this never sets dgain lower then 32? */ @@ -306,11 +269,9 @@ static int gc0310_gain_set(struct gc0310_device *sensor, u32 gain) dgain = gain / 2; } - ret = i2c_smbus_write_byte_data(client, GC0310_AGC_ADJ, again); - if (ret) - return ret; - - return i2c_smbus_write_byte_data(client, GC0310_DGC_ADJ, dgain); + cci_write(sensor->regmap, GC0310_AGC_ADJ_REG, again, &ret); + cci_write(sensor->regmap, GC0310_DGC_ADJ_REG, dgain, &ret); + return ret; } static int gc0310_s_ctrl(struct v4l2_ctrl *ctrl) @@ -325,7 +286,8 @@ static int gc0310_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_EXPOSURE: - ret = gc0310_exposure_set(sensor, ctrl->val); + ret = cci_write(sensor->regmap, GC0310_AEC_PK_EXPO_REG, + ctrl->val, NULL); break; case V4L2_CID_GAIN: ret = gc0310_gain_set(sensor, ctrl->val); @@ -390,28 +352,30 @@ static int gc0310_get_fmt(struct v4l2_subdev *sd, return 0; } -static int gc0310_detect(struct i2c_client *client) +static int gc0310_detect(struct gc0310_device *sensor) { - struct i2c_adapter *adapter = client->adapter; + struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); + u64 val; int ret; - if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; ret = pm_runtime_get_sync(&client->dev); if (ret >= 0) - ret = i2c_smbus_read_word_swapped(client, GC0310_SC_CMMN_CHIP_ID_H); + ret = cci_read(sensor->regmap, GC0310_SC_CMMN_CHIP_ID_REG, + &val, NULL); pm_runtime_put(&client->dev); if (ret < 0) { dev_err(&client->dev, "read sensor_id failed: %d\n", ret); return -ENODEV; } - dev_dbg(&client->dev, "sensor ID = 0x%x\n", ret); + dev_dbg(&client->dev, "sensor ID = 0x%llx\n", val); - if (ret != GC0310_ID) { - dev_err(&client->dev, "sensor ID error, read id = 0x%x, target id = 0x%x\n", - ret, GC0310_ID); + if (val != GC0310_ID) { + dev_err(&client->dev, "sensor ID error, read id = 0x%llx, target id = 0x%x\n", + val, GC0310_ID); return -ENODEV; } @@ -436,12 +400,14 @@ static int gc0310_s_stream(struct v4l2_subdev *sd, int enable) msleep(100); - ret = gc0310_write_reg_array(client, gc0310_reset_register, + ret = regmap_multi_reg_write(sensor->regmap, + gc0310_reset_register, ARRAY_SIZE(gc0310_reset_register)); if (ret) goto error_power_down; - ret = gc0310_write_reg_array(client, gc0310_VGA_30fps, + ret = regmap_multi_reg_write(sensor->regmap, + gc0310_VGA_30fps, ARRAY_SIZE(gc0310_VGA_30fps)); if (ret) goto error_power_down; @@ -452,21 +418,16 @@ static int gc0310_s_stream(struct v4l2_subdev *sd, int enable) goto error_power_down; /* enable per frame MIPI and sensor ctrl reset */ - ret = i2c_smbus_write_byte_data(client, 0xFE, 0x30); - if (ret) - goto error_power_down; + cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, 0x30, &ret); } - ret = i2c_smbus_write_byte_data(client, GC0310_RESET_RELATED, GC0310_REGISTER_PAGE_3); - if (ret) - goto error_power_down; - - ret = i2c_smbus_write_byte_data(client, GC0310_SW_STREAM, - enable ? GC0310_START_STREAMING : GC0310_STOP_STREAMING); - if (ret) - goto error_power_down; - - ret = i2c_smbus_write_byte_data(client, GC0310_RESET_RELATED, GC0310_REGISTER_PAGE_0); + cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, + GC0310_REGISTER_PAGE_3, &ret); + cci_write(sensor->regmap, GC0310_SW_STREAM_REG, + enable ? GC0310_START_STREAMING : GC0310_STOP_STREAMING, + &ret); + cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, + GC0310_REGISTER_PAGE_0, &ret); if (ret) goto error_power_down; @@ -627,12 +588,16 @@ static int gc0310_probe(struct i2c_client *client) v4l2_i2c_subdev_init(&sensor->sd, client, &gc0310_ops); gc0310_fill_format(&sensor->mode.fmt); + sensor->regmap = devm_cci_regmap_init_i2c(client, 8); + if (IS_ERR(sensor->regmap)) + return PTR_ERR(sensor->regmap); + pm_runtime_set_suspended(&client->dev); pm_runtime_enable(&client->dev); pm_runtime_set_autosuspend_delay(&client->dev, 1000); pm_runtime_use_autosuspend(&client->dev); - ret = gc0310_detect(client); + ret = gc0310_detect(sensor); if (ret) { gc0310_remove(client); return ret; -- cgit v1.2.3 From 782101916bf23fdee62e2b317472678a3a3fb231 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Jul 2025 22:54:19 +0200 Subject: media: atomisp: gc0310: Use V4L2_CID_ANALOGUE_GAIN for gain control Use V4L2_CID_ANALOGUE_GAIN for gain control, as expected by userspace. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20250517114106.43494-6-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index 91f95db94283..1f1795fc625f 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -289,7 +289,7 @@ static int gc0310_s_ctrl(struct v4l2_ctrl *ctrl) ret = cci_write(sensor->regmap, GC0310_AEC_PK_EXPO_REG, ctrl->val, NULL); break; - case V4L2_CID_GAIN: + case V4L2_CID_ANALOGUE_GAIN: ret = gc0310_gain_set(sensor, ctrl->val); break; default: @@ -533,7 +533,7 @@ static int gc0310_init_controls(struct gc0310_device *sensor) /* 32 steps at base gain 1 + 64 half steps at base gain 2 */ sensor->ctrls.gain = - v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_GAIN, 0, 95, 1, 31); + v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_ANALOGUE_GAIN, 0, 95, 1, 31); return hdl->error; } -- cgit v1.2.3 From 5446808037f6a4db04210db3bf95b216f9588076 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 6 Jul 2025 11:47:22 +0200 Subject: media: atomisp: gc0310: Add selection API support Add support for the selection API as expected by libcamera. Note the driver only supports a single fixed resolution and no cropping, so this is a simple read-only implementation. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-7-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 27 +++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index 1f1795fc625f..15ca7bc82322 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -3,7 +3,7 @@ * Support for GalaxyCore GC0310 VGA camera sensor. * * Copyright (c) 2013 Intel Corporation. All Rights Reserved. - * Copyright (c) 2023 Hans de Goede + * Copyright (c) 2023-2025 Hans de Goede */ #include @@ -352,6 +352,28 @@ static int gc0310_get_fmt(struct v4l2_subdev *sd, return 0; } +static int gc0310_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + /* Only the single fixed 656x496 mode is supported, without croping */ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = GC0310_NATIVE_WIDTH; + sel->r.height = GC0310_NATIVE_HEIGHT; + break; + default: + return -EINVAL; + } + + return 0; +} + static int gc0310_detect(struct gc0310_device *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); @@ -509,6 +531,8 @@ static const struct v4l2_subdev_pad_ops gc0310_pad_ops = { .enum_frame_size = gc0310_enum_frame_size, .get_fmt = gc0310_get_fmt, .set_fmt = gc0310_set_fmt, + .get_selection = gc0310_get_selection, + .set_selection = gc0310_get_selection, .get_frame_interval = gc0310_get_frame_interval, }; @@ -671,5 +695,6 @@ static struct i2c_driver gc0310_driver = { module_i2c_driver(gc0310_driver); MODULE_AUTHOR("Lai, Angie "); +MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("A low-level driver for GalaxyCore GC0310 sensors"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From cd3a72c6430c6edb77268a3653858908c5427e76 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 6 Jul 2025 11:53:15 +0200 Subject: media: atomisp: gc0310: Add link-frequency and pixelrate controls Add support for the pixelrate control as expected by libcamera, while at it also add the link-frequency control. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20250517114106.43494-8-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 33 ++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index 15ca7bc82322..cff1db5390bc 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -24,6 +24,17 @@ #define GC0310_NATIVE_WIDTH 656 #define GC0310_NATIVE_HEIGHT 496 +/* + * The actual PLL output rate is unknown, the datasheet + * says that the formula for the frame-time in pixels is: + * rowtime = win-width + hblank + sh-delay + 4 + * frametime = rowtime * (win-height + vblank) + * Filling this in and multiplying by 30 fps gives: + * pixelrate = (660 + 178 + 42 + 4) * (498 + 27) * 30 = 13923000 + */ +#define GC0310_PIXELRATE 13923000 +/* single lane, bus-format is 8 bpp, CSI-2 is double data rate */ +#define GC0310_LINK_FREQ (GC0310_PIXELRATE * 8 / 2) #define GC0310_FPS 30 #define GC0310_SKIP_FRAMES 3 @@ -76,6 +87,8 @@ struct gc0310_device { struct v4l2_ctrl_handler handler; struct v4l2_ctrl *exposure; struct v4l2_ctrl *gain; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; } ctrls; }; @@ -251,6 +264,10 @@ static const struct reg_sequence gc0310_VGA_30fps[] = { { 0xfe, 0x00 }, }; +static const s64 link_freq_menu_items[] = { + GC0310_LINK_FREQ, +}; + static int gc0310_gain_set(struct gc0310_device *sensor, u32 gain) { u8 again, dgain; @@ -546,7 +563,7 @@ static int gc0310_init_controls(struct gc0310_device *sensor) { struct v4l2_ctrl_handler *hdl = &sensor->ctrls.handler; - v4l2_ctrl_handler_init(hdl, 2); + v4l2_ctrl_handler_init(hdl, 4); /* Use the same lock for controls as for everything else */ hdl->lock = &sensor->input_lock; @@ -559,7 +576,19 @@ static int gc0310_init_controls(struct gc0310_device *sensor) sensor->ctrls.gain = v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_ANALOGUE_GAIN, 0, 95, 1, 31); - return hdl->error; + sensor->ctrls.link_freq = + v4l2_ctrl_new_int_menu(hdl, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + sensor->ctrls.pixel_rate = + v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE, 0, + GC0310_PIXELRATE, 1, GC0310_PIXELRATE); + + if (hdl->error) + return hdl->error; + + sensor->ctrls.pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->ctrls.link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + return 0; } static void gc0310_remove(struct i2c_client *client) -- cgit v1.2.3 From c914e3971f3ee3d09463b8d0f7802ee83eb728f6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 6 Jul 2025 11:53:24 +0200 Subject: media: atomisp: gc0310: Add vblank and hblank controls Add support for the vblank and hblank controls, these controls are mandatory for using the sensor driver with libcamera. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-9-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 38 ++++++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index cff1db5390bc..dab736f5d4b3 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -62,9 +62,22 @@ #define GC0310_V_CROP_START_REG CCI_REG16(0x0b) #define GC0310_H_OUTSIZE_REG CCI_REG16(0x0f) #define GC0310_V_OUTSIZE_REG CCI_REG16(0x0d) + #define GC0310_H_BLANKING_REG CCI_REG16(0x05) +/* Hblank-register + sh-delay + H-crop + 4 (from hw) */ +#define GC0310_H_BLANK_DEFAULT (178 + 42 + 4 + 4) + #define GC0310_V_BLANKING_REG CCI_REG16(0x07) +/* Vblank needs an offset compensate for the small V-crop done */ +#define GC0310_V_BLANK_OFFSET 2 +/* Vsync start time + 1 row vsync + vsync end time + offset */ +#define GC0310_V_BLANK_MIN (9 + 1 + 4 + GC0310_V_BLANK_OFFSET) +#define GC0310_V_BLANK_DEFAULT (27 + GC0310_V_BLANK_OFFSET) +#define GC0310_V_BLANK_MAX (4095 - GC0310_NATIVE_HEIGHT) + #define GC0310_SH_DELAY_REG CCI_REG8(0x11) +#define GC0310_VS_START_TIME_REG CCI_REG8(0x12) +#define GC0310_VS_END_TIME_REG CCI_REG8(0x13) #define to_gc0310_sensor(x) container_of(x, struct gc0310_device, sd) @@ -89,6 +102,8 @@ struct gc0310_device { struct v4l2_ctrl *gain; struct v4l2_ctrl *link_freq; struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; } ctrls; }; @@ -147,8 +162,7 @@ static const struct reg_sequence gc0310_reset_register[] = { { 0x04, 0xc0 }, /* 0xe8 //58 */ { 0x05, 0x00 }, { 0x06, 0xb2 }, /* 0x0a //HB */ - { 0x07, 0x00 }, - { 0x08, 0x1b }, /* 0x89 //VB */ + /* Vblank (reg 0x07 + 0x08) gets set by the vblank ctrl */ { 0x09, 0x00 }, /* row start */ { 0x0a, 0x00 }, { 0x0b, 0x00 }, /* col start */ @@ -309,6 +323,11 @@ static int gc0310_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_ANALOGUE_GAIN: ret = gc0310_gain_set(sensor, ctrl->val); break; + case V4L2_CID_VBLANK: + ret = cci_write(sensor->regmap, GC0310_V_BLANKING_REG, + ctrl->val - GC0310_V_BLANK_OFFSET, + NULL); + break; default: ret = -EINVAL; break; @@ -563,7 +582,7 @@ static int gc0310_init_controls(struct gc0310_device *sensor) { struct v4l2_ctrl_handler *hdl = &sensor->ctrls.handler; - v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_handler_init(hdl, 6); /* Use the same lock for controls as for everything else */ hdl->lock = &sensor->input_lock; @@ -583,11 +602,24 @@ static int gc0310_init_controls(struct gc0310_device *sensor) v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE, 0, GC0310_PIXELRATE, 1, GC0310_PIXELRATE); + sensor->ctrls.vblank = + v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_VBLANK, + GC0310_V_BLANK_MIN, + GC0310_V_BLANK_MAX, 1, + GC0310_V_BLANK_DEFAULT); + + sensor->ctrls.hblank = + v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_HBLANK, + GC0310_H_BLANK_DEFAULT, + GC0310_H_BLANK_DEFAULT, 1, + GC0310_H_BLANK_DEFAULT); + if (hdl->error) return hdl->error; sensor->ctrls.pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; sensor->ctrls.link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->ctrls.hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; return 0; } -- cgit v1.2.3 From c9524e6b90824ccfb96eac2e88f377f50c6d13ce Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 6 Jul 2025 11:53:40 +0200 Subject: media: atomisp: gc0310: Add camera orientation and sensor rotation controls Add camera orientation and sensor rotation controls using the v4l2_fwnode_device_parse() and v4l2_ctrl_new_fwnode_properties() helpers. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-10-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index dab736f5d4b3..d0e7f0f8452b 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -20,6 +20,7 @@ #include #include #include +#include #define GC0310_NATIVE_WIDTH 656 #define GC0310_NATIVE_HEIGHT 496 @@ -580,9 +581,12 @@ static const struct v4l2_subdev_ops gc0310_ops = { static int gc0310_init_controls(struct gc0310_device *sensor) { + struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); struct v4l2_ctrl_handler *hdl = &sensor->ctrls.handler; + struct v4l2_fwnode_device_properties props; + int ret; - v4l2_ctrl_handler_init(hdl, 6); + v4l2_ctrl_handler_init(hdl, 8); /* Use the same lock for controls as for everything else */ hdl->lock = &sensor->input_lock; @@ -614,6 +618,12 @@ static int gc0310_init_controls(struct gc0310_device *sensor) GC0310_H_BLANK_DEFAULT, 1, GC0310_H_BLANK_DEFAULT); + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + return ret; + + v4l2_ctrl_new_fwnode_properties(hdl, &ctrl_ops, &props); + if (hdl->error) return hdl->error; -- cgit v1.2.3 From bddd68a844f5a1c5ecc2f0409e7a2e7789169477 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 6 Jul 2025 11:53:53 +0200 Subject: media: atomisp: gc0310: Limit max exposure value to mode-height + vblank When an exposure value > (mode-height + vblank) gets set the sensor will automatically increase vblank, lowering the framerate. This is not desirable, limit exposure the maximum exposure to mode-height + vblank to avoid the unwanted framerate slowdown. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-11-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index d0e7f0f8452b..3e94414be6c7 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -306,12 +306,27 @@ static int gc0310_gain_set(struct gc0310_device *sensor, u32 gain) return ret; } +static int gc0310_exposure_update_range(struct gc0310_device *sensor) +{ + int exp_max = GC0310_NATIVE_HEIGHT + sensor->ctrls.vblank->val; + + return __v4l2_ctrl_modify_range(sensor->ctrls.exposure, 0, exp_max, + 1, exp_max); +} + static int gc0310_s_ctrl(struct v4l2_ctrl *ctrl) { struct gc0310_device *sensor = container_of(ctrl->handler, struct gc0310_device, ctrls.handler); int ret; + /* Update exposure range on vblank changes */ + if (ctrl->id == V4L2_CID_VBLANK) { + ret = gc0310_exposure_update_range(sensor); + if (ret) + return ret; + } + /* Only apply changes to the controls if the device is powered up */ if (!pm_runtime_get_if_in_use(sensor->sd.dev)) return 0; @@ -584,7 +599,7 @@ static int gc0310_init_controls(struct gc0310_device *sensor) struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); struct v4l2_ctrl_handler *hdl = &sensor->ctrls.handler; struct v4l2_fwnode_device_properties props; - int ret; + int exp_max, ret; v4l2_ctrl_handler_init(hdl, 8); @@ -592,8 +607,10 @@ static int gc0310_init_controls(struct gc0310_device *sensor) hdl->lock = &sensor->input_lock; sensor->sd.ctrl_handler = hdl; + exp_max = GC0310_NATIVE_HEIGHT + GC0310_V_BLANK_DEFAULT; sensor->ctrls.exposure = - v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_EXPOSURE, 0, 4095, 1, 1023); + v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_EXPOSURE, 0, + exp_max, 1, exp_max); /* 32 steps at base gain 1 + 64 half steps at base gain 2 */ sensor->ctrls.gain = -- cgit v1.2.3 From b9ea99652e973ebdcde855f10c85f690da86cd8d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 May 2025 13:40:54 +0200 Subject: media: atomisp: gc0310: Add check_hwcfg() function Add a check_hwcfg() function to check if the external clk-freq, CSI link-freq and lane-count match the driver's expectations. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-12-hansg@kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 56 ++++++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index 3e94414be6c7..f3dbc3583d97 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -36,6 +36,7 @@ #define GC0310_PIXELRATE 13923000 /* single lane, bus-format is 8 bpp, CSI-2 is double data rate */ #define GC0310_LINK_FREQ (GC0310_PIXELRATE * 8 / 2) +#define GC0310_MCLK_FREQ 19200000 #define GC0310_FPS 30 #define GC0310_SKIP_FRAMES 3 @@ -664,21 +665,68 @@ static void gc0310_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); } -static int gc0310_probe(struct i2c_client *client) +static int gc0310_check_hwcfg(struct device *dev) { + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; struct fwnode_handle *ep_fwnode; - struct gc0310_device *sensor; + unsigned long link_freq_bitmap; + u32 mclk; int ret; /* * Sometimes the fwnode graph is initialized by the bridge driver. * Bridge drivers doing this may also add GPIO mappings, wait for this. */ - ep_fwnode = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); if (!ep_fwnode) - return dev_err_probe(&client->dev, -EPROBE_DEFER, "waiting for fwnode graph endpoint\n"); + return dev_err_probe(dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); + + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &mclk); + if (ret) { + fwnode_handle_put(ep_fwnode); + return dev_err_probe(dev, ret, + "reading clock-frequency property\n"); + } + if (mclk != GC0310_MCLK_FREQ) { + fwnode_handle_put(ep_fwnode); + return dev_err_probe(dev, -EINVAL, + "external clock %u is not supported\n", + mclk); + } + + ret = v4l2_fwnode_endpoint_alloc_parse(ep_fwnode, &bus_cfg); fwnode_handle_put(ep_fwnode); + if (ret) + return dev_err_probe(dev, ret, "parsing endpoint failed\n"); + + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), + &link_freq_bitmap); + + if (ret == 0 && bus_cfg.bus.mipi_csi2.num_data_lanes != 1) + ret = dev_err_probe(dev, -EINVAL, + "number of CSI2 data lanes %u is not supported\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + + v4l2_fwnode_endpoint_free(&bus_cfg); + return ret; +} + +static int gc0310_probe(struct i2c_client *client) +{ + struct gc0310_device *sensor; + int ret; + + ret = gc0310_check_hwcfg(&client->dev); + if (ret) + return ret; sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); if (!sensor) -- cgit v1.2.3 From f934362be2165ca22c3fb460bf5ae9468e5187f5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 May 2025 13:40:55 +0200 Subject: media: atomisp: gc0310: Fix power on/off sleep times Reduce the unnecessary long msleep(100) done on stream start to 10 ms and move this to gc0310_resume() so that it is also done on the initial power-up done by gc0310_detect(), which should fix gc0310_detect() sometimes failing. While at it switch the sleeps from msleep() / usleep_range() to fsleep(). Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-13-hansg@kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index f3dbc3583d97..c89b3e9e3343 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -473,8 +473,6 @@ static int gc0310_s_stream(struct v4l2_subdev *sd, int enable) if (ret < 0) goto error_power_down; - msleep(100); - ret = regmap_multi_reg_write(sensor->regmap, gc0310_reset_register, ARRAY_SIZE(gc0310_reset_register)); @@ -803,10 +801,11 @@ static int gc0310_resume(struct device *dev) struct v4l2_subdev *sd = dev_get_drvdata(dev); struct gc0310_device *sensor = to_gc0310_sensor(sd); - usleep_range(10000, 15000); + fsleep(10 * USEC_PER_MSEC); gpiod_set_value_cansleep(sensor->reset, 0); - usleep_range(10000, 15000); + fsleep(10 * USEC_PER_MSEC); gpiod_set_value_cansleep(sensor->powerdown, 0); + fsleep(10 * USEC_PER_MSEC); return 0; } -- cgit v1.2.3 From 9a21a940681c0ba22d7e9e341a4ab560939fce4b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 May 2025 13:40:56 +0200 Subject: media: atomisp: gc0310: Remove unused is_streaming variable is_streaming is only set and never read, drop it. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-14-hansg@kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index c89b3e9e3343..c6a0bc746d32 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -88,7 +88,6 @@ struct gc0310_device { struct media_pad pad; /* Protect against concurrent changes to controls */ struct mutex input_lock; - bool is_streaming; struct regmap *regmap; struct gpio_desc *reset; @@ -507,13 +506,11 @@ static int gc0310_s_stream(struct v4l2_subdev *sd, int enable) if (!enable) pm_runtime_put(&client->dev); - sensor->is_streaming = enable; mutex_unlock(&sensor->input_lock); return 0; error_power_down: pm_runtime_put(&client->dev); - sensor->is_streaming = false; mutex_unlock(&sensor->input_lock); return ret; } -- cgit v1.2.3 From 889740337f29c12f7eb32c281869654cfe3e32b5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 May 2025 13:40:57 +0200 Subject: media: atomisp: gc0310: Switch to {enable,disable}_streams Switch from s_stream() to enable_streams() and disable_streams() pad operations. They are preferred and required for streams support. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-15-hansg@kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 79 +++++++++++++--------- 1 file changed, 47 insertions(+), 32 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index c6a0bc746d32..2f1f5022e2ac 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -458,58 +458,71 @@ static int gc0310_detect(struct gc0310_device *sensor) return 0; } -static int gc0310_s_stream(struct v4l2_subdev *sd, int enable) +static int gc0310_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) { struct gc0310_device *sensor = to_gc0310_sensor(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = 0; + int ret; - dev_dbg(&client->dev, "%s S enable=%d\n", __func__, enable); mutex_lock(&sensor->input_lock); - if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) - goto error_power_down; + ret = pm_runtime_resume_and_get(&client->dev); + if (ret) + goto error_unlock; - ret = regmap_multi_reg_write(sensor->regmap, - gc0310_reset_register, - ARRAY_SIZE(gc0310_reset_register)); - if (ret) - goto error_power_down; + ret = regmap_multi_reg_write(sensor->regmap, + gc0310_reset_register, + ARRAY_SIZE(gc0310_reset_register)); + if (ret) + goto error_power_down; - ret = regmap_multi_reg_write(sensor->regmap, - gc0310_VGA_30fps, - ARRAY_SIZE(gc0310_VGA_30fps)); - if (ret) - goto error_power_down; + ret = regmap_multi_reg_write(sensor->regmap, + gc0310_VGA_30fps, + ARRAY_SIZE(gc0310_VGA_30fps)); + if (ret) + goto error_power_down; - /* restore value of all ctrls */ - ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler); - if (ret) - goto error_power_down; + /* restore value of all ctrls */ + ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler); - /* enable per frame MIPI and sensor ctrl reset */ - cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, 0x30, &ret); - } + /* enable per frame MIPI and sensor ctrl reset */ + cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, 0x30, &ret); cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, GC0310_REGISTER_PAGE_3, &ret); cci_write(sensor->regmap, GC0310_SW_STREAM_REG, - enable ? GC0310_START_STREAMING : GC0310_STOP_STREAMING, - &ret); + GC0310_START_STREAMING, &ret); cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, GC0310_REGISTER_PAGE_0, &ret); - if (ret) - goto error_power_down; - if (!enable) +error_power_down: + if (ret) pm_runtime_put(&client->dev); +error_unlock: mutex_unlock(&sensor->input_lock); - return 0; + return ret; +} + +static int gc0310_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct gc0310_device *sensor = to_gc0310_sensor(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + mutex_lock(&sensor->input_lock); + + cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, + GC0310_REGISTER_PAGE_3, &ret); + cci_write(sensor->regmap, GC0310_SW_STREAM_REG, + GC0310_STOP_STREAMING, &ret); + cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, + GC0310_REGISTER_PAGE_0, &ret); -error_power_down: pm_runtime_put(&client->dev); mutex_unlock(&sensor->input_lock); return ret; @@ -571,7 +584,7 @@ static const struct v4l2_subdev_sensor_ops gc0310_sensor_ops = { }; static const struct v4l2_subdev_video_ops gc0310_video_ops = { - .s_stream = gc0310_s_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_pad_ops gc0310_pad_ops = { @@ -582,6 +595,8 @@ static const struct v4l2_subdev_pad_ops gc0310_pad_ops = { .get_selection = gc0310_get_selection, .set_selection = gc0310_get_selection, .get_frame_interval = gc0310_get_frame_interval, + .enable_streams = gc0310_enable_streams, + .disable_streams = gc0310_disable_streams, }; static const struct v4l2_subdev_ops gc0310_ops = { -- cgit v1.2.3 From 1e29696182f68b88cf2e459cc928eecc5770b459 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 May 2025 13:40:58 +0200 Subject: media: atomisp: gc0310: Switch to using the sub-device state lock Switch to using the sub-device state lock and properly call v4l2_subdev_init_finalize() / v4l2_subdev_cleanup() on probe() / remove(). Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-16-hansg@kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index 2f1f5022e2ac..baf9355ade4d 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -86,8 +86,6 @@ struct gc0310_device { struct v4l2_subdev sd; struct media_pad pad; - /* Protect against concurrent changes to controls */ - struct mutex input_lock; struct regmap *regmap; struct gpio_desc *reset; @@ -466,11 +464,9 @@ static int gc0310_enable_streams(struct v4l2_subdev *sd, struct i2c_client *client = v4l2_get_subdevdata(sd); int ret; - mutex_lock(&sensor->input_lock); - ret = pm_runtime_resume_and_get(&client->dev); if (ret) - goto error_unlock; + return ret; ret = regmap_multi_reg_write(sensor->regmap, gc0310_reset_register, @@ -501,8 +497,6 @@ error_power_down: if (ret) pm_runtime_put(&client->dev); -error_unlock: - mutex_unlock(&sensor->input_lock); return ret; } @@ -514,8 +508,6 @@ static int gc0310_disable_streams(struct v4l2_subdev *sd, struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; - mutex_lock(&sensor->input_lock); - cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, GC0310_REGISTER_PAGE_3, &ret); cci_write(sensor->regmap, GC0310_SW_STREAM_REG, @@ -524,7 +516,6 @@ static int gc0310_disable_streams(struct v4l2_subdev *sd, GC0310_REGISTER_PAGE_0, &ret); pm_runtime_put(&client->dev); - mutex_unlock(&sensor->input_lock); return ret; } @@ -615,7 +606,6 @@ static int gc0310_init_controls(struct gc0310_device *sensor) v4l2_ctrl_handler_init(hdl, 8); /* Use the same lock for controls as for everything else */ - hdl->lock = &sensor->input_lock; sensor->sd.ctrl_handler = hdl; exp_max = GC0310_NATIVE_HEIGHT + GC0310_V_BLANK_DEFAULT; @@ -669,9 +659,9 @@ static void gc0310_remove(struct i2c_client *client) dev_dbg(&client->dev, "gc0310_remove...\n"); v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sensor->sd.entity); v4l2_ctrl_handler_free(&sensor->ctrls.handler); - mutex_destroy(&sensor->input_lock); pm_runtime_disable(&client->dev); } @@ -754,7 +744,6 @@ static int gc0310_probe(struct i2c_client *client) "getting powerdown GPIO\n"); } - mutex_init(&sensor->input_lock); v4l2_i2c_subdev_init(&sensor->sd, client, &gc0310_ops); gc0310_fill_format(&sensor->mode.fmt); @@ -789,6 +778,13 @@ static int gc0310_probe(struct i2c_client *client) return ret; } + sensor->sd.state_lock = sensor->ctrls.handler.lock; + ret = v4l2_subdev_init_finalize(&sensor->sd); + if (ret) { + gc0310_remove(client); + return ret; + } + ret = v4l2_async_register_subdev_sensor(&sensor->sd); if (ret) { gc0310_remove(client); -- cgit v1.2.3 From 01027ffed991f8e4f05e4d73cd91f17681d1bf51 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 May 2025 13:40:59 +0200 Subject: media: atomisp: gc0310: Implement internal_ops.init_state Implement internal_ops.init_state to fill in the v4l2_mbus_framefmt struct in newly allocated sd-state structs. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-17-hansg@kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index baf9355ade4d..e0a031574a84 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -596,6 +596,17 @@ static const struct v4l2_subdev_ops gc0310_ops = { .sensor = &gc0310_sensor_ops, }; +static int gc0310_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + gc0310_fill_format(v4l2_subdev_state_get_format(sd_state, 0)); + return 0; +} + +static const struct v4l2_subdev_internal_ops gc0310_internal_ops = { + .init_state = gc0310_init_state, +}; + static int gc0310_init_controls(struct gc0310_device *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); @@ -762,6 +773,7 @@ static int gc0310_probe(struct i2c_client *client) return ret; } + sensor->sd.internal_ops = &gc0310_internal_ops; sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; -- cgit v1.2.3 From e0e182ad80e90149d6d73e709d004a640bdce3ae Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 May 2025 13:41:00 +0200 Subject: media: atomisp: gc0310: Use v4l2_subdev_get_fmt() as v4l2_subdev_pad_ops.get_fmt() Now that the sd-state's fmt is properly initialized by internal_ops.init_state(), the driver can be safely switched to v4l2_subdev_get_fmt(). Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-18-hansg@kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index e0a031574a84..f4b302d5eead 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -390,18 +390,6 @@ static int gc0310_set_fmt(struct v4l2_subdev *sd, return 0; } -static int gc0310_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct gc0310_device *sensor = to_gc0310_sensor(sd); - struct v4l2_mbus_framefmt *fmt; - - fmt = gc0310_get_pad_format(sensor, sd_state, format->pad, format->which); - format->format = *fmt; - return 0; -} - static int gc0310_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel) @@ -581,7 +569,7 @@ static const struct v4l2_subdev_video_ops gc0310_video_ops = { static const struct v4l2_subdev_pad_ops gc0310_pad_ops = { .enum_mbus_code = gc0310_enum_mbus_code, .enum_frame_size = gc0310_enum_frame_size, - .get_fmt = gc0310_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = gc0310_set_fmt, .get_selection = gc0310_get_selection, .set_selection = gc0310_get_selection, -- cgit v1.2.3 From 0f8b9632fa87eab457171198e1de8430536cace4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 May 2025 13:41:01 +0200 Subject: media: atomisp: gc0310: Switch to using sd.active_state fmt Stop having a v4l2_mbus_framefmt mode.fmt driver-data member to store the fmt for the active-state, instead use sd.active_state fmt. This also removes the need for gc0310_get_pad_format() since v4l2_subdev_state_get_format() now will return the correct v4l2_mbus_framefmt for all whence values. Instead of switching gc0310_set_fmt() from gc0310_get_pad_format() to v4l2_subdev_state_get_format() just drop it entirely since there is only 1 fixed mode. Otherwise the new gc0310_set_fmt() would be 100% the same as v4l2_subdev_get_fmt() after this. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-19-hansg@kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 32 +--------------------- 1 file changed, 1 insertion(+), 31 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index f4b302d5eead..44e834c608f4 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -91,10 +91,6 @@ struct gc0310_device { struct gpio_desc *reset; struct gpio_desc *powerdown; - struct gc0310_mode { - struct v4l2_mbus_framefmt fmt; - } mode; - struct gc0310_ctrls { struct v4l2_ctrl_handler handler; struct v4l2_ctrl *exposure; @@ -355,17 +351,6 @@ static const struct v4l2_ctrl_ops ctrl_ops = { .s_ctrl = gc0310_s_ctrl, }; -static struct v4l2_mbus_framefmt * -gc0310_get_pad_format(struct gc0310_device *sensor, - struct v4l2_subdev_state *state, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_state_get_format(state, pad); - - return &sensor->mode.fmt; -} - /* The GC0310 currently only supports 1 fixed fmt */ static void gc0310_fill_format(struct v4l2_mbus_framefmt *fmt) { @@ -376,20 +361,6 @@ static void gc0310_fill_format(struct v4l2_mbus_framefmt *fmt) fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8; } -static int gc0310_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct gc0310_device *sensor = to_gc0310_sensor(sd); - struct v4l2_mbus_framefmt *fmt; - - fmt = gc0310_get_pad_format(sensor, sd_state, format->pad, format->which); - gc0310_fill_format(fmt); - - format->format = *fmt; - return 0; -} - static int gc0310_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel) @@ -570,7 +541,7 @@ static const struct v4l2_subdev_pad_ops gc0310_pad_ops = { .enum_mbus_code = gc0310_enum_mbus_code, .enum_frame_size = gc0310_enum_frame_size, .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = gc0310_set_fmt, + .set_fmt = v4l2_subdev_get_fmt, /* Only 1 fixed mode supported */ .get_selection = gc0310_get_selection, .set_selection = gc0310_get_selection, .get_frame_interval = gc0310_get_frame_interval, @@ -744,7 +715,6 @@ static int gc0310_probe(struct i2c_client *client) } v4l2_i2c_subdev_init(&sensor->sd, client, &gc0310_ops); - gc0310_fill_format(&sensor->mode.fmt); sensor->regmap = devm_cci_regmap_init_i2c(client, 8); if (IS_ERR(sensor->regmap)) -- cgit v1.2.3 From 4d697daae8d82201f57c013c745b734cf5f8fd04 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 May 2025 13:41:02 +0200 Subject: media: atomisp: gc0310: Move and rename suspend/resume functions Move the suspend()/resume() functions to above gc0310_detect() and rename the functions to power_off()/power_on(). No functional changes, this is a preparation patch for reworking the runtime-pm handling in probe() and remove(). Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-20-hansg@kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 51 +++++++++++----------- 1 file changed, 26 insertions(+), 25 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index 44e834c608f4..c4d85d65737c 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -383,6 +383,30 @@ static int gc0310_get_selection(struct v4l2_subdev *sd, return 0; } +static int gc0310_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc0310_device *sensor = to_gc0310_sensor(sd); + + gpiod_set_value_cansleep(sensor->powerdown, 1); + gpiod_set_value_cansleep(sensor->reset, 1); + return 0; +} + +static int gc0310_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc0310_device *sensor = to_gc0310_sensor(sd); + + fsleep(10 * USEC_PER_MSEC); + gpiod_set_value_cansleep(sensor->reset, 0); + fsleep(10 * USEC_PER_MSEC); + gpiod_set_value_cansleep(sensor->powerdown, 0); + fsleep(10 * USEC_PER_MSEC); + + return 0; +} + static int gc0310_detect(struct gc0310_device *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); @@ -764,31 +788,8 @@ static int gc0310_probe(struct i2c_client *client) return 0; } -static int gc0310_suspend(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct gc0310_device *sensor = to_gc0310_sensor(sd); - - gpiod_set_value_cansleep(sensor->powerdown, 1); - gpiod_set_value_cansleep(sensor->reset, 1); - return 0; -} - -static int gc0310_resume(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct gc0310_device *sensor = to_gc0310_sensor(sd); - - fsleep(10 * USEC_PER_MSEC); - gpiod_set_value_cansleep(sensor->reset, 0); - fsleep(10 * USEC_PER_MSEC); - gpiod_set_value_cansleep(sensor->powerdown, 0); - fsleep(10 * USEC_PER_MSEC); - - return 0; -} - -static DEFINE_RUNTIME_DEV_PM_OPS(gc0310_pm_ops, gc0310_suspend, gc0310_resume, NULL); +static DEFINE_RUNTIME_DEV_PM_OPS(gc0310_pm_ops, + gc0310_power_off, gc0310_power_on, NULL); static const struct acpi_device_id gc0310_acpi_match[] = { {"INT0310"}, -- cgit v1.2.3 From 4aaa74642d8c808f9602e4b9d276ea4d32c34515 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 May 2025 13:41:03 +0200 Subject: media: atomisp: gc0310: runtime-PM fixes The i2c-client's power-domain has already been powered up when probe() gets called. So e.g. ACPI resources for regulators and clks have already been enabled and only the GPIOs need to be set to the on state. Instead of calling pm_runtime_set_suspended() while the domain is already powered up and then have detect() do a pm_runtime_get() to set the GPIOs do the following: 1. Call gc0310_power_on() to only set the GPIOs 2. Set the device's runtime-PM state to active instead of suspended 3. Avoid the device getting suspended as soon as pm_runtime_enable() gets called by calling pm_runtime_get() before _enable(), this means moving the pm_runtime_get() / _put() from detect() to probe () This fixes power_on() not getting called when runtime-PM is not enabled in the Kconfig and this keeps the sensor powered-up while registering it avoiding unnecessary power cycles. Also modify gc0310_remove() to power-off the device if it is in active state when gc0310_remove() runs. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-21-hansg@kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 58 +++++++++++----------- 1 file changed, 28 insertions(+), 30 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index c4d85d65737c..173ddf41ad47 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -416,11 +416,7 @@ static int gc0310_detect(struct gc0310_device *sensor) if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; - ret = pm_runtime_get_sync(&client->dev); - if (ret >= 0) - ret = cci_read(sensor->regmap, GC0310_SC_CMMN_CHIP_ID_REG, - &val, NULL); - pm_runtime_put(&client->dev); + ret = cci_read(sensor->regmap, GC0310_SC_CMMN_CHIP_ID_REG, &val, NULL); if (ret < 0) { dev_err(&client->dev, "read sensor_id failed: %d\n", ret); return -ENODEV; @@ -650,13 +646,15 @@ static void gc0310_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct gc0310_device *sensor = to_gc0310_sensor(sd); - dev_dbg(&client->dev, "gc0310_remove...\n"); - v4l2_async_unregister_subdev(sd); v4l2_subdev_cleanup(sd); media_entity_cleanup(&sensor->sd.entity); v4l2_ctrl_handler_free(&sensor->ctrls.handler); pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) { + gc0310_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + } } static int gc0310_check_hwcfg(struct device *dev) @@ -744,16 +742,15 @@ static int gc0310_probe(struct i2c_client *client) if (IS_ERR(sensor->regmap)) return PTR_ERR(sensor->regmap); - pm_runtime_set_suspended(&client->dev); + gc0310_power_on(&client->dev); + + pm_runtime_set_active(&client->dev); + pm_runtime_get_noresume(&client->dev); pm_runtime_enable(&client->dev); - pm_runtime_set_autosuspend_delay(&client->dev, 1000); - pm_runtime_use_autosuspend(&client->dev); ret = gc0310_detect(sensor); - if (ret) { - gc0310_remove(client); - return ret; - } + if (ret) + goto err_power_down; sensor->sd.internal_ops = &gc0310_internal_ops; sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -761,31 +758,32 @@ static int gc0310_probe(struct i2c_client *client) sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = gc0310_init_controls(sensor); - if (ret) { - gc0310_remove(client); - return ret; - } + if (ret) + goto err_power_down; ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); - if (ret) { - gc0310_remove(client); - return ret; - } + if (ret) + goto err_power_down; sensor->sd.state_lock = sensor->ctrls.handler.lock; ret = v4l2_subdev_init_finalize(&sensor->sd); - if (ret) { - gc0310_remove(client); - return ret; - } + if (ret) + goto err_power_down; ret = v4l2_async_register_subdev_sensor(&sensor->sd); - if (ret) { - gc0310_remove(client); - return ret; - } + if (ret) + goto err_power_down; + + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put_autosuspend(&client->dev); return 0; + +err_power_down: + pm_runtime_put_noidle(&client->dev); + gc0310_remove(client); + return ret; } static DEFINE_RUNTIME_DEV_PM_OPS(gc0310_pm_ops, -- cgit v1.2.3 From 3c30c8948d8143d1ed38234b3614e81229a7f6b3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 May 2025 13:41:04 +0200 Subject: media: atomisp: gc0310: Drop gc0310_get_frame_interval() On raw camera sensors the framerate is controlled through vblank (and optional) hblank controls. Having a get_frame_interval makes no sense in this case, drop it. Signed-off-by: Hans de Goede Reviewed-by: Kieran Bingham Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-22-hansg@kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index 173ddf41ad47..956d3213d198 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -498,23 +498,6 @@ static int gc0310_disable_streams(struct v4l2_subdev *sd, return ret; } -static int gc0310_get_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_interval *interval) -{ - /* - * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 - * subdev active state API. - */ - if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - interval->interval.numerator = 1; - interval->interval.denominator = GC0310_FPS; - - return 0; -} - static int gc0310_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -564,7 +547,6 @@ static const struct v4l2_subdev_pad_ops gc0310_pad_ops = { .set_fmt = v4l2_subdev_get_fmt, /* Only 1 fixed mode supported */ .get_selection = gc0310_get_selection, .set_selection = gc0310_get_selection, - .get_frame_interval = gc0310_get_frame_interval, .enable_streams = gc0310_enable_streams, .disable_streams = gc0310_disable_streams, }; -- cgit v1.2.3 From 4ea35dbdaf5f914ce78b5197ae2e3dcee58c1280 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 17 May 2025 13:41:05 +0200 Subject: media: atomisp: gc0310: Drop gc0310_g_skip_frames() The g_skip_frames sensor-op is obsolete, drop it. Signed-off-by: Hans de Goede Reviewed-by: Kieran Bingham Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250517114106.43494-23-hansg@kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc0310.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index 956d3213d198..7af4d66f42a0 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -526,16 +526,6 @@ static int gc0310_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int gc0310_g_skip_frames(struct v4l2_subdev *sd, u32 *frames) -{ - *frames = GC0310_SKIP_FRAMES; - return 0; -} - -static const struct v4l2_subdev_sensor_ops gc0310_sensor_ops = { - .g_skip_frames = gc0310_g_skip_frames, -}; - static const struct v4l2_subdev_video_ops gc0310_video_ops = { .s_stream = v4l2_subdev_s_stream_helper, }; @@ -554,7 +544,6 @@ static const struct v4l2_subdev_pad_ops gc0310_pad_ops = { static const struct v4l2_subdev_ops gc0310_ops = { .video = &gc0310_video_ops, .pad = &gc0310_pad_ops, - .sensor = &gc0310_sensor_ops, }; static int gc0310_init_state(struct v4l2_subdev *sd, -- cgit v1.2.3 From 11d3d8dd0252c15eae380760439403d3b333ba5f Mon Sep 17 00:00:00 2001 From: Thomas Andreatta Date: Thu, 19 Jun 2025 10:44:20 +0200 Subject: media: atomisp: gc0310: Remove redundant debug message Checkpatch fix: deleted `dev_dbg()` printing the name of the function. ftrace can be used instead. Signed-off-by: Thomas Andreatta Link: https://lore.kernel.org/r/20250619084420.146151-2-thomas.andreatta2000@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/atomisp-gc2235.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c index 857d7175942c..6fc39ab95e46 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c @@ -779,8 +779,6 @@ static void gc2235_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct gc2235_device *dev = to_gc2235_sensor(sd); - dev_dbg(&client->dev, "gc2235_remove...\n"); - dev->platform_data->csi_cfg(sd, 0); v4l2_device_unregister_subdev(sd); -- cgit v1.2.3 From 51c19651a7dc74a255041acc0b1c91a197088f0e Mon Sep 17 00:00:00 2001 From: Thomas Andreatta Date: Thu, 19 Jun 2025 10:44:22 +0200 Subject: media: atomisp: gc2235: Fix struct definition style Reorder const qualifier in array declaration. Signed-off-by: Thomas Andreatta Link: https://lore.kernel.org/r/20250619084420.146151-3-thomas.andreatta2000@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/gc2235.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/gc2235.h b/drivers/staging/media/atomisp/i2c/gc2235.h index 6c743a17f198..7dd9a676fb98 100644 --- a/drivers/staging/media/atomisp/i2c/gc2235.h +++ b/drivers/staging/media/atomisp/i2c/gc2235.h @@ -179,21 +179,21 @@ struct gc2235_write_ctrl { struct gc2235_write_buffer buffer; }; -static struct gc2235_reg const gc2235_stream_on[] = { +static const struct gc2235_reg gc2235_stream_on[] = { { GC2235_8BIT, 0xfe, 0x03}, /* switch to P3 */ { GC2235_8BIT, 0x10, 0x91}, /* start mipi */ { GC2235_8BIT, 0xfe, 0x00}, /* switch to P0 */ { GC2235_TOK_TERM, 0, 0 } }; -static struct gc2235_reg const gc2235_stream_off[] = { +static const struct gc2235_reg gc2235_stream_off[] = { { GC2235_8BIT, 0xfe, 0x03}, /* switch to P3 */ { GC2235_8BIT, 0x10, 0x01}, /* stop mipi */ { GC2235_8BIT, 0xfe, 0x00}, /* switch to P0 */ { GC2235_TOK_TERM, 0, 0 } }; -static struct gc2235_reg const gc2235_init_settings[] = { +static const struct gc2235_reg gc2235_init_settings[] = { /* System */ { GC2235_8BIT, 0xfe, 0x80 }, { GC2235_8BIT, 0xfe, 0x80 }, @@ -268,7 +268,7 @@ static struct gc2235_reg const gc2235_init_settings[] = { * Register settings for various resolution */ #if ENABLE_NON_PREVIEW -static struct gc2235_reg const gc2235_1296_736_30fps[] = { +static const struct gc2235_reg gc2235_1296_736_30fps[] = { { GC2235_8BIT, 0x8b, 0xa0 }, { GC2235_8BIT, 0x8c, 0x02 }, @@ -321,7 +321,7 @@ static struct gc2235_reg const gc2235_1296_736_30fps[] = { { GC2235_TOK_TERM, 0, 0 } }; -static struct gc2235_reg const gc2235_960_640_30fps[] = { +static const struct gc2235_reg gc2235_960_640_30fps[] = { { GC2235_8BIT, 0x8b, 0xa0 }, { GC2235_8BIT, 0x8c, 0x02 }, @@ -373,7 +373,7 @@ static struct gc2235_reg const gc2235_960_640_30fps[] = { }; #endif -static struct gc2235_reg const gc2235_1600_900_30fps[] = { +static const struct gc2235_reg gc2235_1600_900_30fps[] = { { GC2235_8BIT, 0x8b, 0xa0 }, { GC2235_8BIT, 0x8c, 0x02 }, @@ -418,7 +418,7 @@ static struct gc2235_reg const gc2235_1600_900_30fps[] = { { GC2235_TOK_TERM, 0, 0 } }; -static struct gc2235_reg const gc2235_1616_1082_30fps[] = { +static const struct gc2235_reg gc2235_1616_1082_30fps[] = { { GC2235_8BIT, 0x8b, 0xa0 }, { GC2235_8BIT, 0x8c, 0x02 }, @@ -463,7 +463,7 @@ static struct gc2235_reg const gc2235_1616_1082_30fps[] = { { GC2235_TOK_TERM, 0, 0 } }; -static struct gc2235_reg const gc2235_1616_1216_30fps[] = { +static const struct gc2235_reg gc2235_1616_1216_30fps[] = { { GC2235_8BIT, 0x8b, 0xa0 }, { GC2235_8BIT, 0x8c, 0x02 }, -- cgit v1.2.3 From 3c92c08cd413ef87ae68785cd09a2a9f940521fc Mon Sep 17 00:00:00 2001 From: Thomas Andreatta Date: Thu, 19 Jun 2025 10:44:24 +0200 Subject: media: atomisp: ov2722: Fix struct definition style Reorder const qualifier in array declaration. Signed-off-by: Thomas Andreatta Link: https://lore.kernel.org/r/20250619084420.146151-4-thomas.andreatta2000@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/i2c/ov2722.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/i2c/ov2722.h b/drivers/staging/media/atomisp/i2c/ov2722.h index bc36133f3722..00317d105305 100644 --- a/drivers/staging/media/atomisp/i2c/ov2722.h +++ b/drivers/staging/media/atomisp/i2c/ov2722.h @@ -236,7 +236,7 @@ struct ov2722_write_ctrl { * Register settings for various resolution */ #if 0 -static struct ov2722_reg const ov2722_QVGA_30fps[] = { +static const struct ov2722_reg ov2722_QVGA_30fps[] = { {OV2722_8BIT, 0x3718, 0x10}, {OV2722_8BIT, 0x3702, 0x0c}, {OV2722_8BIT, 0x373a, 0x1c}, @@ -346,7 +346,7 @@ static struct ov2722_reg const ov2722_QVGA_30fps[] = { }; -static struct ov2722_reg const ov2722_480P_30fps[] = { +static const struct ov2722_reg ov2722_480P_30fps[] = { {OV2722_8BIT, 0x3718, 0x10}, {OV2722_8BIT, 0x3702, 0x18}, {OV2722_8BIT, 0x373a, 0x3c}, @@ -455,7 +455,7 @@ static struct ov2722_reg const ov2722_480P_30fps[] = { {OV2722_TOK_TERM, 0, 0}, }; -static struct ov2722_reg const ov2722_VGA_30fps[] = { +static const struct ov2722_reg ov2722_VGA_30fps[] = { {OV2722_8BIT, 0x3718, 0x10}, {OV2722_8BIT, 0x3702, 0x18}, {OV2722_8BIT, 0x373a, 0x3c}, @@ -565,7 +565,7 @@ static struct ov2722_reg const ov2722_VGA_30fps[] = { }; #endif -static struct ov2722_reg const ov2722_1632_1092_30fps[] = { +static const struct ov2722_reg ov2722_1632_1092_30fps[] = { {OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for a whole frame complete.(vblank) */ {OV2722_8BIT, 0x3718, 0x10}, @@ -667,7 +667,7 @@ static struct ov2722_reg const ov2722_1632_1092_30fps[] = { {OV2722_TOK_TERM, 0, 0} }; -static struct ov2722_reg const ov2722_1452_1092_30fps[] = { +static const struct ov2722_reg ov2722_1452_1092_30fps[] = { {OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for a whole frame complete.(vblank) */ {OV2722_8BIT, 0x3718, 0x10}, @@ -769,7 +769,7 @@ static struct ov2722_reg const ov2722_1452_1092_30fps[] = { }; #if 0 -static struct ov2722_reg const ov2722_1M3_30fps[] = { +static const struct ov2722_reg ov2722_1M3_30fps[] = { {OV2722_8BIT, 0x3718, 0x10}, {OV2722_8BIT, 0x3702, 0x24}, {OV2722_8BIT, 0x373a, 0x60}, @@ -877,7 +877,7 @@ static struct ov2722_reg const ov2722_1M3_30fps[] = { }; #endif -static struct ov2722_reg const ov2722_1080p_30fps[] = { +static const struct ov2722_reg ov2722_1080p_30fps[] = { {OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for a whole frame complete.(vblank) */ {OV2722_8BIT, 0x3718, 0x10}, @@ -983,7 +983,7 @@ static struct ov2722_reg const ov2722_1080p_30fps[] = { }; #if 0 /* Currently unused */ -static struct ov2722_reg const ov2722_720p_30fps[] = { +static const struct ov2722_reg ov2722_720p_30fps[] = { {OV2722_8BIT, 0x3021, 0x03}, {OV2722_8BIT, 0x3718, 0x10}, {OV2722_8BIT, 0x3702, 0x24}, -- cgit v1.2.3 From 58913d96525b266d7cf9e6ec787ad1c7df355b2b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 19 May 2025 18:46:47 +0300 Subject: media: atomisp: Remove unused header There are some unused definitions that are using macros from math_support.h. In order to have cleaner future changes, remove them first. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250519155028.526453-2-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Tested-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../pci/isp/modes/interface/input_buf.isp.h | 1 - .../atomisp/pci/isp/modes/interface/isp_const.h | 157 --------------------- .../staging/media/atomisp/pci/sh_css_param_dvs.h | 3 - 3 files changed, 161 deletions(-) delete mode 100644 drivers/staging/media/atomisp/pci/isp/modes/interface/isp_const.h (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h b/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h index c7ade6ce6c68..55de6cb02833 100644 --- a/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h +++ b/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h @@ -10,7 +10,6 @@ Copyright (c) 2010 - 2015, Intel Corporation. /* Temporary include, since IA_CSS_BINARY_MODE_COPY is still needed */ #include "sh_css_defs.h" -#include "isp_const.h" /* MAX_VECTORS_PER_INPUT_LINE */ #define INPUT_BUF_HEIGHT 2 /* double buffer */ #define INPUT_BUF_LINES 2 diff --git a/drivers/staging/media/atomisp/pci/isp/modes/interface/isp_const.h b/drivers/staging/media/atomisp/pci/isp/modes/interface/isp_const.h deleted file mode 100644 index b767b0d35bb4..000000000000 --- a/drivers/staging/media/atomisp/pci/isp/modes/interface/isp_const.h +++ /dev/null @@ -1,157 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/** -Support for Intel Camera Imaging ISP subsystem. -Copyright (c) 2010 - 2015, Intel Corporation. - -*/ - -#ifndef _COMMON_ISP_CONST_H_ -#define _COMMON_ISP_CONST_H_ - -/*#include "isp.h"*/ /* ISP_VEC_NELEMS */ - -/* Binary independent constants */ - -#ifndef NO_HOIST -# define NO_HOIST HIVE_ATTRIBUTE((no_hoist)) -#endif - -#define NO_HOIST_CSE HIVE_ATTRIBUTE((no_hoist, no_cse)) - -#define UNION struct /* Union constructors not allowed in C++ */ - -#define XMEM_WIDTH_BITS HIVE_ISP_DDR_WORD_BITS -#define XMEM_SHORTS_PER_WORD (HIVE_ISP_DDR_WORD_BITS / 16) -#define XMEM_INTS_PER_WORD (HIVE_ISP_DDR_WORD_BITS / 32) -#define XMEM_POW2_BYTES_PER_WORD HIVE_ISP_DDR_WORD_BYTES - -#define BITS8_ELEMENTS_PER_XMEM_ADDR CEIL_DIV(XMEM_WIDTH_BITS, 8) -#define BITS16_ELEMENTS_PER_XMEM_ADDR CEIL_DIV(XMEM_WIDTH_BITS, 16) - -#define ISP_NWAY_LOG2 6 - -/* ***************************** - * ISP input/output buffer sizes - * ****************************/ -/* input image */ -#define INPUT_BUF_DMA_HEIGHT 2 -#define INPUT_BUF_HEIGHT 2 /* double buffer */ -#define OUTPUT_BUF_DMA_HEIGHT 2 -#define OUTPUT_BUF_HEIGHT 2 /* double buffer */ -#define OUTPUT_NUM_TRANSFERS 4 - -/* GDC accelerator: Up/Down Scaling */ -/* These should be moved to the gdc_defs.h in the device */ -#define UDS_SCALING_N HRT_GDC_N -/* AB: This should cover the zooming up to 16MP */ -#define UDS_MAX_OXDIM 5000 -/* We support maximally 2 planes with different parameters - - luma and chroma (YUV420) */ -#define UDS_MAX_PLANES 2 -#define UDS_BLI_BLOCK_HEIGHT 2 -#define UDS_BCI_BLOCK_HEIGHT 4 -#define UDS_BLI_INTERP_ENVELOPE 1 -#define UDS_BCI_INTERP_ENVELOPE 3 -#define UDS_MAX_ZOOM_FAC 64 -/* Make it always one FPGA vector. - Four FPGA vectors are required and - four of them fit in one ASIC vector.*/ -#define UDS_MAX_CHUNKS 16 - -#define ISP_LEFT_PADDING _ISP_LEFT_CROP_EXTRA(ISP_LEFT_CROPPING) -#define ISP_LEFT_PADDING_VECS CEIL_DIV(ISP_LEFT_PADDING, ISP_VEC_NELEMS) -/* in case of continuous the croppong of the current binary doesn't matter for the buffer calculation, but the cropping of the sp copy should be used */ -#define ISP_LEFT_PADDING_CONT _ISP_LEFT_CROP_EXTRA(SH_CSS_MAX_LEFT_CROPPING) -#define ISP_LEFT_PADDING_VECS_CONT CEIL_DIV(ISP_LEFT_PADDING_CONT, ISP_VEC_NELEMS) - -#define CEIL_ROUND_DIV_STRIPE(width, stripe, padding) \ - CEIL_MUL(padding + CEIL_DIV(width - padding, stripe), ((ENABLE_RAW_BINNING || ENABLE_FIXED_BAYER_DS) ? 4 : 2)) - -/* output (Y,U,V) image, 4:2:0 */ -#define MAX_VECTORS_PER_LINE \ - CEIL_ROUND_DIV_STRIPE(CEIL_DIV(ISP_MAX_INTERNAL_WIDTH, ISP_VEC_NELEMS), \ - ISP_NUM_STRIPES, \ - ISP_LEFT_PADDING_VECS) - -/* - * ITERATOR_VECTOR_INCREMENT' explanation: - * when striping an even number of iterations, one of the stripes is - * one iteration wider than the other to account for overlap - * so the calc for the output buffer vmem size is: - * ((width[vectors]/num_of_stripes) + 2[vectors]) - */ -#define MAX_VECTORS_PER_OUTPUT_LINE \ - CEIL_DIV(CEIL_DIV(ISP_MAX_OUTPUT_WIDTH, ISP_NUM_STRIPES) + ISP_LEFT_PADDING, ISP_VEC_NELEMS) - -/* Must be even due to interlaced bayer input */ -#define MAX_VECTORS_PER_INPUT_LINE CEIL_MUL((CEIL_DIV(ISP_MAX_INPUT_WIDTH, ISP_VEC_NELEMS) + ISP_LEFT_PADDING_VECS), 2) -#define MAX_VECTORS_PER_INPUT_STRIPE CEIL_ROUND_DIV_STRIPE(MAX_VECTORS_PER_INPUT_LINE, \ - ISP_NUM_STRIPES, \ - ISP_LEFT_PADDING_VECS) - -/* Add 2 for left croppping */ -#define MAX_SP_RAW_COPY_VECTORS_PER_INPUT_LINE (CEIL_DIV(ISP_MAX_INPUT_WIDTH, ISP_VEC_NELEMS) + 2) - -#define MAX_VECTORS_PER_BUF_LINE \ - (MAX_VECTORS_PER_LINE + DUMMY_BUF_VECTORS) -#define MAX_VECTORS_PER_BUF_INPUT_LINE \ - (MAX_VECTORS_PER_INPUT_STRIPE + DUMMY_BUF_VECTORS) -#define MAX_OUTPUT_Y_FRAME_WIDTH \ - (MAX_VECTORS_PER_LINE * ISP_VEC_NELEMS) -#define MAX_OUTPUT_Y_FRAME_SIMDWIDTH \ - MAX_VECTORS_PER_LINE -#define MAX_OUTPUT_C_FRAME_WIDTH \ - (MAX_OUTPUT_Y_FRAME_WIDTH / 2) -#define MAX_OUTPUT_C_FRAME_SIMDWIDTH \ - CEIL_DIV(MAX_OUTPUT_C_FRAME_WIDTH, ISP_VEC_NELEMS) - -/* should be even */ -#define NO_CHUNKING (OUTPUT_NUM_CHUNKS == 1) - -#define MAX_VECTORS_PER_CHUNK \ - (NO_CHUNKING ? MAX_VECTORS_PER_LINE \ - : 2 * CEIL_DIV(MAX_VECTORS_PER_LINE, \ - 2 * OUTPUT_NUM_CHUNKS)) - -#define MAX_C_VECTORS_PER_CHUNK \ - (MAX_VECTORS_PER_CHUNK / 2) - -/* should be even */ -#define MAX_VECTORS_PER_OUTPUT_CHUNK \ - (NO_CHUNKING ? MAX_VECTORS_PER_OUTPUT_LINE \ - : 2 * CEIL_DIV(MAX_VECTORS_PER_OUTPUT_LINE, \ - 2 * OUTPUT_NUM_CHUNKS)) - -#define MAX_C_VECTORS_PER_OUTPUT_CHUNK \ - (MAX_VECTORS_PER_OUTPUT_CHUNK / 2) - -/* should be even */ -#define MAX_VECTORS_PER_INPUT_CHUNK \ - (INPUT_NUM_CHUNKS == 1 ? MAX_VECTORS_PER_INPUT_STRIPE \ - : 2 * CEIL_DIV(MAX_VECTORS_PER_INPUT_STRIPE, \ - 2 * OUTPUT_NUM_CHUNKS)) - -#define DEFAULT_C_SUBSAMPLING 2 - -/****** DMA buffer properties */ - -#define RAW_BUF_LINES ((ENABLE_RAW_BINNING || ENABLE_FIXED_BAYER_DS) ? 4 : 2) - -/* [isp vmem] table size[vectors] per line per color (GR,R,B,GB), - multiples of NWAY */ -#define ISP2400_SCTBL_VECTORS_PER_LINE_PER_COLOR \ - CEIL_DIV(SH_CSS_MAX_SCTBL_WIDTH_PER_COLOR, ISP_VEC_NELEMS) -#define ISP2401_SCTBL_VECTORS_PER_LINE_PER_COLOR \ - CEIL_DIV(SH_CSS_MAX_SCTBL_WIDTH_PER_COLOR, ISP_VEC_NELEMS) -/* [isp vmem] table size[vectors] per line for 4colors (GR,R,B,GB), - multiples of NWAY */ -#define SCTBL_VECTORS_PER_LINE \ - (SCTBL_VECTORS_PER_LINE_PER_COLOR * IA_CSS_SC_NUM_COLORS) - -/*************/ - -/* Format for fixed primaries */ - -#define ISP_FIXED_PRIMARY_FORMAT IA_CSS_FRAME_FORMAT_NV12 - -#endif /* _COMMON_ISP_CONST_H_ */ diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h index b7057887adea..fd5cf6038829 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h +++ b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h @@ -33,9 +33,6 @@ #define DVS_TABLE_IN_BLOCKDIM_Y_LUMA(X) (DVS_NUM_BLOCKS_Y(X) + 1) #define DVS_TABLE_IN_BLOCKDIM_Y_CHROMA(X) (DVS_NUM_BLOCKS_Y_CHROMA(X) + 1) -#define DVS_ENVELOPE_X(X) (((X) == 0) ? (DVS_ENV_MIN_X) : (X)) -#define DVS_ENVELOPE_Y(X) (((X) == 0) ? (DVS_ENV_MIN_Y) : (X)) - #define DVS_COORD_FRAC_BITS (10) /* ISP2400 */ -- cgit v1.2.3 From 782fc724bfa44daa2e69e364426ea67b66deb1b3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 19 May 2025 18:46:48 +0300 Subject: media: atomisp: Replace macros from math_support.h Replace rarely used macros by generic ones from Linux kernel headers. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250519155028.526453-3-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Tested-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../pci/isp/kernels/anr/anr_1.0/ia_css_anr_types.h | 4 ++- .../pci/isp/kernels/dpc2/ia_css_dpc2_param.h | 6 +++-- .../pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c | 4 +-- .../pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h | 22 ++++++++-------- .../pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c | 6 +++-- .../pci/isp/kernels/sc/sc_1.0/ia_css_sc_param.h | 2 +- .../pci/isp/modes/interface/input_buf.isp.h | 5 +++- .../atomisp/pci/runtime/debug/src/ia_css_debug.c | 1 - .../media/atomisp/pci/runtime/frame/src/frame.c | 29 +++++++++++----------- .../media/atomisp/pci/runtime/ifmtr/src/ifmtr.c | 11 ++++---- .../atomisp/pci/runtime/isys/src/virtual_isys.c | 2 +- drivers/staging/media/atomisp/pci/sh_css_defs.h | 12 ++++----- .../staging/media/atomisp/pci/sh_css_internal.h | 8 +++--- .../staging/media/atomisp/pci/sh_css_param_dvs.h | 19 ++++++++------ drivers/staging/media/atomisp/pci/sh_css_params.c | 12 ++++----- 15 files changed, 77 insertions(+), 66 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr_types.h b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr_types.h index 6e573ceaa9ea..bc2a78dff004 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr_types.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr_types.h @@ -11,9 +11,11 @@ * CSS-API header file for Advanced Noise Reduction kernel v1 */ +#include + /* Application specific DMA settings */ #define ANR_BPP 10 -#define ANR_ELEMENT_BITS ((CEIL_DIV(ANR_BPP, 8)) * 8) +#define ANR_ELEMENT_BITS round_up(ANR_BPP, 8) /* Advanced Noise Reduction configuration. * This is also known as Low-Light. diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/dpc2/ia_css_dpc2_param.h b/drivers/staging/media/atomisp/pci/isp/kernels/dpc2/ia_css_dpc2_param.h index 099f32b8de1a..b1bbc283e367 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/dpc2/ia_css_dpc2_param.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/dpc2/ia_css_dpc2_param.h @@ -7,6 +7,8 @@ #ifndef __IA_CSS_DPC2_PARAM_H #define __IA_CSS_DPC2_PARAM_H +#include + #include "type_support.h" #include "vmem.h" /* for VMEM_ARRAY*/ @@ -19,12 +21,12 @@ /* 3 lines state per color plane input_line_state */ #define DPC2_STATE_INPUT_BUFFER_HEIGHT (3 * NUM_PLANES) /* Each plane has width equal to half frame line */ -#define DPC2_STATE_INPUT_BUFFER_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2) +#define DPC2_STATE_INPUT_BUFFER_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2) /* 1 line state per color plane for local deviation state*/ #define DPC2_STATE_LOCAL_DEVIATION_BUFFER_HEIGHT (1 * NUM_PLANES) /* Each plane has width equal to half frame line */ -#define DPC2_STATE_LOCAL_DEVIATION_BUFFER_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2) +#define DPC2_STATE_LOCAL_DEVIATION_BUFFER_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2) /* MINMAX state buffer stores 1 full input line (GR-R color line) */ #define DPC2_STATE_SECOND_MINMAX_BUFFER_HEIGHT 1 diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c index 30c84639d7e8..e9d6dd0bbfe2 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c @@ -141,10 +141,10 @@ convert_coords_to_ispparams( /* similar to topleft_y calculation, but round up if ymax * has any fraction bits */ - bottom_y = CEIL_DIV(ymax, 1 << DVS_COORD_FRAC_BITS); + bottom_y = DIV_ROUND_UP(ymax, BIT(DVS_COORD_FRAC_BITS)); s.in_block_height = bottom_y - topleft_y + dvs_interp_envelope; - bottom_x = CEIL_DIV(xmax, 1 << DVS_COORD_FRAC_BITS); + bottom_x = DIV_ROUND_UP(xmax, BIT(DVS_COORD_FRAC_BITS)); s.in_block_width = bottom_x - topleft_x + dvs_interp_envelope; topleft_x_frac = topleft_x << (DVS_COORD_FRAC_BITS); diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h index df87770446dd..a24eef965f16 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h @@ -7,6 +7,8 @@ #ifndef __IA_CSS_EED1_8_PARAM_H #define __IA_CSS_EED1_8_PARAM_H +#include + #include "type_support.h" #include "vmem.h" /* needed for VMEM_ARRAY */ @@ -35,35 +37,35 @@ #define EED1_8_STATE_INPUT_BUFFER_HEIGHT (5 * NUM_PLANES) /* Each plane has width equal to half frame line */ -#define EED1_8_STATE_INPUT_BUFFER_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2) +#define EED1_8_STATE_INPUT_BUFFER_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2) /* 1 line state per color plane LD_H state */ #define EED1_8_STATE_LD_H_HEIGHT (1 * NUM_PLANES) -#define EED1_8_STATE_LD_H_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2) +#define EED1_8_STATE_LD_H_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2) /* 1 line state per color plane LD_V state */ #define EED1_8_STATE_LD_V_HEIGHT (1 * NUM_PLANES) -#define EED1_8_STATE_LD_V_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2) +#define EED1_8_STATE_LD_V_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2) /* 1 line (single plane) state for D_Hr state */ #define EED1_8_STATE_D_HR_HEIGHT 1 -#define EED1_8_STATE_D_HR_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2) +#define EED1_8_STATE_D_HR_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2) /* 1 line (single plane) state for D_Hb state */ #define EED1_8_STATE_D_HB_HEIGHT 1 -#define EED1_8_STATE_D_HB_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2) +#define EED1_8_STATE_D_HB_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2) /* 2 lines (single plane) state for D_Vr state */ #define EED1_8_STATE_D_VR_HEIGHT 2 -#define EED1_8_STATE_D_VR_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2) +#define EED1_8_STATE_D_VR_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2) /* 2 line (single plane) state for D_Vb state */ #define EED1_8_STATE_D_VB_HEIGHT 2 -#define EED1_8_STATE_D_VB_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2) +#define EED1_8_STATE_D_VB_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2) /* 2 lines state for R and B (= 2 planes) rb_zipped_state */ #define EED1_8_STATE_RB_ZIPPED_HEIGHT (2 * 2) -#define EED1_8_STATE_RB_ZIPPED_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2) +#define EED1_8_STATE_RB_ZIPPED_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2) #if EED1_8_FC_ENABLE_MEDIAN /* 1 full input line (GR-R color line) for Yc state */ @@ -72,11 +74,11 @@ /* 1 line state per color plane Cg_state */ #define EED1_8_STATE_CG_HEIGHT (1 * NUM_PLANES) -#define EED1_8_STATE_CG_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2) +#define EED1_8_STATE_CG_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2) /* 1 line state per color plane Co_state */ #define EED1_8_STATE_CO_HEIGHT (1 * NUM_PLANES) -#define EED1_8_STATE_CO_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2) +#define EED1_8_STATE_CO_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2) /* 1 full input line (GR-R color line) for AbsK state */ #define EED1_8_STATE_ABSK_HEIGHT 1 diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c index b34feba5340b..e701b7e41ef4 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c @@ -4,6 +4,8 @@ * Copyright (c) 2015, Intel Corporation. */ +#include + #include #include #include @@ -71,9 +73,9 @@ int ia_css_fpn_configure(const struct ia_css_binary *binary, &my_info }; - my_info.res.width = CEIL_DIV(info->res.width, 2); /* Packed by 2x */ + my_info.res.width = DIV_ROUND_UP(info->res.width, 2); /* Packed by 2x */ my_info.res.height = info->res.height; - my_info.padded_width = CEIL_DIV(info->padded_width, 2); /* Packed by 2x */ + my_info.padded_width = DIV_ROUND_UP(info->padded_width, 2); /* Packed by 2x */ my_info.format = info->format; my_info.raw_bit_depth = FPN_BITS_PER_PIXEL; my_info.raw_bayer_order = info->raw_bayer_order; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/sc/sc_1.0/ia_css_sc_param.h b/drivers/staging/media/atomisp/pci/isp/kernels/sc/sc_1.0/ia_css_sc_param.h index 61e9c04d2515..b49761ad39ca 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/sc/sc_1.0/ia_css_sc_param.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/sc/sc_1.0/ia_css_sc_param.h @@ -22,7 +22,7 @@ struct sh_css_isp_sc_params { * vec_slice is used for 2 adjacent vectors of shading gains. * The number of shift times by vec_slice is 8. * Max grid cell bqs to support the shading table centerting: N = 32 - * CEIL_DIV(N-1, ISP_SLICE_NELEMS) = CEIL_DIV(31, 4) = 8 + * DIV_ROUND_UP(N-1, ISP_SLICE_NELEMS) = DIV_ROUND_UP(31, 4) = 8 */ #define SH_CSS_SC_INTERPED_GAIN_HOR_SLICE_TIMES 8 diff --git a/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h b/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h index 55de6cb02833..6a0257359e69 100644 --- a/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h +++ b/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h @@ -8,6 +8,8 @@ Copyright (c) 2010 - 2015, Intel Corporation. #ifndef _INPUT_BUF_ISP_H_ #define _INPUT_BUF_ISP_H_ +#include + /* Temporary include, since IA_CSS_BINARY_MODE_COPY is still needed */ #include "sh_css_defs.h" @@ -21,7 +23,8 @@ Copyright (c) 2010 - 2015, Intel Corporation. /* In continuous mode, the input buffer must be a fixed size for all binaries * and at a fixed address since it will be used by the SP. */ #define EXTRA_INPUT_VECTORS 2 /* For left padding */ -#define MAX_VECTORS_PER_INPUT_LINE_CONT (CEIL_DIV(SH_CSS_MAX_SENSOR_WIDTH, ISP_NWAY) + EXTRA_INPUT_VECTORS) +#define MAX_VECTORS_PER_INPUT_LINE_CONT \ + (DIV_ROUND_UP(SH_CSS_MAX_SENSOR_WIDTH, ISP_NWAY) + EXTRA_INPUT_VECTORS) /* The input buffer should be on a fixed address in vmem, for continuous capture */ #define INPUT_BUF_ADDR 0x0 diff --git a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c index 84220359c957..b411ca2f415e 100644 --- a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c +++ b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c @@ -51,7 +51,6 @@ #include "sp.h" #include "isp.h" #include "type_support.h" -#include "math_support.h" /* CEIL_DIV */ #include "input_system.h" /* input_formatter_reg_load */ #include "ia_css_tagger_common.h" diff --git a/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c b/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c index 4f610f57e6c1..2cb96f9a6030 100644 --- a/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c +++ b/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c @@ -4,15 +4,16 @@ * Copyright (c) 2015, Intel Corporation. */ -#include "hmm.h" +#include +#include -#include "ia_css_frame.h" -#include #include "assert_support.h" +#include "atomisp_internal.h" +#include "hmm.h" #include "ia_css_debug.h" +#include "ia_css_frame.h" #include "isp.h" #include "sh_css_internal.h" -#include "atomisp_internal.h" #define NV12_TILEY_TILE_WIDTH 128 #define NV12_TILEY_TILE_HEIGHT 32 @@ -459,15 +460,16 @@ static void frame_init_single_plane(struct ia_css_frame *frame, unsigned int stride; stride = subpixels_per_line * bytes_per_pixel; - /* Frame height needs to be even number - needed by hw ISYS2401 - In case of odd number, round up to even. - Images won't be impacted by this round up, - only needed by jpeg/embedded data. - As long as buffer allocation and release are using data_bytes, - there won't be memory leak. */ - frame->data_bytes = stride * CEIL_MUL2(height, 2); + /* + * Frame height needs to be even number - needed by hw ISYS2401. + * In case of odd number, round up to even. + * Images won't be impacted by this round up, + * only needed by jpeg/embedded data. + * As long as buffer allocation and release are using data_bytes, + * there won't be memory leak. + */ + frame->data_bytes = stride * round_up(height, 2); frame_init_plane(plane, subpixels_per_line, stride, height, 0); - return; } static void frame_init_raw_single_plane( @@ -486,7 +488,6 @@ static void frame_init_raw_single_plane( HIVE_ISP_DDR_WORD_BITS / bits_per_pixel); frame->data_bytes = stride * height; frame_init_plane(plane, subpixels_per_line, stride, height, 0); - return; } static void frame_init_nv_planes(struct ia_css_frame *frame, @@ -690,7 +691,7 @@ ia_css_elems_bytes_from_info(const struct ia_css_frame_info *info) if (info->format == IA_CSS_FRAME_FORMAT_RAW || (info->format == IA_CSS_FRAME_FORMAT_RAW_PACKED)) { if (info->raw_bit_depth) - return CEIL_DIV(info->raw_bit_depth, 8); + return BITS_TO_BYTES(info->raw_bit_depth); else return 2; /* bytes per pixel */ } diff --git a/drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c b/drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c index d57ffb335fc0..50b0b31d734a 100644 --- a/drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c +++ b/drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c @@ -4,9 +4,9 @@ * Copyright (c) 2010 - 2015, Intel Corporation. */ -#include "system_global.h" -#include +#include +#include "system_global.h" #include "ia_css_ifmtr.h" #include @@ -149,10 +149,9 @@ int ia_css_ifmtr_configure(struct ia_css_stream_config *config, left_padding = 2 * ISP_VEC_NELEMS - config->left_padding; if (left_padding) { - num_vectors = CEIL_DIV(cropped_width + left_padding, - ISP_VEC_NELEMS); + num_vectors = DIV_ROUND_UP(cropped_width + left_padding, ISP_VEC_NELEMS); } else { - num_vectors = CEIL_DIV(cropped_width, ISP_VEC_NELEMS); + num_vectors = DIV_ROUND_UP(cropped_width, ISP_VEC_NELEMS); num_vectors *= buffer_height; /* todo: in case of left padding, num_vectors is vectors per line, @@ -305,7 +304,7 @@ int ia_css_ifmtr_configure(struct ia_css_stream_config *config, if ((!binary) || config->continuous) /* !binary -> sp raw copy pipe */ buffer_height *= 2; - vectors_per_line = CEIL_DIV(cropped_width, ISP_VEC_NELEMS); + vectors_per_line = DIV_ROUND_UP(cropped_width, ISP_VEC_NELEMS); vectors_per_line = CEIL_MUL(vectors_per_line, deinterleaving); break; case ATOMISP_INPUT_FORMAT_RAW_14: diff --git a/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c b/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c index b5395aea89fc..e6c11d5f77b4 100644 --- a/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c +++ b/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c @@ -808,7 +808,7 @@ static bool calculate_isys2401_dma_port_cfg( cfg->elements = HIVE_ISP_DDR_WORD_BITS / bits_per_pixel; cfg->cropping = 0; - cfg->width = CEIL_DIV(cfg->stride, HIVE_ISP_DDR_WORD_BYTES); + cfg->width = DIV_ROUND_UP(cfg->stride, HIVE_ISP_DDR_WORD_BYTES); return true; } diff --git a/drivers/staging/media/atomisp/pci/sh_css_defs.h b/drivers/staging/media/atomisp/pci/sh_css_defs.h index 51491dfe05cc..7bfeeb8cf053 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_defs.h +++ b/drivers/staging/media/atomisp/pci/sh_css_defs.h @@ -7,12 +7,12 @@ #ifndef _SH_CSS_DEFS_H_ #define _SH_CSS_DEFS_H_ +#include + #include "isp.h" /*#include "vamem.h"*/ /* Cannot include for VAMEM properties this file is visible on ISP -> pipeline generator */ -#include "math_support.h" /* max(), min, etc etc */ - /* ID's for refcount */ #define IA_CSS_REFCOUNT_PARAM_SET_POOL 0xCAFE0001 #define IA_CSS_REFCOUNT_PARAM_BUFFER 0xCAFE0002 @@ -182,7 +182,7 @@ RGB[0,8191],coef[-8192,8191] -> RGB[0,8191] The ISP firmware needs these rules to be applied at pre-processor time, that's why these are macros, not functions. */ #define _ISP_BQS(num) ((num) / 2) -#define _ISP_VECS(width) CEIL_DIV(width, ISP_VEC_NELEMS) +#define _ISP_VECS(width) DIV_ROUND_UP(width, ISP_VEC_NELEMS) #define ISP_BQ_GRID_WIDTH(elements_per_line, deci_factor_log2) \ CEIL_SHIFT(elements_per_line / 2, deci_factor_log2) @@ -194,9 +194,9 @@ RGB[0,8191],coef[-8192,8191] -> RGB[0,8191] /* The morphing table is similar to the shading table in the sense that we have 1 more value than we have cells in the grid. */ #define _ISP_MORPH_TABLE_WIDTH(int_width) \ - (CEIL_DIV(int_width, SH_CSS_MORPH_TABLE_GRID) + 1) + (DIV_ROUND_UP(int_width, SH_CSS_MORPH_TABLE_GRID) + 1) #define _ISP_MORPH_TABLE_HEIGHT(int_height) \ - (CEIL_DIV(int_height, SH_CSS_MORPH_TABLE_GRID) + 1) + (DIV_ROUND_UP(int_height, SH_CSS_MORPH_TABLE_GRID) + 1) #define _ISP_MORPH_TABLE_ALIGNED_WIDTH(width) \ CEIL_MUL(_ISP_MORPH_TABLE_WIDTH(width), \ SH_CSS_MORPH_TABLE_ELEMS_PER_DDR_WORD) @@ -298,7 +298,7 @@ RGB[0,8191],coef[-8192,8191] -> RGB[0,8191] c_subsampling, \ num_chunks, \ pipelining) \ - CEIL_MUL2(CEIL_MUL2(MAX(__ISP_PADDED_OUTPUT_WIDTH(out_width, \ + round_up(round_up(MAX(__ISP_PADDED_OUTPUT_WIDTH(out_width, \ dvs_env_width, \ left_crop), \ __ISP_MIN_INTERNAL_WIDTH(num_chunks, \ diff --git a/drivers/staging/media/atomisp/pci/sh_css_internal.h b/drivers/staging/media/atomisp/pci/sh_css_internal.h index 7b3483585748..9155a83fcc03 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_internal.h +++ b/drivers/staging/media/atomisp/pci/sh_css_internal.h @@ -8,6 +8,7 @@ #define _SH_CSS_INTERNAL_H_ #include +#include #include #include @@ -95,7 +96,6 @@ * the SIZE_OF_XXX macro of the corresponding struct. If they are not * equal, functionality will break. */ -#define CALC_ALIGNMENT_MEMBER(x, y) (CEIL_MUL(x, y) - x) #define SIZE_OF_HRT_VADDRESS sizeof(hive_uint32) /* Number of SP's */ @@ -704,13 +704,11 @@ struct sh_css_hmm_buffer { /* Do not use sizeof(uint64_t) since that does not exist of SP */ #define SIZE_OF_SH_CSS_HMM_BUFFER_STRUCT \ - (SIZE_OF_PAYLOAD_UNION + \ - CALC_ALIGNMENT_MEMBER(SIZE_OF_PAYLOAD_UNION, 8) + \ + (round_up(SIZE_OF_PAYLOAD_UNION, 8) + \ 8 + \ 8 + \ SIZE_OF_IA_CSS_TIME_MEAS_STRUCT + \ - SIZE_OF_IA_CSS_CLOCK_TICK_STRUCT + \ - CALC_ALIGNMENT_MEMBER(SIZE_OF_IA_CSS_CLOCK_TICK_STRUCT, 8)) + round_up(SIZE_OF_IA_CSS_CLOCK_TICK_STRUCT, 8)) static_assert(sizeof(struct sh_css_hmm_buffer) == SIZE_OF_SH_CSS_HMM_BUFFER_STRUCT); diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h index fd5cf6038829..b31a0d4e8acb 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h +++ b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h @@ -7,6 +7,8 @@ #ifndef _SH_CSS_PARAMS_DVS_H_ #define _SH_CSS_PARAMS_DVS_H_ +#include + #include #include #include "gdc_global.h" /* gdc_warp_param_mem_t */ @@ -20,16 +22,17 @@ /* ISP2400 */ /* horizontal 64x64 blocks round up to DVS_BLOCKDIM_X, make even */ -#define DVS_NUM_BLOCKS_X(X) (CEIL_MUL(CEIL_DIV((X), DVS_BLOCKDIM_X), 2)) +#define DVS_NUM_BLOCKS_X(X) round_up(DIV_ROUND_UP((X), DVS_BLOCKDIM_X), 2) +#define DVS_NUM_BLOCKS_X_CHROMA(X) DIV_ROUND_UP((X), DVS_BLOCKDIM_X) /* ISP2400 */ /* vertical 64x64 blocks round up to DVS_BLOCKDIM_Y */ -#define DVS_NUM_BLOCKS_Y(X) (CEIL_DIV((X), DVS_BLOCKDIM_Y_LUMA)) -#define DVS_NUM_BLOCKS_X_CHROMA(X) (CEIL_DIV((X), DVS_BLOCKDIM_X)) -#define DVS_NUM_BLOCKS_Y_CHROMA(X) (CEIL_DIV((X), DVS_BLOCKDIM_Y_CHROMA)) +#define DVS_NUM_BLOCKS_Y(X) DIV_ROUND_UP((X), DVS_BLOCKDIM_Y_LUMA) +#define DVS_NUM_BLOCKS_Y_CHROMA(X) DIV_ROUND_UP((X), DVS_BLOCKDIM_Y_CHROMA) -#define DVS_TABLE_IN_BLOCKDIM_X_LUMA(X) (DVS_NUM_BLOCKS_X(X) + 1) /* N blocks have N + 1 set of coords */ -#define DVS_TABLE_IN_BLOCKDIM_X_CHROMA(X) (DVS_NUM_BLOCKS_X_CHROMA(X) + 1) +/* N blocks have N + 1 set of coords */ +#define DVS_TABLE_IN_BLOCKDIM_X_LUMA(X) (DVS_NUM_BLOCKS_X(X) + 1) +#define DVS_TABLE_IN_BLOCKDIM_X_CHROMA(X) (DVS_NUM_BLOCKS_X_CHROMA(X) + 1) #define DVS_TABLE_IN_BLOCKDIM_Y_LUMA(X) (DVS_NUM_BLOCKS_Y(X) + 1) #define DVS_TABLE_IN_BLOCKDIM_Y_CHROMA(X) (DVS_NUM_BLOCKS_Y_CHROMA(X) + 1) @@ -40,8 +43,8 @@ #define XMEM_ALIGN_LOG2 (5) -#define DVS_6AXIS_COORDS_ELEMS CEIL_MUL(sizeof(gdc_warp_param_mem_t) \ - , HIVE_ISP_DDR_WORD_BYTES) +#define DVS_6AXIS_COORDS_ELEMS \ + round_up(sizeof(gdc_warp_param_mem_t), HIVE_ISP_DDR_WORD_BYTES) /* currently we only support two output with the same resolution, output 0 is th default one. */ #define DVS_6AXIS_BYTES(binary) \ diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c index 0d4a936ad80f..11d62313c908 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_params.c +++ b/drivers/staging/media/atomisp/pci/sh_css_params.c @@ -4,6 +4,8 @@ * Copyright (c) 2015, Intel Corporation. */ +#include + #include "gdc_device.h" /* gdc_lut_store(), ... */ #include "isp.h" /* ISP_VEC_ELEMBITS */ #include "vamem.h" @@ -21,8 +23,6 @@ #include "platform_support.h" #include "assert_support.h" -#include "misc_support.h" /* NOT_USED */ -#include "math_support.h" /* max(), min() EVEN_FLOOR()*/ #include "ia_css_stream.h" #include "sh_css_params_internal.h" @@ -4042,10 +4042,10 @@ sh_css_update_uds_and_crop_info( } /* Must enforce that the crop position is even */ - crop_x = EVEN_FLOOR(crop_x); - crop_y = EVEN_FLOOR(crop_y); - uds_xc = EVEN_FLOOR(uds_xc); - uds_yc = EVEN_FLOOR(uds_yc); + crop_x = round_down(crop_x, 2); + crop_y = round_down(crop_y, 2); + uds_xc = round_down(uds_xc, 2); + uds_yc = round_down(uds_yc, 2); uds->xc = (uint16_t)uds_xc; uds->yc = (uint16_t)uds_yc; -- cgit v1.2.3 From 379f5e1587310b7a0b12b2cd515b66271a1dfc5c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 19 May 2025 18:46:49 +0300 Subject: media: atomisp: Remove no more used macros from math_support.h After recent cleanups the few macros become unused. Remove them. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250519155028.526453-4-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Tested-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/atomisp/pci/hive_isp_css_include/math_support.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h index 6d45d0d8d060..2cb5c986790a 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h @@ -10,14 +10,9 @@ /* Override the definition of max/min from Linux kernel */ #include -/* force a value to a lower even value */ -#define EVEN_FLOOR(x) ((x) & ~1) - #define CEIL_DIV(a, b) (((b) != 0) ? ((a) + (b) - 1) / (b) : 0) #define CEIL_MUL(a, b) (CEIL_DIV(a, b) * (b)) -#define CEIL_MUL2(a, b) (((a) + (b) - 1) & ~((b) - 1)) #define CEIL_SHIFT(a, b) (((a) + (1 << (b)) - 1) >> (b)) -#define CEIL_SHIFT_MUL(a, b) (CEIL_SHIFT(a, b) << (b)) /* * For SP and ISP, SDK provides the definition of OP_std_modadd. -- cgit v1.2.3 From 5e2330ef08f4d7c7692ee55d272c0bb489258915 Mon Sep 17 00:00:00 2001 From: Abdelrahman Fekry Date: Fri, 27 Jun 2025 13:06:04 +0300 Subject: media: atomisp: Remove debug sysfs attributes active_bo and free_bo The sysfs attributes active_bo and free_bo expose internal buffer state used only for debugging purposes. These are not part of any standard kernel ABI, and need to be removed before this driver may be moved out of drivers/staging. - Remove active_bo and free_bo attributes - Remove group registration calls form hmm_init() and hmm_cleanup() Suggested-by: Hans de Goede Signed-off-by: Abdelrahman Fekry Reviewed-by: Andy Shevchenko Reviewed-by: Dan Carpenter Link: https://lore.kernel.org/r/20250627100604.29061-1-abdelrahmanfekry375@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/hmm/hmm.c | 91 ----------------------------- 1 file changed, 91 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm.c b/drivers/staging/media/atomisp/pci/hmm/hmm.c index 84102c3aaf97..f998b57f90c4 100644 --- a/drivers/staging/media/atomisp/pci/hmm/hmm.c +++ b/drivers/staging/media/atomisp/pci/hmm/hmm.c @@ -28,88 +28,6 @@ struct hmm_bo_device bo_device; static ia_css_ptr dummy_ptr = mmgr_EXCEPTION; static bool hmm_initialized; -/* - * p: private - * v: vmalloc - */ -static const char hmm_bo_type_string[] = "pv"; - -static ssize_t bo_show(struct device *dev, struct device_attribute *attr, - char *buf, struct list_head *bo_list, bool active) -{ - ssize_t ret = 0; - struct hmm_buffer_object *bo; - unsigned long flags; - int i; - long total[HMM_BO_LAST] = { 0 }; - long count[HMM_BO_LAST] = { 0 }; - int index1 = 0; - int index2 = 0; - - ret = scnprintf(buf, PAGE_SIZE, "type pgnr\n"); - if (ret <= 0) - return 0; - - index1 += ret; - - spin_lock_irqsave(&bo_device.list_lock, flags); - list_for_each_entry(bo, bo_list, list) { - if ((active && (bo->status & HMM_BO_ALLOCED)) || - (!active && !(bo->status & HMM_BO_ALLOCED))) { - ret = scnprintf(buf + index1, PAGE_SIZE - index1, - "%c %d\n", - hmm_bo_type_string[bo->type], bo->pgnr); - - total[bo->type] += bo->pgnr; - count[bo->type]++; - if (ret > 0) - index1 += ret; - } - } - spin_unlock_irqrestore(&bo_device.list_lock, flags); - - for (i = 0; i < HMM_BO_LAST; i++) { - if (count[i]) { - ret = scnprintf(buf + index1 + index2, - PAGE_SIZE - index1 - index2, - "%ld %c buffer objects: %ld KB\n", - count[i], hmm_bo_type_string[i], - total[i] * 4); - if (ret > 0) - index2 += ret; - } - } - - /* Add trailing zero, not included by scnprintf */ - return index1 + index2 + 1; -} - -static ssize_t active_bo_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return bo_show(dev, attr, buf, &bo_device.entire_bo_list, true); -} - -static ssize_t free_bo_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return bo_show(dev, attr, buf, &bo_device.entire_bo_list, false); -} - - -static DEVICE_ATTR_RO(active_bo); -static DEVICE_ATTR_RO(free_bo); - -static struct attribute *sysfs_attrs_ctrl[] = { - &dev_attr_active_bo.attr, - &dev_attr_free_bo.attr, - NULL -}; - -static struct attribute_group atomisp_attribute_group[] = { - {.attrs = sysfs_attrs_ctrl }, -}; - int hmm_init(void) { int ret; @@ -130,14 +48,6 @@ int hmm_init(void) */ dummy_ptr = hmm_alloc(1); - if (!ret) { - ret = sysfs_create_group(&atomisp_dev->kobj, - atomisp_attribute_group); - if (ret) - dev_err(atomisp_dev, - "%s Failed to create sysfs\n", __func__); - } - return ret; } @@ -145,7 +55,6 @@ void hmm_cleanup(void) { if (dummy_ptr == mmgr_EXCEPTION) return; - sysfs_remove_group(&atomisp_dev->kobj, atomisp_attribute_group); /* free dummy memory first */ hmm_free(dummy_ptr); -- cgit v1.2.3 From bd290dddb0d74b3cd34d2cca15d66105dc4a00d4 Mon Sep 17 00:00:00 2001 From: Abdelrahman Fekry Date: Sat, 28 Jun 2025 08:25:36 +0300 Subject: media: atomisp: Fix premature setting of HMM_BO_DEVICE_INITED flag The HMM_BO_DEVICE_INITED flag was being set in hmm_bo_device_init() before key initialization steps like kmem_cache_create(), kmem_cache_alloc(), and __bo_init(). This means that if any of these steps fail, the flag remains set, misleading other parts of the driver (e.g. hmm_bo_alloc()) into thinking the device is initialized. This could lead to undefined behavior or invalid memory use. Additionally, since __bo_init() is called from inside hmm_bo_device_init() after the flag was already set, its internal check for HMM_BO_DEVICE_INITED is redundant. - Move the flag assignment to the end after all allocations succeed. - Remove redundant check of the flag inside __bo_init() See the link [1] below for a backtrace which happens when deliberately triggering the problem of the flag getting set too early. Link: https://lore.kernel.org/linux-media/CAGn2d8ONZpOHXex8kjeUDgRPiMqKp8vZ=xhGbEDGphV1t7ZEFw@mail.gmail.com/ [1] Signed-off-by: Abdelrahman Fekry Link: https://lore.kernel.org/r/20250628052536.43737-1-abdelrahmanfekry375@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/hmm/hmm_bo.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c index 224ca8d42721..5d0cd5260d3a 100644 --- a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c +++ b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c @@ -37,8 +37,6 @@ static int __bo_init(struct hmm_bo_device *bdev, struct hmm_buffer_object *bo, unsigned int pgnr) { check_bodev_null_return(bdev, -EINVAL); - var_equal_return(hmm_bo_device_inited(bdev), 0, -EINVAL, - "hmm_bo_device not inited yet.\n"); /* prevent zero size buffer object */ if (pgnr == 0) { dev_err(atomisp_dev, "0 size buffer is not allowed.\n"); @@ -341,7 +339,6 @@ int hmm_bo_device_init(struct hmm_bo_device *bdev, spin_lock_init(&bdev->list_lock); mutex_init(&bdev->rbtree_mutex); - bdev->flag = HMM_BO_DEVICE_INITED; INIT_LIST_HEAD(&bdev->entire_bo_list); bdev->allocated_rbtree = RB_ROOT; @@ -376,6 +373,8 @@ int hmm_bo_device_init(struct hmm_bo_device *bdev, __bo_insert_to_free_rbtree(&bdev->free_rbtree, bo); + bdev->flag = HMM_BO_DEVICE_INITED; + return 0; } -- cgit v1.2.3 From 54d32a9754a096b2d449588f89c38d5dec52eb49 Mon Sep 17 00:00:00 2001 From: Abdelrahman Fekry Date: Fri, 4 Jul 2025 19:10:51 +0300 Subject: media: atomisp: Remove custom sysfs attributes from atomisp_drvfs.c Continue the cleanup of the AtomISP driver, as discussed with Hans and Andy in [1]. Tackle TODO item: "Remove custom sysfs files created by atomisp_drvfs.c": - Remove the sysfs attributes `dbglvl`, `dbgfun`, and `dbgopt`. - Delete their associated show/store handler functions. - Remove the corresponding attribute group definitions. Link: https://lore.kernel.org/all/836dc6b6-2821-47fc-8f24-0838f979af76@kernel.org/ [1] Suggested-by: Hans de Goede Reviewed-by: Andy Shevchenko Signed-off-by: Abdelrahman Fekry Link: https://lore.kernel.org/r/20250704161051.16733-1-abdelrahmanfekry375@gmail.com [hansg@kernel.org: Completely remove the now empty atomisp_drvfs files] Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/Makefile | 1 - drivers/staging/media/atomisp/TODO | 2 - drivers/staging/media/atomisp/pci/atomisp_drvfs.c | 155 ---------------------- drivers/staging/media/atomisp/pci/atomisp_drvfs.h | 15 --- drivers/staging/media/atomisp/pci/atomisp_v4l2.c | 4 - 5 files changed, 177 deletions(-) delete mode 100644 drivers/staging/media/atomisp/pci/atomisp_drvfs.c delete mode 100644 drivers/staging/media/atomisp/pci/atomisp_drvfs.h (limited to 'drivers/staging') diff --git a/drivers/staging/media/atomisp/Makefile b/drivers/staging/media/atomisp/Makefile index 43116c74781d..1d0fbe22036b 100644 --- a/drivers/staging/media/atomisp/Makefile +++ b/drivers/staging/media/atomisp/Makefile @@ -17,7 +17,6 @@ atomisp-objs += \ pci/atomisp_compat_css20.o \ pci/atomisp_csi2.o \ pci/atomisp_csi2_bridge.o \ - pci/atomisp_drvfs.o \ pci/atomisp_fops.o \ pci/atomisp_ioctl.o \ pci/atomisp_subdev.o \ diff --git a/drivers/staging/media/atomisp/TODO b/drivers/staging/media/atomisp/TODO index 27cbbde93b1e..82be275b4a0a 100644 --- a/drivers/staging/media/atomisp/TODO +++ b/drivers/staging/media/atomisp/TODO @@ -7,8 +7,6 @@ TODO * Remove/disable custom v4l2-ctrls -* Remove custom sysfs files created by atomisp_drvfs.c - * Remove unnecessary/unwanted module parameters * Remove abuse of priv field in various v4l2 userspace API structs diff --git a/drivers/staging/media/atomisp/pci/atomisp_drvfs.c b/drivers/staging/media/atomisp/pci/atomisp_drvfs.c deleted file mode 100644 index 31c82c3c0d33..000000000000 --- a/drivers/staging/media/atomisp/pci/atomisp_drvfs.c +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Support for atomisp driver sysfs interface - * - * Copyright (c) 2014 Intel Corporation. All Rights Reserved. - */ - -#include -#include -#include - -#include "atomisp_compat.h" -#include "atomisp_internal.h" -#include "atomisp_ioctl.h" -#include "atomisp_drvfs.h" -#include "hmm/hmm.h" -#include "ia_css_debug.h" - -#define OPTION_BIN_LIST BIT(0) -#define OPTION_BIN_RUN BIT(1) -#define OPTION_VALID (OPTION_BIN_LIST | OPTION_BIN_RUN) - -/* - * dbgopt: iunit debug option: - * bit 0: binary list - * bit 1: running binary - * bit 2: memory statistic - */ -static unsigned int dbgopt = OPTION_BIN_LIST; - -static inline int iunit_dump_dbgopt(struct atomisp_device *isp, - unsigned int opt) -{ - int ret = 0; - - if (opt & OPTION_VALID) { - if (opt & OPTION_BIN_LIST) { - ret = atomisp_css_dump_blob_infor(isp); - if (ret) { - dev_err(isp->dev, "%s dump blob infor err[ret:%d]\n", - __func__, ret); - goto opt_err; - } - } - - if (opt & OPTION_BIN_RUN) { - if (isp->asd.streaming) { - atomisp_css_dump_sp_raw_copy_linecount(true); - atomisp_css_debug_dump_isp_binary(); - } else { - ret = -EPERM; - dev_err(isp->dev, "%s dump running bin err[ret:%d]\n", - __func__, ret); - goto opt_err; - } - } - } else { - ret = -EINVAL; - dev_err(isp->dev, "%s dump nothing[ret=%d]\n", __func__, ret); - } - -opt_err: - return ret; -} - -static ssize_t dbglvl_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - unsigned int dbglvl = ia_css_debug_get_dtrace_level(); - - return sysfs_emit(buf, "dtrace level:%u\n", dbglvl); -} - -static ssize_t dbglvl_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned int dbglvl; - int ret; - - ret = kstrtouint(buf, 10, &dbglvl); - if (ret) - return ret; - - if (dbglvl < 1 || dbglvl > 9) - return -ERANGE; - - ia_css_debug_set_dtrace_level(dbglvl); - return size; -} -static DEVICE_ATTR_RW(dbglvl); - -static ssize_t dbgfun_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - unsigned int dbgfun = atomisp_get_css_dbgfunc(); - - return sysfs_emit(buf, "dbgfun opt:%u\n", dbgfun); -} - -static ssize_t dbgfun_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t size) -{ - struct atomisp_device *isp = dev_get_drvdata(dev); - unsigned int opt; - int ret; - - ret = kstrtouint(buf, 10, &opt); - if (ret) - return ret; - - return atomisp_set_css_dbgfunc(isp, opt); -} -static DEVICE_ATTR_RW(dbgfun); - -static ssize_t dbgopt_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return sysfs_emit(buf, "option:0x%x\n", dbgopt); -} - -static ssize_t dbgopt_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t size) -{ - struct atomisp_device *isp = dev_get_drvdata(dev); - unsigned int opt; - int ret; - - ret = kstrtouint(buf, 10, &opt); - if (ret) - return ret; - - dbgopt = opt; - ret = iunit_dump_dbgopt(isp, dbgopt); - if (ret) - return ret; - - return size; -} -static DEVICE_ATTR_RW(dbgopt); - -static struct attribute *dbg_attrs[] = { - &dev_attr_dbglvl.attr, - &dev_attr_dbgfun.attr, - &dev_attr_dbgopt.attr, - NULL -}; - -static const struct attribute_group dbg_attr_group = { - .attrs = dbg_attrs, -}; - -const struct attribute_group *dbg_attr_groups[] = { - &dbg_attr_group, - NULL -}; diff --git a/drivers/staging/media/atomisp/pci/atomisp_drvfs.h b/drivers/staging/media/atomisp/pci/atomisp_drvfs.h deleted file mode 100644 index 46ad59b8df28..000000000000 --- a/drivers/staging/media/atomisp/pci/atomisp_drvfs.h +++ /dev/null @@ -1,15 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Support for atomisp driver sysfs interface. - * - * Copyright (c) 2014 Intel Corporation. All Rights Reserved. - */ - -#ifndef __ATOMISP_DRVFS_H__ -#define __ATOMISP_DRVFS_H__ - -#include - -extern const struct attribute_group *dbg_attr_groups[]; - -#endif /* __ATOMISP_DRVFS_H__ */ diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index 3fdaa4b7bbaf..900a67552d6a 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -29,7 +29,6 @@ #include "atomisp_internal.h" #include "atomisp-regs.h" #include "atomisp_dfs_tables.h" -#include "atomisp_drvfs.h" #include "hmm/hmm.h" #include "atomisp_trace_event.h" @@ -1497,9 +1496,6 @@ static const struct pci_device_id atomisp_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, atomisp_pci_tbl); static struct pci_driver atomisp_pci_driver = { - .driver = { - .dev_groups = dbg_attr_groups, - }, .name = "atomisp-isp2", .id_table = atomisp_pci_tbl, .probe = atomisp_pci_probe, -- cgit v1.2.3 From 6af7e00a92e04e0ea41bbe4b83deac9bb11fe4c5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 9 Jul 2025 16:56:56 +0200 Subject: media: staging/ipu7: avoid division by 64-bit value On 32-bit targets, this causes a link failure: x86_64-linux-ld: drivers/staging/media/ipu7/ipu7-isys-csi-phy.o: in function `ipu7_isys_phy_config': ipu7-isys-csi-phy.c:(.text+0x1509): undefined reference to `__udivdi3' Note that this does not divide a 64-bit number by a 32-bit one as usual, but the other way round, which is something that the compiler should really be able to figure out but does not (as of gcc-15). A few lines higher, a similar division is done using the incorrect div_u64() that truncates the 64-bit divisor to 32 bits. Change both to use the safe but slow div64_u64() helper. Fixes: a516d36bdc3d ("media: staging/ipu7: add IPU7 input system device driver") Signed-off-by: Arnd Bergmann Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/ipu7/ipu7-isys-csi-phy.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c index 4407750c7f40..b8c5db7ae300 100644 --- a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c +++ b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c @@ -734,6 +734,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, u16 reg; u16 val; u32 i; + u64 r64; u32 r; if (is_ipu7p5(isys->adev->isp->hw_ver)) @@ -806,8 +807,8 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, dwc_phy_write_mask(isys, id, reg, 2, 0, 2); } - deass_thresh = (u16)div_u64_rem(7 * 1000 * 6, mbps * 5U, &r) + 1; - if (r != 0) + deass_thresh = (u16)div64_u64_rem(7 * 1000 * 6, mbps * 5U, &r64) + 1; + if (r64 != 0) deass_thresh++; reg = CORE_DIG_RW_TRIO0_2; @@ -815,8 +816,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, dwc_phy_write_mask(isys, id, reg + 0x400 * i, deass_thresh, 0, 7); - delay_thresh = - ((224U - (9U * 7U)) * 1000U) / (5U * mbps) - 7U; + delay_thresh = div64_u64((224U - (9U * 7U)) * 1000U, 5U * mbps) - 7u; if (delay_thresh < 1) delay_thresh = 1; -- cgit v1.2.3 From eb6ab8a997ad7425d118fb73d327dac7456b3621 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 9 Jul 2025 16:57:31 +0200 Subject: media: staging/ipu7: add CONFIG_PCI dependency This driver fails to build when PCI is disabled: drivers/staging/media/ipu7/ipu7.c: In function 'ipu7_pci_config_setup': drivers/staging/media/ipu7/ipu7.c:2260:15: error: implicit declaration of function 'pci_enable_msi'; did you mean 'pci_enable_sriov'? [-Wimplicit-function-declaration] drivers/staging/media/ipu7/ipu7.c:2775:1: error: data definition has no type or storage class [-Werror] driver(ipu7_pci_driver); drivers/staging/media/ipu7/ipu7.c:2764:26: error: 'ipu7_pci_driver' defined but not used [-Werror=unused-variable] Add the required Kconfig dependency. Signed-off-by: Arnd Bergmann Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/ipu7/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/staging') diff --git a/drivers/staging/media/ipu7/Kconfig b/drivers/staging/media/ipu7/Kconfig index 5b800e9b09c4..7d831ba7501d 100644 --- a/drivers/staging/media/ipu7/Kconfig +++ b/drivers/staging/media/ipu7/Kconfig @@ -4,6 +4,7 @@ config VIDEO_INTEL_IPU7 depends on VIDEO_DEV depends on X86 && HAS_DMA depends on IPU_BRIDGE || !IPU_BRIDGE + depends on PCI select AUXILIARY_BUS select IOMMU_IOVA select VIDEO_V4L2_SUBDEV_API -- cgit v1.2.3 From e165f6eda53519d31ecb58603a53fcb03fd75eb5 Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Mon, 23 Jun 2025 12:07:21 -0400 Subject: media: rkvdec: Remove TODO file 2 items are present in the TODO file: - HEVC support - Evaluate adding helper for rkvdec_request_validate Missing HEVC support is not a reason for a driver to be in staging, support for different features of the hardware can be added in drivers in the main tree. The rkvdec_request_validate function was simplified in commit 54676d5f5630 ("media: rkvdec: Do not require all controls to be present in every request") by not setting controls that have not changed. As it now basically just calls vb2_request_validate(), there is no need for a helper. Signed-off-by: Detlev Casanova Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/rkvdec/TODO | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 drivers/staging/media/rkvdec/TODO (limited to 'drivers/staging') diff --git a/drivers/staging/media/rkvdec/TODO b/drivers/staging/media/rkvdec/TODO deleted file mode 100644 index 2c0779383276..000000000000 --- a/drivers/staging/media/rkvdec/TODO +++ /dev/null @@ -1,11 +0,0 @@ -* Support for HEVC is planned for this driver. - - Given the V4L controls for that CODEC will be part of - the uABI, it will be required to have the driver in staging. - - For this reason, we are keeping this driver in staging for now. - -* Evaluate introducing a helper to consolidate duplicated - code in rkvdec_request_validate and cedrus_request_validate. - The helper needs to the driver private data associated with - the videobuf2 queue, from a media request. -- cgit v1.2.3 From d968e50b5c26642754492dea23cbd3592bde62d8 Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Mon, 23 Jun 2025 12:07:22 -0400 Subject: media: rkvdec: Unstage the driver The TODO list for unstaging being empty, the driver can now be moved to the main media folder. Also add myself as maintainer. Signed-off-by: Detlev Casanova Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/Kconfig | 2 - drivers/staging/media/Makefile | 1 - drivers/staging/media/rkvdec/Kconfig | 16 - drivers/staging/media/rkvdec/Makefile | 3 - drivers/staging/media/rkvdec/rkvdec-h264.c | 1212 --------------------------- drivers/staging/media/rkvdec/rkvdec-regs.h | 223 ----- drivers/staging/media/rkvdec/rkvdec-vp9.c | 1072 ------------------------ drivers/staging/media/rkvdec/rkvdec.c | 1251 ---------------------------- drivers/staging/media/rkvdec/rkvdec.h | 144 ---- 9 files changed, 3924 deletions(-) delete mode 100644 drivers/staging/media/rkvdec/Kconfig delete mode 100644 drivers/staging/media/rkvdec/Makefile delete mode 100644 drivers/staging/media/rkvdec/rkvdec-h264.c delete mode 100644 drivers/staging/media/rkvdec/rkvdec-regs.h delete mode 100644 drivers/staging/media/rkvdec/rkvdec-vp9.c delete mode 100644 drivers/staging/media/rkvdec/rkvdec.c delete mode 100644 drivers/staging/media/rkvdec/rkvdec.h (limited to 'drivers/staging') diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 043ffbc472fb..ab250c89cd4d 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/rkvdec/Kconfig" - source "drivers/staging/media/starfive/Kconfig" source "drivers/staging/media/sunxi/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 177dfe95289b..4a073938b2b2 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_ROCKCHIP_VDEC) += rkvdec/ obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive/ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ diff --git a/drivers/staging/media/rkvdec/Kconfig b/drivers/staging/media/rkvdec/Kconfig deleted file mode 100644 index 5f3bdd848a2c..000000000000 --- a/drivers/staging/media/rkvdec/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -config VIDEO_ROCKCHIP_VDEC - tristate "Rockchip Video Decoder driver" - depends on ARCH_ROCKCHIP || COMPILE_TEST - depends on VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEOBUF2_DMA_CONTIG - select VIDEOBUF2_VMALLOC - select V4L2_MEM2MEM_DEV - select V4L2_H264 - select V4L2_VP9 - help - Support for the Rockchip Video Decoder IP present on Rockchip SoCs, - which accelerates video decoding. - To compile this driver as a module, choose M here: the module - will be called rockchip-vdec. diff --git a/drivers/staging/media/rkvdec/Makefile b/drivers/staging/media/rkvdec/Makefile deleted file mode 100644 index cb86b429cfaa..000000000000 --- a/drivers/staging/media/rkvdec/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rockchip-vdec.o - -rockchip-vdec-y += rkvdec.o rkvdec-h264.o rkvdec-vp9.o diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c deleted file mode 100644 index d14b4d173448..000000000000 --- a/drivers/staging/media/rkvdec/rkvdec-h264.c +++ /dev/null @@ -1,1212 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Rockchip Video Decoder H264 backend - * - * Copyright (C) 2019 Collabora, Ltd. - * Boris Brezillon - * - * Copyright (C) 2016 Rockchip Electronics Co., Ltd. - * Jeffy Chen - */ - -#include -#include - -#include "rkvdec.h" -#include "rkvdec-regs.h" - -/* Size with u32 units. */ -#define RKV_CABAC_INIT_BUFFER_SIZE (3680 + 128) -#define RKV_RPS_SIZE ((128 + 128) / 4) -#define RKV_ERROR_INFO_SIZE (256 * 144 * 4) - -#define RKVDEC_NUM_REFLIST 3 - -struct rkvdec_h264_scaling_list { - u8 scaling_list_4x4[6][16]; - u8 scaling_list_8x8[6][64]; - u8 padding[128]; -}; - -struct rkvdec_sps_pps_packet { - u32 info[8]; -}; - -struct rkvdec_ps_field { - u16 offset; - u8 len; -}; - -#define PS_FIELD(_offset, _len) \ - ((struct rkvdec_ps_field){ _offset, _len }) - -#define SEQ_PARAMETER_SET_ID PS_FIELD(0, 4) -#define PROFILE_IDC PS_FIELD(4, 8) -#define CONSTRAINT_SET3_FLAG PS_FIELD(12, 1) -#define CHROMA_FORMAT_IDC PS_FIELD(13, 2) -#define BIT_DEPTH_LUMA PS_FIELD(15, 3) -#define BIT_DEPTH_CHROMA PS_FIELD(18, 3) -#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG PS_FIELD(21, 1) -#define LOG2_MAX_FRAME_NUM_MINUS4 PS_FIELD(22, 4) -#define MAX_NUM_REF_FRAMES PS_FIELD(26, 5) -#define PIC_ORDER_CNT_TYPE PS_FIELD(31, 2) -#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 PS_FIELD(33, 4) -#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG PS_FIELD(37, 1) -#define PIC_WIDTH_IN_MBS PS_FIELD(38, 9) -#define PIC_HEIGHT_IN_MBS PS_FIELD(47, 9) -#define FRAME_MBS_ONLY_FLAG PS_FIELD(56, 1) -#define MB_ADAPTIVE_FRAME_FIELD_FLAG PS_FIELD(57, 1) -#define DIRECT_8X8_INFERENCE_FLAG PS_FIELD(58, 1) -#define MVC_EXTENSION_ENABLE PS_FIELD(59, 1) -#define NUM_VIEWS PS_FIELD(60, 2) -#define VIEW_ID(i) PS_FIELD(62 + ((i) * 10), 10) -#define NUM_ANCHOR_REFS_L(i) PS_FIELD(82 + ((i) * 11), 1) -#define ANCHOR_REF_L(i) PS_FIELD(83 + ((i) * 11), 10) -#define NUM_NON_ANCHOR_REFS_L(i) PS_FIELD(104 + ((i) * 11), 1) -#define NON_ANCHOR_REFS_L(i) PS_FIELD(105 + ((i) * 11), 10) -#define PIC_PARAMETER_SET_ID PS_FIELD(128, 8) -#define PPS_SEQ_PARAMETER_SET_ID PS_FIELD(136, 5) -#define ENTROPY_CODING_MODE_FLAG PS_FIELD(141, 1) -#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG PS_FIELD(142, 1) -#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) PS_FIELD(143 + ((i) * 5), 5) -#define WEIGHTED_PRED_FLAG PS_FIELD(153, 1) -#define WEIGHTED_BIPRED_IDC PS_FIELD(154, 2) -#define PIC_INIT_QP_MINUS26 PS_FIELD(156, 7) -#define PIC_INIT_QS_MINUS26 PS_FIELD(163, 6) -#define CHROMA_QP_INDEX_OFFSET PS_FIELD(169, 5) -#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG PS_FIELD(174, 1) -#define CONSTRAINED_INTRA_PRED_FLAG PS_FIELD(175, 1) -#define REDUNDANT_PIC_CNT_PRESENT PS_FIELD(176, 1) -#define TRANSFORM_8X8_MODE_FLAG PS_FIELD(177, 1) -#define SECOND_CHROMA_QP_INDEX_OFFSET PS_FIELD(178, 5) -#define SCALING_LIST_ENABLE_FLAG PS_FIELD(183, 1) -#define SCALING_LIST_ADDRESS PS_FIELD(184, 32) -#define IS_LONG_TERM(i) PS_FIELD(216 + (i), 1) - -#define DPB_OFFS(i, j) (288 + ((j) * 32 * 7) + ((i) * 7)) -#define DPB_INFO(i, j) PS_FIELD(DPB_OFFS(i, j), 5) -#define BOTTOM_FLAG(i, j) PS_FIELD(DPB_OFFS(i, j) + 5, 1) -#define VIEW_INDEX_OFF(i, j) PS_FIELD(DPB_OFFS(i, j) + 6, 1) - -/* Data structure describing auxiliary buffer format. */ -struct rkvdec_h264_priv_tbl { - s8 cabac_table[4][464][2]; - struct rkvdec_h264_scaling_list scaling_list; - u32 rps[RKV_RPS_SIZE]; - struct rkvdec_sps_pps_packet param_set[256]; - u8 err_info[RKV_ERROR_INFO_SIZE]; -}; - -struct rkvdec_h264_reflists { - struct v4l2_h264_reference p[V4L2_H264_REF_LIST_LEN]; - struct v4l2_h264_reference b0[V4L2_H264_REF_LIST_LEN]; - struct v4l2_h264_reference b1[V4L2_H264_REF_LIST_LEN]; -}; - -struct rkvdec_h264_run { - struct rkvdec_run base; - const struct v4l2_ctrl_h264_decode_params *decode_params; - const struct v4l2_ctrl_h264_sps *sps; - const struct v4l2_ctrl_h264_pps *pps; - const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix; - struct vb2_buffer *ref_buf[V4L2_H264_NUM_DPB_ENTRIES]; -}; - -struct rkvdec_h264_ctx { - struct rkvdec_aux_buf priv_tbl; - struct rkvdec_h264_reflists reflists; -}; - -#define CABAC_ENTRY(ctxidx, idc0_m, idc0_n, idc1_m, idc1_n, \ - idc2_m, idc2_n, intra_m, intra_n) \ - [0][(ctxidx)] = {idc0_m, idc0_n}, \ - [1][(ctxidx)] = {idc1_m, idc1_n}, \ - [2][(ctxidx)] = {idc2_m, idc2_n}, \ - [3][(ctxidx)] = {intra_m, intra_n} - -/* - * Constant CABAC table. - * Built from the tables described in section '9.3.1.1 Initialisation process - * for context variables' of the H264 spec. - */ -static const s8 rkvdec_h264_cabac_table[4][464][2] = { - /* Table 9-12 – Values of variables m and n for ctxIdx from 0 to 10 */ - CABAC_ENTRY(0, 20, -15, 20, -15, 20, -15, 20, -15), - CABAC_ENTRY(1, 2, 54, 2, 54, 2, 54, 2, 54), - CABAC_ENTRY(2, 3, 74, 3, 74, 3, 74, 3, 74), - CABAC_ENTRY(3, 20, -15, 20, -15, 20, -15, 20, -15), - CABAC_ENTRY(4, 2, 54, 2, 54, 2, 54, 2, 54), - CABAC_ENTRY(5, 3, 74, 3, 74, 3, 74, 3, 74), - CABAC_ENTRY(6, -28, 127, -28, 127, -28, 127, -28, 127), - CABAC_ENTRY(7, -23, 104, -23, 104, -23, 104, -23, 104), - CABAC_ENTRY(8, -6, 53, -6, 53, -6, 53, -6, 53), - CABAC_ENTRY(9, -1, 54, -1, 54, -1, 54, -1, 54), - CABAC_ENTRY(10, 7, 51, 7, 51, 7, 51, 7, 51), - - /* Table 9-13 – Values of variables m and n for ctxIdx from 11 to 23 */ - CABAC_ENTRY(11, 23, 33, 22, 25, 29, 16, 0, 0), - CABAC_ENTRY(12, 23, 2, 34, 0, 25, 0, 0, 0), - CABAC_ENTRY(13, 21, 0, 16, 0, 14, 0, 0, 0), - CABAC_ENTRY(14, 1, 9, -2, 9, -10, 51, 0, 0), - CABAC_ENTRY(15, 0, 49, 4, 41, -3, 62, 0, 0), - CABAC_ENTRY(16, -37, 118, -29, 118, -27, 99, 0, 0), - CABAC_ENTRY(17, 5, 57, 2, 65, 26, 16, 0, 0), - CABAC_ENTRY(18, -13, 78, -6, 71, -4, 85, 0, 0), - CABAC_ENTRY(19, -11, 65, -13, 79, -24, 102, 0, 0), - CABAC_ENTRY(20, 1, 62, 5, 52, 5, 57, 0, 0), - CABAC_ENTRY(21, 12, 49, 9, 50, 6, 57, 0, 0), - CABAC_ENTRY(22, -4, 73, -3, 70, -17, 73, 0, 0), - CABAC_ENTRY(23, 17, 50, 10, 54, 14, 57, 0, 0), - - /* Table 9-14 – Values of variables m and n for ctxIdx from 24 to 39 */ - CABAC_ENTRY(24, 18, 64, 26, 34, 20, 40, 0, 0), - CABAC_ENTRY(25, 9, 43, 19, 22, 20, 10, 0, 0), - CABAC_ENTRY(26, 29, 0, 40, 0, 29, 0, 0, 0), - CABAC_ENTRY(27, 26, 67, 57, 2, 54, 0, 0, 0), - CABAC_ENTRY(28, 16, 90, 41, 36, 37, 42, 0, 0), - CABAC_ENTRY(29, 9, 104, 26, 69, 12, 97, 0, 0), - CABAC_ENTRY(30, -46, 127, -45, 127, -32, 127, 0, 0), - CABAC_ENTRY(31, -20, 104, -15, 101, -22, 117, 0, 0), - CABAC_ENTRY(32, 1, 67, -4, 76, -2, 74, 0, 0), - CABAC_ENTRY(33, -13, 78, -6, 71, -4, 85, 0, 0), - CABAC_ENTRY(34, -11, 65, -13, 79, -24, 102, 0, 0), - CABAC_ENTRY(35, 1, 62, 5, 52, 5, 57, 0, 0), - CABAC_ENTRY(36, -6, 86, 6, 69, -6, 93, 0, 0), - CABAC_ENTRY(37, -17, 95, -13, 90, -14, 88, 0, 0), - CABAC_ENTRY(38, -6, 61, 0, 52, -6, 44, 0, 0), - CABAC_ENTRY(39, 9, 45, 8, 43, 4, 55, 0, 0), - - /* Table 9-15 – Values of variables m and n for ctxIdx from 40 to 53 */ - CABAC_ENTRY(40, -3, 69, -2, 69, -11, 89, 0, 0), - CABAC_ENTRY(41, -6, 81, -5, 82, -15, 103, 0, 0), - CABAC_ENTRY(42, -11, 96, -10, 96, -21, 116, 0, 0), - CABAC_ENTRY(43, 6, 55, 2, 59, 19, 57, 0, 0), - CABAC_ENTRY(44, 7, 67, 2, 75, 20, 58, 0, 0), - CABAC_ENTRY(45, -5, 86, -3, 87, 4, 84, 0, 0), - CABAC_ENTRY(46, 2, 88, -3, 100, 6, 96, 0, 0), - CABAC_ENTRY(47, 0, 58, 1, 56, 1, 63, 0, 0), - CABAC_ENTRY(48, -3, 76, -3, 74, -5, 85, 0, 0), - CABAC_ENTRY(49, -10, 94, -6, 85, -13, 106, 0, 0), - CABAC_ENTRY(50, 5, 54, 0, 59, 5, 63, 0, 0), - CABAC_ENTRY(51, 4, 69, -3, 81, 6, 75, 0, 0), - CABAC_ENTRY(52, -3, 81, -7, 86, -3, 90, 0, 0), - CABAC_ENTRY(53, 0, 88, -5, 95, -1, 101, 0, 0), - - /* Table 9-16 – Values of variables m and n for ctxIdx from 54 to 59 */ - CABAC_ENTRY(54, -7, 67, -1, 66, 3, 55, 0, 0), - CABAC_ENTRY(55, -5, 74, -1, 77, -4, 79, 0, 0), - CABAC_ENTRY(56, -4, 74, 1, 70, -2, 75, 0, 0), - CABAC_ENTRY(57, -5, 80, -2, 86, -12, 97, 0, 0), - CABAC_ENTRY(58, -7, 72, -5, 72, -7, 50, 0, 0), - CABAC_ENTRY(59, 1, 58, 0, 61, 1, 60, 0, 0), - - /* Table 9-17 – Values of variables m and n for ctxIdx from 60 to 69 */ - CABAC_ENTRY(60, 0, 41, 0, 41, 0, 41, 0, 41), - CABAC_ENTRY(61, 0, 63, 0, 63, 0, 63, 0, 63), - CABAC_ENTRY(62, 0, 63, 0, 63, 0, 63, 0, 63), - CABAC_ENTRY(63, 0, 63, 0, 63, 0, 63, 0, 63), - CABAC_ENTRY(64, -9, 83, -9, 83, -9, 83, -9, 83), - CABAC_ENTRY(65, 4, 86, 4, 86, 4, 86, 4, 86), - CABAC_ENTRY(66, 0, 97, 0, 97, 0, 97, 0, 97), - CABAC_ENTRY(67, -7, 72, -7, 72, -7, 72, -7, 72), - CABAC_ENTRY(68, 13, 41, 13, 41, 13, 41, 13, 41), - CABAC_ENTRY(69, 3, 62, 3, 62, 3, 62, 3, 62), - - /* Table 9-18 – Values of variables m and n for ctxIdx from 70 to 104 */ - CABAC_ENTRY(70, 0, 45, 13, 15, 7, 34, 0, 11), - CABAC_ENTRY(71, -4, 78, 7, 51, -9, 88, 1, 55), - CABAC_ENTRY(72, -3, 96, 2, 80, -20, 127, 0, 69), - CABAC_ENTRY(73, -27, 126, -39, 127, -36, 127, -17, 127), - CABAC_ENTRY(74, -28, 98, -18, 91, -17, 91, -13, 102), - CABAC_ENTRY(75, -25, 101, -17, 96, -14, 95, 0, 82), - CABAC_ENTRY(76, -23, 67, -26, 81, -25, 84, -7, 74), - CABAC_ENTRY(77, -28, 82, -35, 98, -25, 86, -21, 107), - CABAC_ENTRY(78, -20, 94, -24, 102, -12, 89, -27, 127), - CABAC_ENTRY(79, -16, 83, -23, 97, -17, 91, -31, 127), - CABAC_ENTRY(80, -22, 110, -27, 119, -31, 127, -24, 127), - CABAC_ENTRY(81, -21, 91, -24, 99, -14, 76, -18, 95), - CABAC_ENTRY(82, -18, 102, -21, 110, -18, 103, -27, 127), - CABAC_ENTRY(83, -13, 93, -18, 102, -13, 90, -21, 114), - CABAC_ENTRY(84, -29, 127, -36, 127, -37, 127, -30, 127), - CABAC_ENTRY(85, -7, 92, 0, 80, 11, 80, -17, 123), - CABAC_ENTRY(86, -5, 89, -5, 89, 5, 76, -12, 115), - CABAC_ENTRY(87, -7, 96, -7, 94, 2, 84, -16, 122), - CABAC_ENTRY(88, -13, 108, -4, 92, 5, 78, -11, 115), - CABAC_ENTRY(89, -3, 46, 0, 39, -6, 55, -12, 63), - CABAC_ENTRY(90, -1, 65, 0, 65, 4, 61, -2, 68), - CABAC_ENTRY(91, -1, 57, -15, 84, -14, 83, -15, 84), - CABAC_ENTRY(92, -9, 93, -35, 127, -37, 127, -13, 104), - CABAC_ENTRY(93, -3, 74, -2, 73, -5, 79, -3, 70), - CABAC_ENTRY(94, -9, 92, -12, 104, -11, 104, -8, 93), - CABAC_ENTRY(95, -8, 87, -9, 91, -11, 91, -10, 90), - CABAC_ENTRY(96, -23, 126, -31, 127, -30, 127, -30, 127), - CABAC_ENTRY(97, 5, 54, 3, 55, 0, 65, -1, 74), - CABAC_ENTRY(98, 6, 60, 7, 56, -2, 79, -6, 97), - CABAC_ENTRY(99, 6, 59, 7, 55, 0, 72, -7, 91), - CABAC_ENTRY(100, 6, 69, 8, 61, -4, 92, -20, 127), - CABAC_ENTRY(101, -1, 48, -3, 53, -6, 56, -4, 56), - CABAC_ENTRY(102, 0, 68, 0, 68, 3, 68, -5, 82), - CABAC_ENTRY(103, -4, 69, -7, 74, -8, 71, -7, 76), - CABAC_ENTRY(104, -8, 88, -9, 88, -13, 98, -22, 125), - - /* Table 9-19 – Values of variables m and n for ctxIdx from 105 to 165 */ - CABAC_ENTRY(105, -2, 85, -13, 103, -4, 86, -7, 93), - CABAC_ENTRY(106, -6, 78, -13, 91, -12, 88, -11, 87), - CABAC_ENTRY(107, -1, 75, -9, 89, -5, 82, -3, 77), - CABAC_ENTRY(108, -7, 77, -14, 92, -3, 72, -5, 71), - CABAC_ENTRY(109, 2, 54, -8, 76, -4, 67, -4, 63), - CABAC_ENTRY(110, 5, 50, -12, 87, -8, 72, -4, 68), - CABAC_ENTRY(111, -3, 68, -23, 110, -16, 89, -12, 84), - CABAC_ENTRY(112, 1, 50, -24, 105, -9, 69, -7, 62), - CABAC_ENTRY(113, 6, 42, -10, 78, -1, 59, -7, 65), - CABAC_ENTRY(114, -4, 81, -20, 112, 5, 66, 8, 61), - CABAC_ENTRY(115, 1, 63, -17, 99, 4, 57, 5, 56), - CABAC_ENTRY(116, -4, 70, -78, 127, -4, 71, -2, 66), - CABAC_ENTRY(117, 0, 67, -70, 127, -2, 71, 1, 64), - CABAC_ENTRY(118, 2, 57, -50, 127, 2, 58, 0, 61), - CABAC_ENTRY(119, -2, 76, -46, 127, -1, 74, -2, 78), - CABAC_ENTRY(120, 11, 35, -4, 66, -4, 44, 1, 50), - CABAC_ENTRY(121, 4, 64, -5, 78, -1, 69, 7, 52), - CABAC_ENTRY(122, 1, 61, -4, 71, 0, 62, 10, 35), - CABAC_ENTRY(123, 11, 35, -8, 72, -7, 51, 0, 44), - CABAC_ENTRY(124, 18, 25, 2, 59, -4, 47, 11, 38), - CABAC_ENTRY(125, 12, 24, -1, 55, -6, 42, 1, 45), - CABAC_ENTRY(126, 13, 29, -7, 70, -3, 41, 0, 46), - CABAC_ENTRY(127, 13, 36, -6, 75, -6, 53, 5, 44), - CABAC_ENTRY(128, -10, 93, -8, 89, 8, 76, 31, 17), - CABAC_ENTRY(129, -7, 73, -34, 119, -9, 78, 1, 51), - CABAC_ENTRY(130, -2, 73, -3, 75, -11, 83, 7, 50), - CABAC_ENTRY(131, 13, 46, 32, 20, 9, 52, 28, 19), - CABAC_ENTRY(132, 9, 49, 30, 22, 0, 67, 16, 33), - CABAC_ENTRY(133, -7, 100, -44, 127, -5, 90, 14, 62), - CABAC_ENTRY(134, 9, 53, 0, 54, 1, 67, -13, 108), - CABAC_ENTRY(135, 2, 53, -5, 61, -15, 72, -15, 100), - CABAC_ENTRY(136, 5, 53, 0, 58, -5, 75, -13, 101), - CABAC_ENTRY(137, -2, 61, -1, 60, -8, 80, -13, 91), - CABAC_ENTRY(138, 0, 56, -3, 61, -21, 83, -12, 94), - CABAC_ENTRY(139, 0, 56, -8, 67, -21, 64, -10, 88), - CABAC_ENTRY(140, -13, 63, -25, 84, -13, 31, -16, 84), - CABAC_ENTRY(141, -5, 60, -14, 74, -25, 64, -10, 86), - CABAC_ENTRY(142, -1, 62, -5, 65, -29, 94, -7, 83), - CABAC_ENTRY(143, 4, 57, 5, 52, 9, 75, -13, 87), - CABAC_ENTRY(144, -6, 69, 2, 57, 17, 63, -19, 94), - CABAC_ENTRY(145, 4, 57, 0, 61, -8, 74, 1, 70), - CABAC_ENTRY(146, 14, 39, -9, 69, -5, 35, 0, 72), - CABAC_ENTRY(147, 4, 51, -11, 70, -2, 27, -5, 74), - CABAC_ENTRY(148, 13, 68, 18, 55, 13, 91, 18, 59), - CABAC_ENTRY(149, 3, 64, -4, 71, 3, 65, -8, 102), - CABAC_ENTRY(150, 1, 61, 0, 58, -7, 69, -15, 100), - CABAC_ENTRY(151, 9, 63, 7, 61, 8, 77, 0, 95), - CABAC_ENTRY(152, 7, 50, 9, 41, -10, 66, -4, 75), - CABAC_ENTRY(153, 16, 39, 18, 25, 3, 62, 2, 72), - CABAC_ENTRY(154, 5, 44, 9, 32, -3, 68, -11, 75), - CABAC_ENTRY(155, 4, 52, 5, 43, -20, 81, -3, 71), - CABAC_ENTRY(156, 11, 48, 9, 47, 0, 30, 15, 46), - CABAC_ENTRY(157, -5, 60, 0, 44, 1, 7, -13, 69), - CABAC_ENTRY(158, -1, 59, 0, 51, -3, 23, 0, 62), - CABAC_ENTRY(159, 0, 59, 2, 46, -21, 74, 0, 65), - CABAC_ENTRY(160, 22, 33, 19, 38, 16, 66, 21, 37), - CABAC_ENTRY(161, 5, 44, -4, 66, -23, 124, -15, 72), - CABAC_ENTRY(162, 14, 43, 15, 38, 17, 37, 9, 57), - CABAC_ENTRY(163, -1, 78, 12, 42, 44, -18, 16, 54), - CABAC_ENTRY(164, 0, 60, 9, 34, 50, -34, 0, 62), - CABAC_ENTRY(165, 9, 69, 0, 89, -22, 127, 12, 72), - - /* Table 9-20 – Values of variables m and n for ctxIdx from 166 to 226 */ - CABAC_ENTRY(166, 11, 28, 4, 45, 4, 39, 24, 0), - CABAC_ENTRY(167, 2, 40, 10, 28, 0, 42, 15, 9), - CABAC_ENTRY(168, 3, 44, 10, 31, 7, 34, 8, 25), - CABAC_ENTRY(169, 0, 49, 33, -11, 11, 29, 13, 18), - CABAC_ENTRY(170, 0, 46, 52, -43, 8, 31, 15, 9), - CABAC_ENTRY(171, 2, 44, 18, 15, 6, 37, 13, 19), - CABAC_ENTRY(172, 2, 51, 28, 0, 7, 42, 10, 37), - CABAC_ENTRY(173, 0, 47, 35, -22, 3, 40, 12, 18), - CABAC_ENTRY(174, 4, 39, 38, -25, 8, 33, 6, 29), - CABAC_ENTRY(175, 2, 62, 34, 0, 13, 43, 20, 33), - CABAC_ENTRY(176, 6, 46, 39, -18, 13, 36, 15, 30), - CABAC_ENTRY(177, 0, 54, 32, -12, 4, 47, 4, 45), - CABAC_ENTRY(178, 3, 54, 102, -94, 3, 55, 1, 58), - CABAC_ENTRY(179, 2, 58, 0, 0, 2, 58, 0, 62), - CABAC_ENTRY(180, 4, 63, 56, -15, 6, 60, 7, 61), - CABAC_ENTRY(181, 6, 51, 33, -4, 8, 44, 12, 38), - CABAC_ENTRY(182, 6, 57, 29, 10, 11, 44, 11, 45), - CABAC_ENTRY(183, 7, 53, 37, -5, 14, 42, 15, 39), - CABAC_ENTRY(184, 6, 52, 51, -29, 7, 48, 11, 42), - CABAC_ENTRY(185, 6, 55, 39, -9, 4, 56, 13, 44), - CABAC_ENTRY(186, 11, 45, 52, -34, 4, 52, 16, 45), - CABAC_ENTRY(187, 14, 36, 69, -58, 13, 37, 12, 41), - CABAC_ENTRY(188, 8, 53, 67, -63, 9, 49, 10, 49), - CABAC_ENTRY(189, -1, 82, 44, -5, 19, 58, 30, 34), - CABAC_ENTRY(190, 7, 55, 32, 7, 10, 48, 18, 42), - CABAC_ENTRY(191, -3, 78, 55, -29, 12, 45, 10, 55), - CABAC_ENTRY(192, 15, 46, 32, 1, 0, 69, 17, 51), - CABAC_ENTRY(193, 22, 31, 0, 0, 20, 33, 17, 46), - CABAC_ENTRY(194, -1, 84, 27, 36, 8, 63, 0, 89), - CABAC_ENTRY(195, 25, 7, 33, -25, 35, -18, 26, -19), - CABAC_ENTRY(196, 30, -7, 34, -30, 33, -25, 22, -17), - CABAC_ENTRY(197, 28, 3, 36, -28, 28, -3, 26, -17), - CABAC_ENTRY(198, 28, 4, 38, -28, 24, 10, 30, -25), - CABAC_ENTRY(199, 32, 0, 38, -27, 27, 0, 28, -20), - CABAC_ENTRY(200, 34, -1, 34, -18, 34, -14, 33, -23), - CABAC_ENTRY(201, 30, 6, 35, -16, 52, -44, 37, -27), - CABAC_ENTRY(202, 30, 6, 34, -14, 39, -24, 33, -23), - CABAC_ENTRY(203, 32, 9, 32, -8, 19, 17, 40, -28), - CABAC_ENTRY(204, 31, 19, 37, -6, 31, 25, 38, -17), - CABAC_ENTRY(205, 26, 27, 35, 0, 36, 29, 33, -11), - CABAC_ENTRY(206, 26, 30, 30, 10, 24, 33, 40, -15), - CABAC_ENTRY(207, 37, 20, 28, 18, 34, 15, 41, -6), - CABAC_ENTRY(208, 28, 34, 26, 25, 30, 20, 38, 1), - CABAC_ENTRY(209, 17, 70, 29, 41, 22, 73, 41, 17), - CABAC_ENTRY(210, 1, 67, 0, 75, 20, 34, 30, -6), - CABAC_ENTRY(211, 5, 59, 2, 72, 19, 31, 27, 3), - CABAC_ENTRY(212, 9, 67, 8, 77, 27, 44, 26, 22), - CABAC_ENTRY(213, 16, 30, 14, 35, 19, 16, 37, -16), - CABAC_ENTRY(214, 18, 32, 18, 31, 15, 36, 35, -4), - CABAC_ENTRY(215, 18, 35, 17, 35, 15, 36, 38, -8), - CABAC_ENTRY(216, 22, 29, 21, 30, 21, 28, 38, -3), - CABAC_ENTRY(217, 24, 31, 17, 45, 25, 21, 37, 3), - CABAC_ENTRY(218, 23, 38, 20, 42, 30, 20, 38, 5), - CABAC_ENTRY(219, 18, 43, 18, 45, 31, 12, 42, 0), - CABAC_ENTRY(220, 20, 41, 27, 26, 27, 16, 35, 16), - CABAC_ENTRY(221, 11, 63, 16, 54, 24, 42, 39, 22), - CABAC_ENTRY(222, 9, 59, 7, 66, 0, 93, 14, 48), - CABAC_ENTRY(223, 9, 64, 16, 56, 14, 56, 27, 37), - CABAC_ENTRY(224, -1, 94, 11, 73, 15, 57, 21, 60), - CABAC_ENTRY(225, -2, 89, 10, 67, 26, 38, 12, 68), - CABAC_ENTRY(226, -9, 108, -10, 116, -24, 127, 2, 97), - - /* Table 9-21 – Values of variables m and n for ctxIdx from 227 to 275 */ - CABAC_ENTRY(227, -6, 76, -23, 112, -24, 115, -3, 71), - CABAC_ENTRY(228, -2, 44, -15, 71, -22, 82, -6, 42), - CABAC_ENTRY(229, 0, 45, -7, 61, -9, 62, -5, 50), - CABAC_ENTRY(230, 0, 52, 0, 53, 0, 53, -3, 54), - CABAC_ENTRY(231, -3, 64, -5, 66, 0, 59, -2, 62), - CABAC_ENTRY(232, -2, 59, -11, 77, -14, 85, 0, 58), - CABAC_ENTRY(233, -4, 70, -9, 80, -13, 89, 1, 63), - CABAC_ENTRY(234, -4, 75, -9, 84, -13, 94, -2, 72), - CABAC_ENTRY(235, -8, 82, -10, 87, -11, 92, -1, 74), - CABAC_ENTRY(236, -17, 102, -34, 127, -29, 127, -9, 91), - CABAC_ENTRY(237, -9, 77, -21, 101, -21, 100, -5, 67), - CABAC_ENTRY(238, 3, 24, -3, 39, -14, 57, -5, 27), - CABAC_ENTRY(239, 0, 42, -5, 53, -12, 67, -3, 39), - CABAC_ENTRY(240, 0, 48, -7, 61, -11, 71, -2, 44), - CABAC_ENTRY(241, 0, 55, -11, 75, -10, 77, 0, 46), - CABAC_ENTRY(242, -6, 59, -15, 77, -21, 85, -16, 64), - CABAC_ENTRY(243, -7, 71, -17, 91, -16, 88, -8, 68), - CABAC_ENTRY(244, -12, 83, -25, 107, -23, 104, -10, 78), - CABAC_ENTRY(245, -11, 87, -25, 111, -15, 98, -6, 77), - CABAC_ENTRY(246, -30, 119, -28, 122, -37, 127, -10, 86), - CABAC_ENTRY(247, 1, 58, -11, 76, -10, 82, -12, 92), - CABAC_ENTRY(248, -3, 29, -10, 44, -8, 48, -15, 55), - CABAC_ENTRY(249, -1, 36, -10, 52, -8, 61, -10, 60), - CABAC_ENTRY(250, 1, 38, -10, 57, -8, 66, -6, 62), - CABAC_ENTRY(251, 2, 43, -9, 58, -7, 70, -4, 65), - CABAC_ENTRY(252, -6, 55, -16, 72, -14, 75, -12, 73), - CABAC_ENTRY(253, 0, 58, -7, 69, -10, 79, -8, 76), - CABAC_ENTRY(254, 0, 64, -4, 69, -9, 83, -7, 80), - CABAC_ENTRY(255, -3, 74, -5, 74, -12, 92, -9, 88), - CABAC_ENTRY(256, -10, 90, -9, 86, -18, 108, -17, 110), - CABAC_ENTRY(257, 0, 70, 2, 66, -4, 79, -11, 97), - CABAC_ENTRY(258, -4, 29, -9, 34, -22, 69, -20, 84), - CABAC_ENTRY(259, 5, 31, 1, 32, -16, 75, -11, 79), - CABAC_ENTRY(260, 7, 42, 11, 31, -2, 58, -6, 73), - CABAC_ENTRY(261, 1, 59, 5, 52, 1, 58, -4, 74), - CABAC_ENTRY(262, -2, 58, -2, 55, -13, 78, -13, 86), - CABAC_ENTRY(263, -3, 72, -2, 67, -9, 83, -13, 96), - CABAC_ENTRY(264, -3, 81, 0, 73, -4, 81, -11, 97), - CABAC_ENTRY(265, -11, 97, -8, 89, -13, 99, -19, 117), - CABAC_ENTRY(266, 0, 58, 3, 52, -13, 81, -8, 78), - CABAC_ENTRY(267, 8, 5, 7, 4, -6, 38, -5, 33), - CABAC_ENTRY(268, 10, 14, 10, 8, -13, 62, -4, 48), - CABAC_ENTRY(269, 14, 18, 17, 8, -6, 58, -2, 53), - CABAC_ENTRY(270, 13, 27, 16, 19, -2, 59, -3, 62), - CABAC_ENTRY(271, 2, 40, 3, 37, -16, 73, -13, 71), - CABAC_ENTRY(272, 0, 58, -1, 61, -10, 76, -10, 79), - CABAC_ENTRY(273, -3, 70, -5, 73, -13, 86, -12, 86), - CABAC_ENTRY(274, -6, 79, -1, 70, -9, 83, -13, 90), - CABAC_ENTRY(275, -8, 85, -4, 78, -10, 87, -14, 97), - - /* Table 9-22 – Values of variables m and n for ctxIdx from 277 to 337 */ - CABAC_ENTRY(277, -13, 106, -21, 126, -22, 127, -6, 93), - CABAC_ENTRY(278, -16, 106, -23, 124, -25, 127, -6, 84), - CABAC_ENTRY(279, -10, 87, -20, 110, -25, 120, -8, 79), - CABAC_ENTRY(280, -21, 114, -26, 126, -27, 127, 0, 66), - CABAC_ENTRY(281, -18, 110, -25, 124, -19, 114, -1, 71), - CABAC_ENTRY(282, -14, 98, -17, 105, -23, 117, 0, 62), - CABAC_ENTRY(283, -22, 110, -27, 121, -25, 118, -2, 60), - CABAC_ENTRY(284, -21, 106, -27, 117, -26, 117, -2, 59), - CABAC_ENTRY(285, -18, 103, -17, 102, -24, 113, -5, 75), - CABAC_ENTRY(286, -21, 107, -26, 117, -28, 118, -3, 62), - CABAC_ENTRY(287, -23, 108, -27, 116, -31, 120, -4, 58), - CABAC_ENTRY(288, -26, 112, -33, 122, -37, 124, -9, 66), - CABAC_ENTRY(289, -10, 96, -10, 95, -10, 94, -1, 79), - CABAC_ENTRY(290, -12, 95, -14, 100, -15, 102, 0, 71), - CABAC_ENTRY(291, -5, 91, -8, 95, -10, 99, 3, 68), - CABAC_ENTRY(292, -9, 93, -17, 111, -13, 106, 10, 44), - CABAC_ENTRY(293, -22, 94, -28, 114, -50, 127, -7, 62), - CABAC_ENTRY(294, -5, 86, -6, 89, -5, 92, 15, 36), - CABAC_ENTRY(295, 9, 67, -2, 80, 17, 57, 14, 40), - CABAC_ENTRY(296, -4, 80, -4, 82, -5, 86, 16, 27), - CABAC_ENTRY(297, -10, 85, -9, 85, -13, 94, 12, 29), - CABAC_ENTRY(298, -1, 70, -8, 81, -12, 91, 1, 44), - CABAC_ENTRY(299, 7, 60, -1, 72, -2, 77, 20, 36), - CABAC_ENTRY(300, 9, 58, 5, 64, 0, 71, 18, 32), - CABAC_ENTRY(301, 5, 61, 1, 67, -1, 73, 5, 42), - CABAC_ENTRY(302, 12, 50, 9, 56, 4, 64, 1, 48), - CABAC_ENTRY(303, 15, 50, 0, 69, -7, 81, 10, 62), - CABAC_ENTRY(304, 18, 49, 1, 69, 5, 64, 17, 46), - CABAC_ENTRY(305, 17, 54, 7, 69, 15, 57, 9, 64), - CABAC_ENTRY(306, 10, 41, -7, 69, 1, 67, -12, 104), - CABAC_ENTRY(307, 7, 46, -6, 67, 0, 68, -11, 97), - CABAC_ENTRY(308, -1, 51, -16, 77, -10, 67, -16, 96), - CABAC_ENTRY(309, 7, 49, -2, 64, 1, 68, -7, 88), - CABAC_ENTRY(310, 8, 52, 2, 61, 0, 77, -8, 85), - CABAC_ENTRY(311, 9, 41, -6, 67, 2, 64, -7, 85), - CABAC_ENTRY(312, 6, 47, -3, 64, 0, 68, -9, 85), - CABAC_ENTRY(313, 2, 55, 2, 57, -5, 78, -13, 88), - CABAC_ENTRY(314, 13, 41, -3, 65, 7, 55, 4, 66), - CABAC_ENTRY(315, 10, 44, -3, 66, 5, 59, -3, 77), - CABAC_ENTRY(316, 6, 50, 0, 62, 2, 65, -3, 76), - CABAC_ENTRY(317, 5, 53, 9, 51, 14, 54, -6, 76), - CABAC_ENTRY(318, 13, 49, -1, 66, 15, 44, 10, 58), - CABAC_ENTRY(319, 4, 63, -2, 71, 5, 60, -1, 76), - CABAC_ENTRY(320, 6, 64, -2, 75, 2, 70, -1, 83), - CABAC_ENTRY(321, -2, 69, -1, 70, -2, 76, -7, 99), - CABAC_ENTRY(322, -2, 59, -9, 72, -18, 86, -14, 95), - CABAC_ENTRY(323, 6, 70, 14, 60, 12, 70, 2, 95), - CABAC_ENTRY(324, 10, 44, 16, 37, 5, 64, 0, 76), - CABAC_ENTRY(325, 9, 31, 0, 47, -12, 70, -5, 74), - CABAC_ENTRY(326, 12, 43, 18, 35, 11, 55, 0, 70), - CABAC_ENTRY(327, 3, 53, 11, 37, 5, 56, -11, 75), - CABAC_ENTRY(328, 14, 34, 12, 41, 0, 69, 1, 68), - CABAC_ENTRY(329, 10, 38, 10, 41, 2, 65, 0, 65), - CABAC_ENTRY(330, -3, 52, 2, 48, -6, 74, -14, 73), - CABAC_ENTRY(331, 13, 40, 12, 41, 5, 54, 3, 62), - CABAC_ENTRY(332, 17, 32, 13, 41, 7, 54, 4, 62), - CABAC_ENTRY(333, 7, 44, 0, 59, -6, 76, -1, 68), - CABAC_ENTRY(334, 7, 38, 3, 50, -11, 82, -13, 75), - CABAC_ENTRY(335, 13, 50, 19, 40, -2, 77, 11, 55), - CABAC_ENTRY(336, 10, 57, 3, 66, -2, 77, 5, 64), - CABAC_ENTRY(337, 26, 43, 18, 50, 25, 42, 12, 70), - - /* Table 9-23 – Values of variables m and n for ctxIdx from 338 to 398 */ - CABAC_ENTRY(338, 14, 11, 19, -6, 17, -13, 15, 6), - CABAC_ENTRY(339, 11, 14, 18, -6, 16, -9, 6, 19), - CABAC_ENTRY(340, 9, 11, 14, 0, 17, -12, 7, 16), - CABAC_ENTRY(341, 18, 11, 26, -12, 27, -21, 12, 14), - CABAC_ENTRY(342, 21, 9, 31, -16, 37, -30, 18, 13), - CABAC_ENTRY(343, 23, -2, 33, -25, 41, -40, 13, 11), - CABAC_ENTRY(344, 32, -15, 33, -22, 42, -41, 13, 15), - CABAC_ENTRY(345, 32, -15, 37, -28, 48, -47, 15, 16), - CABAC_ENTRY(346, 34, -21, 39, -30, 39, -32, 12, 23), - CABAC_ENTRY(347, 39, -23, 42, -30, 46, -40, 13, 23), - CABAC_ENTRY(348, 42, -33, 47, -42, 52, -51, 15, 20), - CABAC_ENTRY(349, 41, -31, 45, -36, 46, -41, 14, 26), - CABAC_ENTRY(350, 46, -28, 49, -34, 52, -39, 14, 44), - CABAC_ENTRY(351, 38, -12, 41, -17, 43, -19, 17, 40), - CABAC_ENTRY(352, 21, 29, 32, 9, 32, 11, 17, 47), - CABAC_ENTRY(353, 45, -24, 69, -71, 61, -55, 24, 17), - CABAC_ENTRY(354, 53, -45, 63, -63, 56, -46, 21, 21), - CABAC_ENTRY(355, 48, -26, 66, -64, 62, -50, 25, 22), - CABAC_ENTRY(356, 65, -43, 77, -74, 81, -67, 31, 27), - CABAC_ENTRY(357, 43, -19, 54, -39, 45, -20, 22, 29), - CABAC_ENTRY(358, 39, -10, 52, -35, 35, -2, 19, 35), - CABAC_ENTRY(359, 30, 9, 41, -10, 28, 15, 14, 50), - CABAC_ENTRY(360, 18, 26, 36, 0, 34, 1, 10, 57), - CABAC_ENTRY(361, 20, 27, 40, -1, 39, 1, 7, 63), - CABAC_ENTRY(362, 0, 57, 30, 14, 30, 17, -2, 77), - CABAC_ENTRY(363, -14, 82, 28, 26, 20, 38, -4, 82), - CABAC_ENTRY(364, -5, 75, 23, 37, 18, 45, -3, 94), - CABAC_ENTRY(365, -19, 97, 12, 55, 15, 54, 9, 69), - CABAC_ENTRY(366, -35, 125, 11, 65, 0, 79, -12, 109), - CABAC_ENTRY(367, 27, 0, 37, -33, 36, -16, 36, -35), - CABAC_ENTRY(368, 28, 0, 39, -36, 37, -14, 36, -34), - CABAC_ENTRY(369, 31, -4, 40, -37, 37, -17, 32, -26), - CABAC_ENTRY(370, 27, 6, 38, -30, 32, 1, 37, -30), - CABAC_ENTRY(371, 34, 8, 46, -33, 34, 15, 44, -32), - CABAC_ENTRY(372, 30, 10, 42, -30, 29, 15, 34, -18), - CABAC_ENTRY(373, 24, 22, 40, -24, 24, 25, 34, -15), - CABAC_ENTRY(374, 33, 19, 49, -29, 34, 22, 40, -15), - CABAC_ENTRY(375, 22, 32, 38, -12, 31, 16, 33, -7), - CABAC_ENTRY(376, 26, 31, 40, -10, 35, 18, 35, -5), - CABAC_ENTRY(377, 21, 41, 38, -3, 31, 28, 33, 0), - CABAC_ENTRY(378, 26, 44, 46, -5, 33, 41, 38, 2), - CABAC_ENTRY(379, 23, 47, 31, 20, 36, 28, 33, 13), - CABAC_ENTRY(380, 16, 65, 29, 30, 27, 47, 23, 35), - CABAC_ENTRY(381, 14, 71, 25, 44, 21, 62, 13, 58), - CABAC_ENTRY(382, 8, 60, 12, 48, 18, 31, 29, -3), - CABAC_ENTRY(383, 6, 63, 11, 49, 19, 26, 26, 0), - CABAC_ENTRY(384, 17, 65, 26, 45, 36, 24, 22, 30), - CABAC_ENTRY(385, 21, 24, 22, 22, 24, 23, 31, -7), - CABAC_ENTRY(386, 23, 20, 23, 22, 27, 16, 35, -15), - CABAC_ENTRY(387, 26, 23, 27, 21, 24, 30, 34, -3), - CABAC_ENTRY(388, 27, 32, 33, 20, 31, 29, 34, 3), - CABAC_ENTRY(389, 28, 23, 26, 28, 22, 41, 36, -1), - CABAC_ENTRY(390, 28, 24, 30, 24, 22, 42, 34, 5), - CABAC_ENTRY(391, 23, 40, 27, 34, 16, 60, 32, 11), - CABAC_ENTRY(392, 24, 32, 18, 42, 15, 52, 35, 5), - CABAC_ENTRY(393, 28, 29, 25, 39, 14, 60, 34, 12), - CABAC_ENTRY(394, 23, 42, 18, 50, 3, 78, 39, 11), - CABAC_ENTRY(395, 19, 57, 12, 70, -16, 123, 30, 29), - CABAC_ENTRY(396, 22, 53, 21, 54, 21, 53, 34, 26), - CABAC_ENTRY(397, 22, 61, 14, 71, 22, 56, 29, 39), - CABAC_ENTRY(398, 11, 86, 11, 83, 25, 61, 19, 66), - - /* Values of variables m and n for ctxIdx from 399 to 463 (not documented) */ - CABAC_ENTRY(399, 12, 40, 25, 32, 21, 33, 31, 21), - CABAC_ENTRY(400, 11, 51, 21, 49, 19, 50, 31, 31), - CABAC_ENTRY(401, 14, 59, 21, 54, 17, 61, 25, 50), - CABAC_ENTRY(402, -4, 79, -5, 85, -3, 78, -17, 120), - CABAC_ENTRY(403, -7, 71, -6, 81, -8, 74, -20, 112), - CABAC_ENTRY(404, -5, 69, -10, 77, -9, 72, -18, 114), - CABAC_ENTRY(405, -9, 70, -7, 81, -10, 72, -11, 85), - CABAC_ENTRY(406, -8, 66, -17, 80, -18, 75, -15, 92), - CABAC_ENTRY(407, -10, 68, -18, 73, -12, 71, -14, 89), - CABAC_ENTRY(408, -19, 73, -4, 74, -11, 63, -26, 71), - CABAC_ENTRY(409, -12, 69, -10, 83, -5, 70, -15, 81), - CABAC_ENTRY(410, -16, 70, -9, 71, -17, 75, -14, 80), - CABAC_ENTRY(411, -15, 67, -9, 67, -14, 72, 0, 68), - CABAC_ENTRY(412, -20, 62, -1, 61, -16, 67, -14, 70), - CABAC_ENTRY(413, -19, 70, -8, 66, -8, 53, -24, 56), - CABAC_ENTRY(414, -16, 66, -14, 66, -14, 59, -23, 68), - CABAC_ENTRY(415, -22, 65, 0, 59, -9, 52, -24, 50), - CABAC_ENTRY(416, -20, 63, 2, 59, -11, 68, -11, 74), - CABAC_ENTRY(417, 9, -2, 17, -10, 9, -2, 23, -13), - CABAC_ENTRY(418, 26, -9, 32, -13, 30, -10, 26, -13), - CABAC_ENTRY(419, 33, -9, 42, -9, 31, -4, 40, -15), - CABAC_ENTRY(420, 39, -7, 49, -5, 33, -1, 49, -14), - CABAC_ENTRY(421, 41, -2, 53, 0, 33, 7, 44, 3), - CABAC_ENTRY(422, 45, 3, 64, 3, 31, 12, 45, 6), - CABAC_ENTRY(423, 49, 9, 68, 10, 37, 23, 44, 34), - CABAC_ENTRY(424, 45, 27, 66, 27, 31, 38, 33, 54), - CABAC_ENTRY(425, 36, 59, 47, 57, 20, 64, 19, 82), - CABAC_ENTRY(426, -6, 66, -5, 71, -9, 71, -3, 75), - CABAC_ENTRY(427, -7, 35, 0, 24, -7, 37, -1, 23), - CABAC_ENTRY(428, -7, 42, -1, 36, -8, 44, 1, 34), - CABAC_ENTRY(429, -8, 45, -2, 42, -11, 49, 1, 43), - CABAC_ENTRY(430, -5, 48, -2, 52, -10, 56, 0, 54), - CABAC_ENTRY(431, -12, 56, -9, 57, -12, 59, -2, 55), - CABAC_ENTRY(432, -6, 60, -6, 63, -8, 63, 0, 61), - CABAC_ENTRY(433, -5, 62, -4, 65, -9, 67, 1, 64), - CABAC_ENTRY(434, -8, 66, -4, 67, -6, 68, 0, 68), - CABAC_ENTRY(435, -8, 76, -7, 82, -10, 79, -9, 92), - CABAC_ENTRY(436, -5, 85, -3, 81, -3, 78, -14, 106), - CABAC_ENTRY(437, -6, 81, -3, 76, -8, 74, -13, 97), - CABAC_ENTRY(438, -10, 77, -7, 72, -9, 72, -15, 90), - CABAC_ENTRY(439, -7, 81, -6, 78, -10, 72, -12, 90), - CABAC_ENTRY(440, -17, 80, -12, 72, -18, 75, -18, 88), - CABAC_ENTRY(441, -18, 73, -14, 68, -12, 71, -10, 73), - CABAC_ENTRY(442, -4, 74, -3, 70, -11, 63, -9, 79), - CABAC_ENTRY(443, -10, 83, -6, 76, -5, 70, -14, 86), - CABAC_ENTRY(444, -9, 71, -5, 66, -17, 75, -10, 73), - CABAC_ENTRY(445, -9, 67, -5, 62, -14, 72, -10, 70), - CABAC_ENTRY(446, -1, 61, 0, 57, -16, 67, -10, 69), - CABAC_ENTRY(447, -8, 66, -4, 61, -8, 53, -5, 66), - CABAC_ENTRY(448, -14, 66, -9, 60, -14, 59, -9, 64), - CABAC_ENTRY(449, 0, 59, 1, 54, -9, 52, -5, 58), - CABAC_ENTRY(450, 2, 59, 2, 58, -11, 68, 2, 59), - CABAC_ENTRY(451, 21, -13, 17, -10, 9, -2, 21, -10), - CABAC_ENTRY(452, 33, -14, 32, -13, 30, -10, 24, -11), - CABAC_ENTRY(453, 39, -7, 42, -9, 31, -4, 28, -8), - CABAC_ENTRY(454, 46, -2, 49, -5, 33, -1, 28, -1), - CABAC_ENTRY(455, 51, 2, 53, 0, 33, 7, 29, 3), - CABAC_ENTRY(456, 60, 6, 64, 3, 31, 12, 29, 9), - CABAC_ENTRY(457, 61, 17, 68, 10, 37, 23, 35, 20), - CABAC_ENTRY(458, 55, 34, 66, 27, 31, 38, 29, 36), - CABAC_ENTRY(459, 42, 62, 47, 57, 20, 64, 14, 67), -}; - -static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value) -{ - u8 bit = field.offset % 32, word = field.offset / 32; - u64 mask = GENMASK_ULL(bit + field.len - 1, bit); - u64 val = ((u64)value << bit) & mask; - - buf[word] &= ~mask; - buf[word] |= val; - if (bit + field.len > 32) { - buf[word + 1] &= ~(mask >> 32); - buf[word + 1] |= val >> 32; - } -} - -static void assemble_hw_pps(struct rkvdec_ctx *ctx, - struct rkvdec_h264_run *run) -{ - struct rkvdec_h264_ctx *h264_ctx = ctx->priv; - const struct v4l2_ctrl_h264_sps *sps = run->sps; - const struct v4l2_ctrl_h264_pps *pps = run->pps; - const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; - const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb; - struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu; - struct rkvdec_sps_pps_packet *hw_ps; - dma_addr_t scaling_list_address; - u32 scaling_distance; - u32 i; - - /* - * HW read the SPS/PPS information from PPS packet index by PPS id. - * offset from the base can be calculated by PPS_id * 32 (size per PPS - * packet unit). so the driver copy SPS/PPS information to the exact PPS - * packet unit for HW accessing. - */ - hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id]; - memset(hw_ps, 0, sizeof(*hw_ps)); - -#define WRITE_PPS(value, field) set_ps_field(hw_ps->info, field, value) - /* write sps */ - WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID); - WRITE_PPS(sps->profile_idc, PROFILE_IDC); - WRITE_PPS(!!(sps->constraint_set_flags & (1 << 3)), CONSTRAINT_SET3_FLAG); - WRITE_PPS(sps->chroma_format_idc, CHROMA_FORMAT_IDC); - WRITE_PPS(sps->bit_depth_luma_minus8, BIT_DEPTH_LUMA); - WRITE_PPS(sps->bit_depth_chroma_minus8, BIT_DEPTH_CHROMA); - WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS), - QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG); - WRITE_PPS(sps->log2_max_frame_num_minus4, LOG2_MAX_FRAME_NUM_MINUS4); - WRITE_PPS(sps->max_num_ref_frames, MAX_NUM_REF_FRAMES); - WRITE_PPS(sps->pic_order_cnt_type, PIC_ORDER_CNT_TYPE); - WRITE_PPS(sps->log2_max_pic_order_cnt_lsb_minus4, - LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4); - WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO), - DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG); - - /* - * Use the SPS values since they are already in macroblocks - * dimensions, height can be field height (halved) if - * V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY is not set and also it allows - * decoding smaller images into larger allocation which can be used - * to implementing SVC spatial layer support. - */ - WRITE_PPS(sps->pic_width_in_mbs_minus1 + 1, PIC_WIDTH_IN_MBS); - WRITE_PPS(sps->pic_height_in_map_units_minus1 + 1, PIC_HEIGHT_IN_MBS); - - WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY), - FRAME_MBS_ONLY_FLAG); - WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD), - MB_ADAPTIVE_FRAME_FIELD_FLAG); - WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE), - DIRECT_8X8_INFERENCE_FLAG); - - /* write pps */ - WRITE_PPS(pps->pic_parameter_set_id, PIC_PARAMETER_SET_ID); - WRITE_PPS(pps->seq_parameter_set_id, PPS_SEQ_PARAMETER_SET_ID); - WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE), - ENTROPY_CODING_MODE_FLAG); - WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT), - BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG); - WRITE_PPS(pps->num_ref_idx_l0_default_active_minus1, - NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(0)); - WRITE_PPS(pps->num_ref_idx_l1_default_active_minus1, - NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(1)); - WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED), - WEIGHTED_PRED_FLAG); - WRITE_PPS(pps->weighted_bipred_idc, WEIGHTED_BIPRED_IDC); - WRITE_PPS(pps->pic_init_qp_minus26, PIC_INIT_QP_MINUS26); - WRITE_PPS(pps->pic_init_qs_minus26, PIC_INIT_QS_MINUS26); - WRITE_PPS(pps->chroma_qp_index_offset, CHROMA_QP_INDEX_OFFSET); - WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT), - DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG); - WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED), - CONSTRAINED_INTRA_PRED_FLAG); - WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT), - REDUNDANT_PIC_CNT_PRESENT); - WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE), - TRANSFORM_8X8_MODE_FLAG); - WRITE_PPS(pps->second_chroma_qp_index_offset, - SECOND_CHROMA_QP_INDEX_OFFSET); - - WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT), - SCALING_LIST_ENABLE_FLAG); - /* To be on the safe side, program the scaling matrix address */ - scaling_distance = offsetof(struct rkvdec_h264_priv_tbl, scaling_list); - scaling_list_address = h264_ctx->priv_tbl.dma + scaling_distance; - WRITE_PPS(scaling_list_address, SCALING_LIST_ADDRESS); - - for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { - u32 is_longterm = 0; - - if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) - is_longterm = 1; - - WRITE_PPS(is_longterm, IS_LONG_TERM(i)); - } -} - -static void lookup_ref_buf_idx(struct rkvdec_ctx *ctx, - struct rkvdec_h264_run *run) -{ - const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; - u32 i; - - for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { - struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; - const struct v4l2_h264_dpb_entry *dpb = run->decode_params->dpb; - struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q; - struct vb2_buffer *buf = NULL; - - if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) { - buf = vb2_find_buffer(cap_q, dpb[i].reference_ts); - if (!buf) - pr_debug("No buffer for reference_ts %llu", - dpb[i].reference_ts); - } - - run->ref_buf[i] = buf; - } -} - -static void assemble_hw_rps(struct rkvdec_ctx *ctx, - struct v4l2_h264_reflist_builder *builder, - struct rkvdec_h264_run *run) -{ - const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; - const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb; - struct rkvdec_h264_ctx *h264_ctx = ctx->priv; - struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu; - - u32 *hw_rps = priv_tbl->rps; - u32 i, j; - u16 *p = (u16 *)hw_rps; - - memset(hw_rps, 0, sizeof(priv_tbl->rps)); - - /* - * Assign an invalid pic_num if DPB entry at that position is inactive. - * If we assign 0 in that position hardware will treat that as a real - * reference picture with pic_num 0, triggering output picture - * corruption. - */ - for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { - if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) - continue; - - p[i] = builder->refs[i].frame_num; - } - - for (j = 0; j < RKVDEC_NUM_REFLIST; j++) { - for (i = 0; i < builder->num_valid; i++) { - struct v4l2_h264_reference *ref; - bool dpb_valid; - bool bottom; - - switch (j) { - case 0: - ref = &h264_ctx->reflists.p[i]; - break; - case 1: - ref = &h264_ctx->reflists.b0[i]; - break; - case 2: - ref = &h264_ctx->reflists.b1[i]; - break; - } - - if (WARN_ON(ref->index >= ARRAY_SIZE(dec_params->dpb))) - continue; - - dpb_valid = run->ref_buf[ref->index] != NULL; - bottom = ref->fields == V4L2_H264_BOTTOM_FIELD_REF; - - set_ps_field(hw_rps, DPB_INFO(i, j), - ref->index | dpb_valid << 4); - set_ps_field(hw_rps, BOTTOM_FLAG(i, j), bottom); - } - } -} - -static void assemble_hw_scaling_list(struct rkvdec_ctx *ctx, - struct rkvdec_h264_run *run) -{ - const struct v4l2_ctrl_h264_scaling_matrix *scaling = run->scaling_matrix; - const struct v4l2_ctrl_h264_pps *pps = run->pps; - struct rkvdec_h264_ctx *h264_ctx = ctx->priv; - struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu; - - if (!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT)) - return; - - BUILD_BUG_ON(sizeof(tbl->scaling_list.scaling_list_4x4) != - sizeof(scaling->scaling_list_4x4)); - BUILD_BUG_ON(sizeof(tbl->scaling_list.scaling_list_8x8) != - sizeof(scaling->scaling_list_8x8)); - - memcpy(tbl->scaling_list.scaling_list_4x4, - scaling->scaling_list_4x4, - sizeof(scaling->scaling_list_4x4)); - - memcpy(tbl->scaling_list.scaling_list_8x8, - scaling->scaling_list_8x8, - sizeof(scaling->scaling_list_8x8)); -} - -/* - * dpb poc related registers table - */ -static const u32 poc_reg_tbl_top_field[16] = { - RKVDEC_REG_H264_POC_REFER0(0), - RKVDEC_REG_H264_POC_REFER0(2), - RKVDEC_REG_H264_POC_REFER0(4), - RKVDEC_REG_H264_POC_REFER0(6), - RKVDEC_REG_H264_POC_REFER0(8), - RKVDEC_REG_H264_POC_REFER0(10), - RKVDEC_REG_H264_POC_REFER0(12), - RKVDEC_REG_H264_POC_REFER0(14), - RKVDEC_REG_H264_POC_REFER1(1), - RKVDEC_REG_H264_POC_REFER1(3), - RKVDEC_REG_H264_POC_REFER1(5), - RKVDEC_REG_H264_POC_REFER1(7), - RKVDEC_REG_H264_POC_REFER1(9), - RKVDEC_REG_H264_POC_REFER1(11), - RKVDEC_REG_H264_POC_REFER1(13), - RKVDEC_REG_H264_POC_REFER2(0) -}; - -static const u32 poc_reg_tbl_bottom_field[16] = { - RKVDEC_REG_H264_POC_REFER0(1), - RKVDEC_REG_H264_POC_REFER0(3), - RKVDEC_REG_H264_POC_REFER0(5), - RKVDEC_REG_H264_POC_REFER0(7), - RKVDEC_REG_H264_POC_REFER0(9), - RKVDEC_REG_H264_POC_REFER0(11), - RKVDEC_REG_H264_POC_REFER0(13), - RKVDEC_REG_H264_POC_REFER1(0), - RKVDEC_REG_H264_POC_REFER1(2), - RKVDEC_REG_H264_POC_REFER1(4), - RKVDEC_REG_H264_POC_REFER1(6), - RKVDEC_REG_H264_POC_REFER1(8), - RKVDEC_REG_H264_POC_REFER1(10), - RKVDEC_REG_H264_POC_REFER1(12), - RKVDEC_REG_H264_POC_REFER1(14), - RKVDEC_REG_H264_POC_REFER2(1) -}; - -static void config_registers(struct rkvdec_ctx *ctx, - struct rkvdec_h264_run *run) -{ - struct rkvdec_dev *rkvdec = ctx->dev; - const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; - const struct v4l2_ctrl_h264_sps *sps = run->sps; - const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb; - struct rkvdec_h264_ctx *h264_ctx = ctx->priv; - dma_addr_t priv_start_addr = h264_ctx->priv_tbl.dma; - const struct v4l2_pix_format_mplane *dst_fmt; - struct vb2_v4l2_buffer *src_buf = run->base.bufs.src; - struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst; - const struct v4l2_format *f; - dma_addr_t rlc_addr; - dma_addr_t refer_addr; - u32 rlc_len; - u32 hor_virstride; - u32 ver_virstride; - u32 y_virstride; - u32 yuv_virstride = 0; - u32 offset; - dma_addr_t dst_addr; - u32 reg, i; - - reg = RKVDEC_MODE(RKVDEC_MODE_H264); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_SYSCTRL); - - f = &ctx->decoded_fmt; - dst_fmt = &f->fmt.pix_mp; - hor_virstride = dst_fmt->plane_fmt[0].bytesperline; - ver_virstride = dst_fmt->height; - y_virstride = hor_virstride * ver_virstride; - - if (sps->chroma_format_idc == 0) - yuv_virstride = y_virstride; - else if (sps->chroma_format_idc == 1) - yuv_virstride = y_virstride + y_virstride / 2; - else if (sps->chroma_format_idc == 2) - yuv_virstride = 2 * y_virstride; - - reg = RKVDEC_Y_HOR_VIRSTRIDE(hor_virstride / 16) | - RKVDEC_UV_HOR_VIRSTRIDE(hor_virstride / 16) | - RKVDEC_SLICE_NUM_HIGHBIT | - RKVDEC_SLICE_NUM_LOWBITS(0x7ff); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_PICPAR); - - /* config rlc base address */ - rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); - writel_relaxed(rlc_addr, rkvdec->regs + RKVDEC_REG_STRM_RLC_BASE); - writel_relaxed(rlc_addr, rkvdec->regs + RKVDEC_REG_RLCWRITE_BASE); - - rlc_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0); - reg = RKVDEC_STRM_LEN(rlc_len); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_STRM_LEN); - - /* config cabac table */ - offset = offsetof(struct rkvdec_h264_priv_tbl, cabac_table); - writel_relaxed(priv_start_addr + offset, - rkvdec->regs + RKVDEC_REG_CABACTBL_PROB_BASE); - - /* config output base address */ - dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - writel_relaxed(dst_addr, rkvdec->regs + RKVDEC_REG_DECOUT_BASE); - - reg = RKVDEC_Y_VIRSTRIDE(y_virstride / 16); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_Y_VIRSTRIDE); - - reg = RKVDEC_YUV_VIRSTRIDE(yuv_virstride / 16); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_YUV_VIRSTRIDE); - - /* config ref pic address & poc */ - for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { - struct vb2_buffer *vb_buf = run->ref_buf[i]; - - /* - * If a DPB entry is unused or invalid, address of current destination - * buffer is returned. - */ - if (!vb_buf) - vb_buf = &dst_buf->vb2_buf; - refer_addr = vb2_dma_contig_plane_dma_addr(vb_buf, 0); - - if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) - refer_addr |= RKVDEC_COLMV_USED_FLAG_REF; - if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD) - refer_addr |= RKVDEC_FIELD_REF; - - if (dpb[i].fields & V4L2_H264_TOP_FIELD_REF) - refer_addr |= RKVDEC_TOPFIELD_USED_REF; - if (dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF) - refer_addr |= RKVDEC_BOTFIELD_USED_REF; - - writel_relaxed(dpb[i].top_field_order_cnt, - rkvdec->regs + poc_reg_tbl_top_field[i]); - writel_relaxed(dpb[i].bottom_field_order_cnt, - rkvdec->regs + poc_reg_tbl_bottom_field[i]); - - if (i < V4L2_H264_NUM_DPB_ENTRIES - 1) - writel_relaxed(refer_addr, - rkvdec->regs + RKVDEC_REG_H264_BASE_REFER(i)); - else - writel_relaxed(refer_addr, - rkvdec->regs + RKVDEC_REG_H264_BASE_REFER15); - } - - reg = RKVDEC_CUR_POC(dec_params->top_field_order_cnt); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_CUR_POC0); - - reg = RKVDEC_CUR_POC(dec_params->bottom_field_order_cnt); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_CUR_POC1); - - /* config hw pps address */ - offset = offsetof(struct rkvdec_h264_priv_tbl, param_set); - writel_relaxed(priv_start_addr + offset, - rkvdec->regs + RKVDEC_REG_PPS_BASE); - - /* config hw rps address */ - offset = offsetof(struct rkvdec_h264_priv_tbl, rps); - writel_relaxed(priv_start_addr + offset, - rkvdec->regs + RKVDEC_REG_RPS_BASE); - - reg = RKVDEC_AXI_DDR_RDATA(0); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_RDATA); - - reg = RKVDEC_AXI_DDR_WDATA(0); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_WDATA); - - offset = offsetof(struct rkvdec_h264_priv_tbl, err_info); - writel_relaxed(priv_start_addr + offset, - rkvdec->regs + RKVDEC_REG_H264_ERRINFO_BASE); -} - -#define RKVDEC_H264_MAX_DEPTH_IN_BYTES 2 - -static int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx, - struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp; - - fmt->num_planes = 1; - if (!fmt->plane_fmt[0].sizeimage) - fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height * - RKVDEC_H264_MAX_DEPTH_IN_BYTES; - return 0; -} - -static enum rkvdec_image_fmt rkvdec_h264_get_image_fmt(struct rkvdec_ctx *ctx, - struct v4l2_ctrl *ctrl) -{ - const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; - - if (ctrl->id != V4L2_CID_STATELESS_H264_SPS) - return RKVDEC_IMG_FMT_ANY; - - if (sps->bit_depth_luma_minus8 == 0) { - if (sps->chroma_format_idc == 2) - return RKVDEC_IMG_FMT_422_8BIT; - else - return RKVDEC_IMG_FMT_420_8BIT; - } else if (sps->bit_depth_luma_minus8 == 2) { - if (sps->chroma_format_idc == 2) - return RKVDEC_IMG_FMT_422_10BIT; - else - return RKVDEC_IMG_FMT_420_10BIT; - } - - return RKVDEC_IMG_FMT_ANY; -} - -static int rkvdec_h264_validate_sps(struct rkvdec_ctx *ctx, - const struct v4l2_ctrl_h264_sps *sps) -{ - unsigned int width, height; - - if (sps->chroma_format_idc > 2) - /* Only 4:0:0, 4:2:0 and 4:2:2 are supported */ - return -EINVAL; - if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) - /* Luma and chroma bit depth mismatch */ - return -EINVAL; - if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2) - /* Only 8-bit and 10-bit is supported */ - return -EINVAL; - - width = (sps->pic_width_in_mbs_minus1 + 1) * 16; - height = (sps->pic_height_in_map_units_minus1 + 1) * 16; - - /* - * When frame_mbs_only_flag is not set, this is field height, - * which is half the final height (see (7-18) in the - * specification) - */ - if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)) - height *= 2; - - if (width > ctx->coded_fmt.fmt.pix_mp.width || - height > ctx->coded_fmt.fmt.pix_mp.height) - return -EINVAL; - - return 0; -} - -static int rkvdec_h264_start(struct rkvdec_ctx *ctx) -{ - struct rkvdec_dev *rkvdec = ctx->dev; - struct rkvdec_h264_priv_tbl *priv_tbl; - struct rkvdec_h264_ctx *h264_ctx; - struct v4l2_ctrl *ctrl; - int ret; - - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_STATELESS_H264_SPS); - if (!ctrl) - return -EINVAL; - - ret = rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps); - if (ret) - return ret; - - h264_ctx = kzalloc(sizeof(*h264_ctx), GFP_KERNEL); - if (!h264_ctx) - return -ENOMEM; - - priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl), - &h264_ctx->priv_tbl.dma, GFP_KERNEL); - if (!priv_tbl) { - ret = -ENOMEM; - goto err_free_ctx; - } - - h264_ctx->priv_tbl.size = sizeof(*priv_tbl); - h264_ctx->priv_tbl.cpu = priv_tbl; - memcpy(priv_tbl->cabac_table, rkvdec_h264_cabac_table, - sizeof(rkvdec_h264_cabac_table)); - - ctx->priv = h264_ctx; - return 0; - -err_free_ctx: - kfree(h264_ctx); - return ret; -} - -static void rkvdec_h264_stop(struct rkvdec_ctx *ctx) -{ - struct rkvdec_h264_ctx *h264_ctx = ctx->priv; - struct rkvdec_dev *rkvdec = ctx->dev; - - dma_free_coherent(rkvdec->dev, h264_ctx->priv_tbl.size, - h264_ctx->priv_tbl.cpu, h264_ctx->priv_tbl.dma); - kfree(h264_ctx); -} - -static void rkvdec_h264_run_preamble(struct rkvdec_ctx *ctx, - struct rkvdec_h264_run *run) -{ - struct v4l2_ctrl *ctrl; - - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_STATELESS_H264_DECODE_PARAMS); - run->decode_params = ctrl ? ctrl->p_cur.p : NULL; - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_STATELESS_H264_SPS); - run->sps = ctrl ? ctrl->p_cur.p : NULL; - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_STATELESS_H264_PPS); - run->pps = ctrl ? ctrl->p_cur.p : NULL; - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_STATELESS_H264_SCALING_MATRIX); - run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL; - - rkvdec_run_preamble(ctx, &run->base); -} - -static int rkvdec_h264_run(struct rkvdec_ctx *ctx) -{ - struct v4l2_h264_reflist_builder reflist_builder; - struct rkvdec_dev *rkvdec = ctx->dev; - struct rkvdec_h264_ctx *h264_ctx = ctx->priv; - struct rkvdec_h264_run run; - - rkvdec_h264_run_preamble(ctx, &run); - - /* Build the P/B{0,1} ref lists. */ - v4l2_h264_init_reflist_builder(&reflist_builder, run.decode_params, - run.sps, run.decode_params->dpb); - v4l2_h264_build_p_ref_list(&reflist_builder, h264_ctx->reflists.p); - v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0, - h264_ctx->reflists.b1); - - assemble_hw_scaling_list(ctx, &run); - assemble_hw_pps(ctx, &run); - lookup_ref_buf_idx(ctx, &run); - assemble_hw_rps(ctx, &reflist_builder, &run); - config_registers(ctx, &run); - - rkvdec_run_postamble(ctx, &run.base); - - schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000)); - - writel(0, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN); - writel(0, rkvdec->regs + RKVDEC_REG_H264_ERR_E); - writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND); - writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND); - - /* Start decoding! */ - writel(RKVDEC_INTERRUPT_DEC_E | RKVDEC_CONFIG_DEC_CLK_GATE_E | - RKVDEC_TIMEOUT_E | RKVDEC_BUF_EMPTY_E, - rkvdec->regs + RKVDEC_REG_INTERRUPT); - - return 0; -} - -static int rkvdec_h264_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl) -{ - if (ctrl->id == V4L2_CID_STATELESS_H264_SPS) - return rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps); - - return 0; -} - -const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops = { - .adjust_fmt = rkvdec_h264_adjust_fmt, - .start = rkvdec_h264_start, - .stop = rkvdec_h264_stop, - .run = rkvdec_h264_run, - .try_ctrl = rkvdec_h264_try_ctrl, - .get_image_fmt = rkvdec_h264_get_image_fmt, -}; diff --git a/drivers/staging/media/rkvdec/rkvdec-regs.h b/drivers/staging/media/rkvdec/rkvdec-regs.h deleted file mode 100644 index 15b9bee92016..000000000000 --- a/drivers/staging/media/rkvdec/rkvdec-regs.h +++ /dev/null @@ -1,223 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#ifndef RKVDEC_REGS_H_ -#define RKVDEC_REGS_H_ - -/* rkvcodec registers */ -#define RKVDEC_REG_INTERRUPT 0x004 -#define RKVDEC_INTERRUPT_DEC_E BIT(0) -#define RKVDEC_CONFIG_DEC_CLK_GATE_E BIT(1) -#define RKVDEC_E_STRMD_CLKGATE_DIS BIT(2) -#define RKVDEC_TIMEOUT_MODE BIT(3) -#define RKVDEC_IRQ_DIS BIT(4) -#define RKVDEC_TIMEOUT_E BIT(5) -#define RKVDEC_BUF_EMPTY_E BIT(6) -#define RKVDEC_STRM_E_WAITDECFIFO_EMPTY BIT(7) -#define RKVDEC_IRQ BIT(8) -#define RKVDEC_IRQ_RAW BIT(9) -#define RKVDEC_E_REWRITE_VALID BIT(10) -#define RKVDEC_COMMONIRQ_MODE BIT(11) -#define RKVDEC_RDY_STA BIT(12) -#define RKVDEC_BUS_STA BIT(13) -#define RKVDEC_ERR_STA BIT(14) -#define RKVDEC_TIMEOUT_STA BIT(15) -#define RKVDEC_BUF_EMPTY_STA BIT(16) -#define RKVDEC_COLMV_REF_ERR_STA BIT(17) -#define RKVDEC_CABU_END_STA BIT(18) -#define RKVDEC_H264ORVP9_ERR_MODE BIT(19) -#define RKVDEC_SOFTRST_EN_P BIT(20) -#define RKVDEC_FORCE_SOFTRESET_VALID BIT(21) -#define RKVDEC_SOFTRESET_RDY BIT(22) - -#define RKVDEC_REG_SYSCTRL 0x008 -#define RKVDEC_IN_ENDIAN BIT(0) -#define RKVDEC_IN_SWAP32_E BIT(1) -#define RKVDEC_IN_SWAP64_E BIT(2) -#define RKVDEC_STR_ENDIAN BIT(3) -#define RKVDEC_STR_SWAP32_E BIT(4) -#define RKVDEC_STR_SWAP64_E BIT(5) -#define RKVDEC_OUT_ENDIAN BIT(6) -#define RKVDEC_OUT_SWAP32_E BIT(7) -#define RKVDEC_OUT_CBCR_SWAP BIT(8) -#define RKVDEC_RLC_MODE_DIRECT_WRITE BIT(10) -#define RKVDEC_RLC_MODE BIT(11) -#define RKVDEC_STRM_START_BIT(x) (((x) & 0x7f) << 12) -#define RKVDEC_MODE(x) (((x) & 0x03) << 20) -#define RKVDEC_MODE_H264 1 -#define RKVDEC_MODE_VP9 2 -#define RKVDEC_RPS_MODE BIT(24) -#define RKVDEC_STRM_MODE BIT(25) -#define RKVDEC_H264_STRM_LASTPKT BIT(26) -#define RKVDEC_H264_FIRSTSLICE_FLAG BIT(27) -#define RKVDEC_H264_FRAME_ORSLICE BIT(28) -#define RKVDEC_BUSPR_SLOT_DIS BIT(29) - -#define RKVDEC_REG_PICPAR 0x00C -#define RKVDEC_Y_HOR_VIRSTRIDE(x) ((x) & 0x1ff) -#define RKVDEC_SLICE_NUM_HIGHBIT BIT(11) -#define RKVDEC_UV_HOR_VIRSTRIDE(x) (((x) & 0x1ff) << 12) -#define RKVDEC_SLICE_NUM_LOWBITS(x) (((x) & 0x7ff) << 21) - -#define RKVDEC_REG_STRM_RLC_BASE 0x010 - -#define RKVDEC_REG_STRM_LEN 0x014 -#define RKVDEC_STRM_LEN(x) ((x) & 0x7ffffff) - -#define RKVDEC_REG_CABACTBL_PROB_BASE 0x018 -#define RKVDEC_REG_DECOUT_BASE 0x01C - -#define RKVDEC_REG_Y_VIRSTRIDE 0x020 -#define RKVDEC_Y_VIRSTRIDE(x) ((x) & 0xfffff) - -#define RKVDEC_REG_YUV_VIRSTRIDE 0x024 -#define RKVDEC_YUV_VIRSTRIDE(x) ((x) & 0x1fffff) -#define RKVDEC_REG_H264_BASE_REFER(i) (((i) * 0x04) + 0x028) - -#define RKVDEC_REG_H264_BASE_REFER15 0x0C0 -#define RKVDEC_FIELD_REF BIT(0) -#define RKVDEC_TOPFIELD_USED_REF BIT(1) -#define RKVDEC_BOTFIELD_USED_REF BIT(2) -#define RKVDEC_COLMV_USED_FLAG_REF BIT(3) - -#define RKVDEC_REG_VP9_LAST_FRAME_BASE 0x02c -#define RKVDEC_REG_VP9_GOLDEN_FRAME_BASE 0x030 -#define RKVDEC_REG_VP9_ALTREF_FRAME_BASE 0x034 - -#define RKVDEC_REG_VP9_CPRHEADER_OFFSET 0x028 -#define RKVDEC_VP9_CPRHEADER_OFFSET(x) ((x) & 0xffff) - -#define RKVDEC_REG_VP9_REFERLAST_BASE 0x02C -#define RKVDEC_REG_VP9_REFERGOLDEN_BASE 0x030 -#define RKVDEC_REG_VP9_REFERALFTER_BASE 0x034 - -#define RKVDEC_REG_VP9COUNT_BASE 0x038 -#define RKVDEC_VP9COUNT_UPDATE_EN BIT(0) - -#define RKVDEC_REG_VP9_SEGIDLAST_BASE 0x03C -#define RKVDEC_REG_VP9_SEGIDCUR_BASE 0x040 -#define RKVDEC_REG_VP9_FRAME_SIZE(i) ((i) * 0x04 + 0x044) -#define RKVDEC_VP9_FRAMEWIDTH(x) (((x) & 0xffff) << 0) -#define RKVDEC_VP9_FRAMEHEIGHT(x) (((x) & 0xffff) << 16) - -#define RKVDEC_VP9_SEGID_GRP(i) ((i) * 0x04 + 0x050) -#define RKVDEC_SEGID_ABS_DELTA(x) ((x) & 0x1) -#define RKVDEC_SEGID_FRAME_QP_DELTA_EN(x) (((x) & 0x1) << 1) -#define RKVDEC_SEGID_FRAME_QP_DELTA(x) (((x) & 0x1ff) << 2) -#define RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE_EN(x) (((x) & 0x1) << 11) -#define RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE(x) (((x) & 0x7f) << 12) -#define RKVDEC_SEGID_REFERINFO_EN(x) (((x) & 0x1) << 19) -#define RKVDEC_SEGID_REFERINFO(x) (((x) & 0x03) << 20) -#define RKVDEC_SEGID_FRAME_SKIP_EN(x) (((x) & 0x1) << 22) - -#define RKVDEC_VP9_CPRHEADER_CONFIG 0x070 -#define RKVDEC_VP9_TX_MODE(x) ((x) & 0x07) -#define RKVDEC_VP9_FRAME_REF_MODE(x) (((x) & 0x03) << 3) - -#define RKVDEC_VP9_REF_SCALE(i) ((i) * 0x04 + 0x074) -#define RKVDEC_VP9_REF_HOR_SCALE(x) ((x) & 0xffff) -#define RKVDEC_VP9_REF_VER_SCALE(x) (((x) & 0xffff) << 16) - -#define RKVDEC_VP9_REF_DELTAS_LASTFRAME 0x080 -#define RKVDEC_REF_DELTAS_LASTFRAME(pos, val) (((val) & 0x7f) << ((pos) * 7)) - -#define RKVDEC_VP9_INFO_LASTFRAME 0x084 -#define RKVDEC_MODE_DELTAS_LASTFRAME(pos, val) (((val) & 0x7f) << ((pos) * 7)) -#define RKVDEC_SEG_EN_LASTFRAME BIT(16) -#define RKVDEC_LAST_SHOW_FRAME BIT(17) -#define RKVDEC_LAST_INTRA_ONLY BIT(18) -#define RKVDEC_LAST_WIDHHEIGHT_EQCUR BIT(19) -#define RKVDEC_COLOR_SPACE_LASTKEYFRAME(x) (((x) & 0x07) << 20) - -#define RKVDEC_VP9_INTERCMD_BASE 0x088 - -#define RKVDEC_VP9_INTERCMD_NUM 0x08C -#define RKVDEC_INTERCMD_NUM(x) ((x) & 0xffffff) - -#define RKVDEC_VP9_LASTTILE_SIZE 0x090 -#define RKVDEC_LASTTILE_SIZE(x) ((x) & 0xffffff) - -#define RKVDEC_VP9_HOR_VIRSTRIDE(i) ((i) * 0x04 + 0x094) -#define RKVDEC_HOR_Y_VIRSTRIDE(x) ((x) & 0x1ff) -#define RKVDEC_HOR_UV_VIRSTRIDE(x) (((x) & 0x1ff) << 16) - -#define RKVDEC_REG_H264_POC_REFER0(i) (((i) * 0x04) + 0x064) -#define RKVDEC_REG_H264_POC_REFER1(i) (((i) * 0x04) + 0x0C4) -#define RKVDEC_REG_H264_POC_REFER2(i) (((i) * 0x04) + 0x120) -#define RKVDEC_POC_REFER(x) ((x) & 0xffffffff) - -#define RKVDEC_REG_CUR_POC0 0x0A0 -#define RKVDEC_REG_CUR_POC1 0x128 -#define RKVDEC_CUR_POC(x) ((x) & 0xffffffff) - -#define RKVDEC_REG_RLCWRITE_BASE 0x0A4 -#define RKVDEC_REG_PPS_BASE 0x0A8 -#define RKVDEC_REG_RPS_BASE 0x0AC - -#define RKVDEC_REG_STRMD_ERR_EN 0x0B0 -#define RKVDEC_STRMD_ERR_EN(x) ((x) & 0xffffffff) - -#define RKVDEC_REG_STRMD_ERR_STA 0x0B4 -#define RKVDEC_STRMD_ERR_STA(x) ((x) & 0xfffffff) -#define RKVDEC_COLMV_ERR_REF_PICIDX(x) (((x) & 0x0f) << 28) - -#define RKVDEC_REG_STRMD_ERR_CTU 0x0B8 -#define RKVDEC_STRMD_ERR_CTU(x) ((x) & 0xff) -#define RKVDEC_STRMD_ERR_CTU_YOFFSET(x) (((x) & 0xff) << 8) -#define RKVDEC_STRMFIFO_SPACE2FULL(x) (((x) & 0x7f) << 16) -#define RKVDEC_VP9_ERR_EN_CTU0 BIT(24) - -#define RKVDEC_REG_SAO_CTU_POS 0x0BC -#define RKVDEC_SAOWR_XOFFSET(x) ((x) & 0x1ff) -#define RKVDEC_SAOWR_YOFFSET(x) (((x) & 0x3ff) << 16) - -#define RKVDEC_VP9_LAST_FRAME_YSTRIDE 0x0C0 -#define RKVDEC_VP9_GOLDEN_FRAME_YSTRIDE 0x0C4 -#define RKVDEC_VP9_ALTREF_FRAME_YSTRIDE 0x0C8 -#define RKVDEC_VP9_REF_YSTRIDE(x) (((x) & 0xfffff) << 0) - -#define RKVDEC_VP9_LAST_FRAME_YUVSTRIDE 0x0CC -#define RKVDEC_VP9_REF_YUVSTRIDE(x) (((x) & 0x1fffff) << 0) - -#define RKVDEC_VP9_REF_COLMV_BASE 0x0D0 - -#define RKVDEC_REG_PERFORMANCE_CYCLE 0x100 -#define RKVDEC_PERFORMANCE_CYCLE(x) ((x) & 0xffffffff) - -#define RKVDEC_REG_AXI_DDR_RDATA 0x104 -#define RKVDEC_AXI_DDR_RDATA(x) ((x) & 0xffffffff) - -#define RKVDEC_REG_AXI_DDR_WDATA 0x108 -#define RKVDEC_AXI_DDR_WDATA(x) ((x) & 0xffffffff) - -#define RKVDEC_REG_FPGADEBUG_RESET 0x10C -#define RKVDEC_BUSIFD_RESETN BIT(0) -#define RKVDEC_CABAC_RESETN BIT(1) -#define RKVDEC_DEC_CTRL_RESETN BIT(2) -#define RKVDEC_TRANSD_RESETN BIT(3) -#define RKVDEC_INTRA_RESETN BIT(4) -#define RKVDEC_INTER_RESETN BIT(5) -#define RKVDEC_RECON_RESETN BIT(6) -#define RKVDEC_FILER_RESETN BIT(7) - -#define RKVDEC_REG_PERFORMANCE_SEL 0x110 -#define RKVDEC_PERF_SEL_CNT0(x) ((x) & 0x3f) -#define RKVDEC_PERF_SEL_CNT1(x) (((x) & 0x3f) << 8) -#define RKVDEC_PERF_SEL_CNT2(x) (((x) & 0x3f) << 16) - -#define RKVDEC_REG_PERFORMANCE_CNT(i) ((i) * 0x04 + 0x114) -#define RKVDEC_PERF_CNT(x) ((x) & 0xffffffff) - -#define RKVDEC_REG_H264_ERRINFO_BASE 0x12C - -#define RKVDEC_REG_H264_ERRINFO_NUM 0x130 -#define RKVDEC_SLICEDEC_NUM(x) ((x) & 0x3fff) -#define RKVDEC_STRMD_DECT_ERR_FLAG BIT(15) -#define RKVDEC_ERR_PKT_NUM(x) (((x) & 0x3fff) << 16) - -#define RKVDEC_REG_H264_ERR_E 0x134 -#define RKVDEC_H264_ERR_EN_HIGHBITS(x) ((x) & 0x3fffffff) - -#define RKVDEC_REG_PREF_LUMA_CACHE_COMMAND 0x410 -#define RKVDEC_REG_PREF_CHR_CACHE_COMMAND 0x450 - -#endif /* RKVDEC_REGS_H_ */ diff --git a/drivers/staging/media/rkvdec/rkvdec-vp9.c b/drivers/staging/media/rkvdec/rkvdec-vp9.c deleted file mode 100644 index 0e7e16f20eeb..000000000000 --- a/drivers/staging/media/rkvdec/rkvdec-vp9.c +++ /dev/null @@ -1,1072 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Rockchip Video Decoder VP9 backend - * - * Copyright (C) 2019 Collabora, Ltd. - * Boris Brezillon - * Copyright (C) 2021 Collabora, Ltd. - * Andrzej Pietrasiewicz - * - * Copyright (C) 2016 Rockchip Electronics Co., Ltd. - * Alpha Lin - */ - -/* - * For following the vp9 spec please start reading this driver - * code from rkvdec_vp9_run() followed by rkvdec_vp9_done(). - */ - -#include -#include -#include -#include - -#include "rkvdec.h" -#include "rkvdec-regs.h" - -#define RKVDEC_VP9_PROBE_SIZE 4864 -#define RKVDEC_VP9_COUNT_SIZE 13232 -#define RKVDEC_VP9_MAX_SEGMAP_SIZE 73728 - -struct rkvdec_vp9_intra_mode_probs { - u8 y_mode[105]; - u8 uv_mode[23]; -}; - -struct rkvdec_vp9_intra_only_frame_probs { - u8 coef_intra[4][2][128]; - struct rkvdec_vp9_intra_mode_probs intra_mode[10]; -}; - -struct rkvdec_vp9_inter_frame_probs { - u8 y_mode[4][9]; - u8 comp_mode[5]; - u8 comp_ref[5]; - u8 single_ref[5][2]; - u8 inter_mode[7][3]; - u8 interp_filter[4][2]; - u8 padding0[11]; - u8 coef[2][4][2][128]; - u8 uv_mode_0_2[3][9]; - u8 padding1[5]; - u8 uv_mode_3_5[3][9]; - u8 padding2[5]; - u8 uv_mode_6_8[3][9]; - u8 padding3[5]; - u8 uv_mode_9[9]; - u8 padding4[7]; - u8 padding5[16]; - struct { - u8 joint[3]; - u8 sign[2]; - u8 classes[2][10]; - u8 class0_bit[2]; - u8 bits[2][10]; - u8 class0_fr[2][2][3]; - u8 fr[2][3]; - u8 class0_hp[2]; - u8 hp[2]; - } mv; -}; - -struct rkvdec_vp9_probs { - u8 partition[16][3]; - u8 pred[3]; - u8 tree[7]; - u8 skip[3]; - u8 tx32[2][3]; - u8 tx16[2][2]; - u8 tx8[2][1]; - u8 is_inter[4]; - /* 128 bit alignment */ - u8 padding0[3]; - union { - struct rkvdec_vp9_inter_frame_probs inter; - struct rkvdec_vp9_intra_only_frame_probs intra_only; - }; - /* 128 bit alignment */ - u8 padding1[11]; -}; - -/* Data structure describing auxiliary buffer format. */ -struct rkvdec_vp9_priv_tbl { - struct rkvdec_vp9_probs probs; - u8 segmap[2][RKVDEC_VP9_MAX_SEGMAP_SIZE]; -}; - -struct rkvdec_vp9_refs_counts { - u32 eob[2]; - u32 coeff[3]; -}; - -struct rkvdec_vp9_inter_frame_symbol_counts { - u32 partition[16][4]; - u32 skip[3][2]; - u32 inter[4][2]; - u32 tx32p[2][4]; - u32 tx16p[2][4]; - u32 tx8p[2][2]; - u32 y_mode[4][10]; - u32 uv_mode[10][10]; - u32 comp[5][2]; - u32 comp_ref[5][2]; - u32 single_ref[5][2][2]; - u32 mv_mode[7][4]; - u32 filter[4][3]; - u32 mv_joint[4]; - u32 sign[2][2]; - /* add 1 element for align */ - u32 classes[2][11 + 1]; - u32 class0[2][2]; - u32 bits[2][10][2]; - u32 class0_fp[2][2][4]; - u32 fp[2][4]; - u32 class0_hp[2][2]; - u32 hp[2][2]; - struct rkvdec_vp9_refs_counts ref_cnt[2][4][2][6][6]; -}; - -struct rkvdec_vp9_intra_frame_symbol_counts { - u32 partition[4][4][4]; - u32 skip[3][2]; - u32 intra[4][2]; - u32 tx32p[2][4]; - u32 tx16p[2][4]; - u32 tx8p[2][2]; - struct rkvdec_vp9_refs_counts ref_cnt[2][4][2][6][6]; -}; - -struct rkvdec_vp9_run { - struct rkvdec_run base; - const struct v4l2_ctrl_vp9_frame *decode_params; -}; - -struct rkvdec_vp9_frame_info { - u32 valid : 1; - u32 segmapid : 1; - u32 frame_context_idx : 2; - u32 reference_mode : 2; - u32 tx_mode : 3; - u32 interpolation_filter : 3; - u32 flags; - u64 timestamp; - struct v4l2_vp9_segmentation seg; - struct v4l2_vp9_loop_filter lf; -}; - -struct rkvdec_vp9_ctx { - struct rkvdec_aux_buf priv_tbl; - struct rkvdec_aux_buf count_tbl; - struct v4l2_vp9_frame_symbol_counts inter_cnts; - struct v4l2_vp9_frame_symbol_counts intra_cnts; - struct v4l2_vp9_frame_context probability_tables; - struct v4l2_vp9_frame_context frame_context[4]; - struct rkvdec_vp9_frame_info cur; - struct rkvdec_vp9_frame_info last; -}; - -static void write_coeff_plane(const u8 coef[6][6][3], u8 *coeff_plane) -{ - unsigned int idx = 0, byte_count = 0; - int k, m, n; - u8 p; - - for (k = 0; k < 6; k++) { - for (m = 0; m < 6; m++) { - for (n = 0; n < 3; n++) { - p = coef[k][m][n]; - coeff_plane[idx++] = p; - byte_count++; - if (byte_count == 27) { - idx += 5; - byte_count = 0; - } - } - } - } -} - -static void init_intra_only_probs(struct rkvdec_ctx *ctx, - const struct rkvdec_vp9_run *run) -{ - struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; - struct rkvdec_vp9_priv_tbl *tbl = vp9_ctx->priv_tbl.cpu; - struct rkvdec_vp9_intra_only_frame_probs *rkprobs; - const struct v4l2_vp9_frame_context *probs; - unsigned int i, j, k; - - rkprobs = &tbl->probs.intra_only; - probs = &vp9_ctx->probability_tables; - - /* - * intra only 149 x 128 bits ,aligned to 152 x 128 bits coeff related - * prob 64 x 128 bits - */ - for (i = 0; i < ARRAY_SIZE(probs->coef); i++) { - for (j = 0; j < ARRAY_SIZE(probs->coef[0]); j++) - write_coeff_plane(probs->coef[i][j][0], - rkprobs->coef_intra[i][j]); - } - - /* intra mode prob 80 x 128 bits */ - for (i = 0; i < ARRAY_SIZE(v4l2_vp9_kf_y_mode_prob); i++) { - unsigned int byte_count = 0; - int idx = 0; - - /* vp9_kf_y_mode_prob */ - for (j = 0; j < ARRAY_SIZE(v4l2_vp9_kf_y_mode_prob[0]); j++) { - for (k = 0; k < ARRAY_SIZE(v4l2_vp9_kf_y_mode_prob[0][0]); - k++) { - u8 val = v4l2_vp9_kf_y_mode_prob[i][j][k]; - - rkprobs->intra_mode[i].y_mode[idx++] = val; - byte_count++; - if (byte_count == 27) { - byte_count = 0; - idx += 5; - } - } - } - } - - for (i = 0; i < sizeof(v4l2_vp9_kf_uv_mode_prob); ++i) { - const u8 *ptr = (const u8 *)v4l2_vp9_kf_uv_mode_prob; - - rkprobs->intra_mode[i / 23].uv_mode[i % 23] = ptr[i]; - } -} - -static void init_inter_probs(struct rkvdec_ctx *ctx, - const struct rkvdec_vp9_run *run) -{ - struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; - struct rkvdec_vp9_priv_tbl *tbl = vp9_ctx->priv_tbl.cpu; - struct rkvdec_vp9_inter_frame_probs *rkprobs; - const struct v4l2_vp9_frame_context *probs; - unsigned int i, j, k; - - rkprobs = &tbl->probs.inter; - probs = &vp9_ctx->probability_tables; - - /* - * inter probs - * 151 x 128 bits, aligned to 152 x 128 bits - * inter only - * intra_y_mode & inter_block info 6 x 128 bits - */ - - memcpy(rkprobs->y_mode, probs->y_mode, sizeof(rkprobs->y_mode)); - memcpy(rkprobs->comp_mode, probs->comp_mode, - sizeof(rkprobs->comp_mode)); - memcpy(rkprobs->comp_ref, probs->comp_ref, - sizeof(rkprobs->comp_ref)); - memcpy(rkprobs->single_ref, probs->single_ref, - sizeof(rkprobs->single_ref)); - memcpy(rkprobs->inter_mode, probs->inter_mode, - sizeof(rkprobs->inter_mode)); - memcpy(rkprobs->interp_filter, probs->interp_filter, - sizeof(rkprobs->interp_filter)); - - /* 128 x 128 bits coeff related */ - for (i = 0; i < ARRAY_SIZE(probs->coef); i++) { - for (j = 0; j < ARRAY_SIZE(probs->coef[0]); j++) { - for (k = 0; k < ARRAY_SIZE(probs->coef[0][0]); k++) - write_coeff_plane(probs->coef[i][j][k], - rkprobs->coef[k][i][j]); - } - } - - /* intra uv mode 6 x 128 */ - memcpy(rkprobs->uv_mode_0_2, &probs->uv_mode[0], - sizeof(rkprobs->uv_mode_0_2)); - memcpy(rkprobs->uv_mode_3_5, &probs->uv_mode[3], - sizeof(rkprobs->uv_mode_3_5)); - memcpy(rkprobs->uv_mode_6_8, &probs->uv_mode[6], - sizeof(rkprobs->uv_mode_6_8)); - memcpy(rkprobs->uv_mode_9, &probs->uv_mode[9], - sizeof(rkprobs->uv_mode_9)); - - /* mv related 6 x 128 */ - memcpy(rkprobs->mv.joint, probs->mv.joint, - sizeof(rkprobs->mv.joint)); - memcpy(rkprobs->mv.sign, probs->mv.sign, - sizeof(rkprobs->mv.sign)); - memcpy(rkprobs->mv.classes, probs->mv.classes, - sizeof(rkprobs->mv.classes)); - memcpy(rkprobs->mv.class0_bit, probs->mv.class0_bit, - sizeof(rkprobs->mv.class0_bit)); - memcpy(rkprobs->mv.bits, probs->mv.bits, - sizeof(rkprobs->mv.bits)); - memcpy(rkprobs->mv.class0_fr, probs->mv.class0_fr, - sizeof(rkprobs->mv.class0_fr)); - memcpy(rkprobs->mv.fr, probs->mv.fr, - sizeof(rkprobs->mv.fr)); - memcpy(rkprobs->mv.class0_hp, probs->mv.class0_hp, - sizeof(rkprobs->mv.class0_hp)); - memcpy(rkprobs->mv.hp, probs->mv.hp, - sizeof(rkprobs->mv.hp)); -} - -static void init_probs(struct rkvdec_ctx *ctx, - const struct rkvdec_vp9_run *run) -{ - const struct v4l2_ctrl_vp9_frame *dec_params; - struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; - struct rkvdec_vp9_priv_tbl *tbl = vp9_ctx->priv_tbl.cpu; - struct rkvdec_vp9_probs *rkprobs = &tbl->probs; - const struct v4l2_vp9_segmentation *seg; - const struct v4l2_vp9_frame_context *probs; - bool intra_only; - - dec_params = run->decode_params; - probs = &vp9_ctx->probability_tables; - seg = &dec_params->seg; - - memset(rkprobs, 0, sizeof(*rkprobs)); - - intra_only = !!(dec_params->flags & - (V4L2_VP9_FRAME_FLAG_KEY_FRAME | - V4L2_VP9_FRAME_FLAG_INTRA_ONLY)); - - /* sb info 5 x 128 bit */ - memcpy(rkprobs->partition, - intra_only ? v4l2_vp9_kf_partition_probs : probs->partition, - sizeof(rkprobs->partition)); - - memcpy(rkprobs->pred, seg->pred_probs, sizeof(rkprobs->pred)); - memcpy(rkprobs->tree, seg->tree_probs, sizeof(rkprobs->tree)); - memcpy(rkprobs->skip, probs->skip, sizeof(rkprobs->skip)); - memcpy(rkprobs->tx32, probs->tx32, sizeof(rkprobs->tx32)); - memcpy(rkprobs->tx16, probs->tx16, sizeof(rkprobs->tx16)); - memcpy(rkprobs->tx8, probs->tx8, sizeof(rkprobs->tx8)); - memcpy(rkprobs->is_inter, probs->is_inter, sizeof(rkprobs->is_inter)); - - if (intra_only) - init_intra_only_probs(ctx, run); - else - init_inter_probs(ctx, run); -} - -struct rkvdec_vp9_ref_reg { - u32 reg_frm_size; - u32 reg_hor_stride; - u32 reg_y_stride; - u32 reg_yuv_stride; - u32 reg_ref_base; -}; - -static struct rkvdec_vp9_ref_reg ref_regs[] = { - { - .reg_frm_size = RKVDEC_REG_VP9_FRAME_SIZE(0), - .reg_hor_stride = RKVDEC_VP9_HOR_VIRSTRIDE(0), - .reg_y_stride = RKVDEC_VP9_LAST_FRAME_YSTRIDE, - .reg_yuv_stride = RKVDEC_VP9_LAST_FRAME_YUVSTRIDE, - .reg_ref_base = RKVDEC_REG_VP9_LAST_FRAME_BASE, - }, - { - .reg_frm_size = RKVDEC_REG_VP9_FRAME_SIZE(1), - .reg_hor_stride = RKVDEC_VP9_HOR_VIRSTRIDE(1), - .reg_y_stride = RKVDEC_VP9_GOLDEN_FRAME_YSTRIDE, - .reg_yuv_stride = 0, - .reg_ref_base = RKVDEC_REG_VP9_GOLDEN_FRAME_BASE, - }, - { - .reg_frm_size = RKVDEC_REG_VP9_FRAME_SIZE(2), - .reg_hor_stride = RKVDEC_VP9_HOR_VIRSTRIDE(2), - .reg_y_stride = RKVDEC_VP9_ALTREF_FRAME_YSTRIDE, - .reg_yuv_stride = 0, - .reg_ref_base = RKVDEC_REG_VP9_ALTREF_FRAME_BASE, - } -}; - -static struct rkvdec_decoded_buffer * -get_ref_buf(struct rkvdec_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp) -{ - struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; - struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q; - struct vb2_buffer *buf; - - /* - * If a ref is unused or invalid, address of current destination - * buffer is returned. - */ - buf = vb2_find_buffer(cap_q, timestamp); - if (!buf) - buf = &dst->vb2_buf; - - return vb2_to_rkvdec_decoded_buf(buf); -} - -static dma_addr_t get_mv_base_addr(struct rkvdec_decoded_buffer *buf) -{ - unsigned int aligned_pitch, aligned_height, yuv_len; - - aligned_height = round_up(buf->vp9.height, 64); - aligned_pitch = round_up(buf->vp9.width * buf->vp9.bit_depth, 512) / 8; - yuv_len = (aligned_height * aligned_pitch * 3) / 2; - - return vb2_dma_contig_plane_dma_addr(&buf->base.vb.vb2_buf, 0) + - yuv_len; -} - -static void config_ref_registers(struct rkvdec_ctx *ctx, - const struct rkvdec_vp9_run *run, - struct rkvdec_decoded_buffer *ref_buf, - struct rkvdec_vp9_ref_reg *ref_reg) -{ - unsigned int aligned_pitch, aligned_height, y_len, yuv_len; - struct rkvdec_dev *rkvdec = ctx->dev; - - aligned_height = round_up(ref_buf->vp9.height, 64); - writel_relaxed(RKVDEC_VP9_FRAMEWIDTH(ref_buf->vp9.width) | - RKVDEC_VP9_FRAMEHEIGHT(ref_buf->vp9.height), - rkvdec->regs + ref_reg->reg_frm_size); - - writel_relaxed(vb2_dma_contig_plane_dma_addr(&ref_buf->base.vb.vb2_buf, 0), - rkvdec->regs + ref_reg->reg_ref_base); - - if (&ref_buf->base.vb == run->base.bufs.dst) - return; - - aligned_pitch = round_up(ref_buf->vp9.width * ref_buf->vp9.bit_depth, 512) / 8; - y_len = aligned_height * aligned_pitch; - yuv_len = (y_len * 3) / 2; - - writel_relaxed(RKVDEC_HOR_Y_VIRSTRIDE(aligned_pitch / 16) | - RKVDEC_HOR_UV_VIRSTRIDE(aligned_pitch / 16), - rkvdec->regs + ref_reg->reg_hor_stride); - writel_relaxed(RKVDEC_VP9_REF_YSTRIDE(y_len / 16), - rkvdec->regs + ref_reg->reg_y_stride); - - if (!ref_reg->reg_yuv_stride) - return; - - writel_relaxed(RKVDEC_VP9_REF_YUVSTRIDE(yuv_len / 16), - rkvdec->regs + ref_reg->reg_yuv_stride); -} - -static void config_seg_registers(struct rkvdec_ctx *ctx, unsigned int segid) -{ - struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; - const struct v4l2_vp9_segmentation *seg; - struct rkvdec_dev *rkvdec = ctx->dev; - s16 feature_val; - int feature_id; - u32 val = 0; - - seg = vp9_ctx->last.valid ? &vp9_ctx->last.seg : &vp9_ctx->cur.seg; - feature_id = V4L2_VP9_SEG_LVL_ALT_Q; - if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) { - feature_val = seg->feature_data[segid][feature_id]; - val |= RKVDEC_SEGID_FRAME_QP_DELTA_EN(1) | - RKVDEC_SEGID_FRAME_QP_DELTA(feature_val); - } - - feature_id = V4L2_VP9_SEG_LVL_ALT_L; - if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) { - feature_val = seg->feature_data[segid][feature_id]; - val |= RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE_EN(1) | - RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE(feature_val); - } - - feature_id = V4L2_VP9_SEG_LVL_REF_FRAME; - if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) { - feature_val = seg->feature_data[segid][feature_id]; - val |= RKVDEC_SEGID_REFERINFO_EN(1) | - RKVDEC_SEGID_REFERINFO(feature_val); - } - - feature_id = V4L2_VP9_SEG_LVL_SKIP; - if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) - val |= RKVDEC_SEGID_FRAME_SKIP_EN(1); - - if (!segid && - (seg->flags & V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE)) - val |= RKVDEC_SEGID_ABS_DELTA(1); - - writel_relaxed(val, rkvdec->regs + RKVDEC_VP9_SEGID_GRP(segid)); -} - -static void update_dec_buf_info(struct rkvdec_decoded_buffer *buf, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - buf->vp9.width = dec_params->frame_width_minus_1 + 1; - buf->vp9.height = dec_params->frame_height_minus_1 + 1; - buf->vp9.bit_depth = dec_params->bit_depth; -} - -static void update_ctx_cur_info(struct rkvdec_vp9_ctx *vp9_ctx, - struct rkvdec_decoded_buffer *buf, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - vp9_ctx->cur.valid = true; - vp9_ctx->cur.reference_mode = dec_params->reference_mode; - vp9_ctx->cur.interpolation_filter = dec_params->interpolation_filter; - vp9_ctx->cur.flags = dec_params->flags; - vp9_ctx->cur.timestamp = buf->base.vb.vb2_buf.timestamp; - vp9_ctx->cur.seg = dec_params->seg; - vp9_ctx->cur.lf = dec_params->lf; -} - -static void update_ctx_last_info(struct rkvdec_vp9_ctx *vp9_ctx) -{ - vp9_ctx->last = vp9_ctx->cur; -} - -static void config_registers(struct rkvdec_ctx *ctx, - const struct rkvdec_vp9_run *run) -{ - unsigned int y_len, uv_len, yuv_len, bit_depth, aligned_height, aligned_pitch, stream_len; - const struct v4l2_ctrl_vp9_frame *dec_params; - struct rkvdec_decoded_buffer *ref_bufs[3]; - struct rkvdec_decoded_buffer *dst, *last, *mv_ref; - struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; - u32 val, last_frame_info = 0; - const struct v4l2_vp9_segmentation *seg; - struct rkvdec_dev *rkvdec = ctx->dev; - dma_addr_t addr; - bool intra_only; - unsigned int i; - - dec_params = run->decode_params; - dst = vb2_to_rkvdec_decoded_buf(&run->base.bufs.dst->vb2_buf); - ref_bufs[0] = get_ref_buf(ctx, &dst->base.vb, dec_params->last_frame_ts); - ref_bufs[1] = get_ref_buf(ctx, &dst->base.vb, dec_params->golden_frame_ts); - ref_bufs[2] = get_ref_buf(ctx, &dst->base.vb, dec_params->alt_frame_ts); - - if (vp9_ctx->last.valid) - last = get_ref_buf(ctx, &dst->base.vb, vp9_ctx->last.timestamp); - else - last = dst; - - update_dec_buf_info(dst, dec_params); - update_ctx_cur_info(vp9_ctx, dst, dec_params); - seg = &dec_params->seg; - - intra_only = !!(dec_params->flags & - (V4L2_VP9_FRAME_FLAG_KEY_FRAME | - V4L2_VP9_FRAME_FLAG_INTRA_ONLY)); - - writel_relaxed(RKVDEC_MODE(RKVDEC_MODE_VP9), - rkvdec->regs + RKVDEC_REG_SYSCTRL); - - bit_depth = dec_params->bit_depth; - aligned_height = round_up(ctx->decoded_fmt.fmt.pix_mp.height, 64); - - aligned_pitch = round_up(ctx->decoded_fmt.fmt.pix_mp.width * - bit_depth, - 512) / 8; - y_len = aligned_height * aligned_pitch; - uv_len = y_len / 2; - yuv_len = y_len + uv_len; - - writel_relaxed(RKVDEC_Y_HOR_VIRSTRIDE(aligned_pitch / 16) | - RKVDEC_UV_HOR_VIRSTRIDE(aligned_pitch / 16), - rkvdec->regs + RKVDEC_REG_PICPAR); - writel_relaxed(RKVDEC_Y_VIRSTRIDE(y_len / 16), - rkvdec->regs + RKVDEC_REG_Y_VIRSTRIDE); - writel_relaxed(RKVDEC_YUV_VIRSTRIDE(yuv_len / 16), - rkvdec->regs + RKVDEC_REG_YUV_VIRSTRIDE); - - stream_len = vb2_get_plane_payload(&run->base.bufs.src->vb2_buf, 0); - writel_relaxed(RKVDEC_STRM_LEN(stream_len), - rkvdec->regs + RKVDEC_REG_STRM_LEN); - - /* - * Reset count buffer, because decoder only output intra related syntax - * counts when decoding intra frame, but update entropy need to update - * all the probabilities. - */ - if (intra_only) - memset(vp9_ctx->count_tbl.cpu, 0, vp9_ctx->count_tbl.size); - - vp9_ctx->cur.segmapid = vp9_ctx->last.segmapid; - if (!intra_only && - !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) && - (!(seg->flags & V4L2_VP9_SEGMENTATION_FLAG_ENABLED) || - (seg->flags & V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP))) - vp9_ctx->cur.segmapid++; - - for (i = 0; i < ARRAY_SIZE(ref_bufs); i++) - config_ref_registers(ctx, run, ref_bufs[i], &ref_regs[i]); - - for (i = 0; i < 8; i++) - config_seg_registers(ctx, i); - - writel_relaxed(RKVDEC_VP9_TX_MODE(vp9_ctx->cur.tx_mode) | - RKVDEC_VP9_FRAME_REF_MODE(dec_params->reference_mode), - rkvdec->regs + RKVDEC_VP9_CPRHEADER_CONFIG); - - if (!intra_only) { - const struct v4l2_vp9_loop_filter *lf; - s8 delta; - - if (vp9_ctx->last.valid) - lf = &vp9_ctx->last.lf; - else - lf = &vp9_ctx->cur.lf; - - val = 0; - for (i = 0; i < ARRAY_SIZE(lf->ref_deltas); i++) { - delta = lf->ref_deltas[i]; - val |= RKVDEC_REF_DELTAS_LASTFRAME(i, delta); - } - - writel_relaxed(val, - rkvdec->regs + RKVDEC_VP9_REF_DELTAS_LASTFRAME); - - for (i = 0; i < ARRAY_SIZE(lf->mode_deltas); i++) { - delta = lf->mode_deltas[i]; - last_frame_info |= RKVDEC_MODE_DELTAS_LASTFRAME(i, - delta); - } - } - - if (vp9_ctx->last.valid && !intra_only && - vp9_ctx->last.seg.flags & V4L2_VP9_SEGMENTATION_FLAG_ENABLED) - last_frame_info |= RKVDEC_SEG_EN_LASTFRAME; - - if (vp9_ctx->last.valid && - vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_SHOW_FRAME) - last_frame_info |= RKVDEC_LAST_SHOW_FRAME; - - if (vp9_ctx->last.valid && - vp9_ctx->last.flags & - (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY)) - last_frame_info |= RKVDEC_LAST_INTRA_ONLY; - - if (vp9_ctx->last.valid && - last->vp9.width == dst->vp9.width && - last->vp9.height == dst->vp9.height) - last_frame_info |= RKVDEC_LAST_WIDHHEIGHT_EQCUR; - - writel_relaxed(last_frame_info, - rkvdec->regs + RKVDEC_VP9_INFO_LASTFRAME); - - writel_relaxed(stream_len - dec_params->compressed_header_size - - dec_params->uncompressed_header_size, - rkvdec->regs + RKVDEC_VP9_LASTTILE_SIZE); - - for (i = 0; !intra_only && i < ARRAY_SIZE(ref_bufs); i++) { - unsigned int refw = ref_bufs[i]->vp9.width; - unsigned int refh = ref_bufs[i]->vp9.height; - u32 hscale, vscale; - - hscale = (refw << 14) / dst->vp9.width; - vscale = (refh << 14) / dst->vp9.height; - writel_relaxed(RKVDEC_VP9_REF_HOR_SCALE(hscale) | - RKVDEC_VP9_REF_VER_SCALE(vscale), - rkvdec->regs + RKVDEC_VP9_REF_SCALE(i)); - } - - addr = vb2_dma_contig_plane_dma_addr(&dst->base.vb.vb2_buf, 0); - writel_relaxed(addr, rkvdec->regs + RKVDEC_REG_DECOUT_BASE); - addr = vb2_dma_contig_plane_dma_addr(&run->base.bufs.src->vb2_buf, 0); - writel_relaxed(addr, rkvdec->regs + RKVDEC_REG_STRM_RLC_BASE); - writel_relaxed(vp9_ctx->priv_tbl.dma + - offsetof(struct rkvdec_vp9_priv_tbl, probs), - rkvdec->regs + RKVDEC_REG_CABACTBL_PROB_BASE); - writel_relaxed(vp9_ctx->count_tbl.dma, - rkvdec->regs + RKVDEC_REG_VP9COUNT_BASE); - - writel_relaxed(vp9_ctx->priv_tbl.dma + - offsetof(struct rkvdec_vp9_priv_tbl, segmap) + - (RKVDEC_VP9_MAX_SEGMAP_SIZE * vp9_ctx->cur.segmapid), - rkvdec->regs + RKVDEC_REG_VP9_SEGIDCUR_BASE); - writel_relaxed(vp9_ctx->priv_tbl.dma + - offsetof(struct rkvdec_vp9_priv_tbl, segmap) + - (RKVDEC_VP9_MAX_SEGMAP_SIZE * (!vp9_ctx->cur.segmapid)), - rkvdec->regs + RKVDEC_REG_VP9_SEGIDLAST_BASE); - - if (!intra_only && - !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) && - vp9_ctx->last.valid) - mv_ref = last; - else - mv_ref = dst; - - writel_relaxed(get_mv_base_addr(mv_ref), - rkvdec->regs + RKVDEC_VP9_REF_COLMV_BASE); - - writel_relaxed(ctx->decoded_fmt.fmt.pix_mp.width | - (ctx->decoded_fmt.fmt.pix_mp.height << 16), - rkvdec->regs + RKVDEC_REG_PERFORMANCE_CYCLE); -} - -static int validate_dec_params(struct rkvdec_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - unsigned int aligned_width, aligned_height; - - /* We only support profile 0. */ - if (dec_params->profile != 0) { - dev_err(ctx->dev->dev, "unsupported profile %d\n", - dec_params->profile); - return -EINVAL; - } - - aligned_width = round_up(dec_params->frame_width_minus_1 + 1, 64); - aligned_height = round_up(dec_params->frame_height_minus_1 + 1, 64); - - /* - * Userspace should update the capture/decoded format when the - * resolution changes. - */ - if (aligned_width != ctx->decoded_fmt.fmt.pix_mp.width || - aligned_height != ctx->decoded_fmt.fmt.pix_mp.height) { - dev_err(ctx->dev->dev, - "unexpected bitstream resolution %dx%d\n", - dec_params->frame_width_minus_1 + 1, - dec_params->frame_height_minus_1 + 1); - return -EINVAL; - } - - return 0; -} - -static int rkvdec_vp9_run_preamble(struct rkvdec_ctx *ctx, - struct rkvdec_vp9_run *run) -{ - const struct v4l2_ctrl_vp9_frame *dec_params; - const struct v4l2_ctrl_vp9_compressed_hdr *prob_updates; - struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; - struct v4l2_ctrl *ctrl; - unsigned int fctx_idx; - int ret; - - /* v4l2-specific stuff */ - rkvdec_run_preamble(ctx, &run->base); - - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_STATELESS_VP9_FRAME); - if (WARN_ON(!ctrl)) - return -EINVAL; - dec_params = ctrl->p_cur.p; - - ret = validate_dec_params(ctx, dec_params); - if (ret) - return ret; - - run->decode_params = dec_params; - - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, V4L2_CID_STATELESS_VP9_COMPRESSED_HDR); - if (WARN_ON(!ctrl)) - return -EINVAL; - prob_updates = ctrl->p_cur.p; - vp9_ctx->cur.tx_mode = prob_updates->tx_mode; - - /* - * vp9 stuff - * - * by this point the userspace has done all parts of 6.2 uncompressed_header() - * except this fragment: - * if ( FrameIsIntra || error_resilient_mode ) { - * setup_past_independence ( ) - * if ( frame_type == KEY_FRAME || error_resilient_mode == 1 || - * reset_frame_context == 3 ) { - * for ( i = 0; i < 4; i ++ ) { - * save_probs( i ) - * } - * } else if ( reset_frame_context == 2 ) { - * save_probs( frame_context_idx ) - * } - * frame_context_idx = 0 - * } - */ - fctx_idx = v4l2_vp9_reset_frame_ctx(dec_params, vp9_ctx->frame_context); - vp9_ctx->cur.frame_context_idx = fctx_idx; - - /* 6.1 frame(sz): load_probs() and load_probs2() */ - vp9_ctx->probability_tables = vp9_ctx->frame_context[fctx_idx]; - - /* - * The userspace has also performed 6.3 compressed_header(), but handling the - * probs in a special way. All probs which need updating, except MV-related, - * have been read from the bitstream and translated through inv_map_table[], - * but no 6.3.6 inv_recenter_nonneg(v, m) has been performed. The values passed - * by userspace are either translated values (there are no 0 values in - * inv_map_table[]), or zero to indicate no update. All MV-related probs which need - * updating have been read from the bitstream and (mv_prob << 1) | 1 has been - * performed. The values passed by userspace are either new values - * to replace old ones (the above mentioned shift and bitwise or never result in - * a zero) or zero to indicate no update. - * fw_update_probs() performs actual probs updates or leaves probs as-is - * for values for which a zero was passed from userspace. - */ - v4l2_vp9_fw_update_probs(&vp9_ctx->probability_tables, prob_updates, dec_params); - - return 0; -} - -static int rkvdec_vp9_run(struct rkvdec_ctx *ctx) -{ - struct rkvdec_dev *rkvdec = ctx->dev; - struct rkvdec_vp9_run run = { }; - int ret; - - ret = rkvdec_vp9_run_preamble(ctx, &run); - if (ret) { - rkvdec_run_postamble(ctx, &run.base); - return ret; - } - - /* Prepare probs. */ - init_probs(ctx, &run); - - /* Configure hardware registers. */ - config_registers(ctx, &run); - - rkvdec_run_postamble(ctx, &run.base); - - schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000)); - - writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND); - writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND); - - writel(0xe, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN); - /* Start decoding! */ - writel(RKVDEC_INTERRUPT_DEC_E | RKVDEC_CONFIG_DEC_CLK_GATE_E | - RKVDEC_TIMEOUT_E | RKVDEC_BUF_EMPTY_E, - rkvdec->regs + RKVDEC_REG_INTERRUPT); - - return 0; -} - -#define copy_tx_and_skip(p1, p2) \ -do { \ - memcpy((p1)->tx8, (p2)->tx8, sizeof((p1)->tx8)); \ - memcpy((p1)->tx16, (p2)->tx16, sizeof((p1)->tx16)); \ - memcpy((p1)->tx32, (p2)->tx32, sizeof((p1)->tx32)); \ - memcpy((p1)->skip, (p2)->skip, sizeof((p1)->skip)); \ -} while (0) - -static void rkvdec_vp9_done(struct rkvdec_ctx *ctx, - struct vb2_v4l2_buffer *src_buf, - struct vb2_v4l2_buffer *dst_buf, - enum vb2_buffer_state result) -{ - struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; - unsigned int fctx_idx; - - /* v4l2-specific stuff */ - if (result == VB2_BUF_STATE_ERROR) - goto out_update_last; - - /* - * vp9 stuff - * - * 6.1.2 refresh_probs() - * - * In the spec a complementary condition goes last in 6.1.2 refresh_probs(), - * but it makes no sense to perform all the activities from the first "if" - * there if we actually are not refreshing the frame context. On top of that, - * because of 6.2 uncompressed_header() whenever error_resilient_mode == 1, - * refresh_frame_context == 0. Consequently, if we don't jump to out_update_last - * it means error_resilient_mode must be 0. - */ - if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_REFRESH_FRAME_CTX)) - goto out_update_last; - - fctx_idx = vp9_ctx->cur.frame_context_idx; - - if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_PARALLEL_DEC_MODE)) { - /* error_resilient_mode == 0 && frame_parallel_decoding_mode == 0 */ - struct v4l2_vp9_frame_context *probs = &vp9_ctx->probability_tables; - bool frame_is_intra = vp9_ctx->cur.flags & - (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY); - struct tx_and_skip { - u8 tx8[2][1]; - u8 tx16[2][2]; - u8 tx32[2][3]; - u8 skip[3]; - } _tx_skip, *tx_skip = &_tx_skip; - struct v4l2_vp9_frame_symbol_counts *counts; - - /* buffer the forward-updated TX and skip probs */ - if (frame_is_intra) - copy_tx_and_skip(tx_skip, probs); - - /* 6.1.2 refresh_probs(): load_probs() and load_probs2() */ - *probs = vp9_ctx->frame_context[fctx_idx]; - - /* if FrameIsIntra then undo the effect of load_probs2() */ - if (frame_is_intra) - copy_tx_and_skip(probs, tx_skip); - - counts = frame_is_intra ? &vp9_ctx->intra_cnts : &vp9_ctx->inter_cnts; - v4l2_vp9_adapt_coef_probs(probs, counts, - !vp9_ctx->last.valid || - vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME, - frame_is_intra); - if (!frame_is_intra) { - const struct rkvdec_vp9_inter_frame_symbol_counts *inter_cnts; - u32 classes[2][11]; - int i; - - inter_cnts = vp9_ctx->count_tbl.cpu; - for (i = 0; i < ARRAY_SIZE(classes); ++i) - memcpy(classes[i], inter_cnts->classes[i], sizeof(classes[0])); - counts->classes = &classes; - - /* load_probs2() already done */ - v4l2_vp9_adapt_noncoef_probs(&vp9_ctx->probability_tables, counts, - vp9_ctx->cur.reference_mode, - vp9_ctx->cur.interpolation_filter, - vp9_ctx->cur.tx_mode, vp9_ctx->cur.flags); - } - } - - /* 6.1.2 refresh_probs(): save_probs(fctx_idx) */ - vp9_ctx->frame_context[fctx_idx] = vp9_ctx->probability_tables; - -out_update_last: - update_ctx_last_info(vp9_ctx); -} - -static void rkvdec_init_v4l2_vp9_count_tbl(struct rkvdec_ctx *ctx) -{ - struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; - struct rkvdec_vp9_intra_frame_symbol_counts *intra_cnts = vp9_ctx->count_tbl.cpu; - struct rkvdec_vp9_inter_frame_symbol_counts *inter_cnts = vp9_ctx->count_tbl.cpu; - int i, j, k, l, m; - - vp9_ctx->inter_cnts.partition = &inter_cnts->partition; - vp9_ctx->inter_cnts.skip = &inter_cnts->skip; - vp9_ctx->inter_cnts.intra_inter = &inter_cnts->inter; - vp9_ctx->inter_cnts.tx32p = &inter_cnts->tx32p; - vp9_ctx->inter_cnts.tx16p = &inter_cnts->tx16p; - vp9_ctx->inter_cnts.tx8p = &inter_cnts->tx8p; - - vp9_ctx->intra_cnts.partition = (u32 (*)[16][4])(&intra_cnts->partition); - vp9_ctx->intra_cnts.skip = &intra_cnts->skip; - vp9_ctx->intra_cnts.intra_inter = &intra_cnts->intra; - vp9_ctx->intra_cnts.tx32p = &intra_cnts->tx32p; - vp9_ctx->intra_cnts.tx16p = &intra_cnts->tx16p; - vp9_ctx->intra_cnts.tx8p = &intra_cnts->tx8p; - - vp9_ctx->inter_cnts.y_mode = &inter_cnts->y_mode; - vp9_ctx->inter_cnts.uv_mode = &inter_cnts->uv_mode; - vp9_ctx->inter_cnts.comp = &inter_cnts->comp; - vp9_ctx->inter_cnts.comp_ref = &inter_cnts->comp_ref; - vp9_ctx->inter_cnts.single_ref = &inter_cnts->single_ref; - vp9_ctx->inter_cnts.mv_mode = &inter_cnts->mv_mode; - vp9_ctx->inter_cnts.filter = &inter_cnts->filter; - vp9_ctx->inter_cnts.mv_joint = &inter_cnts->mv_joint; - vp9_ctx->inter_cnts.sign = &inter_cnts->sign; - /* - * rk hardware actually uses "u32 classes[2][11 + 1];" - * instead of "u32 classes[2][11];", so this must be explicitly - * copied into vp9_ctx->classes when passing the data to the - * vp9 library function - */ - vp9_ctx->inter_cnts.class0 = &inter_cnts->class0; - vp9_ctx->inter_cnts.bits = &inter_cnts->bits; - vp9_ctx->inter_cnts.class0_fp = &inter_cnts->class0_fp; - vp9_ctx->inter_cnts.fp = &inter_cnts->fp; - vp9_ctx->inter_cnts.class0_hp = &inter_cnts->class0_hp; - vp9_ctx->inter_cnts.hp = &inter_cnts->hp; - -#define INNERMOST_LOOP \ - do { \ - for (m = 0; m < ARRAY_SIZE(vp9_ctx->inter_cnts.coeff[0][0][0][0]); ++m) {\ - vp9_ctx->inter_cnts.coeff[i][j][k][l][m] = \ - &inter_cnts->ref_cnt[k][i][j][l][m].coeff; \ - vp9_ctx->inter_cnts.eob[i][j][k][l][m][0] = \ - &inter_cnts->ref_cnt[k][i][j][l][m].eob[0]; \ - vp9_ctx->inter_cnts.eob[i][j][k][l][m][1] = \ - &inter_cnts->ref_cnt[k][i][j][l][m].eob[1]; \ - \ - vp9_ctx->intra_cnts.coeff[i][j][k][l][m] = \ - &intra_cnts->ref_cnt[k][i][j][l][m].coeff; \ - vp9_ctx->intra_cnts.eob[i][j][k][l][m][0] = \ - &intra_cnts->ref_cnt[k][i][j][l][m].eob[0]; \ - vp9_ctx->intra_cnts.eob[i][j][k][l][m][1] = \ - &intra_cnts->ref_cnt[k][i][j][l][m].eob[1]; \ - } \ - } while (0) - - for (i = 0; i < ARRAY_SIZE(vp9_ctx->inter_cnts.coeff); ++i) - for (j = 0; j < ARRAY_SIZE(vp9_ctx->inter_cnts.coeff[0]); ++j) - for (k = 0; k < ARRAY_SIZE(vp9_ctx->inter_cnts.coeff[0][0]); ++k) - for (l = 0; l < ARRAY_SIZE(vp9_ctx->inter_cnts.coeff[0][0][0]); ++l) - INNERMOST_LOOP; -#undef INNERMOST_LOOP -} - -static int rkvdec_vp9_start(struct rkvdec_ctx *ctx) -{ - struct rkvdec_dev *rkvdec = ctx->dev; - struct rkvdec_vp9_priv_tbl *priv_tbl; - struct rkvdec_vp9_ctx *vp9_ctx; - unsigned char *count_tbl; - int ret; - - vp9_ctx = kzalloc(sizeof(*vp9_ctx), GFP_KERNEL); - if (!vp9_ctx) - return -ENOMEM; - - ctx->priv = vp9_ctx; - - BUILD_BUG_ON(sizeof(priv_tbl->probs) % 16); /* ensure probs size is 128-bit aligned */ - priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl), - &vp9_ctx->priv_tbl.dma, GFP_KERNEL); - if (!priv_tbl) { - ret = -ENOMEM; - goto err_free_ctx; - } - - vp9_ctx->priv_tbl.size = sizeof(*priv_tbl); - vp9_ctx->priv_tbl.cpu = priv_tbl; - - count_tbl = dma_alloc_coherent(rkvdec->dev, RKVDEC_VP9_COUNT_SIZE, - &vp9_ctx->count_tbl.dma, GFP_KERNEL); - if (!count_tbl) { - ret = -ENOMEM; - goto err_free_priv_tbl; - } - - vp9_ctx->count_tbl.size = RKVDEC_VP9_COUNT_SIZE; - vp9_ctx->count_tbl.cpu = count_tbl; - rkvdec_init_v4l2_vp9_count_tbl(ctx); - - return 0; - -err_free_priv_tbl: - dma_free_coherent(rkvdec->dev, vp9_ctx->priv_tbl.size, - vp9_ctx->priv_tbl.cpu, vp9_ctx->priv_tbl.dma); - -err_free_ctx: - kfree(vp9_ctx); - return ret; -} - -static void rkvdec_vp9_stop(struct rkvdec_ctx *ctx) -{ - struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; - struct rkvdec_dev *rkvdec = ctx->dev; - - dma_free_coherent(rkvdec->dev, vp9_ctx->count_tbl.size, - vp9_ctx->count_tbl.cpu, vp9_ctx->count_tbl.dma); - dma_free_coherent(rkvdec->dev, vp9_ctx->priv_tbl.size, - vp9_ctx->priv_tbl.cpu, vp9_ctx->priv_tbl.dma); - kfree(vp9_ctx); -} - -static int rkvdec_vp9_adjust_fmt(struct rkvdec_ctx *ctx, - struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp; - - fmt->num_planes = 1; - if (!fmt->plane_fmt[0].sizeimage) - fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height * 2; - return 0; -} - -const struct rkvdec_coded_fmt_ops rkvdec_vp9_fmt_ops = { - .adjust_fmt = rkvdec_vp9_adjust_fmt, - .start = rkvdec_vp9_start, - .stop = rkvdec_vp9_stop, - .run = rkvdec_vp9_run, - .done = rkvdec_vp9_done, -}; diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c deleted file mode 100644 index d707088ec0dc..000000000000 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ /dev/null @@ -1,1251 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Rockchip Video Decoder driver - * - * Copyright (C) 2019 Collabora, Ltd. - * - * Based on rkvdec driver by Google LLC. (Tomasz Figa ) - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rkvdec.h" -#include "rkvdec-regs.h" - -static bool rkvdec_image_fmt_match(enum rkvdec_image_fmt fmt1, - enum rkvdec_image_fmt fmt2) -{ - return fmt1 == fmt2 || fmt2 == RKVDEC_IMG_FMT_ANY || - fmt1 == RKVDEC_IMG_FMT_ANY; -} - -static bool rkvdec_image_fmt_changed(struct rkvdec_ctx *ctx, - enum rkvdec_image_fmt image_fmt) -{ - if (image_fmt == RKVDEC_IMG_FMT_ANY) - return false; - - return ctx->image_fmt != image_fmt; -} - -static u32 rkvdec_enum_decoded_fmt(struct rkvdec_ctx *ctx, int index, - enum rkvdec_image_fmt image_fmt) -{ - const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; - int fmt_idx = -1; - unsigned int i; - - if (WARN_ON(!desc)) - return 0; - - for (i = 0; i < desc->num_decoded_fmts; i++) { - if (!rkvdec_image_fmt_match(desc->decoded_fmts[i].image_fmt, - image_fmt)) - continue; - fmt_idx++; - if (index == fmt_idx) - return desc->decoded_fmts[i].fourcc; - } - - return 0; -} - -static bool rkvdec_is_valid_fmt(struct rkvdec_ctx *ctx, u32 fourcc, - enum rkvdec_image_fmt image_fmt) -{ - const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; - unsigned int i; - - for (i = 0; i < desc->num_decoded_fmts; i++) { - if (rkvdec_image_fmt_match(desc->decoded_fmts[i].image_fmt, - image_fmt) && - desc->decoded_fmts[i].fourcc == fourcc) - return true; - } - - return false; -} - -static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx, - struct v4l2_pix_format_mplane *pix_mp) -{ - v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, - pix_mp->width, pix_mp->height); - pix_mp->plane_fmt[0].sizeimage += 128 * - DIV_ROUND_UP(pix_mp->width, 16) * - DIV_ROUND_UP(pix_mp->height, 16); -} - -static void rkvdec_reset_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f, - u32 fourcc) -{ - memset(f, 0, sizeof(*f)); - f->fmt.pix_mp.pixelformat = fourcc; - f->fmt.pix_mp.field = V4L2_FIELD_NONE; - f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; - f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; - f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; -} - -static void rkvdec_reset_decoded_fmt(struct rkvdec_ctx *ctx) -{ - struct v4l2_format *f = &ctx->decoded_fmt; - u32 fourcc; - - fourcc = rkvdec_enum_decoded_fmt(ctx, 0, ctx->image_fmt); - rkvdec_reset_fmt(ctx, f, fourcc); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - f->fmt.pix_mp.width = ctx->coded_fmt.fmt.pix_mp.width; - f->fmt.pix_mp.height = ctx->coded_fmt.fmt.pix_mp.height; - rkvdec_fill_decoded_pixfmt(ctx, &f->fmt.pix_mp); -} - -static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) -{ - struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl); - const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; - - if (desc->ops->try_ctrl) - return desc->ops->try_ctrl(ctx, ctrl); - - return 0; -} - -static int rkvdec_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl); - const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; - enum rkvdec_image_fmt image_fmt; - struct vb2_queue *vq; - - /* Check if this change requires a capture format reset */ - if (!desc->ops->get_image_fmt) - return 0; - - image_fmt = desc->ops->get_image_fmt(ctx, ctrl); - if (rkvdec_image_fmt_changed(ctx, image_fmt)) { - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (vb2_is_busy(vq)) - return -EBUSY; - - ctx->image_fmt = image_fmt; - rkvdec_reset_decoded_fmt(ctx); - } - - return 0; -} - -static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = { - .try_ctrl = rkvdec_try_ctrl, - .s_ctrl = rkvdec_s_ctrl, -}; - -static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { - { - .cfg.id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, - }, - { - .cfg.id = V4L2_CID_STATELESS_H264_SPS, - .cfg.ops = &rkvdec_ctrl_ops, - }, - { - .cfg.id = V4L2_CID_STATELESS_H264_PPS, - }, - { - .cfg.id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, - }, - { - .cfg.id = V4L2_CID_STATELESS_H264_DECODE_MODE, - .cfg.min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, - .cfg.max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, - .cfg.def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, - }, - { - .cfg.id = V4L2_CID_STATELESS_H264_START_CODE, - .cfg.min = V4L2_STATELESS_H264_START_CODE_ANNEX_B, - .cfg.def = V4L2_STATELESS_H264_START_CODE_ANNEX_B, - .cfg.max = V4L2_STATELESS_H264_START_CODE_ANNEX_B, - }, - { - .cfg.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, - .cfg.min = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE, - .cfg.max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA, - .cfg.menu_skip_mask = - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE), - .cfg.def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, - }, - { - .cfg.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, - .cfg.min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, - .cfg.max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1, - }, -}; - -static const struct rkvdec_ctrls rkvdec_h264_ctrls = { - .ctrls = rkvdec_h264_ctrl_descs, - .num_ctrls = ARRAY_SIZE(rkvdec_h264_ctrl_descs), -}; - -static const struct rkvdec_decoded_fmt_desc rkvdec_h264_decoded_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .image_fmt = RKVDEC_IMG_FMT_420_8BIT, - }, - { - .fourcc = V4L2_PIX_FMT_NV15, - .image_fmt = RKVDEC_IMG_FMT_420_10BIT, - }, - { - .fourcc = V4L2_PIX_FMT_NV16, - .image_fmt = RKVDEC_IMG_FMT_422_8BIT, - }, - { - .fourcc = V4L2_PIX_FMT_NV20, - .image_fmt = RKVDEC_IMG_FMT_422_10BIT, - }, -}; - -static const struct rkvdec_ctrl_desc rkvdec_vp9_ctrl_descs[] = { - { - .cfg.id = V4L2_CID_STATELESS_VP9_FRAME, - }, - { - .cfg.id = V4L2_CID_STATELESS_VP9_COMPRESSED_HDR, - }, - { - .cfg.id = V4L2_CID_MPEG_VIDEO_VP9_PROFILE, - .cfg.min = V4L2_MPEG_VIDEO_VP9_PROFILE_0, - .cfg.max = V4L2_MPEG_VIDEO_VP9_PROFILE_0, - .cfg.def = V4L2_MPEG_VIDEO_VP9_PROFILE_0, - }, -}; - -static const struct rkvdec_ctrls rkvdec_vp9_ctrls = { - .ctrls = rkvdec_vp9_ctrl_descs, - .num_ctrls = ARRAY_SIZE(rkvdec_vp9_ctrl_descs), -}; - -static const struct rkvdec_decoded_fmt_desc rkvdec_vp9_decoded_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .image_fmt = RKVDEC_IMG_FMT_420_8BIT, - }, -}; - -static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_H264_SLICE, - .frmsize = { - .min_width = 64, - .max_width = 4096, - .step_width = 64, - .min_height = 48, - .max_height = 2560, - .step_height = 16, - }, - .ctrls = &rkvdec_h264_ctrls, - .ops = &rkvdec_h264_fmt_ops, - .num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_decoded_fmts), - .decoded_fmts = rkvdec_h264_decoded_fmts, - .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF, - }, - { - .fourcc = V4L2_PIX_FMT_VP9_FRAME, - .frmsize = { - .min_width = 64, - .max_width = 4096, - .step_width = 64, - .min_height = 64, - .max_height = 2304, - .step_height = 64, - }, - .ctrls = &rkvdec_vp9_ctrls, - .ops = &rkvdec_vp9_fmt_ops, - .num_decoded_fmts = ARRAY_SIZE(rkvdec_vp9_decoded_fmts), - .decoded_fmts = rkvdec_vp9_decoded_fmts, - } -}; - -static const struct rkvdec_coded_fmt_desc * -rkvdec_find_coded_fmt_desc(u32 fourcc) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) { - if (rkvdec_coded_fmts[i].fourcc == fourcc) - return &rkvdec_coded_fmts[i]; - } - - return NULL; -} - -static void rkvdec_reset_coded_fmt(struct rkvdec_ctx *ctx) -{ - struct v4l2_format *f = &ctx->coded_fmt; - - ctx->coded_fmt_desc = &rkvdec_coded_fmts[0]; - rkvdec_reset_fmt(ctx, f, ctx->coded_fmt_desc->fourcc); - - f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - f->fmt.pix_mp.width = ctx->coded_fmt_desc->frmsize.min_width; - f->fmt.pix_mp.height = ctx->coded_fmt_desc->frmsize.min_height; - - if (ctx->coded_fmt_desc->ops->adjust_fmt) - ctx->coded_fmt_desc->ops->adjust_fmt(ctx, f); -} - -static int rkvdec_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - const struct rkvdec_coded_fmt_desc *fmt; - - if (fsize->index != 0) - return -EINVAL; - - fmt = rkvdec_find_coded_fmt_desc(fsize->pixel_format); - if (!fmt) - return -EINVAL; - - fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; - fsize->stepwise.min_width = 1; - fsize->stepwise.max_width = fmt->frmsize.max_width; - fsize->stepwise.step_width = 1; - fsize->stepwise.min_height = 1; - fsize->stepwise.max_height = fmt->frmsize.max_height; - fsize->stepwise.step_height = 1; - - return 0; -} - -static int rkvdec_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct rkvdec_dev *rkvdec = video_drvdata(file); - struct video_device *vdev = video_devdata(file); - - strscpy(cap->driver, rkvdec->dev->driver->name, - sizeof(cap->driver)); - strscpy(cap->card, vdev->name, sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", - rkvdec->dev->driver->name); - return 0; -} - -static int rkvdec_try_capture_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; - struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); - const struct rkvdec_coded_fmt_desc *coded_desc; - - /* - * The codec context should point to a coded format desc, if the format - * on the coded end has not been set yet, it should point to the - * default value. - */ - coded_desc = ctx->coded_fmt_desc; - if (WARN_ON(!coded_desc)) - return -EINVAL; - - if (!rkvdec_is_valid_fmt(ctx, pix_mp->pixelformat, ctx->image_fmt)) - pix_mp->pixelformat = rkvdec_enum_decoded_fmt(ctx, 0, - ctx->image_fmt); - - /* Always apply the frmsize constraint of the coded end. */ - pix_mp->width = max(pix_mp->width, ctx->coded_fmt.fmt.pix_mp.width); - pix_mp->height = max(pix_mp->height, ctx->coded_fmt.fmt.pix_mp.height); - v4l2_apply_frmsize_constraints(&pix_mp->width, - &pix_mp->height, - &coded_desc->frmsize); - - rkvdec_fill_decoded_pixfmt(ctx, pix_mp); - pix_mp->field = V4L2_FIELD_NONE; - - return 0; -} - -static int rkvdec_try_output_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; - struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); - const struct rkvdec_coded_fmt_desc *desc; - - desc = rkvdec_find_coded_fmt_desc(pix_mp->pixelformat); - if (!desc) { - pix_mp->pixelformat = rkvdec_coded_fmts[0].fourcc; - desc = &rkvdec_coded_fmts[0]; - } - - v4l2_apply_frmsize_constraints(&pix_mp->width, - &pix_mp->height, - &desc->frmsize); - - pix_mp->field = V4L2_FIELD_NONE; - /* All coded formats are considered single planar for now. */ - pix_mp->num_planes = 1; - - if (desc->ops->adjust_fmt) { - int ret; - - ret = desc->ops->adjust_fmt(ctx, f); - if (ret) - return ret; - } - - return 0; -} - -static int rkvdec_s_capture_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); - struct vb2_queue *vq; - int ret; - - /* Change not allowed if queue is busy */ - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (vb2_is_busy(vq)) - return -EBUSY; - - ret = rkvdec_try_capture_fmt(file, priv, f); - if (ret) - return ret; - - ctx->decoded_fmt = *f; - return 0; -} - -static int rkvdec_s_output_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); - struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; - const struct rkvdec_coded_fmt_desc *desc; - struct v4l2_format *cap_fmt; - struct vb2_queue *peer_vq, *vq; - int ret; - - /* - * In order to support dynamic resolution change, the decoder admits - * a resolution change, as long as the pixelformat remains. Can't be - * done if streaming. - */ - vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (vb2_is_streaming(vq) || - (vb2_is_busy(vq) && - f->fmt.pix_mp.pixelformat != ctx->coded_fmt.fmt.pix_mp.pixelformat)) - return -EBUSY; - - /* - * Since format change on the OUTPUT queue will reset the CAPTURE - * queue, we can't allow doing so when the CAPTURE queue has buffers - * allocated. - */ - peer_vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (vb2_is_busy(peer_vq)) - return -EBUSY; - - ret = rkvdec_try_output_fmt(file, priv, f); - if (ret) - return ret; - - desc = rkvdec_find_coded_fmt_desc(f->fmt.pix_mp.pixelformat); - if (!desc) - return -EINVAL; - ctx->coded_fmt_desc = desc; - ctx->coded_fmt = *f; - - /* - * Current decoded format might have become invalid with newly - * selected codec, so reset it to default just to be safe and - * keep internal driver state sane. User is mandated to set - * the decoded format again after we return, so we don't need - * anything smarter. - * - * Note that this will propagates any size changes to the decoded format. - */ - ctx->image_fmt = RKVDEC_IMG_FMT_ANY; - rkvdec_reset_decoded_fmt(ctx); - - /* Propagate colorspace information to capture. */ - cap_fmt = &ctx->decoded_fmt; - cap_fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace; - cap_fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func; - cap_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; - cap_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization; - - /* Enable format specific queue features */ - vq->subsystem_flags |= desc->subsystem_flags; - - return 0; -} - -static int rkvdec_g_output_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); - - *f = ctx->coded_fmt; - return 0; -} - -static int rkvdec_g_capture_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); - - *f = ctx->decoded_fmt; - return 0; -} - -static int rkvdec_enum_output_fmt(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (f->index >= ARRAY_SIZE(rkvdec_coded_fmts)) - return -EINVAL; - - f->pixelformat = rkvdec_coded_fmts[f->index].fourcc; - return 0; -} - -static int rkvdec_enum_capture_fmt(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); - u32 fourcc; - - fourcc = rkvdec_enum_decoded_fmt(ctx, f->index, ctx->image_fmt); - if (!fourcc) - return -EINVAL; - - f->pixelformat = fourcc; - return 0; -} - -static const struct v4l2_ioctl_ops rkvdec_ioctl_ops = { - .vidioc_querycap = rkvdec_querycap, - .vidioc_enum_framesizes = rkvdec_enum_framesizes, - - .vidioc_try_fmt_vid_cap_mplane = rkvdec_try_capture_fmt, - .vidioc_try_fmt_vid_out_mplane = rkvdec_try_output_fmt, - .vidioc_s_fmt_vid_out_mplane = rkvdec_s_output_fmt, - .vidioc_s_fmt_vid_cap_mplane = rkvdec_s_capture_fmt, - .vidioc_g_fmt_vid_out_mplane = rkvdec_g_output_fmt, - .vidioc_g_fmt_vid_cap_mplane = rkvdec_g_capture_fmt, - .vidioc_enum_fmt_vid_out = rkvdec_enum_output_fmt, - .vidioc_enum_fmt_vid_cap = rkvdec_enum_capture_fmt, - - .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, - .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, - .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, - .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, - .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, - .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, - .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, - - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, - - .vidioc_streamon = v4l2_m2m_ioctl_streamon, - .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - - .vidioc_decoder_cmd = v4l2_m2m_ioctl_stateless_decoder_cmd, - .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_stateless_try_decoder_cmd, -}; - -static int rkvdec_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, - unsigned int *num_planes, unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct rkvdec_ctx *ctx = vb2_get_drv_priv(vq); - struct v4l2_format *f; - unsigned int i; - - if (V4L2_TYPE_IS_OUTPUT(vq->type)) - f = &ctx->coded_fmt; - else - f = &ctx->decoded_fmt; - - if (*num_planes) { - if (*num_planes != f->fmt.pix_mp.num_planes) - return -EINVAL; - - for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { - if (sizes[i] < f->fmt.pix_mp.plane_fmt[i].sizeimage) - return -EINVAL; - } - } else { - *num_planes = f->fmt.pix_mp.num_planes; - for (i = 0; i < f->fmt.pix_mp.num_planes; i++) - sizes[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage; - } - - return 0; -} - -static int rkvdec_buf_prepare(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct rkvdec_ctx *ctx = vb2_get_drv_priv(vq); - struct v4l2_format *f; - unsigned int i; - - if (V4L2_TYPE_IS_OUTPUT(vq->type)) - f = &ctx->coded_fmt; - else - f = &ctx->decoded_fmt; - - for (i = 0; i < f->fmt.pix_mp.num_planes; ++i) { - u32 sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage; - - if (vb2_plane_size(vb, i) < sizeimage) - return -EINVAL; - } - - /* - * Buffer's bytesused must be written by driver for CAPTURE buffers. - * (for OUTPUT buffers, if userspace passes 0 bytesused, v4l2-core sets - * it to buffer length). - */ - if (V4L2_TYPE_IS_CAPTURE(vq->type)) - vb2_set_plane_payload(vb, 0, f->fmt.pix_mp.plane_fmt[0].sizeimage); - - return 0; -} - -static void rkvdec_buf_queue(struct vb2_buffer *vb) -{ - struct rkvdec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); -} - -static int rkvdec_buf_out_validate(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - - vbuf->field = V4L2_FIELD_NONE; - return 0; -} - -static void rkvdec_buf_request_complete(struct vb2_buffer *vb) -{ - struct rkvdec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - - v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_hdl); -} - -static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct rkvdec_ctx *ctx = vb2_get_drv_priv(q); - const struct rkvdec_coded_fmt_desc *desc; - int ret; - - if (V4L2_TYPE_IS_CAPTURE(q->type)) - return 0; - - desc = ctx->coded_fmt_desc; - if (WARN_ON(!desc)) - return -EINVAL; - - if (desc->ops->start) { - ret = desc->ops->start(ctx); - if (ret) - return ret; - } - - return 0; -} - -static void rkvdec_queue_cleanup(struct vb2_queue *vq, u32 state) -{ - struct rkvdec_ctx *ctx = vb2_get_drv_priv(vq); - - while (true) { - struct vb2_v4l2_buffer *vbuf; - - if (V4L2_TYPE_IS_OUTPUT(vq->type)) - vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - else - vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - - if (!vbuf) - break; - - v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, - &ctx->ctrl_hdl); - v4l2_m2m_buf_done(vbuf, state); - } -} - -static void rkvdec_stop_streaming(struct vb2_queue *q) -{ - struct rkvdec_ctx *ctx = vb2_get_drv_priv(q); - - if (V4L2_TYPE_IS_OUTPUT(q->type)) { - const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; - - if (WARN_ON(!desc)) - return; - - if (desc->ops->stop) - desc->ops->stop(ctx); - } - - rkvdec_queue_cleanup(q, VB2_BUF_STATE_ERROR); -} - -static const struct vb2_ops rkvdec_queue_ops = { - .queue_setup = rkvdec_queue_setup, - .buf_prepare = rkvdec_buf_prepare, - .buf_queue = rkvdec_buf_queue, - .buf_out_validate = rkvdec_buf_out_validate, - .buf_request_complete = rkvdec_buf_request_complete, - .start_streaming = rkvdec_start_streaming, - .stop_streaming = rkvdec_stop_streaming, -}; - -static int rkvdec_request_validate(struct media_request *req) -{ - unsigned int count; - - count = vb2_request_buffer_cnt(req); - if (!count) - return -ENOENT; - else if (count > 1) - return -EINVAL; - - return vb2_request_validate(req); -} - -static const struct media_device_ops rkvdec_media_ops = { - .req_validate = rkvdec_request_validate, - .req_queue = v4l2_m2m_request_queue, -}; - -static void rkvdec_job_finish_no_pm(struct rkvdec_ctx *ctx, - enum vb2_buffer_state result) -{ - if (ctx->coded_fmt_desc->ops->done) { - struct vb2_v4l2_buffer *src_buf, *dst_buf; - - src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - ctx->coded_fmt_desc->ops->done(ctx, src_buf, dst_buf, result); - } - - v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, - result); -} - -static void rkvdec_job_finish(struct rkvdec_ctx *ctx, - enum vb2_buffer_state result) -{ - struct rkvdec_dev *rkvdec = ctx->dev; - - pm_runtime_mark_last_busy(rkvdec->dev); - pm_runtime_put_autosuspend(rkvdec->dev); - rkvdec_job_finish_no_pm(ctx, result); -} - -void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run) -{ - struct media_request *src_req; - - memset(run, 0, sizeof(*run)); - - run->bufs.src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - run->bufs.dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - - /* Apply request(s) controls if needed. */ - src_req = run->bufs.src->vb2_buf.req_obj.req; - if (src_req) - v4l2_ctrl_request_setup(src_req, &ctx->ctrl_hdl); - - v4l2_m2m_buf_copy_metadata(run->bufs.src, run->bufs.dst, true); -} - -void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run) -{ - struct media_request *src_req = run->bufs.src->vb2_buf.req_obj.req; - - if (src_req) - v4l2_ctrl_request_complete(src_req, &ctx->ctrl_hdl); -} - -static void rkvdec_device_run(void *priv) -{ - struct rkvdec_ctx *ctx = priv; - struct rkvdec_dev *rkvdec = ctx->dev; - const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; - int ret; - - if (WARN_ON(!desc)) - return; - - ret = pm_runtime_resume_and_get(rkvdec->dev); - if (ret < 0) { - rkvdec_job_finish_no_pm(ctx, VB2_BUF_STATE_ERROR); - return; - } - - ret = desc->ops->run(ctx); - if (ret) - rkvdec_job_finish(ctx, VB2_BUF_STATE_ERROR); -} - -static const struct v4l2_m2m_ops rkvdec_m2m_ops = { - .device_run = rkvdec_device_run, -}; - -static int rkvdec_queue_init(void *priv, - struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - struct rkvdec_ctx *ctx = priv; - struct rkvdec_dev *rkvdec = ctx->dev; - int ret; - - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - src_vq->io_modes = VB2_MMAP | VB2_DMABUF; - src_vq->drv_priv = ctx; - src_vq->ops = &rkvdec_queue_ops; - src_vq->mem_ops = &vb2_dma_contig_memops; - - /* - * Driver does mostly sequential access, so sacrifice TLB efficiency - * for faster allocation. Also, no CPU access on the source queue, - * so no kernel mapping needed. - */ - src_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES | - DMA_ATTR_NO_KERNEL_MAPPING; - src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->lock = &rkvdec->vdev_lock; - src_vq->dev = rkvdec->v4l2_dev.dev; - src_vq->supports_requests = true; - src_vq->requires_requests = true; - - ret = vb2_queue_init(src_vq); - if (ret) - return ret; - - dst_vq->bidirectional = true; - dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES | - DMA_ATTR_NO_KERNEL_MAPPING; - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; - dst_vq->drv_priv = ctx; - dst_vq->ops = &rkvdec_queue_ops; - dst_vq->buf_struct_size = sizeof(struct rkvdec_decoded_buffer); - dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->lock = &rkvdec->vdev_lock; - dst_vq->dev = rkvdec->v4l2_dev.dev; - - return vb2_queue_init(dst_vq); -} - -static int rkvdec_add_ctrls(struct rkvdec_ctx *ctx, - const struct rkvdec_ctrls *ctrls) -{ - unsigned int i; - - for (i = 0; i < ctrls->num_ctrls; i++) { - const struct v4l2_ctrl_config *cfg = &ctrls->ctrls[i].cfg; - - v4l2_ctrl_new_custom(&ctx->ctrl_hdl, cfg, ctx); - if (ctx->ctrl_hdl.error) - return ctx->ctrl_hdl.error; - } - - return 0; -} - -static int rkvdec_init_ctrls(struct rkvdec_ctx *ctx) -{ - unsigned int i, nctrls = 0; - int ret; - - for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) - nctrls += rkvdec_coded_fmts[i].ctrls->num_ctrls; - - v4l2_ctrl_handler_init(&ctx->ctrl_hdl, nctrls); - - for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) { - ret = rkvdec_add_ctrls(ctx, rkvdec_coded_fmts[i].ctrls); - if (ret) - goto err_free_handler; - } - - ret = v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); - if (ret) - goto err_free_handler; - - ctx->fh.ctrl_handler = &ctx->ctrl_hdl; - return 0; - -err_free_handler: - v4l2_ctrl_handler_free(&ctx->ctrl_hdl); - return ret; -} - -static int rkvdec_open(struct file *filp) -{ - struct rkvdec_dev *rkvdec = video_drvdata(filp); - struct rkvdec_ctx *ctx; - int ret; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->dev = rkvdec; - rkvdec_reset_coded_fmt(ctx); - rkvdec_reset_decoded_fmt(ctx); - v4l2_fh_init(&ctx->fh, video_devdata(filp)); - - ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(rkvdec->m2m_dev, ctx, - rkvdec_queue_init); - if (IS_ERR(ctx->fh.m2m_ctx)) { - ret = PTR_ERR(ctx->fh.m2m_ctx); - goto err_free_ctx; - } - - ret = rkvdec_init_ctrls(ctx); - if (ret) - goto err_cleanup_m2m_ctx; - - filp->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); - - return 0; - -err_cleanup_m2m_ctx: - v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); - -err_free_ctx: - kfree(ctx); - return ret; -} - -static int rkvdec_release(struct file *filp) -{ - struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(filp->private_data); - - v4l2_fh_del(&ctx->fh); - v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); - v4l2_ctrl_handler_free(&ctx->ctrl_hdl); - v4l2_fh_exit(&ctx->fh); - kfree(ctx); - - return 0; -} - -static const struct v4l2_file_operations rkvdec_fops = { - .owner = THIS_MODULE, - .open = rkvdec_open, - .release = rkvdec_release, - .poll = v4l2_m2m_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = v4l2_m2m_fop_mmap, -}; - -static int rkvdec_v4l2_init(struct rkvdec_dev *rkvdec) -{ - int ret; - - ret = v4l2_device_register(rkvdec->dev, &rkvdec->v4l2_dev); - if (ret) { - dev_err(rkvdec->dev, "Failed to register V4L2 device\n"); - return ret; - } - - rkvdec->m2m_dev = v4l2_m2m_init(&rkvdec_m2m_ops); - if (IS_ERR(rkvdec->m2m_dev)) { - v4l2_err(&rkvdec->v4l2_dev, "Failed to init mem2mem device\n"); - ret = PTR_ERR(rkvdec->m2m_dev); - goto err_unregister_v4l2; - } - - rkvdec->mdev.dev = rkvdec->dev; - strscpy(rkvdec->mdev.model, "rkvdec", sizeof(rkvdec->mdev.model)); - strscpy(rkvdec->mdev.bus_info, "platform:rkvdec", - sizeof(rkvdec->mdev.bus_info)); - media_device_init(&rkvdec->mdev); - rkvdec->mdev.ops = &rkvdec_media_ops; - rkvdec->v4l2_dev.mdev = &rkvdec->mdev; - - rkvdec->vdev.lock = &rkvdec->vdev_lock; - rkvdec->vdev.v4l2_dev = &rkvdec->v4l2_dev; - rkvdec->vdev.fops = &rkvdec_fops; - rkvdec->vdev.release = video_device_release_empty; - rkvdec->vdev.vfl_dir = VFL_DIR_M2M; - rkvdec->vdev.device_caps = V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_M2M_MPLANE; - rkvdec->vdev.ioctl_ops = &rkvdec_ioctl_ops; - video_set_drvdata(&rkvdec->vdev, rkvdec); - strscpy(rkvdec->vdev.name, "rkvdec", sizeof(rkvdec->vdev.name)); - - ret = video_register_device(&rkvdec->vdev, VFL_TYPE_VIDEO, -1); - if (ret) { - v4l2_err(&rkvdec->v4l2_dev, "Failed to register video device\n"); - goto err_cleanup_mc; - } - - ret = v4l2_m2m_register_media_controller(rkvdec->m2m_dev, &rkvdec->vdev, - MEDIA_ENT_F_PROC_VIDEO_DECODER); - if (ret) { - v4l2_err(&rkvdec->v4l2_dev, - "Failed to initialize V4L2 M2M media controller\n"); - goto err_unregister_vdev; - } - - ret = media_device_register(&rkvdec->mdev); - if (ret) { - v4l2_err(&rkvdec->v4l2_dev, "Failed to register media device\n"); - goto err_unregister_mc; - } - - return 0; - -err_unregister_mc: - v4l2_m2m_unregister_media_controller(rkvdec->m2m_dev); - -err_unregister_vdev: - video_unregister_device(&rkvdec->vdev); - -err_cleanup_mc: - media_device_cleanup(&rkvdec->mdev); - v4l2_m2m_release(rkvdec->m2m_dev); - -err_unregister_v4l2: - v4l2_device_unregister(&rkvdec->v4l2_dev); - return ret; -} - -static void rkvdec_v4l2_cleanup(struct rkvdec_dev *rkvdec) -{ - media_device_unregister(&rkvdec->mdev); - v4l2_m2m_unregister_media_controller(rkvdec->m2m_dev); - video_unregister_device(&rkvdec->vdev); - media_device_cleanup(&rkvdec->mdev); - v4l2_m2m_release(rkvdec->m2m_dev); - v4l2_device_unregister(&rkvdec->v4l2_dev); -} - -static void rkvdec_iommu_restore(struct rkvdec_dev *rkvdec) -{ - if (rkvdec->empty_domain) { - /* - * To rewrite mapping into the attached IOMMU core, attach a new empty domain that - * will program an empty table, then detach it to restore the default domain and - * all cached mappings. - * This is safely done in this interrupt handler to make sure no memory get mapped - * through the IOMMU while the empty domain is attached. - */ - iommu_attach_device(rkvdec->empty_domain, rkvdec->dev); - iommu_detach_device(rkvdec->empty_domain, rkvdec->dev); - } -} - -static irqreturn_t rkvdec_irq_handler(int irq, void *priv) -{ - struct rkvdec_dev *rkvdec = priv; - struct rkvdec_ctx *ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev); - enum vb2_buffer_state state; - u32 status; - - status = readl(rkvdec->regs + RKVDEC_REG_INTERRUPT); - writel(0, rkvdec->regs + RKVDEC_REG_INTERRUPT); - - if (status & RKVDEC_RDY_STA) { - state = VB2_BUF_STATE_DONE; - } else { - state = VB2_BUF_STATE_ERROR; - if (status & RKVDEC_SOFTRESET_RDY) - rkvdec_iommu_restore(rkvdec); - } - - if (cancel_delayed_work(&rkvdec->watchdog_work)) - rkvdec_job_finish(ctx, state); - - return IRQ_HANDLED; -} - -static void rkvdec_watchdog_func(struct work_struct *work) -{ - struct rkvdec_dev *rkvdec; - struct rkvdec_ctx *ctx; - - rkvdec = container_of(to_delayed_work(work), struct rkvdec_dev, - watchdog_work); - ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev); - if (ctx) { - dev_err(rkvdec->dev, "Frame processing timed out!\n"); - writel(RKVDEC_IRQ_DIS, rkvdec->regs + RKVDEC_REG_INTERRUPT); - writel(0, rkvdec->regs + RKVDEC_REG_SYSCTRL); - rkvdec_job_finish(ctx, VB2_BUF_STATE_ERROR); - } -} - -static const struct of_device_id of_rkvdec_match[] = { - { .compatible = "rockchip,rk3399-vdec" }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, of_rkvdec_match); - -static const char * const rkvdec_clk_names[] = { - "axi", "ahb", "cabac", "core" -}; - -static int rkvdec_probe(struct platform_device *pdev) -{ - struct rkvdec_dev *rkvdec; - unsigned int i; - int ret, irq; - - rkvdec = devm_kzalloc(&pdev->dev, sizeof(*rkvdec), GFP_KERNEL); - if (!rkvdec) - return -ENOMEM; - - platform_set_drvdata(pdev, rkvdec); - rkvdec->dev = &pdev->dev; - mutex_init(&rkvdec->vdev_lock); - INIT_DELAYED_WORK(&rkvdec->watchdog_work, rkvdec_watchdog_func); - - rkvdec->clocks = devm_kcalloc(&pdev->dev, ARRAY_SIZE(rkvdec_clk_names), - sizeof(*rkvdec->clocks), GFP_KERNEL); - if (!rkvdec->clocks) - return -ENOMEM; - - for (i = 0; i < ARRAY_SIZE(rkvdec_clk_names); i++) - rkvdec->clocks[i].id = rkvdec_clk_names[i]; - - ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(rkvdec_clk_names), - rkvdec->clocks); - if (ret) - return ret; - - rkvdec->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(rkvdec->regs)) - return PTR_ERR(rkvdec->regs); - - ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (ret) { - dev_err(&pdev->dev, "Could not set DMA coherent mask.\n"); - return ret; - } - - if (iommu_get_domain_for_dev(&pdev->dev)) { - rkvdec->empty_domain = iommu_paging_domain_alloc(rkvdec->dev); - - if (!rkvdec->empty_domain) - dev_warn(rkvdec->dev, "cannot alloc new empty domain\n"); - } - - vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); - - irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return -ENXIO; - - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - rkvdec_irq_handler, IRQF_ONESHOT, - dev_name(&pdev->dev), rkvdec); - if (ret) { - dev_err(&pdev->dev, "Could not request vdec IRQ\n"); - return ret; - } - - pm_runtime_set_autosuspend_delay(&pdev->dev, 100); - pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_enable(&pdev->dev); - - ret = rkvdec_v4l2_init(rkvdec); - if (ret) - goto err_disable_runtime_pm; - - return 0; - -err_disable_runtime_pm: - pm_runtime_dont_use_autosuspend(&pdev->dev); - pm_runtime_disable(&pdev->dev); - return ret; -} - -static void rkvdec_remove(struct platform_device *pdev) -{ - struct rkvdec_dev *rkvdec = platform_get_drvdata(pdev); - - cancel_delayed_work_sync(&rkvdec->watchdog_work); - - rkvdec_v4l2_cleanup(rkvdec); - pm_runtime_disable(&pdev->dev); - pm_runtime_dont_use_autosuspend(&pdev->dev); - - if (rkvdec->empty_domain) - iommu_domain_free(rkvdec->empty_domain); -} - -#ifdef CONFIG_PM -static int rkvdec_runtime_resume(struct device *dev) -{ - struct rkvdec_dev *rkvdec = dev_get_drvdata(dev); - - return clk_bulk_prepare_enable(ARRAY_SIZE(rkvdec_clk_names), - rkvdec->clocks); -} - -static int rkvdec_runtime_suspend(struct device *dev) -{ - struct rkvdec_dev *rkvdec = dev_get_drvdata(dev); - - clk_bulk_disable_unprepare(ARRAY_SIZE(rkvdec_clk_names), - rkvdec->clocks); - return 0; -} -#endif - -static const struct dev_pm_ops rkvdec_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(rkvdec_runtime_suspend, rkvdec_runtime_resume, NULL) -}; - -static struct platform_driver rkvdec_driver = { - .probe = rkvdec_probe, - .remove = rkvdec_remove, - .driver = { - .name = "rkvdec", - .of_match_table = of_rkvdec_match, - .pm = &rkvdec_pm_ops, - }, -}; -module_platform_driver(rkvdec_driver); - -MODULE_AUTHOR("Boris Brezillon "); -MODULE_DESCRIPTION("Rockchip Video Decoder driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/rkvdec/rkvdec.h b/drivers/staging/media/rkvdec/rkvdec.h deleted file mode 100644 index f6e8bf38add3..000000000000 --- a/drivers/staging/media/rkvdec/rkvdec.h +++ /dev/null @@ -1,144 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Rockchip Video Decoder driver - * - * Copyright (C) 2019 Collabora, Ltd. - * - * Based on rkvdec driver by Google LLC. (Tomasz Figa ) - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - */ -#ifndef RKVDEC_H_ -#define RKVDEC_H_ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -struct rkvdec_ctx; - -struct rkvdec_ctrl_desc { - struct v4l2_ctrl_config cfg; -}; - -struct rkvdec_ctrls { - const struct rkvdec_ctrl_desc *ctrls; - unsigned int num_ctrls; -}; - -struct rkvdec_run { - struct { - struct vb2_v4l2_buffer *src; - struct vb2_v4l2_buffer *dst; - } bufs; -}; - -struct rkvdec_vp9_decoded_buffer_info { - /* Info needed when the decoded frame serves as a reference frame. */ - unsigned short width; - unsigned short height; - unsigned int bit_depth : 4; -}; - -struct rkvdec_decoded_buffer { - /* Must be the first field in this struct. */ - struct v4l2_m2m_buffer base; - - union { - struct rkvdec_vp9_decoded_buffer_info vp9; - }; -}; - -static inline struct rkvdec_decoded_buffer * -vb2_to_rkvdec_decoded_buf(struct vb2_buffer *buf) -{ - return container_of(buf, struct rkvdec_decoded_buffer, - base.vb.vb2_buf); -} - -struct rkvdec_coded_fmt_ops { - int (*adjust_fmt)(struct rkvdec_ctx *ctx, - struct v4l2_format *f); - int (*start)(struct rkvdec_ctx *ctx); - void (*stop)(struct rkvdec_ctx *ctx); - int (*run)(struct rkvdec_ctx *ctx); - void (*done)(struct rkvdec_ctx *ctx, struct vb2_v4l2_buffer *src_buf, - struct vb2_v4l2_buffer *dst_buf, - enum vb2_buffer_state result); - int (*try_ctrl)(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl); - enum rkvdec_image_fmt (*get_image_fmt)(struct rkvdec_ctx *ctx, - struct v4l2_ctrl *ctrl); -}; - -enum rkvdec_image_fmt { - RKVDEC_IMG_FMT_ANY = 0, - RKVDEC_IMG_FMT_420_8BIT, - RKVDEC_IMG_FMT_420_10BIT, - RKVDEC_IMG_FMT_422_8BIT, - RKVDEC_IMG_FMT_422_10BIT, -}; - -struct rkvdec_decoded_fmt_desc { - u32 fourcc; - enum rkvdec_image_fmt image_fmt; -}; - -struct rkvdec_coded_fmt_desc { - u32 fourcc; - struct v4l2_frmsize_stepwise frmsize; - const struct rkvdec_ctrls *ctrls; - const struct rkvdec_coded_fmt_ops *ops; - unsigned int num_decoded_fmts; - const struct rkvdec_decoded_fmt_desc *decoded_fmts; - u32 subsystem_flags; -}; - -struct rkvdec_dev { - struct v4l2_device v4l2_dev; - struct media_device mdev; - struct video_device vdev; - struct v4l2_m2m_dev *m2m_dev; - struct device *dev; - struct clk_bulk_data *clocks; - void __iomem *regs; - struct mutex vdev_lock; /* serializes ioctls */ - struct delayed_work watchdog_work; - struct iommu_domain *empty_domain; -}; - -struct rkvdec_ctx { - struct v4l2_fh fh; - struct v4l2_format coded_fmt; - struct v4l2_format decoded_fmt; - const struct rkvdec_coded_fmt_desc *coded_fmt_desc; - struct v4l2_ctrl_handler ctrl_hdl; - struct rkvdec_dev *dev; - enum rkvdec_image_fmt image_fmt; - void *priv; -}; - -static inline struct rkvdec_ctx *fh_to_rkvdec_ctx(struct v4l2_fh *fh) -{ - return container_of(fh, struct rkvdec_ctx, fh); -} - -struct rkvdec_aux_buf { - void *cpu; - dma_addr_t dma; - size_t size; -}; - -void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run); -void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run); - -extern const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops; -extern const struct rkvdec_coded_fmt_ops rkvdec_vp9_fmt_ops; - -#endif /* RKVDEC_H_ */ -- cgit v1.2.3