X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=drivers%2Fvideo%2Frockchip%2Fvcodec%2Fvcodec_service.c;h=5fba743e63b4775eccaf60b0c79029def6ed35f8;hb=fa9bd02d2f4b6639914795ec605cb96ac9e2589c;hp=69e04b84df8b459040da8054ec52761c751f2b6e;hpb=f14530d44cee134be240d851925fffb487e509c0;p=firefly-linux-kernel-4.4.55.git diff --git a/drivers/video/rockchip/vcodec/vcodec_service.c b/drivers/video/rockchip/vcodec/vcodec_service.c index 69e04b84df8b..5fba743e63b4 100644 --- a/drivers/video/rockchip/vcodec/vcodec_service.c +++ b/drivers/video/rockchip/vcodec/vcodec_service.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -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; }