drivers: video: rockchip: vcodec_dma_map_sg maybe fail
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / vcodec / vcodec_service.c
index 69e04b84df8b459040da8054ec52761c751f2b6e..5fba743e63b4775eccaf60b0c79029def6ed35f8 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/uaccess.h>
 #include <linux/debugfs.h>
 #include <linux/pm_runtime.h>
+#include <linux/iopoll.h>
 
 #include <linux/rockchip/cru.h>
 #include <linux/rockchip/pmu.h>
@@ -353,11 +354,14 @@ struct vpu_subdev_data {
 
        struct device *mmu_dev;
        struct vcodec_iommu_info *iommu_info;
+       struct work_struct set_work;
 };
 
 struct vpu_service_info {
        struct wake_lock wake_lock;
        struct delayed_work power_off_work;
+       struct wake_lock set_wake_lock;
+       struct workqueue_struct *set_workq;
        ktime_t last; /* record previous power-on time */
        /* vpu service structure global lock */
        struct mutex lock;
@@ -476,8 +480,23 @@ static void vcodec_enter_mode(struct vpu_subdev_data *data)
        struct vpu_service_info *pservice = data->pservice;
        struct vpu_subdev_data *subdata, *n;
 
-       if (pservice->subcnt < 2)
+       if (pservice->subcnt < 2) {
+               if (data->mmu_dev && !test_bit(MMU_ACTIVATED, &data->state)) {
+                       set_bit(MMU_ACTIVATED, &data->state);
+
+                       if (atomic_read(&pservice->enabled)) {
+                               if (vcodec_iommu_attach(data->iommu_info))
+                                       dev_err(data->dev,
+                                               "vcodec service attach failed\n"
+                                               );
+                               else
+                                       BUG_ON(
+                                              !atomic_read(&pservice->enabled)
+                                              );
+                       }
+               }
                return;
+       }
 
        if (pservice->curr_mode == data->mode)
                return;
@@ -488,6 +507,7 @@ static void vcodec_enter_mode(struct vpu_subdev_data *data)
                if (data != subdata && subdata->mmu_dev &&
                    test_bit(MMU_ACTIVATED, &subdata->state)) {
                        clear_bit(MMU_ACTIVATED, &subdata->state);
+                       vcodec_iommu_detach(subdata->iommu_info);
                }
        }
        bits = 1 << pservice->mode_bit;
@@ -533,7 +553,9 @@ static void vcodec_enter_mode(struct vpu_subdev_data *data)
 #endif
        if (data->mmu_dev && !test_bit(MMU_ACTIVATED, &data->state)) {
                set_bit(MMU_ACTIVATED, &data->state);
-               if (!atomic_read(&pservice->enabled))
+               if (atomic_read(&pservice->enabled))
+                       vcodec_iommu_attach(data->iommu_info);
+               else
                        /* FIXME BUG_ON should not be used in mass produce */
                        BUG_ON(!atomic_read(&pservice->enabled));
        }
@@ -660,10 +682,10 @@ static void vpu_reset(struct vpu_subdev_data *data)
 
        _vpu_reset(data);
        if (data->mmu_dev && test_bit(MMU_ACTIVATED, &data->state)) {
+               clear_bit(MMU_ACTIVATED, &data->state);
                if (atomic_read(&pservice->enabled)) {
                        /* Need to reset iommu */
                        vcodec_iommu_detach(data->iommu_info);
-                       vcodec_iommu_attach(data->iommu_info);
                } else {
                        /* FIXME BUG_ON should not be used in mass produce */
                        BUG_ON(!atomic_read(&pservice->enabled));
@@ -698,7 +720,7 @@ static void vpu_service_clear(struct vpu_subdev_data *data)
        struct vpu_service_info *pservice = data->pservice;
 
        list_for_each_entry_safe(reg, n, &pservice->waiting, status_link) {
-               reg_deinit(data, reg);
+               reg_deinit(reg->data, reg);
        }
 
        /* wake up session wait event to prevent the timeout hw reset
@@ -798,13 +820,8 @@ static void vpu_service_power_on(struct vpu_subdev_data *data,
                pservice->last = now;
        }
        ret = atomic_add_unless(&pservice->enabled, 1, 1);
-       if (!ret) {
-               if (data->mmu_dev && !test_bit(MMU_ACTIVATED, &data->state)) {
-                       set_bit(MMU_ACTIVATED, &data->state);
-                       vcodec_iommu_attach(data->iommu_info);
-               }
+       if (!ret)
                return;
-       }
 
        dev_dbg(pservice->dev, "power on\n");
 
@@ -828,18 +845,6 @@ static void vpu_service_power_on(struct vpu_subdev_data *data,
 #endif
        pm_runtime_get_sync(pservice->dev);
 
-       if (data->mmu_dev && !test_bit(MMU_ACTIVATED, &data->state)) {
-               set_bit(MMU_ACTIVATED, &data->state);
-               if (atomic_read(&pservice->enabled))
-                       vcodec_iommu_attach(data->iommu_info);
-               else
-                       /*
-                        * FIXME BUG_ON should not be used in mass
-                        * produce.
-                        */
-                       BUG_ON(!atomic_read(&pservice->enabled));
-       }
-
        udelay(5);
        atomic_add(1, &pservice->power_on_cnt);
        wake_lock(&pservice->wake_lock);
@@ -1074,8 +1079,6 @@ static int vcodec_bufid_to_iova(struct vpu_subdev_data *data,
                        if (pps_info_count) {
                                u8 *pps;
 
-                               mutex_lock(&pservice->lock);
-
                                pps = vcodec_iommu_map_kernel
                                        (data->iommu_info, session, hdl);
 
@@ -1090,7 +1093,6 @@ static int vcodec_bufid_to_iova(struct vpu_subdev_data *data,
 
                                vcodec_iommu_unmap_kernel
                                        (data->iommu_info, session, hdl);
-                               mutex_unlock(&pservice->lock);
                        }
                }
 
@@ -1470,7 +1472,8 @@ static void reg_copy_to_hw(struct vpu_subdev_data *data, struct vpu_reg *reg)
 
                pservice->reg_codec = reg;
 
-               vpu_debug(DEBUG_TASK_INFO, "reg: base %3d end %d en %2d mask: en %x gate %x\n",
+               vpu_debug(DEBUG_TASK_INFO,
+                         "reg: base %3d end %d en %2d mask: en %x gate %x\n",
                          base, end, reg_en, enable_mask, gating_mask);
 
                VEPU_CLEAN_CACHE(dst);
@@ -1506,7 +1509,8 @@ static void reg_copy_to_hw(struct vpu_subdev_data *data, struct vpu_reg *reg)
 
                pservice->reg_codec = reg;
 
-               vpu_debug(DEBUG_TASK_INFO, "reg: base %3d end %d en %2d mask: en %x gate %x\n",
+               vpu_debug(DEBUG_TASK_INFO,
+                         "reg: base %3d end %d en %2d mask: en %x gate %x\n",
                          base, end, reg_en, enable_mask, gating_mask);
 
                VDPU_CLEAN_CACHE(dst);
@@ -1544,7 +1548,8 @@ static void reg_copy_to_hw(struct vpu_subdev_data *data, struct vpu_reg *reg)
 
                pservice->reg_pproc = reg;
 
-               vpu_debug(DEBUG_TASK_INFO, "reg: base %3d end %d en %2d mask: en %x gate %x\n",
+               vpu_debug(DEBUG_TASK_INFO,
+                         "reg: base %3d end %d en %2d mask: en %x gate %x\n",
                          base, end, reg_en, enable_mask, gating_mask);
 
                if (debug & DEBUG_SET_REG)
@@ -1570,7 +1575,8 @@ static void reg_copy_to_hw(struct vpu_subdev_data *data, struct vpu_reg *reg)
                pservice->reg_codec = reg;
                pservice->reg_pproc = reg;
 
-               vpu_debug(DEBUG_TASK_INFO, "reg: base %3d end %d en %2d mask: en %x gate %x\n",
+               vpu_debug(DEBUG_TASK_INFO,
+                         "reg: base %3d end %d en %2d mask: en %x gate %x\n",
                          base, end, reg_en, enable_mask, gating_mask);
 
                /* VDPU_SOFT_RESET(dst); */
@@ -1623,6 +1629,8 @@ static void try_set_reg(struct vpu_subdev_data *data)
                struct vpu_reg *reg = list_entry(pservice->waiting.next,
                                struct vpu_reg, status_link);
 
+               vpu_service_power_on(data, pservice);
+
                if (change_able || !reset_request) {
                        switch (reg->type) {
                        case VPU_ENC: {
@@ -1683,6 +1691,18 @@ static void try_set_reg(struct vpu_subdev_data *data)
        vpu_debug_leave();
 }
 
+static void vpu_set_register_work(struct work_struct *work_s)
+{
+       struct vpu_subdev_data *data = container_of(work_s,
+                                                   struct vpu_subdev_data,
+                                                   set_work);
+       struct vpu_service_info *pservice = data->pservice;
+
+       mutex_lock(&pservice->lock);
+       try_set_reg(data);
+       mutex_unlock(&pservice->lock);
+}
+
 static int return_reg(struct vpu_subdev_data *data,
                      struct vpu_reg *reg, u32 __user *dst)
 {
@@ -1767,8 +1787,6 @@ static long vpu_service_ioctl(struct file *filp, unsigned int cmd,
                struct vpu_request req;
                struct vpu_reg *reg;
 
-               vpu_service_power_on(data, pservice);
-
                vpu_debug(DEBUG_IOCTL, "pid %d set reg type %d\n",
                          session->pid, session->type);
                if (copy_from_user(&req, (void __user *)arg,
@@ -1781,9 +1799,7 @@ static long vpu_service_ioctl(struct file *filp, unsigned int cmd,
                if (NULL == reg) {
                        return -EFAULT;
                } else {
-                       mutex_lock(&pservice->lock);
-                       try_set_reg(data);
-                       mutex_unlock(&pservice->lock);
+                       queue_work(pservice->set_workq, &data->set_work);
                }
        } break;
        case VPU_IOC_GET_REG: {
@@ -1791,8 +1807,6 @@ static long vpu_service_ioctl(struct file *filp, unsigned int cmd,
                struct vpu_reg *reg;
                int ret;
 
-               vpu_service_power_on(data, pservice);
-
                vpu_debug(DEBUG_IOCTL, "pid %d get reg type %d\n",
                          session->pid, session->type);
                if (copy_from_user(&req, (void __user *)arg,
@@ -1833,15 +1847,15 @@ static long vpu_service_ioctl(struct file *filp, unsigned int cmd,
                                           &pservice->total_running);
                                dev_err(pservice->dev,
                                        "%d task is running but not return, reset hardware...",
-                                      task_running);
+                                       task_running);
                                vpu_reset(data);
                                dev_err(pservice->dev, "done\n");
                        }
                        vpu_service_session_clear(data, session);
                        mutex_unlock(&pservice->lock);
+
                        return ret;
                }
-
                mutex_lock(&pservice->lock);
                reg = list_entry(session->done.next,
                                 struct vpu_reg, session_link);
@@ -1918,8 +1932,6 @@ static long compat_vpu_service_ioctl(struct file *filp, unsigned int cmd,
                struct compat_vpu_request req;
                struct vpu_reg *reg;
 
-               vpu_service_power_on(data, pservice);
-
                vpu_debug(DEBUG_IOCTL, "compat set reg type %d\n",
                          session->type);
                if (copy_from_user(&req, compat_ptr((compat_uptr_t)arg),
@@ -1932,9 +1944,7 @@ static long compat_vpu_service_ioctl(struct file *filp, unsigned int cmd,
                if (NULL == reg) {
                        return -EFAULT;
                } else {
-                       mutex_lock(&pservice->lock);
-                       try_set_reg(data);
-                       mutex_unlock(&pservice->lock);
+                       queue_work(pservice->set_workq, &data->set_work);
                }
        } break;
        case COMPAT_VPU_IOC_GET_REG: {
@@ -1942,8 +1952,6 @@ static long compat_vpu_service_ioctl(struct file *filp, unsigned int cmd,
                struct vpu_reg *reg;
                int ret;
 
-               vpu_service_power_on(data, pservice);
-
                vpu_debug(DEBUG_IOCTL, "compat get reg type %d\n",
                          session->type);
                if (copy_from_user(&req, compat_ptr((compat_uptr_t)arg),
@@ -2100,7 +2108,6 @@ static int vpu_service_release(struct inode *inode, struct file *filp)
        }
        wake_up(&session->wait);
 
-       vpu_service_power_on(data, pservice);
        mutex_lock(&pservice->lock);
        /* remove this filp from the asynchronusly notified filp's */
        list_del_init(&session->list_session);
@@ -2261,6 +2268,8 @@ static int vcodec_subdev_probe(struct platform_device *pdev,
 
        data->pservice = pservice;
        data->dev = dev;
+
+       INIT_WORK(&data->set_work, vpu_set_register_work);
        of_property_read_u32(np, "dev_mode", (u32 *)&data->mode);
 
        if (pservice->reg_base == 0) {
@@ -2314,11 +2323,18 @@ static int vcodec_subdev_probe(struct platform_device *pdev,
        clear_bit(MMU_ACTIVATED, &data->state);
        vpu_service_power_on(data, pservice);
 
+       of_property_read_u32(np, "allocator", (u32 *)&pservice->alloc_type);
+       data->iommu_info = vcodec_iommu_info_create(dev, data->mmu_dev,
+                                                   pservice->alloc_type);
+       dev_info(dev, "allocator is %s\n", pservice->alloc_type == 1 ? "drm" :
+               (pservice->alloc_type == 2 ? "ion" : "null"));
+       vcodec_enter_mode(data);
        ret = vpu_service_check_hw(data);
        if (ret < 0) {
                vpu_err("error: hw info check faild\n");
                goto err;
        }
+       vcodec_exit_mode(data);
 
        hw_info = data->hw_info;
        regs = (u8 *)data->regs;
@@ -2364,16 +2380,9 @@ static int vcodec_subdev_probe(struct platform_device *pdev,
        atomic_set(&data->enc_dev.irq_count_codec, 0);
        atomic_set(&data->enc_dev.irq_count_pp, 0);
 
-       vcodec_enter_mode(data);
-       of_property_read_u32(np, "allocator", (u32 *)&pservice->alloc_type);
-       data->iommu_info = vcodec_iommu_info_create(dev, data->mmu_dev,
-                                                   pservice->alloc_type);
-       dev_info(dev, "allocator is %s\n", pservice->alloc_type == 1 ? "drm" :
-               (pservice->alloc_type == 2 ? "ion" : "null"));
        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);
        if (ret) {
@@ -2548,6 +2557,12 @@ static int vcodec_probe(struct platform_device *pdev)
                return -ENOMEM;
        pservice->dev = dev;
 
+       pservice->set_workq = create_singlethread_workqueue("vcodec");
+       if (!pservice->set_workq) {
+               dev_err(dev, "failed to create workqueue\n");
+               return -ENOMEM;
+       }
+
        driver_data = vcodec_get_drv_data(pdev);
        if (!driver_data)
                return -EINVAL;
@@ -2594,6 +2609,13 @@ static int vcodec_probe(struct platform_device *pdev)
        pm_runtime_enable(dev);
 
        if (of_property_read_bool(np, "subcnt")) {
+               struct vpu_subdev_data *data = NULL;
+
+               data = devm_kzalloc(dev, sizeof(struct vpu_subdev_data),
+                                   GFP_KERNEL);
+               if (!data)
+                       return -ENOMEM;
+
                for (i = 0; i < pservice->subcnt; i++) {
                        struct device_node *sub_np;
                        struct platform_device *sub_pdev;
@@ -2603,6 +2625,8 @@ static int vcodec_probe(struct platform_device *pdev)
 
                        vcodec_subdev_probe(sub_pdev, pservice);
                }
+               data->pservice = pservice;
+               platform_set_drvdata(pdev, data);
        } else {
                vcodec_subdev_probe(pdev, pservice);
        }
@@ -2616,6 +2640,7 @@ static int vcodec_probe(struct platform_device *pdev)
 err:
        dev_info(dev, "init failed\n");
        vpu_service_power_off(pservice);
+       destroy_workqueue(pservice->set_workq);
        wake_lock_destroy(&pservice->wake_lock);
 
        return ret;
@@ -2636,6 +2661,10 @@ static void vcodec_shutdown(struct platform_device *pdev)
 {
        struct vpu_subdev_data *data = platform_get_drvdata(pdev);
        struct vpu_service_info *pservice = data->pservice;
+       struct device_node *np = pdev->dev.of_node;
+       int val;
+       int ret;
+       int i;
 
        dev_info(&pdev->dev, "vcodec shutdown");
 
@@ -2643,11 +2672,27 @@ static void vcodec_shutdown(struct platform_device *pdev)
        atomic_set(&pservice->service_on, 0);
        mutex_unlock(&pservice->shutdown_lock);
 
-       vcodec_exit_mode(data);
+       ret = readx_poll_timeout(atomic_read,
+                                &pservice->total_running,
+                                val, val == 0, 20000, 200000);
+       if (ret == -ETIMEDOUT)
+               dev_err(&pdev->dev, "wait total running time out\n");
 
-       vpu_service_power_on(data, pservice);
+       vcodec_exit_mode(data);
        vpu_service_clear(data);
-       vcodec_subdev_remove(data);
+       if (of_property_read_bool(np, "subcnt")) {
+               for (i = 0; i < pservice->subcnt; i++) {
+                       struct device_node *sub_np;
+                       struct platform_device *sub_pdev;
+
+                       sub_np = of_parse_phandle(np, "rockchip,sub", i);
+                       sub_pdev = of_find_device_by_node(sub_np);
+                       vcodec_subdev_remove(platform_get_drvdata(sub_pdev));
+               }
+
+       } else {
+               vcodec_subdev_remove(data);
+       }
 
        pm_runtime_disable(&pdev->dev);
 }
@@ -2745,7 +2790,8 @@ static void get_hw_info(struct vpu_subdev_data *data)
                }
 
                pservice->auto_freq = true;
-               vpu_debug(DEBUG_EXTRA_INFO, "vpu_service set to auto frequency mode\n");
+               vpu_debug(DEBUG_EXTRA_INFO,
+                         "vpu_service set to auto frequency mode\n");
                atomic_set(&pservice->freq_status, VPU_FREQ_BUT);
 
                pservice->bug_dec_addr = of_machine_is_compatible
@@ -2782,7 +2828,8 @@ static irqreturn_t vdpu_irq(int irq, void *dev_id)
        raw_status = readl_relaxed(dev->regs + task->reg_irq);
        dec_status = raw_status;
 
-       vpu_debug(DEBUG_TASK_INFO, "vdpu_irq reg %d status %x mask: irq %x ready %x error %0x\n",
+       vpu_debug(DEBUG_TASK_INFO,
+                 "vdpu_irq reg %d status %x mask: irq %x ready %x error %0x\n",
                  task->reg_irq, dec_status,
                  task->irq_mask, task->ready_mask, task->error_mask);
 
@@ -2865,7 +2912,8 @@ static irqreturn_t vdpu_isr(int irq, void *dev_id)
                else
                        reg_from_run_to_done(data, pservice->reg_pproc);
        }
-       try_set_reg(data);
+
+       queue_work(pservice->set_workq, &data->set_work);
        mutex_unlock(&pservice->lock);
        return IRQ_HANDLED;
 }
@@ -2880,7 +2928,8 @@ static irqreturn_t vepu_irq(int irq, void *dev_id)
 
        irq_status = readl_relaxed(dev->regs + task->reg_irq);
 
-       vpu_debug(DEBUG_TASK_INFO, "vepu_irq reg %d status %x mask: irq %x ready %x error %0x\n",
+       vpu_debug(DEBUG_TASK_INFO,
+                 "vepu_irq reg %d status %x mask: irq %x ready %x error %0x\n",
                  task->reg_irq, irq_status,
                  task->irq_mask, task->ready_mask, task->error_mask);
 
@@ -2922,8 +2971,9 @@ static irqreturn_t vepu_isr(int irq, void *dev_id)
                else
                        reg_from_run_to_done(data, pservice->reg_codec);
        }
-       try_set_reg(data);
+       queue_work(pservice->set_workq, &data->set_work);
        mutex_unlock(&pservice->lock);
+
        return IRQ_HANDLED;
 }