From: Huang Jiachai Date: Fri, 5 Aug 2016 08:50:16 +0000 (+0800) Subject: video: rockchip: lcdc: add support dmc X-Git-Tag: firefly_0821_release~1665 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=3018fa691ec6a44b8f8d7dd1feceeda52fa86e38;p=firefly-linux-kernel-4.4.55.git video: rockchip: lcdc: add support dmc Register dmc notify after than dmc driver. Change-Id: I11c7daee1b4882da87d209854f0bda980c14551b Signed-off-by: Huang Jiachai Signed-off-by: Jianqun Xu --- diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c index e89098a7463b..17ade53a5788 100644 --- a/drivers/devfreq/rk3399_dmc.c +++ b/drivers/devfreq/rk3399_dmc.c @@ -27,6 +27,7 @@ #include #include +#include #include struct dram_timing { @@ -508,6 +509,9 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) data->dev = dev; platform_set_drvdata(pdev, data); + if (vop_register_dmc()) + dev_err(dev, "fail to register notify to vop.\n"); + return 0; } diff --git a/drivers/video/rockchip/lcdc/rk322x_lcdc.c b/drivers/video/rockchip/lcdc/rk322x_lcdc.c index c4af864426b2..708003dddf8e 100644 --- a/drivers/video/rockchip/lcdc/rk322x_lcdc.c +++ b/drivers/video/rockchip/lcdc/rk322x_lcdc.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -34,6 +36,7 @@ #include #include #include +#include #include "rk322x_lcdc.h" @@ -2176,16 +2179,45 @@ static void vop_layer_enable(struct vop_device *vop_dev, /* if no layer used,disable lcdc */ if (vop_dev->prop == EXTEND) { if (!vop_dev->atv_layer_cnt && !open) { + if (!wait_event_timeout(vop_dev->wait_dmc_queue, + !vop_dev->dmc_in_process, HZ / 5)) + dev_warn(vop_dev->dev, + "Timeout waiting for dmc when vop disable\n"); + + vop_dev->vop_switch_status = 1; vop_early_suspend(&vop_dev->driver); dev_info(vop_dev->dev, "no layer is used,go to standby!\n"); vop_dev->standby = 1; + + vop_dev->vop_switch_status = 0; + wake_up(&vop_dev->wait_vop_switch_queue); + /* + * if clsoe enxtend vop need to enable dmc again. + */ + if (vop_dev->devfreq) { + if (vop_dev->devfreq_event_dev) + devfreq_event_enable_edev(vop_dev->devfreq_event_dev); + devfreq_resume_device(vop_dev->devfreq); + } } else if (open) { vop_early_resume(&vop_dev->driver); + vop_dev->vop_switch_status = 0; + wake_up(&vop_dev->wait_vop_switch_queue); + /* if enable two vop, need to disable dmc */ + if (vop_dev->devfreq) { + if (vop_dev->devfreq_event_dev) + devfreq_event_disable_edev(vop_dev->devfreq_event_dev); + devfreq_suspend_device(vop_dev->devfreq); + } dev_info(vop_dev->dev, "wake up from standby!\n"); } + } else if (vop_dev->prop == PRMRY) { + if ((open) && (!vop_dev->atv_layer_cnt)) { + vop_dev->vop_switch_status = 0; + wake_up(&vop_dev->wait_vop_switch_queue); + } } - } static int vop_enable_irq(struct rk_lcdc_driver *dev_drv) @@ -2207,6 +2239,31 @@ static int vop_enable_irq(struct rk_lcdc_driver *dev_drv) return 0; } +static int dmc_notify(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct vop_device *vop = container_of(nb, struct vop_device, dmc_nb); + + if (event == DEVFREQ_PRECHANGE) { + + /* + * check if vop in enable or disable process, + * if yes, wait until it finish, use 200ms as + * timeout. + */ + if (!wait_event_timeout(vop->wait_vop_switch_queue, + !vop->vop_switch_status, HZ / 5)) + dev_warn(vop->dev, + "Timeout waiting for vop swtich status\n"); + vop->dmc_in_process = 1; + } else if (event == DEVFREQ_POSTCHANGE) { + vop->dmc_in_process = 0; + wake_up(&vop->wait_dmc_queue); + } + + return NOTIFY_OK; +} + static int vop_open(struct rk_lcdc_driver *dev_drv, int win_id, bool open) { @@ -2216,6 +2273,11 @@ static int vop_open(struct rk_lcdc_driver *dev_drv, int win_id, /* enable clk,when first layer open */ if ((open) && (!vop_dev->atv_layer_cnt)) { /* rockchip_set_system_status(sys_status); */ + if (!wait_event_timeout(vop_dev->wait_dmc_queue, + !vop_dev->dmc_in_process, HZ / 5)) + dev_warn(vop_dev->dev, + "Timeout waiting for dmc when vop enable\n"); + vop_dev->vop_switch_status = 1; vop_pre_init(dev_drv); vop_clk_enable(vop_dev); vop_enable_irq(dev_drv); @@ -4890,6 +4952,46 @@ static int vop_parse_dt(struct vop_device *vop_dev) return 0; } +static struct platform_device *rk322x_pdev; + +int vop_register_dmc(void) +{ + struct platform_device *pdev = rk322x_pdev; + struct vop_device *vop_dev; + struct device *dev = &pdev->dev; + struct devfreq *devfreq; + struct devfreq_event_dev *event_dev; + + if (!pdev) + return -ENODEV; + + vop_dev = platform_get_drvdata(pdev);; + if (!vop_dev) + return -ENODEV; + + dev = &pdev->dev; + devfreq = devfreq_get_devfreq_by_phandle(dev, 0); + if (IS_ERR(devfreq)) { + dev_err(vop_dev->dev, "fail to get devfreq for dmc\n"); + return -ENODEV; + } + + vop_dev->devfreq = devfreq; + vop_dev->dmc_nb.notifier_call = dmc_notify; + devfreq_register_notifier(vop_dev->devfreq, &vop_dev->dmc_nb, + DEVFREQ_TRANSITION_NOTIFIER); + + event_dev = devfreq_event_get_edev_by_phandle(vop_dev->devfreq->dev.parent, + 0); + if (IS_ERR(event_dev)) { + dev_err(vop_dev->dev, "fail to get edev for dmc\n"); + return -ENODEV; + } + + vop_dev->devfreq_event_dev = event_dev; + return 0; +} + static int vop_probe(struct platform_device *pdev) { struct vop_device *vop_dev = NULL; @@ -4998,6 +5100,11 @@ static int vop_probe(struct platform_device *pdev) vop_dev->data->win[3].property.feature &= ~SUPPORT_HW_EXIST; } + init_waitqueue_head(&vop_dev->wait_vop_switch_queue); + vop_dev->vop_switch_status = 0; + init_waitqueue_head(&vop_dev->wait_dmc_queue); + vop_dev->dmc_in_process = 0; + ret = rk_fb_register(dev_drv, vop_dev->data->win, vop_dev->id); if (ret < 0) { dev_err(dev, "register fb for lcdc%d failed!\n", vop_dev->id); @@ -5007,6 +5114,8 @@ static int vop_probe(struct platform_device *pdev) dev_info(dev, "lcdc%d probe ok, iommu %s\n", vop_dev->id, dev_drv->iommu_enabled ? "enabled" : "disabled"); + rk322x_pdev = pdev; + return 0; } diff --git a/drivers/video/rockchip/lcdc/rk322x_lcdc.h b/drivers/video/rockchip/lcdc/rk322x_lcdc.h index a3c34889cd38..09d900a47c56 100644 --- a/drivers/video/rockchip/lcdc/rk322x_lcdc.h +++ b/drivers/video/rockchip/lcdc/rk322x_lcdc.h @@ -1432,6 +1432,13 @@ struct vop_device { /* lock vop irq reg */ spinlock_t irq_lock; + struct devfreq *devfreq; + struct devfreq_event_dev *devfreq_event_dev; + struct notifier_block dmc_nb; + int dmc_in_process; + int vop_switch_status; + wait_queue_head_t wait_dmc_queue; + wait_queue_head_t wait_vop_switch_queue; }; static inline void vop_writel(struct vop_device *vop_dev, u32 offset, u32 v) diff --git a/include/soc/rockchip/rkfb_dmc.h b/include/soc/rockchip/rkfb_dmc.h new file mode 100644 index 000000000000..28b2e3979b6b --- /dev/null +++ b/include/soc/rockchip/rkfb_dmc.h @@ -0,0 +1,10 @@ +/* + * Rockchip devfb driver will probe earlier than devfreq, so it needs to register + * dmc_notify after than rk3399 dmc driver. +*/ + +#if defined(CONFIG_LCDC_RK322X) +int vop_register_dmc(void); +#else +static inline int vop_register_dmc(void) { return 0;}; +#endif