summaryrefslogtreecommitdiff
path: root/drivers/mxc/hantro/hantrodec.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mxc/hantro/hantrodec.c')
-rwxr-xr-xdrivers/mxc/hantro/hantrodec.c1918
1 files changed, 1918 insertions, 0 deletions
diff --git a/drivers/mxc/hantro/hantrodec.c b/drivers/mxc/hantro/hantrodec.c
new file mode 100755
index 000000000000..0346a06fbb15
--- /dev/null
+++ b/drivers/mxc/hantro/hantrodec.c
@@ -0,0 +1,1918 @@
+/*****************************************************************************
+ * The GPL License (GPL)
+ *
+ * Copyright (c) 2015-2017, 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 <linux/hantrodec.h>
+#include "dwl_defs.h"
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/wait.h>
+#include <linux/timer.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/busfreq-imx.h>
+
+#ifdef CONFIG_DEVICE_THERMAL
+#include <linux/device_cooling.h>
+#define HANTRO_REG_THERMAL_NOTIFIER(a) register_devfreq_cooling_notifier(a)
+#define HANTRO_UNREG_THERMAL_NOTIFIER(a) unregister_devfreq_cooling_notifier(a)
+DEFINE_SPINLOCK(thermal_lock);
+/*1:hot, 0: not hot*/
+static int thermal_event;
+static int thermal_cur;
+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 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_G1_DEF_CLK (600000000)
+#define HANTRO_G2_DEF_CLK (600000000)
+#define HANTRO_BUS_DEF_CLK (800000000)
+#define HANTRO_CLK_VOL_THR (600000000)
+/***********************************************************************/
+
+#define IS_G1(hw_id) ((hw_id == 0x6731) ? 1:0)
+
+static const int DecHwId[] = {
+ 0x8190, /* Legacy HW */
+ 0x8170,
+ 0x9170,
+ 0x9190,
+ 0x6731, /* G1 */
+ 0x6732 /* G2 */
+};
+
+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;
+static struct clk *hantro_clk_g1;
+static struct clk *hantro_clk_g2;
+static struct clk *hantro_clk_bus;
+static struct regulator *hantro_regulator;
+
+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(hantro_dev, fmt, ## arg); \
+ } \
+ } while (0)
+
+
+static int hantrodec_major; /* dynamic allocation */
+
+/* here's all the must remember stuff */
+typedef struct {
+ char *buffer;
+ unsigned int iosize[HXDEC_MAX_CORES];
+ volatile u8 *hwregs[HXDEC_MAX_CORES];
+ int irq[HXDEC_MAX_CORES];
+ int hw_id[HXDEC_MAX_CORES];
+ int cores;
+ struct fasync_struct *async_queue_dec;
+ struct fasync_struct *async_queue_pp;
+} hantrodec_t;
+
+static hantrodec_t hantrodec_data; /* dynamic allocation? */
+
+static int ReserveIO(void);
+static void ReleaseIO(void);
+
+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 u32 dec_regs[HXDEC_MAX_CORES][DEC_IO_SIZE_MAX/4];
+struct semaphore dec_core_sem;
+struct semaphore pp_core_sem;
+
+static int dec_irq;
+static int pp_irq;
+
+atomic_t irq_rx = ATOMIC_INIT(0);
+atomic_t irq_tx = ATOMIC_INIT(0);
+
+static struct file *dec_owner[HXDEC_MAX_CORES];
+static struct file *pp_owner[HXDEC_MAX_CORES];
+
+/* spinlock_t owner_lock = SPIN_LOCK_UNLOCKED; */
+DEFINE_SPINLOCK(owner_lock);
+
+DECLARE_WAIT_QUEUE_HEAD(dec_wait_queue);
+DECLARE_WAIT_QUEUE_HEAD(pp_wait_queue);
+
+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 u32 cfg[HXDEC_MAX_CORES];
+static u32 timeout;
+
+static int hantro_update_voltage(struct device *dev)
+{
+ unsigned long new_vol, old_vol;
+ int ret;
+ unsigned long clk1, clk2;
+
+ clk1 = clk_get_rate(hantro_clk_g1);
+ clk2 = clk_get_rate(hantro_clk_g2);
+
+ if (!clk1 || !clk2)
+ return -1;
+
+ old_vol = regulator_get_voltage(hantro_regulator);
+ if ((clk1 >= HANTRO_CLK_VOL_THR) || (clk2 >= HANTRO_CLK_VOL_THR))
+ new_vol = 1000000; // 1.0v
+ else
+ new_vol = 900000; // 0.9v
+
+ if (old_vol != new_vol) {
+ ret = regulator_set_voltage_tol(hantro_regulator, new_vol, 0);
+ if (ret)
+ pr_err("failed to set hantro voltage: %ld mV\n", new_vol/1000);
+ else
+ pr_info("update hantro voltage from %ld mV to %ld mV\n", old_vol/1000, new_vol/1000);
+ }
+ return 0;
+}
+
+static int hantro_clk_enable(struct device *dev)
+{
+ clk_prepare(hantro_clk_g1);
+ clk_enable(hantro_clk_g1);
+ clk_prepare(hantro_clk_g2);
+ clk_enable(hantro_clk_g2);
+ clk_prepare(hantro_clk_bus);
+ clk_enable(hantro_clk_bus);
+ return 0;
+}
+
+static int hantro_clk_disable(struct device *dev)
+{
+ if (hantro_clk_g1) {
+ clk_disable(hantro_clk_g1);
+ clk_unprepare(hantro_clk_g1);
+ }
+ if (hantro_clk_g2) {
+ clk_disable(hantro_clk_g2);
+ clk_unprepare(hantro_clk_g2);
+ }
+ if (hantro_clk_bus) {
+ clk_disable(hantro_clk_bus);
+ clk_unprepare(hantro_clk_bus);
+ }
+ return 0;
+}
+
+static int hantro_ctrlblk_reset(struct device *dev)
+{
+ volatile u8 *iobase;
+
+ //config G1/G2
+ hantro_clk_enable(dev);
+ iobase = (volatile u8 *)ioremap_nocache(BLK_CTL_BASE, 0x10000);
+ iowrite32(0x3, iobase); //VPUMIX G1/G2 block soft reset control
+ iowrite32(0x3, iobase+4); //VPUMIX G1/G2 block clock enable control
+ iowrite32(0xFFFFFFFF, iobase + 0x8); // all G1 fuse dec enable
+ iowrite32(0xFFFFFFFF, iobase + 0xC); // all G1 fuse pp enable
+ iowrite32(0xFFFFFFFF, iobase + 0x10); // all G2 fuse dec enable
+ iounmap(iobase);
+ hantro_clk_disable(dev);
+ return 0;
+}
+
+#ifdef CONFIG_DEVICE_THERMAL
+static int hantro_thermal_check(struct device *dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&thermal_lock, flags);
+ if (thermal_event == thermal_cur) {
+ /*nothing to do and return directly*/
+ spin_unlock_irqrestore(&thermal_lock, flags);
+ return 0;
+ }
+ thermal_cur = thermal_event;
+ spin_unlock_irqrestore(&thermal_lock, flags);
+
+ if (thermal_cur) {
+ int ratio = hantro_clock_ratio;
+
+ pr_debug("hantro: too hot, need to decrease clock, ratio: 1/%d\n", ratio);
+ /*clock disable/enable are not required for vpu clock rate operation*/
+ clk_set_rate(hantro_clk_g1, HANTRO_G1_DEF_CLK/ratio);
+ clk_set_rate(hantro_clk_g2, HANTRO_G2_DEF_CLK/ratio);
+ clk_set_rate(hantro_clk_bus, HANTRO_BUS_DEF_CLK/ratio);
+ } else {
+ pr_debug("hantro: not hot again, will restore default clock\n");
+ clk_set_rate(hantro_clk_g1, HANTRO_G1_DEF_CLK);
+ clk_set_rate(hantro_clk_g2, HANTRO_G2_DEF_CLK);
+ clk_set_rate(hantro_clk_bus, HANTRO_BUS_DEF_CLK);
+ }
+ pr_info("hantro: event(%d), g1, g2, bus clock: %ld, %ld, %ld\n", thermal_cur,
+ clk_get_rate(hantro_clk_g1), clk_get_rate(hantro_clk_g2), clk_get_rate(hantro_clk_bus));
+ hantro_update_voltage(dev);
+ 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
+
+static void ReadCoreConfig(hantrodec_t *dev)
+{
+ int c;
+ u32 reg, tmp, mask;
+
+ memset(cfg, 0, sizeof(cfg));
+
+ for (c = 0; c < dev->cores; c++) {
+ /* Decoder configuration */
+ if (IS_G1(dev->hw_id[c])) {
+ reg = ioread32(dev->hwregs[c] + HANTRODEC_SYNTH_CFG * 4);
+
+ tmp = (reg >> DWL_H264_E) & 0x3U;
+ if (tmp)
+ pr_debug("hantrodec: Core[%d] has H264\n", c);
+ cfg[c] |= 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);
+ cfg[c] |= 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);
+ cfg[c] |= 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);
+ cfg[c] |= 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);
+ cfg[c] |= 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);
+ cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP6_DEC : 0;
+
+ reg = ioread32(dev->hwregs[c] + 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);
+ cfg[c] |= 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);
+ cfg[c] |= 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);
+ cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_RV_DEC : 0;
+
+ /* Post-processor configuration */
+ reg = ioread32(dev->hwregs[c] + HANTROPP_SYNTH_CFG * 4);
+ } else {
+ reg = ioread32(dev->hwregs[c] + HANTRODEC_SYNTH_CFG_2 * 4);
+
+ tmp = (reg >> DWL_HEVC_E) & 0x3U;
+ if (tmp)
+ pr_debug("hantrodec: Core[%d] has HEVC\n", c);
+ cfg[c] |= 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);
+ cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP9_DEC : 0;
+ }
+
+ /* Post-processor configuration */
+ reg = ioread32(dev->hwregs[c] + HANTRODECPP_SYNTH_CFG * 4);
+
+ tmp = (reg >> DWL_PP_E) & 0x01U;
+ if (tmp)
+ pr_debug("hantrodec: Core[%d] has PP\n", c);
+ cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_PP : 0;
+ }
+}
+
+static int CoreHasFormat(const u32 *cfg, int Core, u32 format)
+{
+ return (cfg[Core] & (1 << format)) ? 1 : 0;
+}
+
+int GetDecCore(long Core, hantrodec_t *dev, struct file *filp)
+{
+ int success = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&owner_lock, flags);
+ if (dec_owner[Core] == NULL) {
+ dec_owner[Core] = filp;
+ success = 1;
+ }
+
+ spin_unlock_irqrestore(&owner_lock, flags);
+
+ return success;
+}
+
+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(cfg, c, format) && GetDecCore(c, dev, filp)) {
+ success = 1;
+ *Core = c;
+ break;
+ }
+ }
+
+ return success;
+}
+int GetDecCoreID(hantrodec_t *dev, struct file *filp,
+ unsigned long format) {
+ long c;
+
+ int core_id = -1;
+
+ for (c = 0; c < dev->cores; c++) {
+ /* a Core that has format */
+ if (CoreHasFormat(cfg, c, format)) {
+ core_id = c;
+ break;
+ }
+ }
+ PDEBUG("GetDecCoreID=%d\n", core_id);
+ return core_id;
+}
+
+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;
+}
+
+
+long ReserveDecoder(hantrodec_t *dev, struct file *filp, unsigned long format)
+{
+ long Core = -1;
+
+ /* reserve a Core */
+ if (down_interruptible(&dec_core_sem))
+ return -ERESTARTSYS;
+
+ /* lock a Core that has specific format*/
+ if (wait_event_interruptible(hw_queue, GetDecCoreAny(&Core, dev, filp, format) != 0))
+ return -ERESTARTSYS;
+
+ if (IS_G1(dev->hw_id[Core])) {
+ 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;
+ }
+
+#ifdef CONFIG_DEVICE_THERMAL
+ if (hantro_dynamic_clock)
+ hantro_thermal_check(hantro_dev);
+#endif
+
+ return Core;
+}
+
+void ReleaseDecoder(hantrodec_t *dev, long Core)
+{
+ u32 status;
+ unsigned long flags;
+
+ status = ioread32(dev->hwregs[Core] + HANTRODEC_IRQ_STAT_DEC_OFF);
+
+ /* make sure HW is disabled */
+ if (status & HANTRODEC_DEC_E) {
+ pr_info("hantrodec: DEC[%li] still enabled -> reset\n", Core);
+
+ /* abort decoder */
+ status |= HANTRODEC_DEC_ABORT | HANTRODEC_DEC_IRQ_DISABLE;
+ iowrite32(status, dev->hwregs[Core] + HANTRODEC_IRQ_STAT_DEC_OFF);
+ }
+
+ spin_lock_irqsave(&owner_lock, flags);
+
+ dec_owner[Core] = NULL;
+
+ spin_unlock_irqrestore(&owner_lock, flags);
+
+ up(&dec_core_sem);
+
+ wake_up_interruptible_all(&hw_queue);
+
+}
+
+long ReservePostProcessor(hantrodec_t *dev, struct file *filp)
+{
+ unsigned long flags;
+
+ long Core = 0;
+
+ /* single Core PP only */
+ if (down_interruptible(&pp_core_sem))
+ return -ERESTARTSYS;
+
+ spin_lock_irqsave(&owner_lock, flags);
+
+ pp_owner[Core] = filp;
+
+ spin_unlock_irqrestore(&owner_lock, flags);
+
+ return Core;
+}
+
+void ReleasePostProcessor(hantrodec_t *dev, long Core)
+{
+ unsigned long flags;
+
+ u32 status = ioread32(dev->hwregs[Core] + HANTRO_IRQ_STAT_PP_OFF);
+
+ /* make sure HW is disabled */
+ if (status & HANTRO_PP_E) {
+ pr_info("hantrodec: PP[%li] still enabled -> reset\n", Core);
+
+ /* disable IRQ */
+ status |= HANTRO_PP_IRQ_DISABLE;
+
+ /* disable postprocessor */
+ status &= (~HANTRO_PP_E);
+ iowrite32(0x10, dev->hwregs[Core] + HANTRO_IRQ_STAT_PP_OFF);
+ }
+
+ spin_lock_irqsave(&owner_lock, flags);
+
+ pp_owner[Core] = NULL;
+
+ spin_unlock_irqrestore(&owner_lock, flags);
+
+ up(&pp_core_sem);
+}
+
+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(cfg, Core, format))
+ return -EFAULT;
+
+ /* check that Core has PP */
+ if (!CoreHasFormat(cfg, Core, DWL_CLIENT_TYPE_PP))
+ return -EFAULT;
+
+ /* reserve a Core */
+ if (down_interruptible(&dec_core_sem))
+ return -ERESTARTSYS;
+
+ /* wait until the Core is available */
+ if (wait_event_interruptible(hw_queue, GetDecCore(Core, dev, filp) != 0)) {
+ up(&dec_core_sem);
+ return -ERESTARTSYS;
+ }
+
+ if (down_interruptible(&pp_core_sem)) {
+ ReleaseDecoder(dev, Core);
+ return -ERESTARTSYS;
+ }
+
+ spin_lock_irqsave(&owner_lock, flags);
+ pp_owner[Core] = filp;
+ spin_unlock_irqrestore(&owner_lock, flags);
+
+ return Core;
+}
+
+long DecFlushRegs(hantrodec_t *dev, struct core_desc *Core)
+{
+ long ret = 0, i;
+
+ u32 id = Core->id;
+
+ if (IS_G1(dev->hw_id[id])) {
+ /* copy original dec regs to kernal space*/
+ ret = copy_from_user(dec_regs[id], 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(dec_regs[id] + 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(dec_regs[id][i], dev->hwregs[id] + i*4);
+#ifdef USE_64BIT_ENV
+ for (i = HANTRO_DEC_EXT_FIRST_REG; i <= HANTRO_DEC_EXT_LAST_REG; i++)
+ iowrite32(dec_regs[id][i], dev->hwregs[id] + i*4);
+#endif
+ } else {
+ ret = copy_from_user(dec_regs[id], 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(dec_regs[id][i], dev->hwregs[id] + i*4);
+ }
+
+ /* write the status register, which may start the decoder */
+ iowrite32(dec_regs[id][1], dev->hwregs[id] + 4);
+
+ PDEBUG("flushed registers on Core %d\n", id);
+
+ return 0;
+}
+
+long DecRefreshRegs(hantrodec_t *dev, struct core_desc *Core)
+{
+ long ret, i;
+ u32 id = Core->id;
+
+ if (IS_G1(dev->hw_id[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++)
+ dec_regs[id][i] = ioread32(dev->hwregs[id] + i*4);
+#ifdef USE_64BIT_ENV
+ for (i = HANTRO_DEC_EXT_FIRST_REG; i <= HANTRO_DEC_EXT_LAST_REG; i++)
+ dec_regs[id][i] = ioread32(dev->hwregs[id] + i*4);
+#endif
+
+ if (timeout) {
+ /* Enable TIMEOUT bits in Reg[1] */
+ dec_regs[id][1] = 0x40100;
+ /* Reset HW */
+ ResetAsic(dev);
+ timeout = 0;
+ }
+
+ /* put registers to user space*/
+ /* put original registers to user space*/
+ ret = copy_to_user(Core->regs, dec_regs[id], 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,
+ dec_regs[id] + 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++)
+ dec_regs[id][i] = ioread32(dev->hwregs[id] + i*4);
+
+ if (timeout) {
+ /* Enable TIMEOUT bits in Reg[1] */
+ dec_regs[id][1] = 0x40100;
+ /* Reset HW */
+ ResetAsic(dev);
+ timeout = 0;
+ }
+
+ /* put registers to user space*/
+ ret = copy_to_user(Core->regs, dec_regs[id], HANTRO_G2_DEC_REGS*4);
+ if (ret) {
+ pr_err("copy_to_user failed, returned %li\n", ret);
+ return -EFAULT;
+ }
+ }
+ return 0;
+}
+
+static int CheckDecIrq(hantrodec_t *dev, int id)
+{
+ unsigned long flags;
+ int rdy = 0;
+
+ const u32 irq_mask = (1 << 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;
+}
+
+long WaitDecReadyAndRefreshRegs(hantrodec_t *dev, struct core_desc *Core)
+{
+ u32 id = Core->id;
+ long ret;
+
+ PDEBUG("wait_event_interruptible DEC[%d]\n", id);
+
+ ret = wait_event_interruptible_timeout(dec_wait_queue, CheckDecIrq(dev, id), msecs_to_jiffies(200));
+ if (ret == -ERESTARTSYS) {
+ pr_err("DEC[%d] failed to wait_event_interruptible interrupted\n", id);
+ return -ERESTARTSYS;
+ } else if (ret == 0) {
+ pr_err("DEC[%d] wait_event_interruptible timeout\n", id);
+ timeout = 1;
+ }
+
+ atomic_inc(&irq_tx);
+
+ /* refresh registers */
+ return DecRefreshRegs(dev, Core);
+}
+
+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(dec_regs[id] + 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(dec_regs[id] + 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(dec_regs[id][i], dev->hwregs[id] + i*4);
+#ifdef USE_64BIT_ENV
+ for (i = HANTRO_PP_EXT_FIRST_REG; i <= HANTRO_PP_EXT_LAST_REG; i++)
+ iowrite32(dec_regs[id][i], dev->hwregs[id] + i*4);
+#endif
+ /* write the stat reg, which may start the PP */
+ iowrite32(dec_regs[id][HANTRO_PP_ORG_FIRST_REG],
+ dev->hwregs[id] + HANTRO_PP_ORG_FIRST_REG * 4);
+
+ return 0;
+}
+
+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++)
+ dec_regs[id][i] = ioread32(dev->hwregs[id] + i*4);
+#ifdef USE_64BIT_ENV
+ for (i = HANTRO_PP_EXT_FIRST_REG; i <= HANTRO_PP_EXT_LAST_REG; i++)
+ dec_regs[id][i] = ioread32(dev->hwregs[id] + 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,
+ dec_regs[id] + 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,
+ dec_regs[id] + 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, int id)
+{
+ unsigned long flags;
+ int rdy = 0;
+
+ const u32 irq_mask = (1 << 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;
+}
+
+long WaitPPReadyAndRefreshRegs(hantrodec_t *dev, struct core_desc *Core)
+{
+ u32 id = Core->id;
+
+ PDEBUG("wait_event_interruptible PP[%d]\n", id);
+
+ if (wait_event_interruptible(pp_wait_queue, CheckPPIrq(dev, id))) {
+ pr_err("PP[%d] failed to wait_event_interruptible interrupted\n", id);
+ return -ERESTARTSYS;
+ }
+
+ atomic_inc(&irq_tx);
+
+ /* refresh registers */
+ return PPRefreshRegs(dev, Core);
+}
+
+static int CheckCoreIrq(hantrodec_t *dev, const struct file *filp, int *id)
+{
+ unsigned long flags;
+ int rdy = 0, n = 0;
+
+ do {
+ u32 irq_mask = (1 << n);
+
+ spin_lock_irqsave(&owner_lock, flags);
+
+ if (dec_irq & irq_mask) {
+ if (dec_owner[n] == 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 = n;
+
+ rdy = 1;
+
+ spin_unlock_irqrestore(&owner_lock, flags);
+ break;
+ } else if (dec_owner[n] == 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 < dev->cores);
+
+ return rdy;
+}
+
+long WaitCoreReady(hantrodec_t *dev, const struct file *filp, int *id)
+{
+ PDEBUG("wait_event_interruptible CORE\n");
+
+ if (wait_event_interruptible(dec_wait_queue, CheckCoreIrq(dev, filp, id))) {
+ pr_err("CORE failed to wait_event_interruptible interrupted\n");
+ return -ERESTARTSYS;
+ }
+
+ atomic_inc(&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(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(HANTRODEC_IOC_CLI): {
+ __u32 id;
+
+ __get_user(id, (__u32 *)arg);
+ if (id >= hantrodec_data.cores)
+ return -EFAULT;
+ disable_irq(hantrodec_data.irq[id]);
+ break;
+ }
+ case _IOC_NR(HANTRODEC_IOC_STI): {
+ __u32 id;
+
+ __get_user(id, (__u32 *)arg);
+ if (id >= hantrodec_data.cores)
+ return -EFAULT;
+ enable_irq(hantrodec_data.irq[id]);
+ break;
+ }
+ case _IOC_NR(HANTRODEC_IOCGHWOFFSET): {
+ __u32 id;
+
+ __get_user(id, (__u32 *)arg);
+ if (id >= hantrodec_data.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 >= hantrodec_data.cores)
+ return -EFAULT;
+ io_size = hantrodec_data.iosize[id];
+ __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(hantrodec_data.cores, (unsigned int *) arg);
+ PDEBUG("hantrodec_data.cores=%d\n", hantrodec_data.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;
+ }
+
+ DecFlushRegs(&hantrodec_data, &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;
+ }
+
+ PPFlushRegs(&hantrodec_data, &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;
+ }
+
+ return DecRefreshRegs(&hantrodec_data, &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;
+ }
+
+ return PPRefreshRegs(&hantrodec_data, &Core);
+ }
+ case _IOC_NR(HANTRODEC_IOCH_DEC_RESERVE): {
+ PDEBUG("Reserve DEC Core, format = %li\n", arg);
+ return ReserveDecoder(&hantrodec_data, filp, arg);
+ }
+ case _IOC_NR(HANTRODEC_IOCT_DEC_RELEASE): {
+ if (arg >= hantrodec_data.cores || dec_owner[arg] != 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, filp);
+ case _IOC_NR(HANTRODEC_IOCT_PP_RELEASE): {
+ if (arg != 0 || pp_owner[arg] != 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;
+ }
+
+ return WaitDecReadyAndRefreshRegs(&hantrodec_data, &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;
+ }
+
+ return WaitPPReadyAndRefreshRegs(&hantrodec_data, &Core);
+ }
+ case _IOC_NR(HANTRODEC_IOCG_CORE_WAIT): {
+ int id;
+
+ tmp = WaitCoreReady(&hantrodec_data, 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 >= hantrodec_data.cores)
+ return -EFAULT;
+ id = ioread32(hantrodec_data.hwregs[id]);
+ __put_user(id, (u32 *) arg);
+ return 0;
+ }
+ case _IOC_NR(HANTRODEC_IOCG_CORE_ID): {
+ PDEBUG("Get DEC Core_id, format = %li\n", arg);
+ return GetDecCoreID(&hantrodec_data, filp, arg);
+ }
+ 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 < hantrodec_data.cores; tmp++) {
+ PDEBUG("hantrodec: dec_core[%li] %s\n",
+ tmp, dec_owner[tmp] == NULL ? "FREE" : "RESERVED");
+ PDEBUG("hantrodec: pp_core[%li] %s\n",
+ tmp, pp_owner[tmp] == NULL ? "FREE" : "RESERVED");
+ }
+ }
+ 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(VERIFY_READ, 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(VERIFY_WRITE, 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)
+{
+ PDEBUG("dev opened\n");
+ hantro_clk_enable(hantro_dev);
+ pm_runtime_get_sync(hantro_dev);
+ 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;
+
+ PDEBUG("closing ...\n");
+
+ for (n = 0; n < dev->cores; n++) {
+ if (dec_owner[n] == filp) {
+ PDEBUG("releasing dec Core %i lock\n", n);
+ ReleaseDecoder(dev, n);
+ }
+ }
+
+ for (n = 0; n < 1; n++) {
+ if (pp_owner[n] == filp) {
+ PDEBUG("releasing pp Core %i lock\n", n);
+ ReleasePostProcessor(dev, n);
+ }
+ }
+
+ pm_runtime_put_sync(hantro_dev);
+ hantro_clk_disable(hantro_dev);
+ PDEBUG("closed\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
+ *---------------------------------------------------------------------------
+ */
+int hantrodec_init(struct platform_device *pdev)
+{
+ int result;
+ int irq_0, irq_1;
+
+ 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;
+
+ 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;
+ }
+
+ result = ReserveIO();
+ if (result < 0)
+ goto err;
+
+ memset(dec_owner, 0, sizeof(dec_owner));
+ memset(pp_owner, 0, sizeof(pp_owner));
+
+ sema_init(&dec_core_sem, hantrodec_data.cores-1);
+ sema_init(&pp_core_sem, 1);
+
+ /* read configuration fo all cores */
+ ReadCoreConfig(&hantrodec_data);
+
+ /* reset hardware */
+ ResetAsic(&hantrodec_data);
+
+ /* register irq for each core*/
+ irq_0 = platform_get_irq_byname(pdev, "irq_hantro_g1");
+ if (irq_0 > 0) {
+ hantrodec_data.irq[0] = irq_0;
+ result = request_irq(irq_0, hantrodec_isr, IRQF_SHARED,
+ "hantrodec", (void *) &hantrodec_data);
+
+ 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.irq[0]);
+ }
+ ReleaseIO();
+ goto err;
+ }
+ } else {
+ pr_err("hantrodec: IRQ0 not in use!\n");
+ goto err;
+ }
+
+ irq_1 = platform_get_irq_byname(pdev, "irq_hantro_g2");
+ if (irq_1 > 0) {
+ hantrodec_data.irq[1] = irq_1;
+ result = request_irq(irq_1, hantrodec_isr, IRQF_SHARED,
+ "hantrodec", (void *) &hantrodec_data);
+
+ 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.irq[1]);
+ }
+
+ ReleaseIO();
+ goto err;
+ }
+ } else {
+ pr_err("hantrodec: IRQ1 not in use!\n");
+ goto err;
+ }
+ pr_info("hantrodec: module inserted. Major = %d\n", hantrodec_major);
+
+ return 0;
+
+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
+ *---------------------------------------------------------------------------
+ */
+void hantrodec_cleanup(void)
+{
+ hantrodec_t *dev = &hantrodec_data;
+ int n = 0;
+ /* reset hardware */
+ ResetAsic(dev);
+
+ /* free the IRQ */
+ for (n = 0; n < dev->cores; n++) {
+ if (dev->irq[n] != -1)
+ free_irq(dev->irq[n], (void *) dev);
+ }
+
+ ReleaseIO();
+
+ 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 < dev->cores; i++) {
+ if (dev->hwregs[i] != NULL) {
+ hwid = readl(dev->hwregs[i]);
+ pr_debug("hantrodec: Core %d HW ID=0x%16lx\n", i, 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[i]);
+ found++;
+ dev->hw_id[i] = hwid;
+ break;
+ }
+ }
+ if (!found) {
+ pr_err("hantrodec: Unknown HW found at 0x%16lx\n", multicorebase[i]);
+ return 0;
+ }
+ found = 0;
+ num_hw = sizeof(DecHwId) / sizeof(*DecHwId);
+ }
+ }
+
+ return 1;
+}
+
+/*---------------------------------------------------------------------------
+ *Function name : ReserveIO
+ *Description : IO reserve
+ *
+ *Return type : int
+ *---------------------------------------------------------------------------
+ */
+static int ReserveIO(void)
+{
+ int i;
+
+ for (i = 0; i < HXDEC_MAX_CORES; i++) {
+ if (multicorebase[i] != -1) {
+ if (!request_mem_region(multicorebase[i], hantrodec_data.iosize[i], "hantrodec0")) {
+ pr_err("hantrodec: failed to reserve HW regs\n");
+ return -EBUSY;
+ }
+
+ hantrodec_data.hwregs[i] = (volatile u8 *) ioremap_nocache(multicorebase[i],
+ hantrodec_data.iosize[i]);
+
+ if (hantrodec_data.hwregs[i] == NULL) {
+ pr_err("hantrodec: failed to ioremap HW regs\n");
+ ReleaseIO();
+ return -EBUSY;
+ }
+ hantrodec_data.cores++;
+ }
+ }
+
+ /* check for correct HW */
+ if (!CheckHwId(&hantrodec_data)) {
+ ReleaseIO();
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ *Function name : releaseIO
+ *Description : release
+ *
+ *Return type : void
+ *---------------------------------------------------------------------------
+ */
+static void ReleaseIO(void)
+{
+ int i;
+
+ for (i = 0; i < hantrodec_data.cores; i++) {
+ if (hantrodec_data.hwregs[i])
+ iounmap((void *) hantrodec_data.hwregs[i]);
+ release_mem_region(multicorebase[i], hantrodec_data.iosize[i]);
+ }
+}
+
+/*---------------------------------------------------------------------------
+ *Function name : hantrodec_isr
+ *Description : interrupt handler
+ *
+ *Return type : irqreturn_t
+ *---------------------------------------------------------------------------
+ */
+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 < dev->cores; i++) {
+ volatile u8 *hwregs = dev->hwregs[i];
+
+ /* 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", i);
+
+ atomic_inc(&irq_rx);
+
+ dec_irq |= (1 << i);
+
+ wake_up_interruptible_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 :
+ *---------------------------------------------------------------------------
+ */
+void ResetAsic(hantrodec_t *dev)
+{
+ int i, j;
+ u32 status;
+
+ for (j = 0; j < dev->cores; j++) {
+ status = ioread32(dev->hwregs[j] + 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[j] + HANTRODEC_IRQ_STAT_DEC_OFF);
+ }
+
+ if (IS_G1(dev->hw_id[j]))
+ /* reset PP */
+ iowrite32(0, dev->hwregs[j] + HANTRO_IRQ_STAT_PP_OFF);
+
+ for (i = 4; i < dev->iosize[j]; i += 4)
+ iowrite32(0, dev->hwregs[j] + i);
+ }
+}
+
+/*---------------------------------------------------------------------------
+ *Function name : dump_regs
+ *Description : Dump registers
+ *
+ *Return type :
+ *---------------------------------------------------------------------------
+ */
+#ifdef HANTRODEC_DEBUG
+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 device *temp_class;
+ struct resource *res;
+ unsigned long reg_base;
+
+ hantro_dev = &pdev->dev;
+ 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[0]) {
+ pr_err("hantrodec: regbase(0x%lX) not equal to expected value(0x%lX)\n", reg_base, multicorebase[0]);
+ return -ENODEV;
+ }
+
+ hantro_clk_g1 = clk_get(&pdev->dev, "clk_hantro_g1");
+ hantro_clk_g2 = clk_get(&pdev->dev, "clk_hantro_g2");
+ hantro_clk_bus = clk_get(&pdev->dev, "clk_hantro_bus");
+ if (IS_ERR(hantro_clk_g1) || IS_ERR(hantro_clk_g2) || IS_ERR(hantro_clk_bus)) {
+ pr_err("hantro: get clock failed\n");
+ return -ENODEV;
+ }
+ pr_debug("hantro: g1, g2, bus clock: 0x%lX, 0x%lX, 0x%lX\n", clk_get_rate(hantro_clk_g1),
+ clk_get_rate(hantro_clk_g2), clk_get_rate(hantro_clk_bus));
+
+ hantro_regulator = regulator_get(&pdev->dev, "regulator");
+ if (IS_ERR(hantro_regulator)) {
+ pr_err("hantro: get regulator failed\n");
+ return -ENODEV;
+ }
+ hantro_update_voltage(&pdev->dev);
+
+ hantro_clk_enable(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+ hantro_ctrlblk_reset(&pdev->dev);
+
+ err = hantrodec_init(pdev);
+ if (0 != err) {
+ pr_err("hantro: hantrodec_init failed\n");
+ goto error;
+ }
+
+ hantro_class = class_create(THIS_MODULE, "mxc_hantro");
+ if (IS_ERR(hantro_class)) {
+ err = PTR_ERR(hantro_class);
+ goto error;
+ }
+ temp_class = device_create(hantro_class, NULL, MKDEV(hantrodec_major, 0), NULL, DEVICE_NAME);
+ if (IS_ERR(temp_class)) {
+ err = PTR_ERR(temp_class);
+ goto err_out_class;
+ }
+
+#ifdef CONFIG_DEVICE_THERMAL
+ HANTRO_REG_THERMAL_NOTIFIER(&hantro_thermal_hot_notifier);
+ thermal_event = 0;
+ thermal_cur = 0;
+ hantro_dynamic_clock = 0;
+#endif
+ timeout = 0;
+ goto out;
+
+err_out_class:
+ device_destroy(hantro_class, MKDEV(hantrodec_major, 0));
+ class_destroy(hantro_class);
+error:
+ pr_err("hantro probe failed\n");
+out:
+ pm_runtime_put_sync(&pdev->dev);
+ hantro_clk_disable(&pdev->dev);
+ return err;
+}
+
+static int hantro_dev_remove(struct platform_device *pdev)
+{
+ hantro_clk_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+ if (hantrodec_major > 0) {
+ device_destroy(hantro_class, MKDEV(hantrodec_major, 0));
+ class_destroy(hantro_class);
+ hantrodec_cleanup();
+ hantrodec_major = 0;
+ }
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ hantro_clk_disable(&pdev->dev);
+
+#ifdef CONFIG_DEVICE_THERMAL
+ HANTRO_UNREG_THERMAL_NOTIFIER(&hantro_thermal_hot_notifier);
+#endif
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int hantro_suspend(struct device *dev)
+{
+ pm_runtime_put_sync_suspend(dev); //power off
+ return 0;
+}
+static int hantro_resume(struct device *dev)
+{
+ pm_runtime_get_sync(dev); //power on
+ hantro_ctrlblk_reset(dev);
+ 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)
+{
+ request_bus_freq(BUS_FREQ_HIGH);
+ hantro_ctrlblk_reset(dev);
+ 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,imx8mq-hantro", },
+ {/* sentinel */}
+};
+MODULE_DEVICE_TABLE(of, vpu_of_match);
+
+
+static struct platform_driver mxchantro_driver = {
+ .driver = {
+ .name = "mxc_hantro",
+ .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)
+{
+ if (!IS_ERR(hantro_clk_g1))
+ clk_put(hantro_clk_g1);
+ if (!IS_ERR(hantro_clk_g2))
+ clk_put(hantro_clk_g2);
+ if (!IS_ERR(hantro_clk_bus))
+ clk_put(hantro_clk_bus);
+ if (!IS_ERR(hantro_regulator))
+ regulator_put(hantro_regulator);
+ platform_driver_unregister(&mxchantro_driver);
+}
+
+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");
+