diff options
Diffstat (limited to 'drivers/mxc/hantro_845_h1/hx280enc.c')
-rwxr-xr-x | drivers/mxc/hantro_845_h1/hx280enc.c | 883 |
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 + |