summaryrefslogtreecommitdiff
path: root/drivers/mxc/hantro_845_h1/hx280enc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mxc/hantro_845_h1/hx280enc.c')
-rwxr-xr-xdrivers/mxc/hantro_845_h1/hx280enc.c883
1 files changed, 883 insertions, 0 deletions
diff --git a/drivers/mxc/hantro_845_h1/hx280enc.c b/drivers/mxc/hantro_845_h1/hx280enc.c
new file mode 100755
index 000000000000..0ba6440212a8
--- /dev/null
+++ b/drivers/mxc/hantro_845_h1/hx280enc.c
@@ -0,0 +1,883 @@
+/*
+ * Encoder device driver (kernel module)
+ *
+ * Copyright (c) 2013-2018, VeriSilicon Inc.
+ * Copyright (C) 2012 Google Finland Oy.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+--------------------------------------------------------------------------------
+--
+-- Abstract : 6280/7280/8270/8290/H1 Encoder device driver (kernel module)
+--
+------------------------------------------------------------------------------*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+/* needed for __init,__exit directives */
+#include <linux/init.h>
+/* needed for remap_page_range
+ SetPageReserved
+ ClearPageReserved
+*/
+#include <linux/mm.h>
+/* obviously, for kmalloc */
+#include <linux/slab.h>
+/* for struct file_operations, register_chrdev() */
+#include <linux/fs.h>
+/* standard error codes */
+#include <linux/errno.h>
+
+#include <linux/moduleparam.h>
+/* request_irq(), free_irq() */
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+
+/* needed for virt_to_phys() */
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <linux/uaccess.h>
+#include <linux/ioport.h>
+
+#include <asm/irq.h>
+#include <linux/of_irq.h>
+#include <linux/version.h>
+
+/* our own stuff */
+#include <linux/hx280enc.h>
+
+#ifndef VSI
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+//static int hantro_h1_major = -1; /* dynamic allocation */
+static struct class *hantro_h1_class;
+#define DEVICE_NAME "mxc_hantro_h1"
+static struct device *hantro_h1_dev;
+static struct clk *hantro_clk_h1;
+static struct clk *hantro_clk_h1_bus;
+#define IRQF_DISABLED 0x0
+#endif
+
+#include <linux/delay.h>
+
+/* module description */
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Google Finland Oy");
+MODULE_DESCRIPTION("Hantro 6280/7280/8270/8290/H1 Encoder driver");
+
+/* this is ARM Integrator specific stuff */
+#ifndef VSI
+#define INTEGRATOR_LOGIC_MODULE0_BASE 0x38320000
+#else
+#define INTEGRATOR_LOGIC_MODULE0_BASE 0xC0000000
+#endif
+#define BLK_CTL_BASE 0x38330000 //0x38320000
+/*
+#define INTEGRATOR_LOGIC_MODULE1_BASE 0xD0000000
+#define INTEGRATOR_LOGIC_MODULE2_BASE 0xE0000000
+#define INTEGRATOR_LOGIC_MODULE3_BASE 0xF0000000
+*/
+
+#define VP_PB_INT_LT 30
+/*
+#define INT_EXPINT1 10
+#define INT_EXPINT2 11
+#define INT_EXPINT3 12
+*/
+/* these could be module params in the future */
+
+#define ENC_IO_BASE INTEGRATOR_LOGIC_MODULE0_BASE
+#define ENC_IO_SIZE (500 * 4) /* bytes */
+
+#define ENC_HW_ID1 0x62800000
+#define ENC_HW_ID2 0x72800000
+#define ENC_HW_ID3 0x82700000
+#define ENC_HW_ID4 0x82900000
+#define ENC_HW_ID5 0x48310000
+
+#define HX280ENC_BUF_SIZE 0
+
+static unsigned long base_port = INTEGRATOR_LOGIC_MODULE0_BASE;
+static int irq = VP_PB_INT_LT;
+
+/* for critical data access */
+static DEFINE_SPINLOCK(owner_lock);
+/* for irq wait */
+static DECLARE_WAIT_QUEUE_HEAD(enc_wait_queue);
+/* for reserve hw */
+static DECLARE_WAIT_QUEUE_HEAD(enc_hw_queue);
+
+/* module_param(name, type, perm) */
+module_param(base_port, ulong, 0644);
+module_param(irq, int, 0644);
+
+/* and this is our MAJOR; use 0 for dynamic allocation (recommended)*/
+static int hx280enc_major;
+
+/* here's all the must remember stuff */
+typedef struct {
+ u32 hw_id; //hw id to indicate project
+ u32 is_valid; //indicate this core is hantro's core or not
+ u32 is_reserved; //indicate this core is occupied by user or not
+ int pid; //indicate which process is occupying the core
+ u32 irq_received; //indicate this core receives irq
+ u32 irq_status;
+ int irq;
+ unsigned long iobaseaddr;
+ unsigned int iosize;
+
+ volatile u8 *hwregs;
+ struct fasync_struct *async_queue;
+ struct device *dev;
+ struct mutex dev_mutex;
+} hx280enc_t;
+
+/* dynamic allocation? */
+static hx280enc_t hx280enc_data;
+
+static int ReserveIO(void);
+static void ReleaseIO(void);
+static void ResetAsic(hx280enc_t *dev);
+
+#ifdef HX280ENC_DEBUG
+static void dump_regs(unsigned long data);
+#endif
+
+/* IRQ handler */
+static irqreturn_t hx280enc_isr(int irq, void *dev_id);
+
+#ifndef VSI
+static int hantro_h1_clk_enable(struct device *dev)
+{
+ clk_prepare(hantro_clk_h1);
+ clk_enable(hantro_clk_h1);
+ clk_prepare(hantro_clk_h1_bus);
+ clk_enable(hantro_clk_h1_bus);
+ return 0;
+}
+
+static int hantro_h1_clk_disable(struct device *dev)
+{
+ if (hantro_clk_h1) {
+ clk_disable(hantro_clk_h1);
+ clk_unprepare(hantro_clk_h1);
+ }
+ if (hantro_clk_h1_bus) {
+ clk_disable(hantro_clk_h1_bus);
+ clk_unprepare(hantro_clk_h1_bus);
+ }
+ return 0;
+}
+
+static int hantro_h1_ctrlblk_reset(struct device *dev)
+{
+ volatile u8 *iobase;
+ u32 val;
+
+ //config H1
+ hantro_h1_clk_enable(dev);
+ iobase = (volatile u8 *)ioremap_nocache(BLK_CTL_BASE, 0x10000);
+
+ val = ioread32(iobase);
+ val &= (~0x4);
+ iowrite32(val, iobase); // assert soft reset
+ udelay(2);
+
+ val = ioread32(iobase);
+ val |= 0x4;
+ iowrite32(val, iobase); // release soft reset
+
+ val = ioread32(iobase+0x4);
+ val |= 0x4;
+ iowrite32(val, iobase + 0x4); // enable clock
+
+ iowrite32(0xFFFFFFFF, iobase + 0x14); // H1 fuse encoder enable
+ iounmap(iobase);
+ hantro_h1_clk_disable(dev);
+ return 0;
+}
+
+static int hantro_h1_power_on_disirq(hx280enc_t *hx280enc)
+{
+ //spin_lock_irq(&owner_lock);
+ mutex_lock(&hx280enc->dev_mutex);
+ disable_irq(hx280enc->irq);
+ pm_runtime_get_sync(hx280enc->dev);
+ enable_irq(hx280enc->irq);
+ mutex_unlock(&hx280enc->dev_mutex);
+ //spin_unlock_irq(&owner_lock);
+ return 0;
+}
+
+#endif
+
+/* the device's mmap method. The VFS has kindly prepared the process's
+ * vm_area_struct for us, so we examine this to see what was requested.
+ */
+
+static int hx280enc_mmap(struct file *filp, struct vm_area_struct *vm)
+{
+#ifdef VSI
+ int result = -EINVAL;
+
+ result = -EINVAL;
+ vma->vm_ops = &hx280enc_vm_ops;
+ return result;
+#else
+ if (vm->vm_pgoff == (hx280enc_data.iobaseaddr >> PAGE_SHIFT)) {
+ vm->vm_flags |= VM_IO;
+ vm->vm_page_prot = pgprot_noncached(vm->vm_page_prot);
+ PDEBUG("hx280enc mmap: size=0x%lX, page off=0x%lX\n", (vm->vm_end - vm->vm_start), vm->vm_pgoff);
+ return remap_pfn_range(vm, vm->vm_start, vm->vm_pgoff, vm->vm_end - vm->vm_start,
+ vm->vm_page_prot) ? -EAGAIN : 0;
+ } else {
+ pr_err("invalid map offset :0x%lX\n", vm->vm_pgoff);
+ return -EINVAL;
+ }
+#endif
+}
+
+static int CheckEncIrq(hx280enc_t *dev)
+{
+ unsigned long flags;
+ int rdy = 0;
+
+ spin_lock_irqsave(&owner_lock, flags);
+
+ if (dev->irq_received) {
+ /* reset the wait condition(s) */
+ PDEBUG("check irq ready\n");
+ dev->irq_received = 0;
+ rdy = 1;
+ }
+
+ spin_unlock_irqrestore(&owner_lock, flags);
+ //printk("rdy=%d\n",rdy);
+
+ return rdy;
+}
+
+unsigned int WaitEncReady(hx280enc_t *dev)
+{
+ PDEBUG("WaitEncReady\n");
+
+ if (wait_event_interruptible(enc_wait_queue, CheckEncIrq(dev))) {
+ PDEBUG("ENC wait_event_interruptible interrupted\n");
+ return -ERESTARTSYS;
+ }
+
+ return 0;
+}
+
+int CheckCoreOccupation(hx280enc_t *dev)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&owner_lock, flags);
+ if (!dev->is_reserved) {
+ dev->is_reserved = 1;
+ dev->pid = current->pid;
+ ret = 1;
+ PDEBUG("CheckCoreOccupation pid=%d\n", dev->pid);
+ }
+ spin_unlock_irqrestore(&owner_lock, flags);
+
+ return ret;
+}
+
+int GetWorkableCore(hx280enc_t *dev)
+{
+ int ret = 0;
+
+ PDEBUG("GetWorkableCore\n");
+
+ if (dev->is_valid && CheckCoreOccupation(dev))
+ ret = 1;
+
+ return ret;
+}
+
+long ReserveEncoder(hx280enc_t *dev)
+{
+ /* lock a core that has specified core id*/
+ if (wait_event_interruptible(enc_hw_queue, GetWorkableCore(dev) != 0))
+ return -ERESTARTSYS;
+
+ return 0;
+}
+
+void ReleaseEncoder(hx280enc_t *dev)
+{
+ unsigned long flags;
+
+ PDEBUG("ReleaseEncoder\n");
+
+ spin_lock_irqsave(&owner_lock, flags);
+ PDEBUG("relase reseve by pid=%d with current->pid=%d\n", dev->pid, current->pid);
+ if (dev->is_reserved && dev->pid == current->pid) {
+ dev->pid = -1;
+ dev->is_reserved = 0;
+ }
+
+ dev->irq_received = 0;
+ dev->irq_status = 0;
+ spin_unlock_irqrestore(&owner_lock, flags);
+
+ wake_up_interruptible_all(&enc_hw_queue);
+
+}
+
+
+static long hx280enc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+
+ PDEBUG("ioctl cmd 0x%X\n", cmd);
+ /*
+ * extract the type and number bitfields, and don't encode
+ * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
+ */
+ if (_IOC_TYPE(cmd) != HX280ENC_IOC_MAGIC)
+ return -ENOTTY;
+ if (_IOC_NR(cmd) > HX280ENC_IOC_MAXNR)
+ return -ENOTTY;
+
+ /*
+ * the direction is a bitmask, and VERIFY_WRITE catches R/W
+ * transfers. `Type' is user-oriented, while
+ * access_ok is kernel-oriented, so the concept of "read" and
+ * "write" is reversed
+ */
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ err = !access_ok(VERIFY_WRITE, (void *) arg, _IOC_SIZE(cmd));
+ else if (_IOC_DIR(cmd) & _IOC_WRITE)
+ err = !access_ok(VERIFY_READ, (void *) arg, _IOC_SIZE(cmd));
+ if (err)
+ return -EFAULT;
+
+ switch (_IOC_NR(cmd)) {
+ case _IOC_NR(HX280ENC_IOCGHWOFFSET):
+ __put_user(hx280enc_data.iobaseaddr, (unsigned long *) arg);
+ break;
+ case _IOC_NR(HX280ENC_IOCGHWIOSIZE):
+ __put_user(hx280enc_data.iosize, (unsigned int *) arg);
+ break;
+ case _IOC_NR(HX280ENC_IOCH_ENC_RESERVE): {
+ int ret;
+
+ PDEBUG("Reserve ENC Cores\n");
+ ret = ReserveEncoder(&hx280enc_data);
+ return ret;
+ }
+ case _IOC_NR(HX280ENC_IOCH_ENC_RELEASE):
+ PDEBUG("Release ENC Core\n");
+ ReleaseEncoder(&hx280enc_data);
+ break;
+ case _IOC_NR(HX280ENC_IOCG_CORE_WAIT): {
+ int ret;
+
+ ret = WaitEncReady(&hx280enc_data);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int hx280enc_open(struct inode *inode, struct file *filp)
+{
+ int result = 0;
+ hx280enc_t *dev = &hx280enc_data;
+
+ filp->private_data = (void *) dev;
+
+#ifndef VSI
+ hantro_h1_clk_enable(dev->dev);
+ hantro_h1_power_on_disirq(dev);
+#endif
+
+ PDEBUG("dev opened\n");
+ return result;
+}
+
+static int hx280enc_release(struct inode *inode, struct file *filp)
+{
+ hx280enc_t *dev = (hx280enc_t *) filp->private_data;
+ unsigned long flags;
+#ifdef HX280ENC_DEBUG
+ dump_regs((unsigned long) dev); /* dump the regs */
+#endif
+
+ PDEBUG("dev closed\n");
+ spin_lock_irqsave(&owner_lock, flags);
+ if (dev->is_reserved == 1 && dev->pid == current->pid) {
+ dev->pid = -1;
+ dev->is_reserved = 0;
+ dev->irq_received = 0;
+ dev->irq_status = 0;
+ PDEBUG("release reserved core\n");
+ }
+ spin_unlock_irqrestore(&owner_lock, flags);
+ wake_up_interruptible_all(&enc_hw_queue);
+
+#ifndef VSI
+ pm_runtime_put_sync(hantro_h1_dev);
+ hantro_h1_clk_disable(hantro_h1_dev);
+#endif
+
+ return 0;
+}
+
+static long hx280enc_ioctl32(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ long err = 0;
+#define HX280ENC_IOCTL32(err, filp, cmd, arg) { \
+ mm_segment_t old_fs = get_fs(); \
+ set_fs(KERNEL_DS); \
+ err = hx280enc_ioctl(filp, cmd, arg); \
+ if (err) \
+ return err; \
+ set_fs(old_fs); \
+}
+
+union {
+ unsigned long kux;
+ unsigned int kui;
+} karg;
+ void __user *up = compat_ptr(arg);
+
+ switch (_IOC_NR(cmd)) {
+ case _IOC_NR(HX280ENC_IOCGHWOFFSET):
+ err = get_user(karg.kux, (s32 __user *)up);
+ if (err)
+ return err;
+ HX280ENC_IOCTL32(err, filp, cmd, (unsigned long)&karg);
+ err = put_user(((s32)karg.kux), (s32 __user *)up);
+ break;
+ case _IOC_NR(HX280ENC_IOCGHWIOSIZE):
+ err = get_user(karg.kui, (s32 __user *)up);
+ if (err)
+ return err;
+ HX280ENC_IOCTL32(err, filp, cmd, (unsigned long)&karg);
+ err = put_user(((s32)karg.kui), (s32 __user *)up);
+ break;
+ case _IOC_NR(HX280ENC_IOCH_ENC_RESERVE): {
+ int ret;
+ PDEBUG("Reserve ENC Cores\n");
+ ret = ReserveEncoder(&hx280enc_data);
+ return ret;
+ }
+ case _IOC_NR(HX280ENC_IOCH_ENC_RELEASE): {
+ PDEBUG("Release ENC Core\n");
+ ReleaseEncoder(&hx280enc_data);
+ break;
+ }
+ case _IOC_NR(HX280ENC_IOCG_CORE_WAIT): {
+ int ret;
+ ret = WaitEncReady(&hx280enc_data);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+/* VFS methods */
+static struct file_operations hx280enc_fops = {
+ .owner = THIS_MODULE,
+ .open = hx280enc_open,
+ .release = hx280enc_release,
+ .unlocked_ioctl = hx280enc_ioctl,
+ .fasync = NULL,
+ .mmap = hx280enc_mmap,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = hx280enc_ioctl32,
+#endif
+};
+
+#ifndef VSI
+static int hx280enc_init(void)
+#else
+static int __init hx280enc_init(void)
+#endif
+{
+ int result;
+
+ PDEBUG(KERN_INFO "hx280enc: module init - base_port=0x%08lx irq=%i\n",
+ base_port, irq);
+
+ hx280enc_data.iobaseaddr = base_port;
+ hx280enc_data.iosize = ENC_IO_SIZE;
+ hx280enc_data.irq = irq;
+ hx280enc_data.async_queue = NULL;
+ hx280enc_data.hwregs = NULL;
+
+ result = register_chrdev(hx280enc_major, "hx280enc", &hx280enc_fops);
+ if (result < 0) {
+ PDEBUG(KERN_INFO "hx280enc: unable to get major <%d>\n", hx280enc_major);
+ return result;
+ } else if (result != 0) /* this is for dynamic major */
+ hx280enc_major = result;
+
+ result = ReserveIO();
+ if (result < 0)
+ goto err;
+
+ ResetAsic(&hx280enc_data); /* reset hardware */
+
+ /* get the IRQ line */
+ if (irq != -1) {
+ result = request_irq(irq, hx280enc_isr,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ SA_INTERRUPT | SA_SHIRQ,
+#else
+ //IRQF_DISABLED | IRQF_SHARED,
+ IRQF_SHARED,
+#endif
+ "hx280enc", (void *) &hx280enc_data);
+ if (result == -EINVAL) {
+ PDEBUG(KERN_ERR "hx280enc: Bad irq number or handler\n");
+ ReleaseIO();
+ goto err;
+ } else if (result == -EBUSY) {
+ PDEBUG(KERN_ERR "hx280enc: IRQ <%d> busy, change your config\n",
+ hx280enc_data.irq);
+ ReleaseIO();
+ goto err;
+ }
+ } else
+ PDEBUG(KERN_INFO "hx280enc: IRQ not in use!\n");
+ irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);
+
+ pr_info("hx280enc: module inserted. Major <%d>\n", hx280enc_major);
+ return 0;
+
+err:
+ unregister_chrdev(hx280enc_major, "hx280enc");
+ PDEBUG(KERN_ERR "hx280enc: module not inserted\n");
+ return result;
+}
+
+#ifndef VSI
+static void hx280enc_cleanup(void)
+#else
+static void __exit hx280enc_cleanup(void)
+#endif
+{
+ writel(0, hx280enc_data.hwregs + 0x38); /* disable HW */
+ writel(0, hx280enc_data.hwregs + 0x04); /* clear enc IRQ */
+
+ /* free the encoder IRQ */
+ if (hx280enc_data.irq != -1)
+ free_irq(hx280enc_data.irq, (void *) &hx280enc_data);
+
+ ReleaseIO();
+ unregister_chrdev(hx280enc_major, "hx280enc");
+
+ PDEBUG(KERN_INFO "hx280enc: module removed\n");
+}
+
+#ifdef VSI
+module_init(hx280enc_init);
+module_exit(hx280enc_cleanup);
+#endif
+static int ReserveIO(void)
+{
+ long int hwid;
+
+ if (!request_mem_region(hx280enc_data.iobaseaddr, hx280enc_data.iosize, "hx280enc")) {
+ PDEBUG(KERN_INFO "hx280enc: failed to reserve HW regs\n");
+ return -EBUSY;
+ }
+ hx280enc_data.hwregs = (volatile u8 *) ioremap_nocache(hx280enc_data.iobaseaddr, hx280enc_data.iosize);
+ if (hx280enc_data.hwregs == NULL) {
+ PDEBUG(KERN_INFO "hx280enc: failed to ioremap HW regs\n");
+ ReleaseIO();
+ return -EBUSY;
+ }
+
+ hwid = readl(hx280enc_data.hwregs);
+
+ /* check for encoder HW ID */
+ if ((((hwid >> 16) & 0xFFFF) != ((ENC_HW_ID1 >> 16) & 0xFFFF)) &&
+ (((hwid >> 16) & 0xFFFF) != ((ENC_HW_ID2 >> 16) & 0xFFFF)) &&
+ (((hwid >> 16) & 0xFFFF) != ((ENC_HW_ID3 >> 16) & 0xFFFF)) &&
+ (((hwid >> 16) & 0xFFFF) != ((ENC_HW_ID4 >> 16) & 0xFFFF)) &&
+ (((hwid >> 16) & 0xFFFF) != ((ENC_HW_ID5 >> 16) & 0xFFFF))) {
+ PDEBUG(KERN_ERR "hx280enc: HW not found at 0x%08lx\n", hx280enc_data.iobaseaddr);
+#ifdef HX280ENC_DEBUG
+ dump_regs((unsigned long) &hx280enc_data);
+#endif
+ ReleaseIO();
+ return -EBUSY;
+ }
+
+ hx280enc_data.hw_id = hwid;
+ hx280enc_data.is_valid = 1;
+
+ PDEBUG(KERN_INFO "hx280enc: HW at base <0x%08lx> with ID <0x%08lx>\n", hx280enc_data.iobaseaddr, hwid);
+ return 0;
+}
+
+static void ReleaseIO(void)
+{
+ if (hx280enc_data.is_valid == 0)
+ return;
+ if (hx280enc_data.hwregs)
+ iounmap((void *) hx280enc_data.hwregs);
+ release_mem_region(hx280enc_data.iobaseaddr, hx280enc_data.iosize);
+}
+
+irqreturn_t hx280enc_isr(int irq, void *dev_id)
+{
+ hx280enc_t *dev = (hx280enc_t *) dev_id;
+ u32 irq_status;
+ unsigned long flags;
+ u32 is_write1_clr;
+
+ spin_lock_irqsave(&owner_lock, flags);
+ if (!dev->is_reserved) {
+ spin_unlock_irqrestore(&owner_lock, flags);
+ return IRQ_HANDLED;
+ }
+ spin_unlock_irqrestore(&owner_lock, flags);
+ irq_status = readl(dev->hwregs + 0x04);
+
+ /* BASE_HWFuse2 = 0x4a0; HWCFGIrqClearSupport = 0x00800000 */
+ is_write1_clr = (readl(dev->hwregs + 0x4a0) & 0x00800000);
+ if (irq_status & 0x01) {
+ /* clear enc IRQ and slice ready interrupt bit */
+ if (is_write1_clr)
+ writel(irq_status & (0x101), dev->hwregs + 0x04);
+ else
+ writel(irq_status & (~0x101), dev->hwregs + 0x04);
+
+ /* Handle slice ready interrupts. The reference implementation
+ * doesn't signal slice ready interrupts to EWL.
+ * The EWL will poll the slices ready register value. */
+ if ((irq_status & 0x1FE) == 0x100) {
+ PDEBUG("Slice ready IRQ handled!\n");
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&owner_lock, flags);
+ dev->irq_received = 1;
+ dev->irq_status = irq_status & (~0x01);
+ spin_unlock_irqrestore(&owner_lock, flags);
+
+ wake_up_interruptible_all(&enc_wait_queue);
+
+ PDEBUG("IRQ handled!\n");
+ return IRQ_HANDLED;
+ } else {
+ PDEBUG("IRQ received, but NOT handled!\n");
+ return IRQ_NONE;
+ }
+}
+
+static void ResetAsic(hx280enc_t *dev)
+{
+ int i;
+
+ if (dev->is_valid == 0)
+ return;
+
+ writel(0, dev->hwregs + 0x38);
+
+ for (i = 4; i < dev->iosize; i += 4)
+ writel(0, dev->hwregs + i);
+}
+
+#ifdef HX280ENC_DEBUG
+static void dump_regs(unsigned long data)
+{
+ hx280enc_t *dev = (hx280enc_t *) data;
+ int i;
+
+ PDEBUG("Reg Dump Start\n");
+ for (i = 0; i < dev->iosize; i += 4)
+ PDEBUG("\toffset %02X = %08X\n", i, readl(dev->hwregs + i));
+ PDEBUG("Reg Dump End\n");
+}
+#endif
+
+
+
+#ifndef VSI
+static int hantro_h1_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct device *temp_class;
+ struct resource *res;
+ unsigned long reg_base;
+
+ hantro_h1_dev = &pdev->dev;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs_hantro_h1");
+ if (!res) {
+ pr_err("hantro h1: unable to get vpu base addr\n");
+ return -ENODEV;
+ }
+ reg_base = res->start;
+ if ((ulong)reg_base != base_port) {
+ pr_err("hantro h1: regbase(0x%lX) not equal to expected value(0x%lX)\n", reg_base, base_port);
+ return -ENODEV;
+ }
+
+ irq = platform_get_irq_byname(pdev, "irq_hantro_h1");
+ if (irq < 0) {
+ pr_err("hantro h1: not find valid irq\n");
+ return -ENODEV;
+ }
+
+ hantro_clk_h1 = clk_get(&pdev->dev, "clk_hantro_h1");
+ if (IS_ERR(hantro_clk_h1)) {
+ pr_err("hantro h1: get clock failed, %p\n", hantro_clk_h1);
+ err = -ENXIO;
+ goto error;
+ }
+ hantro_clk_h1_bus = clk_get(&pdev->dev, "clk_hantro_h1_bus");
+ if (IS_ERR(hantro_clk_h1_bus)) {
+ pr_err("hantro h1: get bus clock failed, %p\n", hantro_clk_h1_bus);
+ err = -ENXIO;
+ goto error;
+ }
+
+ PDEBUG("hantro: h1 clock: 0x%lX, 0x%lX\n", clk_get_rate(hantro_clk_h1), clk_get_rate(hantro_clk_h1_bus));
+
+ hantro_h1_clk_enable(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+ hantro_h1_ctrlblk_reset(&pdev->dev);
+
+ err = hx280enc_init();
+ if (0 != err) {
+ pr_err("hantro h1: init failed\n");
+ goto error;
+ }
+
+ hantro_h1_class = class_create(THIS_MODULE, "mxc_hantro_h1");
+ if (IS_ERR(hantro_h1_class)) {
+ err = PTR_ERR(hantro_h1_class);
+ goto error;
+ }
+ temp_class = device_create(hantro_h1_class, NULL, MKDEV(hx280enc_major, 0), NULL, DEVICE_NAME);
+ if (IS_ERR(temp_class)) {
+ err = PTR_ERR(temp_class);
+ goto err_out_class;
+ }
+ hx280enc_data.dev = &pdev->dev;
+ platform_set_drvdata(pdev, &hx280enc_data);
+ mutex_init(&hx280enc_data.dev_mutex);
+
+ goto out;
+
+err_out_class:
+ device_destroy(hantro_h1_class, MKDEV(hx280enc_major, 0));
+ class_destroy(hantro_h1_class);
+error:
+ pr_err("hantro probe failed\n");
+out:
+ pm_runtime_put_sync(&pdev->dev);
+ hantro_h1_clk_disable(&pdev->dev);
+ return err;
+}
+
+static int hantro_h1_dev_remove(struct platform_device *pdev)
+{
+ hantro_h1_clk_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+ if (hx280enc_major > 0) {
+ device_destroy(hantro_h1_class, MKDEV(hx280enc_major, 0));
+ class_destroy(hantro_h1_class);
+ hx280enc_cleanup();
+ hx280enc_major = 0;
+ }
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ hantro_h1_clk_disable(&pdev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int hantro_h1_suspend(struct device *dev)
+{
+ pm_runtime_put_sync_suspend(dev); //power off
+ return 0;
+}
+static int hantro_h1_resume(struct device *dev)
+{
+ hx280enc_t *hx280enc = dev_get_drvdata(dev);
+
+ hantro_h1_power_on_disirq(hx280enc);
+ hantro_h1_ctrlblk_reset(dev);
+ return 0;
+}
+static int hantro_h1_runtime_suspend(struct device *dev)
+{
+ //release_bus_freq(BUS_FREQ_HIGH);
+ return 0;
+}
+
+static int hantro_h1_runtime_resume(struct device *dev)
+{
+ //request_bus_freq(BUS_FREQ_HIGH);
+ hantro_h1_ctrlblk_reset(dev);
+ return 0;
+}
+
+static const struct dev_pm_ops hantro_h1_pm_ops = {
+ SET_RUNTIME_PM_OPS(hantro_h1_runtime_suspend, hantro_h1_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(hantro_h1_suspend, hantro_h1_resume)
+};
+#endif //CONFIG_PM
+
+static const struct of_device_id hantro_h1_of_match[] = {
+ { .compatible = "nxp,imx8mm-hantro-h1", },
+ {/* sentinel */}
+};
+MODULE_DEVICE_TABLE(of, vpu_of_match);
+
+
+static struct platform_driver mxchantro_h1_driver = {
+ .driver = {
+ .name = "mxc_hantro_h1",
+ .of_match_table = hantro_h1_of_match,
+#ifdef CONFIG_PM
+ .pm = &hantro_h1_pm_ops,
+#endif
+ },
+ .probe = hantro_h1_probe,
+ .remove = hantro_h1_dev_remove,
+};
+
+static int __init hantro_h1_init(void)
+{
+ int ret = platform_driver_register(&mxchantro_h1_driver);
+
+ return ret;
+}
+
+static void __exit hantro_h1_exit(void)
+{
+ //clk_put(hantro_clk);
+ platform_driver_unregister(&mxchantro_h1_driver);
+}
+
+module_init(hantro_h1_init);
+module_exit(hantro_h1_exit);
+
+#endif
+