summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuofei Ma <ruofeim@google.com>2021-08-28 00:29:07 +0000
committerRuofei Ma <ruofeim@google.com>2021-08-28 00:29:07 +0000
commit167d0bf2341ffee9c4374d13ec4063c45c067ad8 (patch)
treeecc9cec8c59fde65875f35463f0570be57fa8fc7
parent1159ce6ca2d69f65e8e7a6a168ee22c8f2fd9349 (diff)
downloadgchips-167d0bf2341ffee9c4374d13ec4063c45c067ad8.tar.gz
Add BigOcean driver
Bug: 195458188 Signed-off-by: Ruofei Ma <ruofeim@google.com> Change-Id: I5975a32a5a1ce5593a84f455f54c887191fb00fe
-rw-r--r--Kconfig13
-rw-r--r--Makefile20
-rw-r--r--bigo.c574
-rw-r--r--bigo_debug.c57
-rw-r--r--bigo_debug.h21
-rw-r--r--bigo_io.c123
-rw-r--r--bigo_io.h62
-rw-r--r--bigo_iommu.c150
-rw-r--r--bigo_iommu.h22
-rw-r--r--bigo_of.c221
-rw-r--r--bigo_of.h16
-rw-r--r--bigo_pm.c140
-rw-r--r--bigo_pm.h25
-rw-r--r--bigo_priv.h125
-rw-r--r--bigo_slc.c97
-rw-r--r--bigo_slc.h30
16 files changed, 1696 insertions, 0 deletions
diff --git a/Kconfig b/Kconfig
new file mode 100644
index 0000000..c8f1142
--- /dev/null
+++ b/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# BigOcean configuration
+#
+comment "BigOcean video codec"
+
+config BIGOCEAN
+ tristate "BigOcean video codec"
+ depends on EXYNOS_BTS
+ default n
+ select PM
+ help
+ Driver for BigOcean video codec
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e37b31d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for bigocean
+#
+
+obj-$(CONFIG_BIGOCEAN) += bigocean.o
+bigocean-$(CONFIG_BIGOCEAN) += bigo.o bigo_pm.o bigo_io.o bigo_of.o bigo_iommu.o
+bigocean-$(CONFIG_SLC_PARTITION_MANAGER) += bigo_slc.o
+bigocean-$(CONFIG_DEBUG_FS) += bigo_debug.o
+
+KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
+M ?= $(shell pwd)
+
+KBUILD_OPTIONS += CONFIG_BIGOCEAN=m CONFIG_SLC_PARTITION_MANAGER=m \
+ CONFIG_DEBUG_FS=m
+
+ccflags-y := -I$(KERNEL_SRC)/../google-modules/video/gchips
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNEL_SRC) M=$(M) $(KBUILD_OPTIONS) W=1 $(@)
diff --git a/bigo.c b/bigo.c
new file mode 100644
index 0000000..08b7500
--- /dev/null
+++ b/bigo.c
@@ -0,0 +1,574 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for BigOcean video accelerator
+ *
+ * Copyright 2020 Google LLC.
+ *
+ * Author: Vinay Kalia <vinaykalia@google.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/uaccess.h>
+#include <linux/platform_data/sscoredump.h>
+#include <linux/soc/samsung/exynos-smc.h>
+
+#include "bigo_io.h"
+#include "bigo_iommu.h"
+#include "bigo_of.h"
+#include "bigo_pm.h"
+#include "bigo_priv.h"
+#include "bigo_slc.h"
+#include "bigo_debug.h"
+
+#define BIGO_DEVCLASS_NAME "video_codec"
+#define BIGO_CHRDEV_NAME "bigocean"
+
+#define DEFAULT_WIDTH 3840
+#define DEFAULT_HEIGHT 2160
+#define DEFAULT_FPS 60
+#define BIGO_SMC_ID 0xd
+
+static struct sscd_platform_data bigo_sscd_platdata;
+
+static void bigo_sscd_release(struct device *dev)
+{
+ pr_debug("sscd release\n");
+}
+
+static struct platform_device bigo_sscd_dev = {
+ .name = BIGO_CHRDEV_NAME,
+ .driver_override = SSCD_NAME,
+ .id = -1,
+ .dev = {
+ .platform_data = &bigo_sscd_platdata,
+ .release = bigo_sscd_release,
+ },
+};
+
+static void bigo_coredump(struct bigo_core *core, const char *crash_info)
+{
+ struct sscd_platform_data *sscd_platdata = &bigo_sscd_platdata;
+ struct sscd_segment seg;
+
+ if (!sscd_platdata->sscd_report)
+ return;
+
+ memset(&seg, 0, sizeof(seg));
+ seg.addr = (void *)core->base;
+ seg.size = core->regs_size;
+ seg.flags = 0;
+ seg.paddr = (void *)core->paddr;
+ seg.vaddr = (void *)core->base;
+
+ sscd_platdata->sscd_report(&bigo_sscd_dev, &seg, 1,
+ SSCD_FLAGS_ELFARM64HDR, crash_info);
+}
+
+static inline int on_first_instance_open(struct bigo_core *core)
+{
+ int rc = bigo_pt_client_enable(core);
+
+ if (rc)
+ pr_info("failed to enable SLC");
+#if IS_ENABLED(CONFIG_PM)
+ rc = pm_runtime_get_sync(core->dev);
+ if (rc)
+ pr_err("failed to resume: %d\n", rc);
+#endif
+ return rc;
+}
+
+static inline void on_last_inst_close(struct bigo_core *core)
+{
+#if IS_ENABLED(CONFIG_PM)
+ if (pm_runtime_put_sync_suspend(core->dev))
+ pr_warn("failed to suspend\n");
+#endif
+ bigo_pt_client_disable(core);
+ kfree(core->job.regs);
+ core->job.regs = NULL;
+}
+
+static int bigo_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct bigo_core *core = container_of(inode->i_cdev, struct bigo_core, cdev);
+ struct bigo_inst *inst;
+
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+ if (!inst) {
+ rc = -ENOMEM;
+ pr_err("Failed to create instance\n");
+ goto err;
+ }
+ INIT_LIST_HEAD(&inst->list);
+ INIT_LIST_HEAD(&inst->buffers);
+ mutex_init(&inst->lock);
+ file->private_data = inst;
+ inst->height = DEFAULT_WIDTH;
+ inst->width = DEFAULT_HEIGHT;
+ inst->fps = DEFAULT_FPS;
+ inst->core = core;
+ mutex_lock(&core->lock);
+ if (list_empty(&core->instances)) {
+ rc = on_first_instance_open(core);
+ if (rc) {
+ pr_err("failed to setup first instance");
+ mutex_unlock(&core->lock);
+ goto err;
+ }
+ }
+ list_add_tail(&inst->list, &core->instances);
+ mutex_unlock(&core->lock);
+ bigo_update_qos(core);
+ pr_info("opened bigocean instance\n");
+
+err:
+ return rc;
+}
+
+static int bigo_release(struct inode *inode, struct file *file)
+{
+ struct bigo_core *core =
+ container_of(inode->i_cdev, struct bigo_core, cdev);
+ struct bigo_inst *inst = file->private_data;
+
+ if (!inst || !core) {
+ pr_err("No instance or core\n");
+ return -EINVAL;
+ }
+ bigo_unmap_all(inst);
+ mutex_lock(&core->lock);
+ list_del(&inst->list);
+ kfree(inst);
+ if (list_empty(&core->instances))
+ on_last_inst_close(core);
+ mutex_unlock(&core->lock);
+ bigo_update_qos(core);
+ pr_info("closed bigocean instance\n");
+ return 0;
+}
+
+static int bigo_run_job(struct bigo_core *core, struct bigo_job *job)
+{
+ long ret = 0;
+ int rc = 0;
+ u32 status = 0;
+
+ bigo_bypass_ssmt_pid(core);
+ bigo_push_regs(core, job->regs);
+ bigo_core_enable(core);
+ ret = wait_for_completion_timeout(&core->frame_done,
+ msecs_to_jiffies(JOB_COMPLETE_TIMEOUT_MS));
+ if (!ret) {
+ pr_err("timed out waiting for HW\n");
+ bigo_core_disable(core);
+ rc = -ETIMEDOUT;
+ } else {
+ rc = 0;
+ }
+
+ status = bigo_check_status(core);
+ ret = bigo_wait_disabled(core, BIGO_DISABLE_TIMEOUT_MS);
+ if (rc || ret || core->debugfs.trigger_ssr) {
+ if(core->debugfs.trigger_ssr)
+ rc = -EFAULT;
+ pr_err("timed out or failed to disable hw: %d, %ld, status: 0x%x\n",
+ rc, ret, status);
+ bigo_coredump(core, "bigo_timeout");
+ }
+
+ bigo_pull_regs(core, job->regs);
+ *(u32 *)(job->regs + BIGO_REG_STAT) = status;
+ if (rc || ret)
+ rc = -ETIMEDOUT;
+ return rc;
+}
+
+static int bigo_process(struct bigo_core *core, struct bigo_ioc_regs *desc)
+{
+ int rc = 0;
+ struct bigo_job *job = &core->job;
+
+ if (!desc) {
+ pr_err("Invalid input\n");
+ return -EINVAL;
+ }
+ if (desc->regs_size != core->regs_size) {
+ pr_err("Register size passed from userspace(%u) is different(%u)\n",
+ (unsigned int)desc->regs_size, core->regs_size);
+ return -EINVAL;
+ }
+
+ mutex_lock(&core->lock);
+
+ if (!job->regs) {
+ job->regs = kzalloc(core->regs_size, GFP_KERNEL);
+ if (!job->regs) {
+ rc = -ENOMEM;
+ goto unlock;
+ }
+ }
+
+ if (copy_from_user(job->regs, (void *)desc->regs, core->regs_size)) {
+ pr_err("Failed to copy from user\n");
+ rc = -EFAULT;
+ goto unlock;
+ }
+
+ /*TODO(vinaykalia@): Replace this with EDF scheduler.*/
+ rc = bigo_run_job(core, job);
+ if (rc) {
+ pr_err("Error running job\n");
+ goto unlock;
+ }
+
+ if (copy_to_user((void *)desc->regs, job->regs, core->regs_size)) {
+ pr_err("Failed to copy to user\n");
+ rc = -EFAULT;
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&core->lock);
+ return rc;
+}
+
+inline void bigo_config_frmrate(struct bigo_inst *inst, __u32 frmrate)
+{
+ mutex_lock(&inst->lock);
+ inst->fps = frmrate;
+ mutex_unlock(&inst->lock);
+ bigo_update_qos(inst->core);
+}
+
+inline void bigo_config_frmsize(struct bigo_inst *inst,
+ struct bigo_ioc_frmsize *frmsize)
+{
+ mutex_lock(&inst->lock);
+ inst->height = frmsize->height;
+ inst->width = frmsize->width;
+ mutex_unlock(&inst->lock);
+ bigo_update_qos(inst->core);
+}
+
+inline void bigo_config_secure(struct bigo_inst *inst, __u32 is_secure)
+{
+ mutex_lock(&inst->lock);
+ inst->is_secure = is_secure;
+ mutex_unlock(&inst->lock);
+}
+
+static long bigo_unlocked_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct bigo_inst *inst = file->private_data;
+ struct bigo_core *core =
+ container_of(file->f_inode->i_cdev, struct bigo_core, cdev);
+ void __user *user_desc = (void __user *)arg;
+ struct bigo_ioc_regs desc;
+ struct bigo_ioc_mapping mapping;
+ struct bigo_ioc_frmsize frmsize;
+ struct bigo_cache_info cinfo;
+ int rc = 0;
+
+ if (_IOC_TYPE(cmd) != BIGO_IOC_MAGIC) {
+ pr_err("Bad IOCTL\n");
+ return -EINVAL;
+ }
+ if (_IOC_NR(cmd) > BIGO_CMD_MAXNR) {
+ pr_err("Bad IOCTL\n");
+ return -EINVAL;
+ }
+ if (!inst || !core) {
+ pr_err("No instance or core\n");
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case BIGO_IOCX_PROCESS:
+ if (copy_from_user(&desc, user_desc, sizeof(desc))) {
+ pr_err("Failed to copy from user\n");
+ return -EFAULT;
+ }
+
+ if (inst->is_secure) {
+ rc = exynos_smc(SMC_PROTECTION_SET, 0, BIGO_SMC_ID,
+ SMC_PROTECTION_ENABLE);
+ if (rc) {
+ pr_err("failed to enable SMC_PROTECTION_SET: %d\n", rc);
+ break;
+ }
+ }
+
+ rc = bigo_process(core, &desc);
+ if (rc)
+ pr_err("Error processing data: %d\n", rc);
+
+ if (inst->is_secure) {
+ rc = exynos_smc(SMC_PROTECTION_SET, 0, BIGO_SMC_ID,
+ SMC_PROTECTION_DISABLE);
+ if (rc)
+ pr_err("failed to disable SMC_PROTECTION_SET: %d\n", rc);
+ }
+ break;
+ case BIGO_IOCX_MAP:
+ if (copy_from_user(&mapping, user_desc, sizeof(mapping))) {
+ pr_err("Failed to copy from user\n");
+ return -EFAULT;
+ }
+ rc = bigo_map(core, inst, &mapping);
+ if (rc)
+ pr_err("Error mapping: %d\n", mapping.fd);
+ if (copy_to_user(user_desc, &mapping, sizeof(mapping))) {
+ pr_err("Failed to copy to user\n");
+ rc = -EFAULT;
+ }
+ break;
+ case BIGO_IOCX_UNMAP:
+ if (copy_from_user(&mapping, user_desc, sizeof(mapping))) {
+ pr_err("Failed to copy from user\n");
+ return -EFAULT;
+ }
+ rc = bigo_unmap(inst, &mapping);
+ if (rc)
+ pr_err("Error un-mapping: %d\n", mapping.fd);
+ break;
+ case BIGO_IOCX_CONFIG_FRMRATE: {
+ u32 frmrate = (u32)arg;
+
+ bigo_config_frmrate(inst, frmrate);
+ break;
+ }
+ case BIGO_IOCX_CONFIG_FRMSIZE:
+ if (copy_from_user(&frmsize, user_desc, sizeof(frmsize))) {
+ pr_err("Failed to copy from user\n");
+ return -EFAULT;
+ }
+ bigo_config_frmsize(inst, &frmsize);
+ break;
+ case BIGO_IOCX_GET_CACHE_INFO:
+ bigo_get_cache_info(inst->core, &cinfo);
+ if (copy_to_user(user_desc, &cinfo, sizeof(cinfo))) {
+ pr_err("Failed to copy to user\n");
+ rc = -EFAULT;
+ }
+ break;
+ case BIGO_IOCX_CONFIG_SECURE: {
+ u32 is_secure = (u32)arg;
+ bigo_config_secure(inst, is_secure);
+ break;
+ }
+ case BIGO_IOCX_ABORT:
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static irqreturn_t bigo_isr(int irq, void *arg)
+{
+ struct bigo_core *core = (struct bigo_core *)arg;
+ u32 bigo_stat;
+ unsigned long flags;
+
+ bigo_stat = bigo_core_readl(core, BIGO_REG_STAT);
+
+ if (!(bigo_stat & BIGO_STAT_IRQ))
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&core->status_lock, flags);
+ core->stat_with_irq = bigo_stat;
+ spin_unlock_irqrestore(&core->status_lock, flags);
+ bigo_stat &= ~BIGO_STAT_IRQMASK;
+ bigo_core_writel(core, BIGO_REG_STAT, bigo_stat);
+ complete(&core->frame_done);
+ return IRQ_HANDLED;
+}
+
+#if IS_ENABLED(CONFIG_PM)
+static const struct dev_pm_ops bigo_pm_ops = {
+ SET_RUNTIME_PM_OPS(bigo_runtime_suspend, bigo_runtime_resume, NULL)
+};
+#endif
+
+static const struct file_operations bigo_fops = {
+ .owner = THIS_MODULE,
+ .open = bigo_open,
+ .release = bigo_release,
+ .unlocked_ioctl = bigo_unlocked_ioctl,
+ .compat_ioctl = bigo_unlocked_ioctl,
+};
+
+static int init_chardev(struct bigo_core *core)
+{
+ int rc;
+
+ cdev_init(&core->cdev, &bigo_fops);
+ core->cdev.owner = THIS_MODULE;
+ rc = alloc_chrdev_region(&core->devno, 0, 1, BIGO_CHRDEV_NAME);
+ if (rc < 0) {
+ pr_err("Failed to alloc chrdev region\n");
+ goto err;
+ }
+ rc = cdev_add(&core->cdev, core->devno, 1);
+ if (rc) {
+ pr_err("Failed to register chrdev\n");
+ goto err_cdev_add;
+ }
+
+ core->_class = class_create(THIS_MODULE, BIGO_DEVCLASS_NAME);
+ if (IS_ERR(core->_class)) {
+ rc = PTR_ERR(core->_class);
+ goto err_class_create;
+ }
+
+ core->svc_dev = device_create(core->_class, NULL, core->cdev.dev, core,
+ BIGO_CHRDEV_NAME);
+ if (IS_ERR(core->svc_dev)) {
+ pr_err("device_create err\n");
+ rc = PTR_ERR(core->svc_dev);
+ goto err_device_create;
+ }
+ return rc;
+
+err_device_create:
+ class_destroy(core->_class);
+err_class_create:
+ cdev_del(&core->cdev);
+err_cdev_add:
+ unregister_chrdev_region(core->devno, 1);
+err:
+ return rc;
+}
+
+static void deinit_chardev(struct bigo_core *core)
+{
+ if (!core)
+ return;
+
+ device_destroy(core->_class, core->devno);
+ class_destroy(core->_class);
+ cdev_del(&core->cdev);
+ unregister_chrdev_region(core->devno, 1);
+}
+
+static int bigo_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct bigo_core *core;
+
+ core = devm_kzalloc(&pdev->dev, sizeof(struct bigo_core), GFP_KERNEL);
+ if (!core) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ mutex_init(&core->lock);
+ INIT_LIST_HEAD(&core->instances);
+ INIT_LIST_HEAD(&core->pm.opps);
+ INIT_LIST_HEAD(&core->pm.bw);
+ spin_lock_init(&core->status_lock);
+ init_completion(&core->frame_done);
+ core->dev = &pdev->dev;
+ platform_set_drvdata(pdev, core);
+
+ rc = init_chardev(core);
+ if (rc) {
+ pr_err("Failed to initialize chardev for bigocean: %d\n", rc);
+ goto err_init_chardev;
+ }
+
+ rc = bigo_of_dt_parse(core);
+ if (rc) {
+ pr_err("Failed to parse DT node\n");
+ goto err_dt_parse;
+ }
+
+ rc = bigo_init_io(core, bigo_isr);
+ if (rc < 0) {
+ pr_err("failed to request irq\n");
+ goto err_io;
+ }
+
+ /* TODO(vinaykalia@): Move pm_runtime_enable call somewhere else? */
+ pm_runtime_enable(&pdev->dev);
+ rc = bigo_pm_init(core);
+ if (rc) {
+ pr_err("Failed to initialize power management\n");
+ goto err_io;
+ }
+
+ rc = iommu_register_device_fault_handler(&pdev->dev, bigo_iommu_fault_handler, core);
+ if (rc) {
+ pr_err("failed to register iommu fault handler: %d\n", rc);
+ goto err_fault_handler;
+ }
+
+ bigo_pt_client_register(pdev->dev.of_node, core);
+
+ if(platform_device_register(&bigo_sscd_dev))
+ pr_warn("Failed to register bigo_sscd_dev.\n");
+
+ bigo_init_debugfs(core);
+
+ return rc;
+
+err_fault_handler:
+ pm_runtime_disable(&pdev->dev);
+err_io:
+ bigo_of_dt_release(core);
+err_dt_parse:
+ deinit_chardev(core);
+err_init_chardev:
+ platform_set_drvdata(pdev, NULL);
+err:
+ return rc;
+}
+
+static int bigo_remove(struct platform_device *pdev)
+{
+ struct bigo_core *core = (struct bigo_core *)platform_get_drvdata(pdev);
+
+ bigo_uninit_debugfs(core);
+ platform_device_unregister(&bigo_sscd_dev);
+ bigo_pt_client_unregister(core);
+ iommu_unregister_device_fault_handler(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ bigo_of_dt_release(core);
+ deinit_chardev(core);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static const struct of_device_id bigo_dt_match[] = {
+ { .compatible = "google,bigocean" },
+ {}
+};
+
+static struct platform_driver bigo_driver = {
+ .probe = bigo_probe,
+ .remove = bigo_remove,
+ .driver = {
+ .name = "bigocean",
+ .owner = THIS_MODULE,
+ .of_match_table = bigo_dt_match,
+#ifdef CONFIG_PM
+ .pm = &bigo_pm_ops,
+#endif
+ },
+};
+
+module_platform_driver(bigo_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vinay Kalia <vinaykalia@google.com>");
+MODULE_DESCRIPTION("BigOcean driver");
diff --git a/bigo_debug.c b/bigo_debug.c
new file mode 100644
index 0000000..a8c539d
--- /dev/null
+++ b/bigo_debug.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for BigOcean video accelerator
+ *
+ * Copyright 2021 Google LLC.
+ *
+ * Author: Ruofei Ma <ruofeim@google.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include "bigo_debug.h"
+
+static int avail_freq_show(struct seq_file *s, void *unused)
+{
+ struct bigo_core *core = s->private;
+ struct bigo_opp *opp;
+
+ list_for_each_entry (opp, &core->pm.opps, list)
+ seq_printf(s, "%d\n", opp->freq_khz);
+
+ return 0;
+}
+
+static int avail_freqs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, avail_freq_show, inode->i_private);
+}
+
+static const struct file_operations avail_freqs_fops = {
+ .open = avail_freqs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void bigo_init_debugfs(struct bigo_core *core)
+{
+ struct bigo_debugfs *debugfs = &core->debugfs;
+
+ debugfs->set_freq = 0;
+ debugfs->trigger_ssr = 0;
+
+ debugfs->root = debugfs_create_dir("bigo", NULL);
+ debugfs_create_file("avail_freqs", 0400, debugfs->root, core,
+ &avail_freqs_fops);
+ debugfs_create_u32("set_freq", 0200, debugfs->root, &debugfs->set_freq);
+ debugfs_create_u32("trigger_ssr", 0600, debugfs->root,
+ &debugfs->trigger_ssr);
+}
+
+void bigo_uninit_debugfs(struct bigo_core *core)
+{
+ debugfs_remove_recursive(core->debugfs.root);
+} \ No newline at end of file
diff --git a/bigo_debug.h b/bigo_debug.h
new file mode 100644
index 0000000..42ddfce
--- /dev/null
+++ b/bigo_debug.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2021 Google LLC.
+ *
+ * Author: Ruofei Ma <ruofeim@google.com>
+ */
+
+#ifndef _BIGO_DEBUG_H_
+#define _BIGO_DEBUG_H_
+
+#include "bigo_priv.h"
+
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+void bigo_init_debugfs(struct bigo_core *core);
+void bigo_uninit_debugfs(struct bigo_core *core);
+#else
+static inline void bigo_init_debugfs(struct bigo_core *core) { }
+static inline void bigo_uninit_debugfs(struct bigo_core *core) { }
+#endif
+
+#endif /* _BIGO_DEBUG_H_ */
diff --git a/bigo_io.c b/bigo_io.c
new file mode 100644
index 0000000..c866f8d
--- /dev/null
+++ b/bigo_io.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * I/O methods for communication with BigOcean
+ *
+ * Copyright 2020 Google LLC.
+ *
+ * Author: Vinay Kalia <vinaykalia@google.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/module.h>
+
+#include "bigo_io.h"
+
+int bigo_init_io(struct bigo_core *core, irq_handler_t handler)
+{
+ struct platform_device *pdev = to_platform_device(core->dev);
+ int rc;
+
+ rc = devm_request_irq(&pdev->dev, core->irq, handler, IRQF_SHARED,
+ dev_name(&pdev->dev), core);
+ if (rc < 0)
+ pr_err("failed to request irq: %d\n", rc);
+ return rc;
+}
+
+u32 bigo_core_readl(struct bigo_core *core, ptrdiff_t offset)
+{
+ return readl(core->base + offset);
+}
+
+void bigo_core_writel(struct bigo_core *core, ptrdiff_t offset, u32 val)
+{
+ writel(val, core->base + offset);
+}
+
+void bigo_push_regs(struct bigo_core *core, void *regs)
+{
+ memcpy_toio(core->base, regs, core->regs_size);
+}
+
+void bigo_pull_regs(struct bigo_core *core, void *regs)
+{
+ memcpy_fromio(regs, core->base, core->regs_size);
+}
+
+void bigo_core_enable(struct bigo_core *core)
+{
+ u32 bigo_stat = bigo_core_readl(core, BIGO_REG_STAT);
+
+ bigo_stat |= BIGO_STAT_ENABLE;
+ bigo_core_writel(core, BIGO_REG_STAT, bigo_stat);
+ pr_debug("core enable\n");
+}
+
+void bigo_core_disable(struct bigo_core *core)
+{
+ u32 bigo_stat = bigo_core_readl(core, BIGO_REG_STAT);
+
+ bigo_stat &= ~BIGO_STAT_ENABLE;
+ bigo_core_writel(core, BIGO_REG_STAT, bigo_stat);
+ pr_debug("core disable\n");
+}
+
+inline bool bigo_core_is_enabled(struct bigo_core *core)
+{
+ return bigo_core_readl(core, BIGO_REG_STAT) & BIGO_STAT_ENABLE;
+}
+
+inline int bigo_wait_disabled(struct bigo_core *core, int timeout_ms)
+{
+ int i;
+
+ for (i = 0; i < timeout_ms; ++i) {
+ if (!bigo_core_is_enabled(core))
+ break;
+ usleep_range(900, 1100);
+ }
+ if (i >= timeout_ms) {
+ pr_err("Failed to disable the core in %d ms\n", i);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+u32 bigo_check_status(struct bigo_core *core)
+{
+ u32 status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&core->status_lock, flags);
+ status = core->stat_with_irq;
+ spin_unlock_irqrestore(&core->status_lock, flags);
+
+ if (status & BIGO_STAT_IRQ_TIMEOUT)
+ pr_err("hw timedout: 0x%x\n", status);
+ if (status & BIGO_STAT_IRQ_BUS_ERROR)
+ pr_err("bus error: 0x%x\n", status);
+ if (status & BIGO_STAT_IRQ_DEC_ERROR)
+ pr_err("decoding error: 0x%x\n", status);
+ if (status & BIGO_STAT_AXI_RD_OVERFLOW) {
+ pr_err("axi read overflow, status: 0x%x, id: %lu\n", status,
+ status & BIG_STAT_AXI_OVERFLOW_ID);
+ }
+ if (status & BIGO_STAT_AXI_WR_OVERFLOW) {
+ pr_err("axi write undererflow, status: 0x%x, id: %lu\n", status,
+ status & BIG_STAT_AXI_OVERFLOW_ID);
+ }
+ if (status & BIGO_STAT_AXI_RD_PENDING)
+ pr_err("axi read pending: 0x%x\n", status);
+ if (status & BIGO_STAT_AXI_WR_PENDING)
+ pr_err("axi write pending: 0x%x\n", status);
+
+ return status;
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vinay Kalia <vinaykalia@google.com>");
diff --git a/bigo_io.h b/bigo_io.h
new file mode 100644
index 0000000..040680e
--- /dev/null
+++ b/bigo_io.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2020 Google LLC.
+ *
+ * Author: Vinay Kalia <vinaykalia@google.com>
+ */
+
+#ifndef _BIGO_IO_H_
+#define _BIGO_IO_H_
+
+#include <linux/interrupt.h>
+
+#include "bigo_priv.h"
+
+#define BIGO_REG_PRODUCT 0x00
+#define BIGO_REG_CFG 0x04
+#define BIGO_REG_STAT 0x08
+
+#define BIGO_CFG_H264 BIT(2)
+#define BIGO_CFG_VP9D BIT(3)
+#define BIGO_CFG_VP9E BIT(4)
+#define BIGO_CFG_AV1 BIT(5)
+
+#define BIGO_STAT_ENABLE BIT(0)
+#define BIGO_STAT_MODE BIT(1)
+#define BIGO_STAT_IRQ_TIMEOUT BIT(2)
+#define BIGO_STAT_IRQ_BUS_ERROR BIT(3)
+#define BIGO_STAT_IRQ_FRAME_READY BIT(4)
+#define BIGO_STAT_IRQ_DEC_ERROR BIT(5)
+#define BIGO_STAT_IRQ BIT(6)
+#define BIGO_STAT_AXI_RD_OVERFLOW BIT(9)
+#define BIGO_STAT_AXI_WR_OVERFLOW BIT(10)
+#define BIGO_STAT_AXI_RD_PENDING BIT(19)
+#define BIGO_STAT_AXI_WR_PENDING BIT(20)
+#define BIG_STAT_AXI_OVERFLOW_ID GENMASK(30, 23)
+
+#define BIGO_STAT_IRQMASK GENMASK(6, 2)
+
+#define BIGO_DISABLE_TIMEOUT_MS 10
+/*
+ * 1. This timeout should be more than the max time HW takes to
+ * process max resolution frame at min frequency (usecase:
+ * non-realtime scenarios)
+ * 2. This timeout is not for catching bigocean hardware hangs
+ * because if hardware is really hung, it should trigger an IRQ with
+ * BIGO_STAT_IRQ_TIMEOUT_BIT so HW hang should be caught there.
+ * 3. This timeout is to catch any other issues with the system.
+ */
+#define JOB_COMPLETE_TIMEOUT_MS 500
+
+int bigo_init_io(struct bigo_core *core, irq_handler_t handler);
+u32 bigo_core_readl(struct bigo_core *core, ptrdiff_t offset);
+void bigo_core_writel(struct bigo_core *core, ptrdiff_t offset, u32 val);
+void bigo_push_regs(struct bigo_core *core, void *regs);
+void bigo_pull_regs(struct bigo_core *core, void *regs);
+void bigo_core_enable(struct bigo_core *core);
+void bigo_core_disable(struct bigo_core *core);
+bool bigo_core_is_enabled(struct bigo_core *core);
+int bigo_wait_disabled(struct bigo_core *core, int timeout_ms);
+u32 bigo_check_status(struct bigo_core *core);
+
+#endif //_BIGO_REGS_H_
diff --git a/bigo_iommu.c b/bigo_iommu.c
new file mode 100644
index 0000000..f59d279
--- /dev/null
+++ b/bigo_iommu.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * IOMMU operations for BigOcean
+ *
+ * Copyright 2020 Google LLC.
+ *
+ * Author: Vinay Kalia <vinaykalia@google.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/samsung-dma-mapping.h>
+
+#include "bigo_iommu.h"
+
+static void bigo_unmap_one(struct bigo_core *core, struct bufinfo *binfo)
+{
+ /* This lock is needed to unmap. Look @ b/180443732 */
+ mutex_lock(&core->lock);
+ binfo->attachment->dma_map_attrs |= DMA_ATTR_SKIP_LAZY_UNMAP;
+ dma_buf_unmap_attachment(binfo->attachment, binfo->sgt,
+ DMA_BIDIRECTIONAL);
+ dma_buf_detach(binfo->dmabuf, binfo->attachment);
+ dma_buf_put(binfo->dmabuf);
+ mutex_unlock(&core->lock);
+}
+
+void bigo_unmap_all(struct bigo_inst *inst)
+{
+ struct bufinfo *curr, *next;
+
+ mutex_lock(&inst->lock);
+ list_for_each_entry_safe(curr, next, &inst->buffers, list) {
+ list_del(&curr->list);
+ bigo_unmap_one(inst->core, curr);
+ kfree(curr);
+ }
+ mutex_unlock(&inst->lock);
+}
+
+static int check_mapped_list(struct bigo_core *core, struct bigo_inst *inst,
+ struct bigo_ioc_mapping *mapping)
+{
+ int found = -1;
+ struct bufinfo *binfo;
+
+ mutex_lock(&inst->lock);
+ list_for_each_entry(binfo, &inst->buffers, list) {
+ /*
+ * TODO(vinaykalia@): Do we need to check for size,
+ * offset, etc?
+ */
+ if (binfo->fd == mapping->fd) {
+ mapping->iova = binfo->iova;
+ found = 0;
+ break;
+ }
+ }
+ mutex_unlock(&inst->lock);
+ return found;
+}
+
+static int add_to_mapped_list(struct bigo_core *core, struct bigo_inst *inst,
+ struct bigo_ioc_mapping *mapping)
+{
+ int rc = 0;
+ struct bufinfo *binfo;
+
+ binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
+ if (!binfo)
+ return -ENOMEM;
+ binfo->dmabuf = dma_buf_get(mapping->fd);
+ if (IS_ERR(binfo->dmabuf)) {
+ rc = PTR_ERR(binfo->dmabuf);
+ pr_err("failed to get dma buf(%d): %d\n", mapping->fd, rc);
+ goto fail_buf_get;
+ }
+
+ binfo->attachment = dma_buf_attach(binfo->dmabuf, core->dev);
+ if (IS_ERR(binfo->attachment)) {
+ rc = PTR_ERR(binfo->attachment);
+ pr_err("failed to dma_buf_attach: %d\n", rc);
+ goto fail_attach;
+ }
+
+ binfo->sgt = dma_buf_map_attachment(binfo->attachment, DMA_BIDIRECTIONAL);
+ if (IS_ERR(binfo->sgt)) {
+ rc = PTR_ERR(binfo->sgt);
+ pr_err("failed to dma_buf_map_attachment: %d\n", rc);
+ goto fail_map_attachment;
+ }
+ binfo->iova = sg_dma_address(binfo->sgt->sgl);
+ binfo->fd = mapping->fd;
+ binfo->size = mapping->size;
+ binfo->offset = mapping->offset;
+ mutex_lock(&inst->lock);
+ list_add_tail(&binfo->list, &inst->buffers);
+ mutex_unlock(&inst->lock);
+ mapping->iova = binfo->iova;
+ return rc;
+
+fail_map_attachment:
+ dma_buf_detach(binfo->dmabuf, binfo->attachment);
+fail_attach:
+ dma_buf_put(binfo->dmabuf);
+fail_buf_get:
+ kfree(binfo);
+ return rc;
+}
+
+int bigo_map(struct bigo_core *core, struct bigo_inst *inst,
+ struct bigo_ioc_mapping *mapping)
+{
+ if (!check_mapped_list(core, inst, mapping))
+ return 0;
+
+ return add_to_mapped_list(core, inst, mapping);
+}
+
+int bigo_unmap(struct bigo_inst *inst, struct bigo_ioc_mapping *mapping)
+{
+ struct bufinfo *curr, *next;
+ struct bufinfo *found = NULL;
+
+ mutex_lock(&inst->lock);
+ list_for_each_entry_safe(curr, next, &inst->buffers, list) {
+ if (curr->fd == mapping->fd) {
+ list_del(&curr->list);
+ found = curr;
+ break;
+ }
+ }
+ mutex_unlock(&inst->lock);
+ if (!found)
+ return -ENOENT;
+
+ bigo_unmap_one(inst->core, found);
+ kfree(found);
+ return 0;
+}
+
+int bigo_iommu_fault_handler(struct iommu_fault *fault, void *param)
+{
+ return NOTIFY_OK;
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vinay Kalia <vinaykalia@google.com>");
diff --git a/bigo_iommu.h b/bigo_iommu.h
new file mode 100644
index 0000000..79da419
--- /dev/null
+++ b/bigo_iommu.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2020 Google LLC.
+ *
+ * Author: Vinay Kalia <vinaykalia@google.com>
+ */
+
+#ifndef _BIGO_IOMMU_H_
+#define _BIGO_IOMMU_H_
+
+#include <linux/dma-buf.h>
+#include <linux/iommu.h>
+
+#include "bigo_priv.h"
+
+void bigo_unmap_all(struct bigo_inst *inst);
+int bigo_map(struct bigo_core *core, struct bigo_inst *inst,
+ struct bigo_ioc_mapping *mapping);
+int bigo_unmap(struct bigo_inst *inst, struct bigo_ioc_mapping *mapping);
+int bigo_iommu_fault_handler(struct iommu_fault *fault, void *param);
+
+#endif //_BIGO_IOMMU_H_
diff --git a/bigo_of.c b/bigo_of.c
new file mode 100644
index 0000000..c7ed5e5
--- /dev/null
+++ b/bigo_of.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Parses BigOcean device tree node
+ *
+ * Copyright 2020 Google LLC.
+ *
+ * Author: Vinay Kalia <vinaykalia@google.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <soc/google/bts.h>
+
+#include "bigo_of.h"
+
+static int bigo_of_get_resource(struct bigo_core *core)
+{
+ struct platform_device *pdev = to_platform_device(core->dev);
+ struct resource *res;
+ int rc = 0;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bo");
+ if (IS_ERR_OR_NULL(res)) {
+ rc = PTR_ERR(res);
+ pr_err("Failed to find bo register base: %d\n", rc);
+ goto err;
+ }
+ core->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR_OR_NULL(core->base)) {
+ rc = PTR_ERR(core->base);
+ if (rc == 0)
+ rc = -EIO;
+ pr_err("Failed to map bo register base: %d\n", rc);
+ core->base = NULL;
+ goto err;
+ }
+ core->regs_size = res->end - res->start + 1;
+ core->paddr = (phys_addr_t)res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ssmt_bo_pid");
+ if (IS_ERR_OR_NULL(res)) {
+ rc = PTR_ERR(res);
+ pr_err("Failed to find ssmt_bo register base: %d\n", rc);
+ goto err;
+ }
+ core->slc.ssmt_pid_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR_OR_NULL(core->slc.ssmt_pid_base)) {
+ pr_warn("Failed to map ssmt_bo register base: %ld\n",
+ PTR_ERR(core->slc.ssmt_pid_base));
+ core->slc.ssmt_pid_base = NULL;
+ }
+
+ core->irq = platform_get_irq(pdev, 0);
+
+ if (core->irq < 0) {
+ rc = core->irq;
+ pr_err("platform_get_irq failed: %d\n", rc);
+ goto err;
+ }
+
+err:
+ return rc;
+}
+
+static void bigo_of_remove_opp_table(struct bigo_core *core)
+{
+ struct bigo_opp *opp, *tmp;
+
+ list_for_each_entry_safe(opp, tmp, &core->pm.opps, list) {
+ list_del(&opp->list);
+ kfree(opp);
+ }
+}
+
+static void bigo_of_remove_bw_table(struct bigo_core *core)
+{
+ struct bigo_bw *bw, *tmp;
+
+ list_for_each_entry_safe(bw, tmp, &core->pm.bw, list) {
+ list_del(&bw->list);
+ kfree(bw);
+ }
+}
+
+static int bigo_of_parse_opp_table(struct bigo_core *core)
+{
+ int rc = 0;
+ struct device_node *np;
+ struct bigo_opp *opp;
+
+ struct device_node *opp_np =
+ of_parse_phandle(core->dev->of_node, "bigo-opp-table", 0);
+ if (!opp_np) {
+ return -ENOENT;
+ goto err_add_table;
+ }
+ for_each_available_child_of_node(opp_np, np) {
+ opp = kmalloc(sizeof(*opp), GFP_KERNEL);
+ if (!opp) {
+ rc = -ENOMEM;
+ goto err_entry;
+ }
+ rc = of_property_read_u32(np, "load-pps", &opp->load_pps);
+ if (rc < 0) {
+ kfree(opp);
+ goto err_entry;
+ }
+ core->pm.max_load = opp->load_pps;
+ rc = of_property_read_u32(np, "freq-khz", &opp->freq_khz);
+ if (rc < 0) {
+ kfree(opp);
+ goto err_entry;
+ }
+
+ list_add_tail(&opp->list, &core->pm.opps);
+ }
+ return rc;
+err_entry:
+ bigo_of_remove_opp_table(core);
+err_add_table:
+ return rc;
+}
+
+static int bigo_of_parse_bw_table(struct bigo_core *core)
+{
+ int rc = 0;
+ struct device_node *np;
+ struct bigo_bw *bw;
+
+ struct device_node *bw_np =
+ of_parse_phandle(core->dev->of_node, "bigo-bw-table", 0);
+ if (!bw_np) {
+ return -ENOENT;
+ goto err_add_table;
+ }
+ for_each_available_child_of_node(bw_np, np) {
+ bw = kmalloc(sizeof(*bw), GFP_KERNEL);
+ if (!bw) {
+ rc = -ENOMEM;
+ goto err_entry;
+ }
+ rc = of_property_read_u32(np, "load-pps", &bw->load_pps);
+ if (rc < 0) {
+ kfree(bw);
+ goto err_entry;
+ }
+ rc = of_property_read_u32(np, "rd-bw", &bw->rd_bw);
+ if (rc < 0) {
+ kfree(bw);
+ goto err_entry;
+ }
+ rc = of_property_read_u32(np, "wr-bw", &bw->wr_bw);
+ if (rc < 0) {
+ kfree(bw);
+ goto err_entry;
+ }
+ rc = of_property_read_u32(np, "pk-bw", &bw->pk_bw);
+ if (rc < 0) {
+ kfree(bw);
+ goto err_entry;
+ }
+ list_add_tail(&bw->list, &core->pm.bw);
+ }
+ return rc;
+err_entry:
+ bigo_of_remove_bw_table(core);
+err_add_table:
+ return rc;
+}
+
+int bigo_of_dt_parse(struct bigo_core *core)
+{
+ int rc = 0;
+
+ rc = bigo_of_get_resource(core);
+ if (rc < 0) {
+ pr_err("failed to get respource: %d\n", rc);
+ goto err_get_res;
+ }
+
+ rc = bigo_of_parse_opp_table(core);
+ if (rc < 0) {
+ pr_err("failed to parse bigocean OPP table\n");
+ goto err_parse_opp_table;
+ }
+
+ rc = bigo_of_parse_bw_table(core);
+ if (rc < 0) {
+ pr_err("failed to parse bigocean bandwidth table\n");
+ goto err_parse_bw_table;
+ }
+
+ core->pm.bwindex = bts_get_bwindex("bo");
+ if (core->pm.bwindex < 0) {
+ rc = core->pm.bwindex;
+ goto err_bwindex;
+ }
+
+ return rc;
+err_bwindex:
+ bigo_of_remove_bw_table(core);
+err_parse_bw_table:
+ bigo_of_remove_opp_table(core);
+err_parse_opp_table:
+err_get_res:
+ return rc;
+}
+
+void bigo_of_dt_release(struct bigo_core *core)
+{
+ if (!core)
+ return;
+ bigo_of_remove_opp_table(core);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vinay Kalia <vinaykalia@google.com>");
diff --git a/bigo_of.h b/bigo_of.h
new file mode 100644
index 0000000..70e0fd4
--- /dev/null
+++ b/bigo_of.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2020 Google LLC.
+ *
+ * Author: Vinay Kalia <vinaykalia@google.com>
+ */
+
+#ifndef _BIGO_OF_H_
+#define _BIGO_OF_H_
+
+#include "bigo_priv.h"
+
+int bigo_of_dt_parse(struct bigo_core *core);
+void bigo_of_dt_release(struct bigo_core *core);
+
+#endif //_BIGO_OF_H_
diff --git a/bigo_pm.c b/bigo_pm.c
new file mode 100644
index 0000000..25fc7aa
--- /dev/null
+++ b/bigo_pm.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * BigOcean power management
+ *
+ * Copyright 2020 Google LLC.
+ *
+ * Author: Vinay Kalia <vinaykalia@google.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/pm_opp.h>
+#include <soc/google/bts.h>
+
+#include "bigo_pm.h"
+#include "bigo_io.h"
+
+static inline u32 bigo_get_total_load(struct bigo_core *core)
+{
+ struct bigo_inst *inst;
+ u32 load = 0;
+ u32 curr_load = 0;
+
+ if (list_empty(&core->instances))
+ return 0;
+
+ list_for_each_entry(inst, &core->instances, list) {
+ curr_load = inst->width * inst->height * inst->fps / 1024;
+ if (curr_load < core->pm.max_load - load) {
+ load += curr_load;
+ } else {
+ load = core->pm.max_load;
+ break;
+ }
+ }
+ /* 1 <= load <= core->pm.max_load */
+ load = max(1U, load);
+ load = min(load, core->pm.max_load);
+ return load;
+}
+
+static inline u32 bigo_get_target_freq(struct bigo_core *core, u32 load)
+{
+ struct bigo_opp *opp;
+
+ list_for_each_entry(opp, &core->pm.opps, list) {
+ if (opp->load_pps >= load)
+ break;
+ }
+ return opp->freq_khz;
+}
+
+static inline struct bigo_bw *bigo_get_target_bw(struct bigo_core *core, u32 load)
+{
+ struct bigo_bw *bw;
+
+ list_for_each_entry(bw, &core->pm.bw, list) {
+ if (bw->load_pps >= load)
+ break;
+ }
+ return bw;
+}
+
+static inline void bigo_set_freq(struct bigo_core *core, u32 freq)
+{
+ if (core->debugfs.set_freq)
+ freq = core->debugfs.set_freq;
+
+ if (!exynos_pm_qos_request_active(&core->pm.qos_bigo))
+ exynos_pm_qos_add_request(&core->pm.qos_bigo, PM_QOS_BO_THROUGHPUT, freq);
+ else
+ exynos_pm_qos_update_request(&core->pm.qos_bigo, freq);
+}
+
+static void bigo_scale_freq(struct bigo_core *core)
+{
+ u32 load = bigo_get_total_load(core);
+ u32 freq = bigo_get_target_freq(core, load);
+
+ bigo_set_freq(core, freq);
+}
+
+static void bigo_get_bw(struct bigo_core *core, struct bts_bw *bw)
+{
+ u32 load = bigo_get_total_load(core);
+ struct bigo_bw *bandwidth = bigo_get_target_bw(core, load);
+
+ bw->read = bandwidth->rd_bw;
+ bw->write = bandwidth->wr_bw;
+ bw->peak = bandwidth->pk_bw;
+ pr_debug("BW: load: %u, rd: %u, wr: %u, pk: %u", load, bw->read, bw->write, bw->peak);
+}
+
+static int bigo_scale_bw(struct bigo_core *core)
+{
+ struct bts_bw bw;
+
+ bigo_get_bw(core, &bw);
+ return bts_update_bw(core->pm.bwindex, bw);
+}
+
+void bigo_update_qos(struct bigo_core *core)
+{
+ int rc;
+
+ mutex_lock(&core->lock);
+ rc = bigo_scale_bw(core);
+
+ if (rc)
+ pr_warn("%s: failed to scale bandwidth: %d\n", __func__, rc);
+
+ bigo_scale_freq(core);
+ mutex_unlock(&core->lock);
+}
+
+/*
+ * bigo_pm_init(): Initializes power management for bigocean.
+ * @core: the bigocean core
+ */
+int bigo_pm_init(struct bigo_core *core)
+{
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_PM)
+int bigo_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+int bigo_runtime_resume(struct device *dev)
+{
+ return 0;
+}
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vinay Kalia <vinaykalia@google.com>");
diff --git a/bigo_pm.h b/bigo_pm.h
new file mode 100644
index 0000000..c571b87
--- /dev/null
+++ b/bigo_pm.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2020 Google LLC.
+ *
+ * Author: Vinay Kalia <vinaykalia@google.com>
+ */
+
+#ifndef _BIGO_PM_H_
+#define _BIGO_PM_H_
+
+#include <linux/device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+
+#include "bigo_priv.h"
+
+int bigo_pm_init(struct bigo_core *core);
+
+#if IS_ENABLED(CONFIG_PM)
+int bigo_runtime_suspend(struct device *dev);
+int bigo_runtime_resume(struct device *dev);
+#endif
+void bigo_update_qos(struct bigo_core *core);
+
+#endif //_BIGO_PM_H_
diff --git a/bigo_priv.h b/bigo_priv.h
new file mode 100644
index 0000000..649dbc6
--- /dev/null
+++ b/bigo_priv.h
@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2020 Google LLC.
+ *
+ * Author: Vinay Kalia <vinaykalia@google.com>
+ */
+
+#ifndef _BIGO_PRIV_H_
+#define _BIGO_PRIV_H_
+
+#include <linux/cdev.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <soc/google/exynos_pm_qos.h>
+
+#include "uapi/linux/bigo.h"
+
+#if IS_ENABLED(CONFIG_SLC_PARTITION_MANAGER)
+#include <soc/google/pt.h>
+#endif
+
+
+#define AVG_CNT 30
+#define PEAK_CNT 5
+#define BUS_WIDTH 16
+
+struct bufinfo {
+ struct list_head list;
+ struct dma_buf *dmabuf;
+ struct sg_table *sgt;
+ struct dma_buf_attachment *attachment;
+ size_t size;
+ off_t offset;
+ int fd;
+ dma_addr_t iova;
+};
+
+struct bigo_opp {
+ struct list_head list;
+ u32 freq_khz;
+ u32 load_pps;
+};
+
+struct bigo_bw {
+ struct list_head list;
+ u32 load_pps;
+ u32 rd_bw;
+ u32 wr_bw;
+ u32 pk_bw;
+};
+
+struct power_manager {
+ int bwindex;
+ struct exynos_pm_qos_request qos_bigo;
+ struct list_head opps;
+ struct list_head bw;
+ u32 max_load;
+};
+
+struct slc_manager {
+#if IS_ENABLED(CONFIG_SLC_PARTITION_MANAGER)
+ struct pt_handle *pt_hnd;
+ ptid_t pid;
+#endif
+ void __iomem *ssmt_pid_base;
+ size_t size;
+};
+
+struct bigo_job {
+ void *regs;
+ size_t regs_size;
+};
+
+struct bigo_debugfs {
+ struct dentry *root;
+ u32 set_freq;
+ u32 trigger_ssr;
+};
+
+struct bigo_core {
+ struct class *_class;
+ struct cdev cdev;
+ struct device *svc_dev;
+ struct device *dev;
+ dev_t devno;
+ /* mutex protecting this data structure */
+ struct mutex lock;
+ void __iomem *base;
+ int irq;
+ struct completion frame_done;
+ struct list_head instances;
+ struct ion_client *mem_client;
+ u32 stat_with_irq;
+ struct bigo_job job;
+ struct power_manager pm;
+ struct slc_manager slc;
+ unsigned int regs_size;
+ struct bigo_inst *curr_inst;
+ phys_addr_t paddr;
+ struct bigo_debugfs debugfs;
+ spinlock_t status_lock;
+};
+
+struct bigo_inst {
+ struct list_head list;
+ struct list_head buffers;
+ /* mutex protecting this data structure */
+ struct mutex lock;
+ struct bigo_core *core;
+ u32 height;
+ u32 width;
+ u32 fps;
+ u32 is_secure;
+ struct bigo_bw avg_bw[AVG_CNT];
+ struct bigo_bw pk_bw[AVG_CNT];
+ int job_cnt;
+ u32 hw_cycles[AVG_CNT];
+};
+
+inline void set_curr_inst(struct bigo_core *core, struct bigo_inst *inst);
+inline struct bigo_inst *get_curr_inst(struct bigo_core *core);
+
+#endif //_BIGO_PRIV_H_
diff --git a/bigo_slc.c b/bigo_slc.c
new file mode 100644
index 0000000..744d77c
--- /dev/null
+++ b/bigo_slc.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SLC operations for BigOcean
+ *
+ * Copyright 2020 Google LLC.
+ *
+ * Author: Vinay Kalia <vinaykalia@google.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/io.h>
+#include <linux/module.h>
+
+#include "bigo_slc.h"
+
+#define SID_S3_TEMPORAL 3
+#define SID_COMP_INFO 6
+#define SID_S4_SECONDARY_COLBUF 19
+#define SID_S4_COMP_TILE_COL 20
+#define SID_S4_CDEC_TILE_COL 21
+
+void bigo_bypass_ssmt_pid(struct bigo_core *core)
+{
+ int sid;
+ unsigned int offset;
+ const int cache_off = 0x400;
+ const int rd_alloc_off = 0x600;
+ const int wr_alloc_off = 0x800;
+
+ if (!core->slc.ssmt_pid_base)
+ return;
+
+ for (sid = 0; sid < 32; sid++) {
+ offset = sid * 4;
+ if (sid == SID_S3_TEMPORAL || sid == SID_COMP_INFO ||
+ sid == SID_S4_SECONDARY_COLBUF || sid == SID_S4_COMP_TILE_COL ||
+ sid == SID_S4_CDEC_TILE_COL) {
+ writel(core->slc.pid, core->slc.ssmt_pid_base + offset);
+ }
+ writel(0xe, core->slc.ssmt_pid_base + cache_off + offset);
+ writel(0x80000000, core->slc.ssmt_pid_base + rd_alloc_off + offset);
+ writel(0x80000000, core->slc.ssmt_pid_base + wr_alloc_off + offset);
+ }
+}
+
+int bigo_pt_client_enable(struct bigo_core *core)
+{
+ int rc = 0;
+
+ if (!core->slc.pt_hnd)
+ return 0;
+
+ core->slc.pid = pt_client_enable_size(core->slc.pt_hnd, 0, &core->slc.size);
+ if (core->slc.pid < 0) {
+ pr_warn("Failed to get BO pid\n");
+ rc = core->slc.pid;
+ }
+ return rc;
+}
+
+void bigo_pt_client_disable(struct bigo_core *core)
+{
+ if (core->slc.pt_hnd)
+ pt_client_disable(core->slc.pt_hnd, 0);
+}
+
+void bigo_get_cache_info(struct bigo_core *core, struct bigo_cache_info *cinfo)
+{
+ cinfo->size = core->slc.size;
+ cinfo->pid = core->slc.pid;
+}
+
+static void bigo_pt_resize_cb(void *data, int id, size_t size_allocated)
+{
+ struct bigo_core *core = (struct bigo_core *)data;
+
+ pr_debug("Received SLC resize callback, id: %d, size: %zu\n", id, size_allocated);
+ core->slc.size = size_allocated;
+}
+
+void bigo_pt_client_register(struct device_node *node, struct bigo_core *core)
+{
+ core->slc.pt_hnd = pt_client_register(node, (void *)core, bigo_pt_resize_cb);
+ if (IS_ERR(core->slc.pt_hnd)) {
+ core->slc.pt_hnd = NULL;
+ pr_warn("Failed to register pt_client.\n");
+ }
+}
+
+void bigo_pt_client_unregister(struct bigo_core *core)
+{
+ pt_client_unregister(core->slc.pt_hnd);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vinay Kalia <vinaykalia@google.com>");
diff --git a/bigo_slc.h b/bigo_slc.h
new file mode 100644
index 0000000..cf9c1df
--- /dev/null
+++ b/bigo_slc.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2020 Google LLC.
+ *
+ * Author: Vinay Kalia <vinaykalia@google.com>
+ */
+
+#ifndef _BIGO_SLC_H_
+#define _BIGO_SLC_H_
+
+#include "bigo_priv.h"
+
+#if IS_ENABLED(CONFIG_SLC_PARTITION_MANAGER)
+#include <soc/google/pt.h>
+void bigo_pt_client_register(struct device_node *node, struct bigo_core *core);
+void bigo_pt_client_unregister(struct bigo_core *core);
+int bigo_pt_client_enable(struct bigo_core *core);
+void bigo_pt_client_disable(struct bigo_core *core);
+void bigo_get_cache_info(struct bigo_core *core, struct bigo_cache_info *cinfo);
+void bigo_bypass_ssmt_pid(struct bigo_core *core);
+#else
+static inline void bigo_pt_client_register(struct device_node *node, struct bigo_core *core) { }
+static inline void bigo_pt_client_unregister(struct bigo_core *core) { }
+static inline int bigo_pt_client_enable(struct bigo_core *core) { return -EINVAL; }
+static inline void bigo_pt_client_disable(struct bigo_core *core) { }
+static inline void bigo_get_cache_info(struct bigo_core *core, struct bigo_cache_info *cinfo) { }
+static inline void bigo_bypass_ssmt_pid(struct bigo_core *core) { }
+#endif
+
+#endif //_BIGO_SLC_H_