VPU: pmu idle request before cru operation.
[firefly-linux-kernel-4.4.55.git] / arch / arm / mach-rockchip / vcodec_service.c
index 48803f83ef4dc9d4694dee2850d3e4b4b2c709e3..cb260d89d414f9abdfdc578b76e43dad87979551 100755 (executable)
@@ -30,6 +30,7 @@
 #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>
@@ -39,6 +40,7 @@
 #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
@@ -394,6 +396,7 @@ typedef struct vpu_reg {
        unsigned long size;
 #if defined(CONFIG_VCODEC_MMU)
        struct list_head mem_region_list;
+       u32 dec_base;
 #endif
        u32 *reg;
 } vpu_reg;
@@ -493,9 +496,15 @@ typedef struct vpu_service_info {
        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;
@@ -538,10 +547,12 @@ struct vpu_request {
        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;
@@ -768,6 +779,11 @@ static void vpu_put_clk(struct vpu_service_info *pservice)
 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)
@@ -794,11 +810,35 @@ static void vpu_reset(struct vpu_subdev_data *data)
        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);
@@ -808,6 +848,8 @@ static void vpu_reset(struct vpu_subdev_data *data)
                        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);
@@ -1076,6 +1118,16 @@ static int vcodec_bufid_to_iova(struct vpu_subdev_data *data, u8 *tbl,
                                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, &reg->mem_region_list);
@@ -1232,7 +1284,7 @@ static vpu_reg *reg_init(struct vpu_subdev_data *data,
                                } 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)) {
@@ -1332,6 +1384,15 @@ static void reg_from_run_to_done(struct vpu_subdev_data *data,
                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;
        }
@@ -1346,6 +1407,15 @@ static void reg_from_run_to_done(struct vpu_subdev_data *data,
                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 : {
@@ -1517,41 +1587,55 @@ static void try_set_reg(struct vpu_subdev_data *data)
        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);
@@ -2157,6 +2241,9 @@ static int vcodec_subdev_probe(struct platform_device *pdev,
                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);
@@ -2188,8 +2275,6 @@ static int vcodec_subdev_probe(struct platform_device *pdev,
        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);
@@ -2265,6 +2350,28 @@ static void vcodec_read_property(struct device_node *np,
                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);
 }
 
@@ -2287,6 +2394,7 @@ static void vcodec_init_drvdata(struct vpu_service_info *pservice)
        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);
 
@@ -2311,6 +2419,8 @@ static int vcodec_probe(struct platform_device *pdev)
 
        pr_info("probe device %s\n", dev_name(dev));
 
+       pservice->dev = dev;
+
        vcodec_read_property(np, pservice);
        vcodec_init_drvdata(pservice);
 
@@ -2321,8 +2431,6 @@ static int vcodec_probe(struct platform_device *pdev)
        else
                pservice->dev_id = VCODEC_DEVICE_ID_COMBO;
 
-       pservice->dev = dev;
-
        if (0 > vpu_get_clk(pservice))
                goto err;
 
@@ -2489,27 +2597,38 @@ static void get_hw_info(struct vpu_subdev_data *data)
        }
 }
 
+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);
@@ -2518,12 +2637,16 @@ static irqreturn_t vdpu_irq(int irq, void *dev_id)
        }
 
        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]);
                }
@@ -2585,6 +2708,10 @@ static irqreturn_t vepu_irq(int irq, void *dev_id)
 
        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);