#include <linux/mm.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
+#include <linux/reset.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/wakelock.h>
#include <linux/of_irq.h>
#include <linux/rockchip/cpu.h>
#include <linux/rockchip/cru.h>
+#include <linux/rockchip/pmu.h>
#ifdef CONFIG_MFD_SYSCON
#include <linux/regmap.h>
#endif
unsigned long size;
#if defined(CONFIG_VCODEC_MMU)
struct list_head mem_region_list;
+ u32 dec_base;
#endif
u32 *reg;
} vpu_reg;
struct clk *clk_cabac;
struct clk *pd_video;
+#ifdef CONFIG_RESET_CONTROLLER
+ struct reset_control *rst_a;
+ struct reset_control *rst_h;
+ struct reset_control *rst_v;
+#endif
struct device *dev;
u32 irq_status;
+ atomic_t reset_request;
#if defined(CONFIG_VCODEC_MMU)
struct ion_client *ion_client;
struct list_head mem_region_list;
u32 size;
};
+#ifdef CONFIG_COMPAT
struct compat_vpu_request {
compat_uptr_t req;
u32 size;
};
+#endif
/* debugfs root directory for all device (vpu, hevc).*/
static struct dentry *parent;
static void vpu_reset(struct vpu_subdev_data *data)
{
struct vpu_service_info *pservice = data->pservice;
+ enum pmu_idle_req type = IDLE_REQ_VIDEO;
+
+ if (pservice->dev_id == VCODEC_DEVICE_ID_HEVC)
+ type = IDLE_REQ_HEVC;
+
pr_info("%s: resetting...", dev_name(pservice->dev));
#if defined(CONFIG_ARCH_RK29)
cru_set_soft_reset(SOFT_RST_VCODEC_NIU_AXI, false);
cru_set_soft_reset(SOFT_RST_CPU_VCODEC, false);
pmu_set_idle_request(IDLE_REQ_VIDEO, false);
+#else
#endif
+ WARN_ON(pservice->reg_codec != NULL);
+ WARN_ON(pservice->reg_pproc != NULL);
+ WARN_ON(pservice->reg_resev != NULL);
pservice->reg_codec = NULL;
pservice->reg_pproc = NULL;
pservice->reg_resev = NULL;
+ pr_info("for 3288/3368...");
+#ifdef CONFIG_RESET_CONTROLLER
+ if (pservice->rst_a && pservice->rst_h) {
+ if (rockchip_pmu_ops.set_idle_request)
+ rockchip_pmu_ops.set_idle_request(type, true);
+ pr_info("reset in\n");
+ if (pservice->rst_v)
+ reset_control_assert(pservice->rst_v);
+ reset_control_assert(pservice->rst_a);
+ reset_control_assert(pservice->rst_h);
+ usleep_range(10, 20);
+ reset_control_deassert(pservice->rst_h);
+ reset_control_deassert(pservice->rst_a);
+ if (pservice->rst_v)
+ reset_control_deassert(pservice->rst_v);
+ if (rockchip_pmu_ops.set_idle_request)
+ rockchip_pmu_ops.set_idle_request(type, false);
+ }
+#endif
+
#if defined(CONFIG_VCODEC_MMU)
if (data->mmu_dev && test_bit(MMU_ACTIVATED, &data->state)) {
clear_bit(MMU_ACTIVATED, &data->state);
BUG_ON(!atomic_read(&pservice->enabled));
}
#endif
+ atomic_set(&pservice->reset_request, 0);
+ pr_info("done\n");
}
static void reg_deinit(struct vpu_subdev_data *data, vpu_reg *reg);
ion_free(pservice->ion_client, hdl);
return ret;
}
+
+ /* special for vpu dec num 12: record decoded length
+ hacking for decoded length
+ NOTE: not a perfect fix, the fd is not recorded */
+ if (tbl[i] == 12 && data->hw_info->hw_id != HEVC_ID &&
+ (reg->type == VPU_DEC || reg->type == VPU_DEC_PP)) {
+ reg->dec_base = mem_region->iova + offset;
+ vpu_debug(DEBUG_REGISTER, "dec_set %08x\n", reg->dec_base);
+ }
+
reg->reg[tbl[i]] = mem_region->iova + offset;
INIT_LIST_HEAD(&mem_region->reg_lnk);
list_add_tail(&mem_region->reg_lnk, ®->mem_region_list);
} else if (reg_check_fmt(reg) == VPU_DEC_FMT_H264) {
if (reg_probe_width(reg) > 3200) {
/*raise frequency for 4k avc.*/
- reg->freq = VPU_FREQ_500M;
+ reg->freq = VPU_FREQ_600M;
}
} else {
if (reg_check_interlace(reg)) {
int reg_len = REG_NUM_9190_DEC;
pservice->reg_codec = NULL;
reg_copy_from_hw(reg, data->dec_dev.hwregs, reg_len);
+#if defined(CONFIG_VCODEC_MMU)
+ /* revert hack for decoded length */
+ if (data->hw_info->hw_id != HEVC_ID) {
+ u32 dec_get = reg->reg[12];
+ s32 dec_length = dec_get - reg->dec_base;
+ vpu_debug(DEBUG_REGISTER, "dec_get %08x dec_length %d\n", dec_get, dec_length);
+ reg->reg[12] = dec_length << 10;
+ }
+#endif
irq_reg = DEC_INTERRUPT_REGISTER;
break;
}
pservice->reg_pproc = NULL;
reg_copy_from_hw(reg, data->dec_dev.hwregs, REG_NUM_9190_DEC_PP);
data->dec_dev.hwregs[PP_INTERRUPT_REGISTER] = 0;
+#if defined(CONFIG_VCODEC_MMU)
+ /* revert hack for decoded length */
+ if (data->hw_info->hw_id != HEVC_ID) {
+ u32 dec_get = reg->reg[12];
+ s32 dec_length = dec_get - reg->dec_base;
+ vpu_debug(DEBUG_REGISTER, "dec_get %08x dec_length %d\n", dec_get, dec_length);
+ reg->reg[12] = dec_length << 10;
+ }
+#endif
break;
}
default : {
vpu_debug_enter();
if (!list_empty(&pservice->waiting)) {
int can_set = 0;
+ bool change_able = (NULL == pservice->reg_codec) && (NULL == pservice->reg_pproc);
+ int reset_request = atomic_read(&pservice->reset_request);
vpu_reg *reg = list_entry(pservice->waiting.next, vpu_reg, status_link);
vpu_service_power_on(pservice);
- switch (reg->type) {
- case VPU_ENC : {
- if ((NULL == pservice->reg_codec) && (NULL == pservice->reg_pproc))
- can_set = 1;
- } break;
- case VPU_DEC : {
- if (NULL == pservice->reg_codec)
- can_set = 1;
- if (pservice->auto_freq && (NULL != pservice->reg_pproc))
- can_set = 0;
- } break;
- case VPU_PP : {
- if (NULL == pservice->reg_codec) {
- if (NULL == pservice->reg_pproc)
+ // first check can_set flag
+ if (change_able || !reset_request) {
+ switch (reg->type) {
+ case VPU_ENC : {
+ if (change_able)
can_set = 1;
- } else {
- if ((VPU_DEC == pservice->reg_codec->type) && (NULL == pservice->reg_pproc))
+ } break;
+ case VPU_DEC : {
+ if (NULL == pservice->reg_codec)
can_set = 1;
- /* can not charge frequency when vpu is working */
- if (pservice->auto_freq)
+ if (pservice->auto_freq && (NULL != pservice->reg_pproc))
can_set = 0;
- }
- } break;
- case VPU_DEC_PP : {
- if ((NULL == pservice->reg_codec) && (NULL == pservice->reg_pproc))
- can_set = 1;
} break;
- default : {
- printk("undefined reg type %d\n", reg->type);
- } break;
+ case VPU_PP : {
+ if (NULL == pservice->reg_codec) {
+ if (NULL == pservice->reg_pproc)
+ can_set = 1;
+ } else {
+ if ((VPU_DEC == pservice->reg_codec->type) && (NULL == pservice->reg_pproc))
+ can_set = 1;
+ /* can not charge frequency when vpu is working */
+ if (pservice->auto_freq)
+ can_set = 0;
+ }
+ } break;
+ case VPU_DEC_PP : {
+ if (change_able)
+ can_set = 1;
+ } break;
+ default : {
+ printk("undefined reg type %d\n", reg->type);
+ } break;
+ }
}
+
+ // then check reset request
+ if (reset_request && !change_able)
+ reset_request = 0;
+
+ // do reset before setting registers
+ if (reset_request)
+ vpu_reset(data);
+
if (can_set) {
reg_from_wait_to_run(pservice, reg);
reg_copy_to_hw(reg->data, reg);
rockchip_iovmm_set_fault_handler(dev, vcodec_sysmmu_fault_hdl);
}
#endif
+ get_hw_info(data);
+ pservice->auto_freq = true;
+
vcodec_exit_mode(data);
/* create device node */
ret = alloc_chrdev_region(&data->dev_t, 0, 1, name);
data->child_dev = device_create(data->cls, dev,
data->dev_t, NULL, name);
- get_hw_info(data);
-
platform_set_drvdata(pdev, data);
INIT_LIST_HEAD(&data->lnk_service);
return;
#endif
}
+
+#ifdef CONFIG_RESET_CONTROLLER
+ pservice->rst_a = devm_reset_control_get(pservice->dev, "video_a");
+ pservice->rst_h = devm_reset_control_get(pservice->dev, "video_h");
+ pservice->rst_v = devm_reset_control_get(pservice->dev, "video");
+
+ if (IS_ERR_OR_NULL(pservice->rst_a)) {
+ pr_warn("No reset resource define\n");
+ pservice->rst_a = NULL;
+ }
+
+ if (IS_ERR_OR_NULL(pservice->rst_h)) {
+ pr_warn("No reset resource define\n");
+ pservice->rst_h = NULL;
+ }
+
+ if (IS_ERR_OR_NULL(pservice->rst_v)) {
+ pr_warn("No reset resource define\n");
+ pservice->rst_v = NULL;
+ }
+#endif
+
of_property_read_string(np, "name", (const char**)&pservice->name);
}
atomic_set(&pservice->enabled, 0);
atomic_set(&pservice->power_on_cnt, 0);
atomic_set(&pservice->power_off_cnt, 0);
+ atomic_set(&pservice->reset_request, 0);
INIT_DELAYED_WORK(&pservice->power_off_work, vpu_power_off_work);
pr_info("probe device %s\n", dev_name(dev));
+ pservice->dev = dev;
+
vcodec_read_property(np, pservice);
vcodec_init_drvdata(pservice);
else
pservice->dev_id = VCODEC_DEVICE_ID_COMBO;
- pservice->dev = dev;
-
if (0 > vpu_get_clk(pservice))
goto err;
}
}
+static bool check_irq_err(task_info *task, u32 irq_status)
+{
+ return (task->error_mask & irq_status) ? true : false;
+}
+
static irqreturn_t vdpu_irq(int irq, void *dev_id)
{
struct vpu_subdev_data *data = (struct vpu_subdev_data*)dev_id;
struct vpu_service_info *pservice = data->pservice;
vpu_device *dev = &data->dec_dev;
u32 raw_status;
- u32 irq_status;
+ u32 dec_status;
/*vcodec_enter_mode(data);*/
- irq_status = raw_status = readl(dev->hwregs + DEC_INTERRUPT_REGISTER);
+ dec_status = raw_status = readl(dev->hwregs + DEC_INTERRUPT_REGISTER);
- if (irq_status & DEC_INTERRUPT_BIT) {
+ if (dec_status & DEC_INTERRUPT_BIT) {
time_record(&tasks[TASK_VPU_DEC], 1);
- vpu_debug(DEBUG_IRQ_STATUS, "vdpu_irq dec status %08x\n", irq_status);
- if ((irq_status & 0x40001) == 0x40001) {
+ vpu_debug(DEBUG_IRQ_STATUS, "vdpu_irq dec status %08x\n", dec_status);
+ if ((dec_status & 0x40001) == 0x40001) {
do {
- irq_status =
+ dec_status =
readl(dev->hwregs +
DEC_INTERRUPT_REGISTER);
- } while ((irq_status & 0x40001) == 0x40001);
+ } while ((dec_status & 0x40001) == 0x40001);
+ }
+
+ if (check_irq_err((data->hw_info->hw_id == HEVC_ID)?
+ (&tasks[TASK_RKDEC_HEVC]) : (&tasks[TASK_VPU_DEC]),
+ dec_status)) {
+ atomic_add(1, &pservice->reset_request);
}
writel(0, dev->hwregs + DEC_INTERRUPT_REGISTER);
}
if (data->hw_info->hw_id != HEVC_ID) {
- irq_status = readl(dev->hwregs + PP_INTERRUPT_REGISTER);
- if (irq_status & PP_INTERRUPT_BIT) {
+ u32 pp_status = readl(dev->hwregs + PP_INTERRUPT_REGISTER);
+ if (pp_status & PP_INTERRUPT_BIT) {
time_record(&tasks[TASK_VPU_PP], 1);
- vpu_debug(DEBUG_IRQ_STATUS, "vdpu_irq pp status %08x\n", irq_status);
+ vpu_debug(DEBUG_IRQ_STATUS, "vdpu_irq pp status %08x\n", pp_status);
+
+ if (check_irq_err(&tasks[TASK_VPU_PP], dec_status))
+ atomic_add(1, &pservice->reset_request);
+
/* clear pp IRQ */
- writel(irq_status & (~DEC_INTERRUPT_BIT), dev->hwregs + PP_INTERRUPT_REGISTER);
+ writel(pp_status & (~DEC_INTERRUPT_BIT), dev->hwregs + PP_INTERRUPT_REGISTER);
atomic_add(1, &dev->irq_count_pp);
time_diff(&tasks[TASK_VPU_PP]);
}
if (likely(irq_status & ENC_INTERRUPT_BIT)) {
time_record(&tasks[TASK_VPU_ENC], 1);
+
+ if (check_irq_err(&tasks[TASK_VPU_ENC], irq_status))
+ atomic_add(1, &pservice->reset_request);
+
/* clear enc IRQ */
writel(irq_status & (~ENC_INTERRUPT_BIT), dev->hwregs + ENC_INTERRUPT_REGISTER);
atomic_add(1, &dev->irq_count_codec);