diff options
author | Ruofei Ma <ruofeim@google.com> | 2021-08-28 00:29:07 +0000 |
---|---|---|
committer | Ruofei Ma <ruofeim@google.com> | 2021-08-28 00:29:07 +0000 |
commit | 167d0bf2341ffee9c4374d13ec4063c45c067ad8 (patch) | |
tree | ecc9cec8c59fde65875f35463f0570be57fa8fc7 | |
parent | 1159ce6ca2d69f65e8e7a6a168ee22c8f2fd9349 (diff) | |
download | gchips-167d0bf2341ffee9c4374d13ec4063c45c067ad8.tar.gz |
Add BigOcean driver
Bug: 195458188
Signed-off-by: Ruofei Ma <ruofeim@google.com>
Change-Id: I5975a32a5a1ce5593a84f455f54c887191fb00fe
-rw-r--r-- | Kconfig | 13 | ||||
-rw-r--r-- | Makefile | 20 | ||||
-rw-r--r-- | bigo.c | 574 | ||||
-rw-r--r-- | bigo_debug.c | 57 | ||||
-rw-r--r-- | bigo_debug.h | 21 | ||||
-rw-r--r-- | bigo_io.c | 123 | ||||
-rw-r--r-- | bigo_io.h | 62 | ||||
-rw-r--r-- | bigo_iommu.c | 150 | ||||
-rw-r--r-- | bigo_iommu.h | 22 | ||||
-rw-r--r-- | bigo_of.c | 221 | ||||
-rw-r--r-- | bigo_of.h | 16 | ||||
-rw-r--r-- | bigo_pm.c | 140 | ||||
-rw-r--r-- | bigo_pm.h | 25 | ||||
-rw-r--r-- | bigo_priv.h | 125 | ||||
-rw-r--r-- | bigo_slc.c | 97 | ||||
-rw-r--r-- | bigo_slc.h | 30 |
16 files changed, 1696 insertions, 0 deletions
@@ -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 $(@) @@ -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_ |