diff options
Diffstat (limited to 'drivers/gpu/drm/rockchip')
-rw-r--r-- | drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 103 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/cdn-dp-core.c | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 30 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/inno_hdmi.c | 25 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 26 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 125 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_gem.h | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 92 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_psr.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 109 |
12 files changed, 339 insertions, 200 deletions
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index 1262120a3834..3e8bf79bea58 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -71,40 +71,22 @@ struct rockchip_dp_device { struct regmap *grf; struct reset_control *rst; - struct work_struct psr_work; - struct mutex psr_lock; - unsigned int psr_state; - const struct rockchip_dp_chip_data *data; + struct analogix_dp_device *adp; struct analogix_dp_plat_data plat_data; }; static void analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled) { struct rockchip_dp_device *dp = to_dp(encoder); + int ret; - if (!analogix_dp_psr_supported(dp->dev)) + if (!analogix_dp_psr_enabled(dp->adp)) return; DRM_DEV_DEBUG(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit"); - mutex_lock(&dp->psr_lock); - if (enabled) - dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE; - else - dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE; - - schedule_work(&dp->psr_work); - mutex_unlock(&dp->psr_lock); -} - -static void analogix_dp_psr_work(struct work_struct *work) -{ - struct rockchip_dp_device *dp = - container_of(work, typeof(*dp), psr_work); - int ret; - ret = rockchip_drm_wait_vact_end(dp->encoder.crtc, PSR_WAIT_LINE_FLAG_TIMEOUT_MS); if (ret) { @@ -112,12 +94,10 @@ static void analogix_dp_psr_work(struct work_struct *work) return; } - mutex_lock(&dp->psr_lock); - if (dp->psr_state == EDP_VSC_PSR_STATE_ACTIVE) - analogix_dp_enable_psr(dp->dev); + if (enabled) + analogix_dp_enable_psr(dp->adp); else - analogix_dp_disable_psr(dp->dev); - mutex_unlock(&dp->psr_lock); + analogix_dp_disable_psr(dp->adp); } static int rockchip_dp_pre_init(struct rockchip_dp_device *dp) @@ -134,8 +114,6 @@ static int rockchip_dp_poweron(struct analogix_dp_plat_data *plat_data) struct rockchip_dp_device *dp = to_dp(plat_data); int ret; - cancel_work_sync(&dp->psr_work); - ret = clk_prepare_enable(dp->pclk); if (ret < 0) { DRM_DEV_ERROR(dp->dev, "failed to enable pclk %d\n", ret); @@ -149,12 +127,17 @@ static int rockchip_dp_poweron(struct analogix_dp_plat_data *plat_data) return ret; } - return 0; + return rockchip_drm_psr_activate(&dp->encoder); } static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data) { struct rockchip_dp_device *dp = to_dp(plat_data); + int ret; + + ret = rockchip_drm_psr_deactivate(&dp->encoder); + if (ret != 0) + return ret; clk_disable_unprepare(dp->pclk); @@ -258,13 +241,8 @@ static struct drm_encoder_helper_funcs rockchip_dp_encoder_helper_funcs = { .atomic_check = rockchip_dp_drm_encoder_atomic_check, }; -static void rockchip_dp_drm_encoder_destroy(struct drm_encoder *encoder) -{ - drm_encoder_cleanup(encoder); -} - static struct drm_encoder_funcs rockchip_dp_encoder_funcs = { - .destroy = rockchip_dp_drm_encoder_destroy, + .destroy = drm_encoder_cleanup, }; static int rockchip_dp_of_probe(struct rockchip_dp_device *dp) @@ -334,13 +312,6 @@ static int rockchip_dp_bind(struct device *dev, struct device *master, struct drm_device *drm_dev = data; int ret; - /* - * Just like the probe function said, we don't need the - * device drvrate anymore, we should leave the charge to - * analogix dp driver, set the device drvdata to NULL. - */ - dev_set_drvdata(dev, NULL); - dp_data = of_device_get_match_data(dev); if (!dp_data) return -ENODEV; @@ -361,13 +332,22 @@ static int rockchip_dp_bind(struct device *dev, struct device *master, dp->plat_data.power_off = rockchip_dp_powerdown; dp->plat_data.get_modes = rockchip_dp_get_modes; - mutex_init(&dp->psr_lock); - dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE; - INIT_WORK(&dp->psr_work, analogix_dp_psr_work); + ret = rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set); + if (ret < 0) + goto err_cleanup_encoder; - rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set); + dp->adp = analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data); + if (IS_ERR(dp->adp)) { + ret = PTR_ERR(dp->adp); + goto err_unreg_psr; + } - return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data); + return 0; +err_unreg_psr: + rockchip_drm_psr_unregister(&dp->encoder); +err_cleanup_encoder: + dp->encoder.funcs->destroy(&dp->encoder); + return ret; } static void rockchip_dp_unbind(struct device *dev, struct device *master, @@ -375,9 +355,9 @@ static void rockchip_dp_unbind(struct device *dev, struct device *master, { struct rockchip_dp_device *dp = dev_get_drvdata(dev); + analogix_dp_unbind(dp->adp); rockchip_drm_psr_unregister(&dp->encoder); - - analogix_dp_unbind(dev, master, data); + dp->encoder.funcs->destroy(&dp->encoder); } static const struct component_ops rockchip_dp_component_ops = { @@ -407,11 +387,6 @@ static int rockchip_dp_probe(struct platform_device *pdev) if (ret < 0) return ret; - /* - * We just use the drvdata until driver run into component - * add function, and then we would set drvdata to null, so - * that analogix dp driver could take charge of the drvdata. - */ platform_set_drvdata(pdev, dp); return component_add(dev, &rockchip_dp_component_ops); @@ -424,10 +399,26 @@ static int rockchip_dp_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int rockchip_dp_suspend(struct device *dev) +{ + struct rockchip_dp_device *dp = dev_get_drvdata(dev); + + return analogix_dp_suspend(dp->adp); +} + +static int rockchip_dp_resume(struct device *dev) +{ + struct rockchip_dp_device *dp = dev_get_drvdata(dev); + + return analogix_dp_resume(dp->adp); +} +#endif + static const struct dev_pm_ops rockchip_dp_pm_ops = { #ifdef CONFIG_PM_SLEEP - .suspend = analogix_dp_suspend, - .resume_early = analogix_dp_resume, + .suspend = rockchip_dp_suspend, + .resume_early = rockchip_dp_resume, #endif }; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index ec999d9f15f6..c6fbdcd87c16 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -43,8 +43,6 @@ #define GRF_SOC_CON9 0x6224 #define DP_SEL_VOP_LIT BIT(12) #define GRF_SOC_CON26 0x6268 -#define UPHY_SEL_BIT 3 -#define UPHY_SEL_MASK BIT(19) #define DPTX_HPD_SEL (3 << 12) #define DPTX_HPD_DEL (2 << 12) #define DPTX_HPD_SEL_MASK (3 << 28) @@ -394,11 +392,6 @@ static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port) union extcon_property_value property; int ret; - ret = cdn_dp_grf_write(dp, GRF_SOC_CON26, - (port->id << UPHY_SEL_BIT) | UPHY_SEL_MASK); - if (ret) - return ret; - if (!port->phy_enabled) { ret = phy_power_on(port->phy); if (ret) { diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c index b1fe0639227e..d53d5a09547f 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c @@ -1202,9 +1202,6 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master, return ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - dsi->base = devm_ioremap_resource(dev, res); if (IS_ERR(dsi->base)) return PTR_ERR(dsi->base); @@ -1305,8 +1302,8 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master, err_mipi_dsi_host: mipi_dsi_host_unregister(&dsi->dsi_host); err_cleanup: - drm_encoder_cleanup(&dsi->encoder); - drm_connector_cleanup(&dsi->connector); + dsi->connector.funcs->destroy(&dsi->connector); + dsi->encoder.funcs->destroy(&dsi->encoder); err_pllref: clk_disable_unprepare(dsi->pllref_clk); return ret; @@ -1319,6 +1316,10 @@ static void dw_mipi_dsi_unbind(struct device *dev, struct device *master, mipi_dsi_host_unregister(&dsi->dsi_host); pm_runtime_disable(dev); + + dsi->connector.funcs->destroy(&dsi->connector); + dsi->encoder.funcs->destroy(&dsi->encoder); + clk_disable_unprepare(dsi->pllref_clk); } diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 1eb02a82fd91..11309a2a4e43 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -48,6 +48,7 @@ struct rockchip_hdmi { const struct rockchip_hdmi_chip_data *chip_data; struct clk *vpll_clk; struct clk *grf_clk; + struct dw_hdmi *hdmi; }; #define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x) @@ -164,7 +165,6 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = { static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) { struct device_node *np = hdmi->dev->of_node; - int ret; hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); if (IS_ERR(hdmi->regmap)) { @@ -192,13 +192,6 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) return PTR_ERR(hdmi->grf_clk); } - ret = clk_prepare_enable(hdmi->vpll_clk); - if (ret) { - DRM_DEV_ERROR(hdmi->dev, - "Failed to enable HDMI vpll: %d\n", ret); - return ret; - } - return 0; } @@ -373,18 +366,30 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, return ret; } + ret = clk_prepare_enable(hdmi->vpll_clk); + if (ret) { + DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI vpll: %d\n", + ret); + return ret; + } + drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); - ret = dw_hdmi_bind(pdev, encoder, plat_data); + platform_set_drvdata(pdev, hdmi); + + hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data); /* * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(), * which would have called the encoder cleanup. Do it manually. */ - if (ret) + if (IS_ERR(hdmi->hdmi)) { + ret = PTR_ERR(hdmi->hdmi); drm_encoder_cleanup(encoder); + clk_disable_unprepare(hdmi->vpll_clk); + } return ret; } @@ -392,7 +397,10 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master, void *data) { - return dw_hdmi_unbind(dev); + struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); + + dw_hdmi_unbind(hdmi->hdmi); + clk_disable_unprepare(hdmi->vpll_clk); } static const struct component_ops dw_hdmi_rockchip_ops = { diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c index fab30927a889..88d0774c97bd 100644 --- a/drivers/gpu/drm/rockchip/inno_hdmi.c +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c @@ -831,9 +831,6 @@ static int inno_hdmi_bind(struct device *dev, struct device *master, hdmi->drm_dev = drm; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iores) - return -ENXIO; - hdmi->regs = devm_ioremap_resource(dev, iores); if (IS_ERR(hdmi->regs)) return PTR_ERR(hdmi->regs); @@ -852,8 +849,10 @@ static int inno_hdmi_bind(struct device *dev, struct device *master, } irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + if (irq < 0) { + ret = irq; + goto err_disable_clk; + } inno_hdmi_reset(hdmi); @@ -861,7 +860,7 @@ static int inno_hdmi_bind(struct device *dev, struct device *master, if (IS_ERR(hdmi->ddc)) { ret = PTR_ERR(hdmi->ddc); hdmi->ddc = NULL; - return ret; + goto err_disable_clk; } /* @@ -875,7 +874,7 @@ static int inno_hdmi_bind(struct device *dev, struct device *master, ret = inno_hdmi_register(drm, hdmi); if (ret) - return ret; + goto err_put_adapter; dev_set_drvdata(dev, hdmi); @@ -885,7 +884,17 @@ static int inno_hdmi_bind(struct device *dev, struct device *master, ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq, inno_hdmi_irq, IRQF_SHARED, dev_name(dev), hdmi); + if (ret < 0) + goto err_cleanup_hdmi; + return 0; +err_cleanup_hdmi: + hdmi->connector.funcs->destroy(&hdmi->connector); + hdmi->encoder.funcs->destroy(&hdmi->encoder); +err_put_adapter: + i2c_put_adapter(hdmi->ddc); +err_disable_clk: + clk_disable_unprepare(hdmi->pclk); return ret; } @@ -897,8 +906,8 @@ static void inno_hdmi_unbind(struct device *dev, struct device *master, hdmi->connector.funcs->destroy(&hdmi->connector); hdmi->encoder.funcs->destroy(&hdmi->encoder); - clk_disable_unprepare(hdmi->pclk); i2c_put_adapter(hdmi->ddc); + clk_disable_unprepare(hdmi->pclk); } static const struct component_ops inno_hdmi_ops = { diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index d85431400a0d..f814d37b1db2 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -134,7 +134,7 @@ static int rockchip_drm_bind(struct device *dev) drm_dev->dev_private = private; INIT_LIST_HEAD(&private->psr_list); - spin_lock_init(&private->psr_list_lock); + mutex_init(&private->psr_list_lock); ret = rockchip_drm_init_iommu(drm_dev); if (ret) @@ -230,6 +230,7 @@ static struct drm_driver rockchip_drm_driver = { .gem_prime_import = drm_gem_prime_import, .gem_prime_export = drm_gem_prime_export, .gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table, + .gem_prime_import_sg_table = rockchip_gem_prime_import_sg_table, .gem_prime_vmap = rockchip_gem_prime_vmap, .gem_prime_vunmap = rockchip_gem_prime_vunmap, .gem_prime_mmap = rockchip_gem_mmap_buf, @@ -313,6 +314,14 @@ static int compare_dev(struct device *dev, void *data) return dev == (struct device *)data; } +static void rockchip_drm_match_remove(struct device *dev) +{ + struct device_link *link; + + list_for_each_entry(link, &dev->links.consumers, s_node) + device_link_del(link); +} + static struct component_match *rockchip_drm_match_add(struct device *dev) { struct component_match *match = NULL; @@ -330,10 +339,15 @@ static struct component_match *rockchip_drm_match_add(struct device *dev) if (!d) break; + + device_link_add(dev, d, DL_FLAG_STATELESS); component_match_add(dev, &match, compare_dev, d); } while (true); } + if (IS_ERR(match)) + rockchip_drm_match_remove(dev); + return match ?: ERR_PTR(-ENODEV); } @@ -410,13 +424,21 @@ static int rockchip_drm_platform_probe(struct platform_device *pdev) if (IS_ERR(match)) return PTR_ERR(match); - return component_master_add_with_match(dev, &rockchip_drm_ops, match); + ret = component_master_add_with_match(dev, &rockchip_drm_ops, match); + if (ret < 0) { + rockchip_drm_match_remove(dev); + return ret; + } + + return 0; } static int rockchip_drm_platform_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &rockchip_drm_ops); + rockchip_drm_match_remove(&pdev->dev); + return 0; } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 498dfbc52cec..9c064a40458b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -55,7 +55,7 @@ struct rockchip_drm_private { struct mutex mm_lock; struct drm_mm mm; struct list_head psr_list; - spinlock_t psr_list_lock; + struct mutex psr_list_lock; }; int rockchip_drm_dma_attach_device(struct drm_device *drm_dev, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index 1d9655576b6e..074db7a92809 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -16,6 +16,8 @@ #include <drm/drmP.h> #include <drm/drm_gem.h> #include <drm/drm_vma_manager.h> + +#include <linux/dma-buf.h> #include <linux/iommu.h> #include "rockchip_drm_drv.h" @@ -262,7 +264,6 @@ static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj, * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap(). */ vma->vm_flags &= ~VM_PFNMAP; - vma->vm_pgoff = 0; if (rk_obj->pages) ret = rockchip_drm_gem_object_mmap_iommu(obj, vma); @@ -297,6 +298,12 @@ int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma) if (ret) return ret; + /* + * Set vm_pgoff (used as a fake buffer offset by DRM) to 0 and map the + * whole buffer from the start. + */ + vma->vm_pgoff = 0; + obj = vma->vm_private_data; return rockchip_drm_gem_object_mmap(obj, vma); @@ -309,12 +316,10 @@ static void rockchip_gem_release_object(struct rockchip_gem_object *rk_obj) } struct rockchip_gem_object * - rockchip_gem_create_object(struct drm_device *drm, unsigned int size, - bool alloc_kmap) + rockchip_gem_alloc_object(struct drm_device *drm, unsigned int size) { struct rockchip_gem_object *rk_obj; struct drm_gem_object *obj; - int ret; size = round_up(size, PAGE_SIZE); @@ -326,6 +331,20 @@ struct rockchip_gem_object * drm_gem_object_init(drm, obj, size); + return rk_obj; +} + +struct rockchip_gem_object * +rockchip_gem_create_object(struct drm_device *drm, unsigned int size, + bool alloc_kmap) +{ + struct rockchip_gem_object *rk_obj; + int ret; + + rk_obj = rockchip_gem_alloc_object(drm, size); + if (IS_ERR(rk_obj)) + return rk_obj; + ret = rockchip_gem_alloc_buf(rk_obj, alloc_kmap); if (ret) goto err_free_rk_obj; @@ -343,11 +362,21 @@ err_free_rk_obj: */ void rockchip_gem_free_object(struct drm_gem_object *obj) { - struct rockchip_gem_object *rk_obj; - - rk_obj = to_rockchip_obj(obj); + struct drm_device *drm = obj->dev; + struct rockchip_drm_private *private = drm->dev_private; + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); - rockchip_gem_free_buf(rk_obj); + if (obj->import_attach) { + if (private->domain) { + rockchip_gem_iommu_unmap(rk_obj); + } else { + dma_unmap_sg(drm->dev, rk_obj->sgt->sgl, + rk_obj->sgt->nents, DMA_BIDIRECTIONAL); + } + drm_prime_gem_destroy(obj, rk_obj->sgt); + } else { + rockchip_gem_free_buf(rk_obj); + } rockchip_gem_release_object(rk_obj); } @@ -451,6 +480,86 @@ struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj) return sgt; } +static unsigned long rockchip_sg_get_contiguous_size(struct sg_table *sgt, + int count) +{ + struct scatterlist *s; + dma_addr_t expected = sg_dma_address(sgt->sgl); + unsigned int i; + unsigned long size = 0; + + for_each_sg(sgt->sgl, s, count, i) { + if (sg_dma_address(s) != expected) + break; + expected = sg_dma_address(s) + sg_dma_len(s); + size += sg_dma_len(s); + } + return size; +} + +static int +rockchip_gem_iommu_map_sg(struct drm_device *drm, + struct dma_buf_attachment *attach, + struct sg_table *sg, + struct rockchip_gem_object *rk_obj) +{ + rk_obj->sgt = sg; + return rockchip_gem_iommu_map(rk_obj); +} + +static int +rockchip_gem_dma_map_sg(struct drm_device *drm, + struct dma_buf_attachment *attach, + struct sg_table *sg, + struct rockchip_gem_object *rk_obj) +{ + int count = dma_map_sg(drm->dev, sg->sgl, sg->nents, + DMA_BIDIRECTIONAL); + if (!count) + return -EINVAL; + + if (rockchip_sg_get_contiguous_size(sg, count) < attach->dmabuf->size) { + DRM_ERROR("failed to map sg_table to contiguous linear address.\n"); + dma_unmap_sg(drm->dev, sg->sgl, sg->nents, + DMA_BIDIRECTIONAL); + return -EINVAL; + } + + rk_obj->dma_addr = sg_dma_address(sg->sgl); + rk_obj->sgt = sg; + return 0; +} + +struct drm_gem_object * +rockchip_gem_prime_import_sg_table(struct drm_device *drm, + struct dma_buf_attachment *attach, + struct sg_table *sg) +{ + struct rockchip_drm_private *private = drm->dev_private; + struct rockchip_gem_object *rk_obj; + int ret; + + rk_obj = rockchip_gem_alloc_object(drm, attach->dmabuf->size); + if (IS_ERR(rk_obj)) + return ERR_CAST(rk_obj); + + if (private->domain) + ret = rockchip_gem_iommu_map_sg(drm, attach, sg, rk_obj); + else + ret = rockchip_gem_dma_map_sg(drm, attach, sg, rk_obj); + + if (ret < 0) { + DRM_ERROR("failed to import sg table: %d\n", ret); + goto err_free_rk_obj; + } + + return &rk_obj->base; + +err_free_rk_obj: + rockchip_gem_release_object(rk_obj); + return ERR_PTR(ret); +} + void *rockchip_gem_prime_vmap(struct drm_gem_object *obj) { struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h index f237375582fb..d41fa65219d2 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h @@ -36,8 +36,9 @@ struct rockchip_gem_object { struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj); struct drm_gem_object * -rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size, - struct sg_table *sgt); +rockchip_gem_prime_import_sg_table(struct drm_device *dev, + struct dma_buf_attachment *attach, + struct sg_table *sg); void *rockchip_gem_prime_vmap(struct drm_gem_object *obj); void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c index 3acfd576b7df..b339ca943139 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c @@ -18,7 +18,7 @@ #include "rockchip_drm_drv.h" #include "rockchip_drm_psr.h" -#define PSR_FLUSH_TIMEOUT msecs_to_jiffies(100) +#define PSR_FLUSH_TIMEOUT_MS 100 enum psr_state { PSR_FLUSH, @@ -30,11 +30,11 @@ struct psr_drv { struct list_head list; struct drm_encoder *encoder; - spinlock_t lock; + struct mutex lock; bool active; enum psr_state state; - struct timer_list flush_timer; + struct delayed_work flush_work; void (*set)(struct drm_encoder *encoder, bool enable); }; @@ -43,9 +43,8 @@ static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc) { struct rockchip_drm_private *drm_drv = crtc->dev->dev_private; struct psr_drv *psr; - unsigned long flags; - spin_lock_irqsave(&drm_drv->psr_list_lock, flags); + mutex_lock(&drm_drv->psr_list_lock); list_for_each_entry(psr, &drm_drv->psr_list, list) { if (psr->encoder->crtc == crtc) goto out; @@ -53,7 +52,24 @@ static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc) psr = ERR_PTR(-ENODEV); out: - spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); + mutex_unlock(&drm_drv->psr_list_lock); + return psr; +} + +static struct psr_drv *find_psr_by_encoder(struct drm_encoder *encoder) +{ + struct rockchip_drm_private *drm_drv = encoder->dev->dev_private; + struct psr_drv *psr; + + mutex_lock(&drm_drv->psr_list_lock); + list_for_each_entry(psr, &drm_drv->psr_list, list) { + if (psr->encoder == encoder) + goto out; + } + psr = ERR_PTR(-ENODEV); + +out: + mutex_unlock(&drm_drv->psr_list_lock); return psr; } @@ -94,43 +110,40 @@ static void psr_set_state_locked(struct psr_drv *psr, enum psr_state state) static void psr_set_state(struct psr_drv *psr, enum psr_state state) { - unsigned long flags; - - spin_lock_irqsave(&psr->lock, flags); + mutex_lock(&psr->lock); psr_set_state_locked(psr, state); - spin_unlock_irqrestore(&psr->lock, flags); + mutex_unlock(&psr->lock); } -static void psr_flush_handler(struct timer_list *t) +static void psr_flush_handler(struct work_struct *work) { - struct psr_drv *psr = from_timer(psr, t, flush_timer); - unsigned long flags; + struct psr_drv *psr = container_of(to_delayed_work(work), + struct psr_drv, flush_work); /* If the state has changed since we initiated the flush, do nothing */ - spin_lock_irqsave(&psr->lock, flags); + mutex_lock(&psr->lock); if (psr->state == PSR_FLUSH) psr_set_state_locked(psr, PSR_ENABLE); - spin_unlock_irqrestore(&psr->lock, flags); + mutex_unlock(&psr->lock); } /** * rockchip_drm_psr_activate - activate PSR on the given pipe - * @crtc: CRTC to obtain the PSR encoder + * @encoder: encoder to obtain the PSR encoder * * Returns: * Zero on success, negative errno on failure. */ -int rockchip_drm_psr_activate(struct drm_crtc *crtc) +int rockchip_drm_psr_activate(struct drm_encoder *encoder) { - struct psr_drv *psr = find_psr_by_crtc(crtc); - unsigned long flags; + struct psr_drv *psr = find_psr_by_encoder(encoder); if (IS_ERR(psr)) return PTR_ERR(psr); - spin_lock_irqsave(&psr->lock, flags); + mutex_lock(&psr->lock); psr->active = true; - spin_unlock_irqrestore(&psr->lock, flags); + mutex_unlock(&psr->lock); return 0; } @@ -138,23 +151,22 @@ EXPORT_SYMBOL(rockchip_drm_psr_activate); /** * rockchip_drm_psr_deactivate - deactivate PSR on the given pipe - * @crtc: CRTC to obtain the PSR encoder + * @encoder: encoder to obtain the PSR encoder * * Returns: * Zero on success, negative errno on failure. */ -int rockchip_drm_psr_deactivate(struct drm_crtc *crtc) +int rockchip_drm_psr_deactivate(struct drm_encoder *encoder) { - struct psr_drv *psr = find_psr_by_crtc(crtc); - unsigned long flags; + struct psr_drv *psr = find_psr_by_encoder(encoder); if (IS_ERR(psr)) return PTR_ERR(psr); - spin_lock_irqsave(&psr->lock, flags); + mutex_lock(&psr->lock); psr->active = false; - spin_unlock_irqrestore(&psr->lock, flags); - del_timer_sync(&psr->flush_timer); + mutex_unlock(&psr->lock); + cancel_delayed_work_sync(&psr->flush_work); return 0; } @@ -162,9 +174,8 @@ EXPORT_SYMBOL(rockchip_drm_psr_deactivate); static void rockchip_drm_do_flush(struct psr_drv *psr) { - mod_timer(&psr->flush_timer, - round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT)); psr_set_state(psr, PSR_FLUSH); + mod_delayed_work(system_wq, &psr->flush_work, PSR_FLUSH_TIMEOUT_MS); } /** @@ -201,12 +212,11 @@ void rockchip_drm_psr_flush_all(struct drm_device *dev) { struct rockchip_drm_private *drm_drv = dev->dev_private; struct psr_drv *psr; - unsigned long flags; - spin_lock_irqsave(&drm_drv->psr_list_lock, flags); + mutex_lock(&drm_drv->psr_list_lock); list_for_each_entry(psr, &drm_drv->psr_list, list) rockchip_drm_do_flush(psr); - spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); + mutex_unlock(&drm_drv->psr_list_lock); } EXPORT_SYMBOL(rockchip_drm_psr_flush_all); @@ -223,7 +233,6 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder, { struct rockchip_drm_private *drm_drv = encoder->dev->dev_private; struct psr_drv *psr; - unsigned long flags; if (!encoder || !psr_set) return -EINVAL; @@ -232,17 +241,17 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder, if (!psr) return -ENOMEM; - timer_setup(&psr->flush_timer, psr_flush_handler, 0); - spin_lock_init(&psr->lock); + INIT_DELAYED_WORK(&psr->flush_work, psr_flush_handler); + mutex_init(&psr->lock); psr->active = true; psr->state = PSR_DISABLE; psr->encoder = encoder; psr->set = psr_set; - spin_lock_irqsave(&drm_drv->psr_list_lock, flags); + mutex_lock(&drm_drv->psr_list_lock); list_add_tail(&psr->list, &drm_drv->psr_list); - spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); + mutex_unlock(&drm_drv->psr_list_lock); return 0; } @@ -260,16 +269,15 @@ void rockchip_drm_psr_unregister(struct drm_encoder *encoder) { struct rockchip_drm_private *drm_drv = encoder->dev->dev_private; struct psr_drv *psr, *n; - unsigned long flags; - spin_lock_irqsave(&drm_drv->psr_list_lock, flags); + mutex_lock(&drm_drv->psr_list_lock); list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) { if (psr->encoder == encoder) { - del_timer(&psr->flush_timer); + cancel_delayed_work_sync(&psr->flush_work); list_del(&psr->list); kfree(psr); } } - spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); + mutex_unlock(&drm_drv->psr_list_lock); } EXPORT_SYMBOL(rockchip_drm_psr_unregister); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h index b420cf1bf902..b1ea0155e57c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h @@ -18,8 +18,8 @@ void rockchip_drm_psr_flush_all(struct drm_device *dev); int rockchip_drm_psr_flush(struct drm_crtc *crtc); -int rockchip_drm_psr_activate(struct drm_crtc *crtc); -int rockchip_drm_psr_deactivate(struct drm_crtc *crtc); +int rockchip_drm_psr_activate(struct drm_encoder *encoder); +int rockchip_drm_psr_deactivate(struct drm_encoder *encoder); int rockchip_drm_psr_register(struct drm_encoder *encoder, void (*psr_set)(struct drm_encoder *, bool enable)); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index ba7505292b78..53d4afe15278 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -95,9 +95,6 @@ struct vop { struct drm_device *drm_dev; bool is_enabled; - /* mutex vsync_ work */ - struct mutex vsync_mutex; - bool vsync_work_pending; struct completion dsp_hold_completion; /* protected by dev->event_lock */ @@ -120,6 +117,8 @@ struct vop { spinlock_t reg_lock; /* lock vop irq reg */ spinlock_t irq_lock; + /* protects crtc enable/disable */ + struct mutex vop_lock; unsigned int irq; @@ -253,23 +252,15 @@ static bool is_yuv_support(uint32_t format) } } -static bool is_alpha_support(uint32_t format) -{ - switch (format) { - case DRM_FORMAT_ARGB8888: - case DRM_FORMAT_ABGR8888: - return true; - default: - return false; - } -} - static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src, uint32_t dst, bool is_horizontal, int vsu_mode, int *vskiplines) { uint16_t val = 1 << SCL_FT_DEFAULT_FIXPOINT_SHIFT; + if (vskiplines) + *vskiplines = 0; + if (is_horizontal) { if (mode == SCALE_UP) val = GET_SCL_FT_BIC(src, dst); @@ -310,7 +301,7 @@ static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win, uint16_t vsu_mode; uint16_t lb_mode; uint32_t val; - int vskiplines = 0; + int vskiplines; if (dst_w > 3840) { DRM_DEV_ERROR(vop->dev, "Maximum dst width (3840) exceeded\n"); @@ -528,7 +519,10 @@ static int vop_enable(struct drm_crtc *crtc) goto err_disable_aclk; } - memcpy(vop->regs, vop->regsbak, vop->len); + spin_lock(&vop->reg_lock); + for (i = 0; i < vop->len; i += 4) + writel_relaxed(vop->regsbak[i / 4], vop->regs + i); + /* * We need to make sure that all windows are disabled before we * enable the crtc. Otherwise we might try to scan from a destroyed @@ -538,10 +532,9 @@ static int vop_enable(struct drm_crtc *crtc) struct vop_win *vop_win = &vop->win[i]; const struct vop_win_data *win = vop_win->data; - spin_lock(&vop->reg_lock); VOP_WIN_SET(vop, win, enable, 0); - spin_unlock(&vop->reg_lock); } + spin_unlock(&vop->reg_lock); vop_cfg_done(vop); @@ -580,8 +573,7 @@ static void vop_crtc_atomic_disable(struct drm_crtc *crtc, WARN_ON(vop->event); - rockchip_drm_psr_deactivate(&vop->crtc); - + mutex_lock(&vop->vop_lock); drm_crtc_vblank_off(crtc); /* @@ -617,6 +609,7 @@ static void vop_crtc_atomic_disable(struct drm_crtc *crtc, clk_disable(vop->aclk); clk_disable(vop->hclk); pm_runtime_put(vop->dev); + mutex_unlock(&vop->vop_lock); if (crtc->state->event && !crtc->state->active) { spin_lock_irq(&crtc->dev->event_lock); @@ -641,7 +634,6 @@ static int vop_plane_atomic_check(struct drm_plane *plane, struct vop_win *vop_win = to_vop_win(plane); const struct vop_win_data *win = vop_win->data; int ret; - struct drm_rect clip; int min_scale = win->phy->scl ? FRAC_16_16(1, 8) : DRM_PLANE_HELPER_NO_SCALING; int max_scale = win->phy->scl ? FRAC_16_16(8, 1) : @@ -654,12 +646,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane, if (WARN_ON(!crtc_state)) return -EINVAL; - clip.x1 = 0; - clip.y1 = 0; - clip.x2 = crtc_state->adjusted_mode.hdisplay; - clip.y2 = crtc_state->adjusted_mode.vdisplay; - - ret = drm_atomic_helper_check_plane_state(state, crtc_state, &clip, + ret = drm_atomic_helper_check_plane_state(state, crtc_state, min_scale, max_scale, true, true); if (ret) @@ -790,7 +777,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, rb_swap = has_rb_swapped(fb->format->format); VOP_WIN_SET(vop, win, rb_swap, rb_swap); - if (is_alpha_support(fb->format->format)) { + if (fb->format->has_alpha) { VOP_WIN_SET(vop, win, dst_alpha_ctl, DST_FACTOR_M0(ALPHA_SRC_INVERSE)); val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) | @@ -887,10 +874,13 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc, uint32_t pin_pol, val; int ret; + mutex_lock(&vop->vop_lock); + WARN_ON(vop->event); ret = vop_enable(crtc); if (ret) { + mutex_unlock(&vop->vop_lock); DRM_DEV_ERROR(vop->dev, "Failed to enable vop (%d)\n", ret); return; } @@ -954,8 +944,7 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc, clk_set_rate(vop->dclk, adjusted_mode->clock * 1000); VOP_REG_SET(vop, common, standby, 0); - - rockchip_drm_psr_activate(&vop->crtc); + mutex_unlock(&vop->vop_lock); } static bool vop_fs_irq_is_pending(struct vop *vop) @@ -1158,15 +1147,14 @@ static void vop_handle_vblank(struct vop *vop) { struct drm_device *drm = vop->drm_dev; struct drm_crtc *crtc = &vop->crtc; - unsigned long flags; - spin_lock_irqsave(&drm->event_lock, flags); + spin_lock(&drm->event_lock); if (vop->event) { drm_crtc_send_vblank_event(crtc, vop->event); drm_crtc_vblank_put(crtc); vop->event = NULL; } - spin_unlock_irqrestore(&drm->event_lock, flags); + spin_unlock(&drm->event_lock); if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vop->pending)) drm_flip_work_commit(&vop->fb_unref_work, system_unbound_wq); @@ -1177,21 +1165,20 @@ static irqreturn_t vop_isr(int irq, void *data) struct vop *vop = data; struct drm_crtc *crtc = &vop->crtc; uint32_t active_irqs; - unsigned long flags; int ret = IRQ_NONE; /* * interrupt register has interrupt status, enable and clear bits, we * must hold irq_lock to avoid a race with enable/disable_vblank(). */ - spin_lock_irqsave(&vop->irq_lock, flags); + spin_lock(&vop->irq_lock); active_irqs = VOP_INTR_GET_TYPE(vop, status, INTR_MASK); /* Clear all active interrupt sources */ if (active_irqs) VOP_INTR_SET_TYPE(vop, clear, active_irqs, 1); - spin_unlock_irqrestore(&vop->irq_lock, flags); + spin_unlock(&vop->irq_lock); /* This is expected for vop iommu irqs, since the irq is shared */ if (!active_irqs) @@ -1414,7 +1401,11 @@ static int vop_initial(struct vop *vop) usleep_range(10, 20); reset_control_deassert(ahb_rst); - memcpy(vop->regsbak, vop->regs, vop->len); + VOP_INTR_SET_TYPE(vop, clear, INTR_MASK, 1); + VOP_INTR_SET_TYPE(vop, enable, INTR_MASK, 0); + + for (i = 0; i < vop->len; i += sizeof(u32)) + vop->regsbak[i / 4] = readl_relaxed(vop->regs + i); VOP_REG_SET(vop, misc, global_regdone_en, 1); VOP_REG_SET(vop, common, dsp_blank, 0); @@ -1494,15 +1485,21 @@ int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout) { struct vop *vop = to_vop(crtc); unsigned long jiffies_left; + int ret = 0; if (!crtc || !vop->is_enabled) return -ENODEV; - if (mstimeout <= 0) - return -EINVAL; + mutex_lock(&vop->vop_lock); + if (mstimeout <= 0) { + ret = -EINVAL; + goto out; + } - if (vop_line_flag_irq_is_enabled(vop)) - return -EBUSY; + if (vop_line_flag_irq_is_enabled(vop)) { + ret = -EBUSY; + goto out; + } reinit_completion(&vop->line_flag_completion); vop_line_flag_irq_enable(vop); @@ -1513,10 +1510,13 @@ int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout) if (jiffies_left == 0) { DRM_DEV_ERROR(vop->dev, "Timeout waiting for IRQ\n"); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto out; } - return 0; +out: + mutex_unlock(&vop->vop_lock); + return ret; } EXPORT_SYMBOL(rockchip_drm_wait_vact_end); @@ -1566,20 +1566,11 @@ static int vop_bind(struct device *dev, struct device *master, void *data) spin_lock_init(&vop->reg_lock); spin_lock_init(&vop->irq_lock); - - mutex_init(&vop->vsync_mutex); - - ret = devm_request_irq(dev, vop->irq, vop_isr, - IRQF_SHARED, dev_name(dev), vop); - if (ret) - return ret; - - /* IRQ is initially disabled; it gets enabled in power_on */ - disable_irq(vop->irq); + mutex_init(&vop->vop_lock); ret = vop_create_crtc(vop); if (ret) - goto err_enable_irq; + return ret; pm_runtime_enable(&pdev->dev); @@ -1590,13 +1581,19 @@ static int vop_bind(struct device *dev, struct device *master, void *data) goto err_disable_pm_runtime; } + ret = devm_request_irq(dev, vop->irq, vop_isr, + IRQF_SHARED, dev_name(dev), vop); + if (ret) + goto err_disable_pm_runtime; + + /* IRQ is initially disabled; it gets enabled in power_on */ + disable_irq(vop->irq); + return 0; err_disable_pm_runtime: pm_runtime_disable(&pdev->dev); vop_destroy_crtc(vop); -err_enable_irq: - enable_irq(vop->irq); /* To balance out the disable_irq above */ return ret; } |