From f3f18c178ab6525bec31bffac69239a0ebecba3d Mon Sep 17 00:00:00 2001 From: linya14x Date: Thu, 22 Jan 2026 15:31:48 +0800 Subject: [PATCH] Patch: Staging add psys driver Signed-off-by: linya14x --- ...ng-add-enable-CONFIG_INTEL_IPU7_ACPI.patch | 404 --- .../0020-Staging-add-psys-driver.patch | 2856 +++++++++++++++++ 2 files changed, 2856 insertions(+), 404 deletions(-) delete mode 100644 patch/v6.17_iot/0004-staging-add-enable-CONFIG_INTEL_IPU7_ACPI.patch create mode 100644 patch/v6.17_iot/0020-Staging-add-psys-driver.patch diff --git a/patch/v6.17_iot/0004-staging-add-enable-CONFIG_INTEL_IPU7_ACPI.patch b/patch/v6.17_iot/0004-staging-add-enable-CONFIG_INTEL_IPU7_ACPI.patch deleted file mode 100644 index 22e0123..0000000 --- a/patch/v6.17_iot/0004-staging-add-enable-CONFIG_INTEL_IPU7_ACPI.patch +++ /dev/null @@ -1,404 +0,0 @@ -From cfe2cce760509c0731bb9d93c07edf9770ca4230 Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 12:16:13 +0800 -Subject: [PATCH] patch: staging add enable CONFIG_INTEL_IPU_ACPI - -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/ipu7-isys.c | 231 ++++++++++++++++++++++++- - drivers/staging/media/ipu7/ipu7-isys.h | 15 ++ - drivers/staging/media/ipu7/ipu7.c | 10 ++ - drivers/staging/media/ipu7/ipu7.h | 3 + - 4 files changed, 257 insertions(+), 2 deletions(-) - -diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c -index 44ea4eef19..b71e2841de 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.c -+++ b/drivers/staging/media/ipu7/ipu7-isys.c -@@ -96,6 +96,126 @@ skip_unregister_subdev: - return ret; - } - -+struct isys_i2c_test { -+ u8 bus_nr; -+ u16 addr; -+ struct i2c_client *client; -+}; -+ -+static int isys_i2c_test(struct device *dev, void *priv) -+{ -+ struct i2c_client *client = i2c_verify_client(dev); -+ struct isys_i2c_test *test = priv; -+ -+ if (!client) -+ return 0; -+ -+ if (i2c_adapter_id(client->adapter) != test->bus_nr || -+ client->addr != test->addr) -+ return 0; -+ -+ test->client = client; -+ -+ return 0; -+} -+ -+static -+struct i2c_client *isys_find_i2c_subdev(struct i2c_adapter *adapter, -+ struct ipu7_isys_subdev_info *sd_info) -+{ -+ struct i2c_board_info *info = &sd_info->i2c.board_info; -+ struct isys_i2c_test test = { -+ .bus_nr = i2c_adapter_id(adapter), -+ .addr = info->addr, -+ }; -+ int ret; -+ -+ ret = i2c_for_each_dev(&test, isys_i2c_test); -+ if (ret || !test.client) -+ return NULL; -+ return test.client; -+} -+ -+static int isys_register_ext_subdev(struct ipu7_isys *isys, -+ struct ipu7_isys_subdev_info *sd_info) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct i2c_adapter *adapter; -+ struct v4l2_subdev *sd; -+ struct i2c_client *client; -+ int ret; -+ int bus; -+ -+ bus = sd_info->i2c.i2c_adapter_id; -+ adapter = i2c_get_adapter(bus); -+ if (!adapter) { -+ dev_warn(dev, "can't find adapter\n"); -+ return -ENOENT; -+ } -+ -+ dev_info(dev, "creating i2c subdev for %s (address %2.2x, bus %d)\n", -+ sd_info->i2c.board_info.type, sd_info->i2c.board_info.addr, -+ bus); -+ -+ if (sd_info->csi2) { -+ dev_info(dev, "sensor device on CSI port: %d\n", -+ sd_info->csi2->port); -+ if (sd_info->csi2->port >= isys->pdata->ipdata->csi2.nports || -+ !isys->csi2[sd_info->csi2->port].isys) { -+ dev_warn(dev, "invalid csi2 port %u\n", -+ sd_info->csi2->port); -+ ret = -EINVAL; -+ goto skip_put_adapter; -+ } -+ } else { -+ dev_info(dev, "No camera subdevice\n"); -+ } -+ -+ client = isys_find_i2c_subdev(adapter, sd_info); -+ if (client) { -+ dev_warn(dev, "Device exists\n"); -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) -+ /* TODO: remove i2c_unregister_device() */ -+ i2c_unregister_device(client); -+#else -+ ret = 0; -+ goto skip_put_adapter; -+#endif -+ } -+ -+ sd = v4l2_i2c_new_subdev_board(&isys->v4l2_dev, adapter, -+ &sd_info->i2c.board_info, NULL); -+ if (!sd) { -+ dev_warn(dev, "can't create new i2c subdev\n"); -+ ret = -EINVAL; -+ goto skip_put_adapter; -+ } -+ -+ if (!sd_info->csi2) -+ return 0; -+ -+ return isys_complete_ext_device_registration(isys, sd, sd_info->csi2); -+ -+skip_put_adapter: -+ i2c_put_adapter(adapter); -+ -+ return ret; -+} -+ -+static void isys_register_ext_subdevs(struct ipu7_isys *isys) -+{ -+ struct ipu7_isys_subdev_pdata *spdata = isys->pdata->spdata; -+ struct ipu7_isys_subdev_info **sd_info; -+ -+ if (!spdata) { -+ dev_info(&isys->adev->auxdev.dev, -+ "no subdevice info provided\n"); -+ return; -+ } -+ for (sd_info = spdata->subdevs; *sd_info; sd_info++) -+ isys_register_ext_subdev(isys, *sd_info); -+} -+ - static void isys_stream_init(struct ipu7_isys *isys) - { - unsigned int i; -@@ -142,6 +262,7 @@ static int isys_fw_log_init(struct ipu7_isys *isys) - return 0; - } - -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) - /* The .bound() notifier callback when a match is found */ - static int isys_notifier_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, -@@ -260,6 +381,7 @@ static void isys_notifier_cleanup(struct ipu7_isys *isys) - v4l2_async_nf_unregister(&isys->notifier); - v4l2_async_nf_cleanup(&isys->notifier); - } -+#endif - - static void isys_unregister_video_devices(struct ipu7_isys *isys) - { -@@ -374,6 +496,73 @@ static int isys_csi2_create_media_links(struct ipu7_isys *isys) - return 0; - } - -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) -+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; -+ -+ if (!isys->pdata->spdata) { -+ ret = isys_notifier_init(isys); -+ if (ret) -+ goto out_csi2_unregister_subdevices; -+ } else { -+ isys_register_ext_subdevs(isys); -+ ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -+ 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; -+} -+#else - static int isys_register_devices(struct ipu7_isys *isys) - { - struct device *dev = &isys->adev->auxdev.dev; -@@ -409,7 +598,8 @@ static int isys_register_devices(struct ipu7_isys *isys) - if (ret) - goto out_csi2_unregister_subdevices; - -- ret = isys_notifier_init(isys); -+ isys_register_ext_subdevs(isys); -+ ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); - if (ret) - goto out_csi2_unregister_subdevices; - -@@ -432,6 +622,7 @@ out_media_device_unregister: - - return ret; - } -+#endif - - static void isys_unregister_devices(struct ipu7_isys *isys) - { -@@ -579,6 +770,7 @@ static const struct dev_pm_ops isys_pm_ops = { - .resume = isys_resume, - }; - -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) - static void isys_remove(struct auxiliary_device *auxdev) - { - struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); -@@ -600,7 +792,9 @@ static void isys_remove(struct auxiliary_device *auxdev) - ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), - fwmsg, fwmsg->dma_addr, 0); - -- isys_notifier_cleanup(isys); -+ if (!isys->pdata->spdata) -+ isys_notifier_cleanup(isys); -+ - isys_unregister_devices(isys); - - cpu_latency_qos_remove_request(&isys->pm_qos); -@@ -611,6 +805,39 @@ static void isys_remove(struct auxiliary_device *auxdev) - mutex_destroy(&isys->reset_mutex); - #endif - } -+#else -+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); -+ -+#ifdef CONFIG_DEBUG_FS -+ if (adev->isp->ipu7_dir) -+ debugfs_remove_recursive(isys->debugfsdir); -+#endif -+ 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_unregister_devices(isys); -+ -+ cpu_latency_qos_remove_request(&isys->pm_qos); -+ -+ mutex_destroy(&isys->stream_mutex); -+ mutex_destroy(&isys->mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_destroy(&isys->reset_mutex); -+#endif -+} -+#endif - - #ifdef CONFIG_DEBUG_FS - static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, -diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h -index cd73754174..2e45258bb6 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.h -+++ b/drivers/staging/media/ipu7/ipu7-isys.h -@@ -150,6 +150,21 @@ struct ipu7_isys_csi2_config { - enum v4l2_mbus_type bus_type; - }; - -+struct ipu7_isys_subdev_i2c_info { -+ struct i2c_board_info board_info; -+ int i2c_adapter_id; -+ char i2c_adapter_bdf[32]; -+}; -+ -+struct ipu7_isys_subdev_info { -+ struct ipu7_isys_csi2_config *csi2; -+ struct ipu7_isys_subdev_i2c_info i2c; -+}; -+ -+struct ipu7_isys_subdev_pdata { -+ struct ipu7_isys_subdev_info **subdevs; -+}; -+ - struct sensor_async_sd { - struct v4l2_async_connection asc; - struct ipu7_isys_csi2_config csi2; -diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c -index 5857c4a4bd..e4dab55fbb 100644 ---- a/drivers/staging/media/ipu7/ipu7.c -+++ b/drivers/staging/media/ipu7/ipu7.c -@@ -40,6 +40,10 @@ - #include "ipu7-mmu.h" - #include "ipu7-platform-regs.h" - -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) -+#include -+ -+#endif - #define IPU_PCI_BAR 0 - #define IPU_PCI_PBBAR 4 - -@@ -2130,6 +2134,7 @@ static int ipu7_isys_check_fwnode_graph(struct fwnode_handle *fwnode) - static struct ipu7_bus_device * - ipu7_isys_init(struct pci_dev *pdev, struct device *parent, - const struct ipu_buttress_ctrl *ctrl, void __iomem *base, -+ struct ipu7_isys_subdev_pdata *spdata, - const struct ipu_isys_internal_pdata *ipdata, - unsigned int nr) - { -@@ -2160,6 +2165,7 @@ ipu7_isys_init(struct pci_dev *pdev, struct device *parent, - - pdata->base = base; - pdata->ipdata = ipdata; -+ pdata->spdata = spdata; - - isys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl, - IPU_ISYS_NAME); -@@ -2588,7 +2594,11 @@ static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) - goto out_ipu_bus_del_devices; - } - -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) -+ ipu_get_acpi_devices_new(&dev->platform_data); -+#endif - isp->isys = ipu7_isys_init(pdev, dev, isys_ctrl, isys_base, -+ dev->platform_data, - isys_ipdata, 0); - if (IS_ERR(isp->isys)) { - ret = PTR_ERR(isp->isys); -diff --git a/drivers/staging/media/ipu7/ipu7.h b/drivers/staging/media/ipu7/ipu7.h -index 3b3ea5fb61..21988ce41a 100644 ---- a/drivers/staging/media/ipu7/ipu7.h -+++ b/drivers/staging/media/ipu7/ipu7.h -@@ -144,6 +144,8 @@ struct ipu7_device { - /* Currently chosen arbitration mechanism for VC1 */ - #define IPU_BTRS_ARB_STALL_MODE_VC1 IPU_BTRS_ARB_MODE_TYPE_REARB - -+struct ipu7_isys_subdev_pdata; -+ - /* 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 */ -@@ -226,6 +228,7 @@ struct ipu_isys_internal_pdata { - struct ipu7_isys_pdata { - void __iomem *base; - const struct ipu_isys_internal_pdata *ipdata; -+ struct ipu7_isys_subdev_pdata *spdata; - }; - - struct ipu_psys_internal_pdata { --- -2.43.0 - diff --git a/patch/v6.17_iot/0020-Staging-add-psys-driver.patch b/patch/v6.17_iot/0020-Staging-add-psys-driver.patch new file mode 100644 index 0000000..0c0997f --- /dev/null +++ b/patch/v6.17_iot/0020-Staging-add-psys-driver.patch @@ -0,0 +1,2856 @@ +From 3704bf588a9be93b2acdae702c2d879d40c25e3d Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Thu, 22 Jan 2026 15:39:18 +0800 +Subject: [PATCH] Staging add psys driver + +Signed-off-by: linya14x +--- + drivers/staging/media/ipu7/psys/Makefile | 18 + + drivers/staging/media/ipu7/psys/ipu-psys.c | 1551 +++++++++++++++++ + .../staging/media/ipu7/psys/ipu7-fw-psys.c | 603 +++++++ + .../staging/media/ipu7/psys/ipu7-fw-psys.h | 42 + + drivers/staging/media/ipu7/psys/ipu7-psys.c | 398 +++++ + drivers/staging/media/ipu7/psys/ipu7-psys.h | 184 ++ + 6 files changed, 2796 insertions(+) + create mode 100644 drivers/staging/media/ipu7/psys/Makefile + create mode 100644 drivers/staging/media/ipu7/psys/ipu-psys.c + create mode 100644 drivers/staging/media/ipu7/psys/ipu7-fw-psys.c + create mode 100644 drivers/staging/media/ipu7/psys/ipu7-fw-psys.h + create mode 100644 drivers/staging/media/ipu7/psys/ipu7-psys.c + create mode 100644 drivers/staging/media/ipu7/psys/ipu7-psys.h + +diff --git a/drivers/staging/media/ipu7/psys/Makefile b/drivers/staging/media/ipu7/psys/Makefile +new file mode 100644 +index 000000000000..33eb383a14bc +--- /dev/null ++++ b/drivers/staging/media/ipu7/psys/Makefile +@@ -0,0 +1,18 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# Copyright (c) 2017 - 2024 Intel Corporation. ++ ++is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) ++ifeq ($(is_kernel_lt_6_10), 1) ++ifneq ($(EXTERNAL_BUILD), 1) ++src := $(srctree)/$(src) ++endif ++endif ++ ++intel-ipu7-psys-objs += ipu-psys.o \ ++ ipu7-psys.o \ ++ ipu7-fw-psys.o ++ ++obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-psys.o ++ ++ccflags-y += -I$(src)/ ++ccflags-y += -I$(src)/../ +diff --git a/drivers/staging/media/ipu7/psys/ipu-psys.c b/drivers/staging/media/ipu7/psys/ipu-psys.c +new file mode 100644 +index 000000000000..d6f3cea52f8a +--- /dev/null ++++ b/drivers/staging/media/ipu7/psys/ipu-psys.c +@@ -0,0 +1,1551 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2013 - 2024 Intel Corporation ++ ++#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-mmu.h" ++#include "ipu7-bus.h" ++#include "ipu7-buttress.h" ++#include "ipu7-cpd.h" ++#include "ipu7-dma.h" ++#include "ipu7-fw-psys.h" ++#include "ipu7-psys.h" ++#include "ipu7-platform-regs.h" ++#include "ipu7-syscom.h" ++#include "ipu7-boot.h" ++ ++#define IPU_PSYS_NUM_DEVICES 4U ++ ++static int psys_runtime_pm_resume(struct device *dev); ++static int psys_runtime_pm_suspend(struct device *dev); ++ ++#define IPU_FW_CALL_TIMEOUT_JIFFIES \ ++ msecs_to_jiffies(IPU_PSYS_CMD_TIMEOUT_MS) ++ ++static dev_t ipu7_psys_dev_t; ++static DECLARE_BITMAP(ipu7_psys_devices, IPU_PSYS_NUM_DEVICES); ++static DEFINE_MUTEX(ipu7_psys_mutex); ++ ++static int ipu7_psys_get_userpages(struct ipu7_dma_buf_attach *attach) ++{ ++ struct vm_area_struct *vma; ++ unsigned long start, end; ++ int npages, array_size; ++ struct page **pages; ++ struct sg_table *sgt; ++ int ret = -ENOMEM; ++ int nr = 0; ++ u32 flags; ++ ++ start = (unsigned long)attach->userptr; ++ end = PAGE_ALIGN(start + attach->len); ++ npages = PHYS_PFN(end - (start & PAGE_MASK)); ++ array_size = npages * sizeof(struct page *); ++ ++ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); ++ if (!sgt) ++ return -ENOMEM; ++ ++ WARN_ON_ONCE(attach->npages); ++ ++ pages = kvzalloc(array_size, GFP_KERNEL); ++ if (!pages) ++ goto free_sgt; ++ ++ mmap_read_lock(current->mm); ++ vma = vma_lookup(current->mm, start); ++ if (unlikely(!vma)) { ++ ret = -EFAULT; ++ goto error_up_read; ++ } ++ mmap_read_unlock(current->mm); ++ ++ flags = FOLL_WRITE | FOLL_FORCE | FOLL_LONGTERM; ++ nr = pin_user_pages_fast(start & PAGE_MASK, npages, ++ flags, pages); ++ if (nr < npages) ++ goto error; ++ ++ attach->pages = pages; ++ attach->npages = npages; ++ ++ ret = sg_alloc_table_from_pages(sgt, pages, npages, ++ start & ~PAGE_MASK, attach->len, ++ GFP_KERNEL); ++ if (ret < 0) ++ goto error; ++ ++ attach->sgt = sgt; ++ ++ return 0; ++ ++error_up_read: ++ mmap_read_unlock(current->mm); ++error: ++ if (nr) ++ unpin_user_pages(pages, nr); ++ ++ kvfree(pages); ++free_sgt: ++ kfree(sgt); ++ ++ pr_err("failed to get userpages:%d\n", ret); ++ ++ return ret; ++} ++ ++static void ipu7_psys_put_userpages(struct ipu7_dma_buf_attach *attach) ++{ ++ if (!attach || !attach->userptr || !attach->sgt) ++ return; ++ ++ unpin_user_pages(attach->pages, attach->npages); ++ ++ kvfree(attach->pages); ++ ++ sg_free_table(attach->sgt); ++ kfree(attach->sgt); ++ attach->sgt = NULL; ++} ++ ++static int ipu7_dma_buf_attach(struct dma_buf *dbuf, ++ struct dma_buf_attachment *attach) ++{ ++ struct ipu7_psys_kbuffer *kbuf = dbuf->priv; ++ struct ipu7_dma_buf_attach *ipu7_attach; ++ int ret; ++ ++ ipu7_attach = kzalloc(sizeof(*ipu7_attach), GFP_KERNEL); ++ if (!ipu7_attach) ++ return -ENOMEM; ++ ++ ipu7_attach->len = kbuf->len; ++ ipu7_attach->userptr = kbuf->userptr; ++ ++ attach->priv = ipu7_attach; ++ ++ ret = ipu7_psys_get_userpages(ipu7_attach); ++ if (ret) { ++ kfree(ipu7_attach); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void ipu7_dma_buf_detach(struct dma_buf *dbuf, ++ struct dma_buf_attachment *attach) ++{ ++ struct ipu7_dma_buf_attach *ipu7_attach = attach->priv; ++ ++ ipu7_psys_put_userpages(ipu7_attach); ++ kfree(ipu7_attach); ++ attach->priv = NULL; ++} ++ ++static struct sg_table *ipu7_dma_buf_map(struct dma_buf_attachment *attach, ++ enum dma_data_direction dir) ++{ ++ struct ipu7_dma_buf_attach *ipu7_attach = attach->priv; ++ struct pci_dev *pdev = to_pci_dev(attach->dev); ++ struct ipu7_device *isp = pci_get_drvdata(pdev); ++ struct ipu7_bus_device *adev = isp->psys; ++ unsigned long attrs; ++ int ret; ++ ++ attrs = DMA_ATTR_SKIP_CPU_SYNC; ++ ret = dma_map_sgtable(&pdev->dev, ipu7_attach->sgt, DMA_BIDIRECTIONAL, ++ attrs); ++ if (ret) { ++ dev_err(attach->dev, "pci buf map failed\n"); ++ return ERR_PTR(-EIO); ++ } ++ ++ dma_sync_sgtable_for_device(&pdev->dev, ipu7_attach->sgt, ++ DMA_BIDIRECTIONAL); ++ ++ ret = ipu7_dma_map_sgtable(adev, ipu7_attach->sgt, dir, 0); ++ if (ret) { ++ dev_err(attach->dev, "ipu7 buf map failed\n"); ++ return ERR_PTR(-EIO); ++ } ++ ++ ipu7_dma_sync_sgtable(adev, ipu7_attach->sgt); ++ ++ return ipu7_attach->sgt; ++} ++ ++static void ipu7_dma_buf_unmap(struct dma_buf_attachment *attach, ++ struct sg_table *sgt, ++ enum dma_data_direction dir) ++{ ++ struct pci_dev *pdev = to_pci_dev(attach->dev); ++ struct ipu7_device *isp = pci_get_drvdata(pdev); ++ struct ipu7_bus_device *adev = isp->psys; ++ ++ ipu7_dma_unmap_sgtable(adev, sgt, dir, DMA_ATTR_SKIP_CPU_SYNC); ++ dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); ++} ++ ++static int ipu7_dma_buf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma) ++{ ++ return -ENOTTY; ++} ++ ++static void ipu7_dma_buf_release(struct dma_buf *buf) ++{ ++ struct ipu7_psys_kbuffer *kbuf = buf->priv; ++ ++ if (!kbuf) ++ return; ++ ++ if (kbuf->db_attach) ++ ipu7_psys_put_userpages(kbuf->db_attach->priv); ++ ++ kfree(kbuf); ++} ++ ++static int ipu7_dma_buf_begin_cpu_access(struct dma_buf *dma_buf, ++ enum dma_data_direction dir) ++{ ++ return -ENOTTY; ++} ++ ++static int ipu7_dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map) ++{ ++ struct dma_buf_attachment *attach; ++ struct ipu7_dma_buf_attach *ipu7_attach; ++ ++ if (list_empty(&dmabuf->attachments)) ++ return -EINVAL; ++ ++ attach = list_last_entry(&dmabuf->attachments, ++ struct dma_buf_attachment, node); ++ ipu7_attach = attach->priv; ++ ++ if (!ipu7_attach || !ipu7_attach->pages || !ipu7_attach->npages) ++ return -EINVAL; ++ ++ map->vaddr = vm_map_ram(ipu7_attach->pages, ipu7_attach->npages, 0); ++ map->is_iomem = false; ++ if (!map->vaddr) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static void ipu7_dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map) ++{ ++ struct dma_buf_attachment *attach; ++ struct ipu7_dma_buf_attach *ipu7_attach; ++ ++ if (WARN_ON(list_empty(&dmabuf->attachments))) ++ return; ++ ++ attach = list_last_entry(&dmabuf->attachments, ++ struct dma_buf_attachment, node); ++ ipu7_attach = attach->priv; ++ ++ if (WARN_ON(!ipu7_attach || !ipu7_attach->pages || ++ !ipu7_attach->npages)) ++ return; ++ ++ vm_unmap_ram(map->vaddr, ipu7_attach->npages); ++} ++ ++static struct dma_buf_ops ipu7_dma_buf_ops = { ++ .attach = ipu7_dma_buf_attach, ++ .detach = ipu7_dma_buf_detach, ++ .map_dma_buf = ipu7_dma_buf_map, ++ .unmap_dma_buf = ipu7_dma_buf_unmap, ++ .release = ipu7_dma_buf_release, ++ .begin_cpu_access = ipu7_dma_buf_begin_cpu_access, ++ .mmap = ipu7_dma_buf_mmap, ++ .vmap = ipu7_dma_buf_vmap, ++ .vunmap = ipu7_dma_buf_vunmap, ++}; ++ ++static int ipu7_psys_get_graph_id(struct ipu7_psys_fh *fh) ++{ ++ u8 graph_id = 0; ++ ++ for (graph_id = 0; graph_id < IPU_PSYS_NUM_STREAMS; graph_id++) { ++ if (fh->psys->graph_id[graph_id] == INVALID_STREAM_ID) ++ break; ++ } ++ ++ if (graph_id == IPU_PSYS_NUM_STREAMS) ++ return -EBUSY; ++ ++ fh->psys->graph_id[graph_id] = graph_id; ++ return graph_id; ++} ++ ++static void ipu7_psys_put_graph_id(struct ipu7_psys_fh *fh) ++{ ++ fh->psys->graph_id[fh->ip->graph_id] = INVALID_STREAM_ID; ++} ++ ++static void ipu7_psys_free_msg_task(struct ipu_psys_task_queue *tq, ++ struct ipu7_bus_device *adev) ++{ ++ if (tq->msg_task) ++ ipu7_dma_free(adev, sizeof(struct ipu7_msg_task), ++ tq->msg_task, tq->task_dma_addr, 0); ++ ++ list_del(&tq->list); ++ kfree(tq); ++} ++ ++static void ipu7_psys_stream_deinit(struct ipu7_psys_stream *ip, ++ struct ipu7_bus_device *adev) ++{ ++ struct ipu_psys_task_ack *ack; ++ struct ipu_psys_task_ack *event; ++ struct ipu_psys_task_ack *tmp; ++ ++ struct ipu_psys_task_queue *tq; ++ struct ipu_psys_task_queue *tq_tmp; ++ ++ mutex_destroy(&ip->event_mutex); ++ mutex_destroy(&ip->task_mutex); ++ ++ list_for_each_entry_safe(tq, tq_tmp, &ip->tq_list, list) { ++ ipu7_psys_free_msg_task(tq, adev); ++ } ++ ++ list_for_each_entry_safe(tq, tq_tmp, &ip->tq_running_list, list) { ++ ipu7_psys_free_msg_task(tq, adev); ++ } ++ ++ list_for_each_entry_safe(event, tmp, &ip->event_list, list) { ++ list_del(&event->list); ++ kfree(event); ++ } ++ ++ list_for_each_entry_safe(ack, tmp, &ip->ack_list, list) { ++ list_del(&ack->list); ++ kfree(ack); ++ } ++} ++ ++static int ipu7_psys_stream_init(struct ipu7_psys_stream *ip, ++ struct ipu7_bus_device *adev) ++{ ++ struct device *dev = &adev->auxdev.dev; ++ struct ipu_psys_task_ack *event; ++ struct ipu_psys_task_ack *tmp; ++ struct ipu_psys_task_queue *tq; ++ struct ipu_psys_task_queue *tq_tmp; ++ u8 i; ++ ++ INIT_LIST_HEAD(&ip->event_list); ++ INIT_LIST_HEAD(&ip->ack_list); ++ ++ INIT_LIST_HEAD(&ip->tq_running_list); ++ INIT_LIST_HEAD(&ip->tq_list); ++ ++ for (i = 0; i < TASK_EVENT_QUEUE_SIZE; i++) { ++ event = kzalloc(sizeof(*event), GFP_KERNEL); ++ if (!event) ++ goto event_cleanup; ++ ++ list_add(&event->list, &ip->event_list); ++ } ++ ++ for (i = 0; i < TASK_REQUEST_QUEUE_SIZE; i++) { ++ tq = kzalloc(sizeof(*tq), GFP_KERNEL); ++ if (!tq) ++ goto tq_cleanup; ++ ++ list_add(&tq->list, &ip->tq_list); ++ ++ tq->msg_task = ++ ipu7_dma_alloc(adev, sizeof(struct ipu7_msg_task), ++ &tq->task_dma_addr, GFP_KERNEL, 0); ++ ++ if (!tq->msg_task) { ++ dev_err(dev, "Failed to allocate msg task.\n"); ++ goto tq_cleanup; ++ } ++ } ++ ++ init_completion(&ip->graph_open); ++ init_completion(&ip->graph_close); ++ ++ return 0; ++ ++tq_cleanup: ++ list_for_each_entry_safe(tq, tq_tmp, &ip->tq_list, list) { ++ ipu7_psys_free_msg_task(tq, adev); ++ } ++ ++event_cleanup: ++ list_for_each_entry_safe(event, tmp, &ip->event_list, list) { ++ list_del(&event->list); ++ kfree(event); ++ } ++ ++ return -ENOMEM; ++} ++ ++static int ipu7_psys_open(struct inode *inode, struct file *file) ++{ ++ struct ipu7_psys *psys = inode_to_ipu_psys(inode); ++ struct device *dev = &psys->adev->auxdev.dev; ++ struct ipu7_psys_fh *fh; ++ struct ipu7_psys_stream *ip; ++ int ret; ++ ++ fh = kzalloc(sizeof(*fh), GFP_KERNEL); ++ if (!fh) ++ return -ENOMEM; ++ ++ ip = kzalloc(sizeof(*ip), GFP_KERNEL); ++ if (!ip) { ++ ret = -ENOMEM; ++ goto alloc_failed; ++ } ++ ++ ret = ipu7_psys_stream_init(ip, psys->adev); ++ if (ret) ++ goto stream_init_failed; ++ ++ fh->ip = ip; ++ ip->fh = fh; ++ ++ fh->psys = psys; ++ ++ file->private_data = fh; ++ ++ mutex_init(&fh->mutex); ++ INIT_LIST_HEAD(&fh->bufmap); ++ init_waitqueue_head(&fh->wait); ++ ++ mutex_init(&ip->task_mutex); ++ mutex_init(&ip->event_mutex); ++ ++ mutex_lock(&psys->mutex); ++ ++ ret = ipu7_psys_get_graph_id(fh); ++ if (ret < 0) ++ goto open_failed; ++ ++ fh->ip->graph_id = ret; ++ ++ ret = pm_runtime_get_sync(dev); ++ if (ret < 0) { ++ dev_err(dev, "Runtime PM failed (%d)\n", ret); ++ goto rpm_put; ++ } ++ ++ list_add_tail(&fh->list, &psys->fhs); ++ ++ mutex_unlock(&psys->mutex); ++ ++ return 0; ++ ++rpm_put: ++ pm_runtime_put(dev); ++ ipu7_psys_put_graph_id(fh); ++ ++open_failed: ++ ipu7_psys_stream_deinit(ip, psys->adev); ++ ++ mutex_destroy(&fh->mutex); ++ ++ mutex_unlock(&psys->mutex); ++ ++stream_init_failed: ++ kfree(ip); ++ ++alloc_failed: ++ kfree(fh); ++ ++ return ret; ++} ++ ++static inline void ipu7_psys_kbuf_unmap(struct ipu7_psys_fh *fh, ++ struct ipu7_psys_kbuffer *kbuf) ++{ ++ if (!kbuf) ++ return; ++ ++ kbuf->valid = false; ++ if (kbuf->kaddr) { ++ struct iosys_map dmap; ++ ++ iosys_map_set_vaddr(&dmap, kbuf->kaddr); ++ dma_buf_vunmap_unlocked(kbuf->dbuf, &dmap); ++ } ++ ++ if (!kbuf->userptr) ++ ipu7_dma_unmap_sgtable(fh->psys->adev, kbuf->sgt, ++ DMA_BIDIRECTIONAL, 0); ++ ++ if (kbuf->sgt) ++ dma_buf_unmap_attachment_unlocked(kbuf->db_attach, ++ kbuf->sgt, ++ DMA_BIDIRECTIONAL); ++ if (kbuf->db_attach) ++ dma_buf_detach(kbuf->dbuf, kbuf->db_attach); ++ dma_buf_put(kbuf->dbuf); ++ ++ kbuf->db_attach = NULL; ++ kbuf->dbuf = NULL; ++ kbuf->sgt = NULL; ++} ++ ++static int ipu7_psys_release(struct inode *inode, struct file *file) ++{ ++ struct ipu7_psys *psys = inode_to_ipu_psys(inode); ++ struct ipu7_psys_fh *fh = file->private_data; ++ struct ipu7_psys_kbuffer *kbuf, *kbuf0; ++ struct dma_buf_attachment *dba; ++ ++ mutex_lock(&fh->mutex); ++ /* clean up buffers */ ++ if (!list_empty(&fh->bufmap)) { ++ list_for_each_entry_safe(kbuf, kbuf0, &fh->bufmap, list) { ++ list_del(&kbuf->list); ++ dba = kbuf->db_attach; ++ ++ /* Unmap and release buffers */ ++ if (kbuf->dbuf && dba) { ++ ipu7_psys_kbuf_unmap(fh, kbuf); ++ } else { ++ if (dba) ++ ipu7_psys_put_userpages(dba->priv); ++ kfree(kbuf); ++ } ++ } ++ } ++ mutex_unlock(&fh->mutex); ++ ++ ipu7_psys_stream_deinit(fh->ip, psys->adev); ++ ++ mutex_lock(&psys->mutex); ++ list_del(&fh->list); ++ ++ ipu7_psys_put_graph_id(fh); ++ kfree(fh->ip); ++ ++ mutex_unlock(&psys->mutex); ++ mutex_destroy(&fh->mutex); ++ kfree(fh); ++ ++ pm_runtime_put_sync(&psys->adev->auxdev.dev); ++ ++ return 0; ++} ++ ++static int ipu7_psys_getbuf(struct ipu_psys_buffer *buf, ++ struct ipu7_psys_fh *fh) ++{ ++ struct device *dev = &fh->psys->adev->auxdev.dev; ++ struct ipu7_psys_kbuffer *kbuf; ++ ++ DEFINE_DMA_BUF_EXPORT_INFO(exp_info); ++ struct dma_buf *dbuf; ++ int ret; ++ ++ if (!buf->base.userptr) { ++ dev_err(dev, "Buffer allocation not supported\n"); ++ return -EINVAL; ++ } ++ ++ if (!PAGE_ALIGNED(buf->base.userptr)) { ++ dev_err(dev, "Not page-aligned userptr is not supported\n"); ++ return -EINVAL; ++ } ++ ++ kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); ++ if (!kbuf) ++ return -ENOMEM; ++ ++ kbuf->len = buf->len; ++ kbuf->userptr = buf->base.userptr; ++ kbuf->flags = buf->flags; ++ ++ exp_info.ops = &ipu7_dma_buf_ops; ++ exp_info.size = kbuf->len; ++ exp_info.flags = O_RDWR; ++ exp_info.priv = kbuf; ++ ++ dbuf = dma_buf_export(&exp_info); ++ if (IS_ERR(dbuf)) { ++ kfree(kbuf); ++ return PTR_ERR(dbuf); ++ } ++ ++ ret = dma_buf_fd(dbuf, 0); ++ if (ret < 0) { ++ dma_buf_put(dbuf); ++ return ret; ++ } ++ ++ kbuf->fd = ret; ++ buf->base.fd = ret; ++ buf->flags &= ~IPU_BUFFER_FLAG_USERPTR; ++ buf->flags |= IPU_BUFFER_FLAG_DMA_HANDLE; ++ kbuf->flags = buf->flags; ++ ++ mutex_lock(&fh->mutex); ++ list_add(&kbuf->list, &fh->bufmap); ++ mutex_unlock(&fh->mutex); ++ ++ dev_dbg(dev, "IOC_GETBUF: userptr %p size %llu to fd %d", ++ buf->base.userptr, buf->len, buf->base.fd); ++ ++ return 0; ++} ++ ++static int ++ipu7_psys_putbuf(struct ipu_psys_buffer *buf, struct ipu7_psys_fh *fh) ++{ ++ return 0; ++} ++ ++static struct ipu7_psys_kbuffer * ++ipu7_psys_lookup_kbuffer(struct ipu7_psys_fh *fh, int fd) ++{ ++ struct ipu7_psys_kbuffer *kbuf; ++ ++ list_for_each_entry(kbuf, &fh->bufmap, list) { ++ if (kbuf->fd == fd) ++ return kbuf; ++ } ++ ++ return NULL; ++} ++ ++static int ipu7_psys_unmapbuf_locked(int fd, struct ipu7_psys_fh *fh, ++ struct ipu7_psys_kbuffer *kbuf) ++{ ++ struct device *dev = &fh->psys->adev->auxdev.dev; ++ ++ if (!kbuf || fd != kbuf->fd) { ++ dev_err(dev, "invalid kbuffer\n"); ++ return -EINVAL; ++ } ++ ++ /* From now on it is not safe to use this kbuffer */ ++ ipu7_psys_kbuf_unmap(fh, kbuf); ++ ++ list_del(&kbuf->list); ++ ++ if (!kbuf->userptr) ++ kfree(kbuf); ++ ++ dev_dbg(dev, "%s fd %d unmapped\n", __func__, fd); ++ ++ return 0; ++} ++ ++static int ipu7_psys_mapbuf_locked(int fd, struct ipu7_psys_fh *fh, ++ struct ipu7_psys_kbuffer *kbuf) ++{ ++ struct ipu7_psys *psys = fh->psys; ++ struct device *dev = &psys->adev->isp->pdev->dev; ++ struct dma_buf *dbuf; ++ struct iosys_map dmap; ++ int ret; ++ ++ dbuf = dma_buf_get(fd); ++ if (IS_ERR(dbuf)) ++ return -EINVAL; ++ ++ if (!kbuf) { ++ /* This fd isn't generated by ipu7_psys_getbuf, it ++ * is a new fd. Create a new kbuf item for this fd, and ++ * add this kbuf to bufmap list. ++ */ ++ kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); ++ if (!kbuf) { ++ ret = -ENOMEM; ++ goto mapbuf_fail; ++ } ++ ++ list_add(&kbuf->list, &fh->bufmap); ++ } ++ ++ /* fd valid and found, need remap */ ++ if (kbuf->dbuf && (kbuf->dbuf != dbuf || kbuf->len != dbuf->size)) { ++ dev_dbg(dev, "dmabuf fd %d with kbuf %p changed, need remap.\n", ++ fd, kbuf); ++ ret = ipu7_psys_unmapbuf_locked(fd, fh, kbuf); ++ if (ret) ++ goto mapbuf_fail; ++ ++ kbuf = ipu7_psys_lookup_kbuffer(fh, fd); ++ /* changed external dmabuf */ ++ if (!kbuf) { ++ kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); ++ if (!kbuf) { ++ ret = -ENOMEM; ++ goto mapbuf_fail; ++ } ++ list_add(&kbuf->list, &fh->bufmap); ++ } ++ } ++ ++ if (kbuf->sgt) { ++ dev_dbg(dev, "fd %d has been mapped!\n", fd); ++ dma_buf_put(dbuf); ++ goto mapbuf_end; ++ } ++ ++ kbuf->dbuf = dbuf; ++ ++ if (kbuf->len == 0) ++ kbuf->len = kbuf->dbuf->size; ++ ++ kbuf->fd = fd; ++ ++ kbuf->db_attach = dma_buf_attach(kbuf->dbuf, dev); ++ if (IS_ERR(kbuf->db_attach)) { ++ ret = PTR_ERR(kbuf->db_attach); ++ dev_err(dev, "dma buf attach failed\n"); ++ goto attach_fail; ++ } ++ ++ kbuf->sgt = dma_buf_map_attachment_unlocked(kbuf->db_attach, ++ DMA_BIDIRECTIONAL); ++ if (IS_ERR_OR_NULL(kbuf->sgt)) { ++ ret = -EINVAL; ++ kbuf->sgt = NULL; ++ dev_err(dev, "dma buf map attachment failed\n"); ++ goto kbuf_map_fail; ++ } ++ ++ if (!kbuf->userptr) { ++ ret = ipu7_dma_map_sgtable(psys->adev, kbuf->sgt, ++ DMA_BIDIRECTIONAL, 0); ++ if (ret) { ++ dev_dbg(dev, "ipu7 buf map failed\n"); ++ goto kbuf_map_fail; ++ } ++ } ++ ++ kbuf->dma_addr = sg_dma_address(kbuf->sgt->sgl); ++ ++ /* no need vmap for imported dmabufs */ ++ if (!kbuf->userptr) ++ goto mapbuf_end; ++ ++ dmap.is_iomem = false; ++ ret = dma_buf_vmap_unlocked(kbuf->dbuf, &dmap); ++ if (ret) { ++ dev_err(dev, "dma buf vmap failed\n"); ++ goto kbuf_map_fail; ++ } ++ kbuf->kaddr = dmap.vaddr; ++ ++mapbuf_end: ++ dev_dbg(dev, "%s %s kbuf %p fd %d with len %llu mapped\n", ++ __func__, kbuf->kaddr ? "private" : "imported", kbuf, fd, ++ kbuf->len); ++ kbuf->valid = true; ++ ++ return 0; ++ ++kbuf_map_fail: ++ if (!IS_ERR_OR_NULL(kbuf->sgt)) { ++ if (!kbuf->userptr) ++ ipu7_dma_unmap_sgtable(psys->adev, kbuf->sgt, ++ DMA_BIDIRECTIONAL, 0); ++ dma_buf_unmap_attachment_unlocked(kbuf->db_attach, ++ kbuf->sgt, ++ DMA_BIDIRECTIONAL); ++ } ++ dma_buf_detach(kbuf->dbuf, kbuf->db_attach); ++ ++attach_fail: ++ list_del(&kbuf->list); ++ if (!kbuf->userptr) ++ kfree(kbuf); ++ ++mapbuf_fail: ++ dma_buf_put(dbuf); ++ ++ dev_err(dev, "%s failed for fd %d\n", __func__, fd); ++ return ret; ++} ++ ++static long ipu7_psys_mapbuf(int fd, struct ipu7_psys_fh *fh) ++{ ++ long ret; ++ struct ipu7_psys_kbuffer *kbuf; ++ ++ dev_dbg(&fh->psys->adev->auxdev.dev, "IOC_MAPBUF\n"); ++ ++ mutex_lock(&fh->mutex); ++ kbuf = ipu7_psys_lookup_kbuffer(fh, fd); ++ ret = ipu7_psys_mapbuf_locked(fd, fh, kbuf); ++ mutex_unlock(&fh->mutex); ++ ++ return ret; ++} ++ ++static long ipu7_psys_unmapbuf(int fd, struct ipu7_psys_fh *fh) ++{ ++ struct device *dev = &fh->psys->adev->auxdev.dev; ++ struct ipu7_psys_kbuffer *kbuf; ++ long ret; ++ ++ dev_dbg(dev, "IOC_UNMAPBUF\n"); ++ ++ mutex_lock(&fh->mutex); ++ kbuf = ipu7_psys_lookup_kbuffer(fh, fd); ++ if (!kbuf) { ++ dev_err(dev, ++ "buffer with fd %d not found\n", fd); ++ mutex_unlock(&fh->mutex); ++ return -EINVAL; ++ } ++ ret = ipu7_psys_unmapbuf_locked(fd, fh, kbuf); ++ mutex_unlock(&fh->mutex); ++ ++ return ret; ++} ++ ++static long ipu_psys_graph_open(struct ipu_psys_graph_info *graph, ++ struct ipu7_psys_fh *fh) ++{ ++ struct ipu7_psys *psys = fh->psys; ++ int ret = 0; ++ ++ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSED) { ++ dev_err(&psys->dev, "Wrong state %d to open graph %d\n", ++ fh->ip->graph_state, fh->ip->graph_id); ++ return -EINVAL; ++ } ++ ++ if (!graph->nodes || graph->num_nodes > MAX_GRAPH_NODES) { ++ dev_err(&psys->dev, "nodes is wrong\n"); ++ return -EINVAL; ++ } ++ ++ if (copy_from_user(fh->ip->nodes, graph->nodes, ++ graph->num_nodes * sizeof(*graph->nodes))) { ++ dev_err(&psys->dev, "Failed to copy nodes\n"); ++ return -EINVAL; ++ } ++ ++ reinit_completion(&fh->ip->graph_open); ++ ++ ret = ipu7_fw_psys_graph_open(graph, psys, fh->ip); ++ if (ret) { ++ dev_err(&psys->dev, "Failed to open graph %d\n", ++ fh->ip->graph_id); ++ return ret; ++ } ++ ++ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_OPEN_WAIT; ++ ++ ret = wait_for_completion_timeout(&fh->ip->graph_open, ++ IPU_FW_CALL_TIMEOUT_JIFFIES); ++ if (!ret) { ++ dev_err(&psys->dev, "Open graph %d timeout\n", ++ fh->ip->graph_id); ++ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; ++ return -ETIMEDOUT; ++ } ++ ++ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN) { ++ dev_err(&psys->dev, "Failed to set graph\n"); ++ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; ++ return -EINVAL; ++ } ++ ++ graph->graph_id = fh->ip->graph_id; ++ ++ return 0; ++} ++ ++static long ipu_psys_graph_close(int graph_id, struct ipu7_psys_fh *fh) ++{ ++ struct ipu7_psys *psys = fh->psys; ++ int ret = 0; ++ ++ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN) { ++ dev_err(&psys->dev, "Wrong state %d to open graph %d\n", ++ fh->ip->graph_state, fh->ip->graph_id); ++ return -EINVAL; ++ } ++ ++ reinit_completion(&fh->ip->graph_close); ++ ++ ret = ipu7_fw_psys_graph_close(fh->ip->graph_id, fh->psys); ++ if (ret) { ++ dev_err(&psys->dev, "Failed to close graph %d\n", ++ fh->ip->graph_id); ++ return ret; ++ } ++ ++ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSE_WAIT; ++ ++ ret = wait_for_completion_timeout(&fh->ip->graph_close, ++ IPU_FW_CALL_TIMEOUT_JIFFIES); ++ if (!ret) { ++ dev_err(&psys->dev, "Close graph %d timeout\n", ++ fh->ip->graph_id); ++ return -ETIMEDOUT; ++ } ++ ++ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSED) { ++ dev_err(&psys->dev, "Failed to close graph\n"); ++ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static struct ipu_psys_task_queue * ++ipu7_psys_get_task_queue(struct ipu7_psys_stream *ip, ++ struct ipu_psys_task_request *task) ++{ ++ struct device *dev = &ip->fh->psys->dev; ++ struct ipu7_psys_kbuffer *kbuf = NULL; ++ struct ipu_psys_task_queue *tq; ++ int fd, prevfd = -1; ++ u32 i; ++ ++ if (task->term_buf_count > MAX_GRAPH_TERMINALS) { ++ dev_err(dev, "num_teminal_buffer is too large\n"); ++ return NULL; ++ } ++ ++ mutex_lock(&ip->task_mutex); ++ if (list_empty(&ip->tq_list)) { ++ dev_err(dev, "No available take queue for stream %p\n", ip); ++ goto unlock; ++ } ++ ++ tq = list_first_entry(&ip->tq_list, struct ipu_psys_task_queue, ++ list); ++ ++ if (copy_from_user(tq->task_buffers, ++ task->task_buffers, ++ task->term_buf_count * ++ sizeof(*task->task_buffers))) { ++ dev_err(dev, "failed to copy task buffers\n"); ++ goto unlock; ++ } ++ ++ for (i = 0; i < task->term_buf_count; i++) { ++ fd = tq->task_buffers[i].term_buf.base.fd; ++ kbuf = ipu7_psys_lookup_kbuffer(ip->fh, fd); ++ if (!kbuf) { ++ dev_err(dev, "fd %d not found\n", fd); ++ goto unlock; ++ } ++ tq->ipu7_addr[i] = kbuf->dma_addr ++ + tq->task_buffers[i].term_buf.data_offset; ++ ++ if (prevfd == fd || (tq->task_buffers[i].term_buf.flags & ++ IPU_BUFFER_FLAG_NO_FLUSH)) ++ continue; ++ ++ prevfd = fd; ++ ++ if (kbuf->kaddr) ++ clflush_cache_range(kbuf->kaddr, kbuf->len); ++ } ++ ++ dev_dbg(dev, "frame %d to task queue %p\n", task->frame_id, tq); ++ ++ list_move_tail(&tq->list, &ip->tq_running_list); ++ ++ mutex_unlock(&ip->task_mutex); ++ return tq; ++ ++unlock: ++ mutex_unlock(&ip->task_mutex); ++ return NULL; ++} ++ ++static long ipu_psys_task_request(struct ipu_psys_task_request *task, ++ struct ipu7_psys_fh *fh) ++{ ++ struct ipu7_psys *psys = fh->psys; ++ struct ipu_psys_task_queue *tq; ++ int ret = 0; ++ ++ if (task->term_buf_count == 0 || !task->task_buffers) { ++ dev_err(&psys->dev, "task_buffer is NULL\n"); ++ return -EINVAL; ++ } ++ ++ tq = ipu7_psys_get_task_queue(fh->ip, task); ++ if (!tq) { ++ dev_err(&psys->dev, "Failed to get task queue\n"); ++ return -EINVAL; ++ } ++ ++ ret = ipu7_fw_psys_task_request(task, fh->ip, tq, psys); ++ if (ret) { ++ dev_err(&psys->dev, "Failed to request task %d\n", ++ fh->ip->graph_id); ++ mutex_lock(&fh->ip->task_mutex); ++ list_move_tail(&tq->list, &fh->ip->tq_list); ++ mutex_unlock(&fh->ip->task_mutex); ++ return ret; ++ } ++ ++ tq->task_state = IPU_MSG_TASK_STATE_WAIT_DONE; ++ ++ return 0; ++} ++ ++static unsigned int ipu7_psys_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ struct ipu7_psys_fh *fh = file->private_data; ++ struct device *dev = &fh->psys->adev->auxdev.dev; ++ struct ipu7_psys_stream *ip = fh->ip; ++ unsigned int res = 0; ++ ++ dev_dbg(dev, "ipu psys poll\n"); ++ ++ poll_wait(file, &fh->wait, wait); ++ ++ mutex_lock(&ip->event_mutex); ++ if (!list_empty(&ip->ack_list)) ++ res = POLLIN; ++ mutex_unlock(&ip->event_mutex); ++ ++ dev_dbg(dev, "ipu psys poll res %u\n", res); ++ ++ return res; ++} ++ ++static long ipu7_psys_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ union { ++ struct ipu_psys_graph_info graph; ++ struct ipu_psys_task_request task; ++ struct ipu_psys_buffer buf; ++ struct ipu_psys_event ev; ++ } karg; ++ struct ipu7_psys_fh *fh = file->private_data; ++ long err = 0; ++ void __user *up = (void __user *)arg; ++ bool copy = (cmd != IPU_IOC_MAPBUF && cmd != IPU_IOC_UNMAPBUF && ++ cmd != IPU_IOC_GRAPH_CLOSE); ++ ++ if (copy) { ++ if (_IOC_SIZE(cmd) > sizeof(karg)) ++ return -ENOTTY; ++ ++ if (_IOC_DIR(cmd) & _IOC_WRITE) { ++ err = copy_from_user(&karg, up, _IOC_SIZE(cmd)); ++ if (err) ++ return -EFAULT; ++ } ++ } ++ ++ switch (cmd) { ++ case IPU_IOC_MAPBUF: ++ err = ipu7_psys_mapbuf(arg, fh); ++ break; ++ case IPU_IOC_UNMAPBUF: ++ err = ipu7_psys_unmapbuf(arg, fh); ++ break; ++ case IPU_IOC_GETBUF: ++ err = ipu7_psys_getbuf(&karg.buf, fh); ++ break; ++ case IPU_IOC_PUTBUF: ++ err = ipu7_psys_putbuf(&karg.buf, fh); ++ break; ++ case IPU_IOC_GRAPH_OPEN: ++ err = ipu_psys_graph_open(&karg.graph, fh); ++ break; ++ case IPU_IOC_GRAPH_CLOSE: ++ err = ipu_psys_graph_close(arg, fh); ++ break; ++ case IPU_IOC_TASK_REQUEST: ++ err = ipu_psys_task_request(&karg.task, fh); ++ break; ++ case IPU_IOC_DQEVENT: ++ err = ipu7_ioctl_dqevent(&karg.ev, fh, file->f_flags); ++ break; ++ default: ++ err = -ENOTTY; ++ break; ++ } ++ ++ if (err) ++ return err; ++ ++ if (copy && _IOC_DIR(cmd) & _IOC_READ) ++ if (copy_to_user(up, &karg, _IOC_SIZE(cmd))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++static const struct file_operations ipu7_psys_fops = { ++ .open = ipu7_psys_open, ++ .release = ipu7_psys_release, ++ .unlocked_ioctl = ipu7_psys_ioctl, ++ .poll = ipu7_psys_poll, ++ .owner = THIS_MODULE, ++}; ++ ++static void ipu7_psys_dev_release(struct device *dev) ++{ ++} ++ ++static int psys_runtime_pm_resume(struct device *dev) ++{ ++ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); ++ struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); ++ unsigned long flags; ++ int ret; ++ ++ if (!psys) ++ return 0; ++ ++ spin_lock_irqsave(&psys->ready_lock, flags); ++ if (psys->ready) { ++ spin_unlock_irqrestore(&psys->ready_lock, flags); ++ return 0; ++ } ++ spin_unlock_irqrestore(&psys->ready_lock, flags); ++ ++ ret = ipu7_mmu_hw_init(adev->mmu); ++ if (ret) ++ return ret; ++ ++ if (!ipu_buttress_auth_done(adev->isp)) { ++ dev_dbg(dev, "%s: not yet authenticated, skipping\n", __func__); ++ return 0; ++ } ++ ++ ipu7_psys_setup_hw(psys); ++ ++ ipu7_psys_subdomains_power(psys, 1); ++ ++ ret = ipu7_boot_start_fw(psys->adev); ++ if (ret) { ++ dev_err(&psys->dev, "failed to start psys fw. ret: %d\n", ret); ++ return ret; ++ } ++ ++ ret = ipu7_fw_psys_open(psys); ++ if (ret) { ++ dev_err(&psys->adev->auxdev.dev, "Failed to open abi.\n"); ++ return ret; ++ } ++ ++ spin_lock_irqsave(&psys->ready_lock, flags); ++ psys->ready = 1; ++ spin_unlock_irqrestore(&psys->ready_lock, flags); ++ ++ return 0; ++} ++ ++static int psys_runtime_pm_suspend(struct device *dev) ++{ ++ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); ++ struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); ++ unsigned long flags; ++ ++ if (!psys) ++ return 0; ++ ++ spin_lock_irqsave(&psys->ready_lock, flags); ++ if (!psys->ready) { ++ spin_unlock_irqrestore(&psys->ready_lock, flags); ++ return 0; ++ } ++ psys->ready = 0; ++ spin_unlock_irqrestore(&psys->ready_lock, flags); ++ ++ ipu7_fw_psys_close(psys); ++ ++ ipu7_boot_stop_fw(psys->adev); ++ ++ ipu7_psys_subdomains_power(psys, 0); ++ ++ ipu7_mmu_hw_cleanup(adev->mmu); ++ ++ return 0; ++} ++ ++/* The following PM callbacks are needed to enable runtime PM in IPU PCI ++ * device resume, otherwise, runtime PM can't work in PCI resume from ++ * S3 state. ++ */ ++static int psys_resume(struct device *dev) ++{ ++ return 0; ++} ++ ++static int psys_suspend(struct device *dev) ++{ ++ struct ipu7_psys *psys = dev_get_drvdata(dev); ++ unsigned long flags; ++ int ret = 0; ++ ++ spin_lock_irqsave(&psys->ready_lock, flags); ++ if (psys->ready) ++ ret = -EBUSY; ++ spin_unlock_irqrestore(&psys->ready_lock, flags); ++ ++ return ret; ++} ++ ++static const struct dev_pm_ops psys_pm_ops = { ++ .runtime_suspend = psys_runtime_pm_suspend, ++ .runtime_resume = psys_runtime_pm_resume, ++ .suspend = psys_suspend, ++ .resume = psys_resume, ++}; ++ ++#ifdef CONFIG_DEBUG_FS ++static int psys_fw_log_init(struct ipu7_psys *psys) ++{ ++ struct device *dev = &psys->adev->auxdev.dev; ++ struct psys_fw_log *fw_log; ++ void *log_buf; ++ ++ if (psys->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; ++ ++ psys->fw_log = fw_log; ++ ++ return 0; ++} ++ ++static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, ++ loff_t *pos) ++{ ++ struct ipu7_psys *psys = file->private_data; ++ struct psys_fw_log *fw_log = psys->fw_log; ++ struct device *dev = &psys->adev->auxdev.dev; ++ u32 log_size; ++ void *buf; ++ int ret = 0; ++ ++ if (!fw_log) ++ return 0; ++ ++ buf = kvzalloc(FW_LOG_BUF_SIZE, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ mutex_lock(&fw_log->mutex); ++ if (!fw_log->size) { ++ dev_warn(dev, "no available fw log\n"); ++ mutex_unlock(&fw_log->mutex); ++ goto free_and_return; ++ } ++ ++ if (fw_log->size > FW_LOG_BUF_SIZE) ++ log_size = FW_LOG_BUF_SIZE; ++ else ++ log_size = fw_log->size; ++ ++ memcpy(buf, fw_log->addr, log_size); ++ dev_dbg(dev, "copy %d bytes fw log to user\n", log_size); ++ mutex_unlock(&fw_log->mutex); ++ ++ ret = simple_read_from_buffer(userbuf, size, pos, buf, ++ log_size); ++free_and_return: ++ kvfree(buf); ++ ++ return ret; ++} ++ ++static const struct file_operations psys_fw_log_fops = { ++ .open = simple_open, ++ .owner = THIS_MODULE, ++ .read = fwlog_read, ++ .llseek = default_llseek, ++}; ++ ++static int ipu7_psys_init_debugfs(struct ipu7_psys *psys) ++{ ++ struct dentry *file; ++ struct dentry *dir; ++ ++ dir = debugfs_create_dir("psys", psys->adev->isp->ipu7_dir); ++ if (IS_ERR(dir)) ++ return -ENOMEM; ++ ++ file = debugfs_create_file("fwlog", 0400, ++ dir, psys, &psys_fw_log_fops); ++ if (IS_ERR(file)) ++ goto err; ++ ++ psys->debugfsdir = dir; ++ ++ return 0; ++err: ++ debugfs_remove_recursive(dir); ++ return -ENOMEM; ++} ++#endif ++ ++static const struct bus_type ipu7_psys_bus = { ++ .name = "intel-ipu7-psys", ++}; ++ ++static int ipu7_psys_probe(struct auxiliary_device *auxdev, ++ const struct auxiliary_device_id *auxdev_id) ++{ ++ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); ++ struct device *dev = &auxdev->dev; ++ struct ipu7_psys *psys; ++ unsigned int minor; ++ unsigned int i; ++ int ret; ++ ++ if (!adev->isp->ipu7_bus_ready_to_probe) ++ return -EPROBE_DEFER; ++ ++ ret = alloc_chrdev_region(&ipu7_psys_dev_t, 0, ++ IPU_PSYS_NUM_DEVICES, IPU_PSYS_NAME); ++ if (ret) { ++ dev_err(dev, "can't alloc psys chrdev region (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ ret = pm_runtime_resume_and_get(&auxdev->dev); ++ if (ret < 0) ++ return ret; ++ ++ ret = ipu7_mmu_hw_init(adev->mmu); ++ if (ret) ++ goto out_unregister_chr_region; ++ ++ mutex_lock(&ipu7_psys_mutex); ++ ++ minor = find_next_zero_bit(ipu7_psys_devices, IPU_PSYS_NUM_DEVICES, 0); ++ if (minor == IPU_PSYS_NUM_DEVICES) { ++ dev_err(dev, "too many devices\n"); ++ goto out_unlock; ++ } ++ ++ psys = devm_kzalloc(dev, sizeof(*psys), GFP_KERNEL); ++ if (!psys) { ++ ret = -ENOMEM; ++ goto out_unlock; ++ } ++ ++ for (i = 0 ; i < IPU_PSYS_NUM_STREAMS; i++) ++ psys->graph_id[i] = INVALID_STREAM_ID; ++ ++ adev->auxdrv_data = ++ (const struct ipu7_auxdrv_data *)auxdev_id->driver_data; ++ adev->auxdrv = to_auxiliary_drv(dev->driver); ++ ++ psys->adev = adev; ++ psys->pdata = adev->pdata; ++ ++ cdev_init(&psys->cdev, &ipu7_psys_fops); ++ psys->cdev.owner = ipu7_psys_fops.owner; ++ ++ ret = cdev_add(&psys->cdev, MKDEV(MAJOR(ipu7_psys_dev_t), minor), 1); ++ if (ret) { ++ dev_err(dev, "cdev_add failed (%d)\n", ret); ++ goto out_unlock; ++ } ++ ++ set_bit(minor, ipu7_psys_devices); ++ ++ spin_lock_init(&psys->ready_lock); ++ ++ psys->ready = 0; ++ psys->timeout = IPU_PSYS_CMD_TIMEOUT_MS; ++ ++ mutex_init(&psys->mutex); ++ INIT_LIST_HEAD(&psys->fhs); ++ ++ ret = ipu7_fw_psys_init(psys); ++ if (ret) { ++ dev_err(dev, "FW init failed(%d)\n", ret); ++ goto out_mutex_destroy; ++ } ++ ++ psys->dev.bus = &ipu7_psys_bus; ++ psys->dev.parent = dev; ++ psys->dev.devt = MKDEV(MAJOR(ipu7_psys_dev_t), minor); ++ psys->dev.release = ipu7_psys_dev_release; ++ dev_set_name(&psys->dev, "ipu7-psys%d", minor); ++ ret = device_register(&psys->dev); ++ if (ret < 0) { ++ dev_err(&psys->dev, "psys device_register failed\n"); ++ goto out_fw_release; ++ } ++ ++ dev_set_drvdata(dev, psys); ++ mutex_unlock(&ipu7_psys_mutex); ++#ifdef CONFIG_DEBUG_FS ++ psys_fw_log_init(psys); ++ ipu7_psys_init_debugfs(psys); ++#endif ++ dev_info(dev, "IPU psys probe done.\n"); ++ ++ ipu7_mmu_hw_cleanup(adev->mmu); ++ pm_runtime_put(&auxdev->dev); ++ ++ return 0; ++ ++out_fw_release: ++ ipu7_fw_psys_release(psys); ++out_mutex_destroy: ++ mutex_destroy(&psys->mutex); ++ cdev_del(&psys->cdev); ++out_unlock: ++ /* Safe to call even if the init is not called */ ++ mutex_unlock(&ipu7_psys_mutex); ++ ++ ipu7_mmu_hw_cleanup(adev->mmu); ++ ++out_unregister_chr_region: ++ unregister_chrdev_region(ipu7_psys_dev_t, IPU_PSYS_NUM_DEVICES); ++ pm_runtime_put(&auxdev->dev); ++ ++ return ret; ++} ++ ++static void ipu7_psys_remove(struct auxiliary_device *auxdev) ++{ ++ struct ipu7_psys *psys = dev_get_drvdata(&auxdev->dev); ++ struct device *dev = &auxdev->dev; ++#ifdef CONFIG_DEBUG_FS ++ struct ipu7_device *isp = psys->adev->isp; ++ ++ if (isp->ipu7_dir) ++ debugfs_remove_recursive(psys->debugfsdir); ++#endif ++ ++ mutex_lock(&ipu7_psys_mutex); ++ ipu7_fw_psys_release(psys); ++ device_unregister(&psys->dev); ++ clear_bit(MINOR(psys->cdev.dev), ipu7_psys_devices); ++ cdev_del(&psys->cdev); ++ mutex_unlock(&ipu7_psys_mutex); ++ ++ mutex_destroy(&psys->mutex); ++ ++ unregister_chrdev_region(ipu7_psys_dev_t, IPU_PSYS_NUM_DEVICES); ++ ++ dev_info(dev, "removed\n"); ++} ++ ++static irqreturn_t psys_isr_threaded(struct ipu7_bus_device *adev) ++{ ++ struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); ++ struct device *dev = &psys->adev->auxdev.dev; ++ void __iomem *base = psys->pdata->base; ++ u32 status, state; ++ int r; ++ ++ mutex_lock(&psys->mutex); ++ r = pm_runtime_get_if_in_use(dev); ++ if (!r || WARN_ON_ONCE(r < 0)) { ++ mutex_unlock(&psys->mutex); ++ return IRQ_NONE; ++ } ++ ++ state = ipu7_boot_get_boot_state(adev); ++ if (IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(state)) { ++ dev_warn(&psys->dev, "error state %u\n", state); ++ } else { ++ /* Disable irq before clear irq status */ ++ status = readl(base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_STATUS); ++ writel(0, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE); ++ writel(status, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR); ++ ++ if (status & IRQ_FROM_LOCAL_FW) ++ ipu7_psys_handle_events(psys); ++ ++ writel(IRQ_FROM_LOCAL_FW, ++ base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE); ++ } ++ ++ pm_runtime_put(dev); ++ mutex_unlock(&psys->mutex); ++ ++ return IRQ_HANDLED; ++} ++ ++static const struct ipu7_auxdrv_data ipu7_psys_auxdrv_data = { ++ .isr_threaded = psys_isr_threaded, ++ .wake_isr_thread = true, ++}; ++ ++static const struct auxiliary_device_id ipu7_psys_id_table[] = { ++ { ++ .name = "intel_ipu7.psys", ++ .driver_data = (kernel_ulong_t)&ipu7_psys_auxdrv_data, ++ }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(auxiliary, ipu7_psys_id_table); ++ ++static struct auxiliary_driver ipu7_psys_driver = { ++ .name = IPU_PSYS_NAME, ++ .probe = ipu7_psys_probe, ++ .remove = ipu7_psys_remove, ++ .id_table = ipu7_psys_id_table, ++ .driver = { ++ .pm = &psys_pm_ops, ++ }, ++}; ++ ++module_auxiliary_driver(ipu7_psys_driver); ++ ++MODULE_AUTHOR("Bingbu Cao "); ++MODULE_AUTHOR("Qingwu Zhang "); ++MODULE_AUTHOR("Tianshu Qiu "); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Intel ipu7 processing system driver"); ++MODULE_IMPORT_NS("INTEL_IPU7"); ++MODULE_IMPORT_NS("DMA_BUF"); +diff --git a/drivers/staging/media/ipu7/psys/ipu7-fw-psys.c b/drivers/staging/media/ipu7/psys/ipu7-fw-psys.c +new file mode 100644 +index 000000000000..15ba548ecd83 +--- /dev/null ++++ b/drivers/staging/media/ipu7/psys/ipu7-fw-psys.c +@@ -0,0 +1,603 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2016 - 2024 Intel Corporation ++ ++#include ++#include ++ ++#include ++ ++#include "abi/ipu7_fw_common_abi.h" ++#include "abi/ipu7_fw_msg_abi.h" ++#include "abi/ipu7_fw_psys_config_abi.h" ++ ++#include "ipu7-boot.h" ++#include "ipu7-bus.h" ++#include "ipu7-dma.h" ++#include "ipu7-fw-psys.h" ++#include "ipu7-syscom.h" ++#include "ipu7-psys.h" ++ ++#define TLV_TYPE(type) ((u32)(type) & 0x3FU) ++#define TLV_SIZE(buf_size) (((buf_size) / TLV_ITEM_ALIGNMENT) & 0xFFFFU) ++ ++/* ++ * Node resource ID of INSYS, required when there is a link from INSYS to PSYS. ++ */ ++#define IPU_PSYS_NODE_RSRC_ID_IS (0xFEU) ++ ++/* ++ * Special node resource ID to identify a generic external node. ++ * Required when there is a link to/from IPU and that node. ++ */ ++#define IPU_PSYS_NODE_RSRC_ID_EXT_IP (0xFFU) ++ ++int ipu7_fw_psys_init(struct ipu7_psys *psys) ++{ ++ struct ipu7_bus_device *adev = psys->adev; ++ struct device *dev = &adev->auxdev.dev; ++ struct ipu7_syscom_context *syscom; ++ struct ipu7_psys_config *psys_config; ++ struct syscom_queue_config *queue_configs; ++ dma_addr_t psys_config_dma_addr; ++ u32 freq; ++ int i, num_queues, 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 = FWPS_MSG_ABI_MAX_INPUT_QUEUES; ++ syscom->num_output_queues = FWPS_MSG_ABI_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_psys_release(psys); ++ return -ENOMEM; ++ } ++ syscom->queue_configs = queue_configs; ++ queue_configs[FWPS_MSG_ABI_OUT_ACK_QUEUE_ID].max_capacity = ++ IPU_PSYS_ACK_QUEUE_SIZE; ++ queue_configs[FWPS_MSG_ABI_OUT_ACK_QUEUE_ID].token_size_in_bytes = ++ IPU_PSYS_OUT_MSG_SIZE; ++ queue_configs[FWPS_MSG_ABI_OUT_LOG_QUEUE_ID].max_capacity = ++ IPU_PSYS_LOG_QUEUE_SIZE; ++ queue_configs[FWPS_MSG_ABI_OUT_LOG_QUEUE_ID].token_size_in_bytes = ++ IPU_PSYS_OUT_MSG_SIZE; ++ queue_configs[FWPS_MSG_ABI_IN_DEV_QUEUE_ID].max_capacity = ++ IPU_PSYS_CMD_QUEUE_SIZE; ++ queue_configs[FWPS_MSG_ABI_IN_DEV_QUEUE_ID].token_size_in_bytes = ++ FWPS_MSG_HOST2FW_MAX_SIZE; ++ queue_configs[FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID].max_capacity = 0; ++ queue_configs[FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID].token_size_in_bytes = ++ 0; ++ ++ for (i = FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID; i < num_queues; i++) { ++ queue_configs[i].max_capacity = IPU_PSYS_TASK_QUEUE_SIZE; ++ queue_configs[i].token_size_in_bytes = ++ sizeof(struct ia_gofo_msg_indirect); ++ } ++ ++ /* Allocate PSYS subsys config. */ ++ psys_config = ipu7_dma_alloc(adev, sizeof(struct ipu7_psys_config), ++ &psys_config_dma_addr, GFP_KERNEL, 0); ++ if (!psys_config) { ++ dev_err(dev, "Failed to allocate psys subsys config.\n"); ++ ipu7_fw_psys_release(psys); ++ return -ENOMEM; ++ } ++ psys->subsys_config = psys_config; ++ psys->subsys_config_dma_addr = psys_config_dma_addr; ++ memset(psys_config, 0, sizeof(struct ipu7_psys_config)); ++ ret = ipu_buttress_get_psys_freq(adev->isp, &freq); ++ if (ret) { ++ dev_err(dev, "Failed to get PSYS frequency.\n"); ++ ipu7_fw_psys_release(psys); ++ return ret; ++ } ++ ++ ret = ipu7_boot_init_boot_config(adev, queue_configs, num_queues, ++ freq, psys_config_dma_addr, 1U); ++ if (ret) ++ ipu7_fw_psys_release(psys); ++ return ret; ++} ++ ++void ipu7_fw_psys_release(struct ipu7_psys *psys) ++{ ++ struct ipu7_bus_device *adev = psys->adev; ++ ++ ipu7_boot_release_boot_config(adev); ++ if (psys->subsys_config) { ++ ipu7_dma_free(adev, sizeof(struct ipu7_psys_config), ++ psys->subsys_config, ++ psys->subsys_config_dma_addr, 0); ++ psys->subsys_config = NULL; ++ psys->subsys_config_dma_addr = 0; ++ } ++} ++ ++static int ipu7_fw_dev_ready(struct ipu7_psys *psys, u16 type) ++{ ++ const struct ia_gofo_msg_header_ack *ack_header; ++ u8 buffer[FWPS_MSG_FW2HOST_MAX_SIZE]; ++ int ret; ++ ++ ret = ipu7_fw_psys_event_handle(psys, buffer); ++ if (ret) ++ return ret; ++ ++ ack_header = (const struct ia_gofo_msg_header_ack *)buffer; ++ ++ if (ack_header->header.tlv_header.tlv_type == type) ++ return 0; ++ ++ return -EAGAIN; ++} ++ ++static int ipu7_fw_dev_open(struct ipu7_psys *psys) ++{ ++ struct ipu7_syscom_context *ctx = psys->adev->syscom; ++ struct ipu7_msg_dev_open *token; ++ ++ dev_dbg(&psys->dev, "send_token: fw psys open\n"); ++ ++ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); ++ if (!token) ++ return -ENODATA; ++ ++ token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_DEV_OPEN); ++ token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); ++ token->header.user_token = 0; ++ ++ token->max_graphs = IPU_PSYS_MAX_GRAPH_NUMS; ++ token->dev_msg_map = (u8)(IPU_MSG_DEVICE_OPEN_SEND_RESP | ++ IPU_MSG_DEVICE_OPEN_SEND_IRQ); ++ token->enable_power_gating = 0; ++ ++ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); ++ ++ ipu_buttress_wakeup_ps_uc(psys->adev->isp); ++ ++ return 0; ++} ++ ++int ipu7_fw_psys_open(struct ipu7_psys *psys) ++{ ++ u32 retry = IPU_PSYS_OPEN_CLOSE_RETRY; ++ int ret; ++ ++ ret = ipu7_fw_dev_open(psys); ++ if (ret) { ++ dev_err(&psys->dev, "failed to open PSYS dev.\n"); ++ return ret; ++ } ++ psys->dev_state = IPU_MSG_DEV_STATE_OPEN_WAIT; ++ ++ do { ++ usleep_range(IPU_PSYS_OPEN_CLOSE_TIMEOUT_US, ++ IPU_PSYS_OPEN_CLOSE_TIMEOUT_US + 10); ++ ret = ipu7_fw_dev_ready(psys, IPU_MSG_TYPE_DEV_OPEN_ACK); ++ if (!ret) { ++ dev_dbg(&psys->dev, "dev open done.\n"); ++ psys->dev_state = IPU_MSG_DEV_STATE_OPEN; ++ return 0; ++ } ++ } while (retry--); ++ ++ if (!retry) ++ dev_err(&psys->dev, "wait dev open timeout!\n"); ++ ++ return ret; ++} ++ ++static int ipu7_fw_dev_close(struct ipu7_psys *psys) ++{ ++ struct ipu7_syscom_context *ctx = psys->adev->syscom; ++ struct ipu7_msg_dev_close *token; ++ ++ dev_dbg(&psys->dev, "send_token: fw psys close\n"); ++ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); ++ if (!token) ++ return -ENODATA; ++ ++ token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_DEV_CLOSE); ++ token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); ++ token->header.user_token = 0; ++ ++ token->dev_msg_map = (u8)(IPU_MSG_DEVICE_CLOSE_SEND_RESP | ++ IPU_MSG_DEVICE_CLOSE_SEND_IRQ); ++ ++ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); ++ ++ ipu_buttress_wakeup_ps_uc(psys->adev->isp); ++ ++ return 0; ++} ++ ++void ipu7_fw_psys_close(struct ipu7_psys *psys) ++{ ++ u32 retry = IPU_PSYS_OPEN_CLOSE_RETRY; ++ int ret; ++ ++ ret = ipu7_fw_dev_close(psys); ++ if (ret) { ++ dev_err(&psys->dev, "failed to close PSYS dev.\n"); ++ return; ++ } ++ ++ psys->dev_state = IPU_MSG_DEV_STATE_CLOSE_WAIT; ++ ++ do { ++ usleep_range(IPU_PSYS_OPEN_CLOSE_TIMEOUT_US, ++ IPU_PSYS_OPEN_CLOSE_TIMEOUT_US + 10); ++ ret = ipu7_fw_dev_ready(psys, IPU_MSG_TYPE_DEV_CLOSE_ACK); ++ if (!ret) { ++ dev_dbg(&psys->dev, "dev close done.\n"); ++ psys->dev_state = IPU_MSG_DEV_STATE_CLOSED; ++ return; ++ } ++ } while (retry--); ++ ++ if (!retry) ++ dev_err(&psys->dev, "wait dev close timeout!\n"); ++} ++ ++static void ++ipu7_fw_psys_build_node_profile(const struct node_profile *profile, ++ void **buf_ptr_ptr) ++{ ++ struct ipu7_msg_cb_profile *cb_profile = ++ (struct ipu7_msg_cb_profile *)*buf_ptr_ptr; ++ u16 buf_size = sizeof(*cb_profile); ++ ++ memcpy(cb_profile->profile_base.teb, profile->teb, ++ sizeof(cb_profile->profile_base.teb)); ++ ++ memcpy(cb_profile->rbm, profile->rbm, sizeof(cb_profile->rbm)); ++ memcpy(cb_profile->deb, profile->deb, sizeof(cb_profile->deb)); ++ memcpy(cb_profile->reb, profile->reb, sizeof(cb_profile->reb)); ++ ++ cb_profile->profile_base.tlv_header.tlv_type = ++ TLV_TYPE(IPU_MSG_NODE_PROFILE_TYPE_CB); ++ cb_profile->profile_base.tlv_header.tlv_len32 = TLV_SIZE(buf_size); ++ ++ *buf_ptr_ptr += buf_size; ++} ++ ++/* skip term, return false */ ++static bool ipu7_fw_psys_build_node_term(const struct node_ternimal *term, ++ void **buf_ptr_ptr) ++{ ++ struct ipu7_msg_term *msg_term = (struct ipu7_msg_term *)*buf_ptr_ptr; ++ u16 buf_size = sizeof(*msg_term); ++ ++ if (!term->term_id && !term->buf_size) ++ return false; ++ ++ memset(msg_term, 0, sizeof(*msg_term)); ++ msg_term->term_id = term->term_id; ++ /* Disable progress message on connect terminals */ ++ msg_term->event_req_bm = 0U; ++ msg_term->payload_size = term->buf_size; ++ ++ msg_term->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TERM_TYPE_BASE); ++ msg_term->tlv_header.tlv_len32 = TLV_SIZE(buf_size); ++ ++ *buf_ptr_ptr += buf_size; ++ return true; ++} ++ ++/* When skip processing node, just return false */ ++static bool ipu7_fw_psys_build_node(const struct graph_node *node, ++ void **buf_ptr_ptr) ++{ ++ struct ipu7_msg_node *msg_node = (struct ipu7_msg_node *)*buf_ptr_ptr; ++ u16 buf_size = sizeof(*msg_node); ++ bool ret = false; ++ u8 i = 0; ++ u8 max_terms = 0; ++ ++ memset(msg_node, 0, sizeof(*msg_node)); ++ /** ++ * Pass node info to FW, do not check for external IP and ISYS ++ * As FW expects a external node ++ */ ++ if (node->node_rsrc_id != IPU_PSYS_NODE_RSRC_ID_IS && ++ node->node_rsrc_id != IPU_PSYS_NODE_RSRC_ID_EXT_IP) { ++ if (node->profiles[0].teb[0] == 0U) ++ return false; ++ } ++ ++ /** ++ * Sanity check for dummy node, TEB should set to required one ++ */ ++ if (node->node_rsrc_id == IPU_PSYS_NODE_RSRC_ID_IS || ++ node->node_rsrc_id == IPU_PSYS_NODE_RSRC_ID_EXT_IP) { ++ if (node->profiles[0].teb[0] != IPU_MSG_NODE_DONT_CARE_TEB_LO || ++ node->profiles[0].teb[1] != IPU_MSG_NODE_DONT_CARE_TEB_HI) ++ return false; ++ } ++ ++ msg_node->node_rsrc_id = node->node_rsrc_id; ++ msg_node->node_ctx_id = node->node_ctx_id; ++ msg_node->num_frags = 1; ++ ++ *buf_ptr_ptr += buf_size; ++ ++ msg_node->profiles_list.head_offset = ++ (u16)((uintptr_t)*buf_ptr_ptr ++ - (uintptr_t)&msg_node->profiles_list); ++ for (i = 0; i < ARRAY_SIZE(node->profiles); i++) { ++ ipu7_fw_psys_build_node_profile(&node->profiles[i], ++ buf_ptr_ptr); ++ msg_node->profiles_list.num_elems++; ++ } ++ ++ msg_node->terms_list.head_offset = ++ (u16)((uintptr_t)*buf_ptr_ptr - ++ (uintptr_t)&msg_node->terms_list); ++ max_terms = ARRAY_SIZE(node->terminals); ++ for (i = 0; i < max_terms && i < node->num_terms; i++) { ++ ret = ipu7_fw_psys_build_node_term(&node->terminals[i], ++ buf_ptr_ptr); ++ if (ret) ++ msg_node->terms_list.num_elems++; ++ } ++ ++ buf_size = (u32)(uintptr_t)*buf_ptr_ptr - (uintptr_t)msg_node; ++ msg_node->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_NODE_TYPE_BASE); ++ msg_node->tlv_header.tlv_len32 = TLV_SIZE(buf_size); ++ ++ return true; ++} ++ ++static bool ipu7_fw_psys_build_link(const struct graph_link *link, ++ void **buf_ptr_ptr) ++{ ++ struct ipu7_msg_link *msg_link = (struct ipu7_msg_link *)*buf_ptr_ptr; ++ ++ if (!link->ep_src.node_ctx_id && !link->ep_dst.node_ctx_id && ++ !link->ep_src.term_id && !link->ep_dst.term_id) ++ return false; ++ ++ msg_link->endpoints.ep_src.node_ctx_id = link->ep_src.node_ctx_id; ++ msg_link->endpoints.ep_src.term_id = link->ep_src.term_id; ++ ++ msg_link->endpoints.ep_dst.node_ctx_id = link->ep_dst.node_ctx_id; ++ msg_link->endpoints.ep_dst.term_id = link->ep_dst.term_id; ++ ++ msg_link->foreign_key = link->foreign_key; ++ msg_link->streaming_mode = link->streaming_mode; ++ msg_link->pbk_id = link->pbk_id; ++ msg_link->pbk_slot_id = link->pbk_slot_id; ++ msg_link->delayed_link = link->delayed_link; ++ ++ *buf_ptr_ptr += sizeof(*msg_link); ++ ++ msg_link->link_options.num_elems = 0; ++ msg_link->link_options.head_offset = ++ (u16)((uintptr_t)*buf_ptr_ptr - ++ (uintptr_t)&msg_link->link_options); ++ msg_link->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_LINK_TYPE_GENERIC); ++ msg_link->tlv_header.tlv_len32 = TLV_SIZE(sizeof(*msg_link)); ++ ++ return true; ++} ++ ++int ipu7_fw_psys_graph_open(const struct ipu_psys_graph_info *graph, ++ struct ipu7_psys *psys, ++ struct ipu7_psys_stream *ip) ++{ ++ struct ipu7_syscom_context *ctx = psys->adev->syscom; ++ void *buf_ptr; ++ struct ipu7_msg_graph_open *graph_open; ++ u32 buf_size = 0; ++ bool ret = false; ++ u8 i = 0; ++ ++ dev_dbg(&psys->dev, "send_token: fw psys graph open\n"); ++ buf_ptr = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); ++ if (!buf_ptr) ++ return -ENODATA; ++ ++ graph_open = (struct ipu7_msg_graph_open *)buf_ptr; ++ ++ memset(graph_open, 0, sizeof(*graph_open)); ++ graph_open->graph_id = ip->graph_id; ++ graph_open->graph_msg_map = (u8)(IPU_MSG_GRAPH_OPEN_SEND_RESP ++ | IPU_MSG_GRAPH_OPEN_SEND_IRQ); ++ ++ buf_ptr += sizeof(*graph_open); ++ graph_open->nodes.head_offset = (u16)((uintptr_t)buf_ptr ++ - (uintptr_t)&graph_open->nodes); ++ for (i = 0; i < ARRAY_SIZE(ip->nodes); i++) { ++ ret = ipu7_fw_psys_build_node(&ip->nodes[i], &buf_ptr); ++ if (ret) ++ graph_open->nodes.num_elems++; ++ } ++ ++ graph_open->links.head_offset = (u16)((uintptr_t)buf_ptr ++ - (uintptr_t)&graph_open->links); ++ for (i = 0; i < ARRAY_SIZE(graph->links); i++) { ++ ret = ipu7_fw_psys_build_link(&graph->links[i], &buf_ptr); ++ if (ret) ++ graph_open->links.num_elems++; ++ } ++ ++ buf_size = (u32)((uintptr_t)buf_ptr - (uintptr_t)graph_open); ++ graph_open->header.tlv_header.tlv_type = ++ TLV_TYPE(IPU_MSG_TYPE_GRAPH_OPEN); ++ graph_open->header.tlv_header.tlv_len32 = TLV_SIZE(buf_size); ++ graph_open->header.user_token = 0; ++ ++ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); ++ ++ ipu_buttress_wakeup_ps_uc(psys->adev->isp); ++ ++ return 0; ++} ++ ++int ipu7_fw_psys_graph_close(u8 graph_id, struct ipu7_psys *psys) ++{ ++ struct ipu7_syscom_context *ctx = psys->adev->syscom; ++ struct ipu7_msg_graph_close *token; ++ ++ dev_dbg(&psys->dev, "send_token: fw psys graph close\n"); ++ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); ++ if (!token) ++ return -ENODATA; ++ ++ token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_GRAPH_CLOSE); ++ token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); ++ token->header.user_token = 0; ++ ++ token->graph_id = graph_id; ++ token->graph_msg_map = (u8)(IPU_MSG_DEVICE_CLOSE_SEND_RESP ++ | IPU_MSG_DEVICE_CLOSE_SEND_IRQ); ++ ++ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); ++ ++ ipu_buttress_wakeup_ps_uc(psys->adev->isp); ++ ++ return 0; ++} ++ ++int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, ++ struct ipu7_psys_stream *ip, ++ struct ipu_psys_task_queue *tq, ++ struct ipu7_psys *psys) ++{ ++ struct ipu7_syscom_context *ctx = psys->adev->syscom; ++ struct ipu7_msg_task *msg = tq->msg_task; ++ struct ia_gofo_msg_indirect *ind; ++ u32 node_q_id = ip->q_id[task->node_ctx_id]; ++ u32 teb_hi, teb_lo; ++ u64 teb; ++ u8 i, term_id; ++ u8 num_terms; ++ ++ ind = ipu7_syscom_get_token(ctx, node_q_id); ++ if (!ind) ++ return -ENODATA; ++ ++ memset(msg, 0, sizeof(*msg)); ++ msg->graph_id = task->graph_id; ++ msg->node_ctx_id = task->node_ctx_id; ++ msg->profile_idx = 0U; /* Only one profile on HKR */ ++ msg->frame_id = task->frame_id; ++ msg->frag_id = 0U; /* No frag, set to 0 */ ++ /* ++ * Each task has a flag indicating if ack needed, it may be used to ++ * reduce interrupts if multiple CBs supported. ++ */ ++ msg->req_done_msg = 1; ++ msg->req_done_irq = 1; ++ ++ memcpy(msg->payload_reuse_bm, task->payload_reuse_bm, ++ sizeof(task->payload_reuse_bm)); ++ ++ teb_hi = ip->nodes[msg->node_ctx_id].profiles[0].teb[1]; ++ teb_lo = ip->nodes[msg->node_ctx_id].profiles[0].teb[0]; ++ teb = (teb_lo | (((u64)teb_hi) << 32)); ++ ++ num_terms = ip->nodes[msg->node_ctx_id].num_terms; ++ for (i = 0U; i < num_terms; i++) { ++ term_id = tq->task_buffers[i].term_id; ++ if ((1U << term_id) & teb) ++ msg->term_buffers[term_id] = tq->ipu7_addr[i]; ++ } ++ ++ msg->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_TASK_REQ); ++ msg->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*msg)); ++ msg->header.user_token = (uintptr_t)tq; ++ ++ ind->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_INDIRECT); ++ ind->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*ind)); ++ ind->header.msg_options.num_elems = 0; ++ ind->header.msg_options.head_offset = 0; ++ ind->ref_header = msg->header.tlv_header; ++ ind->ref_msg_ptr = tq->task_dma_addr; ++ ++ ipu7_syscom_put_token(ctx, node_q_id); ++ ++ ipu_buttress_wakeup_ps_uc(psys->adev->isp); ++ ++ return 0; ++} ++ ++int ipu7_fw_psys_event_handle(struct ipu7_psys *psys, u8 *buf_ptr) ++{ ++ struct ipu7_syscom_context *ctx = psys->adev->syscom; ++ void *token; ++ ++ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_OUT_ACK_QUEUE_ID); ++ if (!token) ++ return -ENODATA; ++ ++ memcpy(buf_ptr, token, sizeof(u8) * FWPS_MSG_FW2HOST_MAX_SIZE); ++ ++ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_OUT_ACK_QUEUE_ID); ++ return 0; ++} ++ ++int ipu7_fw_psys_get_log(struct ipu7_psys *psys) ++{ ++ void *token; ++ struct ia_gofo_msg_log *log_msg; ++ u8 msg_type, msg_len; ++ u32 count, fmt_id; ++ struct device *dev = &psys->adev->auxdev.dev; ++ struct psys_fw_log *fw_log = psys->fw_log; ++ u32 log_size = sizeof(struct ia_gofo_msg_log_info_ts); ++ ++ token = ipu7_syscom_get_token(psys->adev->syscom, ++ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); ++ if (!token) ++ return -ENODATA; ++ ++ while (token) { ++ log_msg = (struct ia_gofo_msg_log *)token; ++ ++ msg_type = log_msg->header.tlv_header.tlv_type; ++ msg_len = log_msg->header.tlv_header.tlv_len32; ++ if (msg_type != IPU_MSG_TYPE_DEV_LOG || !msg_len) ++ dev_warn(dev, "Invalid msg data from Log queue!\n"); ++ ++ count = log_msg->log_info_ts.log_info.log_counter; ++ fmt_id = log_msg->log_info_ts.log_info.fmt_id; ++ if (count > fw_log->count + 1) ++ dev_warn(dev, "log msg lost, count %u+1 != %u!\n", ++ count, fw_log->count); ++ ++ if (fmt_id == IA_GOFO_MSG_LOG_FMT_ID_INVALID) { ++ dev_err(dev, "invalid log msg fmt_id 0x%x!\n", fmt_id); ++ ipu7_syscom_put_token(psys->adev->syscom, ++ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); ++ return -EIO; ++ } ++ ++ if (log_size + fw_log->head - fw_log->addr > ++ FW_LOG_BUF_SIZE) ++ fw_log->head = fw_log->addr; ++ ++ memcpy(fw_log->head, (void *)&log_msg->log_info_ts, ++ sizeof(struct ia_gofo_msg_log_info_ts)); ++ ++ fw_log->count = count; ++ fw_log->head += log_size; ++ fw_log->size += log_size; ++ ++ ipu7_syscom_put_token(psys->adev->syscom, ++ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); ++ ++ token = ipu7_syscom_get_token(psys->adev->syscom, ++ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); ++ }; ++ ++ return 0; ++} ++ +diff --git a/drivers/staging/media/ipu7/psys/ipu7-fw-psys.h b/drivers/staging/media/ipu7/psys/ipu7-fw-psys.h +new file mode 100644 +index 000000000000..c43f235386d1 +--- /dev/null ++++ b/drivers/staging/media/ipu7/psys/ipu7-fw-psys.h +@@ -0,0 +1,42 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (C) 2016 - 2024 Intel Corporation ++ */ ++ ++#ifndef IPU7_FW_PSYS_H ++#define IPU7_FW_PSYS_H ++ ++#include "abi/ipu7_fw_common_abi.h" ++#include "abi/ipu7_fw_msg_abi.h" ++ ++#include "ipu7-syscom.h" ++ ++#include ++ ++#define IPU_PSYS_MAX_GRAPH_NUMS (8U) ++#define IPU_PSYS_NUM_STREAMS IPU_PSYS_MAX_GRAPH_NUMS ++ ++struct ipu7_msg_to_str { ++ const enum ipu7_msg_type type; ++ const char *msg; ++}; ++ ++struct ipu7_psys; ++struct ipu7_psys_stream; ++struct ipu_psys_task_queue; ++ ++int ipu7_fw_psys_init(struct ipu7_psys *psys); ++void ipu7_fw_psys_release(struct ipu7_psys *psys); ++int ipu7_fw_psys_open(struct ipu7_psys *psys); ++void ipu7_fw_psys_close(struct ipu7_psys *psys); ++int ipu7_fw_psys_graph_open(const struct ipu_psys_graph_info *graph, ++ struct ipu7_psys *psys, ++ struct ipu7_psys_stream *ip); ++int ipu7_fw_psys_graph_close(u8 graph_id, struct ipu7_psys *psys); ++int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, ++ struct ipu7_psys_stream *ip, ++ struct ipu_psys_task_queue *tq, ++ struct ipu7_psys *psys); ++int ipu7_fw_psys_event_handle(struct ipu7_psys *psys, u8 *buf_ptr); ++int ipu7_fw_psys_get_log(struct ipu7_psys *psys); ++#endif /* IPU7_FW_PSYS_H */ +diff --git a/drivers/staging/media/ipu7/psys/ipu7-psys.c b/drivers/staging/media/ipu7/psys/ipu7-psys.c +new file mode 100644 +index 000000000000..1ce52ae75a4c +--- /dev/null ++++ b/drivers/staging/media/ipu7/psys/ipu7-psys.c +@@ -0,0 +1,398 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2020 - 2024 Intel Corporation ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ipu7.h" ++#include "ipu7-bus.h" ++#include "ipu7-buttress-regs.h" ++#include "ipu7-psys.h" ++#include "ipu7-platform-regs.h" ++#include "ipu7-fw-psys.h" ++ ++#define DOMAIN_POWE_TIMEOUT_US (200 * USEC_PER_MSEC) ++ ++static const struct ipu7_msg_to_str ps_fw_msg[] = { ++ {IPU_MSG_TYPE_RESERVED, "IPU_MSG_TYPE_RESERVED"}, ++ {IPU_MSG_TYPE_INDIRECT, "IPU_MSG_TYPE_INDIRECT"}, ++ {IPU_MSG_TYPE_DEV_LOG, "IPU_MSG_TYPE_DEV_LOG"}, ++ {IPU_MSG_TYPE_GENERAL_ERR, "IPU_MSG_TYPE_GENERAL_ERR"}, ++ {IPU_MSG_TYPE_DEV_OPEN, "IPU_MSG_TYPE_DEV_OPEN"}, ++ {IPU_MSG_TYPE_DEV_OPEN_ACK, "IPU_MSG_TYPE_DEV_OPEN_ACK"}, ++ {IPU_MSG_TYPE_GRAPH_OPEN, "IPU_MSG_TYPE_GRAPH_OPEN"}, ++ {IPU_MSG_TYPE_GRAPH_OPEN_ACK, "IPU_MSG_TYPE_GRAPH_OPEN_ACK"}, ++ {IPU_MSG_TYPE_TASK_REQ, "IPU_MSG_TYPE_TASK_REQ"}, ++ {IPU_MSG_TYPE_TASK_DONE, "IPU_MSG_TYPE_TASK_DONE"}, ++ {IPU_MSG_TYPE_GRAPH_CLOSE, "IPU_MSG_TYPE_GRAPH_CLOSE"}, ++ {IPU_MSG_TYPE_GRAPH_CLOSE_ACK, "IPU_MSG_TYPE_GRAPH_CLOSE_ACK"}, ++ {IPU_MSG_TYPE_DEV_CLOSE, "IPU_MSG_TYPE_DEV_CLOSE"}, ++ {IPU_MSG_TYPE_DEV_CLOSE_ACK, "IPU_MSG_TYPE_DEV_CLOSE_ACK"}, ++ {IPU_MSG_TYPE_N, "IPU_MSG_TYPE_N"}, ++}; ++ ++void ipu7_psys_subdomains_power(struct ipu7_psys *psys, bool on) ++{ ++ struct device *dev = &psys->adev->auxdev.dev; ++ struct ipu7_device *isp = psys->adev->isp; ++ void __iomem *base = isp->base; ++ u32 mask; ++ u32 val; ++ u32 req; ++ int ret; ++ ++ /* power domain req */ ++ mask = is_ipu8(isp->hw_ver) ? IPU8_PSYS_DOMAIN_POWER_MASK : ++ IPU7_PSYS_DOMAIN_POWER_MASK; ++ req = on ? mask : 0; ++ val = readl(base + BUTTRESS_REG_PS_DOMAINS_STATUS); ++ ++ dev_dbg(dev, "power %s psys sub-domains. status: 0x%x\n", ++ on ? "UP" : "DOWN", val); ++ if ((val & mask) == req) { ++ dev_warn(dev, ++ "psys sub-domains power already in request state.\n"); ++ return; ++ } ++ writel(req, base + BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ); ++ ret = readl_poll_timeout(base + BUTTRESS_REG_PS_DOMAINS_STATUS, ++ val, ++ !(val & IPU_PSYS_DOMAIN_POWER_IN_PROGRESS) && ++ ((val & mask) == req), ++ 100, DOMAIN_POWE_TIMEOUT_US); ++ if (ret) ++ dev_err(dev, ++ "Psys sub-domains power %s timeout! status: 0x%x\n", ++ on ? "UP" : "DOWN", val); ++} ++ ++void ipu7_psys_setup_hw(struct ipu7_psys *psys) ++{ ++ void __iomem *base = psys->pdata->base; ++ u32 val = IRQ_FROM_LOCAL_FW; ++ ++ /* Enable TO_SW IRQ from FW */ ++ writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR); ++ writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_MASK); ++ writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE); ++ ++ /* correct the initial printf configuration */ ++ writel(0x2, base + PS_UC_CTRL_BASE + PRINTF_AXI_CNTL); ++} ++ ++static struct ipu7_psys_stream* ++ipu7_psys_get_ip_from_fh(struct ipu7_psys *psys, u8 graph_id) ++{ ++ struct ipu7_psys_fh *fh; ++ ++ list_for_each_entry(fh, &psys->fhs, list) { ++ if (fh->ip->graph_id == graph_id) ++ return fh->ip; ++ } ++ ++ return NULL; ++} ++ ++static void ipu7_psys_handle_graph_open_ack(struct ipu7_psys *psys, ++ const void *buffer) ++{ ++ const struct ipu7_msg_graph_open_ack *ack_msg = ++ (const struct ipu7_msg_graph_open_ack *)buffer; ++ const struct ia_gofo_msg_header_ack *ack_header = &ack_msg->header; ++ struct ipu7_psys_stream *ip; ++ struct device *dev = &psys->dev; ++ const struct ia_gofo_tlv_header *msg_tlv_item; ++ u16 num_items; ++ u16 head_offset; ++ u32 i; ++ ++ dev_dbg(dev, "[ACK]%s: graph_id: %d\n", __func__, ack_msg->graph_id); ++ ++ if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { ++ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); ++ return; ++ } ++ ++ ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); ++ if (!ip) { ++ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); ++ return; ++ } ++ ++ if (ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN_WAIT) { ++ dev_err(dev, "%s state %d\n", __func__, ip->graph_state); ++ goto open_graph_exit; ++ } ++ ++ num_items = ack_header->header.msg_options.num_elems; ++ if (!num_items) { ++ dev_err(dev, "%s, num_items is 0\n", __func__); ++ goto open_graph_exit; ++ } ++ ++ head_offset = ack_header->header.msg_options.head_offset; ++ msg_tlv_item = (const struct ia_gofo_tlv_header *) ++ ((u8 *)&ack_header->header.msg_options + head_offset); ++ ++ if (!msg_tlv_item) { ++ dev_err(dev, "%s: failed to get tlv item\n", __func__); ++ goto open_graph_exit; ++ } ++ ++ for (i = 0U; i < num_items; i++) { ++ u32 option_type = msg_tlv_item->tlv_type; ++ u32 option_bytes = msg_tlv_item->tlv_len32 * ++ TLV_ITEM_ALIGNMENT; ++ struct ipu7_msg_graph_open_ack_task_q_info *msg_option = ++ (void *)msg_tlv_item; ++ ++ switch (option_type) { ++ case IPU_MSG_GRAPH_ACK_TASK_Q_INFO: ++ /* ++ * Should do check that: ++ * - Each managed node has a queue ID ++ * - Queue ID's are sane ++ */ ++ dev_dbg(dev, "[ACK]set node_ctx_id %d q_id %d\n", ++ msg_option->node_ctx_id, msg_option->q_id); ++ ip->q_id[msg_option->node_ctx_id] = msg_option->q_id; ++ break; ++ default: ++ /* ++ * Only one option supported ++ */ ++ dev_err(dev, "not supported %u\n", option_type); ++ break; ++ } ++ ++ msg_tlv_item = (struct ia_gofo_tlv_header *) ++ (((u8 *)msg_tlv_item) + option_bytes); ++ } ++ ++ ip->graph_state = IPU_MSG_GRAPH_STATE_OPEN; ++ ++open_graph_exit: ++ complete(&ip->graph_open); ++} ++ ++static int ipu7_psys_handle_task_done(struct ipu7_psys *psys, ++ void *buffer, u32 error) ++{ ++ const struct ipu7_msg_task_done *ack_msg = ++ (const struct ipu7_msg_task_done *)buffer; ++ const struct ia_gofo_msg_header_ack *ack_header = &ack_msg->header; ++ const struct ia_gofo_msg_header *msg_header = &ack_header->header; ++ struct ipu_psys_task_queue *tq; ++ struct device *dev = &psys->dev; ++ struct ipu7_psys_stream *ip; ++ struct ipu_psys_task_ack *event; ++ ++ if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { ++ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); ++ return -EINVAL; ++ } ++ ++ ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); ++ if (!ip) { ++ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); ++ return -EINVAL; ++ } ++ ++ tq = (void *)(uintptr_t)msg_header->user_token; ++ if (!tq) { ++ dev_err(dev, "%s: task_token is NULL\n", __func__); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&ip->task_mutex); ++ if (tq->task_state != IPU_MSG_TASK_STATE_WAIT_DONE) { ++ dev_err(dev, "%s: graph %d node %d error %d\n", __func__, ++ ack_msg->graph_id, ack_msg->node_ctx_id, ++ tq->task_state); ++ ++ list_move_tail(&tq->list, &ip->tq_list); ++ mutex_unlock(&ip->task_mutex); ++ return -ENOENT; ++ } ++ ++ tq->task_state = IPU_MSG_TASK_STATE_DONE; ++ dev_dbg(dev, "%s: task_token(%p)\n", __func__, tq); ++ ++ list_move_tail(&tq->list, &ip->tq_list); ++ mutex_unlock(&ip->task_mutex); ++ ++ mutex_lock(&ip->event_mutex); ++ if (!list_empty(&ip->event_list)) { ++ event = list_first_entry(&ip->event_list, ++ struct ipu_psys_task_ack, list); ++ event->graph_id = ack_msg->graph_id; ++ event->node_ctx_id = ack_msg->node_ctx_id; ++ event->frame_id = ack_msg->frame_id; ++ event->err_code = error; ++ ++ list_move_tail(&event->list, &ip->ack_list); ++ } else { ++ dev_dbg(dev, "event queue is full, add new one\n"); ++ event = kzalloc(sizeof(*event), GFP_KERNEL); ++ ++ if (event) { ++ event->graph_id = ack_msg->graph_id; ++ event->node_ctx_id = ack_msg->node_ctx_id; ++ event->frame_id = ack_msg->frame_id; ++ event->err_code = error; ++ ++ list_add_tail(&event->list, &ip->ack_list); ++ } else { ++ dev_err(dev, "failed to alloc event buf\n"); ++ } ++ } ++ mutex_unlock(&ip->event_mutex); ++ ++ wake_up_interruptible(&ip->fh->wait); ++ ++ return 0; ++} ++ ++static void ipu7_psys_handle_graph_close_ack(struct ipu7_psys *psys, ++ void *buffer) ++{ ++ struct ipu7_msg_graph_close_ack *ack_msg = ++ (struct ipu7_msg_graph_close_ack *)buffer; ++ struct device *dev = &psys->dev; ++ struct ipu7_psys_stream *ip; ++ ++ dev_dbg(dev, "[ACK]%s:graph_id: %d\n", __func__, ack_msg->graph_id); ++ ++ if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { ++ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); ++ return; ++ } ++ ++ ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); ++ if (!ip) { ++ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); ++ return; ++ } ++ ++ if (ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSE_WAIT) { ++ dev_err(dev, "%s state %d\n", __func__, ip->graph_state); ++ goto graph_close_exit; ++ } ++ ++ ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; ++ ++graph_close_exit: ++ complete(&ip->graph_close); ++} ++ ++void ipu7_psys_handle_events(struct ipu7_psys *psys) ++{ ++ const struct ia_gofo_msg_header_ack *ack_header; ++ u8 buffer[FWPS_MSG_FW2HOST_MAX_SIZE]; ++ struct device *dev = &psys->dev; ++ u32 error = 0; ++ int ret = 0; ++ ++ do { ++#ifdef ENABLE_FW_OFFLINE_LOGGER ++ ipu7_fw_psys_get_log(psys); ++#endif ++ ret = ipu7_fw_psys_event_handle(psys, buffer); ++ if (ret) ++ break; ++ ++ ack_header = (const struct ia_gofo_msg_header_ack *)buffer; ++ ++ dev_dbg(dev, "[ACK]%s: ack msg %s received\n", __func__, ++ ps_fw_msg[ack_header->header.tlv_header.tlv_type].msg); ++ ++ if (!IA_GOFO_MSG_ERR_IS_OK(ack_header->err)) { ++ dev_err(dev, "group %d, code %d, detail: %d, %d\n", ++ ack_header->err.err_group, ++ ack_header->err.err_code, ++ ack_header->err.err_detail[0], ++ ack_header->err.err_detail[1]); ++ error = (ack_header->err.err_group == ++ IPU_MSG_ERR_GROUP_TASK) ? ++ IPU_PSYS_EVT_ERROR_FRAME : ++ IPU_PSYS_EVT_ERROR_INTERNAL; ++ } ++ ++ switch (ack_header->header.tlv_header.tlv_type) { ++ case IPU_MSG_TYPE_GRAPH_OPEN_ACK: ++ ipu7_psys_handle_graph_open_ack(psys, buffer); ++ break; ++ case IPU_MSG_TYPE_TASK_DONE: ++ ipu7_psys_handle_task_done(psys, buffer, error); ++ break; ++ case IPU_MSG_TYPE_GRAPH_CLOSE_ACK: ++ ipu7_psys_handle_graph_close_ack(psys, buffer); ++ break; ++ case IPU_MSG_TYPE_GENERAL_ERR: ++ /* already printed the log above for general error */ ++ break; ++ default: ++ dev_err(&psys->dev, "Unknown type %d\n", ++ ack_header->header.tlv_header.tlv_type); ++ } ++ } while (1); ++} ++ ++static int ipu7_psys_get_event(struct ipu7_psys_stream *ip, ++ struct ipu_psys_event *event) ++{ ++ struct ipu_psys_task_ack *ack; ++ ++ mutex_lock(&ip->event_mutex); ++ /* Check if there is already an event in the list */ ++ if (list_empty(&ip->ack_list)) { ++ mutex_unlock(&ip->event_mutex); ++ return -EAGAIN; ++ } ++ ++ ack = list_first_entry(&ip->ack_list, struct ipu_psys_task_ack, list); ++ ++ event->graph_id = ack->graph_id; ++ event->node_ctx_id = ack->node_ctx_id; ++ event->frame_id = ack->frame_id; ++ event->error = ack->err_code; ++ ++ list_move_tail(&ack->list, &ip->event_list); ++ mutex_unlock(&ip->event_mutex); ++ ++ dev_dbg(&ip->fh->psys->dev, "event graph %d cb %d frame %d dequeued", ++ event->graph_id, event->node_ctx_id, event->frame_id); ++ ++ return 0; ++} ++ ++long ipu7_ioctl_dqevent(struct ipu_psys_event *event, ++ struct ipu7_psys_fh *fh, unsigned int f_flags) ++{ ++ struct ipu7_psys *psys = fh->psys; ++ int ret = 0; ++ ++ dev_dbg(&psys->adev->auxdev.dev, "IOC_DQEVENT\n"); ++ ++ if (!(f_flags & O_NONBLOCK)) { ++ ret = wait_event_interruptible(fh->wait, ++ !ipu7_psys_get_event(fh->ip, ++ event)); ++ if (ret == -ERESTARTSYS) ++ return ret; ++ } else { ++ ret = ipu7_psys_get_event(fh->ip, event); ++ } ++ ++ return ret; ++} +diff --git a/drivers/staging/media/ipu7/psys/ipu7-psys.h b/drivers/staging/media/ipu7/psys/ipu7-psys.h +new file mode 100644 +index 000000000000..a72ce4a7c5b9 +--- /dev/null ++++ b/drivers/staging/media/ipu7/psys/ipu7-psys.h +@@ -0,0 +1,184 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2013 - 2024 Intel Corporation */ ++ ++#ifndef IPU7_PSYS_H ++#define IPU7_PSYS_H ++ ++#include ++#include ++#include ++ ++#include "ipu7.h" ++#include "ipu7-fw-psys.h" ++ ++#define IPU_PSYS_WORK_QUEUE system_power_efficient_wq ++ ++#define IPU_PSYS_CMD_QUEUE_SIZE 0x20 ++#define IPU_PSYS_TASK_QUEUE_SIZE 0x20 ++#define IPU_PSYS_ACK_QUEUE_SIZE 0x40 ++#define IPU_PSYS_LOG_QUEUE_SIZE 256 ++#define IPU_PSYS_OUT_MSG_SIZE 256 ++ ++/** ++ * Each event from FW will be first queued into a ++ * event queue, define the queue depth here ++ */ ++#define TASK_EVENT_QUEUE_SIZE 3U ++/** ++ * Each task queue from user will be first queued into ++ * a task queue, define the queue depth here ++ */ ++#define TASK_REQUEST_QUEUE_SIZE 8U ++ ++#define INVALID_STREAM_ID 0xFF ++/** ++ * Task request queues per stream ++ * ++ * Each task will first assigned a task queue buffer here, ++ * all the nodes will share the same task queue, maximum ++ * queue will be full there. ++ */ ++struct ipu_psys_task_queue { ++ struct ipu_psys_term_buffers task_buffers[MAX_GRAPH_TERMINALS]; ++ dma_addr_t ipu7_addr[MAX_GRAPH_TERMINALS]; ++ ++ struct ipu7_msg_task *msg_task; ++ dma_addr_t task_dma_addr; ++ ++ struct list_head list; ++ ++ /* task state of each task input, represent ipu7_msg_task_state */ ++ enum ipu7_msg_task_state task_state; ++}; ++ ++struct psys_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 */ ++}; ++ ++/** ++ * Task quest event context ++ * ++ * Each task request should get its event ack from FW and save ++ * to this structure and for user dequeue purpose. ++ */ ++struct ipu_psys_task_ack { ++ u8 graph_id; /* graph id of the task request */ ++ u8 node_ctx_id; /* logical node id */ ++ u8 frame_id; /* frame id of the original task request */ ++ ++ struct list_head list; ++ ++ u32 err_code; /* error indication to user */ ++}; ++ ++/** ++ * stream here is equal to pipe, each stream has ++ * its dedicated graph_id, and task request queue. ++ * ++ * For multiple stream supported design. ++ */ ++struct ipu7_psys_stream { ++ struct ipu7_psys_fh *fh; ++ ++ u8 graph_id; /* graph_id on this stream */ ++ ++ /* Handle events from FW */ ++ struct mutex event_mutex; ++ struct list_head event_list; /* Reserved event list */ ++ struct list_head ack_list; /* Received ack from FW */ ++ ++ /* Serialize task queue */ ++ struct mutex task_mutex; ++ struct list_head tq_list; /* Reserved task queue list */ ++ struct list_head tq_running_list; /* Running task sent to FW */ ++ ++ u8 num_nodes; /* Number of enabled nodes */ ++ struct graph_node nodes[MAX_GRAPH_NODES]; ++ u8 q_id[MAX_GRAPH_NODES]; /* syscom input queue id assigned by fw */ ++ ++ struct completion graph_open; ++ struct completion graph_close; ++ ++ /* Graph state, represent enum ipu7_msg_graph_state */ ++ enum ipu7_msg_graph_state graph_state; ++}; ++ ++struct task_struct; ++struct ipu7_psys { ++ struct cdev cdev; ++ struct device dev; ++ ++ struct mutex mutex; /* Psys various */ ++ int ready; /* psys fw status */ ++ spinlock_t ready_lock; /* protect psys firmware state */ ++ ++ struct list_head fhs; ++ ++ struct ipu7_psys_pdata *pdata; ++ struct ipu7_bus_device *adev; ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *debugfsdir; ++#endif ++ ++ unsigned long timeout; ++ ++ struct psys_fw_log *fw_log; ++ ++ /* available graph_id range is 0 ~ IPU_PSYS_NUM_STREAMS - 1 */ ++ u8 graph_id[IPU_PSYS_NUM_STREAMS]; ++ ++ /* Device state, represent enum ipu7_msg_dev_state */ ++ enum ipu7_msg_dev_state dev_state; ++ ++ struct ipu7_psys_config *subsys_config; ++ dma_addr_t subsys_config_dma_addr; ++}; ++ ++struct ipu7_psys_fh { ++ struct ipu7_psys *psys; ++ struct mutex mutex; /* Protects bufmap & kcmds fields */ ++ struct list_head list; ++ struct list_head bufmap; ++ wait_queue_head_t wait; ++ ++ struct ipu7_psys_stream *ip; ++}; ++ ++struct ipu7_dma_buf_attach { ++ struct device *dev; ++ u64 len; ++ uintptr_t *userptr; ++ struct sg_table *sgt; ++ struct page **pages; ++ size_t npages; ++}; ++ ++struct ipu7_psys_kbuffer { ++ u64 len; ++ uintptr_t *userptr; ++ u32 flags; ++ int fd; ++ void *kaddr; ++ struct list_head list; ++ dma_addr_t dma_addr; ++ struct sg_table *sgt; ++ struct dma_buf_attachment *db_attach; ++ struct dma_buf *dbuf; ++ bool valid; /* True when buffer is usable */ ++}; ++ ++#define inode_to_ipu_psys(inode) \ ++ container_of((inode)->i_cdev, struct ipu7_psys, cdev) ++ ++void ipu7_psys_setup_hw(struct ipu7_psys *psys); ++void ipu7_psys_subdomains_power(struct ipu7_psys *psys, bool on); ++void ipu7_psys_handle_events(struct ipu7_psys *psys); ++ ++long ipu7_ioctl_dqevent(struct ipu_psys_event *event, ++ struct ipu7_psys_fh *fh, unsigned int f_flags); ++ ++#endif /* IPU7_PSYS_H */ +-- +2.25.1 +