From c3c3ab199bdd5b39f0a16545f4205e20c03ba20f Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Fri, 10 Feb 2017 14:52:40 -0500 Subject: [PATCH 01/47] drm/msm/gpu: move suspend/resume into debugfs->show Each of the per-generation callbacks was doing this. Lets just simplify and move it into toplevel show() fxn. Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 2 -- drivers/gpu/drm/msm/adreno/a4xx_gpu.c | 4 ---- drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 4 ---- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 4 ---- drivers/gpu/drm/msm/msm_debugfs.c | 2 ++ 5 files changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index b999349b7d2d3..7fd77958a4362 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -412,10 +412,8 @@ static const unsigned int a3xx_registers[] = { #ifdef CONFIG_DEBUG_FS static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m) { - gpu->funcs->pm_resume(gpu); seq_printf(m, "status: %08x\n", gpu_read(gpu, REG_A3XX_RBBM_STATUS)); - gpu->funcs->pm_suspend(gpu); adreno_show(gpu, m); } #endif diff --git a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c index 511bc855cc7f3..dfe0eceaae3bf 100644 --- a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c @@ -456,12 +456,8 @@ static const unsigned int a4xx_registers[] = { #ifdef CONFIG_DEBUG_FS static void a4xx_show(struct msm_gpu *gpu, struct seq_file *m) { - gpu->funcs->pm_resume(gpu); - seq_printf(m, "status: %08x\n", gpu_read(gpu, REG_A4XX_RBBM_STATUS)); - gpu->funcs->pm_suspend(gpu); - adreno_show(gpu, m); } diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 4414cf73735d2..d507a2f6dcc98 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -837,12 +837,8 @@ static int a5xx_get_timestamp(struct msm_gpu *gpu, uint64_t *value) #ifdef CONFIG_DEBUG_FS static void a5xx_show(struct msm_gpu *gpu, struct seq_file *m) { - gpu->funcs->pm_resume(gpu); - seq_printf(m, "status: %08x\n", gpu_read(gpu, REG_A5XX_RBBM_STATUS)); - gpu->funcs->pm_suspend(gpu); - adreno_show(gpu, m); } #endif diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index c9bd1e6225f4f..50e0f54f29d2a 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -259,8 +259,6 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m) seq_printf(m, "wptr: %d\n", adreno_gpu->memptrs->wptr); seq_printf(m, "rb wptr: %d\n", get_wptr(gpu->rb)); - gpu->funcs->pm_resume(gpu); - /* dump these out in a form that can be parsed by demsm: */ seq_printf(m, "IO:region %s 00000000 00020000\n", gpu->name); for (i = 0; adreno_gpu->registers[i] != ~0; i += 2) { @@ -273,8 +271,6 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m) seq_printf(m, "IO:R %08x %08x\n", addr<<2, val); } } - - gpu->funcs->pm_suspend(gpu); } #endif diff --git a/drivers/gpu/drm/msm/msm_debugfs.c b/drivers/gpu/drm/msm/msm_debugfs.c index 4f35d4eb85d08..89b3b79f92c89 100644 --- a/drivers/gpu/drm/msm/msm_debugfs.c +++ b/drivers/gpu/drm/msm/msm_debugfs.c @@ -28,7 +28,9 @@ static int msm_gpu_show(struct drm_device *dev, struct seq_file *m) if (gpu) { seq_printf(m, "%s Status:\n", gpu->name); + gpu->funcs->pm_resume(gpu); gpu->funcs->show(gpu, m); + gpu->funcs->pm_suspend(gpu); } return 0; From eeb754746b140c5f55e6b25706a9142aa549b348 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Fri, 10 Feb 2017 15:36:33 -0500 Subject: [PATCH 02/47] drm/msm/gpu: use pm-runtime We need to use pm-runtime properly when IOMMU is using device_link() to control it's own clocks. Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/adreno_device.c | 36 +++++--- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 11 ++- drivers/gpu/drm/msm/msm_debugfs.c | 4 +- drivers/gpu/drm/msm/msm_drv.c | 2 + drivers/gpu/drm/msm/msm_gpu.c | 99 ++++++---------------- drivers/gpu/drm/msm/msm_gpu.h | 12 +-- 6 files changed, 71 insertions(+), 93 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c index ece39b16a8641..bc170b7d5546c 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_device.c +++ b/drivers/gpu/drm/msm/adreno/adreno_device.c @@ -155,21 +155,14 @@ struct msm_gpu *adreno_load_gpu(struct drm_device *dev) if (gpu) { int ret; - mutex_lock(&dev->struct_mutex); - gpu->funcs->pm_resume(gpu); - mutex_unlock(&dev->struct_mutex); - disable_irq(gpu->irq); - - ret = gpu->funcs->hw_init(gpu); + pm_runtime_get_sync(&pdev->dev); + ret = msm_gpu_hw_init(gpu); + pm_runtime_put_sync(&pdev->dev); if (ret) { dev_err(dev->dev, "gpu hw init failed: %d\n", ret); gpu->funcs->destroy(gpu); gpu = NULL; - } else { - enable_irq(gpu->irq); - /* give inactive pm a chance to kick in: */ - msm_gpu_retire(gpu); } } @@ -296,12 +289,35 @@ static const struct of_device_id dt_match[] = { {} }; +#ifdef CONFIG_PM +static int adreno_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct msm_gpu *gpu = platform_get_drvdata(pdev); + + return gpu->funcs->pm_resume(gpu); +} + +static int adreno_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct msm_gpu *gpu = platform_get_drvdata(pdev); + + return gpu->funcs->pm_suspend(gpu); +} +#endif + +static const struct dev_pm_ops adreno_pm_ops = { + SET_RUNTIME_PM_OPS(adreno_suspend, adreno_resume, NULL) +}; + static struct platform_driver adreno_driver = { .probe = adreno_probe, .remove = adreno_remove, .driver = { .name = "adreno", .of_match_table = dt_match, + .pm = &adreno_pm_ops, }, }; diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 50e0f54f29d2a..823f9dfe6db95 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -115,6 +115,9 @@ void adreno_recover(struct msm_gpu *gpu) struct drm_device *dev = gpu->dev; int ret; + // XXX pm-runtime?? we *need* the device to be off after this + // so maybe continuing to call ->pm_suspend/resume() is better? + gpu->funcs->pm_suspend(gpu); /* reset ringbuffer: */ @@ -127,13 +130,11 @@ void adreno_recover(struct msm_gpu *gpu) gpu->funcs->pm_resume(gpu); - disable_irq(gpu->irq); - ret = gpu->funcs->hw_init(gpu); + ret = msm_gpu_hw_init(gpu); if (ret) { dev_err(dev->dev, "gpu hw init failed: %d\n", ret); /* hmm, oh well? */ } - enable_irq(gpu->irq); } void adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, @@ -365,6 +366,10 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, if (ret) return ret; + pm_runtime_set_autosuspend_delay(&pdev->dev, DRM_MSM_INACTIVE_PERIOD); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + ret = request_firmware(&adreno_gpu->pm4, adreno_gpu->info->pm4fw, drm->dev); if (ret) { dev_err(drm->dev, "failed to load %s PM4 firmware: %d\n", diff --git a/drivers/gpu/drm/msm/msm_debugfs.c b/drivers/gpu/drm/msm/msm_debugfs.c index 89b3b79f92c89..1855182c76ced 100644 --- a/drivers/gpu/drm/msm/msm_debugfs.c +++ b/drivers/gpu/drm/msm/msm_debugfs.c @@ -28,9 +28,9 @@ static int msm_gpu_show(struct drm_device *dev, struct seq_file *m) if (gpu) { seq_printf(m, "%s Status:\n", gpu->name); - gpu->funcs->pm_resume(gpu); + pm_runtime_get_sync(&gpu->pdev->dev); gpu->funcs->show(gpu, m); - gpu->funcs->pm_suspend(gpu); + pm_runtime_put_sync(&gpu->pdev->dev); } return 0; diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 9208e67be4538..5875aa8fbe7c6 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -265,6 +265,8 @@ static int msm_drm_uninit(struct device *dev) if (gpu) { mutex_lock(&ddev->struct_mutex); + // XXX what do we do here? + //pm_runtime_enable(&pdev->dev); gpu->funcs->pm_suspend(gpu); mutex_unlock(&ddev->struct_mutex); gpu->funcs->destroy(gpu); diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 99e05aacbee18..41d2abc001ebf 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -152,18 +152,9 @@ static int disable_axi(struct msm_gpu *gpu) int msm_gpu_pm_resume(struct msm_gpu *gpu) { - struct drm_device *dev = gpu->dev; int ret; - DBG("%s: active_cnt=%d", gpu->name, gpu->active_cnt); - - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - - if (gpu->active_cnt++ > 0) - return 0; - - if (WARN_ON(gpu->active_cnt <= 0)) - return -EINVAL; + DBG("%s", gpu->name); ret = enable_pwrrail(gpu); if (ret) @@ -177,23 +168,16 @@ int msm_gpu_pm_resume(struct msm_gpu *gpu) if (ret) return ret; + gpu->needs_hw_init = true; + return 0; } int msm_gpu_pm_suspend(struct msm_gpu *gpu) { - struct drm_device *dev = gpu->dev; int ret; - DBG("%s: active_cnt=%d", gpu->name, gpu->active_cnt); - - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - - if (--gpu->active_cnt > 0) - return 0; - - if (WARN_ON(gpu->active_cnt < 0)) - return -EINVAL; + DBG("%s", gpu->name); ret = disable_axi(gpu); if (ret) @@ -210,53 +194,20 @@ int msm_gpu_pm_suspend(struct msm_gpu *gpu) return 0; } -/* - * Inactivity detection (for suspend): - */ - -static void inactive_worker(struct work_struct *work) -{ - struct msm_gpu *gpu = container_of(work, struct msm_gpu, inactive_work); - struct drm_device *dev = gpu->dev; - - if (gpu->inactive) - return; - - DBG("%s: inactive!\n", gpu->name); - mutex_lock(&dev->struct_mutex); - if (!(msm_gpu_active(gpu) || gpu->inactive)) { - disable_axi(gpu); - disable_clk(gpu); - gpu->inactive = true; - } - mutex_unlock(&dev->struct_mutex); -} - -static void inactive_handler(unsigned long data) +int msm_gpu_hw_init(struct msm_gpu *gpu) { - struct msm_gpu *gpu = (struct msm_gpu *)data; - struct msm_drm_private *priv = gpu->dev->dev_private; + int ret; - queue_work(priv->wq, &gpu->inactive_work); -} + if (!gpu->needs_hw_init) + return 0; -/* cancel inactive timer and make sure we are awake: */ -static void inactive_cancel(struct msm_gpu *gpu) -{ - DBG("%s", gpu->name); - del_timer(&gpu->inactive_timer); - if (gpu->inactive) { - enable_clk(gpu); - enable_axi(gpu); - gpu->inactive = false; - } -} + disable_irq(gpu->irq); + ret = gpu->funcs->hw_init(gpu); + if (!ret) + gpu->needs_hw_init = false; + enable_irq(gpu->irq); -static void inactive_start(struct msm_gpu *gpu) -{ - DBG("%s", gpu->name); - mod_timer(&gpu->inactive_timer, - round_jiffies_up(jiffies + DRM_MSM_INACTIVE_JIFFIES)); + return ret; } /* @@ -296,8 +247,9 @@ static void recover_worker(struct work_struct *work) /* retire completed submits, plus the one that hung: */ retire_submits(gpu); - inactive_cancel(gpu); + pm_runtime_get_sync(&gpu->pdev->dev); gpu->funcs->recover(gpu); + pm_runtime_put_sync(&gpu->pdev->dev); /* replay the remaining submits after the one that hung: */ list_for_each_entry(submit, &gpu->submit_list, node) { @@ -400,6 +352,8 @@ void msm_gpu_perfcntr_start(struct msm_gpu *gpu) { unsigned long flags; + pm_runtime_get_sync(&gpu->pdev->dev); + spin_lock_irqsave(&gpu->perf_lock, flags); /* we could dynamically enable/disable perfcntr registers too.. */ gpu->last_sample.active = msm_gpu_active(gpu); @@ -413,6 +367,7 @@ void msm_gpu_perfcntr_start(struct msm_gpu *gpu) void msm_gpu_perfcntr_stop(struct msm_gpu *gpu) { gpu->perfcntr_active = false; + pm_runtime_put_sync(&gpu->pdev->dev); } /* returns -errno or # of cntrs sampled */ @@ -458,6 +413,8 @@ static void retire_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) drm_gem_object_unreference(&msm_obj->base); } + pm_runtime_mark_last_busy(&gpu->pdev->dev); + pm_runtime_put_autosuspend(&gpu->pdev->dev); msm_gem_submit_free(submit); } @@ -492,9 +449,6 @@ static void retire_worker(struct work_struct *work) mutex_lock(&dev->struct_mutex); retire_submits(gpu); mutex_unlock(&dev->struct_mutex); - - if (!msm_gpu_active(gpu)) - inactive_start(gpu); } /* call from irq handler to schedule work to retire bo's */ @@ -515,7 +469,9 @@ void msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - inactive_cancel(gpu); + pm_runtime_get_sync(&gpu->pdev->dev); + + msm_gpu_hw_init(gpu); list_add_tail(&submit->node, &gpu->submit_list); @@ -576,7 +532,6 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, gpu->dev = drm; gpu->funcs = funcs; gpu->name = name; - gpu->inactive = true; gpu->fctx = msm_fence_context_alloc(drm, name); if (IS_ERR(gpu->fctx)) { ret = PTR_ERR(gpu->fctx); @@ -586,13 +541,10 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, INIT_LIST_HEAD(&gpu->active_list); INIT_WORK(&gpu->retire_work, retire_worker); - INIT_WORK(&gpu->inactive_work, inactive_worker); INIT_WORK(&gpu->recover_work, recover_worker); INIT_LIST_HEAD(&gpu->submit_list); - setup_timer(&gpu->inactive_timer, inactive_handler, - (unsigned long)gpu); setup_timer(&gpu->hangcheck_timer, hangcheck_handler, (unsigned long)gpu); @@ -684,6 +636,9 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, goto fail; } + gpu->pdev = pdev; + platform_set_drvdata(pdev, gpu); + bs_init(gpu); return 0; diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index c4c39d3272c76..72dba973aabf4 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -64,6 +64,7 @@ struct msm_gpu_funcs { struct msm_gpu { const char *name; struct drm_device *dev; + struct platform_device *pdev; const struct msm_gpu_funcs *funcs; /* performance counters (hw & sw): */ @@ -88,9 +89,8 @@ struct msm_gpu { /* fencing: */ struct msm_fence_context *fctx; - /* is gpu powered/active? */ - int active_cnt; - bool inactive; + /* does gpu need hw_init? */ + bool needs_hw_init; /* worker for handling active-list retiring: */ struct work_struct retire_work; @@ -114,9 +114,7 @@ struct msm_gpu { /* Hang and Inactivity Detection: */ #define DRM_MSM_INACTIVE_PERIOD 66 /* in ms (roughly four frames) */ -#define DRM_MSM_INACTIVE_JIFFIES msecs_to_jiffies(DRM_MSM_INACTIVE_PERIOD) - struct timer_list inactive_timer; - struct work_struct inactive_work; + #define DRM_MSM_HANGCHECK_PERIOD 500 /* in ms */ #define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD) struct timer_list hangcheck_timer; @@ -196,6 +194,8 @@ static inline void gpu_write64(struct msm_gpu *gpu, u32 lo, u32 hi, u64 val) int msm_gpu_pm_suspend(struct msm_gpu *gpu); int msm_gpu_pm_resume(struct msm_gpu *gpu); +int msm_gpu_hw_init(struct msm_gpu *gpu); + void msm_gpu_perfcntr_start(struct msm_gpu *gpu); void msm_gpu_perfcntr_stop(struct msm_gpu *gpu); int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime, From de098e5fb123ca142656eaf7936ce2a60b7d1339 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sun, 12 Feb 2017 11:42:14 -0500 Subject: [PATCH 03/47] drm/msm/adreno: reset ringbuffer in hw_init We need to do this also in resume path when we need to re-hw_init(). Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 823f9dfe6db95..05a3d1fd6d302 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -68,6 +68,14 @@ int adreno_hw_init(struct msm_gpu *gpu) return ret; } + /* reset ringbuffer: */ + gpu->rb->cur = gpu->rb->start; + + /* reset completed fence seqno: */ + adreno_gpu->memptrs->fence = gpu->fctx->completed_fence; + adreno_gpu->memptrs->rptr = 0; + adreno_gpu->memptrs->wptr = 0; + /* Setup REG_CP_RB_CNTL: */ adreno_gpu_write(adreno_gpu, REG_ADRENO_CP_RB_CNTL, /* size is log2(quad-words): */ @@ -111,7 +119,6 @@ uint32_t adreno_last_fence(struct msm_gpu *gpu) void adreno_recover(struct msm_gpu *gpu) { - struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct drm_device *dev = gpu->dev; int ret; @@ -119,15 +126,6 @@ void adreno_recover(struct msm_gpu *gpu) // so maybe continuing to call ->pm_suspend/resume() is better? gpu->funcs->pm_suspend(gpu); - - /* reset ringbuffer: */ - gpu->rb->cur = gpu->rb->start; - - /* reset completed fence seqno: */ - adreno_gpu->memptrs->fence = gpu->fctx->completed_fence; - adreno_gpu->memptrs->rptr = 0; - adreno_gpu->memptrs->wptr = 0; - gpu->funcs->pm_resume(gpu); ret = msm_gpu_hw_init(gpu); From f90000492ed8df9cb0214e7551a43a2be170db7a Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 15 Feb 2017 15:44:04 -0500 Subject: [PATCH 04/47] drm/msm: convert to iommu_map_sg Significantly simplifies things. Also iommu_unmap() can unmap an entire iova range. (If backporting to downstream kernel you might need to revert this. Or at least double check older iommu implementation.) Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_iommu.c | 54 +++------------------------------ 1 file changed, 5 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index 7f5779daf5c86..6583fadc6e5b5 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -52,64 +52,20 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, struct sg_table *sgt, unsigned len, int prot) { struct msm_iommu *iommu = to_msm_iommu(mmu); - struct iommu_domain *domain = iommu->domain; - struct scatterlist *sg; - unsigned long da = iova; - unsigned int i, j; - int ret; + size_t ret; - if (!domain || !sgt) - return -EINVAL; + ret = iommu_map_sg(iommu->domain, iova, sgt->sgl, sgt->nents, prot); + WARN_ON(ret < 0); - for_each_sg(sgt->sgl, sg, sgt->nents, i) { - dma_addr_t pa = sg_phys(sg) - sg->offset; - size_t bytes = sg->length + sg->offset; - - VERB("map[%d]: %08lx %08lx(%zx)", i, da, (unsigned long)pa, bytes); - - ret = iommu_map(domain, da, pa, bytes, prot); - if (ret) - goto fail; - - da += bytes; - } - - return 0; - -fail: - da = iova; - - for_each_sg(sgt->sgl, sg, i, j) { - size_t bytes = sg->length + sg->offset; - iommu_unmap(domain, da, bytes); - da += bytes; - } - return ret; + return (ret == len) ? 0 : -EINVAL; } static int msm_iommu_unmap(struct msm_mmu *mmu, uint64_t iova, struct sg_table *sgt, unsigned len) { struct msm_iommu *iommu = to_msm_iommu(mmu); - struct iommu_domain *domain = iommu->domain; - struct scatterlist *sg; - unsigned long da = iova; - int i; - - for_each_sg(sgt->sgl, sg, sgt->nents, i) { - size_t bytes = sg->length + sg->offset; - size_t unmapped; - - unmapped = iommu_unmap(domain, da, bytes); - if (unmapped < bytes) - return unmapped; - - VERB("unmap[%d]: %08lx(%zx)", i, da, bytes); - - BUG_ON(!PAGE_ALIGNED(bytes)); - da += bytes; - } + iommu_unmap(iommu->domain, iova, len); return 0; } From cc69272673ae8ef83982d6e54c67bc01997a13ea Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 15 Feb 2017 16:00:25 -0500 Subject: [PATCH 05/47] drm/msm: pm runtime support for iommu In particular, attach() and unmap() need pm-runtime get/put to ensure iommu clks are enabled. Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_iommu.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index 6583fadc6e5b5..b23d33622f374 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -38,14 +38,23 @@ static int msm_iommu_attach(struct msm_mmu *mmu, const char * const *names, int cnt) { struct msm_iommu *iommu = to_msm_iommu(mmu); - return iommu_attach_device(iommu->domain, mmu->dev); + int ret; + + pm_runtime_get_sync(mmu->dev); + ret = iommu_attach_device(iommu->domain, mmu->dev); + pm_runtime_put_sync(mmu->dev); + + return ret; } static void msm_iommu_detach(struct msm_mmu *mmu, const char * const *names, int cnt) { struct msm_iommu *iommu = to_msm_iommu(mmu); + + pm_runtime_get_sync(mmu->dev); iommu_detach_device(iommu->domain, mmu->dev); + pm_runtime_put_sync(mmu->dev); } static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, @@ -54,7 +63,9 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, struct msm_iommu *iommu = to_msm_iommu(mmu); size_t ret; +// pm_runtime_get_sync(mmu->dev); ret = iommu_map_sg(iommu->domain, iova, sgt->sgl, sgt->nents, prot); +// pm_runtime_put_sync(mmu->dev); WARN_ON(ret < 0); return (ret == len) ? 0 : -EINVAL; @@ -65,7 +76,9 @@ static int msm_iommu_unmap(struct msm_mmu *mmu, uint64_t iova, { struct msm_iommu *iommu = to_msm_iommu(mmu); + pm_runtime_get_sync(mmu->dev); iommu_unmap(iommu->domain, iova, len); + pm_runtime_put_sync(mmu->dev); return 0; } From 48f243c95bdb17c1453d48142abad6aabed339e5 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sat, 25 Feb 2017 10:36:30 -0500 Subject: [PATCH 06/47] drm/msm: move submit fence wait out of struct_mutex Probably a symptom of needing finer grained locking, but if we wait on the incoming fence-fd (which could come from a different context) while holding struct_mutex, that blocks retire_worker so gpu fences cannot get signalled. This causes a problem if userspace manages to get more than a frame ahead, leaving the atomic-commit worker blocked waiting on fences that cannot be signaled because submit is blocked waiting for a fence signalled from vblank (after the atomic commit which is blocked). If we start having multiple fence ctxs for the gpu, submit_fence_sync() would probably need to move outside of struct_mutex as well. Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_gem_submit.c | 39 +++++++++++++--------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 1172fe7a9252c..1c545ebe6a5a0 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -404,6 +404,24 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, if (MSM_PIPE_FLAGS(args->flags) & ~MSM_SUBMIT_FLAGS) return -EINVAL; + if (args->flags & MSM_SUBMIT_FENCE_FD_IN) { + in_fence = sync_file_get_fence(args->fence_fd); + + if (!in_fence) + return -EINVAL; + + /* TODO if we get an array-fence due to userspace merging multiple + * fences, we need a way to determine if all the backing fences + * are from our own context.. + */ + + if (in_fence->context != gpu->fctx->context) { + ret = dma_fence_wait(in_fence, true); + if (ret) + return ret; + } + } + ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) return ret; @@ -431,27 +449,6 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, if (ret) goto out; - if (args->flags & MSM_SUBMIT_FENCE_FD_IN) { - in_fence = sync_file_get_fence(args->fence_fd); - - if (!in_fence) { - ret = -EINVAL; - goto out; - } - - /* TODO if we get an array-fence due to userspace merging multiple - * fences, we need a way to determine if all the backing fences - * are from our own context.. - */ - - if (in_fence->context != gpu->fctx->context) { - ret = dma_fence_wait(in_fence, true); - if (ret) - goto out; - } - - } - if (!(args->fence & MSM_SUBMIT_NO_IMPLICIT)) { ret = submit_fence_sync(submit); if (ret) From 0c3eaf1fbdd9eacf8bf7819737e4e609d5fe8505 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 13 Mar 2017 17:43:48 +0100 Subject: [PATCH 07/47] drm/msm: adreno: fix build error without debugfs The newly added a5xx support fails to build when debugfs is diabled: drivers/gpu/drm/msm/adreno/a5xx_gpu.c:849:4: error: 'struct msm_gpu_funcs' has no member named 'show' drivers/gpu/drm/msm/adreno/a5xx_gpu.c:849:11: error: 'a5xx_show' undeclared here (not in a function); did you mean 'a5xx_irq'? This adds a missing #ifdef. Fixes: b5f103ab98c7 ("drm/msm: gpu: Add A5XX target support") Cc: stable@vger.kernel.org Signed-off-by: Arnd Bergmann Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index d507a2f6dcc98..9c623fdacbe07 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -856,7 +856,9 @@ static const struct adreno_gpu_funcs funcs = { .idle = a5xx_idle, .irq = a5xx_irq, .destroy = a5xx_destroy, +#ifdef CONFIG_DEBUG_FS .show = a5xx_show, +#endif }, .get_timestamp = a5xx_get_timestamp, }; From 94df145c7e17c6212e2078a2ef976c365973eada Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:46 +0100 Subject: [PATCH 08/47] drm/msm: switch to postclose I didn't spot anything that would require ordering here (well not anywhere else either), and I'm trying to unify at least modern drivers on one close hook. Cc: Rob Clark Cc: linux-arm-msm@vger.kernel.org Cc: freedreno@lists.freedesktop.org Signed-off-by: Daniel Vetter Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 5875aa8fbe7c6..3f36c8d931229 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -541,7 +541,7 @@ static int msm_open(struct drm_device *dev, struct drm_file *file) return 0; } -static void msm_preclose(struct drm_device *dev, struct drm_file *file) +static void msm_postclose(struct drm_device *dev, struct drm_file *file) { struct msm_drm_private *priv = dev->dev_private; struct msm_file_private *ctx = file->driver_priv; @@ -814,7 +814,7 @@ static struct drm_driver msm_driver = { DRIVER_ATOMIC | DRIVER_MODESET, .open = msm_open, - .preclose = msm_preclose, + .postclose = msm_postclose, .lastclose = msm_lastclose, .irq_handler = msm_irq, .irq_preinstall = msm_irq_preinstall, From 02efb35959b0dd1792d2ce7fd133e3c7d902572b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:54 +0100 Subject: [PATCH 09/47] drm/msm: Simplify vblank event delivery The core takes care of handling the send_event vs. close() issues, we can remove that driver code. Cc: Rob Clark Cc: linux-arm-msm@vger.kernel.org Cc: freedreno@lists.freedesktop.org Signed-off-by: Daniel Vetter Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c | 12 +++--------- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 12 +++--------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index 1c29618f4ddba..f29194a74a19d 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -114,15 +114,9 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) spin_lock_irqsave(&dev->event_lock, flags); event = mdp4_crtc->event; if (event) { - /* if regular vblank case (!file) or if cancel-flip from - * preclose on file that requested flip, then send the - * event: - */ - if (!file || (event->base.file_priv == file)) { - mdp4_crtc->event = NULL; - DBG("%s: send event: %p", mdp4_crtc->name, event); - drm_crtc_send_vblank_event(crtc, event); - } + mdp4_crtc->event = NULL; + DBG("%s: send event: %p", mdp4_crtc->name, event); + drm_crtc_send_vblank_event(crtc, event); } spin_unlock_irqrestore(&dev->event_lock, flags); } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index d0c8b38b96ce5..87a19e0c0e67c 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -138,15 +138,9 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) spin_lock_irqsave(&dev->event_lock, flags); event = mdp5_crtc->event; if (event) { - /* if regular vblank case (!file) or if cancel-flip from - * preclose on file that requested flip, then send the - * event: - */ - if (!file || (event->base.file_priv == file)) { - mdp5_crtc->event = NULL; - DBG("%s: send event: %p", crtc->name, event); - drm_crtc_send_vblank_event(crtc, event); - } + mdp5_crtc->event = NULL; + DBG("%s: send event: %p", crtc->name, event); + drm_crtc_send_vblank_event(crtc, event); } spin_unlock_irqrestore(&dev->event_lock, flags); From 2002c9c33259fd9147ebe949142f0b585bbfe869 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 7 Mar 2017 09:50:27 -0700 Subject: [PATCH 10/47] drm/msm: Fix wrong pointer check in a5xx_destroy Instead of checking for a5xx_gpu->gpmu_iova during destroy we accidently check a5xx_gpu->gpmu_bo. Signed-off-by: Jordan Crouse Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 9c623fdacbe07..d08f78e8033b3 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016 The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -534,7 +534,7 @@ static void a5xx_destroy(struct msm_gpu *gpu) } if (a5xx_gpu->gpmu_bo) { - if (a5xx_gpu->gpmu_bo) + if (a5xx_gpu->gpmu_iova) msm_gem_put_iova(a5xx_gpu->gpmu_bo, gpu->id); drm_gem_object_unreference_unlocked(a5xx_gpu->gpmu_bo); } From 36849cc361428b0b60c9065f854fe22bd55ce3ab Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 7 Mar 2017 09:50:28 -0700 Subject: [PATCH 11/47] drm/msm: Don't increase priv->num_aspaces until we know that it fits priv->num_aspaces is increased and then checked to see if it still fits in the priv->aspace array. If it doesn't, we warn and exit but priv->num_aspaces remains incremented. Don't incremement the count until we know that it fits in the array. Signed-off-by: Jordan Crouse Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_drv.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 3f36c8d931229..87b5695d4034d 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -55,14 +55,13 @@ int msm_register_address_space(struct drm_device *dev, struct msm_gem_address_space *aspace) { struct msm_drm_private *priv = dev->dev_private; - int idx = priv->num_aspaces++; - if (WARN_ON(idx >= ARRAY_SIZE(priv->aspace))) + if (WARN_ON(priv->num_aspaces >= ARRAY_SIZE(priv->aspace))) return -EINVAL; - priv->aspace[idx] = aspace; + priv->aspace[priv->num_aspaces] = aspace; - return idx; + return priv->num_aspaces++; } #ifdef CONFIG_DRM_MSM_REGISTER_LOGGING From 7352fb5a5b6891c51429b465d16aa16e8391bfc0 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 7 Mar 2017 09:50:29 -0700 Subject: [PATCH 12/47] drm/msm: Pass interrupt status to a5xx_rbbm_err_irq() The interrupt status was being cleared before processing the handlers. a5xx_rbbm_err_irq() was checking the interrupt status again, which would likely turn out bad because the interrupt status would be 0 (or at least different). Pass the original status to the function instead. Also, skip clearing RBBM_AHB_ERROR from the interrupt status. The interrupt will keep firing until the error source is cleared. Skip the clear to avoid a storm until the error is cleared in a5xx_rbbm_err_irq(). Signed-off-by: Jordan Crouse Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index d08f78e8033b3..31a9bceed32ca 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -638,10 +638,8 @@ static void a5xx_cp_err_irq(struct msm_gpu *gpu) } } -static void a5xx_rbbm_err_irq(struct msm_gpu *gpu) +static void a5xx_rbbm_err_irq(struct msm_gpu *gpu, u32 status) { - u32 status = gpu_read(gpu, REG_A5XX_RBBM_INT_0_STATUS); - if (status & A5XX_RBBM_INT_0_MASK_RBBM_AHB_ERROR) { u32 val = gpu_read(gpu, REG_A5XX_RBBM_AHB_ERROR_STATUS); @@ -653,6 +651,10 @@ static void a5xx_rbbm_err_irq(struct msm_gpu *gpu) /* Clear the error */ gpu_write(gpu, REG_A5XX_RBBM_AHB_CMD, (1 << 4)); + + /* Clear the interrupt */ + gpu_write(gpu, REG_A5XX_RBBM_INT_CLEAR_CMD, + A5XX_RBBM_INT_0_MASK_RBBM_AHB_ERROR); } if (status & A5XX_RBBM_INT_0_MASK_RBBM_TRANSFER_TIMEOUT) @@ -704,10 +706,16 @@ static irqreturn_t a5xx_irq(struct msm_gpu *gpu) { u32 status = gpu_read(gpu, REG_A5XX_RBBM_INT_0_STATUS); - gpu_write(gpu, REG_A5XX_RBBM_INT_CLEAR_CMD, status); + /* + * Clear all the interrupts except RBBM_AHB_ERROR - if we clear it + * before the source is cleared the interrupt will storm. + */ + gpu_write(gpu, REG_A5XX_RBBM_INT_CLEAR_CMD, + status & ~A5XX_RBBM_INT_0_MASK_RBBM_AHB_ERROR); + /* Pass status to a5xx_rbbm_err_irq because we've already cleared it */ if (status & RBBM_ERROR_MASK) - a5xx_rbbm_err_irq(gpu); + a5xx_rbbm_err_irq(gpu, status); if (status & A5XX_RBBM_INT_0_MASK_CP_HW_ERROR) a5xx_cp_err_irq(gpu); From 22dd5c14be471564f80b270297b2d3a33906a7fb Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 7 Mar 2017 09:50:30 -0700 Subject: [PATCH 13/47] drm/msm: Support 64 bit iova in RD_CMDSTREAM_ADDR Output the upper 32 bits of a 64 bit iova in the RD_CMDSTREAM_ADDR section while maintaining backwards compatibility for tools that only understand 32 bit iovas. Signed-off-by: Jordan Crouse Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_rd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_rd.c b/drivers/gpu/drm/msm/msm_rd.c index 3df7322fd74ed..0e81faab2c50d 100644 --- a/drivers/gpu/drm/msm/msm_rd.c +++ b/drivers/gpu/drm/msm/msm_rd.c @@ -322,7 +322,7 @@ void msm_rd_dump_submit(struct msm_gem_submit *submit) } for (i = 0; i < submit->nr_cmds; i++) { - uint32_t iova = submit->cmd[i].iova; + uint64_t iova = submit->cmd[i].iova; uint32_t szd = submit->cmd[i].size; /* in dwords */ /* snapshot cmdstream bo's (if we haven't already): */ @@ -341,7 +341,7 @@ void msm_rd_dump_submit(struct msm_gem_submit *submit) case MSM_SUBMIT_CMD_CTX_RESTORE_BUF: case MSM_SUBMIT_CMD_BUF: rd_write_section(rd, RD_CMDSTREAM_ADDR, - (uint32_t[2]){ iova, szd }, 8); + (uint32_t[3]){ iova, szd, iova >> 32 }, 12); break; } } From 0041ba396dd19c22aa884cffd2a50d8bbdb46b48 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 7 Mar 2017 10:02:51 -0700 Subject: [PATCH 14/47] drm/msm: Don't allow zero sized buffer objects Zero sized buffer objects tend to make various bits of the GEM infrastructure complain: WARNING: CPU: 1 PID: 2323 at drivers/gpu/drm/drm_mm.c:389 drm_mm_insert_node_generic+0x258/0x2f0 Modules linked in: CPU: 1 PID: 2323 Comm: drm-api-test Tainted: G W 4.9.0-rc4-00906-g693af44 #213 Hardware name: Qualcomm Technologies, Inc. DB820c (DT) task: ffff8000d7353400 task.stack: ffff8000d7720000 PC is at drm_mm_insert_node_generic+0x258/0x2f0 LR is at drm_vma_offset_add+0x4c/0x70 Zero sized buffers serve no appreciable value to the user so disallow them at create time. Signed-off-by: Jordan Crouse Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_gem.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 59811f29607de..68e509b3b9e4d 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -812,6 +812,12 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, size = PAGE_ALIGN(size); + /* Disallow zero sized objects as they make the underlying + * infrastructure grumpy + */ + if (size == 0) + return ERR_PTR(-EINVAL); + ret = msm_gem_new_impl(dev, size, flags, NULL, &obj); if (ret) goto fail; From 8e25a0e266b05f414a63bf04b30d9a7e085e94fc Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 16 Feb 2017 16:29:04 +0530 Subject: [PATCH 15/47] drm/msm/dsi: Fix bug in dsi_mgr_phy_enable A recent commit introduces a bug in dsi_mgr_phy_enable. In the non dual DSI mode, we reset the mdsi (master DSI) PHY. This isn't right since master and slave DSI exist only in dual DSI mode. For the normal mode of operation, we should simply reset the PHY of the DSI device (i.e. msm_dsi) corresponding to the current bridge. Usage of the wrong DSI pointer also resulted in a static checker warning. That too is resolved with this fix. Fixes: b62aa70a98c5 (drm/msm/dsi: Move PHY operations out of host) Reported-by: Dan Carpenter Signed-off-by: Archit Taneja Reviewed-by: Rob Clark Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/dsi/dsi_manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index 921270ea6059d..a879ffa534b4d 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -171,7 +171,7 @@ dsi_mgr_phy_enable(int id, } } } else { - msm_dsi_host_reset_phy(mdsi->host); + msm_dsi_host_reset_phy(msm_dsi->host); ret = enable_phy(msm_dsi, src_pll_id, &shared_timings[id]); if (ret) return ret; From 87878d2627a5fe1668591f7767306ee81693593f Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Fri, 17 Mar 2017 09:09:48 +0530 Subject: [PATCH 16/47] drm/msm/mdp5: Update SSPP_MAX value 'SSPP_MAX + 1' is the max number of hwpipes that can be present on a MDP5 platform. Recently, 2 new cursor hwpipes were added, which caused overflows in arrays that used SSPP_MAX to represent the number of elements. Update the SSPP_MAX value to incorporate the extra hwpipes. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h index 611da7a660c94..238901987e00b 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h @@ -18,7 +18,8 @@ #ifndef __MDP5_PIPE_H__ #define __MDP5_PIPE_H__ -#define SSPP_MAX (SSPP_RGB3 + 1) /* TODO: Add SSPP_MAX in mdp5.xml.h */ +/* TODO: Add SSPP_MAX in mdp5.xml.h */ +#define SSPP_MAX (SSPP_CURSOR1 + 1) /* represents a hw pipe, which is dynamically assigned to a plane */ struct mdp5_hw_pipe { From 1225bb2b45f1c03dfdf29ab3b4cf065ab6da246f Mon Sep 17 00:00:00 2001 From: Vinay Simha BN Date: Tue, 14 Mar 2017 10:55:56 +0530 Subject: [PATCH 17/47] drm/msm/hdmi: redefinitions of macros not required 4 macros already defined in hdmi.h, which is not required to redefine in hdmi_audio.c Signed-off-by: Vinay Simha BN Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/hdmi/hdmi_audio.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_audio.c b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c index a54d3bb5baad9..8177e8511afd8 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_audio.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c @@ -18,13 +18,6 @@ #include #include "hdmi.h" - -/* Supported HDMI Audio channels */ -#define MSM_HDMI_AUDIO_CHANNEL_2 0 -#define MSM_HDMI_AUDIO_CHANNEL_4 1 -#define MSM_HDMI_AUDIO_CHANNEL_6 2 -#define MSM_HDMI_AUDIO_CHANNEL_8 3 - /* maps MSM_HDMI_AUDIO_CHANNEL_n consts used by audio driver to # of channels: */ static int nchannels[] = { 2, 4, 6, 8 }; From 01f8a9696419133ff4305024f3211f095025d130 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:57:54 +0530 Subject: [PATCH 18/47] drm/msm/mdp5: Bring back pipe_lock to mdp5_plane struct We'd previously moved the pipe_lock spinlock to the hwpipe struct. Bring it back to mdp5_plane. We will need this because an mdp5_plane in the future could comprise of 2 hw pipes. It makes more sense to have a single lock to protect the registers for the hw pipes used by a plane, rather than trying to take individual locks per hwpipe when committing a configuration. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c | 2 -- drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h | 1 - drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 9 +++++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c index 35c4dabb0c0c7..2bfac37126853 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c @@ -135,7 +135,5 @@ struct mdp5_hw_pipe *mdp5_pipe_init(enum mdp5_pipe pipe, hwpipe->caps = caps; hwpipe->flush_mask = mdp_ctl_flush_mask_pipe(pipe); - spin_lock_init(&hwpipe->pipe_lock); - return hwpipe; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h index 238901987e00b..924c3e6f95173 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h @@ -28,7 +28,6 @@ struct mdp5_hw_pipe { const char *name; enum mdp5_pipe pipe; - spinlock_t pipe_lock; /* protect REG_MDP5_PIPE_* registers */ uint32_t reg_offset; uint32_t caps; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 60a5451ae0b9c..49e50debda902 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -22,6 +22,8 @@ struct mdp5_plane { struct drm_plane base; + spinlock_t pipe_lock; /* protect REG_MDP5_PIPE_* registers */ + uint32_t nformats; uint32_t formats[32]; }; @@ -719,6 +721,7 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_rect *src, struct drm_rect *dest) { + struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct drm_plane_state *pstate = plane->state; struct mdp5_hw_pipe *hwpipe = to_mdp5_plane_state(pstate)->hwpipe; struct mdp5_kms *mdp5_kms = get_kms(plane); @@ -798,7 +801,7 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, hflip = !!(rotation & DRM_REFLECT_X); vflip = !!(rotation & DRM_REFLECT_Y); - spin_lock_irqsave(&hwpipe->pipe_lock, flags); + spin_lock_irqsave(&mdp5_plane->pipe_lock, flags); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe), MDP5_PIPE_SRC_IMG_SIZE_WIDTH(min(fb->width, src_w)) | @@ -877,7 +880,7 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, set_scanout_locked(plane, fb); - spin_unlock_irqrestore(&hwpipe->pipe_lock, flags); + spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags); return ret; } @@ -998,6 +1001,8 @@ struct drm_plane *mdp5_plane_init(struct drm_device *dev, mdp5_plane->nformats = mdp_get_formats(mdp5_plane->formats, ARRAY_SIZE(mdp5_plane->formats), false); + spin_lock_init(&mdp5_plane->pipe_lock); + if (type == DRM_PLANE_TYPE_CURSOR) ret = drm_universal_plane_init(dev, plane, 0xff, &mdp5_cursor_plane_funcs, From 384dbd8cda5f9e1090b3593fd63ca3c84b2fdd1b Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:57:55 +0530 Subject: [PATCH 19/47] drm/msm/mdp5: describe LM instances in mdp5_cfg The number of Layer Mixers and the downstream blocks (DSPPs and PPs) connected to each LM can vary with different MDP5 revisions. These parameters are also static. Keep the per instance LM data in mdp5_cfg. This will avoid the need to have macros which identify PP id or DSPP id the LM is connected to. We don't configure DSPPs at the moment, but keeping the DSPP instance # here might come handy later. Also add a 'caps' field that identifies features supported by a LM instance. Introduce the caps MDP_LM_CAP_DISPLAY and MDP_LM_CAP_WB that identify whether a LM instance can be used for display or writeback. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c | 72 +++++++++++++++++++++++++ drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h | 8 +++ drivers/gpu/drm/msm/mdp/mdp_kms.h | 4 ++ 3 files changed, 84 insertions(+) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c index ba2d017f65914..124aa1e62aa5c 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c @@ -70,6 +70,18 @@ const struct mdp5_cfg_hw msm8x74v1_config = { .lm = { .count = 5, .base = { 0x03100, 0x03500, 0x03900, 0x03d00, 0x04100 }, + .instances = { + { .id = 0, .pp = 0, .dspp = 0, + .caps = MDP_LM_CAP_DISPLAY, }, + { .id = 1, .pp = 1, .dspp = 1, + .caps = MDP_LM_CAP_DISPLAY, }, + { .id = 2, .pp = 2, .dspp = 2, + .caps = MDP_LM_CAP_DISPLAY, }, + { .id = 3, .pp = -1, .dspp = -1, + .caps = MDP_LM_CAP_WB }, + { .id = 4, .pp = -1, .dspp = -1, + .caps = MDP_LM_CAP_WB }, + }, .nb_stages = 5, }, .dspp = { @@ -134,6 +146,18 @@ const struct mdp5_cfg_hw msm8x74v2_config = { .lm = { .count = 5, .base = { 0x03100, 0x03500, 0x03900, 0x03d00, 0x04100 }, + .instances = { + { .id = 0, .pp = 0, .dspp = 0, + .caps = MDP_LM_CAP_DISPLAY, }, + { .id = 1, .pp = 1, .dspp = 1, + .caps = MDP_LM_CAP_DISPLAY, }, + { .id = 2, .pp = 2, .dspp = 2, + .caps = MDP_LM_CAP_DISPLAY, }, + { .id = 3, .pp = -1, .dspp = -1, + .caps = MDP_LM_CAP_WB, }, + { .id = 4, .pp = -1, .dspp = -1, + .caps = MDP_LM_CAP_WB, }, + }, .nb_stages = 5, .max_width = 2048, .max_height = 0xFFFF, @@ -211,6 +235,20 @@ const struct mdp5_cfg_hw apq8084_config = { .lm = { .count = 6, .base = { 0x03900, 0x03d00, 0x04100, 0x04500, 0x04900, 0x04d00 }, + .instances = { + { .id = 0, .pp = 0, .dspp = 0, + .caps = MDP_LM_CAP_DISPLAY, }, + { .id = 1, .pp = 1, .dspp = 1, + .caps = MDP_LM_CAP_DISPLAY, }, + { .id = 2, .pp = 2, .dspp = 2, + .caps = MDP_LM_CAP_DISPLAY, }, + { .id = 3, .pp = -1, .dspp = -1, + .caps = MDP_LM_CAP_WB, }, + { .id = 4, .pp = -1, .dspp = -1, + .caps = MDP_LM_CAP_WB, }, + { .id = 5, .pp = 3, .dspp = 3, + .caps = MDP_LM_CAP_DISPLAY, }, + }, .nb_stages = 5, .max_width = 2048, .max_height = 0xFFFF, @@ -282,6 +320,12 @@ const struct mdp5_cfg_hw msm8x16_config = { .lm = { .count = 2, /* LM0 and LM3 */ .base = { 0x44000, 0x47000 }, + .instances = { + { .id = 0, .pp = 0, .dspp = 0, + .caps = MDP_LM_CAP_DISPLAY, }, + { .id = 3, .pp = -1, .dspp = -1, + .caps = MDP_LM_CAP_WB }, + }, .nb_stages = 8, .max_width = 2048, .max_height = 0xFFFF, @@ -350,6 +394,20 @@ const struct mdp5_cfg_hw msm8x94_config = { .lm = { .count = 6, .base = { 0x44000, 0x45000, 0x46000, 0x47000, 0x48000, 0x49000 }, + .instances = { + { .id = 0, .pp = 0, .dspp = 0, + .caps = MDP_LM_CAP_DISPLAY, }, + { .id = 1, .pp = 1, .dspp = 1, + .caps = MDP_LM_CAP_DISPLAY, }, + { .id = 2, .pp = 2, .dspp = 2, + .caps = MDP_LM_CAP_DISPLAY, }, + { .id = 3, .pp = -1, .dspp = -1, + .caps = MDP_LM_CAP_WB, }, + { .id = 4, .pp = -1, .dspp = -1, + .caps = MDP_LM_CAP_WB, }, + { .id = 5, .pp = 3, .dspp = 3, + .caps = MDP_LM_CAP_DISPLAY, }, + }, .nb_stages = 8, .max_width = 2048, .max_height = 0xFFFF, @@ -434,6 +492,20 @@ const struct mdp5_cfg_hw msm8x96_config = { .lm = { .count = 6, .base = { 0x44000, 0x45000, 0x46000, 0x47000, 0x48000, 0x49000 }, + .instances = { + { .id = 0, .pp = 0, .dspp = 0, + .caps = MDP_LM_CAP_DISPLAY }, + { .id = 1, .pp = 1, .dspp = 1, + .caps = MDP_LM_CAP_DISPLAY, }, + { .id = 2, .pp = 2, .dspp = -1, + .caps = MDP_LM_CAP_DISPLAY }, + { .id = 3, .pp = -1, .dspp = -1, + .caps = MDP_LM_CAP_WB, }, + { .id = 4, .pp = -1, .dspp = -1, + .caps = MDP_LM_CAP_WB, }, + { .id = 5, .pp = 3, .dspp = -1, + .caps = MDP_LM_CAP_DISPLAY, }, + }, .nb_stages = 8, .max_width = 2560, .max_height = 0xFFFF, diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h index b1c7daaede86b..75910d0f2f4c7 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h @@ -39,8 +39,16 @@ struct mdp5_sub_block { MDP5_SUB_BLOCK_DEFINITION; }; +struct mdp5_lm_instance { + int id; + int pp; + int dspp; + uint32_t caps; +}; + struct mdp5_lm_block { MDP5_SUB_BLOCK_DEFINITION; + struct mdp5_lm_instance instances[MAX_BASES]; uint32_t nb_stages; /* number of stages per blender */ uint32_t max_width; /* Maximum output resolution */ uint32_t max_height; diff --git a/drivers/gpu/drm/msm/mdp/mdp_kms.h b/drivers/gpu/drm/msm/mdp/mdp_kms.h index 7574cdfef418b..bf4db664ee865 100644 --- a/drivers/gpu/drm/msm/mdp/mdp_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp_kms.h @@ -114,6 +114,10 @@ const struct msm_format *mdp_get_format(struct msm_kms *kms, uint32_t format); #define MDP_PIPE_CAP_SW_PIX_EXT BIT(5) #define MDP_PIPE_CAP_CURSOR BIT(6) +/* MDP layer mixer caps */ +#define MDP_LM_CAP_DISPLAY BIT(0) +#define MDP_LM_CAP_WB BIT(1) + static inline bool pipe_supports_yuv(uint32_t pipe_caps) { return (pipe_caps & MDP_PIPE_CAP_SCALE) && From 6803c60630ac71269eb62a8efb29f8c7ca64f003 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:57:56 +0530 Subject: [PATCH 20/47] drm/msm/mdp5: Add structs for hw Layer Mixers Create a struct to represent MDP5 Layer Mixer instances. This will eventually allow us to detach CRTCs from the Layer Mixers, and generally clean things up a bit. This is very similar to how hwpipes were previously abstracted away from drm planes. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/Makefile | 1 + drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 33 +++++++++++++++++ drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 4 +++ drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c | 44 +++++++++++++++++++++++ drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h | 37 +++++++++++++++++++ 5 files changed, 119 insertions(+) create mode 100644 drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c create mode 100644 drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 39055362da95b..5241ac8803ba6 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -40,6 +40,7 @@ msm-y := \ mdp/mdp5/mdp5_mdss.o \ mdp/mdp5/mdp5_kms.o \ mdp/mdp5/mdp5_pipe.o \ + mdp/mdp5/mdp5_mixer.o \ mdp/mdp5/mdp5_plane.o \ mdp/mdp5/mdp5_smp.o \ msm_atomic.o \ diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index 41ccd2a15d3c6..23141b70d1c70 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -165,6 +165,9 @@ static void mdp5_kms_destroy(struct msm_kms *kms) struct msm_gem_address_space *aspace = mdp5_kms->aspace; int i; + for (i = 0; i < mdp5_kms->num_hwmixers; i++) + mdp5_mixer_destroy(mdp5_kms->hwmixers[i]); + for (i = 0; i < mdp5_kms->num_hwpipes; i++) mdp5_pipe_destroy(mdp5_kms->hwpipes[i]); @@ -829,6 +832,32 @@ static int hwpipe_init(struct mdp5_kms *mdp5_kms) return 0; } +static int hwmixer_init(struct mdp5_kms *mdp5_kms) +{ + struct drm_device *dev = mdp5_kms->dev; + const struct mdp5_cfg_hw *hw_cfg; + int i, ret; + + hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); + + for (i = 0; i < hw_cfg->lm.count; i++) { + struct mdp5_hw_mixer *mixer; + + mixer = mdp5_mixer_init(&hw_cfg->lm.instances[i]); + if (IS_ERR(mixer)) { + ret = PTR_ERR(mixer); + dev_err(dev->dev, "failed to construct LM%d (%d)\n", + i, ret); + return ret; + } + + mixer->idx = mdp5_kms->num_hwmixers; + mdp5_kms->hwmixers[mdp5_kms->num_hwmixers++] = mixer; + } + + return 0; +} + static int mdp5_init(struct platform_device *pdev, struct drm_device *dev) { struct msm_drm_private *priv = dev->dev_private; @@ -929,6 +958,10 @@ static int mdp5_init(struct platform_device *pdev, struct drm_device *dev) if (ret) goto fail; + ret = hwmixer_init(mdp5_kms); + if (ret) + goto fail; + /* set uninit-ed kms */ priv->kms = &mdp5_kms->base.base; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index 9de471191ebaa..9abb2274379f4 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -25,6 +25,7 @@ #include "mdp5.xml.h" #include "mdp5_ctl.h" #include "mdp5_pipe.h" +#include "mdp5_mixer.h" #include "mdp5_smp.h" struct mdp5_state; @@ -39,6 +40,9 @@ struct mdp5_kms { unsigned num_hwpipes; struct mdp5_hw_pipe *hwpipes[SSPP_MAX]; + unsigned num_hwmixers; + struct mdp5_hw_mixer *hwmixers[8]; + struct mdp5_cfg_handler *cfg; uint32_t caps; /* MDP capabilities (MDP_CAP_XXX bits) */ diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c new file mode 100644 index 0000000000000..dd38f0bc2494e --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "mdp5_kms.h" + +void mdp5_mixer_destroy(struct mdp5_hw_mixer *mixer) +{ + kfree(mixer); +} + +static const char * const mixer_names[] = { + "LM0", "LM1", "LM2", "LM3", "LM4", "LM5", +}; + +struct mdp5_hw_mixer *mdp5_mixer_init(const struct mdp5_lm_instance *lm) +{ + struct mdp5_hw_mixer *mixer; + + mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); + if (!mixer) + return ERR_PTR(-ENOMEM); + + mixer->name = mixer_names[lm->id]; + mixer->lm = lm->id; + mixer->caps = lm->caps; + mixer->pp = lm->pp; + mixer->dspp = lm->dspp; + mixer->flush_mask = mdp_ctl_flush_mask_lm(lm->id); + + return mixer; +} diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h new file mode 100644 index 0000000000000..08d2173703bfc --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef __MDP5_LM_H__ +#define __MDP5_LM_H__ + +/* represents a hw Layer Mixer, one (or more) is dynamically assigned to a crtc */ +struct mdp5_hw_mixer { + int idx; + + const char *name; + + int lm; /* the LM instance # */ + uint32_t caps; + int pp; + int dspp; + + uint32_t flush_mask; /* used to commit LM registers */ +}; + +struct mdp5_hw_mixer *mdp5_mixer_init(const struct mdp5_lm_instance *lm); +void mdp5_mixer_destroy(struct mdp5_hw_mixer *lm); + +#endif /* __MDP5_LM_H__ */ From adfc0e63abe552bf1b9d18b3073bf2a421f7ea60 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:57:57 +0530 Subject: [PATCH 21/47] drm/msm/mdp5: Start using mdp5_hw_mixer Use the mdp5_hw_mixer struct in the mdp5_crtc and mdp5_ctl instead of using the LM index. Like before, the Layer Mixers are assigned statically to the CRTCs. The hwmixer(s) will later be dynamically assigned to CRTCs. For now, ignore the hwmixers that can only do WB. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- .../gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c | 9 ++-- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 42 ++++++++++++------- drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c | 38 +++++++++-------- drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h | 2 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c | 4 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 6 ++- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 4 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c | 4 ++ 8 files changed, 66 insertions(+), 43 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c index df1c8adec3f3c..2e6ceafbd3e24 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c @@ -51,7 +51,8 @@ static int pingpong_tearcheck_setup(struct drm_encoder *encoder, struct device *dev = encoder->dev->dev; u32 total_lines_x100, vclks_line, cfg; long vsync_clk_speed; - int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc)); + struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc); + int pp_id = GET_PING_PONG_ID(mixer->lm); if (IS_ERR_OR_NULL(mdp5_kms->vsync_clk)) { dev_err(dev, "vsync_clk is not initialized\n"); @@ -94,7 +95,8 @@ static int pingpong_tearcheck_setup(struct drm_encoder *encoder, static int pingpong_tearcheck_enable(struct drm_encoder *encoder) { struct mdp5_kms *mdp5_kms = get_kms(encoder); - int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc)); + struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc); + int pp_id = GET_PING_PONG_ID(mixer->lm); int ret; ret = clk_set_rate(mdp5_kms->vsync_clk, @@ -119,7 +121,8 @@ static int pingpong_tearcheck_enable(struct drm_encoder *encoder) static void pingpong_tearcheck_disable(struct drm_encoder *encoder) { struct mdp5_kms *mdp5_kms = get_kms(encoder); - int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc)); + struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc); + int pp_id = GET_PING_PONG_ID(mixer->lm); mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 0); clk_disable_unprepare(mdp5_kms->vsync_clk); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 87a19e0c0e67c..ffdb31c4419ff 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -32,10 +32,8 @@ struct mdp5_crtc { int id; bool enabled; - /* layer mixer used for this CRTC (+ its lock): */ -#define GET_LM_ID(crtc_id) ((crtc_id == 3) ? 5 : crtc_id) - int lm; - spinlock_t lm_lock; /* protect REG_MDP5_LM_* registers */ + struct mdp5_hw_mixer *mixer; + spinlock_t lm_lock; /* protect REG_MDP5_LM_* registers */ /* CTL used for this CRTC: */ struct mdp5_ctl *ctl; @@ -111,6 +109,7 @@ static u32 crtc_flush(struct drm_crtc *crtc, u32 flush_mask) static u32 crtc_flush_all(struct drm_crtc *crtc) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct mdp5_hw_mixer *mixer; struct drm_plane *plane; uint32_t flush_mask = 0; @@ -122,7 +121,8 @@ static u32 crtc_flush_all(struct drm_crtc *crtc) flush_mask |= mdp5_plane_get_flush(plane); } - flush_mask |= mdp_ctl_flush_mask_lm(mdp5_crtc->lm); + mixer = mdp5_crtc->mixer; + flush_mask |= mdp_ctl_flush_mask_lm(mixer->lm); return crtc_flush(crtc, flush_mask); } @@ -201,7 +201,8 @@ static void blend_setup(struct drm_crtc *crtc) const struct mdp5_cfg_hw *hw_cfg; struct mdp5_plane_state *pstate, *pstates[STAGE_MAX + 1] = {NULL}; const struct mdp_format *format; - uint32_t lm = mdp5_crtc->lm; + struct mdp5_hw_mixer *mixer = mdp5_crtc->mixer; + uint32_t lm = mixer->lm; uint32_t blend_op, fg_alpha, bg_alpha, ctl_blend_flags = 0; unsigned long flags; enum mdp5_pipe stage[STAGE_MAX + 1] = { SSPP_NONE }; @@ -302,6 +303,8 @@ static void mdp5_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct mdp5_kms *mdp5_kms = get_kms(crtc); + struct mdp5_hw_mixer *mixer = mdp5_crtc->mixer; + uint32_t lm = mixer->lm; unsigned long flags; struct drm_display_mode *mode; @@ -320,7 +323,7 @@ static void mdp5_crtc_mode_set_nofb(struct drm_crtc *crtc) mode->type, mode->flags); spin_lock_irqsave(&mdp5_crtc->lm_lock, flags); - mdp5_write(mdp5_kms, REG_MDP5_LM_OUT_SIZE(mdp5_crtc->lm), + mdp5_write(mdp5_kms, REG_MDP5_LM_OUT_SIZE(lm), MDP5_LM_OUT_SIZE_WIDTH(mode->hdisplay) | MDP5_LM_OUT_SIZE_HEIGHT(mode->vdisplay)); spin_unlock_irqrestore(&mdp5_crtc->lm_lock, flags); @@ -555,7 +558,7 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, if (ret) return -EINVAL; - lm = mdp5_crtc->lm; + lm = mdp5_crtc->mixer->lm; stride = width * drm_format_plane_cpp(DRM_FORMAT_ARGB8888, 0); spin_lock_irqsave(&mdp5_crtc->cursor.lock, flags); @@ -607,6 +610,7 @@ static int mdp5_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) { struct mdp5_kms *mdp5_kms = get_kms(crtc); struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + uint32_t lm = mdp5_crtc->mixer->lm; uint32_t flush_mask = mdp_ctl_flush_mask_cursor(0); uint32_t roi_w; uint32_t roi_h; @@ -622,10 +626,10 @@ static int mdp5_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) get_roi(crtc, &roi_w, &roi_h); spin_lock_irqsave(&mdp5_crtc->cursor.lock, flags); - mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_SIZE(mdp5_crtc->lm), + mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_SIZE(lm), MDP5_LM_CURSOR_SIZE_ROI_H(roi_h) | MDP5_LM_CURSOR_SIZE_ROI_W(roi_w)); - mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_START_XY(mdp5_crtc->lm), + mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_START_XY(lm), MDP5_LM_CURSOR_START_XY_Y_START(y) | MDP5_LM_CURSOR_START_XY_X_START(x)); spin_unlock_irqrestore(&mdp5_crtc->cursor.lock, flags); @@ -709,7 +713,8 @@ static void mdp5_crtc_wait_for_pp_done(struct drm_crtc *crtc) ret = wait_for_completion_timeout(&mdp5_crtc->pp_completion, msecs_to_jiffies(50)); if (ret == 0) - dev_warn(dev->dev, "pp done time out, lm=%d\n", mdp5_crtc->lm); + dev_warn(dev->dev, "pp done time out, lm=%d\n", + mdp5_crtc->mixer->lm); } static void mdp5_crtc_wait_for_flush_done(struct drm_crtc *crtc) @@ -749,7 +754,8 @@ void mdp5_crtc_set_pipeline(struct drm_crtc *crtc, { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct mdp5_kms *mdp5_kms = get_kms(crtc); - int lm = mdp5_crtc_get_lm(crtc); + struct mdp5_hw_mixer *mixer = mdp5_crtc->mixer; + uint32_t lm = mixer->lm; /* now that we know what irq's we want: */ mdp5_crtc->err.irqmask = intf2err(intf->num); @@ -769,7 +775,7 @@ void mdp5_crtc_set_pipeline(struct drm_crtc *crtc, mdp_irq_update(&mdp5_kms->base); mdp5_crtc->ctl = ctl; - mdp5_ctl_set_pipeline(ctl, intf, lm); + mdp5_ctl_set_pipeline(ctl, intf, mixer); } struct mdp5_ctl *mdp5_crtc_get_ctl(struct drm_crtc *crtc) @@ -779,10 +785,11 @@ struct mdp5_ctl *mdp5_crtc_get_ctl(struct drm_crtc *crtc) return mdp5_crtc->ctl; } -int mdp5_crtc_get_lm(struct drm_crtc *crtc) +struct mdp5_hw_mixer *mdp5_crtc_get_mixer(struct drm_crtc *crtc) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); - return WARN_ON(!crtc) ? -EINVAL : mdp5_crtc->lm; + return WARN_ON(!crtc) || WARN_ON(!mdp5_crtc->mixer) ? + ERR_PTR(-EINVAL) : mdp5_crtc->mixer; } void mdp5_crtc_wait_for_commit_done(struct drm_crtc *crtc) @@ -802,6 +809,7 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, { struct drm_crtc *crtc = NULL; struct mdp5_crtc *mdp5_crtc; + struct mdp5_kms *mdp5_kms; mdp5_crtc = kzalloc(sizeof(*mdp5_crtc), GFP_KERNEL); if (!mdp5_crtc) @@ -810,7 +818,6 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, crtc = &mdp5_crtc->base; mdp5_crtc->id = id; - mdp5_crtc->lm = GET_LM_ID(id); spin_lock_init(&mdp5_crtc->lm_lock); spin_lock_init(&mdp5_crtc->cursor.lock); @@ -832,5 +839,8 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs); plane->crtc = crtc; + mdp5_kms = get_kms(crtc); + mdp5_crtc->mixer = mdp5_kms->hwmixers[id]; + return crtc; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c index 8b93f7e13200e..fa4f27a1a5514 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c @@ -43,7 +43,7 @@ struct mdp5_ctl { struct mdp5_ctl_manager *ctlm; u32 id; - int lm; + struct mdp5_hw_mixer *mixer; /* CTL status bitmask */ u32 status; @@ -174,8 +174,8 @@ static void set_ctl_op(struct mdp5_ctl *ctl, struct mdp5_interface *intf) spin_unlock_irqrestore(&ctl->hw_lock, flags); } -int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, - struct mdp5_interface *intf, int lm) +int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_interface *intf, + struct mdp5_hw_mixer *mixer) { struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm; struct mdp5_kms *mdp5_kms = get_kms(ctl_mgr); @@ -187,11 +187,11 @@ int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, return -EINVAL; } - ctl->lm = lm; + ctl->mixer = mixer; memcpy(&ctl->pipeline.intf, intf, sizeof(*intf)); - ctl->pipeline.start_mask = mdp_ctl_flush_mask_lm(ctl->lm) | + ctl->pipeline.start_mask = mdp_ctl_flush_mask_lm(mixer->lm) | mdp_ctl_flush_mask_encoder(intf); /* Virtual interfaces need not set a display intf (e.g.: Writeback) */ @@ -241,7 +241,7 @@ static void refill_start_mask(struct mdp5_ctl *ctl) struct op_mode *pipeline = &ctl->pipeline; struct mdp5_interface *intf = &ctl->pipeline.intf; - pipeline->start_mask = mdp_ctl_flush_mask_lm(ctl->lm); + pipeline->start_mask = mdp_ctl_flush_mask_lm(ctl->mixer->lm); /* * Writeback encoder needs to program & flush @@ -285,24 +285,24 @@ int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, int cursor_id, bool enable) struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm; unsigned long flags; u32 blend_cfg; - int lm = ctl->lm; + struct mdp5_hw_mixer *mixer = ctl->mixer; - if (unlikely(WARN_ON(lm < 0))) { - dev_err(ctl_mgr->dev->dev, "CTL %d cannot find LM: %d", - ctl->id, lm); + if (unlikely(WARN_ON(!mixer))) { + dev_err(ctl_mgr->dev->dev, "CTL %d cannot find LM", + ctl->id); return -EINVAL; } spin_lock_irqsave(&ctl->hw_lock, flags); - blend_cfg = ctl_read(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, lm)); + blend_cfg = ctl_read(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, mixer->lm)); if (enable) blend_cfg |= MDP5_CTL_LAYER_REG_CURSOR_OUT; else blend_cfg &= ~MDP5_CTL_LAYER_REG_CURSOR_OUT; - ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, lm), blend_cfg); + ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, mixer->lm), blend_cfg); ctl->cursor_on = enable; spin_unlock_irqrestore(&ctl->hw_lock, flags); @@ -358,6 +358,7 @@ static u32 mdp_ctl_blend_ext_mask(enum mdp5_pipe pipe, int mdp5_ctl_blend(struct mdp5_ctl *ctl, enum mdp5_pipe *stage, u32 stage_cnt, u32 ctl_blend_op_flags) { + struct mdp5_hw_mixer *mixer = ctl->mixer; unsigned long flags; u32 blend_cfg = 0, blend_ext_cfg = 0; int i, start_stage; @@ -378,13 +379,14 @@ int mdp5_ctl_blend(struct mdp5_ctl *ctl, enum mdp5_pipe *stage, u32 stage_cnt, if (ctl->cursor_on) blend_cfg |= MDP5_CTL_LAYER_REG_CURSOR_OUT; - ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, ctl->lm), blend_cfg); - ctl_write(ctl, REG_MDP5_CTL_LAYER_EXT_REG(ctl->id, ctl->lm), blend_ext_cfg); + ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, mixer->lm), blend_cfg); + ctl_write(ctl, REG_MDP5_CTL_LAYER_EXT_REG(ctl->id, mixer->lm), + blend_ext_cfg); spin_unlock_irqrestore(&ctl->hw_lock, flags); - ctl->pending_ctl_trigger = mdp_ctl_flush_mask_lm(ctl->lm); + ctl->pending_ctl_trigger = mdp_ctl_flush_mask_lm(mixer->lm); - DBG("lm%d: blend config = 0x%08x. ext_cfg = 0x%08x", ctl->lm, + DBG("lm%d: blend config = 0x%08x. ext_cfg = 0x%08x", mixer->lm, blend_cfg, blend_ext_cfg); return 0; @@ -452,7 +454,7 @@ static u32 fix_sw_flush(struct mdp5_ctl *ctl, u32 flush_mask) /* for some targets, cursor bit is the same as LM bit */ if (BIT_NEEDS_SW_FIX(MDP5_CTL_FLUSH_CURSOR_0)) - sw_mask |= mdp_ctl_flush_mask_lm(ctl->lm); + sw_mask |= mdp_ctl_flush_mask_lm(ctl->mixer->lm); return sw_mask; } @@ -620,7 +622,7 @@ struct mdp5_ctl *mdp5_ctlm_request(struct mdp5_ctl_manager *ctl_mgr, found: ctl = &ctl_mgr->ctls[c]; ctl->pipeline.intf.num = intf_num; - ctl->lm = -1; + ctl->mixer = NULL; ctl->status |= CTL_STAT_BUSY; ctl->pending_ctl_trigger = 0; DBG("CTL %d allocated", ctl->id); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h index fda00d33e4dbc..882c9d2be3650 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h @@ -38,7 +38,7 @@ int mdp5_ctl_get_ctl_id(struct mdp5_ctl *ctl); struct mdp5_interface; int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_interface *intf, - int lm); + struct mdp5_hw_mixer *lm); int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, bool enabled); int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, int cursor_id, bool enable); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c index 80fa482ae8ed0..68d048f040f00 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c @@ -215,7 +215,7 @@ static void mdp5_vid_encoder_disable(struct drm_encoder *encoder) struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); struct mdp5_kms *mdp5_kms = get_kms(encoder); struct mdp5_ctl *ctl = mdp5_encoder->ctl; - int lm = mdp5_crtc_get_lm(encoder->crtc); + struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc); struct mdp5_interface *intf = &mdp5_encoder->intf; int intfn = mdp5_encoder->intf.num; unsigned long flags; @@ -238,7 +238,7 @@ static void mdp5_vid_encoder_disable(struct drm_encoder *encoder) * the settings changes for the new modeset (like new * scanout buffer) don't latch properly.. */ - mdp_irq_wait(&mdp5_kms->base, intf2vblank(lm, intf)); + mdp_irq_wait(&mdp5_kms->base, intf2vblank(mixer->lm, intf)); bs_set(mdp5_encoder, 0); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index 23141b70d1c70..e8174d4847b0d 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -428,7 +428,7 @@ static int modeset_init(struct mdp5_kms *mdp5_kms) * the MDP5 interfaces) than the number of layer mixers present in HW, * but let's be safe here anyway */ - num_crtcs = min(priv->num_encoders, mdp5_cfg->lm.count); + num_crtcs = min(priv->num_encoders, mdp5_kms->num_hwmixers); /* * Construct planes equaling the number of hw pipes, and CRTCs for the @@ -851,6 +851,10 @@ static int hwmixer_init(struct mdp5_kms *mdp5_kms) return ret; } + /* Don't create LMs connected to WB for now */ + if (!mixer) + continue; + mixer->idx = mdp5_kms->num_hwmixers; mdp5_kms->hwmixers[mdp5_kms->num_hwmixers++] = mixer; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index 9abb2274379f4..13d9ccf378117 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -23,9 +23,9 @@ #include "mdp/mdp_kms.h" #include "mdp5_cfg.h" /* must be included before mdp5.xml.h */ #include "mdp5.xml.h" -#include "mdp5_ctl.h" #include "mdp5_pipe.h" #include "mdp5_mixer.h" +#include "mdp5_ctl.h" #include "mdp5_smp.h" struct mdp5_state; @@ -253,7 +253,7 @@ struct drm_plane *mdp5_plane_init(struct drm_device *dev, struct mdp5_ctl *mdp5_crtc_get_ctl(struct drm_crtc *crtc); uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc); -int mdp5_crtc_get_lm(struct drm_crtc *crtc); +struct mdp5_hw_mixer *mdp5_crtc_get_mixer(struct drm_crtc *crtc); void mdp5_crtc_set_pipeline(struct drm_crtc *crtc, struct mdp5_interface *intf, struct mdp5_ctl *ctl); void mdp5_crtc_wait_for_commit_done(struct drm_crtc *crtc); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c index dd38f0bc2494e..032dc0a9638f1 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c @@ -29,6 +29,10 @@ struct mdp5_hw_mixer *mdp5_mixer_init(const struct mdp5_lm_instance *lm) { struct mdp5_hw_mixer *mixer; + /* ignore WB bound mixers for now */ + if (lm->caps & MDP_LM_CAP_WB) + return NULL; + mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); if (!mixer) return ERR_PTR(-ENOMEM); From a2380124fbd7bf5c6ab72e1c40a1158d8b501ab3 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:57:58 +0530 Subject: [PATCH 22/47] drm/msm/mdp5: Simplify LM <-> PP mapping PingPong ID for a Layer Mixer is already contained in mdp5_hw_mixer. This avoids the need to retrieve PP ID using macros Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c | 6 +++--- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 5 ++--- drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c | 2 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 10 +++++----- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c index 2e6ceafbd3e24..fd3cb45976f34 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c @@ -52,7 +52,7 @@ static int pingpong_tearcheck_setup(struct drm_encoder *encoder, u32 total_lines_x100, vclks_line, cfg; long vsync_clk_speed; struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc); - int pp_id = GET_PING_PONG_ID(mixer->lm); + int pp_id = mixer->pp; if (IS_ERR_OR_NULL(mdp5_kms->vsync_clk)) { dev_err(dev, "vsync_clk is not initialized\n"); @@ -96,7 +96,7 @@ static int pingpong_tearcheck_enable(struct drm_encoder *encoder) { struct mdp5_kms *mdp5_kms = get_kms(encoder); struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc); - int pp_id = GET_PING_PONG_ID(mixer->lm); + int pp_id = mixer->pp; int ret; ret = clk_set_rate(mdp5_kms->vsync_clk, @@ -122,7 +122,7 @@ static void pingpong_tearcheck_disable(struct drm_encoder *encoder) { struct mdp5_kms *mdp5_kms = get_kms(encoder); struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc); - int pp_id = GET_PING_PONG_ID(mixer->lm); + int pp_id = mixer->pp; mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 0); clk_disable_unprepare(mdp5_kms->vsync_clk); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index ffdb31c4419ff..804777d925b11 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -755,15 +755,14 @@ void mdp5_crtc_set_pipeline(struct drm_crtc *crtc, struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct mdp5_kms *mdp5_kms = get_kms(crtc); struct mdp5_hw_mixer *mixer = mdp5_crtc->mixer; - uint32_t lm = mixer->lm; /* now that we know what irq's we want: */ mdp5_crtc->err.irqmask = intf2err(intf->num); - mdp5_crtc->vblank.irqmask = intf2vblank(lm, intf); + mdp5_crtc->vblank.irqmask = intf2vblank(mixer, intf); if ((intf->type == INTF_DSI) && (intf->mode == MDP5_INTF_DSI_MODE_COMMAND)) { - mdp5_crtc->pp_done.irqmask = lm2ppdone(lm); + mdp5_crtc->pp_done.irqmask = lm2ppdone(mixer); mdp5_crtc->pp_done.irq = mdp5_crtc_pp_done_irq; mdp5_crtc->cmd_mode = true; } else { diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c index 68d048f040f00..c2c204e6ea3bc 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c @@ -238,7 +238,7 @@ static void mdp5_vid_encoder_disable(struct drm_encoder *encoder) * the settings changes for the new modeset (like new * scanout buffer) don't latch properly.. */ - mdp_irq_wait(&mdp5_kms->base, intf2vblank(mixer->lm, intf)); + mdp_irq_wait(&mdp5_kms->base, intf2vblank(mixer, intf)); bs_set(mdp5_encoder, 0); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index 13d9ccf378117..5a1ce871f5eb1 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -201,8 +201,8 @@ static inline uint32_t intf2err(int intf_num) } } -#define GET_PING_PONG_ID(layer_mixer) ((layer_mixer == 5) ? 3 : layer_mixer) -static inline uint32_t intf2vblank(int lm, struct mdp5_interface *intf) +static inline uint32_t intf2vblank(struct mdp5_hw_mixer *mixer, + struct mdp5_interface *intf) { /* * In case of DSI Command Mode, the Ping Pong's read pointer IRQ @@ -212,7 +212,7 @@ static inline uint32_t intf2vblank(int lm, struct mdp5_interface *intf) if ((intf->type == INTF_DSI) && (intf->mode == MDP5_INTF_DSI_MODE_COMMAND)) - return MDP5_IRQ_PING_PONG_0_RD_PTR << GET_PING_PONG_ID(lm); + return MDP5_IRQ_PING_PONG_0_RD_PTR << mixer->pp; if (intf->type == INTF_WB) return MDP5_IRQ_WB_2_DONE; @@ -226,9 +226,9 @@ static inline uint32_t intf2vblank(int lm, struct mdp5_interface *intf) } } -static inline uint32_t lm2ppdone(int lm) +static inline uint32_t lm2ppdone(struct mdp5_hw_mixer *mixer) { - return MDP5_IRQ_PING_PONG_0_DONE << GET_PING_PONG_ID(lm); + return MDP5_IRQ_PING_PONG_0_DONE << mixer->pp; } int mdp5_disable(struct mdp5_kms *mdp5_kms); From 36d1364abbedb405fe7395b2bacdf3f62f4cf7dc Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:57:59 +0530 Subject: [PATCH 23/47] drm/msm/mdp5: Clean up interface assignment mdp5_interface struct contains data corresponding to a INTF instance in MDP5 hardware. This sturct is memcpy'd to the mdp5_encoder struct, and then later to the mdp5_ctl struct. Instead of copying around interface data, create mdp5_interface instances in mdp5_init, like how it's done currently done for pipes and layer mixers. Pass around the interface pointers to mdp5_encoder and mdp5_ctl. This simplifies the code, and allows us to decouple encoders from INTFs in the future if needed. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- .../gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c | 8 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c | 21 ++--- drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c | 37 ++++---- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 85 +++++++++++++------ drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 6 +- 5 files changed, 93 insertions(+), 64 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c index fd3cb45976f34..18c967107bc45 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c @@ -145,7 +145,7 @@ void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder, mode->vsync_end, mode->vtotal, mode->type, mode->flags); pingpong_tearcheck_setup(encoder, mode); - mdp5_crtc_set_pipeline(encoder->crtc, &mdp5_cmd_enc->intf, + mdp5_crtc_set_pipeline(encoder->crtc, mdp5_cmd_enc->intf, mdp5_cmd_enc->ctl); } @@ -153,7 +153,7 @@ void mdp5_cmd_encoder_disable(struct drm_encoder *encoder) { struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder); struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl; - struct mdp5_interface *intf = &mdp5_cmd_enc->intf; + struct mdp5_interface *intf = mdp5_cmd_enc->intf; if (WARN_ON(!mdp5_cmd_enc->enabled)) return; @@ -172,7 +172,7 @@ void mdp5_cmd_encoder_enable(struct drm_encoder *encoder) { struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder); struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl; - struct mdp5_interface *intf = &mdp5_cmd_enc->intf; + struct mdp5_interface *intf = mdp5_cmd_enc->intf; if (WARN_ON(mdp5_cmd_enc->enabled)) return; @@ -200,7 +200,7 @@ int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder, return -EINVAL; mdp5_kms = get_kms(encoder); - intf_num = mdp5_cmd_enc->intf.num; + intf_num = mdp5_cmd_enc->intf->num; /* Switch slave encoder's trigger MUX, to use the master's * start signal for the slave encoder diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c index fa4f27a1a5514..ed184e5491b44 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c @@ -33,7 +33,7 @@ #define CTL_STAT_BOOKED 0x2 struct op_mode { - struct mdp5_interface intf; + struct mdp5_interface *intf; bool encoder_enabled; uint32_t start_mask; @@ -180,16 +180,8 @@ int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_interface *intf, struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm; struct mdp5_kms *mdp5_kms = get_kms(ctl_mgr); - if (unlikely(WARN_ON(intf->num != ctl->pipeline.intf.num))) { - dev_err(mdp5_kms->dev->dev, - "CTL %d is allocated by INTF %d, but used by INTF %d\n", - ctl->id, ctl->pipeline.intf.num, intf->num); - return -EINVAL; - } - ctl->mixer = mixer; - - memcpy(&ctl->pipeline.intf, intf, sizeof(*intf)); + ctl->pipeline.intf = intf; ctl->pipeline.start_mask = mdp_ctl_flush_mask_lm(mixer->lm) | mdp_ctl_flush_mask_encoder(intf); @@ -210,11 +202,11 @@ static bool start_signal_needed(struct mdp5_ctl *ctl) if (!pipeline->encoder_enabled || pipeline->start_mask != 0) return false; - switch (pipeline->intf.type) { + switch (pipeline->intf->type) { case INTF_WB: return true; case INTF_DSI: - return pipeline->intf.mode == MDP5_INTF_DSI_MODE_COMMAND; + return pipeline->intf->mode == MDP5_INTF_DSI_MODE_COMMAND; default: return false; } @@ -239,7 +231,7 @@ static void send_start_signal(struct mdp5_ctl *ctl) static void refill_start_mask(struct mdp5_ctl *ctl) { struct op_mode *pipeline = &ctl->pipeline; - struct mdp5_interface *intf = &ctl->pipeline.intf; + struct mdp5_interface *intf = pipeline->intf; pipeline->start_mask = mdp_ctl_flush_mask_lm(ctl->mixer->lm); @@ -265,7 +257,7 @@ int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, bool enabled) return -EINVAL; ctl->pipeline.encoder_enabled = enabled; - DBG("intf_%d: %s", ctl->pipeline.intf.num, enabled ? "on" : "off"); + DBG("intf_%d: %s", ctl->pipeline.intf->num, enabled ? "on" : "off"); if (start_signal_needed(ctl)) { send_start_signal(ctl); @@ -621,7 +613,6 @@ struct mdp5_ctl *mdp5_ctlm_request(struct mdp5_ctl_manager *ctl_mgr, found: ctl = &ctl_mgr->ctls[c]; - ctl->pipeline.intf.num = intf_num; ctl->mixer = NULL; ctl->status |= CTL_STAT_BUSY; ctl->pending_ctl_trigger = 0; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c index c2c204e6ea3bc..2994841eb00c7 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c @@ -109,7 +109,7 @@ static void mdp5_vid_encoder_mode_set(struct drm_encoder *encoder, struct mdp5_kms *mdp5_kms = get_kms(encoder); struct drm_device *dev = encoder->dev; struct drm_connector *connector; - int intf = mdp5_encoder->intf.num; + int intf = mdp5_encoder->intf->num; uint32_t dtv_hsync_skew, vsync_period, vsync_len, ctrl_pol; uint32_t display_v_start, display_v_end; uint32_t hsync_start_x, hsync_end_x; @@ -130,7 +130,7 @@ static void mdp5_vid_encoder_mode_set(struct drm_encoder *encoder, ctrl_pol = 0; /* DSI controller cannot handle active-low sync signals. */ - if (mdp5_encoder->intf.type != INTF_DSI) { + if (mdp5_encoder->intf->type != INTF_DSI) { if (mode->flags & DRM_MODE_FLAG_NHSYNC) ctrl_pol |= MDP5_INTF_POLARITY_CTL_HSYNC_LOW; if (mode->flags & DRM_MODE_FLAG_NVSYNC) @@ -175,7 +175,7 @@ static void mdp5_vid_encoder_mode_set(struct drm_encoder *encoder, * DISPLAY_V_START = (VBP * HCYCLE) + HBP * DISPLAY_V_END = (VBP + VACTIVE) * HCYCLE - 1 - HFP */ - if (mdp5_encoder->intf.type == INTF_eDP) { + if (mdp5_encoder->intf->type == INTF_eDP) { display_v_start += mode->htotal - mode->hsync_start; display_v_end -= mode->hsync_start - mode->hdisplay; } @@ -206,8 +206,8 @@ static void mdp5_vid_encoder_mode_set(struct drm_encoder *encoder, spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags); - mdp5_crtc_set_pipeline(encoder->crtc, &mdp5_encoder->intf, - mdp5_encoder->ctl); + mdp5_crtc_set_pipeline(encoder->crtc, mdp5_encoder->intf, + mdp5_encoder->ctl); } static void mdp5_vid_encoder_disable(struct drm_encoder *encoder) @@ -216,8 +216,8 @@ static void mdp5_vid_encoder_disable(struct drm_encoder *encoder) struct mdp5_kms *mdp5_kms = get_kms(encoder); struct mdp5_ctl *ctl = mdp5_encoder->ctl; struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc); - struct mdp5_interface *intf = &mdp5_encoder->intf; - int intfn = mdp5_encoder->intf.num; + struct mdp5_interface *intf = mdp5_encoder->intf; + int intfn = mdp5_encoder->intf->num; unsigned long flags; if (WARN_ON(!mdp5_encoder->enabled)) @@ -250,8 +250,8 @@ static void mdp5_vid_encoder_enable(struct drm_encoder *encoder) struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); struct mdp5_kms *mdp5_kms = get_kms(encoder); struct mdp5_ctl *ctl = mdp5_encoder->ctl; - struct mdp5_interface *intf = &mdp5_encoder->intf; - int intfn = mdp5_encoder->intf.num; + struct mdp5_interface *intf = mdp5_encoder->intf; + int intfn = intf->num; unsigned long flags; if (WARN_ON(mdp5_encoder->enabled)) @@ -273,7 +273,7 @@ static void mdp5_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode) { struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); - struct mdp5_interface *intf = &mdp5_encoder->intf; + struct mdp5_interface *intf = mdp5_encoder->intf; if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND) mdp5_cmd_encoder_mode_set(encoder, mode, adjusted_mode); @@ -284,7 +284,7 @@ static void mdp5_encoder_mode_set(struct drm_encoder *encoder, static void mdp5_encoder_disable(struct drm_encoder *encoder) { struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); - struct mdp5_interface *intf = &mdp5_encoder->intf; + struct mdp5_interface *intf = mdp5_encoder->intf; if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND) mdp5_cmd_encoder_disable(encoder); @@ -295,7 +295,7 @@ static void mdp5_encoder_disable(struct drm_encoder *encoder) static void mdp5_encoder_enable(struct drm_encoder *encoder) { struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); - struct mdp5_interface *intf = &mdp5_encoder->intf; + struct mdp5_interface *intf = mdp5_encoder->intf; if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND) mdp5_cmd_encoder_disable(encoder); @@ -313,7 +313,7 @@ int mdp5_encoder_get_linecount(struct drm_encoder *encoder) { struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); struct mdp5_kms *mdp5_kms = get_kms(encoder); - int intf = mdp5_encoder->intf.num; + int intf = mdp5_encoder->intf->num; return mdp5_read(mdp5_kms, REG_MDP5_INTF_LINE_COUNT(intf)); } @@ -322,7 +322,7 @@ u32 mdp5_encoder_get_framecount(struct drm_encoder *encoder) { struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); struct mdp5_kms *mdp5_kms = get_kms(encoder); - int intf = mdp5_encoder->intf.num; + int intf = mdp5_encoder->intf->num; return mdp5_read(mdp5_kms, REG_MDP5_INTF_FRAME_COUNT(intf)); } @@ -340,7 +340,7 @@ int mdp5_vid_encoder_set_split_display(struct drm_encoder *encoder, return -EINVAL; mdp5_kms = get_kms(encoder); - intf_num = mdp5_encoder->intf.num; + intf_num = mdp5_encoder->intf->num; /* Switch slave encoder's TimingGen Sync mode, * to use the master's enable signal for the slave encoder. @@ -369,7 +369,7 @@ int mdp5_vid_encoder_set_split_display(struct drm_encoder *encoder, void mdp5_encoder_set_intf_mode(struct drm_encoder *encoder, bool cmd_mode) { struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); - struct mdp5_interface *intf = &mdp5_encoder->intf; + struct mdp5_interface *intf = mdp5_encoder->intf; /* TODO: Expand this to set writeback modes too */ if (cmd_mode) { @@ -385,7 +385,8 @@ void mdp5_encoder_set_intf_mode(struct drm_encoder *encoder, bool cmd_mode) /* initialize encoder */ struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, - struct mdp5_interface *intf, struct mdp5_ctl *ctl) + struct mdp5_interface *intf, + struct mdp5_ctl *ctl) { struct drm_encoder *encoder = NULL; struct mdp5_encoder *mdp5_encoder; @@ -399,9 +400,9 @@ struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, goto fail; } - memcpy(&mdp5_encoder->intf, intf, sizeof(mdp5_encoder->intf)); encoder = &mdp5_encoder->base; mdp5_encoder->ctl = ctl; + mdp5_encoder->intf = intf; spin_lock_init(&mdp5_encoder->intf_lock); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index e8174d4847b0d..9d855ee0c0a42 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -271,19 +271,14 @@ int mdp5_enable(struct mdp5_kms *mdp5_kms) } static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms, - enum mdp5_intf_type intf_type, int intf_num, - struct mdp5_ctl *ctl) + struct mdp5_interface *intf, + struct mdp5_ctl *ctl) { struct drm_device *dev = mdp5_kms->dev; struct msm_drm_private *priv = dev->dev_private; struct drm_encoder *encoder; - struct mdp5_interface intf = { - .num = intf_num, - .type = intf_type, - .mode = MDP5_INTF_MODE_NONE, - }; - encoder = mdp5_encoder_init(dev, &intf, ctl); + encoder = mdp5_encoder_init(dev, intf, ctl); if (IS_ERR(encoder)) { dev_err(dev->dev, "failed to construct encoder\n"); return encoder; @@ -312,32 +307,28 @@ static int get_dsi_id_from_intf(const struct mdp5_cfg_hw *hw_cfg, int intf_num) return -EINVAL; } -static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) +static int modeset_init_intf(struct mdp5_kms *mdp5_kms, + struct mdp5_interface *intf) { struct drm_device *dev = mdp5_kms->dev; struct msm_drm_private *priv = dev->dev_private; - const struct mdp5_cfg_hw *hw_cfg = - mdp5_cfg_get_hw_config(mdp5_kms->cfg); - enum mdp5_intf_type intf_type = hw_cfg->intf.connect[intf_num]; struct mdp5_ctl_manager *ctlm = mdp5_kms->ctlm; struct mdp5_ctl *ctl; struct drm_encoder *encoder; int ret = 0; - switch (intf_type) { - case INTF_DISABLED: - break; + switch (intf->type) { case INTF_eDP: if (!priv->edp) break; - ctl = mdp5_ctlm_request(ctlm, intf_num); + ctl = mdp5_ctlm_request(ctlm, intf->num); if (!ctl) { ret = -EINVAL; break; } - encoder = construct_encoder(mdp5_kms, INTF_eDP, intf_num, ctl); + encoder = construct_encoder(mdp5_kms, intf, ctl); if (IS_ERR(encoder)) { ret = PTR_ERR(encoder); break; @@ -349,13 +340,13 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) if (!priv->hdmi) break; - ctl = mdp5_ctlm_request(ctlm, intf_num); + ctl = mdp5_ctlm_request(ctlm, intf->num); if (!ctl) { ret = -EINVAL; break; } - encoder = construct_encoder(mdp5_kms, INTF_HDMI, intf_num, ctl); + encoder = construct_encoder(mdp5_kms, intf, ctl); if (IS_ERR(encoder)) { ret = PTR_ERR(encoder); break; @@ -365,11 +356,13 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) break; case INTF_DSI: { - int dsi_id = get_dsi_id_from_intf(hw_cfg, intf_num); + const struct mdp5_cfg_hw *hw_cfg = + mdp5_cfg_get_hw_config(mdp5_kms->cfg); + int dsi_id = get_dsi_id_from_intf(hw_cfg, intf->num); if ((dsi_id >= ARRAY_SIZE(priv->dsi)) || (dsi_id < 0)) { dev_err(dev->dev, "failed to find dsi from intf %d\n", - intf_num); + intf->num); ret = -EINVAL; break; } @@ -377,13 +370,13 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) if (!priv->dsi[dsi_id]) break; - ctl = mdp5_ctlm_request(ctlm, intf_num); + ctl = mdp5_ctlm_request(ctlm, intf->num); if (!ctl) { ret = -EINVAL; break; } - encoder = construct_encoder(mdp5_kms, INTF_DSI, intf_num, ctl); + encoder = construct_encoder(mdp5_kms, intf, ctl); if (IS_ERR(encoder)) { ret = PTR_ERR(encoder); break; @@ -393,7 +386,7 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) break; } default: - dev_err(dev->dev, "unknown intf: %d\n", intf_type); + dev_err(dev->dev, "unknown intf: %d\n", intf->type); ret = -EINVAL; break; } @@ -417,8 +410,8 @@ static int modeset_init(struct mdp5_kms *mdp5_kms) * Construct encoders and modeset initialize connector devices * for each external display interface. */ - for (i = 0; i < ARRAY_SIZE(hw_cfg->intf.connect); i++) { - ret = modeset_init_intf(mdp5_kms, i); + for (i = 0; i < mdp5_kms->num_intfs; i++) { + ret = modeset_init_intf(mdp5_kms, mdp5_kms->intfs[i]); if (ret) goto fail; } @@ -747,6 +740,7 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev) static void mdp5_destroy(struct platform_device *pdev) { struct mdp5_kms *mdp5_kms = platform_get_drvdata(pdev); + int i; if (mdp5_kms->ctlm) mdp5_ctlm_destroy(mdp5_kms->ctlm); @@ -755,6 +749,9 @@ static void mdp5_destroy(struct platform_device *pdev) if (mdp5_kms->cfg) mdp5_cfg_destroy(mdp5_kms->cfg); + for (i = 0; i < mdp5_kms->num_intfs; i++) + kfree(mdp5_kms->intfs[i]); + if (mdp5_kms->rpm_enabled) pm_runtime_disable(&pdev->dev); @@ -862,6 +859,38 @@ static int hwmixer_init(struct mdp5_kms *mdp5_kms) return 0; } +static int interface_init(struct mdp5_kms *mdp5_kms) +{ + struct drm_device *dev = mdp5_kms->dev; + const struct mdp5_cfg_hw *hw_cfg; + const enum mdp5_intf_type *intf_types; + int i; + + hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); + intf_types = hw_cfg->intf.connect; + + for (i = 0; i < ARRAY_SIZE(hw_cfg->intf.connect); i++) { + struct mdp5_interface *intf; + + if (intf_types[i] == INTF_DISABLED) + continue; + + intf = kzalloc(sizeof(*intf), GFP_KERNEL); + if (!intf) { + dev_err(dev->dev, "failed to construct INTF%d\n", i); + return -ENOMEM; + } + + intf->num = i; + intf->type = intf_types[i]; + intf->mode = MDP5_INTF_MODE_NONE; + intf->idx = mdp5_kms->num_intfs; + mdp5_kms->intfs[mdp5_kms->num_intfs++] = intf; + } + + return 0; +} + static int mdp5_init(struct platform_device *pdev, struct drm_device *dev) { struct msm_drm_private *priv = dev->dev_private; @@ -966,6 +995,10 @@ static int mdp5_init(struct platform_device *pdev, struct drm_device *dev) if (ret) goto fail; + ret = interface_init(mdp5_kms); + if (ret) + goto fail; + /* set uninit-ed kms */ priv->kms = &mdp5_kms->base.base; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index 5a1ce871f5eb1..cf821971793df 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -43,6 +43,9 @@ struct mdp5_kms { unsigned num_hwmixers; struct mdp5_hw_mixer *hwmixers[8]; + unsigned num_intfs; + struct mdp5_interface *intfs[5]; + struct mdp5_cfg_handler *cfg; uint32_t caps; /* MDP capabilities (MDP_CAP_XXX bits) */ @@ -125,6 +128,7 @@ enum mdp5_intf_mode { }; struct mdp5_interface { + int idx; int num; /* display interface number */ enum mdp5_intf_type type; enum mdp5_intf_mode mode; @@ -132,11 +136,11 @@ struct mdp5_interface { struct mdp5_encoder { struct drm_encoder base; - struct mdp5_interface intf; spinlock_t intf_lock; /* protect REG_MDP5_INTF_* registers */ bool enabled; uint32_t bsc; + struct mdp5_interface *intf; struct mdp5_ctl *ctl; }; #define to_mdp5_encoder(x) container_of(x, struct mdp5_encoder, base) From eda5dbe55bcd24208ef291429d8f202ac8477a09 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:00 +0530 Subject: [PATCH 24/47] drm/msm/mdp5: Remove the pipeline stuff in mdp5_ctl The mdp5_ctl has an 'op_mode' struct which contains info on the downstream pipeline. Grouping these params together in a struct doesn't serve much purpose in the code. Maybe there was a plan to expand this further that never happened. Remove the op_mode struct, and place its members directly in mdp5_ctl. This will help avoid confusion later when I introduce my own verion of a mdp5 pipeline :) Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c | 42 ++++++++++--------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c index ed184e5491b44..a86f1fd359c35 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c @@ -32,13 +32,6 @@ #define CTL_STAT_BUSY 0x1 #define CTL_STAT_BOOKED 0x2 -struct op_mode { - struct mdp5_interface *intf; - - bool encoder_enabled; - uint32_t start_mask; -}; - struct mdp5_ctl { struct mdp5_ctl_manager *ctlm; @@ -49,7 +42,10 @@ struct mdp5_ctl { u32 status; /* Operation Mode Configuration for the Pipeline */ - struct op_mode pipeline; + struct mdp5_interface *intf; + + bool encoder_enabled; + uint32_t start_mask; /* REG_MDP5_CTL_*() registers access info + lock: */ spinlock_t hw_lock; @@ -181,10 +177,10 @@ int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_interface *intf, struct mdp5_kms *mdp5_kms = get_kms(ctl_mgr); ctl->mixer = mixer; - ctl->pipeline.intf = intf; + ctl->intf = intf; - ctl->pipeline.start_mask = mdp_ctl_flush_mask_lm(mixer->lm) | - mdp_ctl_flush_mask_encoder(intf); + ctl->start_mask = mdp_ctl_flush_mask_lm(mixer->lm) | + mdp_ctl_flush_mask_encoder(intf); /* Virtual interfaces need not set a display intf (e.g.: Writeback) */ if (!mdp5_cfg_intf_is_virtual(intf->type)) @@ -197,16 +193,14 @@ int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_interface *intf, static bool start_signal_needed(struct mdp5_ctl *ctl) { - struct op_mode *pipeline = &ctl->pipeline; - - if (!pipeline->encoder_enabled || pipeline->start_mask != 0) + if (!ctl->encoder_enabled || ctl->start_mask != 0) return false; - switch (pipeline->intf->type) { + switch (ctl->intf->type) { case INTF_WB: return true; case INTF_DSI: - return pipeline->intf->mode == MDP5_INTF_DSI_MODE_COMMAND; + return ctl->intf->mode == MDP5_INTF_DSI_MODE_COMMAND; default: return false; } @@ -230,17 +224,16 @@ static void send_start_signal(struct mdp5_ctl *ctl) static void refill_start_mask(struct mdp5_ctl *ctl) { - struct op_mode *pipeline = &ctl->pipeline; - struct mdp5_interface *intf = pipeline->intf; + struct mdp5_interface *intf = ctl->intf; - pipeline->start_mask = mdp_ctl_flush_mask_lm(ctl->mixer->lm); + ctl->start_mask = mdp_ctl_flush_mask_lm(ctl->mixer->lm); /* * Writeback encoder needs to program & flush * address registers for each page flip.. */ if (intf->type == INTF_WB) - pipeline->start_mask |= mdp_ctl_flush_mask_encoder(intf); + ctl->start_mask |= mdp_ctl_flush_mask_encoder(intf); } /** @@ -256,8 +249,8 @@ int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, bool enabled) if (WARN_ON(!ctl)) return -EINVAL; - ctl->pipeline.encoder_enabled = enabled; - DBG("intf_%d: %s", ctl->pipeline.intf->num, enabled ? "on" : "off"); + ctl->encoder_enabled = enabled; + DBG("intf_%d: %s", ctl->intf->num, enabled ? "on" : "off"); if (start_signal_needed(ctl)) { send_start_signal(ctl); @@ -495,15 +488,14 @@ static void fix_for_single_flush(struct mdp5_ctl *ctl, u32 *flush_mask, u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask) { struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm; - struct op_mode *pipeline = &ctl->pipeline; unsigned long flags; u32 flush_id = ctl->id; u32 curr_ctl_flush_mask; - pipeline->start_mask &= ~flush_mask; + ctl->start_mask &= ~flush_mask; VERB("flush_mask=%x, start_mask=%x, trigger=%x", flush_mask, - pipeline->start_mask, ctl->pending_ctl_trigger); + ctl->start_mask, ctl->pending_ctl_trigger); if (ctl->pending_ctl_trigger & flush_mask) { flush_mask |= MDP5_CTL_FLUSH_CTL; From c1e2a13090e47ec35539e470dc7f4781662e5219 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:01 +0530 Subject: [PATCH 25/47] drm/msm/mdp5: subclass CRTC state Subclass drm_crtc_state so that we can maintain additional state for our CRTCs. Add mdp5_pipeline and mdp5_ctl pointers in the subclassed state. mdp5_pipeline is a grouping of the HW entities that forms the downstream pipeline for a particular CRTC. It currently contains pointers to mdp5_interface and mdp5_hw_mixer tied to this CRTC. Later, we will have 2 hwmixers in this struct. (We could also have 2 intfs if we want to support dual DSI with Source Split enabled. Implementing that feature isn't planned at the moment). The mdp5_pipeline state isn't used at the moment. For now, we just introduce mdp5_crtc_state and the crtc funcs needed to manage the subclassed state. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 72 ++++++++++++++++++++++-- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 14 +++++ 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 804777d925b11..12dc94854410e 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -639,16 +639,75 @@ static int mdp5_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) return 0; } +static void +mdp5_crtc_atomic_print_state(struct drm_printer *p, + const struct drm_crtc_state *state) +{ + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(state); + struct mdp5_pipeline *pipeline = &mdp5_cstate->pipeline; + + if (WARN_ON(!pipeline)) + return; + + drm_printf(p, "\thwmixer=%s\n", pipeline->mixer ? + pipeline->mixer->name : "(null)"); +} + +static void mdp5_crtc_reset(struct drm_crtc *crtc) +{ + struct mdp5_crtc_state *mdp5_cstate; + + if (crtc->state) { + __drm_atomic_helper_crtc_destroy_state(crtc->state); + kfree(to_mdp5_crtc_state(crtc->state)); + } + + mdp5_cstate = kzalloc(sizeof(*mdp5_cstate), GFP_KERNEL); + + if (mdp5_cstate) { + mdp5_cstate->base.crtc = crtc; + crtc->state = &mdp5_cstate->base; + } +} + +static struct drm_crtc_state * +mdp5_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct mdp5_crtc_state *mdp5_cstate; + + if (WARN_ON(!crtc->state)) + return NULL; + + mdp5_cstate = kmemdup(to_mdp5_crtc_state(crtc->state), + sizeof(*mdp5_cstate), GFP_KERNEL); + if (!mdp5_cstate) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &mdp5_cstate->base); + + return &mdp5_cstate->base; +} + +static void mdp5_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state) +{ + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(state); + + __drm_atomic_helper_crtc_destroy_state(state); + + kfree(mdp5_cstate); +} + static const struct drm_crtc_funcs mdp5_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .destroy = mdp5_crtc_destroy, .page_flip = drm_atomic_helper_page_flip, .set_property = drm_atomic_helper_crtc_set_property, - .reset = drm_atomic_helper_crtc_reset, - .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .reset = mdp5_crtc_reset, + .atomic_duplicate_state = mdp5_crtc_duplicate_state, + .atomic_destroy_state = mdp5_crtc_destroy_state, .cursor_set = mdp5_crtc_cursor_set, .cursor_move = mdp5_crtc_cursor_move, + .atomic_print_state = mdp5_crtc_atomic_print_state, }; static const struct drm_crtc_funcs mdp5_crtc_no_lm_cursor_funcs = { @@ -656,9 +715,10 @@ static const struct drm_crtc_funcs mdp5_crtc_no_lm_cursor_funcs = { .destroy = mdp5_crtc_destroy, .page_flip = drm_atomic_helper_page_flip, .set_property = drm_atomic_helper_crtc_set_property, - .reset = drm_atomic_helper_crtc_reset, - .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .reset = mdp5_crtc_reset, + .atomic_duplicate_state = mdp5_crtc_duplicate_state, + .atomic_destroy_state = mdp5_crtc_destroy_state, + .atomic_print_state = mdp5_crtc_atomic_print_state, }; static const struct drm_crtc_helper_funcs mdp5_crtc_helper_funcs = { diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index cf821971793df..84b28936613d6 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -115,6 +115,20 @@ struct mdp5_plane_state { #define to_mdp5_plane_state(x) \ container_of(x, struct mdp5_plane_state, base) +struct mdp5_pipeline { + struct mdp5_interface *intf; + struct mdp5_hw_mixer *mixer; +}; + +struct mdp5_crtc_state { + struct drm_crtc_state base; + + struct mdp5_ctl *ctl; + struct mdp5_pipeline pipeline; +}; +#define to_mdp5_crtc_state(x) \ + container_of(x, struct mdp5_crtc_state, base) + enum mdp5_intf_mode { MDP5_INTF_MODE_NONE = 0, From 894558ec8c4f3e482299905d7f86b7d1c57e1c2f Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:02 +0530 Subject: [PATCH 26/47] drm/msm/mdp5: Prepare for dynamic assignment of mixers Add the stuff needed to allow dynamically assigning a mixer to a CRTC. Since mixers are a resource that can be shared across multiple CRTCs, we need to maintain a 'hwmixer_to_crtc' map in the global atomic state, acquire the mdp5_kms.state_lock modeset lock and so on. The mixer is assigned in the CRTC's atomic_check() func, a failure will result in the new state being cleanly rolled back. The mixer assignment itself is straightforward, and almost identical to what we do for hwpipes. We don't need to grab the old hwmixer_to_crtc state like we do in hwpipes since we don't need to compare anything with the old state at the moment. The only LM capability we care about at the moment is whether the mixer instance can be used to display stuff (i.e, connect to an INTF downstream). Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 31 +++++++++++++ drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 1 + drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 1 + drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c | 55 +++++++++++++++++++++++ drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h | 9 ++++ 5 files changed, 97 insertions(+) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 12dc94854410e..9cfad3defa613 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -367,6 +367,30 @@ static void mdp5_crtc_enable(struct drm_crtc *crtc) mdp5_crtc->enabled = true; } +int mdp5_crtc_setup_pipeline(struct drm_crtc *crtc, + struct drm_crtc_state *new_crtc_state) +{ + struct mdp5_crtc_state *mdp5_cstate = + to_mdp5_crtc_state(new_crtc_state); + struct mdp5_pipeline *pipeline = &mdp5_cstate->pipeline; + bool new_mixer = false; + + new_mixer = !pipeline->mixer; + + if (new_mixer) { + struct mdp5_hw_mixer *old_mixer = pipeline->mixer; + + pipeline->mixer = mdp5_mixer_assign(new_crtc_state->state, crtc, + MDP_LM_CAP_DISPLAY); + if (IS_ERR(pipeline->mixer)) + return PTR_ERR(pipeline->mixer); + + mdp5_mixer_release(new_crtc_state->state, old_mixer); + } + + return 0; +} + struct plane_state { struct drm_plane *plane; struct mdp5_plane_state *state; @@ -399,6 +423,7 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, const struct drm_plane_state *pstate; bool cursor_plane = false; int cnt = 0, base = 0, i; + int ret; DBG("%s: check", crtc->name); @@ -412,6 +437,12 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, cursor_plane = true; } + ret = mdp5_crtc_setup_pipeline(crtc, state); + if (ret) { + dev_err(dev->dev, "couldn't assign mixers %d\n", ret); + return ret; + } + /* assign a stage based on sorted zpos property */ sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index 9d855ee0c0a42..880c7d4208ec5 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -93,6 +93,7 @@ struct mdp5_state *mdp5_get_state(struct drm_atomic_state *s) /* Copy state: */ new_state->hwpipe = mdp5_kms->state->hwpipe; + new_state->hwmixer = mdp5_kms->state->hwmixer; if (mdp5_kms->smp) new_state->smp = mdp5_kms->state->smp; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index 84b28936613d6..35f3c30a6d4a1 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -90,6 +90,7 @@ struct mdp5_kms { */ struct mdp5_state { struct mdp5_hw_pipe_state hwpipe; + struct mdp5_hw_mixer_state hwmixer; struct mdp5_smp_state smp; }; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c index 032dc0a9638f1..6f353c4886e43 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c @@ -16,6 +16,61 @@ #include "mdp5_kms.h" +struct mdp5_hw_mixer *mdp5_mixer_assign(struct drm_atomic_state *s, + struct drm_crtc *crtc, uint32_t caps) +{ + struct msm_drm_private *priv = s->dev->dev_private; + struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms)); + struct mdp5_state *state = mdp5_get_state(s); + struct mdp5_hw_mixer_state *new_state; + struct mdp5_hw_mixer *mixer = NULL; + int i; + + if (IS_ERR(state)) + return ERR_CAST(state); + + new_state = &state->hwmixer; + + for (i = 0; i < mdp5_kms->num_hwmixers; i++) { + struct mdp5_hw_mixer *cur = mdp5_kms->hwmixers[i]; + + /* skip if already in-use */ + if (new_state->hwmixer_to_crtc[cur->idx]) + continue; + + /* skip if doesn't support some required caps: */ + if (caps & ~cur->caps) + continue; + + if (!mixer) + mixer = cur; + } + + if (!mixer) + return ERR_PTR(-ENOMEM); + + new_state->hwmixer_to_crtc[mixer->idx] = crtc; + + return mixer; +} + +void mdp5_mixer_release(struct drm_atomic_state *s, struct mdp5_hw_mixer *mixer) +{ + struct mdp5_state *state = mdp5_get_state(s); + struct mdp5_hw_mixer_state *new_state = &state->hwmixer; + + if (!mixer) + return; + + if (WARN_ON(!new_state->hwmixer_to_crtc[mixer->idx])) + return; + + DBG("%s: release from crtc %s", mixer->name, + new_state->hwmixer_to_crtc[mixer->idx]->name); + + new_state->hwmixer_to_crtc[mixer->idx] = NULL; +} + void mdp5_mixer_destroy(struct mdp5_hw_mixer *mixer) { kfree(mixer); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h index 08d2173703bfc..18ed933ae574e 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h @@ -31,7 +31,16 @@ struct mdp5_hw_mixer { uint32_t flush_mask; /* used to commit LM registers */ }; +/* global atomic state of assignment between CRTCs and Layer Mixers: */ +struct mdp5_hw_mixer_state { + struct drm_crtc *hwmixer_to_crtc[8]; +}; + struct mdp5_hw_mixer *mdp5_mixer_init(const struct mdp5_lm_instance *lm); void mdp5_mixer_destroy(struct mdp5_hw_mixer *lm); +struct mdp5_hw_mixer *mdp5_mixer_assign(struct drm_atomic_state *s, + struct drm_crtc *crtc, uint32_t caps); +void mdp5_mixer_release(struct drm_atomic_state *s, + struct mdp5_hw_mixer *mixer); #endif /* __MDP5_LM_H__ */ From 502e3550c6f58f514d57f9e13ec9a98f6936b243 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:03 +0530 Subject: [PATCH 27/47] drm/msm/mdp5: Assign INTF and CTL in encoder's atomic_check() The INTF and CTL used in a display pipeline are going to be maintained as a part of the CRTC state (i.e, in mdp5_crtc_state). These entities, however, are currently statically assigned to drm_encoders (i.e. mdp5_encoder). Since these aren't directly visible to the CRTC, we assign them to the CRTC state in the encoder's atomic_check() op. With this approach, we assign portions of CRTC state in two different places: the layer mixer in CRTC's atomic_check(), and the INTF and CTL pieces in the encoder's atomic_check() op. We'd have more options here if the drm core maintained encoder state too, but the current approach of clubbing everything in CRTC's state works just fine. Unlike hwpipes and mixers, we don't need to keep a track of INTF/CTL assignments in the global atomic state. This is because they're currently not sharable resources. For example, INTF0 and CTL0 will always be assigned to one drm_encoder. This can change later when we implement writeback and want a CRTC to use a CTL for a while, and then release it for others to use it. Or, when a drm_encoder can switch between using a single INTF vs 2 INTFs. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c index 2994841eb00c7..6d535140ef217 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c @@ -303,10 +303,26 @@ static void mdp5_encoder_enable(struct drm_encoder *encoder) mdp5_vid_encoder_enable(encoder); } +static int mdp5_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc_state); + struct mdp5_interface *intf = mdp5_encoder->intf; + struct mdp5_ctl *ctl = mdp5_encoder->ctl; + + mdp5_cstate->ctl = ctl; + mdp5_cstate->pipeline.intf = intf; + + return 0; +} + static const struct drm_encoder_helper_funcs mdp5_encoder_helper_funcs = { .mode_set = mdp5_encoder_mode_set, .disable = mdp5_encoder_disable, .enable = mdp5_encoder_enable, + .atomic_check = mdp5_encoder_atomic_check, }; int mdp5_encoder_get_linecount(struct drm_encoder *encoder) From bcb877b7fdeaf0867d3363136644e4d378207e31 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:04 +0530 Subject: [PATCH 28/47] drm/msm/mdp5: Add more stuff to CRTC state Things like vblank/err irq masks, mode of operation (command mode or not) are derivative of the interface and mixer state. Therefore, they need to be a part of the CRTC state too. Add them to mdp5_crtc_state, and assign them in the CRTC's atomic_check() func, so that it can be rolled back to a clean state. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 19 +++++++++++++++++++ drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 7 +++++++ 2 files changed, 26 insertions(+) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 9cfad3defa613..7913e93a1d909 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -373,6 +373,7 @@ int mdp5_crtc_setup_pipeline(struct drm_crtc *crtc, struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(new_crtc_state); struct mdp5_pipeline *pipeline = &mdp5_cstate->pipeline; + struct mdp5_interface *intf; bool new_mixer = false; new_mixer = !pipeline->mixer; @@ -388,6 +389,24 @@ int mdp5_crtc_setup_pipeline(struct drm_crtc *crtc, mdp5_mixer_release(new_crtc_state->state, old_mixer); } + /* + * these should have been already set up in the encoder's atomic + * check (called by drm_atomic_helper_check_modeset) + */ + intf = pipeline->intf; + + mdp5_cstate->err_irqmask = intf2err(intf->num); + mdp5_cstate->vblank_irqmask = intf2vblank(pipeline->mixer, intf); + + if ((intf->type == INTF_DSI) && + (intf->mode == MDP5_INTF_DSI_MODE_COMMAND)) { + mdp5_cstate->pp_done_irqmask = lm2ppdone(pipeline->mixer); + mdp5_cstate->cmd_mode = true; + } else { + mdp5_cstate->pp_done_irqmask = 0; + mdp5_cstate->cmd_mode = false; + } + return 0; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index 35f3c30a6d4a1..35a21e6c58563 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -126,6 +126,13 @@ struct mdp5_crtc_state { struct mdp5_ctl *ctl; struct mdp5_pipeline pipeline; + + /* these are derivatives of intf/mixer state in mdp5_pipeline */ + u32 vblank_irqmask; + u32 err_irqmask; + u32 pp_done_irqmask; + + bool cmd_mode; }; #define to_mdp5_crtc_state(x) \ container_of(x, struct mdp5_crtc_state, base) From 0ddc3a630743adc36639f2c4d2134808f0835e9a Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:05 +0530 Subject: [PATCH 29/47] drm/msm/mdp5: Start using parameters from CRTC state In the last few commits, we've been adding params to mdp5_crtc_state, and assigning them in the atomic_check() funcs. Now it's time to actually start using them. Remove the duplicated params from the mdp5_crtc struct, and start using them in the mdp5_crtc code. The majority of the references to these params is in code that executes after the atomic swap has occurred, so it's okay to use crtc->state in them. There are a couple of legacy LM cursor ops that may not use the updated state, but (I think) it's okay to live with that. Now that we dynamically allocate a mixer to the CRTC, we can also remove the static assignment to it in mdp5_crtc_init, and also drop the code that skipped init-ing WB bound mixers (those will now be rejected by mdp5_mixer_assign()). Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 122 ++++++++++++---------- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 4 - drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c | 4 - 3 files changed, 64 insertions(+), 66 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 7913e93a1d909..c33855eeda24e 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -32,12 +32,8 @@ struct mdp5_crtc { int id; bool enabled; - struct mdp5_hw_mixer *mixer; spinlock_t lm_lock; /* protect REG_MDP5_LM_* registers */ - /* CTL used for this CRTC: */ - struct mdp5_ctl *ctl; - /* if there is a pending flip, these will be non-null: */ struct drm_pending_vblank_event *event; @@ -59,8 +55,6 @@ struct mdp5_crtc { struct completion pp_completion; - bool cmd_mode; - struct { /* protect REG_MDP5_LM_CURSOR* registers and cursor scanout_bo*/ spinlock_t lock; @@ -95,10 +89,11 @@ static void request_pp_done_pending(struct drm_crtc *crtc) static u32 crtc_flush(struct drm_crtc *crtc, u32 flush_mask) { - struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); + struct mdp5_ctl *ctl = mdp5_cstate->ctl; DBG("%s: flush=%08x", crtc->name, flush_mask); - return mdp5_ctl_commit(mdp5_crtc->ctl, flush_mask); + return mdp5_ctl_commit(ctl, flush_mask); } /* @@ -108,20 +103,20 @@ static u32 crtc_flush(struct drm_crtc *crtc, u32 flush_mask) */ static u32 crtc_flush_all(struct drm_crtc *crtc) { - struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); struct mdp5_hw_mixer *mixer; struct drm_plane *plane; uint32_t flush_mask = 0; /* this should not happen: */ - if (WARN_ON(!mdp5_crtc->ctl)) + if (WARN_ON(!mdp5_cstate->ctl)) return 0; drm_atomic_crtc_for_each_plane(plane, crtc) { flush_mask |= mdp5_plane_get_flush(plane); } - mixer = mdp5_crtc->mixer; + mixer = mdp5_cstate->pipeline.mixer; flush_mask |= mdp_ctl_flush_mask_lm(mixer->lm); return crtc_flush(crtc, flush_mask); @@ -130,7 +125,9 @@ static u32 crtc_flush_all(struct drm_crtc *crtc) /* if file!=NULL, this is preclose potential cancel-flip path */ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) { + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct mdp5_ctl *ctl = mdp5_cstate->ctl; struct drm_device *dev = crtc->dev; struct drm_pending_vblank_event *event; unsigned long flags; @@ -144,10 +141,11 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) } spin_unlock_irqrestore(&dev->event_lock, flags); - if (mdp5_crtc->ctl && !crtc->state->enable) { + if (ctl && !crtc->state->enable) { /* set STAGE_UNUSED for all layers */ - mdp5_ctl_blend(mdp5_crtc->ctl, NULL, 0, 0); - mdp5_crtc->ctl = NULL; + mdp5_ctl_blend(ctl, NULL, 0, 0); + /* XXX: What to do here? */ + /* mdp5_crtc->ctl = NULL; */ } } @@ -196,13 +194,15 @@ static inline u32 mdp5_lm_use_fg_alpha_mask(enum mdp_mixer_stage_id stage) static void blend_setup(struct drm_crtc *crtc) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); struct mdp5_kms *mdp5_kms = get_kms(crtc); struct drm_plane *plane; const struct mdp5_cfg_hw *hw_cfg; struct mdp5_plane_state *pstate, *pstates[STAGE_MAX + 1] = {NULL}; const struct mdp_format *format; - struct mdp5_hw_mixer *mixer = mdp5_crtc->mixer; + struct mdp5_hw_mixer *mixer = mdp5_cstate->pipeline.mixer; uint32_t lm = mixer->lm; + struct mdp5_ctl *ctl = mdp5_cstate->ctl; uint32_t blend_op, fg_alpha, bg_alpha, ctl_blend_flags = 0; unsigned long flags; enum mdp5_pipe stage[STAGE_MAX + 1] = { SSPP_NONE }; @@ -216,7 +216,8 @@ static void blend_setup(struct drm_crtc *crtc) spin_lock_irqsave(&mdp5_crtc->lm_lock, flags); /* ctl could be released already when we are shutting down: */ - if (!mdp5_crtc->ctl) + /* XXX: Can this happen now? */ + if (!ctl) goto out; /* Collect all plane information */ @@ -293,7 +294,7 @@ static void blend_setup(struct drm_crtc *crtc) mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(lm), mixer_op_mode); - mdp5_ctl_blend(mdp5_crtc->ctl, stage, plane_cnt, ctl_blend_flags); + mdp5_ctl_blend(ctl, stage, plane_cnt, ctl_blend_flags); out: spin_unlock_irqrestore(&mdp5_crtc->lm_lock, flags); @@ -302,8 +303,9 @@ static void blend_setup(struct drm_crtc *crtc) static void mdp5_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); struct mdp5_kms *mdp5_kms = get_kms(crtc); - struct mdp5_hw_mixer *mixer = mdp5_crtc->mixer; + struct mdp5_hw_mixer *mixer = mdp5_cstate->pipeline.mixer; uint32_t lm = mixer->lm; unsigned long flags; struct drm_display_mode *mode; @@ -332,6 +334,7 @@ static void mdp5_crtc_mode_set_nofb(struct drm_crtc *crtc) static void mdp5_crtc_disable(struct drm_crtc *crtc) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); struct mdp5_kms *mdp5_kms = get_kms(crtc); DBG("%s", crtc->name); @@ -339,7 +342,7 @@ static void mdp5_crtc_disable(struct drm_crtc *crtc) if (WARN_ON(!mdp5_crtc->enabled)) return; - if (mdp5_crtc->cmd_mode) + if (mdp5_cstate->cmd_mode) mdp_irq_unregister(&mdp5_kms->base, &mdp5_crtc->pp_done); mdp_irq_unregister(&mdp5_kms->base, &mdp5_crtc->err); @@ -351,6 +354,7 @@ static void mdp5_crtc_disable(struct drm_crtc *crtc) static void mdp5_crtc_enable(struct drm_crtc *crtc) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); struct mdp5_kms *mdp5_kms = get_kms(crtc); DBG("%s", crtc->name); @@ -361,7 +365,7 @@ static void mdp5_crtc_enable(struct drm_crtc *crtc) mdp5_enable(mdp5_kms); mdp_irq_register(&mdp5_kms->base, &mdp5_crtc->err); - if (mdp5_crtc->cmd_mode) + if (mdp5_cstate->cmd_mode) mdp_irq_register(&mdp5_kms->base, &mdp5_crtc->pp_done); mdp5_crtc->enabled = true; @@ -508,6 +512,7 @@ static void mdp5_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); struct drm_device *dev = crtc->dev; unsigned long flags; @@ -524,7 +529,8 @@ static void mdp5_crtc_atomic_flush(struct drm_crtc *crtc, * it means we are trying to flush a CRTC whose state is disabled: * nothing else needs to be done. */ - if (unlikely(!mdp5_crtc->ctl)) + /* XXX: Can this happen now ? */ + if (unlikely(!mdp5_cstate->ctl)) return; blend_setup(crtc); @@ -535,11 +541,16 @@ static void mdp5_crtc_atomic_flush(struct drm_crtc *crtc, * This is safe because no pp_done will happen before SW trigger * in command mode. */ - if (mdp5_crtc->cmd_mode) + if (mdp5_cstate->cmd_mode) request_pp_done_pending(crtc); mdp5_crtc->flushed_mask = crtc_flush_all(crtc); + /* XXX are we leaking out state here? */ + mdp5_crtc->vblank.irqmask = mdp5_cstate->vblank_irqmask; + mdp5_crtc->err.irqmask = mdp5_cstate->err_irqmask; + mdp5_crtc->pp_done.irqmask = mdp5_cstate->pp_done_irqmask; + request_pending(crtc, PENDING_FLIP); } @@ -574,11 +585,13 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, uint32_t width, uint32_t height) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); struct drm_device *dev = crtc->dev; struct mdp5_kms *mdp5_kms = get_kms(crtc); struct drm_gem_object *cursor_bo, *old_bo = NULL; uint32_t blendcfg, stride; uint64_t cursor_addr; + struct mdp5_ctl *ctl; int ret, lm; enum mdp5_cursor_alpha cur_alpha = CURSOR_ALPHA_PER_PIXEL; uint32_t flush_mask = mdp_ctl_flush_mask_cursor(0); @@ -591,7 +604,8 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, return -EINVAL; } - if (NULL == mdp5_crtc->ctl) + ctl = mdp5_cstate->ctl; + if (!ctl) return -EINVAL; if (!handle) { @@ -608,7 +622,7 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, if (ret) return -EINVAL; - lm = mdp5_crtc->mixer->lm; + lm = mdp5_cstate->pipeline.mixer->lm; stride = width * drm_format_plane_cpp(DRM_FORMAT_ARGB8888, 0); spin_lock_irqsave(&mdp5_crtc->cursor.lock, flags); @@ -638,7 +652,7 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, spin_unlock_irqrestore(&mdp5_crtc->cursor.lock, flags); set_cursor: - ret = mdp5_ctl_set_cursor(mdp5_crtc->ctl, 0, cursor_enable); + ret = mdp5_ctl_set_cursor(ctl, 0, cursor_enable); if (ret) { dev_err(dev->dev, "failed to %sable cursor: %d\n", cursor_enable ? "en" : "dis", ret); @@ -660,7 +674,8 @@ static int mdp5_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) { struct mdp5_kms *mdp5_kms = get_kms(crtc); struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); - uint32_t lm = mdp5_crtc->mixer->lm; + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); + uint32_t lm = mdp5_cstate->pipeline.mixer->lm; uint32_t flush_mask = mdp_ctl_flush_mask_cursor(0); uint32_t roi_w; uint32_t roi_h; @@ -818,23 +833,26 @@ static void mdp5_crtc_wait_for_pp_done(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); int ret; ret = wait_for_completion_timeout(&mdp5_crtc->pp_completion, msecs_to_jiffies(50)); if (ret == 0) dev_warn(dev->dev, "pp done time out, lm=%d\n", - mdp5_crtc->mixer->lm); + mdp5_cstate->pipeline.mixer->lm); } static void mdp5_crtc_wait_for_flush_done(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); + struct mdp5_ctl *ctl = mdp5_cstate->ctl; int ret; /* Should not call this function if crtc is disabled. */ - if (!mdp5_crtc->ctl) + if (!ctl) return; ret = drm_crtc_vblank_get(crtc); @@ -842,7 +860,7 @@ static void mdp5_crtc_wait_for_flush_done(struct drm_crtc *crtc) return; ret = wait_event_timeout(dev->vblank[drm_crtc_index(crtc)].queue, - ((mdp5_ctl_get_commit_status(mdp5_crtc->ctl) & + ((mdp5_ctl_get_commit_status(ctl) & mdp5_crtc->flushed_mask) == 0), msecs_to_jiffies(50)); if (ret <= 0) @@ -862,50 +880,41 @@ uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc) void mdp5_crtc_set_pipeline(struct drm_crtc *crtc, struct mdp5_interface *intf, struct mdp5_ctl *ctl) { - struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); + struct mdp5_hw_mixer *mixer = mdp5_cstate->pipeline.mixer; struct mdp5_kms *mdp5_kms = get_kms(crtc); - struct mdp5_hw_mixer *mixer = mdp5_crtc->mixer; - - /* now that we know what irq's we want: */ - mdp5_crtc->err.irqmask = intf2err(intf->num); - mdp5_crtc->vblank.irqmask = intf2vblank(mixer, intf); - - if ((intf->type == INTF_DSI) && - (intf->mode == MDP5_INTF_DSI_MODE_COMMAND)) { - mdp5_crtc->pp_done.irqmask = lm2ppdone(mixer); - mdp5_crtc->pp_done.irq = mdp5_crtc_pp_done_irq; - mdp5_crtc->cmd_mode = true; - } else { - mdp5_crtc->pp_done.irqmask = 0; - mdp5_crtc->pp_done.irq = NULL; - mdp5_crtc->cmd_mode = false; - } + /* should this be done elsewhere ? */ mdp_irq_update(&mdp5_kms->base); - mdp5_crtc->ctl = ctl; mdp5_ctl_set_pipeline(ctl, intf, mixer); } struct mdp5_ctl *mdp5_crtc_get_ctl(struct drm_crtc *crtc) { - struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); - return mdp5_crtc->ctl; + return mdp5_cstate->ctl; } struct mdp5_hw_mixer *mdp5_crtc_get_mixer(struct drm_crtc *crtc) { - struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); - return WARN_ON(!crtc) || WARN_ON(!mdp5_crtc->mixer) ? - ERR_PTR(-EINVAL) : mdp5_crtc->mixer; + struct mdp5_crtc_state *mdp5_cstate; + + if (WARN_ON(!crtc)) + return ERR_PTR(-EINVAL); + + mdp5_cstate = to_mdp5_crtc_state(crtc->state); + + return WARN_ON(!mdp5_cstate->pipeline.mixer) ? + ERR_PTR(-EINVAL) : mdp5_cstate->pipeline.mixer; } void mdp5_crtc_wait_for_commit_done(struct drm_crtc *crtc) { - struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); - if (mdp5_crtc->cmd_mode) + if (mdp5_cstate->cmd_mode) mdp5_crtc_wait_for_pp_done(crtc); else mdp5_crtc_wait_for_flush_done(crtc); @@ -918,7 +927,6 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, { struct drm_crtc *crtc = NULL; struct mdp5_crtc *mdp5_crtc; - struct mdp5_kms *mdp5_kms; mdp5_crtc = kzalloc(sizeof(*mdp5_crtc), GFP_KERNEL); if (!mdp5_crtc) @@ -934,6 +942,7 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, mdp5_crtc->vblank.irq = mdp5_crtc_vblank_irq; mdp5_crtc->err.irq = mdp5_crtc_err_irq; + mdp5_crtc->pp_done.irq = mdp5_crtc_pp_done_irq; if (cursor_plane) drm_crtc_init_with_planes(dev, crtc, plane, cursor_plane, @@ -948,8 +957,5 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs); plane->crtc = crtc; - mdp5_kms = get_kms(crtc); - mdp5_crtc->mixer = mdp5_kms->hwmixers[id]; - return crtc; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index 880c7d4208ec5..24f76f48d56c1 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -849,10 +849,6 @@ static int hwmixer_init(struct mdp5_kms *mdp5_kms) return ret; } - /* Don't create LMs connected to WB for now */ - if (!mixer) - continue; - mixer->idx = mdp5_kms->num_hwmixers; mdp5_kms->hwmixers[mdp5_kms->num_hwmixers++] = mixer; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c index 6f353c4886e43..9bb1f824b2a95 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c @@ -84,10 +84,6 @@ struct mdp5_hw_mixer *mdp5_mixer_init(const struct mdp5_lm_instance *lm) { struct mdp5_hw_mixer *mixer; - /* ignore WB bound mixers for now */ - if (lm->caps & MDP_LM_CAP_WB) - return NULL; - mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); if (!mixer) return ERR_PTR(-ENOMEM); From f316b25a23da330678d476acb8a97048a793376d Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:06 +0530 Subject: [PATCH 30/47] drm/msm/mdp5: Remove mixer/intf pointers from mdp5_ctl These are a part of CRTC state, it doesn't feel nice to leave them hanging in mdp5_ctl struct. Pass mdp5_pipeline pointer instead wherever it is needed. We still have some params in mdp5_ctl like start_mask etc which are derivative of atomic state, and should be rolled back if a commit fails, but it doesn't seem to cause much trouble. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- .../gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c | 15 ++-- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 32 ++++++--- drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c | 68 ++++++++++--------- drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h | 16 +++-- drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c | 13 ++-- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 4 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 3 +- 7 files changed, 88 insertions(+), 63 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c index 18c967107bc45..8dafc7bdba481 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c @@ -132,8 +132,6 @@ void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder); - mode = adjusted_mode; DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", @@ -145,8 +143,7 @@ void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder, mode->vsync_end, mode->vtotal, mode->type, mode->flags); pingpong_tearcheck_setup(encoder, mode); - mdp5_crtc_set_pipeline(encoder->crtc, mdp5_cmd_enc->intf, - mdp5_cmd_enc->ctl); + mdp5_crtc_set_pipeline(encoder->crtc); } void mdp5_cmd_encoder_disable(struct drm_encoder *encoder) @@ -154,14 +151,15 @@ void mdp5_cmd_encoder_disable(struct drm_encoder *encoder) struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder); struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl; struct mdp5_interface *intf = mdp5_cmd_enc->intf; + struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc); if (WARN_ON(!mdp5_cmd_enc->enabled)) return; pingpong_tearcheck_disable(encoder); - mdp5_ctl_set_encoder_state(ctl, false); - mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf)); + mdp5_ctl_set_encoder_state(ctl, pipeline, false); + mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf)); bs_set(mdp5_cmd_enc, 0); @@ -173,6 +171,7 @@ void mdp5_cmd_encoder_enable(struct drm_encoder *encoder) struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder); struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl; struct mdp5_interface *intf = mdp5_cmd_enc->intf; + struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc); if (WARN_ON(mdp5_cmd_enc->enabled)) return; @@ -181,9 +180,9 @@ void mdp5_cmd_encoder_enable(struct drm_encoder *encoder) if (pingpong_tearcheck_enable(encoder)) return; - mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf)); + mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf)); - mdp5_ctl_set_encoder_state(ctl, true); + mdp5_ctl_set_encoder_state(ctl, pipeline, true); mdp5_cmd_enc->enabled = true; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index c33855eeda24e..03a64bd39f22a 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -91,9 +91,10 @@ static u32 crtc_flush(struct drm_crtc *crtc, u32 flush_mask) { struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); struct mdp5_ctl *ctl = mdp5_cstate->ctl; + struct mdp5_pipeline *pipeline = &mdp5_cstate->pipeline; DBG("%s: flush=%08x", crtc->name, flush_mask); - return mdp5_ctl_commit(ctl, flush_mask); + return mdp5_ctl_commit(ctl, pipeline, flush_mask); } /* @@ -126,6 +127,7 @@ static u32 crtc_flush_all(struct drm_crtc *crtc) static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) { struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); + struct mdp5_pipeline *pipeline = &mdp5_cstate->pipeline; struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct mdp5_ctl *ctl = mdp5_cstate->ctl; struct drm_device *dev = crtc->dev; @@ -143,7 +145,7 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) if (ctl && !crtc->state->enable) { /* set STAGE_UNUSED for all layers */ - mdp5_ctl_blend(ctl, NULL, 0, 0); + mdp5_ctl_blend(ctl, pipeline, NULL, 0, 0); /* XXX: What to do here? */ /* mdp5_crtc->ctl = NULL; */ } @@ -195,12 +197,13 @@ static void blend_setup(struct drm_crtc *crtc) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); + struct mdp5_pipeline *pipeline = &mdp5_cstate->pipeline; struct mdp5_kms *mdp5_kms = get_kms(crtc); struct drm_plane *plane; const struct mdp5_cfg_hw *hw_cfg; struct mdp5_plane_state *pstate, *pstates[STAGE_MAX + 1] = {NULL}; const struct mdp_format *format; - struct mdp5_hw_mixer *mixer = mdp5_cstate->pipeline.mixer; + struct mdp5_hw_mixer *mixer = pipeline->mixer; uint32_t lm = mixer->lm; struct mdp5_ctl *ctl = mdp5_cstate->ctl; uint32_t blend_op, fg_alpha, bg_alpha, ctl_blend_flags = 0; @@ -294,7 +297,7 @@ static void blend_setup(struct drm_crtc *crtc) mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(lm), mixer_op_mode); - mdp5_ctl_blend(ctl, stage, plane_cnt, ctl_blend_flags); + mdp5_ctl_blend(ctl, pipeline, stage, plane_cnt, ctl_blend_flags); out: spin_unlock_irqrestore(&mdp5_crtc->lm_lock, flags); @@ -586,6 +589,7 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); + struct mdp5_pipeline *pipeline = &mdp5_cstate->pipeline; struct drm_device *dev = crtc->dev; struct mdp5_kms *mdp5_kms = get_kms(crtc); struct drm_gem_object *cursor_bo, *old_bo = NULL; @@ -652,7 +656,7 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, spin_unlock_irqrestore(&mdp5_crtc->cursor.lock, flags); set_cursor: - ret = mdp5_ctl_set_cursor(ctl, 0, cursor_enable); + ret = mdp5_ctl_set_cursor(ctl, pipeline, 0, cursor_enable); if (ret) { dev_err(dev->dev, "failed to %sable cursor: %d\n", cursor_enable ? "en" : "dis", ret); @@ -877,17 +881,15 @@ uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc) return mdp5_crtc->vblank.irqmask; } -void mdp5_crtc_set_pipeline(struct drm_crtc *crtc, - struct mdp5_interface *intf, struct mdp5_ctl *ctl) +void mdp5_crtc_set_pipeline(struct drm_crtc *crtc) { struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); - struct mdp5_hw_mixer *mixer = mdp5_cstate->pipeline.mixer; struct mdp5_kms *mdp5_kms = get_kms(crtc); /* should this be done elsewhere ? */ mdp_irq_update(&mdp5_kms->base); - mdp5_ctl_set_pipeline(ctl, intf, mixer); + mdp5_ctl_set_pipeline(mdp5_cstate->ctl, &mdp5_cstate->pipeline); } struct mdp5_ctl *mdp5_crtc_get_ctl(struct drm_crtc *crtc) @@ -910,6 +912,18 @@ struct mdp5_hw_mixer *mdp5_crtc_get_mixer(struct drm_crtc *crtc) ERR_PTR(-EINVAL) : mdp5_cstate->pipeline.mixer; } +struct mdp5_pipeline *mdp5_crtc_get_pipeline(struct drm_crtc *crtc) +{ + struct mdp5_crtc_state *mdp5_cstate; + + if (WARN_ON(!crtc)) + return ERR_PTR(-EINVAL); + + mdp5_cstate = to_mdp5_crtc_state(crtc->state); + + return &mdp5_cstate->pipeline; +} + void mdp5_crtc_wait_for_commit_done(struct drm_crtc *crtc) { struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c index a86f1fd359c35..9a0109410974e 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c @@ -36,14 +36,10 @@ struct mdp5_ctl { struct mdp5_ctl_manager *ctlm; u32 id; - struct mdp5_hw_mixer *mixer; /* CTL status bitmask */ u32 status; - /* Operation Mode Configuration for the Pipeline */ - struct mdp5_interface *intf; - bool encoder_enabled; uint32_t start_mask; @@ -170,14 +166,12 @@ static void set_ctl_op(struct mdp5_ctl *ctl, struct mdp5_interface *intf) spin_unlock_irqrestore(&ctl->hw_lock, flags); } -int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_interface *intf, - struct mdp5_hw_mixer *mixer) +int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline) { struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm; struct mdp5_kms *mdp5_kms = get_kms(ctl_mgr); - - ctl->mixer = mixer; - ctl->intf = intf; + struct mdp5_interface *intf = pipeline->intf; + struct mdp5_hw_mixer *mixer = pipeline->mixer; ctl->start_mask = mdp_ctl_flush_mask_lm(mixer->lm) | mdp_ctl_flush_mask_encoder(intf); @@ -191,16 +185,19 @@ int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_interface *intf, return 0; } -static bool start_signal_needed(struct mdp5_ctl *ctl) +static bool start_signal_needed(struct mdp5_ctl *ctl, + struct mdp5_pipeline *pipeline) { + struct mdp5_interface *intf = pipeline->intf; + if (!ctl->encoder_enabled || ctl->start_mask != 0) return false; - switch (ctl->intf->type) { + switch (intf->type) { case INTF_WB: return true; case INTF_DSI: - return ctl->intf->mode == MDP5_INTF_DSI_MODE_COMMAND; + return intf->mode == MDP5_INTF_DSI_MODE_COMMAND; default: return false; } @@ -222,11 +219,13 @@ static void send_start_signal(struct mdp5_ctl *ctl) spin_unlock_irqrestore(&ctl->hw_lock, flags); } -static void refill_start_mask(struct mdp5_ctl *ctl) +static void refill_start_mask(struct mdp5_ctl *ctl, + struct mdp5_pipeline *pipeline) { - struct mdp5_interface *intf = ctl->intf; + struct mdp5_interface *intf = pipeline->intf; + struct mdp5_hw_mixer *mixer = pipeline->mixer; - ctl->start_mask = mdp_ctl_flush_mask_lm(ctl->mixer->lm); + ctl->start_mask = mdp_ctl_flush_mask_lm(mixer->lm); /* * Writeback encoder needs to program & flush @@ -244,17 +243,21 @@ static void refill_start_mask(struct mdp5_ctl *ctl) * Note: * This encoder state is needed to trigger START signal (data path kickoff). */ -int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, bool enabled) +int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, + struct mdp5_pipeline *pipeline, + bool enabled) { + struct mdp5_interface *intf = pipeline->intf; + if (WARN_ON(!ctl)) return -EINVAL; ctl->encoder_enabled = enabled; - DBG("intf_%d: %s", ctl->intf->num, enabled ? "on" : "off"); + DBG("intf_%d: %s", intf->num, enabled ? "on" : "off"); - if (start_signal_needed(ctl)) { + if (start_signal_needed(ctl, pipeline)) { send_start_signal(ctl); - refill_start_mask(ctl); + refill_start_mask(ctl, pipeline); } return 0; @@ -265,12 +268,13 @@ int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, bool enabled) * CTL registers need to be flushed after calling this function * (call mdp5_ctl_commit() with mdp_ctl_flush_mask_ctl() mask) */ -int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, int cursor_id, bool enable) +int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, + int cursor_id, bool enable) { struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm; unsigned long flags; u32 blend_cfg; - struct mdp5_hw_mixer *mixer = ctl->mixer; + struct mdp5_hw_mixer *mixer = pipeline->mixer; if (unlikely(WARN_ON(!mixer))) { dev_err(ctl_mgr->dev->dev, "CTL %d cannot find LM", @@ -340,10 +344,10 @@ static u32 mdp_ctl_blend_ext_mask(enum mdp5_pipe pipe, } } -int mdp5_ctl_blend(struct mdp5_ctl *ctl, enum mdp5_pipe *stage, u32 stage_cnt, - u32 ctl_blend_op_flags) +int mdp5_ctl_blend(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, + enum mdp5_pipe *stage, u32 stage_cnt, u32 ctl_blend_op_flags) { - struct mdp5_hw_mixer *mixer = ctl->mixer; + struct mdp5_hw_mixer *mixer = pipeline->mixer; unsigned long flags; u32 blend_cfg = 0, blend_ext_cfg = 0; int i, start_stage; @@ -430,7 +434,8 @@ u32 mdp_ctl_flush_mask_lm(int lm) } } -static u32 fix_sw_flush(struct mdp5_ctl *ctl, u32 flush_mask) +static u32 fix_sw_flush(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, + u32 flush_mask) { struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm; u32 sw_mask = 0; @@ -439,7 +444,7 @@ static u32 fix_sw_flush(struct mdp5_ctl *ctl, u32 flush_mask) /* for some targets, cursor bit is the same as LM bit */ if (BIT_NEEDS_SW_FIX(MDP5_CTL_FLUSH_CURSOR_0)) - sw_mask |= mdp_ctl_flush_mask_lm(ctl->mixer->lm); + sw_mask |= mdp_ctl_flush_mask_lm(pipeline->mixer->lm); return sw_mask; } @@ -485,7 +490,9 @@ static void fix_for_single_flush(struct mdp5_ctl *ctl, u32 *flush_mask, * * Return H/W flushed bit mask. */ -u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask) +u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, + struct mdp5_pipeline *pipeline, + u32 flush_mask) { struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm; unsigned long flags; @@ -502,7 +509,7 @@ u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask) ctl->pending_ctl_trigger = 0; } - flush_mask |= fix_sw_flush(ctl, flush_mask); + flush_mask |= fix_sw_flush(ctl, pipeline, flush_mask); flush_mask &= ctl_mgr->flush_hw_mask; @@ -516,9 +523,9 @@ u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask) spin_unlock_irqrestore(&ctl->hw_lock, flags); } - if (start_signal_needed(ctl)) { + if (start_signal_needed(ctl, pipeline)) { send_start_signal(ctl); - refill_start_mask(ctl); + refill_start_mask(ctl, pipeline); } return curr_ctl_flush_mask; @@ -605,7 +612,6 @@ struct mdp5_ctl *mdp5_ctlm_request(struct mdp5_ctl_manager *ctl_mgr, found: ctl = &ctl_mgr->ctls[c]; - ctl->mixer = NULL; ctl->status |= CTL_STAT_BUSY; ctl->pending_ctl_trigger = 0; DBG("CTL %d allocated", ctl->id); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h index 882c9d2be3650..751dd861cfd83 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h @@ -37,11 +37,13 @@ struct mdp5_ctl *mdp5_ctlm_request(struct mdp5_ctl_manager *ctlm, int intf_num); int mdp5_ctl_get_ctl_id(struct mdp5_ctl *ctl); struct mdp5_interface; -int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_interface *intf, - struct mdp5_hw_mixer *lm); -int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, bool enabled); +struct mdp5_pipeline; +int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_pipeline *p); +int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, struct mdp5_pipeline *p, + bool enabled); -int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, int cursor_id, bool enable); +int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, + int cursor_id, bool enable); int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable); /* @@ -56,7 +58,8 @@ int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable); * (call mdp5_ctl_commit() with mdp_ctl_flush_mask_ctl() mask) */ #define MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT BIT(0) -int mdp5_ctl_blend(struct mdp5_ctl *ctl, enum mdp5_pipe *stage, u32 stage_cnt, +int mdp5_ctl_blend(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, + enum mdp5_pipe *stage, u32 stage_cnt, u32 ctl_blend_op_flags); /** @@ -71,7 +74,8 @@ u32 mdp_ctl_flush_mask_cursor(int cursor_id); u32 mdp_ctl_flush_mask_encoder(struct mdp5_interface *intf); /* @flush_mask: see CTL flush masks definitions below */ -u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask); +u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, + u32 flush_mask); u32 mdp5_ctl_get_commit_status(struct mdp5_ctl *ctl); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c index 6d535140ef217..c2ab0f0330318 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c @@ -206,8 +206,7 @@ static void mdp5_vid_encoder_mode_set(struct drm_encoder *encoder, spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags); - mdp5_crtc_set_pipeline(encoder->crtc, mdp5_encoder->intf, - mdp5_encoder->ctl); + mdp5_crtc_set_pipeline(encoder->crtc); } static void mdp5_vid_encoder_disable(struct drm_encoder *encoder) @@ -215,6 +214,7 @@ static void mdp5_vid_encoder_disable(struct drm_encoder *encoder) struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); struct mdp5_kms *mdp5_kms = get_kms(encoder); struct mdp5_ctl *ctl = mdp5_encoder->ctl; + struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc); struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc); struct mdp5_interface *intf = mdp5_encoder->intf; int intfn = mdp5_encoder->intf->num; @@ -223,12 +223,12 @@ static void mdp5_vid_encoder_disable(struct drm_encoder *encoder) if (WARN_ON(!mdp5_encoder->enabled)) return; - mdp5_ctl_set_encoder_state(ctl, false); + mdp5_ctl_set_encoder_state(ctl, pipeline, false); spin_lock_irqsave(&mdp5_encoder->intf_lock, flags); mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intfn), 0); spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags); - mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf)); + mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf)); /* * Wait for a vsync so we know the ENABLE=0 latched before @@ -251,6 +251,7 @@ static void mdp5_vid_encoder_enable(struct drm_encoder *encoder) struct mdp5_kms *mdp5_kms = get_kms(encoder); struct mdp5_ctl *ctl = mdp5_encoder->ctl; struct mdp5_interface *intf = mdp5_encoder->intf; + struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc); int intfn = intf->num; unsigned long flags; @@ -261,9 +262,9 @@ static void mdp5_vid_encoder_enable(struct drm_encoder *encoder) spin_lock_irqsave(&mdp5_encoder->intf_lock, flags); mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intfn), 1); spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags); - mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf)); + mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf)); - mdp5_ctl_set_encoder_state(ctl, true); + mdp5_ctl_set_encoder_state(ctl, pipeline, true); mdp5_encoder->enabled = true; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index 35a21e6c58563..fcd067f65a933 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -280,8 +280,8 @@ struct mdp5_ctl *mdp5_crtc_get_ctl(struct drm_crtc *crtc); uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc); struct mdp5_hw_mixer *mdp5_crtc_get_mixer(struct drm_crtc *crtc); -void mdp5_crtc_set_pipeline(struct drm_crtc *crtc, - struct mdp5_interface *intf, struct mdp5_ctl *ctl); +struct mdp5_pipeline *mdp5_crtc_get_pipeline(struct drm_crtc *crtc); +void mdp5_crtc_set_pipeline(struct drm_crtc *crtc); void mdp5_crtc_wait_for_commit_done(struct drm_crtc *crtc); struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, struct drm_plane *plane, diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 49e50debda902..6086a2de3d27f 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -937,6 +937,7 @@ static int mdp5_update_cursor_plane_legacy(struct drm_plane *plane, if (new_plane_state->visible) { struct mdp5_ctl *ctl; + struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(crtc); ret = mdp5_plane_mode_set(plane, crtc, fb, &new_plane_state->src, @@ -945,7 +946,7 @@ static int mdp5_update_cursor_plane_legacy(struct drm_plane *plane, ctl = mdp5_crtc_get_ctl(crtc); - mdp5_ctl_commit(ctl, mdp5_plane_get_flush(plane)); + mdp5_ctl_commit(ctl, pipeline, mdp5_plane_get_flush(plane)); } *to_mdp5_plane_state(plane_state) = From 621da7d93ceca260c2a95ca240e58f08fe30b1dc Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:07 +0530 Subject: [PATCH 31/47] drm/msm/mdp5: Add a CAP for Source Split Some of the newer MDP5 versions support Source Split of SSPPs. It is a feature that allows us to route the output of a hwpipe to 2 Layer Mixers. This is required to achieve the following use cases: - Dual DSI: For high res DSI panels (such as 2560x1600 etc), a single DSI interface doesn't have the bandwidth to drive the required pixel clock. We use 2 DSI interfaces to drive the left and right halves of the panel (i.e, 1280x1600 each). The MDP5 pipeline here would look like: LM0 -- DSPP0 -- INTF1 -- DSI1 / hwpipe-- \ LM1 -- DSPP1 -- INTF2 -- DSI2 A single hwpipe is used to scan out the left and right halves to DSI1 and DSI2 respectively. In order to do this, we need to configure the 2 Layer Mixers in Source Split mode. - HDMI 4K: In order to support resolutions with width higher than the max width supported by a hwpipe, we club 2 hwpipes together: hwpipe1 --- LM0 -- DSPP0 - - \ - -- 3D Mux -- INTF0 -- HDMI - - / hwpipe2 --- LM1 -- DSPP1 hwpipe1 is staged on the 'left' Layer Mixer, and hwpipe2 is staged on the 'right' Layer Mixer. An additional block called the '3D Mux' is used to merge the output of the 2 DSPPs to a single interface. In this use case, it is possible that a 4K surface is downscaled and placed completely within one of the halves. In order to support such scenarios (and keep the programming simple), Layer Mixers with Source Split can be assigned 2 hw pipes per stage. While scanning out, the HW takes care of fetching the pixels fom the correct pipe. Add a MDP cap to tell whether the HW supports source split or not. Add a MDP LM cap that tells whether a LM instance can operate in source split mode (and generate the 'left' part of the display output). Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c | 21 +++++++++++++++------ drivers/gpu/drm/msm/mdp/mdp_kms.h | 2 ++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c index 124aa1e62aa5c..c2bdad88447eb 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c @@ -191,6 +191,7 @@ const struct mdp5_cfg_hw apq8084_config = { .mdp = { .count = 1, .caps = MDP_CAP_SMP | + MDP_CAP_SRC_SPLIT | 0, }, .smp = { @@ -237,11 +238,13 @@ const struct mdp5_cfg_hw apq8084_config = { .base = { 0x03900, 0x03d00, 0x04100, 0x04500, 0x04900, 0x04d00 }, .instances = { { .id = 0, .pp = 0, .dspp = 0, - .caps = MDP_LM_CAP_DISPLAY, }, + .caps = MDP_LM_CAP_DISPLAY | + MDP_LM_CAP_PAIR, }, { .id = 1, .pp = 1, .dspp = 1, .caps = MDP_LM_CAP_DISPLAY, }, { .id = 2, .pp = 2, .dspp = 2, - .caps = MDP_LM_CAP_DISPLAY, }, + .caps = MDP_LM_CAP_DISPLAY | + MDP_LM_CAP_PAIR, }, { .id = 3, .pp = -1, .dspp = -1, .caps = MDP_LM_CAP_WB, }, { .id = 4, .pp = -1, .dspp = -1, @@ -350,6 +353,7 @@ const struct mdp5_cfg_hw msm8x94_config = { .mdp = { .count = 1, .caps = MDP_CAP_SMP | + MDP_CAP_SRC_SPLIT | 0, }, .smp = { @@ -396,11 +400,13 @@ const struct mdp5_cfg_hw msm8x94_config = { .base = { 0x44000, 0x45000, 0x46000, 0x47000, 0x48000, 0x49000 }, .instances = { { .id = 0, .pp = 0, .dspp = 0, - .caps = MDP_LM_CAP_DISPLAY, }, + .caps = MDP_LM_CAP_DISPLAY | + MDP_LM_CAP_PAIR, }, { .id = 1, .pp = 1, .dspp = 1, .caps = MDP_LM_CAP_DISPLAY, }, { .id = 2, .pp = 2, .dspp = 2, - .caps = MDP_LM_CAP_DISPLAY, }, + .caps = MDP_LM_CAP_DISPLAY | + MDP_LM_CAP_PAIR, }, { .id = 3, .pp = -1, .dspp = -1, .caps = MDP_LM_CAP_WB, }, { .id = 4, .pp = -1, .dspp = -1, @@ -443,6 +449,7 @@ const struct mdp5_cfg_hw msm8x96_config = { .count = 1, .caps = MDP_CAP_DSC | MDP_CAP_CDM | + MDP_CAP_SRC_SPLIT | 0, }, .ctl = { @@ -494,11 +501,13 @@ const struct mdp5_cfg_hw msm8x96_config = { .base = { 0x44000, 0x45000, 0x46000, 0x47000, 0x48000, 0x49000 }, .instances = { { .id = 0, .pp = 0, .dspp = 0, - .caps = MDP_LM_CAP_DISPLAY }, + .caps = MDP_LM_CAP_DISPLAY | + MDP_LM_CAP_PAIR, }, { .id = 1, .pp = 1, .dspp = 1, .caps = MDP_LM_CAP_DISPLAY, }, { .id = 2, .pp = 2, .dspp = -1, - .caps = MDP_LM_CAP_DISPLAY }, + .caps = MDP_LM_CAP_DISPLAY | + MDP_LM_CAP_PAIR, }, { .id = 3, .pp = -1, .dspp = -1, .caps = MDP_LM_CAP_WB, }, { .id = 4, .pp = -1, .dspp = -1, diff --git a/drivers/gpu/drm/msm/mdp/mdp_kms.h b/drivers/gpu/drm/msm/mdp/mdp_kms.h index bf4db664ee865..1185487e7e5e3 100644 --- a/drivers/gpu/drm/msm/mdp/mdp_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp_kms.h @@ -104,6 +104,7 @@ const struct msm_format *mdp_get_format(struct msm_kms *kms, uint32_t format); #define MDP_CAP_SMP BIT(0) /* Shared Memory Pool */ #define MDP_CAP_DSC BIT(1) /* VESA Display Stream Compression */ #define MDP_CAP_CDM BIT(2) /* Chroma Down Module (HDMI 2.0 YUV) */ +#define MDP_CAP_SRC_SPLIT BIT(3) /* Source Split of SSPPs */ /* MDP pipe capabilities */ #define MDP_PIPE_CAP_HFLIP BIT(0) @@ -117,6 +118,7 @@ const struct msm_format *mdp_get_format(struct msm_kms *kms, uint32_t format); /* MDP layer mixer caps */ #define MDP_LM_CAP_DISPLAY BIT(0) #define MDP_LM_CAP_WB BIT(1) +#define MDP_LM_CAP_PAIR BIT(2) static inline bool pipe_supports_yuv(uint32_t pipe_caps) { From b7621b2a081c2ac2c1c54070f6e88f774fe4b6b1 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:08 +0530 Subject: [PATCH 32/47] drm/msm/mdp5: Add optional 'right' Layer Mixer in CRTC state Add another mdp5_hw_mixer pointer (r_mixer) in mdp5_crtc_state. This mixer will be used to generate the right half of the scanout. With Source Split, a SSPP can now be connected to 2 Layer Mixers, but has to be at the same blend level (stage #) on both Layer Mixers. A drm_plane that has a lesser width than the max width supported, will comprise of a single SSPP/hwpipe, staged on both the Layer Mixers at the same blend level. A plane that is greater than max width will comprise of 2 SSPPs, with the 'left' SSPP staged on the left LM, and the 'right' SSPP staged on the right LM at the same blend level. For now, the drm_plane consists of only one SSPP, therefore, it needs to be staged on both the LMs in blend_setup() and mdp5_ctl_blend(). We'll extend this logic to support 2 hwpipes per plane later. The crtc cursor ops (using the LM cursors, not SSPP cursors) simply return an error if they're called when the right mixer is assigned to the CRTC state. With source split is enabled, we're expected to only SSPP cursors. This commit adds code that configures the right mixer, but the r_mixer itself isn't assigned at the moment. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 62 +++++++++++++++++++++--- drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c | 44 +++++++++++++++-- drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h | 7 ++- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 1 + 4 files changed, 103 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 03a64bd39f22a..82827f3e2a473 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -105,7 +105,7 @@ static u32 crtc_flush(struct drm_crtc *crtc, u32 flush_mask) static u32 crtc_flush_all(struct drm_crtc *crtc) { struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); - struct mdp5_hw_mixer *mixer; + struct mdp5_hw_mixer *mixer, *r_mixer; struct drm_plane *plane; uint32_t flush_mask = 0; @@ -120,6 +120,10 @@ static u32 crtc_flush_all(struct drm_crtc *crtc) mixer = mdp5_cstate->pipeline.mixer; flush_mask |= mdp_ctl_flush_mask_lm(mixer->lm); + r_mixer = mdp5_cstate->pipeline.r_mixer; + if (r_mixer) + flush_mask |= mdp_ctl_flush_mask_lm(r_mixer->lm); + return crtc_flush(crtc, flush_mask); } @@ -145,7 +149,7 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) if (ctl && !crtc->state->enable) { /* set STAGE_UNUSED for all layers */ - mdp5_ctl_blend(ctl, pipeline, NULL, 0, 0); + mdp5_ctl_blend(ctl, pipeline, NULL, NULL, 0, 0); /* XXX: What to do here? */ /* mdp5_crtc->ctl = NULL; */ } @@ -186,6 +190,12 @@ static inline u32 mdp5_lm_use_fg_alpha_mask(enum mdp_mixer_stage_id stage) } } +/* + * left/right pipe offsets for the stage array used in blend_setup() + */ +#define PIPE_LEFT 0 +#define PIPE_RIGHT 1 + /* * blend_setup() - blend all the planes of a CRTC * @@ -205,10 +215,13 @@ static void blend_setup(struct drm_crtc *crtc) const struct mdp_format *format; struct mdp5_hw_mixer *mixer = pipeline->mixer; uint32_t lm = mixer->lm; + struct mdp5_hw_mixer *r_mixer = pipeline->r_mixer; + uint32_t r_lm = r_mixer ? r_mixer->lm : 0; struct mdp5_ctl *ctl = mdp5_cstate->ctl; uint32_t blend_op, fg_alpha, bg_alpha, ctl_blend_flags = 0; unsigned long flags; - enum mdp5_pipe stage[STAGE_MAX + 1] = { SSPP_NONE }; + enum mdp5_pipe stage[STAGE_MAX + 1][MAX_PIPE_STAGE] = { SSPP_NONE }; + enum mdp5_pipe r_stage[STAGE_MAX + 1][MAX_PIPE_STAGE] = { SSPP_NONE }; int i, plane_cnt = 0; bool bg_alpha_enabled = false; u32 mixer_op_mode = 0; @@ -227,7 +240,15 @@ static void blend_setup(struct drm_crtc *crtc) drm_atomic_crtc_for_each_plane(plane, crtc) { pstate = to_mdp5_plane_state(plane->state); pstates[pstate->stage] = pstate; - stage[pstate->stage] = mdp5_plane_pipe(plane); + stage[pstate->stage][PIPE_LEFT] = mdp5_plane_pipe(plane); + /* + * if we have a right mixer, stage the same pipe as we + * have on the left mixer + */ + if (r_mixer) + r_stage[pstate->stage][PIPE_LEFT] = + mdp5_plane_pipe(plane); + plane_cnt++; } @@ -293,12 +314,23 @@ static void blend_setup(struct drm_crtc *crtc) blender(i)), fg_alpha); mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_BG_ALPHA(lm, blender(i)), bg_alpha); + if (r_mixer) { + mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_OP_MODE(r_lm, + blender(i)), blend_op); + mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_FG_ALPHA(r_lm, + blender(i)), fg_alpha); + mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_BG_ALPHA(r_lm, + blender(i)), bg_alpha); + } } mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(lm), mixer_op_mode); + if (r_mixer) + mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(r_lm), + mixer_op_mode); - mdp5_ctl_blend(ctl, pipeline, stage, plane_cnt, ctl_blend_flags); - + mdp5_ctl_blend(ctl, pipeline, stage, r_stage, plane_cnt, + ctl_blend_flags); out: spin_unlock_irqrestore(&mdp5_crtc->lm_lock, flags); } @@ -309,6 +341,7 @@ static void mdp5_crtc_mode_set_nofb(struct drm_crtc *crtc) struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); struct mdp5_kms *mdp5_kms = get_kms(crtc); struct mdp5_hw_mixer *mixer = mdp5_cstate->pipeline.mixer; + struct mdp5_hw_mixer *r_mixer = mdp5_cstate->pipeline.r_mixer; uint32_t lm = mixer->lm; unsigned long flags; struct drm_display_mode *mode; @@ -331,6 +364,10 @@ static void mdp5_crtc_mode_set_nofb(struct drm_crtc *crtc) mdp5_write(mdp5_kms, REG_MDP5_LM_OUT_SIZE(lm), MDP5_LM_OUT_SIZE_WIDTH(mode->hdisplay) | MDP5_LM_OUT_SIZE_HEIGHT(mode->vdisplay)); + if (r_mixer) + mdp5_write(mdp5_kms, REG_MDP5_LM_OUT_SIZE(r_mixer->lm), + MDP5_LM_OUT_SIZE_WIDTH(mode->hdisplay) | + MDP5_LM_OUT_SIZE_HEIGHT(mode->vdisplay)); spin_unlock_irqrestore(&mdp5_crtc->lm_lock, flags); } @@ -612,6 +649,10 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, if (!ctl) return -EINVAL; + /* don't support LM cursors when we we have source split enabled */ + if (mdp5_cstate->pipeline.r_mixer) + return -EINVAL; + if (!handle) { DBG("Cursor off"); cursor_enable = false; @@ -685,6 +726,10 @@ static int mdp5_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) uint32_t roi_h; unsigned long flags; + /* don't support LM cursors when we we have source split enabled */ + if (mdp5_cstate->pipeline.r_mixer) + return -EINVAL; + /* In case the CRTC is disabled, just drop the cursor update */ if (unlikely(!crtc->state->enable)) return 0; @@ -714,12 +759,17 @@ mdp5_crtc_atomic_print_state(struct drm_printer *p, { struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(state); struct mdp5_pipeline *pipeline = &mdp5_cstate->pipeline; + struct mdp5_kms *mdp5_kms = get_kms(state->crtc); if (WARN_ON(!pipeline)) return; drm_printf(p, "\thwmixer=%s\n", pipeline->mixer ? pipeline->mixer->name : "(null)"); + + if (mdp5_kms->caps & MDP_CAP_SRC_SPLIT) + drm_printf(p, "\tright hwmixer=%s\n", pipeline->r_mixer ? + pipeline->r_mixer->name : "(null)"); } static void mdp5_crtc_reset(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c index 9a0109410974e..1fdbb936877f5 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c @@ -172,9 +172,12 @@ int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline) struct mdp5_kms *mdp5_kms = get_kms(ctl_mgr); struct mdp5_interface *intf = pipeline->intf; struct mdp5_hw_mixer *mixer = pipeline->mixer; + struct mdp5_hw_mixer *r_mixer = pipeline->r_mixer; ctl->start_mask = mdp_ctl_flush_mask_lm(mixer->lm) | mdp_ctl_flush_mask_encoder(intf); + if (r_mixer) + ctl->start_mask |= mdp_ctl_flush_mask_lm(r_mixer->lm); /* Virtual interfaces need not set a display intf (e.g.: Writeback) */ if (!mdp5_cfg_intf_is_virtual(intf->type)) @@ -224,8 +227,11 @@ static void refill_start_mask(struct mdp5_ctl *ctl, { struct mdp5_interface *intf = pipeline->intf; struct mdp5_hw_mixer *mixer = pipeline->mixer; + struct mdp5_hw_mixer *r_mixer = pipeline->r_mixer; ctl->start_mask = mdp_ctl_flush_mask_lm(mixer->lm); + if (r_mixer) + ctl->start_mask |= mdp_ctl_flush_mask_lm(r_mixer->lm); /* * Writeback encoder needs to program & flush @@ -282,6 +288,11 @@ int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, return -EINVAL; } + if (pipeline->r_mixer) { + dev_err(ctl_mgr->dev->dev, "unsupported configuration"); + return -EINVAL; + } + spin_lock_irqsave(&ctl->hw_lock, flags); blend_cfg = ctl_read(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, mixer->lm)); @@ -344,24 +355,40 @@ static u32 mdp_ctl_blend_ext_mask(enum mdp5_pipe pipe, } } +#define PIPE_LEFT 0 +#define PIPE_RIGHT 1 int mdp5_ctl_blend(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, - enum mdp5_pipe *stage, u32 stage_cnt, u32 ctl_blend_op_flags) + enum mdp5_pipe stage[][MAX_PIPE_STAGE], + enum mdp5_pipe r_stage[][MAX_PIPE_STAGE], + u32 stage_cnt, u32 ctl_blend_op_flags) { struct mdp5_hw_mixer *mixer = pipeline->mixer; + struct mdp5_hw_mixer *r_mixer = pipeline->r_mixer; unsigned long flags; u32 blend_cfg = 0, blend_ext_cfg = 0; + u32 r_blend_cfg = 0, r_blend_ext_cfg = 0; int i, start_stage; if (ctl_blend_op_flags & MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT) { start_stage = STAGE0; blend_cfg |= MDP5_CTL_LAYER_REG_BORDER_COLOR; + if (r_mixer) + r_blend_cfg |= MDP5_CTL_LAYER_REG_BORDER_COLOR; } else { start_stage = STAGE_BASE; } for (i = start_stage; stage_cnt && i <= STAGE_MAX; i++) { - blend_cfg |= mdp_ctl_blend_mask(stage[i], i); - blend_ext_cfg |= mdp_ctl_blend_ext_mask(stage[i], i); + blend_cfg |= + mdp_ctl_blend_mask(stage[i][PIPE_LEFT], i); + blend_ext_cfg |= + mdp_ctl_blend_ext_mask(stage[i][PIPE_LEFT], i); + if (r_mixer) { + r_blend_cfg |= + mdp_ctl_blend_mask(r_stage[i][PIPE_LEFT], i); + r_blend_ext_cfg |= + mdp_ctl_blend_ext_mask(r_stage[i][PIPE_LEFT], i); + } } spin_lock_irqsave(&ctl->hw_lock, flags); @@ -371,12 +398,23 @@ int mdp5_ctl_blend(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, mixer->lm), blend_cfg); ctl_write(ctl, REG_MDP5_CTL_LAYER_EXT_REG(ctl->id, mixer->lm), blend_ext_cfg); + if (r_mixer) { + ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, r_mixer->lm), + r_blend_cfg); + ctl_write(ctl, REG_MDP5_CTL_LAYER_EXT_REG(ctl->id, r_mixer->lm), + r_blend_ext_cfg); + } spin_unlock_irqrestore(&ctl->hw_lock, flags); ctl->pending_ctl_trigger = mdp_ctl_flush_mask_lm(mixer->lm); + if (r_mixer) + ctl->pending_ctl_trigger |= mdp_ctl_flush_mask_lm(r_mixer->lm); DBG("lm%d: blend config = 0x%08x. ext_cfg = 0x%08x", mixer->lm, blend_cfg, blend_ext_cfg); + if (r_mixer) + DBG("lm%d: blend config = 0x%08x. ext_cfg = 0x%08x", + r_mixer->lm, r_blend_cfg, r_blend_ext_cfg); return 0; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h index 751dd861cfd83..b63120388dc67 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h @@ -46,6 +46,8 @@ int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, int cursor_id, bool enable); int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable); +#define MAX_PIPE_STAGE 2 + /* * mdp5_ctl_blend() - Blend multiple layers on a Layer Mixer (LM) * @@ -59,8 +61,9 @@ int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable); */ #define MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT BIT(0) int mdp5_ctl_blend(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, - enum mdp5_pipe *stage, u32 stage_cnt, - u32 ctl_blend_op_flags); + enum mdp5_pipe stage[][MAX_PIPE_STAGE], + enum mdp5_pipe r_stage[][MAX_PIPE_STAGE], + u32 stage_cnt, u32 ctl_blend_op_flags); /** * mdp_ctl_flush_mask...() - Register FLUSH masks diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index fcd067f65a933..d68e92d71afef 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -119,6 +119,7 @@ struct mdp5_plane_state { struct mdp5_pipeline { struct mdp5_interface *intf; struct mdp5_hw_mixer *mixer; + struct mdp5_hw_mixer *r_mixer; /* right mixer */ }; struct mdp5_crtc_state { From 821be43ff2424b13bc6152360eb6d5bacfbf8530 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:09 +0530 Subject: [PATCH 33/47] drm/msm/mdp5: Create mdp5_hwpipe_mode_set Refactor mdp5_plane_mode_set to call mdp5_hwpipe_mode_set. The latter func takes in only the hwpipe and the parameters that need to be programmed into the hwpipe registers. All the code that calculates these parameters is left as is in mdp5_plane_mode_set. In the future, when we let drm_plane be comprised of 2 hwpipes, this func allow us to configure each pipe without adding redundant code. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 227 +++++++++++++--------- 1 file changed, 130 insertions(+), 97 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 6086a2de3d27f..2975974728023 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -42,9 +42,6 @@ static int mdp5_update_cursor_plane_legacy(struct drm_plane *plane, uint32_t src_w, uint32_t src_h, struct drm_modeset_acquire_ctx *ctx); -static void set_scanout_locked(struct drm_plane *plane, - struct drm_framebuffer *fb); - static struct mdp5_kms *get_kms(struct drm_plane *plane) { struct msm_drm_private *priv = plane->dev->dev_private; @@ -439,13 +436,10 @@ static const struct drm_plane_helper_funcs mdp5_plane_helper_funcs = { .atomic_update = mdp5_plane_atomic_update, }; -static void set_scanout_locked(struct drm_plane *plane, - struct drm_framebuffer *fb) +static void set_scanout_locked(struct mdp5_kms *mdp5_kms, + enum mdp5_pipe pipe, + struct drm_framebuffer *fb) { - struct mdp5_kms *mdp5_kms = get_kms(plane); - struct mdp5_hw_pipe *hwpipe = to_mdp5_plane_state(plane->state)->hwpipe; - enum mdp5_pipe pipe = hwpipe->pipe; - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe), MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) | MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1])); @@ -462,8 +456,6 @@ static void set_scanout_locked(struct drm_plane *plane, msm_framebuffer_iova(fb, mdp5_kms->id, 2)); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe), msm_framebuffer_iova(fb, mdp5_kms->id, 3)); - - plane->fb = fb; } /* Note: mdp5_plane->pipe_lock must be locked */ @@ -716,6 +708,114 @@ static void mdp5_write_pixel_ext(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe, } } +struct pixel_ext { + int left[COMP_MAX]; + int right[COMP_MAX]; + int top[COMP_MAX]; + int bottom[COMP_MAX]; +}; + +struct phase_step { + u32 x[COMP_MAX]; + u32 y[COMP_MAX]; +}; + +static void mdp5_hwpipe_mode_set(struct mdp5_kms *mdp5_kms, + struct mdp5_hw_pipe *hwpipe, + struct drm_framebuffer *fb, + struct phase_step *step, + struct pixel_ext *pe, + u32 scale_config, u32 hdecm, u32 vdecm, + bool hflip, bool vflip, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + u32 src_img_w, u32 src_img_h, + u32 src_x, u32 src_y, + u32 src_w, u32 src_h) +{ + enum mdp5_pipe pipe = hwpipe->pipe; + bool has_pe = hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT; + const struct mdp_format *format = + to_mdp_format(msm_framebuffer_format(fb)); + + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe), + MDP5_PIPE_SRC_IMG_SIZE_WIDTH(src_img_w) | + MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(src_img_h)); + + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_SIZE(pipe), + MDP5_PIPE_SRC_SIZE_WIDTH(src_w) | + MDP5_PIPE_SRC_SIZE_HEIGHT(src_h)); + + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_XY(pipe), + MDP5_PIPE_SRC_XY_X(src_x) | + MDP5_PIPE_SRC_XY_Y(src_y)); + + mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_SIZE(pipe), + MDP5_PIPE_OUT_SIZE_WIDTH(crtc_w) | + MDP5_PIPE_OUT_SIZE_HEIGHT(crtc_h)); + + mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_XY(pipe), + MDP5_PIPE_OUT_XY_X(crtc_x) | + MDP5_PIPE_OUT_XY_Y(crtc_y)); + + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_FORMAT(pipe), + MDP5_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) | + MDP5_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) | + MDP5_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) | + MDP5_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) | + COND(format->alpha_enable, MDP5_PIPE_SRC_FORMAT_ALPHA_ENABLE) | + MDP5_PIPE_SRC_FORMAT_CPP(format->cpp - 1) | + MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) | + COND(format->unpack_tight, MDP5_PIPE_SRC_FORMAT_UNPACK_TIGHT) | + MDP5_PIPE_SRC_FORMAT_FETCH_TYPE(format->fetch_type) | + MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(format->chroma_sample)); + + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_UNPACK(pipe), + MDP5_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) | + MDP5_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) | + MDP5_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) | + MDP5_PIPE_SRC_UNPACK_ELEM3(format->unpack[3])); + + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_OP_MODE(pipe), + (hflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_LR : 0) | + (vflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_UD : 0) | + COND(has_pe, MDP5_PIPE_SRC_OP_MODE_SW_PIX_EXT_OVERRIDE) | + MDP5_PIPE_SRC_OP_MODE_BWC(BWC_LOSSLESS)); + + /* not using secure mode: */ + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(pipe), 0); + + if (hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT) + mdp5_write_pixel_ext(mdp5_kms, pipe, format, + src_w, pe->left, pe->right, + src_h, pe->top, pe->bottom); + + if (hwpipe->caps & MDP_PIPE_CAP_SCALE) { + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe), + step->x[COMP_0]); + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe), + step->y[COMP_0]); + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_X(pipe), + step->x[COMP_1_2]); + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_Y(pipe), + step->y[COMP_1_2]); + mdp5_write(mdp5_kms, REG_MDP5_PIPE_DECIMATION(pipe), + MDP5_PIPE_DECIMATION_VERT(vdecm) | + MDP5_PIPE_DECIMATION_HORZ(hdecm)); + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe), + scale_config); + } + + if (hwpipe->caps & MDP_PIPE_CAP_CSC) { + if (MDP_FORMAT_IS_YUV(format)) + csc_enable(mdp5_kms, pipe, + mdp_get_default_csc_cfg(CSC_YUV2RGB)); + else + csc_disable(mdp5_kms, pipe); + } + + set_scanout_locked(mdp5_kms, pipe, fb); +} static int mdp5_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, @@ -728,10 +828,8 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, enum mdp5_pipe pipe = hwpipe->pipe; const struct mdp_format *format; uint32_t nplanes, config = 0; - uint32_t phasex_step[COMP_MAX] = {0,}, phasey_step[COMP_MAX] = {0,}; - bool pe = hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT; - int pe_left[COMP_MAX], pe_right[COMP_MAX]; - int pe_top[COMP_MAX], pe_bottom[COMP_MAX]; + struct phase_step step = { 0 }; + struct pixel_ext pe = { 0 }; uint32_t hdecm = 0, vdecm = 0; uint32_t pix_format; unsigned int rotation; @@ -740,6 +838,7 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, unsigned int crtc_w, crtc_h; uint32_t src_x, src_y; uint32_t src_w, src_h; + uint32_t src_img_w, src_img_h; unsigned long flags; int ret; @@ -768,23 +867,26 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, src_w = src_w >> 16; src_h = src_h >> 16; + src_img_w = min(fb->width, src_w); + src_img_h = min(fb->height, src_h); + DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", plane->name, fb->base.id, src_x, src_y, src_w, src_h, crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h); - ret = calc_scalex_steps(plane, pix_format, src_w, crtc_w, phasex_step); + ret = calc_scalex_steps(plane, pix_format, src_w, crtc_w, step.x); if (ret) return ret; - ret = calc_scaley_steps(plane, pix_format, src_h, crtc_h, phasey_step); + ret = calc_scaley_steps(plane, pix_format, src_h, crtc_h, step.y); if (ret) return ret; if (hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT) { - calc_pixel_ext(format, src_w, crtc_w, phasex_step, - pe_left, pe_right, true); - calc_pixel_ext(format, src_h, crtc_h, phasey_step, - pe_top, pe_bottom, false); + calc_pixel_ext(format, src_w, crtc_w, step.x, + pe.left, pe.right, true); + calc_pixel_ext(format, src_h, crtc_h, step.y, + pe.top, pe.bottom, false); } /* TODO calc hdecm, vdecm */ @@ -803,85 +905,16 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, spin_lock_irqsave(&mdp5_plane->pipe_lock, flags); - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe), - MDP5_PIPE_SRC_IMG_SIZE_WIDTH(min(fb->width, src_w)) | - MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(min(fb->height, src_h))); - - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_SIZE(pipe), - MDP5_PIPE_SRC_SIZE_WIDTH(src_w) | - MDP5_PIPE_SRC_SIZE_HEIGHT(src_h)); - - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_XY(pipe), - MDP5_PIPE_SRC_XY_X(src_x) | - MDP5_PIPE_SRC_XY_Y(src_y)); - - mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_SIZE(pipe), - MDP5_PIPE_OUT_SIZE_WIDTH(crtc_w) | - MDP5_PIPE_OUT_SIZE_HEIGHT(crtc_h)); - - mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_XY(pipe), - MDP5_PIPE_OUT_XY_X(crtc_x) | - MDP5_PIPE_OUT_XY_Y(crtc_y)); - - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_FORMAT(pipe), - MDP5_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) | - MDP5_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) | - MDP5_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) | - MDP5_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) | - COND(format->alpha_enable, MDP5_PIPE_SRC_FORMAT_ALPHA_ENABLE) | - MDP5_PIPE_SRC_FORMAT_CPP(format->cpp - 1) | - MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) | - COND(format->unpack_tight, MDP5_PIPE_SRC_FORMAT_UNPACK_TIGHT) | - MDP5_PIPE_SRC_FORMAT_FETCH_TYPE(format->fetch_type) | - MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(format->chroma_sample)); - - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_UNPACK(pipe), - MDP5_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) | - MDP5_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) | - MDP5_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) | - MDP5_PIPE_SRC_UNPACK_ELEM3(format->unpack[3])); - - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_OP_MODE(pipe), - (hflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_LR : 0) | - (vflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_UD : 0) | - COND(pe, MDP5_PIPE_SRC_OP_MODE_SW_PIX_EXT_OVERRIDE) | - MDP5_PIPE_SRC_OP_MODE_BWC(BWC_LOSSLESS)); - - /* not using secure mode: */ - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(pipe), 0); - - if (hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT) - mdp5_write_pixel_ext(mdp5_kms, pipe, format, - src_w, pe_left, pe_right, - src_h, pe_top, pe_bottom); - - if (hwpipe->caps & MDP_PIPE_CAP_SCALE) { - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe), - phasex_step[COMP_0]); - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe), - phasey_step[COMP_0]); - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_X(pipe), - phasex_step[COMP_1_2]); - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_Y(pipe), - phasey_step[COMP_1_2]); - mdp5_write(mdp5_kms, REG_MDP5_PIPE_DECIMATION(pipe), - MDP5_PIPE_DECIMATION_VERT(vdecm) | - MDP5_PIPE_DECIMATION_HORZ(hdecm)); - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe), config); - } - - if (hwpipe->caps & MDP_PIPE_CAP_CSC) { - if (MDP_FORMAT_IS_YUV(format)) - csc_enable(mdp5_kms, pipe, - mdp_get_default_csc_cfg(CSC_YUV2RGB)); - else - csc_disable(mdp5_kms, pipe); - } - - set_scanout_locked(plane, fb); + mdp5_hwpipe_mode_set(mdp5_kms, hwpipe, fb, &step, &pe, + config, hdecm, vdecm, hflip, vflip, + crtc_x, crtc_y, crtc_w, crtc_h, + src_img_w, src_img_h, + src_x, src_y, src_w, src_h); spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags); + plane->fb = fb; + return ret; } From 7a10ee9b579e04241f981d8e237124aa8762c4c4 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:10 +0530 Subject: [PATCH 34/47] drm/msm/mdp5: Assign a 'right hwpipe' to plane state If the drm_plane has a source width that's greater than the max width supported by a SSPP (2560 pixels on 8x96), then we assign a 'r_hwpipe' to it in mdp5_plane_atomic_check(). TODO: There are a few scenarios where the hwpipe assignments aren't recommended by HW. For example, an assignment which results in a drm_plane to of two different types of hwpipes (say RGB0 on left and DMA1 on right) is not recommended. Also, hwpipes have a priority mapping, where the higher priority pipe needs to be staged on left LM, and the lower priority needs to be staged on the right LM. For example, the priority order for VIG pipes in decreasing order of priority is VIG0, VIG1, VIG2, and VIG3. So, VIG0 on left and VIG1 on right is a correct configuration, but VIG1 on left and VIG0 on right isn't. These scenarios are ignored for now for the sake of simplicity. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 1 + drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 57 ++++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index d68e92d71afef..5653917bcc9d1 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -104,6 +104,7 @@ struct mdp5_plane_state { struct drm_plane_state base; struct mdp5_hw_pipe *hwpipe; + struct mdp5_hw_pipe *r_hwpipe; /* right hwpipe */ /* aligned with property */ uint8_t premultiplied; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 2975974728023..caedb9faeae2e 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -177,9 +177,14 @@ mdp5_plane_atomic_print_state(struct drm_printer *p, const struct drm_plane_state *state) { struct mdp5_plane_state *pstate = to_mdp5_plane_state(state); + struct mdp5_kms *mdp5_kms = get_kms(state->plane); drm_printf(p, "\thwpipe=%s\n", pstate->hwpipe ? pstate->hwpipe->name : "(null)"); + if (mdp5_kms->caps & MDP_CAP_SRC_SPLIT) + drm_printf(p, "\tright-hwpipe=%s\n", + pstate->r_hwpipe ? pstate->r_hwpipe->name : + "(null)"); drm_printf(p, "\tpremultiplied=%u\n", pstate->premultiplied); drm_printf(p, "\tzpos=%u\n", pstate->zpos); drm_printf(p, "\talpha=%u\n", pstate->alpha); @@ -299,7 +304,9 @@ static int mdp5_plane_atomic_check_with_state(struct drm_crtc_state *crtc_state, struct drm_plane_state *old_state = plane->state; struct mdp5_cfg *config = mdp5_cfg_get_config(get_kms(plane)->cfg); bool new_hwpipe = false; + bool need_right_hwpipe = false; uint32_t max_width, max_height; + bool out_of_bounds = false; uint32_t caps = 0; struct drm_rect clip; int min_scale, max_scale; @@ -312,7 +319,23 @@ static int mdp5_plane_atomic_check_with_state(struct drm_crtc_state *crtc_state, max_height = config->hw->lm.max_height << 16; /* Make sure source dimensions are within bounds. */ - if ((state->src_w > max_width) || (state->src_h > max_height)) { + if (state->src_h > max_height) + out_of_bounds = true; + + if (state->src_w > max_width) { + /* If source split is supported, we can go up to 2x + * the max LM width, but we'd need to stage another + * hwpipe to the right LM. So, the drm_plane would + * consist of 2 hwpipes. + */ + if (config->hw->mdp.caps & MDP_CAP_SRC_SPLIT && + (state->src_w <= 2 * max_width)) + need_right_hwpipe = true; + else + out_of_bounds = true; + } + + if (out_of_bounds) { struct drm_rect src = drm_plane_state_src(state); DBG("Invalid source size "DRM_RECT_FP_FMT, DRM_RECT_FP_ARG(&src)); @@ -363,6 +386,15 @@ static int mdp5_plane_atomic_check_with_state(struct drm_crtc_state *crtc_state, if (!mdp5_state->hwpipe || (caps & ~mdp5_state->hwpipe->caps)) new_hwpipe = true; + /* + * (re)allocte hw pipe if we're either requesting for 2 hw pipes + * or we're switching from 2 hw pipes to 1 hw pipe because the + * new src_w can be supported by 1 hw pipe itself. + */ + if ((need_right_hwpipe && !mdp5_state->r_hwpipe) || + (!need_right_hwpipe && mdp5_state->r_hwpipe)) + new_hwpipe = true; + if (mdp5_kms->smp) { const struct mdp_format *format = to_mdp_format(msm_framebuffer_format(state->fb)); @@ -381,13 +413,36 @@ static int mdp5_plane_atomic_check_with_state(struct drm_crtc_state *crtc_state, * it available for other planes? */ struct mdp5_hw_pipe *old_hwpipe = mdp5_state->hwpipe; + struct mdp5_hw_pipe *old_right_hwpipe = + mdp5_state->r_hwpipe; + mdp5_state->hwpipe = mdp5_pipe_assign(state->state, plane, caps, blkcfg); if (IS_ERR(mdp5_state->hwpipe)) { DBG("%s: failed to assign hwpipe!", plane->name); return PTR_ERR(mdp5_state->hwpipe); } + + if (need_right_hwpipe) { + mdp5_state->r_hwpipe = + mdp5_pipe_assign(state->state, plane, + caps, blkcfg); + if (IS_ERR(mdp5_state->r_hwpipe)) { + DBG("%s: failed to assign right hwpipe", + plane->name); + return PTR_ERR(mdp5_state->r_hwpipe); + } + } else { + /* + * set it to NULL so that the driver knows we + * don't have a right hwpipe when committing a + * new state + */ + mdp5_state->r_hwpipe = NULL; + } + mdp5_pipe_release(state->state, old_hwpipe); + mdp5_pipe_release(state->state, old_right_hwpipe); } } From c26b4f6cfb2774d13f9ae41cbaaf30f30ac417c6 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:11 +0530 Subject: [PATCH 35/47] drm/msm/mdp5: Configure 'right' hwpipe Now that we have a right hwpipe in mdp5_plane_state, configure it mdp5_plane_mode_set(). The only parameters that vary between the left and right hwpipes are the src_w, src_img_w, src_x and crtc_x as we just even chop the fb into left and right halves. Add a mdp5_plane_right_pipe() which will be used by the crtc code to set up LM stages. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 1 + drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 46 ++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index 5653917bcc9d1..8bdb7ee4983bb 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -275,6 +275,7 @@ void mdp5_irq_domain_fini(struct mdp5_kms *mdp5_kms); uint32_t mdp5_plane_get_flush(struct drm_plane *plane); enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane); +enum mdp5_pipe mdp5_plane_right_pipe(struct drm_plane *plane); struct drm_plane *mdp5_plane_init(struct drm_device *dev, enum drm_plane_type type); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index caedb9faeae2e..a38c5fe6cc197 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -881,6 +881,7 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, struct mdp5_hw_pipe *hwpipe = to_mdp5_plane_state(pstate)->hwpipe; struct mdp5_kms *mdp5_kms = get_kms(plane); enum mdp5_pipe pipe = hwpipe->pipe; + struct mdp5_hw_pipe *right_hwpipe; const struct mdp_format *format; uint32_t nplanes, config = 0; struct phase_step step = { 0 }; @@ -894,6 +895,8 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, uint32_t src_x, src_y; uint32_t src_w, src_h; uint32_t src_img_w, src_img_h; + uint32_t src_x_r; + int crtc_x_r; unsigned long flags; int ret; @@ -929,6 +932,21 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, fb->base.id, src_x, src_y, src_w, src_h, crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h); + right_hwpipe = to_mdp5_plane_state(pstate)->r_hwpipe; + if (right_hwpipe) { + /* + * if the plane comprises of 2 hw pipes, assume that the width + * is split equally across them. The only parameters that varies + * between the 2 pipes are src_x and crtc_x + */ + crtc_w /= 2; + src_w /= 2; + src_img_w /= 2; + + crtc_x_r = crtc_x + crtc_w; + src_x_r = src_x + src_w; + } + ret = calc_scalex_steps(plane, pix_format, src_w, crtc_w, step.x); if (ret) return ret; @@ -965,6 +983,12 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, crtc_x, crtc_y, crtc_w, crtc_h, src_img_w, src_img_h, src_x, src_y, src_w, src_h); + if (right_hwpipe) + mdp5_hwpipe_mode_set(mdp5_kms, right_hwpipe, fb, &step, &pe, + config, hdecm, vdecm, hflip, vflip, + crtc_x_r, crtc_y, crtc_w, crtc_h, + src_img_w, src_img_h, + src_x_r, src_y, src_w, src_h); spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags); @@ -1051,6 +1075,10 @@ static int mdp5_update_cursor_plane_legacy(struct drm_plane *plane, src_x, src_y, src_w, src_h, ctx); } +/* + * Use this func and the one below only after the atomic state has been + * successfully swapped + */ enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane) { struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state); @@ -1061,14 +1089,30 @@ enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane) return pstate->hwpipe->pipe; } +enum mdp5_pipe mdp5_plane_right_pipe(struct drm_plane *plane) +{ + struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state); + + if (!pstate->r_hwpipe) + return SSPP_NONE; + + return pstate->r_hwpipe->pipe; +} + uint32_t mdp5_plane_get_flush(struct drm_plane *plane) { struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state); + u32 mask; if (WARN_ON(!pstate->hwpipe)) return 0; - return pstate->hwpipe->flush_mask; + mask = pstate->hwpipe->flush_mask; + + if (pstate->r_hwpipe) + mask |= pstate->r_hwpipe->flush_mask; + + return mask; } /* initialize plane */ From ed78560dc4ee352a6f991ef87b2747bdc90297a3 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:12 +0530 Subject: [PATCH 36/47] drm/msm/mdp5: Prepare Layer Mixers for source split In order to enable Source Split in HW, we need to add/modify a few LM register configurations: - Configure the LM width to be half the mode width, so that each LM manages one half of the scanout. - Tell the 'right' LM that it is configured to be the 'right' LM in source split mode. - Since we now have 2 places where REG_MDP5_LM_BLEND_COLOR_OUT is configured, do a read-update-store for the register instead of directly writing a value to it. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 39 +++++++++++++++++++----- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 82827f3e2a473..aa67b77eea4b1 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -225,6 +225,7 @@ static void blend_setup(struct drm_crtc *crtc) int i, plane_cnt = 0; bool bg_alpha_enabled = false; u32 mixer_op_mode = 0; + u32 val; #define blender(stage) ((stage) - STAGE0) hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); @@ -324,10 +325,14 @@ static void blend_setup(struct drm_crtc *crtc) } } - mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(lm), mixer_op_mode); - if (r_mixer) + val = mdp5_read(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(lm)); + mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(lm), + val | mixer_op_mode); + if (r_mixer) { + val = mdp5_read(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(r_lm)); mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(r_lm), - mixer_op_mode); + val | mixer_op_mode); + } mdp5_ctl_blend(ctl, pipeline, stage, r_stage, plane_cnt, ctl_blend_flags); @@ -343,6 +348,7 @@ static void mdp5_crtc_mode_set_nofb(struct drm_crtc *crtc) struct mdp5_hw_mixer *mixer = mdp5_cstate->pipeline.mixer; struct mdp5_hw_mixer *r_mixer = mdp5_cstate->pipeline.r_mixer; uint32_t lm = mixer->lm; + u32 mixer_width, val; unsigned long flags; struct drm_display_mode *mode; @@ -360,14 +366,33 @@ static void mdp5_crtc_mode_set_nofb(struct drm_crtc *crtc) mode->vsync_end, mode->vtotal, mode->type, mode->flags); + mixer_width = mode->hdisplay; + if (r_mixer) + mixer_width /= 2; + spin_lock_irqsave(&mdp5_crtc->lm_lock, flags); mdp5_write(mdp5_kms, REG_MDP5_LM_OUT_SIZE(lm), - MDP5_LM_OUT_SIZE_WIDTH(mode->hdisplay) | + MDP5_LM_OUT_SIZE_WIDTH(mixer_width) | MDP5_LM_OUT_SIZE_HEIGHT(mode->vdisplay)); - if (r_mixer) - mdp5_write(mdp5_kms, REG_MDP5_LM_OUT_SIZE(r_mixer->lm), - MDP5_LM_OUT_SIZE_WIDTH(mode->hdisplay) | + + /* Assign mixer to LEFT side in source split mode */ + val = mdp5_read(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(lm)); + val &= ~MDP5_LM_BLEND_COLOR_OUT_SPLIT_LEFT_RIGHT; + mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(lm), val); + + if (r_mixer) { + u32 r_lm = r_mixer->lm; + + mdp5_write(mdp5_kms, REG_MDP5_LM_OUT_SIZE(r_lm), + MDP5_LM_OUT_SIZE_WIDTH(mixer_width) | MDP5_LM_OUT_SIZE_HEIGHT(mode->vdisplay)); + + /* Assign mixer to RIGHT side in source split mode */ + val = mdp5_read(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(r_lm)); + val |= MDP5_LM_BLEND_COLOR_OUT_SPLIT_LEFT_RIGHT; + mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(r_lm), val); + } + spin_unlock_irqrestore(&mdp5_crtc->lm_lock, flags); } From bf8dc0a04e52ddccfaa47cf7a3238753344155fa Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:13 +0530 Subject: [PATCH 37/47] drm/msm/mdp5: Stage right side hwpipes on Right-side Layer Mixer Now that our mdp5_planes can consist of 2 hwpipes, update the blend_setup() code to stage the right hwpipe to the left and right LMs Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 12 ++++++++++++ drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c | 12 ++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index aa67b77eea4b1..1276f81b413b6 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -239,6 +239,8 @@ static void blend_setup(struct drm_crtc *crtc) /* Collect all plane information */ drm_atomic_crtc_for_each_plane(plane, crtc) { + enum mdp5_pipe right_pipe; + pstate = to_mdp5_plane_state(plane->state); pstates[pstate->stage] = pstate; stage[pstate->stage][PIPE_LEFT] = mdp5_plane_pipe(plane); @@ -249,6 +251,16 @@ static void blend_setup(struct drm_crtc *crtc) if (r_mixer) r_stage[pstate->stage][PIPE_LEFT] = mdp5_plane_pipe(plane); + /* + * if we have a right pipe (i.e, the plane comprises of 2 + * hwpipes, then stage the right pipe on the right side of both + * the layer mixers + */ + right_pipe = mdp5_plane_right_pipe(plane); + if (right_pipe) { + stage[pstate->stage][PIPE_RIGHT] = right_pipe; + r_stage[pstate->stage][PIPE_RIGHT] = right_pipe; + } plane_cnt++; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c index 1fdbb936877f5..15d78b2189359 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c @@ -380,14 +380,18 @@ int mdp5_ctl_blend(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, for (i = start_stage; stage_cnt && i <= STAGE_MAX; i++) { blend_cfg |= - mdp_ctl_blend_mask(stage[i][PIPE_LEFT], i); + mdp_ctl_blend_mask(stage[i][PIPE_LEFT], i) | + mdp_ctl_blend_mask(stage[i][PIPE_RIGHT], i); blend_ext_cfg |= - mdp_ctl_blend_ext_mask(stage[i][PIPE_LEFT], i); + mdp_ctl_blend_ext_mask(stage[i][PIPE_LEFT], i) | + mdp_ctl_blend_ext_mask(stage[i][PIPE_RIGHT], i); if (r_mixer) { r_blend_cfg |= - mdp_ctl_blend_mask(r_stage[i][PIPE_LEFT], i); + mdp_ctl_blend_mask(r_stage[i][PIPE_LEFT], i) | + mdp_ctl_blend_mask(r_stage[i][PIPE_RIGHT], i); r_blend_ext_cfg |= - mdp_ctl_blend_ext_mask(r_stage[i][PIPE_LEFT], i); + mdp_ctl_blend_ext_mask(r_stage[i][PIPE_LEFT], i) | + mdp_ctl_blend_ext_mask(r_stage[i][PIPE_RIGHT], i); } } From 359ae86248a5f92dd475e2d8012364a97e57026a Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:14 +0530 Subject: [PATCH 38/47] drm/msm/mdp5: Stage border out on base stage if CRTC has 2 LMs If a CRTC comprises of 2 LMs, it is mandatory to enable border out and assign it to the base stage. We had to enable border out also when the base plane wasn't fullscreen. Club these checks and put them in a separate function called get_start_stage() that returns the starting stage for assigning planes. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 45 ++++++++++++++++++------ 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 1276f81b413b6..708d748275cdf 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -512,6 +512,29 @@ static bool is_fullscreen(struct drm_crtc_state *cstate, ((pstate->crtc_y + pstate->crtc_h) >= cstate->mode.vdisplay); } +enum mdp_mixer_stage_id get_start_stage(struct drm_crtc *crtc, + struct drm_crtc_state *new_crtc_state, + struct drm_plane_state *bpstate) +{ + struct mdp5_crtc_state *mdp5_cstate = + to_mdp5_crtc_state(new_crtc_state); + + /* + * if we're in source split mode, it's mandatory to have + * border out on the base stage + */ + if (mdp5_cstate->pipeline.r_mixer) + return STAGE0; + + /* if the bottom-most layer is not fullscreen, we need to use + * it for solid-color: + */ + if (!is_fullscreen(new_crtc_state, bpstate)) + return STAGE0; + + return STAGE_BASE; +} + static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { @@ -522,8 +545,9 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, const struct mdp5_cfg_hw *hw_cfg; const struct drm_plane_state *pstate; bool cursor_plane = false; - int cnt = 0, base = 0, i; + int cnt = 0, i; int ret; + enum mdp_mixer_stage_id start; DBG("%s: check", crtc->name); @@ -537,6 +561,10 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, cursor_plane = true; } + /* bail out early if there aren't any planes */ + if (!cnt) + return 0; + ret = mdp5_crtc_setup_pipeline(crtc, state); if (ret) { dev_err(dev->dev, "couldn't assign mixers %d\n", ret); @@ -546,23 +574,20 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, /* assign a stage based on sorted zpos property */ sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL); - /* if the bottom-most layer is not fullscreen, we need to use - * it for solid-color: - */ - if ((cnt > 0) && !is_fullscreen(state, &pstates[0].state->base)) - base++; - /* trigger a warning if cursor isn't the highest zorder */ WARN_ON(cursor_plane && (pstates[cnt - 1].plane->type != DRM_PLANE_TYPE_CURSOR)); + start = get_start_stage(crtc, state, &pstates[0].state->base); + /* verify that there are not too many planes attached to crtc * and that we don't have conflicting mixer stages: */ hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); - if ((cnt + base) >= hw_cfg->lm.nb_stages) { - dev_err(dev->dev, "too many planes! cnt=%d, base=%d\n", cnt, base); + if ((cnt + start - 1) >= hw_cfg->lm.nb_stages) { + dev_err(dev->dev, "too many planes! cnt=%d, start stage=%d\n", + cnt, start); return -EINVAL; } @@ -570,7 +595,7 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, if (cursor_plane && (i == (cnt - 1))) pstates[i].state->stage = hw_cfg->lm.nb_stages; else - pstates[i].state->stage = STAGE_BASE + i + base; + pstates[i].state->stage = start + i; DBG("%s: assign pipe %s on stage=%d", crtc->name, pstates[i].plane->name, pstates[i].state->stage); From 8480adacfd58c79d884aa17af77a7fa7efae7257 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:15 +0530 Subject: [PATCH 39/47] drm/msm/mdp5: Assign 'right' mixer to CRTC state Dynamically assign a right mixer to mdp5_crtc_state in the CRTC's atomic_check path. Assigning the right mixer has some constraints, i.e, only a few LMs can be paired together. Update mdp5_mixer_assign to handle these constraints. Firstly, we need to identify whether we need a right mixer or not. At the moment, there are 2 scenarios where a right mixer might be needed: - If any of the planes connected to this CRTC is too wide (i.e, is comprised of 2 hwpipes). - If the CRTC's mode itself is too wide (i.e, a 4K mode on HDMI). We implement both these checks in the mdp5_crtc_atomic_check(), and pass 'need_right_mixer' to mdp5_setup_pipeline. If a CRTC is already assigned a single mixer, and a new atomic commit brings in a drm_plane that needs 2 hwpipes, we can successfully commit this mode without requiring a full modeset, provided that we still use the previously assigned mixer as the left mixer. If such an assignment isn't possible, we'd need to do a full modeset. This scenario has been ignored for now. The mixer assignment code is a bit messy, considering we have at most 4 LM instances in hardware. This can probably be re-visited later with simplified logic. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 49 ++++++++++-- drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c | 97 ++++++++++++++++++++--- drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h | 5 +- 3 files changed, 129 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 708d748275cdf..9217e0d6e93e0 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -449,7 +449,8 @@ static void mdp5_crtc_enable(struct drm_crtc *crtc) } int mdp5_crtc_setup_pipeline(struct drm_crtc *crtc, - struct drm_crtc_state *new_crtc_state) + struct drm_crtc_state *new_crtc_state, + bool need_right_mixer) { struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(new_crtc_state); @@ -459,15 +460,32 @@ int mdp5_crtc_setup_pipeline(struct drm_crtc *crtc, new_mixer = !pipeline->mixer; + if ((need_right_mixer && !pipeline->r_mixer) || + (!need_right_mixer && pipeline->r_mixer)) + new_mixer = true; + if (new_mixer) { struct mdp5_hw_mixer *old_mixer = pipeline->mixer; + struct mdp5_hw_mixer *old_r_mixer = pipeline->r_mixer; + u32 caps; + int ret; + + caps = MDP_LM_CAP_DISPLAY; + if (need_right_mixer) + caps |= MDP_LM_CAP_PAIR; - pipeline->mixer = mdp5_mixer_assign(new_crtc_state->state, crtc, - MDP_LM_CAP_DISPLAY); - if (IS_ERR(pipeline->mixer)) - return PTR_ERR(pipeline->mixer); + ret = mdp5_mixer_assign(new_crtc_state->state, crtc, caps, + &pipeline->mixer, need_right_mixer ? + &pipeline->r_mixer : NULL); + if (ret) + return ret; mdp5_mixer_release(new_crtc_state->state, old_mixer); + if (old_r_mixer) { + mdp5_mixer_release(new_crtc_state->state, old_r_mixer); + if (!need_right_mixer) + pipeline->r_mixer = NULL; + } } /* @@ -544,7 +562,9 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, struct plane_state pstates[STAGE_MAX + 1]; const struct mdp5_cfg_hw *hw_cfg; const struct drm_plane_state *pstate; + const struct drm_display_mode *mode = &state->adjusted_mode; bool cursor_plane = false; + bool need_right_mixer = false; int cnt = 0, i; int ret; enum mdp_mixer_stage_id start; @@ -555,6 +575,12 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, pstates[cnt].plane = plane; pstates[cnt].state = to_mdp5_plane_state(pstate); + /* + * if any plane on this crtc uses 2 hwpipes, then we need + * the crtc to have a right hwmixer. + */ + if (pstates[cnt].state->r_hwpipe) + need_right_mixer = true; cnt++; if (plane->type == DRM_PLANE_TYPE_CURSOR) @@ -565,7 +591,16 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, if (!cnt) return 0; - ret = mdp5_crtc_setup_pipeline(crtc, state); + hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); + + /* + * we need a right hwmixer if the mode's width is greater than a single + * LM's max width + */ + if (mode->hdisplay > hw_cfg->lm.max_width) + need_right_mixer = true; + + ret = mdp5_crtc_setup_pipeline(crtc, state, need_right_mixer); if (ret) { dev_err(dev->dev, "couldn't assign mixers %d\n", ret); return ret; @@ -583,8 +618,6 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, /* verify that there are not too many planes attached to crtc * and that we don't have conflicting mixer stages: */ - hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); - if ((cnt + start - 1) >= hw_cfg->lm.nb_stages) { dev_err(dev->dev, "too many planes! cnt=%d, start stage=%d\n", cnt, start); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c index 9bb1f824b2a95..8a00991f03c75 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.c @@ -16,42 +16,115 @@ #include "mdp5_kms.h" -struct mdp5_hw_mixer *mdp5_mixer_assign(struct drm_atomic_state *s, - struct drm_crtc *crtc, uint32_t caps) +/* + * As of now, there are only 2 combinations possible for source split: + * + * Left | Right + * -----|------ + * LM0 | LM1 + * LM2 | LM5 + * + */ +static int lm_right_pair[] = { 1, -1, 5, -1, -1, -1 }; + +static int get_right_pair_idx(struct mdp5_kms *mdp5_kms, int lm) +{ + int i; + int pair_lm; + + pair_lm = lm_right_pair[lm]; + if (pair_lm < 0) + return -EINVAL; + + for (i = 0; i < mdp5_kms->num_hwmixers; i++) { + struct mdp5_hw_mixer *mixer = mdp5_kms->hwmixers[i]; + + if (mixer->lm == pair_lm) + return mixer->idx; + } + + return -1; +} + +int mdp5_mixer_assign(struct drm_atomic_state *s, struct drm_crtc *crtc, + uint32_t caps, struct mdp5_hw_mixer **mixer, + struct mdp5_hw_mixer **r_mixer) { struct msm_drm_private *priv = s->dev->dev_private; struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms)); struct mdp5_state *state = mdp5_get_state(s); struct mdp5_hw_mixer_state *new_state; - struct mdp5_hw_mixer *mixer = NULL; int i; if (IS_ERR(state)) - return ERR_CAST(state); + return PTR_ERR(state); new_state = &state->hwmixer; for (i = 0; i < mdp5_kms->num_hwmixers; i++) { struct mdp5_hw_mixer *cur = mdp5_kms->hwmixers[i]; - /* skip if already in-use */ - if (new_state->hwmixer_to_crtc[cur->idx]) + /* + * skip if already in-use by a different CRTC. If there is a + * mixer already assigned to this CRTC, it means this call is + * a request to get an additional right mixer. Assume that the + * existing mixer is the 'left' one, and try to see if we can + * get its corresponding 'right' pair. + */ + if (new_state->hwmixer_to_crtc[cur->idx] && + new_state->hwmixer_to_crtc[cur->idx] != crtc) continue; /* skip if doesn't support some required caps: */ if (caps & ~cur->caps) continue; - if (!mixer) - mixer = cur; + if (r_mixer) { + int pair_idx; + + pair_idx = get_right_pair_idx(mdp5_kms, cur->lm); + if (pair_idx < 0) + return -EINVAL; + + if (new_state->hwmixer_to_crtc[pair_idx]) + continue; + + *r_mixer = mdp5_kms->hwmixers[pair_idx]; + } + + /* + * prefer a pair-able LM over an unpairable one. We can + * switch the CRTC from Normal mode to Source Split mode + * without requiring a full modeset if we had already + * assigned this CRTC a pair-able LM. + * + * TODO: There will be assignment sequences which would + * result in the CRTC requiring a full modeset, even + * if we have the LM resources to prevent it. For a platform + * with a few displays, we don't run out of pair-able LMs + * so easily. For now, ignore the possibility of requiring + * a full modeset. + */ + if (!(*mixer) || cur->caps & MDP_LM_CAP_PAIR) + *mixer = cur; } - if (!mixer) - return ERR_PTR(-ENOMEM); + if (!(*mixer)) + return -ENOMEM; - new_state->hwmixer_to_crtc[mixer->idx] = crtc; + if (r_mixer && !(*r_mixer)) + return -ENOMEM; - return mixer; + DBG("assigning Layer Mixer %d to crtc %s", (*mixer)->lm, crtc->name); + + new_state->hwmixer_to_crtc[(*mixer)->idx] = crtc; + if (r_mixer) { + DBG("assigning Right Layer Mixer %d to crtc %s", (*r_mixer)->lm, + crtc->name); + new_state->hwmixer_to_crtc[(*r_mixer)->idx] = crtc; + } + + return 0; } void mdp5_mixer_release(struct drm_atomic_state *s, struct mdp5_hw_mixer *mixer) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h index 18ed933ae574e..9be94f567fbdb 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mixer.h @@ -38,8 +38,9 @@ struct mdp5_hw_mixer_state { struct mdp5_hw_mixer *mdp5_mixer_init(const struct mdp5_lm_instance *lm); void mdp5_mixer_destroy(struct mdp5_hw_mixer *lm); -struct mdp5_hw_mixer *mdp5_mixer_assign(struct drm_atomic_state *s, - struct drm_crtc *crtc, uint32_t caps); +int mdp5_mixer_assign(struct drm_atomic_state *s, struct drm_crtc *crtc, + uint32_t caps, struct mdp5_hw_mixer **mixer, + struct mdp5_hw_mixer **r_mixer); void mdp5_mixer_release(struct drm_atomic_state *s, struct mdp5_hw_mixer *mixer); From 0d1d3e44653ff5e806b4007d0d952491d745cbbf Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:16 +0530 Subject: [PATCH 40/47] drm/msm/mdp5: Reset CTL blend registers before configuring them Assigning LMs dynamically to CRTCs results in REG_MDP5_CTL_LAYER_REGs and REG_MDP5_CTL_LAYER_EXT_REGs maintaining old values for a LM that isn't used by our CTL instance anymore. Clear the ctl's CTL_LAYER_REG and CTL_LAYER_EXT_REGs for all LM instances. The ones that need to be configured are configured later in this func. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c index 15d78b2189359..fddde84afdc39 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c @@ -355,6 +355,22 @@ static u32 mdp_ctl_blend_ext_mask(enum mdp5_pipe pipe, } } +static void mdp5_ctl_reset_blend_regs(struct mdp5_ctl *ctl) +{ + unsigned long flags; + struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm; + int i; + + spin_lock_irqsave(&ctl->hw_lock, flags); + + for (i = 0; i < ctl_mgr->nlm; i++) { + ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, i), 0x0); + ctl_write(ctl, REG_MDP5_CTL_LAYER_EXT_REG(ctl->id, i), 0x0); + } + + spin_unlock_irqrestore(&ctl->hw_lock, flags); +} + #define PIPE_LEFT 0 #define PIPE_RIGHT 1 int mdp5_ctl_blend(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, @@ -369,6 +385,8 @@ int mdp5_ctl_blend(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline, u32 r_blend_cfg = 0, r_blend_ext_cfg = 0; int i, start_stage; + mdp5_ctl_reset_blend_regs(ctl); + if (ctl_blend_op_flags & MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT) { start_stage = STAGE0; blend_cfg |= MDP5_CTL_LAYER_REG_BORDER_COLOR; From 3a882143794ab31aa25551ba90b2d0740c4973c4 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 23 Mar 2017 15:58:17 +0530 Subject: [PATCH 41/47] drm/msm/mdp5: Enable 3D mux in mdp5_ctl 3D mux is a small block placed after the DSPPs in MDP5. It can merge 2 LM/DSPP outputs and feed it to a single interface. Enable 3D Mux if our mdp5_pipeline has 2 active LMs. This check will need to be made more specific later when we add Dual DSI support with source split enabled. In that use case, each LM feeds to a separae INTF, so the 3D mux isn't needed. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c index fddde84afdc39..439e0a300e258 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c @@ -138,9 +138,10 @@ static void set_display_intf(struct mdp5_kms *mdp5_kms, spin_unlock_irqrestore(&mdp5_kms->resource_lock, flags); } -static void set_ctl_op(struct mdp5_ctl *ctl, struct mdp5_interface *intf) +static void set_ctl_op(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline) { unsigned long flags; + struct mdp5_interface *intf = pipeline->intf; u32 ctl_op = 0; if (!mdp5_cfg_intf_is_virtual(intf->type)) @@ -161,6 +162,10 @@ static void set_ctl_op(struct mdp5_ctl *ctl, struct mdp5_interface *intf) break; } + if (pipeline->r_mixer) + ctl_op |= MDP5_CTL_OP_PACK_3D_ENABLE | + MDP5_CTL_OP_PACK_3D(1); + spin_lock_irqsave(&ctl->hw_lock, flags); ctl_write(ctl, REG_MDP5_CTL_OP(ctl->id), ctl_op); spin_unlock_irqrestore(&ctl->hw_lock, flags); @@ -183,7 +188,7 @@ int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_pipeline *pipeline) if (!mdp5_cfg_intf_is_virtual(intf->type)) set_display_intf(mdp5_kms, intf); - set_ctl_op(ctl, intf); + set_ctl_op(ctl, pipeline); return 0; } From 9873ef0743535ee71efecfb5228f96432e393f8a Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Mon, 6 Feb 2017 10:39:29 -0700 Subject: [PATCH 42/47] drm/msm: Make sure to detach the MMU during GPU cleanup We should be detaching the MMU before destroying the address space. To do this cleanly, the detach has to happen in adreno_gpu_cleanup() because it needs access to structs in adreno_gpu.c. Plus it is better symmetry to have the attach and detach at the same code level. Signed-off-by: Jordan Crouse Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 29 ++++++++++++++++--------- drivers/gpu/drm/msm/msm_gpu.c | 3 --- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 05a3d1fd6d302..a50f9ba3bafa8 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -417,18 +417,27 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, return 0; } -void adreno_gpu_cleanup(struct adreno_gpu *gpu) +void adreno_gpu_cleanup(struct adreno_gpu *adreno_gpu) { - if (gpu->memptrs_bo) { - if (gpu->memptrs) - msm_gem_put_vaddr(gpu->memptrs_bo); + struct msm_gpu *gpu = &adreno_gpu->base; + + if (adreno_gpu->memptrs_bo) { + if (adreno_gpu->memptrs) + msm_gem_put_vaddr(adreno_gpu->memptrs_bo); + + if (adreno_gpu->memptrs_iova) + msm_gem_put_iova(adreno_gpu->memptrs_bo, gpu->id); + + drm_gem_object_unreference_unlocked(adreno_gpu->memptrs_bo); + } + release_firmware(adreno_gpu->pm4); + release_firmware(adreno_gpu->pfp); - if (gpu->memptrs_iova) - msm_gem_put_iova(gpu->memptrs_bo, gpu->base.id); + msm_gpu_cleanup(gpu); - drm_gem_object_unreference_unlocked(gpu->memptrs_bo); + if (gpu->aspace) { + gpu->aspace->mmu->funcs->detach(gpu->aspace->mmu, + iommu_ports, ARRAY_SIZE(iommu_ports)); + msm_gem_address_space_destroy(gpu->aspace); } - release_firmware(gpu->pm4); - release_firmware(gpu->pfp); - msm_gpu_cleanup(&gpu->base); } diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 41d2abc001ebf..937f1a0c5d3c2 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -661,9 +661,6 @@ void msm_gpu_cleanup(struct msm_gpu *gpu) msm_ringbuffer_destroy(gpu->rb); } - if (gpu->aspace) - msm_gem_address_space_destroy(gpu->aspace); - if (gpu->fctx) msm_fence_context_free(gpu->fctx); } From ee546cd34ae846a8202b78d1834170e2b3ee063d Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 7 Mar 2017 10:02:52 -0700 Subject: [PATCH 43/47] drm/msm: Reference count address spaces There are reasons for a memory object to outlive the file descriptor that created it and so the address space that a buffer object is attached to must also outlive the file descriptor. Reference count the address space so that it can remain viable until all the objects have released their addresses. Signed-off-by: Jordan Crouse Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 2 +- drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c | 2 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 2 +- drivers/gpu/drm/msm/msm_drv.h | 3 ++- drivers/gpu/drm/msm/msm_gem.h | 2 ++ drivers/gpu/drm/msm/msm_gem_vma.c | 35 ++++++++++++++++++------- 6 files changed, 33 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index a50f9ba3bafa8..9a92bcf982b85 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -438,6 +438,6 @@ void adreno_gpu_cleanup(struct adreno_gpu *adreno_gpu) if (gpu->aspace) { gpu->aspace->mmu->funcs->detach(gpu->aspace->mmu, iommu_ports, ARRAY_SIZE(iommu_ports)); - msm_gem_address_space_destroy(gpu->aspace); + msm_gem_address_space_put(gpu->aspace); } } diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index a4e1206a66a8d..6295204cabe99 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -169,7 +169,7 @@ static void mdp4_destroy(struct msm_kms *kms) if (aspace) { aspace->mmu->funcs->detach(aspace->mmu, iommu_ports, ARRAY_SIZE(iommu_ports)); - msm_gem_address_space_destroy(aspace); + msm_gem_address_space_put(aspace); } if (mdp4_kms->rpm_enabled) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index 24f76f48d56c1..d3d6b4cae1e6c 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -175,7 +175,7 @@ static void mdp5_kms_destroy(struct msm_kms *kms) if (aspace) { aspace->mmu->funcs->detach(aspace->mmu, iommu_ports, ARRAY_SIZE(iommu_ports)); - msm_gem_address_space_destroy(aspace); + msm_gem_address_space_put(aspace); } } diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index b885c3d5ae4df..28b6f9ba50664 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -191,7 +191,8 @@ void msm_gem_unmap_vma(struct msm_gem_address_space *aspace, int msm_gem_map_vma(struct msm_gem_address_space *aspace, struct msm_gem_vma *vma, struct sg_table *sgt, int npages); -void msm_gem_address_space_destroy(struct msm_gem_address_space *aspace); +void msm_gem_address_space_put(struct msm_gem_address_space *aspace); + struct msm_gem_address_space * msm_gem_address_space_create(struct device *dev, struct iommu_domain *domain, const char *name); diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index 7d529516b3327..1b4cf20043ea3 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -18,6 +18,7 @@ #ifndef __MSM_GEM_H__ #define __MSM_GEM_H__ +#include #include #include "msm_drv.h" @@ -31,6 +32,7 @@ struct msm_gem_address_space { */ struct drm_mm mm; struct msm_mmu *mmu; + struct kref kref; }; struct msm_gem_vma { diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index b654eca7636aa..f285d7e210db4 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -19,6 +19,25 @@ #include "msm_gem.h" #include "msm_mmu.h" +static void +msm_gem_address_space_destroy(struct kref *kref) +{ + struct msm_gem_address_space *aspace = container_of(kref, + struct msm_gem_address_space, kref); + + drm_mm_takedown(&aspace->mm); + if (aspace->mmu) + aspace->mmu->funcs->destroy(aspace->mmu); + kfree(aspace); +} + + +void msm_gem_address_space_put(struct msm_gem_address_space *aspace) +{ + if (aspace) + kref_put(&aspace->kref, msm_gem_address_space_destroy); +} + void msm_gem_unmap_vma(struct msm_gem_address_space *aspace, struct msm_gem_vma *vma, struct sg_table *sgt) @@ -34,6 +53,8 @@ msm_gem_unmap_vma(struct msm_gem_address_space *aspace, drm_mm_remove_node(&vma->node); vma->iova = 0; + + msm_gem_address_space_put(aspace); } int @@ -57,16 +78,10 @@ msm_gem_map_vma(struct msm_gem_address_space *aspace, size, IOMMU_READ | IOMMU_WRITE); } - return ret; -} + /* Get a reference to the aspace to keep it around */ + kref_get(&aspace->kref); -void -msm_gem_address_space_destroy(struct msm_gem_address_space *aspace) -{ - drm_mm_takedown(&aspace->mm); - if (aspace->mmu) - aspace->mmu->funcs->destroy(aspace->mmu); - kfree(aspace); + return ret; } struct msm_gem_address_space * @@ -85,5 +100,7 @@ msm_gem_address_space_create(struct device *dev, struct iommu_domain *domain, drm_mm_init(&aspace->mm, (domain->geometry.aperture_start >> PAGE_SHIFT), (domain->geometry.aperture_end >> PAGE_SHIFT) - 1); + kref_init(&aspace->kref); + return aspace; } From e3689e470fa0d9ebaa9d13d069e8d73c8d82a11d Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 7 Mar 2017 10:02:53 -0700 Subject: [PATCH 44/47] drm/msm: Add MSM_PARAM_GMEM_BASE User space needs to know where the GMEM whole starts so that they can set up the addressing correctly. Signed-off-by: Jordan Crouse Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 3 +++ include/uapi/drm/msm_drm.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 9a92bcf982b85..4cac22633ce4e 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -35,6 +35,9 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value) case MSM_PARAM_GMEM_SIZE: *value = adreno_gpu->gmem; return 0; + case MSM_PARAM_GMEM_BASE: + *value = 0x100000; + return 0; case MSM_PARAM_CHIP_ID: *value = adreno_gpu->rev.patchid | (adreno_gpu->rev.minor << 8) | diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index 4d5d6a2bc59e8..a4a189a240d71 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -72,6 +72,7 @@ struct drm_msm_timespec { #define MSM_PARAM_CHIP_ID 0x03 #define MSM_PARAM_MAX_FREQ 0x04 #define MSM_PARAM_TIMESTAMP 0x05 +#define MSM_PARAM_GMEM_BASE 0x06 struct drm_msm_param { __u32 pipe; /* in, MSM_PIPE_x */ From bf5af4ae875d8803db98d38ed988c2ec4c941a00 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 7 Mar 2017 10:02:54 -0700 Subject: [PATCH 45/47] drm/msm: Hard code the GPU "slow frequency" Some A3XX and A4XX GPU targets required that the GPU clock be programmed to a non zero value when it was disabled so 27Mhz was chosen as the "invalid" frequency. Even though newer targets do not have the same clock restrictions we still write 27Mhz on clock disable and expect the clock subsystem to round down to zero. For unknown reasons even though the slow clock speed is always 27Mhz and it isn't actually a functional level the legacy device tree frequency tables always defined it and then did gymnastics to work around it. Instead of playing the same silly games just hard code the "slow" clock speed in the code as 27MHz and save ourselves a bit of infrastructure. Signed-off-by: Jordan Crouse Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/adreno_device.c | 5 +---- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 5 ++--- drivers/gpu/drm/msm/adreno/adreno_gpu.h | 2 +- drivers/gpu/drm/msm/msm_gpu.c | 8 ++++++-- drivers/gpu/drm/msm/msm_gpu.h | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c index bc170b7d5546c..9cae68bed0d6b 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_device.c +++ b/drivers/gpu/drm/msm/adreno/adreno_device.c @@ -2,7 +2,7 @@ * Copyright (C) 2013-2014 Red Hat * Author: Rob Clark * - * Copyright (c) 2014 The Linux Foundation. All rights reserved. + * Copyright (c) 2014,2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by @@ -231,7 +231,6 @@ static int adreno_bind(struct device *dev, struct device *master, void *data) /* find clock rates: */ config.fast_rate = 0; - config.slow_rate = ~0; for_each_child_of_node(node, child) { if (of_device_is_compatible(child, "qcom,gpu-pwrlevels")) { struct device_node *pwrlvl; @@ -242,7 +241,6 @@ static int adreno_bind(struct device *dev, struct device *master, void *data) return ret; } config.fast_rate = max(config.fast_rate, val); - config.slow_rate = min(config.slow_rate, val); } } } @@ -251,7 +249,6 @@ static int adreno_bind(struct device *dev, struct device *master, void *data) dev_warn(dev, "could not find clk rates\n"); /* This is a safe low speed for all devices: */ config.fast_rate = 200000000; - config.slow_rate = 27000000; } dev->platform_data = &config; diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 4cac22633ce4e..5b63fc649dcc5 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -352,14 +352,13 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, adreno_gpu->rev = config->rev; gpu->fast_rate = config->fast_rate; - gpu->slow_rate = config->slow_rate; gpu->bus_freq = config->bus_freq; #ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING gpu->bus_scale_table = config->bus_scale_table; #endif - DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u", - gpu->fast_rate, gpu->slow_rate, gpu->bus_freq); + DBG("fast_rate=%u, slow_rate=27000000, bus_freq=%u", + gpu->fast_rate, gpu->bus_freq); ret = msm_gpu_init(drm, pdev, &adreno_gpu->base, &funcs->base, adreno_gpu->info->name, "kgsl_3d0_reg_memory", "kgsl_3d0_irq", diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index 42e444a67630c..fb4831f9f80bb 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -123,7 +123,7 @@ struct adreno_gpu { /* platform config data (ie. from DT, or pdata) */ struct adreno_platform_config { struct adreno_rev rev; - uint32_t fast_rate, slow_rate, bus_freq; + uint32_t fast_rate, bus_freq; #ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING struct msm_bus_scale_pdata *bus_scale_table; #endif diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 937f1a0c5d3c2..f8ee1489e691a 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -123,8 +123,12 @@ static int disable_clk(struct msm_gpu *gpu) if (gpu->grp_clks[i]) clk_unprepare(gpu->grp_clks[i]); - if (gpu->grp_clks[0] && gpu->slow_rate) - clk_set_rate(gpu->grp_clks[0], gpu->slow_rate); + /* + * Set the clock to a deliberately low rate. On older targets the clock + * speed had to be non zero to avoid problems. On newer targets this + * will be rounded down to zero anyway so it all works out. + */ + clk_set_rate(gpu->grp_clks[0], 27000000); if (gpu->grp_clks[2]) clk_set_rate(gpu->grp_clks[2], 0); diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 72dba973aabf4..44f0c34ee5e4c 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -104,7 +104,7 @@ struct msm_gpu { /* Power Control: */ struct regulator *gpu_reg, *gpu_cx; struct clk *ebi1_clk, *grp_clks[6]; - uint32_t fast_rate, slow_rate, bus_freq; + uint32_t fast_rate, bus_freq; #ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING struct msm_bus_scale_pdata *bus_scale_table; From e2af8b6b0ca1f55e9b2c8a034c352c56ae054066 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 7 Mar 2017 10:02:55 -0700 Subject: [PATCH 46/47] drm/msm: gpu: Use OPP tables if we can If a OPP table is defined for the GPU device in the device tree use that in lieu of the downstream style GPU frequency table. If we do use the downstream table convert it to a OPP table so that we can take advantage of the OPP lookup facilities later. Signed-off-by: Jordan Crouse Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/adreno_device.c | 85 +++++++++++++++++----- 1 file changed, 66 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c index 9cae68bed0d6b..c0fa5d1c75ffc 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_device.c +++ b/drivers/gpu/drm/msm/adreno/adreno_device.c @@ -17,6 +17,7 @@ * this program. If not, see . */ +#include #include "adreno_gpu.h" #define ANY_ID 0xff @@ -213,10 +214,71 @@ static int find_chipid(struct device *dev, u32 *chipid) return 0; } +/* Get legacy powerlevels from qcom,gpu-pwrlevels and populate the opp table */ +static int adreno_get_legacy_pwrlevels(struct device *dev) +{ + struct device_node *child, *node; + int ret; + + node = of_find_compatible_node(dev->of_node, NULL, + "qcom,gpu-pwrlevels"); + if (!node) { + dev_err(dev, "Could not find the GPU powerlevels\n"); + return -ENXIO; + } + + for_each_child_of_node(node, child) { + unsigned int val; + + ret = of_property_read_u32(child, "qcom,gpu-freq", &val); + if (ret) + continue; + + /* + * Skip the intentionally bogus clock value found at the bottom + * of most legacy frequency tables + */ + if (val != 27000000) + dev_pm_opp_add(dev, val, 0); + } + + return 0; +} + +static int adreno_get_pwrlevels(struct device *dev, + struct adreno_platform_config *config) +{ + unsigned long freq = ULONG_MAX; + struct dev_pm_opp *opp; + int ret; + + /* You down with OPP? */ + if (!of_find_property(dev->of_node, "operating-points-v2", NULL)) + ret = adreno_get_legacy_pwrlevels(dev); + else + ret = dev_pm_opp_of_add_table(dev); + + if (ret) + return ret; + + /* Find the fastest defined rate */ + opp = dev_pm_opp_find_freq_floor(dev, &freq); + if (!IS_ERR(opp)) + config->fast_rate = dev_pm_opp_get_freq(opp); + + if (!config->fast_rate) { + DRM_DEV_INFO(dev, + "Could not find clock rate. Using default\n"); + /* Pick a suitably safe clock speed for any target */ + config->fast_rate = 200000000; + } + + return 0; +} + static int adreno_bind(struct device *dev, struct device *master, void *data) { static struct adreno_platform_config config = {}; - struct device_node *child, *node = dev->of_node; u32 val; int ret; @@ -231,25 +293,10 @@ static int adreno_bind(struct device *dev, struct device *master, void *data) /* find clock rates: */ config.fast_rate = 0; - for_each_child_of_node(node, child) { - if (of_device_is_compatible(child, "qcom,gpu-pwrlevels")) { - struct device_node *pwrlvl; - for_each_child_of_node(child, pwrlvl) { - ret = of_property_read_u32(pwrlvl, "qcom,gpu-freq", &val); - if (ret) { - dev_err(dev, "could not find gpu-freq: %d\n", ret); - return ret; - } - config.fast_rate = max(config.fast_rate, val); - } - } - } - if (!config.fast_rate) { - dev_warn(dev, "could not find clk rates\n"); - /* This is a safe low speed for all devices: */ - config.fast_rate = 200000000; - } + ret = adreno_get_pwrlevels(dev, &config); + if (ret) + return ret; dev->platform_data = &config; set_gpu_pdev(dev_get_drvdata(master), to_platform_device(dev)); From 98db803f6413e6d4bf1f590ea57e9e7dfe1eb32b Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 7 Mar 2017 10:02:56 -0700 Subject: [PATCH 47/47] msm/drm: gpu: Dynamically locate the clocks from the device tree Instead of using a fixed list of clock names use the clock-names list in the device tree to discover and get the list of clocks that we need. Signed-off-by: Jordan Crouse Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_gpu.c | 78 ++++++++++++++++++++++++----------- drivers/gpu/drm/msm/msm_gpu.h | 4 +- 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index f8ee1489e691a..97b9c38c6b3ff 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -93,18 +93,18 @@ static int enable_clk(struct msm_gpu *gpu) { int i; - if (gpu->grp_clks[0] && gpu->fast_rate) - clk_set_rate(gpu->grp_clks[0], gpu->fast_rate); + if (gpu->core_clk && gpu->fast_rate) + clk_set_rate(gpu->core_clk, gpu->fast_rate); /* Set the RBBM timer rate to 19.2Mhz */ - if (gpu->grp_clks[2]) - clk_set_rate(gpu->grp_clks[2], 19200000); + if (gpu->rbbmtimer_clk) + clk_set_rate(gpu->rbbmtimer_clk, 19200000); - for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i >= 0; i--) + for (i = gpu->nr_clocks - 1; i >= 0; i--) if (gpu->grp_clks[i]) clk_prepare(gpu->grp_clks[i]); - for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i >= 0; i--) + for (i = gpu->nr_clocks - 1; i >= 0; i--) if (gpu->grp_clks[i]) clk_enable(gpu->grp_clks[i]); @@ -115,11 +115,11 @@ static int disable_clk(struct msm_gpu *gpu) { int i; - for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i >= 0; i--) + for (i = gpu->nr_clocks - 1; i >= 0; i--) if (gpu->grp_clks[i]) clk_disable(gpu->grp_clks[i]); - for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i >= 0; i--) + for (i = gpu->nr_clocks - 1; i >= 0; i--) if (gpu->grp_clks[i]) clk_unprepare(gpu->grp_clks[i]); @@ -128,10 +128,11 @@ static int disable_clk(struct msm_gpu *gpu) * speed had to be non zero to avoid problems. On newer targets this * will be rounded down to zero anyway so it all works out. */ - clk_set_rate(gpu->grp_clks[0], 27000000); + if (gpu->core_clk) + clk_set_rate(gpu->core_clk, 27000000); - if (gpu->grp_clks[2]) - clk_set_rate(gpu->grp_clks[2], 0); + if (gpu->rbbmtimer_clk) + clk_set_rate(gpu->rbbmtimer_clk, 0); return 0; } @@ -519,16 +520,52 @@ static irqreturn_t irq_handler(int irq, void *data) return gpu->funcs->irq(gpu); } -static const char *clk_names[] = { - "core", "iface", "rbbmtimer", "mem", "mem_iface", "alt_mem_iface", -}; +static struct clk *get_clock(struct device *dev, const char *name) +{ + struct clk *clk = devm_clk_get(dev, name); + + return IS_ERR(clk) ? NULL : clk; +} + +static int get_clocks(struct platform_device *pdev, struct msm_gpu *gpu) +{ + struct device *dev = &pdev->dev; + struct property *prop; + const char *name; + int i = 0; + + gpu->nr_clocks = of_property_count_strings(dev->of_node, "clock-names"); + if (gpu->nr_clocks < 1) { + gpu->nr_clocks = 0; + return 0; + } + + gpu->grp_clks = devm_kcalloc(dev, sizeof(struct clk *), gpu->nr_clocks, + GFP_KERNEL); + if (!gpu->grp_clks) + return -ENOMEM; + + of_property_for_each_string(dev->of_node, "clock-names", prop, name) { + gpu->grp_clks[i] = get_clock(dev, name); + + /* Remember the key clocks that we need to control later */ + if (!strcmp(name, "core")) + gpu->core_clk = gpu->grp_clks[i]; + else if (!strcmp(name, "rbbmtimer")) + gpu->rbbmtimer_clk = gpu->grp_clks[i]; + + ++i; + } + + return 0; +} int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs, const char *name, const char *ioname, const char *irqname, int ringsz) { struct iommu_domain *iommu; - int i, ret; + int ret; if (WARN_ON(gpu->num_perfcntrs > ARRAY_SIZE(gpu->last_cntrs))) gpu->num_perfcntrs = ARRAY_SIZE(gpu->last_cntrs); @@ -554,7 +591,6 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, spin_lock_init(&gpu->perf_lock); - BUG_ON(ARRAY_SIZE(clk_names) != ARRAY_SIZE(gpu->grp_clks)); /* Map registers: */ gpu->mmio = msm_ioremap(pdev, ioname, name); @@ -578,13 +614,9 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, goto fail; } - /* Acquire clocks: */ - for (i = 0; i < ARRAY_SIZE(clk_names); i++) { - gpu->grp_clks[i] = msm_clk_get(pdev, clk_names[i]); - DBG("grp_clks[%s]: %p", clk_names[i], gpu->grp_clks[i]); - if (IS_ERR(gpu->grp_clks[i])) - gpu->grp_clks[i] = NULL; - } + ret = get_clocks(pdev, gpu); + if (ret) + goto fail; gpu->ebi1_clk = msm_clk_get(pdev, "bus"); DBG("ebi1_clk: %p", gpu->ebi1_clk); diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 44f0c34ee5e4c..aa3241000455a 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -103,7 +103,9 @@ struct msm_gpu { /* Power Control: */ struct regulator *gpu_reg, *gpu_cx; - struct clk *ebi1_clk, *grp_clks[6]; + struct clk **grp_clks; + int nr_clocks; + struct clk *ebi1_clk, *core_clk, *rbbmtimer_clk; uint32_t fast_rate, bus_freq; #ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING