/***************************************************************************** * The GPL License (GPL) * * Copyright (c) 2015-2020, VeriSilicon Inc. * Copyright (c) 2011-2014, Google Inc. * * 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 may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html *****************************************************************************/ #include #include "dwl_defs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define CONFIG_DEVICE_THERMAL_HANTRO #ifdef CONFIG_DEVICE_THERMAL_HANTRO #include #define HANTRO_REG_THERMAL_NOTIFIER(a) register_devfreq_cooling_notifier(a) #define HANTRO_UNREG_THERMAL_NOTIFIER(a) unregister_devfreq_cooling_notifier(a) static DEFINE_SPINLOCK(thermal_lock); /*1:hot, 0: not hot*/ static int thermal_event; static int hantro_clock_ratio = 2; static int hantro_dynamic_clock; module_param(hantro_clock_ratio, int, 0644); module_param(hantro_dynamic_clock, int, 0644); MODULE_PARM_DESC(hantro_clock_ratio, "clock ratio 1/N"); MODULE_PARM_DESC(hantro_dynamic_clock, "enable or disable dynamic clock rate"); #endif /*hantro G1 regs config including dec and pp*/ #define HANTRO_DEC_ORG_REGS 60 #define HANTRO_PP_ORG_REGS 41 #define HANTRO_DEC_EXT_REGS 27 #define HANTRO_PP_EXT_REGS 9 #define HANTRO_G1_DEC_TOTAL_REGS (HANTRO_DEC_ORG_REGS + HANTRO_DEC_EXT_REGS) #define HANTRO_PP_TOTAL_REGS (HANTRO_PP_ORG_REGS + HANTRO_PP_EXT_REGS) #define HANTRO_G1_TOTAL_REGS 155 /*G1 total regs*/ #define HANTRO_DEC_ORG_FIRST_REG 0 #define HANTRO_DEC_ORG_LAST_REG 59 #define HANTRO_DEC_EXT_FIRST_REG 119 #define HANTRO_DEC_EXT_LAST_REG 145 #define HANTRO_PP_ORG_FIRST_REG 60 #define HANTRO_PP_ORG_LAST_REG 100 #define HANTRO_PP_EXT_FIRST_REG 146 #define HANTRO_PP_EXT_LAST_REG 154 /*hantro G2 reg config*/ #define HANTRO_G2_DEC_REGS 265 /*G2 total regs*/ #define HANTRO_G2_DEC_FIRST_REG 0 #define HANTRO_G2_DEC_LAST_REG (HANTRO_G2_DEC_REGS - 1) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define DEC_IO_SIZE_MAX (MAX(HANTRO_G2_DEC_REGS, HANTRO_G1_TOTAL_REGS) * 4) /******************************************************************** * PORTING SEGMENT * NOTES: customer should modify these configuration if do porting to own platform. * Please guarantee the base_addr, io_size,dec_irq belong to same core. ********************************************************************/ #define HXDEC_MAX_CORES 2 /* Logic module base address */ #define SOCLE_LOGIC_0_BASE 0x38300000 #define SOCLE_LOGIC_1_BASE 0x38310000 #define BLK_CTL_BASE 0x38330000 //0x38320000 #define VEXPRESS_LOGIC_0_BASE 0xFC010000 #define VEXPRESS_LOGIC_1_BASE 0xFC020000 #define DEC_IO_SIZE_0 ((HANTRO_G2_DEC_REGS) * 4) /* bytes */ #define DEC_IO_SIZE_1 ((HANTRO_G2_DEC_REGS) * 4) /* bytes */ #define HANTRO_DEC_DEF_CLK (600000000) #define HANTRO_BUS_DEF_CLK (800000000) /***********************************************************************/ #define IS_G1(hw_id) ((hw_id == 0x6731) ? 1:0) static const int DecHwId[] = { 0x8190, /* Legacy HW */ 0x8170, 0x9170, 0x9190, 0x6731, /* G1 */ 0x6732 /* G2 */ }; static ulong multicorebase[HXDEC_MAX_CORES] = { SOCLE_LOGIC_0_BASE, SOCLE_LOGIC_1_BASE }; static struct class *hantro_class; #define DEVICE_NAME "mxc_hantro" //static struct device *hantro_dev[HXDEC_MAX_CORES]; typedef struct { struct clk *dec; struct clk *bus; } hantrodec_clk; static int hantro_dbg = -1; module_param(hantro_dbg, int, 0644); MODULE_PARM_DESC(hantro_dbg, "Debug level (0-1)"); #undef PDEBUG #define PDEBUG(fmt, arg...) \ do { \ if (hantro_dbg > 0) { \ dev_info(hantrodec_data[0].dev, fmt, ## arg); \ } \ } while (0) static int hantrodec_major; static int cores = 2; /* here's all the must remember stuff */ typedef struct { //char *buffer; unsigned int iosize; volatile u8 *hwregs; int irq; int hw_id; int hw_active; int core_id; //int cores; //struct fasync_struct *async_queue_dec; //struct fasync_struct *async_queue_pp; hantrodec_clk clk; u32 dec_regs[DEC_IO_SIZE_MAX/4]; struct semaphore dec_core_sem; struct semaphore pp_core_sem; struct file *dec_owner; struct file *pp_owner; u32 cfg; u32 timeout; atomic_t irq_rx; atomic_t irq_tx; struct device *dev; struct mutex dev_mutex; int thermal_cur; } hantrodec_t; static hantrodec_t hantrodec_data[HXDEC_MAX_CORES]; /* dynamic allocation? */ typedef struct { char inst_id; char core_id; //1:g1; 2:g2; 3:unknow } hantrodec_instance; static unsigned long instance_mask; #define MAX_HANTRODEC_INSTANCE 32 static hantrodec_instance hantrodec_ctx[MAX_HANTRODEC_INSTANCE]; static int ReserveIO(int); static void ReleaseIO(int); static void ResetAsic(hantrodec_t *dev); #ifdef HANTRODEC_DEBUG static void dump_regs(hantrodec_t *dev); #endif /* IRQ handler */ static irqreturn_t hantrodec_isr(int irq, void *dev_id); static int dec_irq; static int pp_irq; /* spinlock_t owner_lock = SPIN_LOCK_UNLOCKED; */ static DEFINE_SPINLOCK(owner_lock); static DECLARE_WAIT_QUEUE_HEAD(dec_wait_queue); static DECLARE_WAIT_QUEUE_HEAD(pp_wait_queue); static DECLARE_WAIT_QUEUE_HEAD(hw_queue); #define DWL_CLIENT_TYPE_H264_DEC 1U #define DWL_CLIENT_TYPE_MPEG4_DEC 2U #define DWL_CLIENT_TYPE_JPEG_DEC 3U #define DWL_CLIENT_TYPE_PP 4U #define DWL_CLIENT_TYPE_VC1_DEC 5U #define DWL_CLIENT_TYPE_MPEG2_DEC 6U #define DWL_CLIENT_TYPE_VP6_DEC 7U #define DWL_CLIENT_TYPE_AVS_DEC 8U #define DWL_CLIENT_TYPE_RV_DEC 9U #define DWL_CLIENT_TYPE_VP8_DEC 10U #define DWL_CLIENT_TYPE_VP9_DEC 11U #define DWL_CLIENT_TYPE_HEVC_DEC 12U static int hantro_device_id(struct device *dev) { int id; if (strcmp("vpu_g1", dev->of_node->name) == 0) { id = 0; } else if (strcmp("vpu_g2", dev->of_node->name) == 0) { id = 1; } else { return id = -1; } return id; } static int hantro_clk_enable(hantrodec_clk *clk) { clk_prepare(clk->dec); clk_enable(clk->dec); clk_prepare(clk->bus); clk_enable(clk->bus); return 0; } static int hantro_clk_disable(hantrodec_clk *clk) { clk_disable(clk->dec); clk_unprepare(clk->dec); clk_disable(clk->bus); clk_unprepare(clk->bus); return 0; } static int hantro_ctrlblk_reset(hantrodec_t *dev) { volatile u8 *iobase; u32 val; //config G1/G2 hantro_clk_enable(&dev->clk); iobase = (volatile u8 *)ioremap_nocache(BLK_CTL_BASE, 0x10000); if (dev->core_id == 0) { val = ioread32(iobase); val &= (~0x2); iowrite32(val, iobase); //assert G1 block soft reset control udelay(2); val = ioread32(iobase); val |= 0x2; iowrite32(val, iobase); //desert G1 block soft reset control val = ioread32(iobase+4); val |= 0x2; iowrite32(val, iobase+4); //VPUMIX G1 block clock enable control iowrite32(0xFFFFFFFF, iobase + 0x8); // all G1 fuse dec enable iowrite32(0xFFFFFFFF, iobase + 0xC); // all G1 fuse pp enable } else { val = ioread32(iobase); val &= (~0x1); iowrite32(val, iobase); //assert G2 block soft reset control udelay(2); val = ioread32(iobase); val |= 0x1; iowrite32(val, iobase); //desert G2 block soft reset control val = ioread32(iobase+4); val |= 0x1; iowrite32(val, iobase+4); //VPUMIX G2 block clock enable control iowrite32(0xFFFFFFFF, iobase + 0x10); // all G2 fuse dec enable } iounmap(iobase); hantro_clk_disable(&dev->clk); return 0; } static int hantro_power_on_disirq(hantrodec_t *hantrodev) { //spin_lock_irq(&owner_lock); mutex_lock(&hantrodev->dev_mutex); disable_irq(hantrodev->irq); pm_runtime_get_sync(hantrodev->dev); enable_irq(hantrodev->irq); mutex_unlock(&hantrodev->dev_mutex); //spin_unlock_irq(&owner_lock); return 0; } static int hantro_new_instance(void) { int idx; unsigned long flags; spin_lock_irqsave(&owner_lock, flags); if (instance_mask == ((1UL << MAX_HANTRODEC_INSTANCE) - 1)) { spin_unlock_irqrestore(&owner_lock, flags); return -1; } idx = ffz(instance_mask); set_bit(idx, &instance_mask); spin_unlock_irqrestore(&owner_lock, flags); return idx; } static int hantro_free_instance(int idx) { unsigned long flags; spin_lock_irqsave(&owner_lock, flags); clear_bit(idx, &instance_mask); spin_unlock_irqrestore(&owner_lock, flags); return 0; } #ifdef CONFIG_DEVICE_THERMAL_HANTRO static int hantro_thermal_check(struct device *dev) { hantrodec_t *hantrodev = dev_get_drvdata(dev); unsigned long flags; spin_lock_irqsave(&thermal_lock, flags); if (thermal_event == hantrodev->thermal_cur) { /*nothing to do and return directly*/ spin_unlock_irqrestore(&thermal_lock, flags); return 0; } hantrodev->thermal_cur = thermal_event; spin_unlock_irqrestore(&thermal_lock, flags); if (hantrodev->thermal_cur) { int ratio = hantro_clock_ratio; pr_debug("hantro[%d]: too hot, need to decrease clock, ratio: 1/%d\n", hantrodev->core_id, ratio); /*clock disable/enable are not required for vpu clock rate operation*/ clk_set_rate(hantrodev->clk.dec, HANTRO_DEC_DEF_CLK/ratio); clk_set_rate(hantrodev->clk.bus, HANTRO_BUS_DEF_CLK/ratio); } else { pr_debug("hantro[%d]: not hot again, will restore default clock\n", hantrodev->core_id); clk_set_rate(hantrodev->clk.dec, HANTRO_DEC_DEF_CLK); clk_set_rate(hantrodev->clk.bus, HANTRO_BUS_DEF_CLK); } pr_info("hantro[%d]: event(%d), dec, bus clock: %ld, %ld\n", hantrodev->core_id, hantrodev->thermal_cur, clk_get_rate(hantrodev->clk.dec), clk_get_rate(hantrodev->clk.bus)); return 0; } static int hantro_thermal_hot_notify(struct notifier_block *nb, unsigned long event, void *dummy) { unsigned long flags; spin_lock_irqsave(&thermal_lock, flags); thermal_event = event; /*event: 1: hot, 0: cool*/ spin_unlock_irqrestore(&thermal_lock, flags); pr_info("hantro receive hot notification event: %ld\n", event); return NOTIFY_OK; } static struct notifier_block hantro_thermal_hot_notifier = { .notifier_call = hantro_thermal_hot_notify, }; #endif //CONFIG_DEVICE_THERMAL_HANTRO static void ReadCoreConfig(hantrodec_t *dev) { int c = dev->core_id; u32 reg, tmp, mask; memset(&dev->cfg, 0, sizeof(dev->cfg)); //for (c = 0; c < dev->cores; c++) { /* Decoder configuration */ if (IS_G1(dev->hw_id)) { reg = ioread32(dev->hwregs + HANTRODEC_SYNTH_CFG * 4); tmp = (reg >> DWL_H264_E) & 0x3U; if (tmp) pr_debug("hantrodec: Core[%d] has H264\n", c); dev->cfg |= tmp ? 1 << DWL_CLIENT_TYPE_H264_DEC : 0; tmp = (reg >> DWL_JPEG_E) & 0x01U; if (tmp) pr_debug("hantrodec: Core[%d] has JPEG\n", c); dev->cfg |= tmp ? 1 << DWL_CLIENT_TYPE_JPEG_DEC : 0; tmp = (reg >> DWL_MPEG4_E) & 0x3U; if (tmp) pr_debug("hantrodec: Core[%d] has MPEG4\n", c); dev->cfg |= tmp ? 1 << DWL_CLIENT_TYPE_MPEG4_DEC : 0; tmp = (reg >> DWL_VC1_E) & 0x3U; if (tmp) pr_debug("hantrodec: Core[%d] has VC1\n", c); dev->cfg |= tmp ? 1 << DWL_CLIENT_TYPE_VC1_DEC : 0; tmp = (reg >> DWL_MPEG2_E) & 0x01U; if (tmp) pr_debug("hantrodec: Core[%d] has MPEG2\n", c); dev->cfg |= tmp ? 1 << DWL_CLIENT_TYPE_MPEG2_DEC : 0; tmp = (reg >> DWL_VP6_E) & 0x01U; if (tmp) pr_debug("hantrodec: Core[%d] has VP6\n", c); dev->cfg |= tmp ? 1 << DWL_CLIENT_TYPE_VP6_DEC : 0; reg = ioread32(dev->hwregs + HANTRODEC_SYNTH_CFG_2 * 4); /* VP7 and WEBP is part of VP8 */ mask = (1 << DWL_VP8_E) | (1 << DWL_VP7_E) | (1 << DWL_WEBP_E); tmp = (reg & mask); if (tmp & (1 << DWL_VP8_E)) pr_debug("hantrodec: Core[%d] has VP8\n", c); if (tmp & (1 << DWL_VP7_E)) pr_debug("hantrodec: Core[%d] has VP7\n", c); if (tmp & (1 << DWL_WEBP_E)) pr_debug("hantrodec: Core[%d] has WebP\n", c); dev->cfg |= tmp ? 1 << DWL_CLIENT_TYPE_VP8_DEC : 0; tmp = (reg >> DWL_AVS_E) & 0x01U; if (tmp) pr_debug("hantrodec: Core[%d] has AVS\n", c); dev->cfg |= tmp ? 1 << DWL_CLIENT_TYPE_AVS_DEC : 0; tmp = (reg >> DWL_RV_E) & 0x03U; if (tmp) pr_debug("hantrodec: Core[%d] has RV\n", c); dev->cfg |= tmp ? 1 << DWL_CLIENT_TYPE_RV_DEC : 0; /* Post-processor configuration */ //reg = ioread32(dev->hwregs + HANTROPP_SYNTH_CFG * 4); } else { reg = ioread32(dev->hwregs + HANTRODEC_SYNTH_CFG_2 * 4); tmp = (reg >> DWL_HEVC_E) & 0x3U; if (tmp) pr_debug("hantrodec: Core[%d] has HEVC\n", c); dev->cfg |= tmp ? 1 << DWL_CLIENT_TYPE_HEVC_DEC : 0; tmp = (reg >> DWL_VP9_E) & 0x03U; if (tmp) pr_debug("hantrodec: Core[%d] has VP9\n", c); dev->cfg |= tmp ? 1 << DWL_CLIENT_TYPE_VP9_DEC : 0; } /* Post-processor configuration */ reg = ioread32(dev->hwregs + HANTRODECPP_SYNTH_CFG * 4); tmp = (reg >> DWL_PP_E) & 0x01U; if (tmp) pr_debug("hantrodec: Core[%d] has PP\n", c); dev->cfg |= tmp ? 1 << DWL_CLIENT_TYPE_PP : 0; //} } static int CoreHasFormat(const u32 cfg, u32 format) { return (cfg & (1 << format)) ? 1 : 0; } static int GetDecCore(hantrodec_t *dev, struct file *filp) { int success = 0; unsigned long flags; spin_lock_irqsave(&owner_lock, flags); if (dev->dec_owner == NULL) { dev->dec_owner = filp; success = 1; } spin_unlock_irqrestore(&owner_lock, flags); return success; } static int GetDecCoreAny(long *Core, hantrodec_t *dev, struct file *filp, unsigned long format) { int success = 0; //long c; *Core = -1; //for (c = 0; c < dev->cores; c++) { /* a free Core that has format */ if (CoreHasFormat(dev->cfg, format) && GetDecCore(dev, filp)) { success = 1; *Core = dev->core_id; //break; } //} return success; } static int GetDecCoreID(unsigned long format) { long c; int core_id = -1; for (c = 0; c < cores; c++) { /* a Core that has format */ if (CoreHasFormat(hantrodec_data[c].cfg, format)) { core_id = c; break; } } PDEBUG("GetDecCoreID=%d\n", core_id); return core_id; } #if 0 static int hantrodec_choose_core(int is_g1) { volatile unsigned char *reg = NULL; unsigned int blk_base = BLK_CTL_BASE; PDEBUG("hantrodec_choose_core\n"); if (!request_mem_region(blk_base, 0x1000, "blk_ctl")) { pr_err("blk_ctl: failed to reserve HW regs\n"); return -EBUSY; } reg = (volatile u8 *) ioremap_nocache(blk_base, 0x1000); if (reg == NULL) { pr_err("blk_ctl: failed to ioremap HW regs\n"); if (reg) iounmap((void *)reg); release_mem_region(blk_base, 0x1000); return -EBUSY; } // G1 use, set to 1; G2 use, set to 0, choose the one you are using if (is_g1) iowrite32(0x1, reg + 0x14); // VPUMIX only use G1 else iowrite32(0x0, reg + 0x14); // VPUMIX only use G2 if (reg) iounmap((void *)reg); release_mem_region(blk_base, 0x1000); PDEBUG("hantrodec_choose_core OK!\n"); return 0; } #endif static long ReserveDecoder(hantrodec_t *dev, struct file *filp, unsigned long format) { long Core = -1; /* reserve a Core */ if (down_interruptible(&dev->dec_core_sem)) return -ERESTARTSYS; #if 1 if (GetDecCoreAny(&Core, dev, filp, format) == 0) { pr_err("Core %d is already been reserved !\n", dev->core_id); return -1; } #else /* lock a Core that has specific format*/ if (wait_event_interruptible(hw_queue, GetDecCoreAny(&Core, dev, filp, format) != 0)) return -ERESTARTSYS; #endif #if 0 if (IS_G1(dev->hw_id)) { if (0 == hantrodec_choose_core(1)) PDEBUG("G1 is reserved\n"); else return -1; } else { if (0 == hantrodec_choose_core(0)) PDEBUG("G2 is reserved\n"); else return -1; } #endif #ifdef CONFIG_DEVICE_THERMAL_HANTRO if (hantro_dynamic_clock) hantro_thermal_check(dev->dev); #endif return Core; } static void ReleaseDecoder(hantrodec_t *dev) { u32 status; unsigned long flags; status = ioread32(dev->hwregs + HANTRODEC_IRQ_STAT_DEC_OFF); /* make sure HW is disabled */ if (status & HANTRODEC_DEC_E) { pr_info("hantrodec: DEC[%d] still enabled -> reset\n", dev->core_id); /* abort decoder */ status |= HANTRODEC_DEC_ABORT | HANTRODEC_DEC_IRQ_DISABLE; iowrite32(status, dev->hwregs + HANTRODEC_IRQ_STAT_DEC_OFF); } spin_lock_irqsave(&owner_lock, flags); dev->dec_owner = NULL; spin_unlock_irqrestore(&owner_lock, flags); up(&dev->dec_core_sem); //wake_up_interruptible_all(&hw_queue); } static long ReservePostProcessor(hantrodec_t *dev, struct file *filp) { unsigned long flags; long Core = 0; /* single Core PP only */ if (down_interruptible(&dev->pp_core_sem)) return -ERESTARTSYS; spin_lock_irqsave(&owner_lock, flags); dev->pp_owner = filp; spin_unlock_irqrestore(&owner_lock, flags); return Core; } static void ReleasePostProcessor(hantrodec_t *dev) { unsigned long flags; u32 status = ioread32(dev->hwregs + HANTRO_IRQ_STAT_PP_OFF); /* make sure HW is disabled */ if (status & HANTRO_PP_E) { pr_info("hantrodec: PP[%d] still enabled -> reset\n", dev->core_id); /* disable IRQ */ status |= HANTRO_PP_IRQ_DISABLE; /* disable postprocessor */ status &= (~HANTRO_PP_E); iowrite32(0x10, dev->hwregs + HANTRO_IRQ_STAT_PP_OFF); } spin_lock_irqsave(&owner_lock, flags); dev->pp_owner = NULL; spin_unlock_irqrestore(&owner_lock, flags); up(&dev->pp_core_sem); } #if 0 static long ReserveDecPp(hantrodec_t *dev, struct file *filp, unsigned long format) { /* reserve Core 0, DEC+PP for pipeline */ unsigned long flags; long Core = 0; /* check that Core has the requested dec format */ if (!CoreHasFormat(dev->cfg, format)) return -EFAULT; /* check that Core has PP */ if (!CoreHasFormat(dev->cfg, DWL_CLIENT_TYPE_PP)) return -EFAULT; /* reserve a Core */ if (down_interruptible(&dev->dec_core_sem)) return -ERESTARTSYS; /* wait until the Core is available */ if (wait_event_interruptible(hw_queue, GetDecCore(dev, filp) != 0)) { up(&dev->dec_core_sem); return -ERESTARTSYS; } if (down_interruptible(&dev->pp_core_sem)) { ReleaseDecoder(dev); return -ERESTARTSYS; } spin_lock_irqsave(&owner_lock, flags); dev->pp_owner = filp; spin_unlock_irqrestore(&owner_lock, flags); return Core; } #endif static long DecFlushRegs(hantrodec_t *dev, struct core_desc *Core) { long ret = 0, i; //u32 id = Core->id; if (IS_G1(dev->hw_id)) { /* copy original dec regs to kernal space*/ ret = copy_from_user(dev->dec_regs, Core->regs, HANTRO_DEC_ORG_REGS*4); if (ret) { pr_err("copy_from_user failed, returned %li\n", ret); return -EFAULT; } #ifdef USE_64BIT_ENV /* copy extended dec regs to kernal space*/ ret = copy_from_user(dev->dec_regs + HANTRO_DEC_EXT_FIRST_REG, Core->regs + HANTRO_DEC_EXT_FIRST_REG, HANTRO_DEC_EXT_REGS * 4); #endif if (ret) { pr_err("copy_from_user failed, returned %li\n", ret); return -EFAULT; } /* write dec regs but the status reg[1] to hardware */ /* both original and extended regs need to be written */ for (i = 2; i <= HANTRO_DEC_ORG_LAST_REG; i++) iowrite32(dev->dec_regs[i], dev->hwregs + i*4); #ifdef USE_64BIT_ENV for (i = HANTRO_DEC_EXT_FIRST_REG; i <= HANTRO_DEC_EXT_LAST_REG; i++) iowrite32(dev->dec_regs[i], dev->hwregs + i*4); #endif } else { ret = copy_from_user(dev->dec_regs, Core->regs, HANTRO_G2_DEC_REGS*4); if (ret) { pr_err("copy_from_user failed, returned %li\n", ret); return -EFAULT; } /* write all regs but the status reg[1] to hardware */ for (i = 2; i <= HANTRO_G2_DEC_LAST_REG; i++) iowrite32(dev->dec_regs[i], dev->hwregs + i*4); } if (dev->dec_regs[1] & 0x1) dev->hw_active = 1; /* write the status register, which may start the decoder */ iowrite32(dev->dec_regs[1], dev->hwregs + 4); PDEBUG("flushed registers on Core %d\n", dev->core_id); return 0; } static long DecRefreshRegs(hantrodec_t *dev, struct core_desc *Core) { long ret, i; //u32 id = Core->id; if (IS_G1(dev->hw_id)) { /* user has to know exactly what they are asking for */ //if(Core->size != (HANTRO_DEC_ORG_REGS * 4)) // return -EFAULT; /* read all registers from hardware */ /* both original and extended regs need to be read */ for (i = 0; i <= HANTRO_DEC_ORG_LAST_REG; i++) dev->dec_regs[i] = ioread32(dev->hwregs + i*4); #ifdef USE_64BIT_ENV for (i = HANTRO_DEC_EXT_FIRST_REG; i <= HANTRO_DEC_EXT_LAST_REG; i++) dev->dec_regs[i] = ioread32(dev->hwregs + i*4); #endif if (dev->timeout) { /* Enable TIMEOUT bits in Reg[1] */ dev->dec_regs[1] = 0x40100; /* Reset HW */ ResetAsic(dev); dev->timeout = 0; } /* put registers to user space*/ /* put original registers to user space*/ ret = copy_to_user(Core->regs, dev->dec_regs, HANTRO_DEC_ORG_REGS*4); #ifdef USE_64BIT_ENV /*put extended registers to user space*/ ret = copy_to_user(Core->regs + HANTRO_DEC_EXT_FIRST_REG, dev->dec_regs + HANTRO_DEC_EXT_FIRST_REG, HANTRO_DEC_EXT_REGS * 4); #endif if (ret) { pr_err("copy_to_user failed, returned %li\n", ret); return -EFAULT; } } else { /* user has to know exactly what they are asking for */ if (Core->size != (HANTRO_G2_DEC_REGS * 4)) return -EFAULT; /* read all registers from hardware */ for (i = 0; i <= HANTRO_G2_DEC_LAST_REG; i++) dev->dec_regs[i] = ioread32(dev->hwregs + i*4); if (dev->timeout) { /* Enable TIMEOUT bits in Reg[1] */ dev->dec_regs[1] = 0x40100; /* Reset HW */ ResetAsic(dev); dev->timeout = 0; } /* put registers to user space*/ ret = copy_to_user(Core->regs, dev->dec_regs, HANTRO_G2_DEC_REGS*4); if (ret) { pr_err("copy_to_user failed, returned %li\n", ret); return -EFAULT; } } return 0; } static long DecRestoreRegs(hantrodec_t *dev) { long i; if (IS_G1(dev->hw_id)) { /* write dec regs to hardware */ /* both original and extended regs need to be written */ for (i = 1; i <= HANTRO_DEC_ORG_LAST_REG; i++) iowrite32(dev->dec_regs[i], dev->hwregs + i*4); #ifdef USE_64BIT_ENV for (i = HANTRO_DEC_EXT_FIRST_REG; i <= HANTRO_DEC_EXT_LAST_REG; i++) iowrite32(dev->dec_regs[i], dev->hwregs + i*4); #endif } else { /* write all regs to hardware */ for (i = 1; i <= HANTRO_G2_DEC_LAST_REG; i++) iowrite32(dev->dec_regs[i], dev->hwregs + i*4); } return 0; } static long DecStoreRegs(hantrodec_t *dev) { long i; if (IS_G1(dev->hw_id)) { /* read all registers from hardware */ /* both original and extended regs need to be read */ for (i = 0; i <= HANTRO_DEC_ORG_LAST_REG; i++) dev->dec_regs[i] = ioread32(dev->hwregs + i*4); #ifdef USE_64BIT_ENV for (i = HANTRO_DEC_EXT_FIRST_REG; i <= HANTRO_DEC_EXT_LAST_REG; i++) dev->dec_regs[i] = ioread32(dev->hwregs + i*4); #endif } else { /* read all registers from hardware */ for (i = 0; i <= HANTRO_G2_DEC_LAST_REG; i++) dev->dec_regs[i] = ioread32(dev->hwregs + i*4); } return 0; } static int CheckDecIrq(hantrodec_t *dev) { unsigned long flags; int rdy = 0; const u32 irq_mask = (1 << dev->core_id); spin_lock_irqsave(&owner_lock, flags); if (dec_irq & irq_mask) { /* reset the wait condition(s) */ dec_irq &= ~irq_mask; rdy = 1; } spin_unlock_irqrestore(&owner_lock, flags); return rdy; } static long WaitDecReadyAndRefreshRegs(hantrodec_t *dev, struct core_desc *Core) { //u32 id = Core->id; long ret; PDEBUG("wait_event_interruptible DEC[%d]\n", dev->core_id); //ret = wait_event_interruptible_timeout(dec_wait_queue, CheckDecIrq(dev), msecs_to_jiffies(200)); ret = wait_event_timeout(dec_wait_queue, CheckDecIrq(dev), msecs_to_jiffies(200)); if (ret == -ERESTARTSYS) { pr_err("DEC[%d] failed to wait_event_interruptible interrupted\n", dev->core_id); return -ERESTARTSYS; } else if (ret == 0) { pr_err("DEC[%d] wait_event_interruptible timeout\n", dev->core_id); dev->timeout = 1; } atomic_inc(&dev->irq_tx); /* refresh registers */ return DecRefreshRegs(dev, Core); } static long PPFlushRegs(hantrodec_t *dev, struct core_desc *Core) { long ret = 0; //u32 id = Core->id; u32 i; /* copy original dec regs to kernal space*/ ret = copy_from_user(dev->dec_regs + HANTRO_PP_ORG_FIRST_REG, Core->regs + HANTRO_PP_ORG_FIRST_REG, HANTRO_PP_ORG_REGS*4); #ifdef USE_64BIT_ENV /* copy extended dec regs to kernal space*/ ret = copy_from_user(dev->dec_regs + HANTRO_PP_EXT_FIRST_REG, Core->regs + HANTRO_PP_EXT_FIRST_REG, HANTRO_PP_EXT_REGS*4); #endif if (ret) { pr_err("copy_from_user failed, returned %li\n", ret); return -EFAULT; } /* write all regs but the status reg[1] to hardware */ /* both original and extended regs need to be written */ for (i = HANTRO_PP_ORG_FIRST_REG + 1; i <= HANTRO_PP_ORG_LAST_REG; i++) iowrite32(dev->dec_regs[i], dev->hwregs + i*4); #ifdef USE_64BIT_ENV for (i = HANTRO_PP_EXT_FIRST_REG; i <= HANTRO_PP_EXT_LAST_REG; i++) iowrite32(dev->dec_regs[i], dev->hwregs + i*4); #endif /* write the stat reg, which may start the PP */ iowrite32(dev->dec_regs[HANTRO_PP_ORG_FIRST_REG], dev->hwregs + HANTRO_PP_ORG_FIRST_REG * 4); return 0; } static long PPRefreshRegs(hantrodec_t *dev, struct core_desc *Core) { long i, ret; //u32 id = Core->id; #ifdef USE_64BIT_ENV /* user has to know exactly what they are asking for */ if (Core->size != (HANTRO_PP_TOTAL_REGS * 4)) return -EFAULT; #else /* user has to know exactly what they are asking for */ if (Core->size != (HANTRO_PP_ORG_REGS * 4)) return -EFAULT; #endif /* read all registers from hardware */ /* both original and extended regs need to be read */ for (i = HANTRO_PP_ORG_FIRST_REG; i <= HANTRO_PP_ORG_LAST_REG; i++) dev->dec_regs[i] = ioread32(dev->hwregs + i*4); #ifdef USE_64BIT_ENV for (i = HANTRO_PP_EXT_FIRST_REG; i <= HANTRO_PP_EXT_LAST_REG; i++) dev->dec_regs[i] = ioread32(dev->hwregs + i*4); #endif /* put registers to user space*/ /* put original registers to user space*/ ret = copy_to_user(Core->regs + HANTRO_PP_ORG_FIRST_REG, dev->dec_regs + HANTRO_PP_ORG_FIRST_REG, HANTRO_PP_ORG_REGS*4); #ifdef USE_64BIT_ENV /* put extended registers to user space*/ ret = copy_to_user(Core->regs + HANTRO_PP_EXT_FIRST_REG, dev->dec_regs + HANTRO_PP_EXT_FIRST_REG, HANTRO_PP_EXT_REGS * 4); #endif if (ret) { pr_err("copy_to_user failed, returned %li\n", ret); return -EFAULT; } return 0; } static int CheckPPIrq(hantrodec_t *dev) { unsigned long flags; int rdy = 0; const u32 irq_mask = (1 << dev->core_id); spin_lock_irqsave(&owner_lock, flags); if (pp_irq & irq_mask) { /* reset the wait condition(s) */ pp_irq &= ~irq_mask; rdy = 1; } spin_unlock_irqrestore(&owner_lock, flags); return rdy; } static long WaitPPReadyAndRefreshRegs(hantrodec_t *dev, struct core_desc *Core) { //u32 id = Core->id; PDEBUG("wait_event_interruptible PP[%d]\n", dev->core_id); if (wait_event_interruptible(pp_wait_queue, CheckPPIrq(dev))) { pr_err("PP[%d] failed to wait_event_interruptible interrupted\n", dev->core_id); return -ERESTARTSYS; } atomic_inc(&dev->irq_tx); /* refresh registers */ return PPRefreshRegs(dev, Core); } static int CheckCoreIrq(const struct file *filp, int *id) { unsigned long flags; int rdy = 0, n = 0; hantrodec_t *dev; do { u32 irq_mask; dev = &hantrodec_data[n]; irq_mask = (1 << dev->core_id); spin_lock_irqsave(&owner_lock, flags); if (dec_irq & irq_mask) { if (dev->dec_owner == filp) { /* we have an IRQ for our client */ /* reset the wait condition(s) */ dec_irq &= ~irq_mask; /* signal ready Core no. for our client */ *id = dev->core_id; rdy = 1; spin_unlock_irqrestore(&owner_lock, flags); break; } else if (dev->dec_owner == NULL) { /* zombie IRQ */ pr_info("IRQ on Core[%d], but no owner!!!\n", n); /* reset the wait condition(s) */ dec_irq &= ~irq_mask; } } spin_unlock_irqrestore(&owner_lock, flags); n++; /* next Core */ } while (n < cores); return rdy; } static long WaitCoreReady(const struct file *filp, int *id) { PDEBUG("wait_event_interruptible CORE\n"); if (wait_event_interruptible(dec_wait_queue, CheckCoreIrq(filp, id))) { pr_err("CORE failed to wait_event_interruptible interrupted\n"); return -ERESTARTSYS; } atomic_inc(&hantrodec_data[*id].irq_tx); return 0; } /*------------------------------------------------------------------------- *Function name : hantrodec_ioctl *Description : communication method to/from the user space * *Return type : long *------------------------------------------------------------------------- */ static long hantrodec_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int err = 0; long tmp; PDEBUG("ioctl cmd 0x%08x\n", cmd); /* * extract the type and number bitfields, and don't decode * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() */ if (_IOC_TYPE(cmd) != HANTRODEC_IOC_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > HANTRODEC_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((void *) arg, _IOC_SIZE(cmd)); else if (_IOC_DIR(cmd) & _IOC_WRITE) err = !access_ok((void *) arg, _IOC_SIZE(cmd)); if (err) return -EFAULT; switch (_IOC_NR(cmd)) { case _IOC_NR(HANTRODEC_IOC_CLI): { __u32 id; __get_user(id, (__u32 *)arg); if (id >= cores) return -EFAULT; disable_irq(hantrodec_data[id].irq); break; } case _IOC_NR(HANTRODEC_IOC_STI): { __u32 id; __get_user(id, (__u32 *)arg); if (id >= cores) return -EFAULT; enable_irq(hantrodec_data[id].irq); break; } case _IOC_NR(HANTRODEC_IOCGHWOFFSET): { __u32 id; __get_user(id, (__u32 *)arg); if (id >= cores) return -EFAULT; __put_user(multicorebase[id], (unsigned long *) arg); break; } case _IOC_NR(HANTRODEC_IOCGHWIOSIZE): { __u32 id; __u32 io_size; __get_user(id, (__u32 *)arg); if (id >= cores) return -EFAULT; io_size = hantrodec_data[id].iosize; __put_user(io_size, (u32 *) arg); return 0; } case _IOC_NR(HANTRODEC_IOC_MC_OFFSETS): { tmp = copy_to_user((u64 *) arg, multicorebase, sizeof(multicorebase)); if (err) { pr_err("copy_to_user failed, returned %li\n", tmp); return -EFAULT; } break; } case _IOC_NR(HANTRODEC_IOC_MC_CORES): __put_user(cores, (unsigned int *) arg); PDEBUG("hantrodec_data.cores=%d\n", cores); break; case _IOC_NR(HANTRODEC_IOCS_DEC_PUSH_REG): { struct core_desc Core; /* get registers from user space*/ tmp = copy_from_user(&Core, (void *)arg, sizeof(struct core_desc)); if (tmp) { pr_err("copy_from_user failed, returned %li\n", tmp); return -EFAULT; } if (Core.id >= cores) return -EFAULT; DecFlushRegs(&hantrodec_data[Core.id], &Core); break; } case _IOC_NR(HANTRODEC_IOCS_PP_PUSH_REG): { struct core_desc Core; /* get registers from user space*/ tmp = copy_from_user(&Core, (void *)arg, sizeof(struct core_desc)); if (tmp) { pr_err("copy_from_user failed, returned %li\n", tmp); return -EFAULT; } if (Core.id >= cores) return -EFAULT; PPFlushRegs(&hantrodec_data[Core.id], &Core); break; } case _IOC_NR(HANTRODEC_IOCS_DEC_PULL_REG): { struct core_desc Core; /* get registers from user space*/ tmp = copy_from_user(&Core, (void *)arg, sizeof(struct core_desc)); if (tmp) { pr_err("copy_from_user failed, returned %li\n", tmp); return -EFAULT; } if (Core.id >= cores) return -EFAULT; return DecRefreshRegs(&hantrodec_data[Core.id], &Core); } case _IOC_NR(HANTRODEC_IOCS_PP_PULL_REG): { struct core_desc Core; /* get registers from user space*/ tmp = copy_from_user(&Core, (void *)arg, sizeof(struct core_desc)); if (tmp) { pr_err("copy_from_user failed, returned %li\n", tmp); return -EFAULT; } if (Core.id >= cores) return -EFAULT; return PPRefreshRegs(&hantrodec_data[Core.id], &Core); } case _IOC_NR(HANTRODEC_IOCH_DEC_RESERVE): { int id; PDEBUG("Reserve DEC Core, format = %li\n", arg); id = GetDecCoreID(arg); if (id < 0) { pr_err("invalid format: %ld\n", arg); return -EFAULT; } return ReserveDecoder(&hantrodec_data[id], filp, arg); } case _IOC_NR(HANTRODEC_IOCT_DEC_RELEASE): { if (arg >= cores || hantrodec_data[arg].dec_owner != filp) { pr_err("bogus DEC release, Core = %li\n", arg); return -EFAULT; } PDEBUG("Release DEC, Core = %li\n", arg); ReleaseDecoder(&hantrodec_data[arg]); break; } case _IOC_NR(HANTRODEC_IOCQ_PP_RESERVE): return ReservePostProcessor(&hantrodec_data[0], filp); case _IOC_NR(HANTRODEC_IOCT_PP_RELEASE): { if (arg != 0 || hantrodec_data[arg].pp_owner != filp) { pr_err("bogus PP release %li\n", arg); return -EFAULT; } ReleasePostProcessor(&hantrodec_data[arg]); break; } case _IOC_NR(HANTRODEC_IOCX_DEC_WAIT): { struct core_desc Core; /* get registers from user space */ tmp = copy_from_user(&Core, (void *)arg, sizeof(struct core_desc)); if (tmp) { pr_err("copy_from_user failed, returned %li\n", tmp); return -EFAULT; } if (Core.id >= cores) return -EFAULT; return WaitDecReadyAndRefreshRegs(&hantrodec_data[Core.id], &Core); } case _IOC_NR(HANTRODEC_IOCX_PP_WAIT): { struct core_desc Core; /* get registers from user space */ tmp = copy_from_user(&Core, (void *)arg, sizeof(struct core_desc)); if (tmp) { pr_err("copy_from_user failed, returned %li\n", tmp); return -EFAULT; } if (Core.id >= cores) return -EFAULT; return WaitPPReadyAndRefreshRegs(&hantrodec_data[Core.id], &Core); } case _IOC_NR(HANTRODEC_IOCG_CORE_WAIT): { int id; tmp = WaitCoreReady(filp, &id); __put_user(id, (int *) arg); return tmp; } case _IOC_NR(HANTRODEC_IOX_ASIC_ID): { u32 id; __get_user(id, (u32 *)arg); if (id >= cores) return -EFAULT; id = ioread32(hantrodec_data[id].hwregs); __put_user(id, (u32 *) arg); return 0; } case _IOC_NR(HANTRODEC_IOCG_CORE_ID): { int id; hantrodec_instance *ctx = (hantrodec_instance *)filp->private_data; PDEBUG("Get DEC Core_id, format = %li\n", arg); id = GetDecCoreID(arg); if ((ctx->core_id == 3) && (id >= 0)) { if (id == 0) { ctx->core_id = 1; //g1 /*power off g2*/ pm_runtime_put_sync(hantrodec_data[1].dev); hantro_clk_disable(&hantrodec_data[1].clk); } else if (id == 1) { ctx->core_id = 2; //g2 /*power off g1*/ pm_runtime_put_sync(hantrodec_data[0].dev); hantro_clk_disable(&hantrodec_data[0].clk); } } return id; } case _IOC_NR(HANTRODEC_DEBUG_STATUS): { PDEBUG("hantrodec: dec_irq = 0x%08x\n", dec_irq); PDEBUG("hantrodec: pp_irq = 0x%08x\n", pp_irq); //PDEBUG("hantrodec: IRQs received/sent2user = %d / %d\n", //atomic_read(&irq_rx), atomic_read(&irq_tx)); for (tmp = 0; tmp < cores; tmp++) { PDEBUG("hantrodec: Core %ld IRQs received/sent2user = %d / %d\n", tmp, atomic_read(&hantrodec_data[tmp].irq_rx), atomic_read(&hantrodec_data[tmp].irq_tx)); PDEBUG("hantrodec: dec_core[%li] %s\n", tmp, hantrodec_data[tmp].dec_owner == NULL ? "FREE" : "RESERVED"); PDEBUG("hantrodec: pp_core[%li] %s\n", tmp, hantrodec_data[tmp].pp_owner == NULL ? "FREE" : "RESERVED"); } break; } default: return -ENOTTY; } return 0; } #ifdef CONFIG_COMPAT struct core_desc_32 { __u32 id; /* id of the Core */ compat_caddr_t regs; /* pointer to user registers */ __u32 size; /* size of register space */ }; static int get_hantro_core_desc32(struct core_desc *kp, struct core_desc_32 __user *up) { u32 tmp; if (!access_ok(up, sizeof(struct core_desc_32)) || get_user(kp->id, &up->id) || get_user(kp->size, &up->size) || get_user(tmp, &up->regs)) { return -EFAULT; } kp->regs = (__force u32 *)compat_ptr(tmp); return 0; } static int put_hantro_core_desc32(struct core_desc *kp, struct core_desc_32 __user *up) { u32 tmp = (u32)((unsigned long)kp->regs); if (!access_ok(up, sizeof(struct core_desc_32)) || put_user(kp->id, &up->id) || put_user(kp->size, &up->size) || put_user(tmp, &up->regs)) { return -EFAULT; } return 0; } static long hantrodec_ioctl32(struct file *filp, unsigned int cmd, unsigned long arg) { #define HANTRO_IOCTL32(err, filp, cmd, arg) { \ mm_segment_t old_fs = get_fs(); \ set_fs(KERNEL_DS); \ err = hantrodec_ioctl(filp, cmd, arg); \ if (err) \ return err; \ set_fs(old_fs); \ } union { struct core_desc kcore; unsigned long kux; unsigned int kui; } karg; void __user *up = compat_ptr(arg); long err = 0; switch (_IOC_NR(cmd)) { case _IOC_NR(HANTRODEC_IOCGHWOFFSET): case _IOC_NR(HANTRODEC_IOC_MC_OFFSETS): err = get_user(karg.kux, (s32 __user *)up); if (err) return err; HANTRO_IOCTL32(err, filp, cmd, (unsigned long)&karg); err = put_user(((s32)karg.kux), (s32 __user *)up); break; case _IOC_NR(HANTRODEC_IOCGHWIOSIZE): case _IOC_NR(HANTRODEC_IOC_MC_CORES): case _IOC_NR(HANTRODEC_IOCG_CORE_WAIT): case _IOC_NR(HANTRODEC_IOX_ASIC_ID): err = get_user(karg.kui, (s32 __user *)up); if (err) return err; HANTRO_IOCTL32(err, filp, cmd, (unsigned long)&karg); err = put_user(((s32)karg.kui), (s32 __user *)up); break; case _IOC_NR(HANTRODEC_IOCS_DEC_PUSH_REG): case _IOC_NR(HANTRODEC_IOCS_PP_PUSH_REG): case _IOC_NR(HANTRODEC_IOCX_DEC_WAIT): case _IOC_NR(HANTRODEC_IOCX_PP_WAIT): case _IOC_NR(HANTRODEC_IOCS_DEC_PULL_REG): case _IOC_NR(HANTRODEC_IOCS_PP_PULL_REG): err = get_hantro_core_desc32(&karg.kcore, up); if (err) return err; HANTRO_IOCTL32(err, filp, cmd, (unsigned long)&karg); err = put_hantro_core_desc32(&karg.kcore, up); break; default: err = hantrodec_ioctl(filp, cmd, (unsigned long)up); break; } return err; } #endif //ifdef CONFIG_COMPAT /*-------------------------------------------------------------------------- *Function name : hantrodec_open *Description : open method * *Return type : int *--------------------------------------------------------------------------- */ static int hantrodec_open(struct inode *inode, struct file *filp) { int i; int idx; idx = hantro_new_instance(); if (idx < 0) return -ENOMEM; PDEBUG("dev opened: id: %d\n", idx); hantrodec_ctx[idx].core_id = 3; //unknow hantrodec_ctx[idx].inst_id = idx; filp->private_data = (void *)(&hantrodec_ctx[idx]); /*not yet know which core id, so power on both g1 and g2 firstly*/ for (i = 0; i < 2; i++) { hantro_clk_enable(&hantrodec_data[i].clk); hantro_power_on_disirq(&hantrodec_data[i]); } return 0; } /*--------------------------------------------------------------------------- *Function name : hantrodec_release *Description : Release driver * *Return type : int *---------------------------------------------------------------------------- */ static int hantrodec_release(struct inode *inode, struct file *filp) { int n; //hantrodec_t *dev = &hantrodec_data; hantrodec_instance *ctx = (hantrodec_instance *)filp->private_data; PDEBUG("closing ...\n"); for (n = 0; n < cores; n++) { if (hantrodec_data[n].dec_owner == filp) { PDEBUG("releasing dec Core %i lock\n", n); ReleaseDecoder(&hantrodec_data[n]); } } for (n = 0; n < 1; n++) { if (hantrodec_data[n].pp_owner == filp) { PDEBUG("releasing pp Core %i lock\n", n); ReleasePostProcessor(&hantrodec_data[n]); } } if (ctx->core_id & 0x1) { pm_runtime_put_sync(hantrodec_data[0].dev); hantro_clk_disable(&hantrodec_data[0].clk); } if (ctx->core_id & 0x2) { pm_runtime_put_sync(hantrodec_data[1].dev); hantro_clk_disable(&hantrodec_data[1].clk); } hantro_free_instance(ctx->inst_id); PDEBUG("closed: id: %d\n", n); return 0; } /*--------------------------------------------------------------------------- *Function name : hantro_mmap *Description : memory map interface for hantro file operation * *Return type : int *--------------------------------------------------------------------------- */ static int hantro_mmap(struct file *fp, struct vm_area_struct *vm) { if (vm->vm_pgoff == (multicorebase[0] >> PAGE_SHIFT) || vm->vm_pgoff == (multicorebase[1] >> PAGE_SHIFT)) { vm->vm_flags |= VM_IO; vm->vm_page_prot = pgprot_noncached(vm->vm_page_prot); PDEBUG("hantro 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; } } /* VFS methods */ static const struct file_operations hantrodec_fops = { .owner = THIS_MODULE, .open = hantrodec_open, .release = hantrodec_release, .unlocked_ioctl = hantrodec_ioctl, .fasync = NULL, .mmap = hantro_mmap, #ifdef CONFIG_COMPAT .compat_ioctl = hantrodec_ioctl32, #endif }; /*--------------------------------------------------------------------------- *Function name : hantrodec_init *Description : Initialize the driver * *Return type : int *--------------------------------------------------------------------------- */ static int hantrodec_init(struct platform_device *pdev, int id) { int result; int irq; struct device *temp_class; //dec_irq = 0; //pp_irq = 0; pr_debug("hantrodec: Init multi Core[0] at 0x%16lx\n" " Core[1] at 0x%16lx\n", multicorebase[0], multicorebase[1]); //hantrodec_data.cores = 0; //hantrodec_data.iosize[0] = DEC_IO_SIZE_0; //hantrodec_data.iosize[1] = DEC_IO_SIZE_1; //hantrodec_data.async_queue_dec = NULL; //hantrodec_data.async_queue_pp = NULL; hantrodec_data[id].iosize = (id == 0) ? DEC_IO_SIZE_0 : DEC_IO_SIZE_1; if (!hantrodec_major) { dec_irq = 0; pp_irq = 0; result = register_chrdev(hantrodec_major, "hantrodec", &hantrodec_fops); if (result < 0) { pr_err("hantrodec: unable to get major %d\n", hantrodec_major); goto err; } else if (result != 0) { /* this is for dynamic major */ hantrodec_major = result; } hantro_class = class_create(THIS_MODULE, "mxc_hantro_845"); if (IS_ERR(hantro_class)) { result = -1; goto err; } temp_class = device_create(hantro_class, NULL, MKDEV(hantrodec_major, 0), NULL, DEVICE_NAME); if (IS_ERR(temp_class)) { result = -1; goto err_out_class; } } result = ReserveIO(id); if (result < 0) goto err; hantrodec_data[id].dec_owner = 0; hantrodec_data[id].pp_owner = 0; sema_init(&hantrodec_data[id].dec_core_sem, 1); sema_init(&hantrodec_data[id].pp_core_sem, 1); /* read configuration fo all cores */ ReadCoreConfig(&hantrodec_data[id]); /* reset hardware */ ResetAsic(&hantrodec_data[id]); /* register irq for each core*/ irq = platform_get_irq_byname(pdev, "irq_hantro"); if (irq > 0) { hantrodec_data[id].irq = irq; result = request_irq(irq, hantrodec_isr, IRQF_SHARED, "hantrodec", (void *) &hantrodec_data[id]); if (result != 0) { if (result == -EINVAL) pr_err("hantrodec: Bad irq number or handler\n"); else if (result == -EBUSY) { pr_err("hantrodec: IRQ <%d> busy, change your config\n", hantrodec_data[id].irq); } ReleaseIO(id); goto err; } } else { pr_err("hantrodec: IRQ0 not in use!\n"); goto err; } hantrodec_data[id].irq_rx.counter = 0; hantrodec_data[id].irq_tx.counter = 0; irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY); pr_info("hantrodec %d : module inserted. Major = %d\n", id, hantrodec_major); return 0; err_out_class: device_destroy(hantro_class, MKDEV(hantrodec_major, 0)); class_destroy(hantro_class); err: pr_err("hantrodec: module not inserted\n"); unregister_chrdev(hantrodec_major, "hantrodec"); return result; } /*--------------------------------------------------------------------------- *Function name : hantrodec_cleanup *Description : clean up * *Return type : int *--------------------------------------------------------------------------- */ static void hantrodec_cleanup(int id) { hantrodec_t *dev = &hantrodec_data[id]; //int n = 0; /* reset hardware */ ResetAsic(dev); /* free the IRQ */ //for (n = 0; n < dev->cores; n++) { if (dev->irq != -1) free_irq(dev->irq, (void *) dev); //} ReleaseIO(id); //unregister_chrdev(hantrodec_major, "hantrodec"); PDEBUG("hantrodec: module removed\n"); } /*--------------------------------------------------------------------------- *Function name : CheckHwId *Return type : int *--------------------------------------------------------------------------- */ static int CheckHwId(hantrodec_t *dev) { long int hwid; //int i; size_t num_hw = sizeof(DecHwId) / sizeof(*DecHwId); int found = 0; //for (i = 0; i < cores; i++) { if (dev->hwregs != NULL) { hwid = readl(dev->hwregs); pr_debug("hantrodec: Core %d HW ID=0x%16lx\n", dev->core_id, hwid); hwid = (hwid >> 16) & 0xFFFF; /* product version only */ while (num_hw--) { if (hwid == DecHwId[num_hw]) { pr_debug("hantrodec: Supported HW found at 0x%16lx\n", multicorebase[dev->core_id]); found++; dev->hw_id = hwid; break; } } if (!found) { pr_err("hantrodec: Unknown HW found at 0x%16lx\n", multicorebase[dev->core_id]); return 0; } found = 0; num_hw = sizeof(DecHwId) / sizeof(*DecHwId); } //} return 1; } /*--------------------------------------------------------------------------- *Function name : ReserveIO *Description : IO reserve * *Return type : int *--------------------------------------------------------------------------- */ static int ReserveIO(int i) { //int i; //for (i = 0; i < HXDEC_MAX_CORES; i++) { if (multicorebase[i] != -1) { if (!request_mem_region(multicorebase[i], hantrodec_data[i].iosize, "hantrodec0")) { pr_err("hantrodec: failed to reserve HW regs, %d, base: 0x%lX, %d\n", i, multicorebase[i], hantrodec_data[i].iosize); return -EBUSY; } hantrodec_data[i].hwregs = (volatile u8 *) ioremap_nocache(multicorebase[i], hantrodec_data[i].iosize); if (hantrodec_data[i].hwregs == NULL) { pr_err("hantrodec: failed to ioremap HW regs\n"); ReleaseIO(i); return -EBUSY; } //hantrodec_data.cores++; } //} /* check for correct HW */ if (!CheckHwId(&hantrodec_data[i])) { ReleaseIO(i); return -EBUSY; } return 0; } /*--------------------------------------------------------------------------- *Function name : releaseIO *Description : release * *Return type : void *--------------------------------------------------------------------------- */ static void ReleaseIO(int i) { //int i; //for (i = 0; i < hantrodec_data.cores; i++) { if (hantrodec_data[i].hwregs) iounmap((void *) hantrodec_data[i].hwregs); release_mem_region(multicorebase[i], hantrodec_data[i].iosize); //} } /*--------------------------------------------------------------------------- *Function name : hantrodec_isr *Description : interrupt handler * *Return type : irqreturn_t *--------------------------------------------------------------------------- */ static irqreturn_t hantrodec_isr(int irq, void *dev_id) { unsigned long flags; unsigned int handled = 0; //int i; volatile u8 *hwregs; hantrodec_t *dev = (hantrodec_t *) dev_id; u32 irq_status_dec; spin_lock_irqsave(&owner_lock, flags); //for (i = 0; i < cores; i++) { hwregs = dev->hwregs; /* interrupt status register read */ irq_status_dec = ioread32(hwregs + HANTRODEC_IRQ_STAT_DEC_OFF); if (irq_status_dec & HANTRODEC_DEC_IRQ) { /* clear dec IRQ */ irq_status_dec &= (~HANTRODEC_DEC_IRQ); iowrite32(irq_status_dec, hwregs + HANTRODEC_IRQ_STAT_DEC_OFF); PDEBUG("decoder IRQ received! Core %d\n", dev->core_id); dev->hw_active = 0; atomic_inc(&hantrodec_data[dev->core_id].irq_rx); dec_irq |= (1 << dev->core_id); //wake_up_interruptible_all(&dec_wait_queue); wake_up_all(&dec_wait_queue); handled++; } //} spin_unlock_irqrestore(&owner_lock, flags); if (!handled) pr_info("IRQ received, but not hantrodec's!\n"); (void)hwregs; return IRQ_RETVAL(handled); } /*--------------------------------------------------------------------------- *Function name : ResetAsic *Description : reset asic * *Return type : *--------------------------------------------------------------------------- */ static void ResetAsic(hantrodec_t *dev) { int i; u32 status; //for (j = 0; j < dev->cores; j++) { status = ioread32(dev->hwregs + HANTRODEC_IRQ_STAT_DEC_OFF); if (status & HANTRODEC_DEC_E) { /* abort with IRQ disabled */ status = HANTRODEC_DEC_ABORT | HANTRODEC_DEC_IRQ_DISABLE; iowrite32(status, dev->hwregs + HANTRODEC_IRQ_STAT_DEC_OFF); } if (IS_G1(dev->hw_id)) /* reset PP */ iowrite32(0, dev->hwregs + HANTRO_IRQ_STAT_PP_OFF); for (i = 4; i < dev->iosize; i += 4) iowrite32(0, dev->hwregs + i); //} } /*--------------------------------------------------------------------------- *Function name : dump_regs *Description : Dump registers * *Return type : *--------------------------------------------------------------------------- */ #ifdef HANTRODEC_DEBUG static void dump_regs(hantrodec_t *dev) { int i, c; PDEBUG("Reg Dump Start\n"); for (c = 0; c < dev->cores; c++) { for (i = 0; i < dev->iosize[c]; i += 4*4) { PDEBUG("\toffset %04X: %08X %08X %08X %08X\n", i, ioread32(dev->hwregs[c] + i), ioread32(dev->hwregs[c] + i + 4), ioread32(dev->hwregs[c] + i + 8), ioread32(dev->hwregs[c] + i + 12)); } } PDEBUG("Reg Dump End\n"); } #endif static int hantro_dev_probe(struct platform_device *pdev) { int err = 0; struct resource *res; unsigned long reg_base; int id; id = hantro_device_id(&pdev->dev); if (id < 0) return -ENODEV; hantrodec_data[id].dev = &pdev->dev; hantrodec_data[id].core_id = id; platform_set_drvdata(pdev, &hantrodec_data[id]); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs_hantro"); if (!res) { pr_err("hantro: unable to get vpu base addr\n"); return -ENODEV; } reg_base = res->start; if ((ulong)reg_base != multicorebase[id]) { pr_err("hantrodec %d: regbase(0x%lX) not equal to expected value(0x%lX)\n", id, reg_base, multicorebase[id]); return -ENODEV; } hantrodec_data[id].clk.dec = clk_get(&pdev->dev, "clk_hantro"); hantrodec_data[id].clk.bus = clk_get(&pdev->dev, "clk_hantro_bus"); if (IS_ERR(hantrodec_data[id].clk.dec) || IS_ERR(hantrodec_data[id].clk.bus)) { pr_err("hantro: get clock failed\n"); return -ENODEV; } pr_debug("hantro: dec, bus clock: 0x%lX, 0x%lX\n", clk_get_rate(hantrodec_data[id].clk.dec), clk_get_rate(hantrodec_data[id].clk.bus)); hantro_clk_enable(&hantrodec_data[id].clk); pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); hantro_ctrlblk_reset(&hantrodec_data[id]); err = hantrodec_init(pdev, id); if (err != 0) { pr_err("hantro: hantrodec_init failed\n"); goto error; } #ifdef CONFIG_DEVICE_THERMAL_HANTRO HANTRO_REG_THERMAL_NOTIFIER(&hantro_thermal_hot_notifier); thermal_event = 0; hantro_dynamic_clock = 0; hantrodec_data[id].thermal_cur = 0; #endif hantrodec_data[id].timeout = 0; mutex_init(&hantrodec_data[id].dev_mutex); instance_mask = 0; goto out; error: pr_err("hantro probe failed\n"); out: pm_runtime_put_sync(&pdev->dev); hantro_clk_disable(&hantrodec_data[id].clk); return err; } static int hantro_dev_remove(struct platform_device *pdev) { hantrodec_t *dev = platform_get_drvdata(pdev); hantro_clk_enable(&dev->clk); pm_runtime_get_sync(&pdev->dev); hantrodec_cleanup(dev->core_id); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); hantro_clk_disable(&dev->clk); if (!IS_ERR(dev->clk.dec)) clk_put(dev->clk.dec); if (!IS_ERR(dev->clk.bus)) clk_put(dev->clk.bus); #ifdef CONFIG_DEVICE_THERMAL_HANTRO HANTRO_UNREG_THERMAL_NOTIFIER(&hantro_thermal_hot_notifier); #endif return 0; } #ifdef CONFIG_PM static int __maybe_unused hantro_suspend(struct device *dev) { hantrodec_t *hantrodev = dev_get_drvdata(dev); if (hantrodev->dec_owner) { /* polling until hw is idle */ while (hantrodev->hw_active) { pr_info("DEC[%d] is still in active when suspend !\n", hantrodev->core_id); usleep_range(5000, 10000); } /* let's backup all registers from H/W to shadow register to support suspend */ DecStoreRegs(hantrodev); } pm_runtime_put_sync_suspend(dev); //power off return 0; } static int __maybe_unused hantro_resume(struct device *dev) { hantrodec_t *hantrodev = dev_get_drvdata(dev); hantro_power_on_disirq(hantrodev); hantro_ctrlblk_reset(hantrodev); if (hantrodev->dec_owner) { /* let's restore registers from shadow register to H/W to support resume */ DecRestoreRegs(hantrodev); } return 0; } static int hantro_runtime_suspend(struct device *dev) { release_bus_freq(BUS_FREQ_HIGH); return 0; } static int hantro_runtime_resume(struct device *dev) { hantrodec_t *hantrodev = dev_get_drvdata(dev); request_bus_freq(BUS_FREQ_HIGH); hantro_ctrlblk_reset(hantrodev); return 0; } static const struct dev_pm_ops hantro_pm_ops = { SET_RUNTIME_PM_OPS(hantro_runtime_suspend, hantro_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(hantro_suspend, hantro_resume) }; #endif //CONFIG_PM static const struct of_device_id hantro_of_match[] = { { .compatible = "nxp,imx8mm-hantro", }, {/* sentinel */} }; MODULE_DEVICE_TABLE(of, hantro_of_match); static struct platform_driver mxchantro_driver = { .driver = { .name = "mxc_hantro_845", .of_match_table = hantro_of_match, #ifdef CONFIG_PM .pm = &hantro_pm_ops, #endif }, .probe = hantro_dev_probe, .remove = hantro_dev_remove, }; static int __init hantro_init(void) { int ret = platform_driver_register(&mxchantro_driver); return ret; } static void __exit hantro_exit(void) { platform_driver_unregister(&mxchantro_driver); if (hantrodec_major > 0) { device_destroy(hantro_class, MKDEV(hantrodec_major, 0)); class_destroy(hantro_class); unregister_chrdev(hantrodec_major, "hantrodec"); hantrodec_major = 0; } } module_init(hantro_init); module_exit(hantro_exit); /* module description */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Google Finland Oy"); MODULE_DESCRIPTION("Driver module for Hantro Decoder/Post-Processor");