2109 lines
54 KiB
C
Executable File
2109 lines
54 KiB
C
Executable File
/*****************************************************************************
|
|
* 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 <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>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
//#define CONFIG_DEVICE_THERMAL_HANTRO
|
|
#ifdef CONFIG_DEVICE_THERMAL_HANTRO
|
|
#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)
|
|
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");
|
|
|