From 96354b5ca4ac74d2e47e75987283963001b517f6 Mon Sep 17 00:00:00 2001 From: Philip Yang Date: Wed, 17 Apr 2019 22:15:19 +0000 Subject: drm: increase drm mmap_range size to 1TB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After patch "drm: Use the same mmap-range offset and size for GEM and TTM", application failed to create bo of system memory because drm mmap_range size decrease to 64GB from original 1TB. This is not big enough for applications. Increase the drm mmap_range size to 1TB. Reviewed-by: Thomas Zimmermann Reviewed-by: Christian König Signed-off-by: Philip Yang Signed-off-by: Alex Deucher Link: https://patchwork.freedesktop.org/patch/msgid/20190417221507.933-1-Philip.Yang@amd.com --- include/drm/drm_vma_manager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/drm') diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h index f4f8ff1cdeec..76ac5e97a559 100644 --- a/include/drm/drm_vma_manager.h +++ b/include/drm/drm_vma_manager.h @@ -35,7 +35,7 @@ */ #if BITS_PER_LONG == 64 #define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1) -#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16) +#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 256) #else #define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFUL >> PAGE_SHIFT) + 1) #define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFUL >> PAGE_SHIFT) * 16) -- cgit v1.2.3 From 7d26097b4beb802fb6ce7cd6389ac4bf12b1a6ae Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Fri, 1 Mar 2019 13:56:12 +0100 Subject: drm/atomic: Create __drm_atomic_helper_crtc_reset() for subclassing crtc_state. We already have __drm_atomic_helper_connector_reset() and __drm_atomic_helper_plane_reset(), extend this to crtc as well. This will allow us to set default values in the crtc_state, without having to do it in each driver separately. Of all drivers that need conversion, only nouveau is done in this commit, because it wrote its own __drm_atomic_helper_crtc_reset(), clashing with the drm core. Signed-off-by: Maarten Lankhorst Cc: Ben Skeggs Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190301125627.7285-3-maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/drm_atomic_state_helper.c | 34 +++++++++++++++++++++++++------ drivers/gpu/drm/nouveau/dispnv50/head.c | 13 +++--------- include/drm/drm_atomic_state_helper.h | 2 ++ 3 files changed, 33 insertions(+), 16 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 59ffb6b9c745..06a97a268e81 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -56,6 +56,29 @@ * for these functions. */ +/** + * __drm_atomic_helper_crtc_reset - reset state on CRTC + * @crtc: drm CRTC + * @crtc_state: CRTC state to assign + * + * Initializes the newly allocated @crtc_state and assigns it to + * the &drm_crtc->state pointer of @crtc, usually required when + * initializing the drivers or when called from the &drm_crtc_funcs.reset + * hook. + * + * This is useful for drivers that subclass the CRTC state. + */ +void +__drm_atomic_helper_crtc_reset(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state) +{ + if (crtc_state) + crtc_state->crtc = crtc; + + crtc->state = crtc_state; +} +EXPORT_SYMBOL(__drm_atomic_helper_crtc_reset); + /** * drm_atomic_helper_crtc_reset - default &drm_crtc_funcs.reset hook for CRTCs * @crtc: drm CRTC @@ -65,14 +88,13 @@ */ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) { - if (crtc->state) - __drm_atomic_helper_crtc_destroy_state(crtc->state); - - kfree(crtc->state); - crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL); + struct drm_crtc_state *crtc_state = + kzalloc(sizeof(*crtc->state), GFP_KERNEL); if (crtc->state) - crtc->state->crtc = crtc; + crtc->funcs->atomic_destroy_state(crtc, crtc->state); + + __drm_atomic_helper_crtc_reset(crtc, crtc_state); } EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.c b/drivers/gpu/drm/nouveau/dispnv50/head.c index 2e7a0c347ddb..93754743090f 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head.c +++ b/drivers/gpu/drm/nouveau/dispnv50/head.c @@ -419,16 +419,6 @@ nv50_head_atomic_duplicate_state(struct drm_crtc *crtc) return &asyh->state; } -static void -__drm_atomic_helper_crtc_reset(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - if (crtc->state) - crtc->funcs->atomic_destroy_state(crtc, crtc->state); - crtc->state = state; - crtc->state->crtc = crtc; -} - static void nv50_head_reset(struct drm_crtc *crtc) { @@ -437,6 +427,9 @@ nv50_head_reset(struct drm_crtc *crtc) if (WARN_ON(!(asyh = kzalloc(sizeof(*asyh), GFP_KERNEL)))) return; + if (crtc->state) + nv50_head_atomic_destroy_state(crtc, crtc->state); + __drm_atomic_helper_crtc_reset(crtc, &asyh->state); } diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index 66c92cbd8e16..4e6d2e7a40b8 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -37,6 +37,8 @@ struct drm_private_state; struct drm_modeset_acquire_ctx; struct drm_device; +void __drm_atomic_helper_crtc_reset(struct drm_crtc *crtc, + struct drm_crtc_state *state); void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc); void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, struct drm_crtc_state *state); -- cgit v1.2.3 From 5918045c4ed492fb5813f980dcf89a90fefd0a4e Mon Sep 17 00:00:00 2001 From: Christian König Date: Thu, 18 Apr 2019 11:00:21 -0400 Subject: drm/scheduler: rework job destruction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now destroy finished jobs from the worker thread to make sure that we never destroy a job currently in timeout processing. By this we avoid holding lock around ring mirror list in drm_sched_stop which should solve a deadlock reported by a user. v2: Remove unused variable. v4: Move guilty job free into sched code. v5: Move sched->hw_rq_count to drm_sched_start to account for counter decrement in drm_sched_stop even when we don't call resubmit jobs if guily job did signal. v6: remove unused variable Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=109692 Acked-by: Chunming Zhou Signed-off-by: Christian König Signed-off-by: Andrey Grodzovsky Signed-off-by: Alex Deucher Link: https://patchwork.freedesktop.org/patch/msgid/1555599624-12285-3-git-send-email-andrey.grodzovsky@amd.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 9 +- drivers/gpu/drm/etnaviv/etnaviv_dump.c | 5 - drivers/gpu/drm/etnaviv/etnaviv_sched.c | 2 +- drivers/gpu/drm/lima/lima_sched.c | 2 +- drivers/gpu/drm/panfrost/panfrost_job.c | 2 +- drivers/gpu/drm/scheduler/sched_main.c | 159 +++++++++++++++++------------ drivers/gpu/drm/v3d/v3d_sched.c | 2 +- include/drm/gpu_scheduler.h | 6 +- 8 files changed, 102 insertions(+), 85 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 7cee269ec3e3..a0e165c91a78 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -3334,7 +3334,7 @@ static int amdgpu_device_pre_asic_reset(struct amdgpu_device *adev, if (!ring || !ring->sched.thread) continue; - drm_sched_stop(&ring->sched); + drm_sched_stop(&ring->sched, &job->base); /* after all hw jobs are reset, hw fence is meaningless, so force_completion */ amdgpu_fence_driver_force_completion(ring); @@ -3343,8 +3343,6 @@ static int amdgpu_device_pre_asic_reset(struct amdgpu_device *adev, if(job) drm_sched_increase_karma(&job->base); - - if (!amdgpu_sriov_vf(adev)) { if (!need_full_reset) @@ -3482,8 +3480,7 @@ end: return r; } -static void amdgpu_device_post_asic_reset(struct amdgpu_device *adev, - struct amdgpu_job *job) +static void amdgpu_device_post_asic_reset(struct amdgpu_device *adev) { int i; @@ -3623,7 +3620,7 @@ retry: /* Rest of adevs pre asic reset from XGMI hive. */ /* Post ASIC reset for all devs .*/ list_for_each_entry(tmp_adev, device_list_handle, gmc.xgmi.head) { - amdgpu_device_post_asic_reset(tmp_adev, tmp_adev == adev ? job : NULL); + amdgpu_device_post_asic_reset(tmp_adev); if (r) { /* bad news, how to tell it to userspace ? */ diff --git a/drivers/gpu/drm/etnaviv/etnaviv_dump.c b/drivers/gpu/drm/etnaviv/etnaviv_dump.c index 33854c94cb85..b24ddc406bba 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_dump.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_dump.c @@ -118,7 +118,6 @@ void etnaviv_core_dump(struct etnaviv_gpu *gpu) unsigned int n_obj, n_bomap_pages; size_t file_size, mmu_size; __le64 *bomap, *bomap_start; - unsigned long flags; /* Only catch the first event, or when manually re-armed */ if (!etnaviv_dump_core) @@ -135,13 +134,11 @@ void etnaviv_core_dump(struct etnaviv_gpu *gpu) mmu_size + gpu->buffer.size; /* Add in the active command buffers */ - spin_lock_irqsave(&gpu->sched.job_list_lock, flags); list_for_each_entry(s_job, &gpu->sched.ring_mirror_list, node) { submit = to_etnaviv_submit(s_job); file_size += submit->cmdbuf.size; n_obj++; } - spin_unlock_irqrestore(&gpu->sched.job_list_lock, flags); /* Add in the active buffer objects */ list_for_each_entry(vram, &gpu->mmu->mappings, mmu_node) { @@ -183,14 +180,12 @@ void etnaviv_core_dump(struct etnaviv_gpu *gpu) gpu->buffer.size, etnaviv_cmdbuf_get_va(&gpu->buffer)); - spin_lock_irqsave(&gpu->sched.job_list_lock, flags); list_for_each_entry(s_job, &gpu->sched.ring_mirror_list, node) { submit = to_etnaviv_submit(s_job); etnaviv_core_dump_mem(&iter, ETDUMP_BUF_CMD, submit->cmdbuf.vaddr, submit->cmdbuf.size, etnaviv_cmdbuf_get_va(&submit->cmdbuf)); } - spin_unlock_irqrestore(&gpu->sched.job_list_lock, flags); /* Reserve space for the bomap */ if (n_bomap_pages) { diff --git a/drivers/gpu/drm/etnaviv/etnaviv_sched.c b/drivers/gpu/drm/etnaviv/etnaviv_sched.c index 6d24fea1766b..a813c824e154 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_sched.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_sched.c @@ -109,7 +109,7 @@ static void etnaviv_sched_timedout_job(struct drm_sched_job *sched_job) } /* block scheduler */ - drm_sched_stop(&gpu->sched); + drm_sched_stop(&gpu->sched, sched_job); if(sched_job) drm_sched_increase_karma(sched_job); diff --git a/drivers/gpu/drm/lima/lima_sched.c b/drivers/gpu/drm/lima/lima_sched.c index d53bd45f8d96..58a15479d175 100644 --- a/drivers/gpu/drm/lima/lima_sched.c +++ b/drivers/gpu/drm/lima/lima_sched.c @@ -258,7 +258,7 @@ static struct dma_fence *lima_sched_run_job(struct drm_sched_job *job) static void lima_sched_handle_error_task(struct lima_sched_pipe *pipe, struct lima_sched_task *task) { - drm_sched_stop(&pipe->base); + drm_sched_stop(&pipe->base, &task->base); if (task) drm_sched_increase_karma(&task->base); diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c index a5716c8fe8b3..9bb9260d9181 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.c +++ b/drivers/gpu/drm/panfrost/panfrost_job.c @@ -387,7 +387,7 @@ static void panfrost_job_timedout(struct drm_sched_job *sched_job) mutex_lock(&pfdev->reset_lock); for (i = 0; i < NUM_JOB_SLOTS; i++) - drm_sched_stop(&pfdev->js->queue[i].sched); + drm_sched_stop(&pfdev->js->queue[i].sched, sched_job); if (sched_job) drm_sched_increase_karma(sched_job); diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 19fc601c9eeb..7816de7e8c82 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -265,32 +265,6 @@ void drm_sched_resume_timeout(struct drm_gpu_scheduler *sched, } EXPORT_SYMBOL(drm_sched_resume_timeout); -/* job_finish is called after hw fence signaled - */ -static void drm_sched_job_finish(struct work_struct *work) -{ - struct drm_sched_job *s_job = container_of(work, struct drm_sched_job, - finish_work); - struct drm_gpu_scheduler *sched = s_job->sched; - unsigned long flags; - - /* - * Canceling the timeout without removing our job from the ring mirror - * list is safe, as we will only end up in this worker if our jobs - * finished fence has been signaled. So even if some another worker - * manages to find this job as the next job in the list, the fence - * signaled check below will prevent the timeout to be restarted. - */ - cancel_delayed_work_sync(&sched->work_tdr); - - spin_lock_irqsave(&sched->job_list_lock, flags); - /* queue TDR for next job */ - drm_sched_start_timeout(sched); - spin_unlock_irqrestore(&sched->job_list_lock, flags); - - sched->ops->free_job(s_job); -} - static void drm_sched_job_begin(struct drm_sched_job *s_job) { struct drm_gpu_scheduler *sched = s_job->sched; @@ -315,6 +289,13 @@ static void drm_sched_job_timedout(struct work_struct *work) if (job) job->sched->ops->timedout_job(job); + /* + * Guilty job did complete and hence needs to be manually removed + * See drm_sched_stop doc. + */ + if (list_empty(&job->node)) + job->sched->ops->free_job(job); + spin_lock_irqsave(&sched->job_list_lock, flags); drm_sched_start_timeout(sched); spin_unlock_irqrestore(&sched->job_list_lock, flags); @@ -371,23 +352,26 @@ EXPORT_SYMBOL(drm_sched_increase_karma); * @sched: scheduler instance * @bad: bad scheduler job * + * Stop the scheduler and also removes and frees all completed jobs. + * Note: bad job will not be freed as it might be used later and so it's + * callers responsibility to release it manually if it's not part of the + * mirror list any more. + * */ -void drm_sched_stop(struct drm_gpu_scheduler *sched) +void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad) { - struct drm_sched_job *s_job; + struct drm_sched_job *s_job, *tmp; unsigned long flags; - struct dma_fence *last_fence = NULL; kthread_park(sched->thread); /* - * Verify all the signaled jobs in mirror list are removed from the ring - * by waiting for the latest job to enter the list. This should insure that - * also all the previous jobs that were in flight also already singaled - * and removed from the list. + * Iterate the job list from later to earlier one and either deactive + * their HW callbacks or remove them from mirror list if they already + * signaled. + * This iteration is thread safe as sched thread is stopped. */ - spin_lock_irqsave(&sched->job_list_lock, flags); - list_for_each_entry_reverse(s_job, &sched->ring_mirror_list, node) { + list_for_each_entry_safe_reverse(s_job, tmp, &sched->ring_mirror_list, node) { if (s_job->s_fence->parent && dma_fence_remove_callback(s_job->s_fence->parent, &s_job->cb)) { @@ -395,16 +379,30 @@ void drm_sched_stop(struct drm_gpu_scheduler *sched) s_job->s_fence->parent = NULL; atomic_dec(&sched->hw_rq_count); } else { - last_fence = dma_fence_get(&s_job->s_fence->finished); - break; + /* + * remove job from ring_mirror_list. + * Locking here is for concurrent resume timeout + */ + spin_lock_irqsave(&sched->job_list_lock, flags); + list_del_init(&s_job->node); + spin_unlock_irqrestore(&sched->job_list_lock, flags); + + /* + * Wait for job's HW fence callback to finish using s_job + * before releasing it. + * + * Job is still alive so fence refcount at least 1 + */ + dma_fence_wait(&s_job->s_fence->finished, false); + + /* + * We must keep bad job alive for later use during + * recovery by some of the drivers + */ + if (bad != s_job) + sched->ops->free_job(s_job); } } - spin_unlock_irqrestore(&sched->job_list_lock, flags); - - if (last_fence) { - dma_fence_wait(last_fence, false); - dma_fence_put(last_fence); - } } EXPORT_SYMBOL(drm_sched_stop); @@ -418,21 +416,22 @@ EXPORT_SYMBOL(drm_sched_stop); void drm_sched_start(struct drm_gpu_scheduler *sched, bool full_recovery) { struct drm_sched_job *s_job, *tmp; + unsigned long flags; int r; - if (!full_recovery) - goto unpark; - /* * Locking the list is not required here as the sched thread is parked - * so no new jobs are being pushed in to HW and in drm_sched_stop we - * flushed all the jobs who were still in mirror list but who already - * signaled and removed them self from the list. Also concurrent + * so no new jobs are being inserted or removed. Also concurrent * GPU recovers can't run in parallel. */ list_for_each_entry_safe(s_job, tmp, &sched->ring_mirror_list, node) { struct dma_fence *fence = s_job->s_fence->parent; + atomic_inc(&sched->hw_rq_count); + + if (!full_recovery) + continue; + if (fence) { r = dma_fence_add_callback(fence, &s_job->cb, drm_sched_process_job); @@ -445,9 +444,12 @@ void drm_sched_start(struct drm_gpu_scheduler *sched, bool full_recovery) drm_sched_process_job(NULL, &s_job->cb); } - drm_sched_start_timeout(sched); + if (full_recovery) { + spin_lock_irqsave(&sched->job_list_lock, flags); + drm_sched_start_timeout(sched); + spin_unlock_irqrestore(&sched->job_list_lock, flags); + } -unpark: kthread_unpark(sched->thread); } EXPORT_SYMBOL(drm_sched_start); @@ -464,7 +466,6 @@ void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched) uint64_t guilty_context; bool found_guilty = false; - /*TODO DO we need spinlock here ? */ list_for_each_entry_safe(s_job, tmp, &sched->ring_mirror_list, node) { struct drm_sched_fence *s_fence = s_job->s_fence; @@ -477,7 +478,6 @@ void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched) dma_fence_set_error(&s_fence->finished, -ECANCELED); s_job->s_fence->parent = sched->ops->run_job(s_job); - atomic_inc(&sched->hw_rq_count); } } EXPORT_SYMBOL(drm_sched_resubmit_jobs); @@ -514,7 +514,6 @@ int drm_sched_job_init(struct drm_sched_job *job, return -ENOMEM; job->id = atomic64_inc_return(&sched->job_id_count); - INIT_WORK(&job->finish_work, drm_sched_job_finish); INIT_LIST_HEAD(&job->node); return 0; @@ -597,24 +596,53 @@ static void drm_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb) struct drm_sched_job *s_job = container_of(cb, struct drm_sched_job, cb); struct drm_sched_fence *s_fence = s_job->s_fence; struct drm_gpu_scheduler *sched = s_fence->sched; - unsigned long flags; - - cancel_delayed_work(&sched->work_tdr); atomic_dec(&sched->hw_rq_count); atomic_dec(&sched->num_jobs); - spin_lock_irqsave(&sched->job_list_lock, flags); - /* remove job from ring_mirror_list */ - list_del_init(&s_job->node); - spin_unlock_irqrestore(&sched->job_list_lock, flags); + trace_drm_sched_process_job(s_fence); drm_sched_fence_finished(s_fence); - - trace_drm_sched_process_job(s_fence); wake_up_interruptible(&sched->wake_up_worker); +} + +/** + * drm_sched_cleanup_jobs - destroy finished jobs + * + * @sched: scheduler instance + * + * Remove all finished jobs from the mirror list and destroy them. + */ +static void drm_sched_cleanup_jobs(struct drm_gpu_scheduler *sched) +{ + unsigned long flags; + + /* Don't destroy jobs while the timeout worker is running */ + if (!cancel_delayed_work(&sched->work_tdr)) + return; + + + while (!list_empty(&sched->ring_mirror_list)) { + struct drm_sched_job *job; + + job = list_first_entry(&sched->ring_mirror_list, + struct drm_sched_job, node); + if (!dma_fence_is_signaled(&job->s_fence->finished)) + break; + + spin_lock_irqsave(&sched->job_list_lock, flags); + /* remove job from ring_mirror_list */ + list_del_init(&job->node); + spin_unlock_irqrestore(&sched->job_list_lock, flags); + + sched->ops->free_job(job); + } + + /* queue timeout for next job */ + spin_lock_irqsave(&sched->job_list_lock, flags); + drm_sched_start_timeout(sched); + spin_unlock_irqrestore(&sched->job_list_lock, flags); - schedule_work(&s_job->finish_work); } /** @@ -656,9 +684,10 @@ static int drm_sched_main(void *param) struct dma_fence *fence; wait_event_interruptible(sched->wake_up_worker, + (drm_sched_cleanup_jobs(sched), (!drm_sched_blocked(sched) && (entity = drm_sched_select_entity(sched))) || - kthread_should_stop()); + kthread_should_stop())); if (!entity) continue; diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index b4255807b3a7..8c2df6d95283 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -268,7 +268,7 @@ v3d_gpu_reset_for_timeout(struct v3d_dev *v3d, struct drm_sched_job *sched_job) /* block scheduler */ for (q = 0; q < V3D_MAX_QUEUES; q++) - drm_sched_stop(&v3d->queue[q].sched); + drm_sched_stop(&v3d->queue[q].sched, sched_job); if (sched_job) drm_sched_increase_karma(sched_job); diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index 0daca4d8dad9..9ee0f2735d71 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -167,9 +167,6 @@ struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f); * @sched: the scheduler instance on which this job is scheduled. * @s_fence: contains the fences for the scheduling of job. * @finish_cb: the callback for the finished fence. - * @finish_work: schedules the function @drm_sched_job_finish once the job has - * finished to remove the job from the - * @drm_gpu_scheduler.ring_mirror_list. * @node: used to append this struct to the @drm_gpu_scheduler.ring_mirror_list. * @id: a unique id assigned to each job scheduled on the scheduler. * @karma: increment on every hang caused by this job. If this exceeds the hang @@ -188,7 +185,6 @@ struct drm_sched_job { struct drm_gpu_scheduler *sched; struct drm_sched_fence *s_fence; struct dma_fence_cb finish_cb; - struct work_struct finish_work; struct list_head node; uint64_t id; atomic_t karma; @@ -296,7 +292,7 @@ int drm_sched_job_init(struct drm_sched_job *job, void *owner); void drm_sched_job_cleanup(struct drm_sched_job *job); void drm_sched_wakeup(struct drm_gpu_scheduler *sched); -void drm_sched_stop(struct drm_gpu_scheduler *sched); +void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad); void drm_sched_start(struct drm_gpu_scheduler *sched, bool full_recovery); void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched); void drm_sched_increase_karma(struct drm_sched_job *bad); -- cgit v1.2.3 From a5343b8a2ca5799ee6370e3cca77369a4c598221 Mon Sep 17 00:00:00 2001 From: Andrey Grodzovsky Date: Thu, 18 Apr 2019 11:00:23 -0400 Subject: drm/scheduler: Add flag to hint the release of guilty job. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Sched thread's cleanup function races against TO handler and removes the guilty job from mirror list and we have no way of differentiating if the job was removed from within the TO handler or from the sched thread's clean-up function. Fix: Add a flag to scheduler to hint the TO handler that the guilty job needs to be explicitly released. v2: whitespace fix Reviewed-by: Christian König Signed-off-by: Andrey Grodzovsky Signed-off-by: Alex Deucher Link: https://patchwork.freedesktop.org/patch/msgid/1555599624-12285-5-git-send-email-andrey.grodzovsky@amd.com --- drivers/gpu/drm/scheduler/sched_main.c | 9 +++++++-- include/drm/gpu_scheduler.h | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 03e6bd8a1a42..f8f0e1c19002 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -293,8 +293,10 @@ static void drm_sched_job_timedout(struct work_struct *work) * Guilty job did complete and hence needs to be manually removed * See drm_sched_stop doc. */ - if (list_empty(&job->node)) + if (sched->free_guilty) { job->sched->ops->free_job(job); + sched->free_guilty = false; + } spin_lock_irqsave(&sched->job_list_lock, flags); drm_sched_start_timeout(sched); @@ -395,10 +397,13 @@ void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad) /* * We must keep bad job alive for later use during - * recovery by some of the drivers + * recovery by some of the drivers but leave a hint + * that the guilty job must be released. */ if (bad != s_job) sched->ops->free_job(s_job); + else + sched->free_guilty = true; } } diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index 9ee0f2735d71..57b4121c750a 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -259,6 +259,7 @@ struct drm_sched_backend_ops { * guilty and it will be considered for scheduling further. * @num_jobs: the number of jobs in queue in the scheduler * @ready: marks if the underlying HW is ready to work + * @free_guilty: A hit to time out handler to free the guilty job. * * One scheduler is implemented for each hardware ring. */ @@ -279,6 +280,7 @@ struct drm_gpu_scheduler { int hang_limit; atomic_t num_jobs; bool ready; + bool free_guilty; }; int drm_sched_init(struct drm_gpu_scheduler *sched, -- cgit v1.2.3 From 27edadf6df811bcad0f7fa2f89321b4590a425bc Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 6 May 2019 16:46:29 +0200 Subject: drm/doc: Improve docs for conn_state->best_encoder It's mandatory and considered core state since ioctls rely on this working. Thanks to Laurent for pointing out this gap. v2: Clarify to "atomic drivers" only. Cc: Laurent Pinchart Cc: Sean Paul Acked-by: Sean Paul Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190506144629.5976-1-daniel.vetter@ffwll.ch --- include/drm/drm_connector.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/drm') diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 02a131202add..f43f40d5888a 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -517,6 +517,10 @@ struct drm_connector_state { * Used by the atomic helpers to select the encoder, through the * &drm_connector_helper_funcs.atomic_best_encoder or * &drm_connector_helper_funcs.best_encoder callbacks. + * + * NOTE: Atomic drivers must fill this out (either themselves or through + * helpers), for otherwise the GETCONNECTOR and GETENCODER IOCTLs will + * not return correct data to userspace. */ struct drm_encoder *best_encoder; -- cgit v1.2.3 From 585b000de23ba06818975734da2303bd6ba2dcdd Mon Sep 17 00:00:00 2001 From: Ramalingam C Date: Tue, 7 May 2019 21:57:35 +0530 Subject: drm: move content protection property to mode_config Content protection property is created once and stored in drm_mode_config. And attached to all HDCP capable connectors. Signed-off-by: Ramalingam C Reviewed-by: Daniel Vetter Acked-by: Dave Airlie Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190507162745.25600-2-ramalingam.c@intel.com --- drivers/gpu/drm/drm_atomic_uapi.c | 4 ++-- drivers/gpu/drm/drm_connector.c | 13 +++++++------ include/drm/drm_connector.h | 6 ------ include/drm/drm_mode_config.h | 6 ++++++ 4 files changed, 15 insertions(+), 14 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 428d82662dc4..4131e669785a 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -732,7 +732,7 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, state->content_type = val; } else if (property == connector->scaling_mode_property) { state->scaling_mode = val; - } else if (property == connector->content_protection_property) { + } else if (property == config->content_protection_property) { if (val == DRM_MODE_CONTENT_PROTECTION_ENABLED) { DRM_DEBUG_KMS("only drivers can set CP Enabled\n"); return -EINVAL; @@ -814,7 +814,7 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = state->colorspace; } else if (property == connector->scaling_mode_property) { *val = state->scaling_mode; - } else if (property == connector->content_protection_property) { + } else if (property == config->content_protection_property) { *val = state->content_protection; } else if (property == config->writeback_fb_id_property) { /* Writeback framebuffer is one-shot, write and forget */ diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 2355124849db..7c0eda9cca60 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1534,18 +1534,19 @@ int drm_connector_attach_content_protection_property( struct drm_connector *connector) { struct drm_device *dev = connector->dev; - struct drm_property *prop; + struct drm_property *prop = + dev->mode_config.content_protection_property; - prop = drm_property_create_enum(dev, 0, "Content Protection", - drm_cp_enum_list, - ARRAY_SIZE(drm_cp_enum_list)); + if (!prop) + prop = drm_property_create_enum(dev, 0, "Content Protection", + drm_cp_enum_list, + ARRAY_SIZE(drm_cp_enum_list)); if (!prop) return -ENOMEM; drm_object_attach_property(&connector->base, prop, DRM_MODE_CONTENT_PROTECTION_UNDESIRED); - - connector->content_protection_property = prop; + dev->mode_config.content_protection_property = prop; return 0; } diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 02a131202add..5e41942e5679 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1061,12 +1061,6 @@ struct drm_connector { */ struct drm_property *vrr_capable_property; - /** - * @content_protection_property: DRM ENUM property for content - * protection. See drm_connector_attach_content_protection_property(). - */ - struct drm_property *content_protection_property; - /** * @colorspace_property: Connector property to set the suitable * colorspace supported by the sink. diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 7f60e8eb269a..5764ee3c7453 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -836,6 +836,12 @@ struct drm_mode_config { */ struct drm_property *writeback_out_fence_ptr_property; + /** + * @content_protection_property: DRM ENUM property for content + * protection. See drm_connector_attach_content_protection_property(). + */ + struct drm_property *content_protection_property; + /* dumb ioctl parameters */ uint32_t preferred_depth, prefer_shadow; -- cgit v1.2.3 From 0de655cae416b20ed7876adb480e5c65810274ea Mon Sep 17 00:00:00 2001 From: Ramalingam C Date: Tue, 7 May 2019 21:57:37 +0530 Subject: drm: generic fn converting be24 to cpu and vice versa Existing functions for converting a 3bytes(be24) of big endian value into u32 of little endian and vice versa are renamed as s/drm_hdcp2_seq_num_to_u32/drm_hdcp_be24_to_cpu s/drm_hdcp2_u32_to_seq_num/drm_hdcp_cpu_to_be24 Signed-off-by: Ramalingam C Suggested-by: Daniel Vetter cc: Tomas Winkler Acked-by: Dave Airlie Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190507162745.25600-4-ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_hdcp.c | 5 +++-- drivers/misc/mei/hdcp/mei_hdcp.c | 2 +- include/drm/drm_hdcp.h | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index b8c8d6d1a33d..c308dfee9ca4 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -1305,7 +1305,7 @@ int hdcp2_propagate_stream_management_info(struct intel_connector *connector) /* Prepare RepeaterAuth_Stream_Manage msg */ msgs.stream_manage.msg_id = HDCP_2_2_REP_STREAM_MANAGE; - drm_hdcp2_u32_to_seq_num(msgs.stream_manage.seq_num_m, hdcp->seq_num_m); + drm_hdcp_cpu_to_be24(msgs.stream_manage.seq_num_m, hdcp->seq_num_m); /* K no of streams is fixed as 1. Stored as big-endian. */ msgs.stream_manage.k = cpu_to_be16(1); @@ -1370,7 +1370,8 @@ int hdcp2_authenticate_repeater_topology(struct intel_connector *connector) } /* Converting and Storing the seq_num_v to local variable as DWORD */ - seq_num_v = drm_hdcp2_seq_num_to_u32(msgs.recvid_list.seq_num_v); + seq_num_v = + drm_hdcp_be24_to_cpu((const u8 *)msgs.recvid_list.seq_num_v); if (seq_num_v < hdcp->seq_num_v) { /* Roll over of the seq_num_v from repeater. Reauthenticate. */ diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 90b6ae8e9dae..2f192d6d8b54 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -576,7 +576,7 @@ static int mei_hdcp_verify_mprime(struct device *dev, memcpy(verify_mprime_in.m_prime, stream_ready->m_prime, HDCP_2_2_MPRIME_LEN); - drm_hdcp2_u32_to_seq_num(verify_mprime_in.seq_num_m, data->seq_num_m); + drm_hdcp_cpu_to_be24(verify_mprime_in.seq_num_m, data->seq_num_m); memcpy(verify_mprime_in.streams, data->streams, (data->k * sizeof(struct hdcp2_streamid_type))); diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h index f243408ecf26..1cc66df05a43 100644 --- a/include/drm/drm_hdcp.h +++ b/include/drm/drm_hdcp.h @@ -252,13 +252,13 @@ struct hdcp2_rep_stream_ready { * host format and back */ static inline -u32 drm_hdcp2_seq_num_to_u32(u8 seq_num[HDCP_2_2_SEQ_NUM_LEN]) +u32 drm_hdcp_be24_to_cpu(const u8 seq_num[HDCP_2_2_SEQ_NUM_LEN]) { return (u32)(seq_num[2] | seq_num[1] << 8 | seq_num[0] << 16); } static inline -void drm_hdcp2_u32_to_seq_num(u8 seq_num[HDCP_2_2_SEQ_NUM_LEN], u32 val) +void drm_hdcp_cpu_to_be24(u8 seq_num[HDCP_2_2_SEQ_NUM_LEN], u32 val) { seq_num[0] = val >> 16; seq_num[1] = val >> 8; -- cgit v1.2.3 From 6498bf5800a302ef69e7f4914e727893f278bb2f Mon Sep 17 00:00:00 2001 From: Ramalingam C Date: Tue, 7 May 2019 21:57:38 +0530 Subject: drm: revocation check at drm subsystem On every hdcp revocation check request SRM is read from fw file /lib/firmware/display_hdcp_srm.bin SRM table is parsed and stored at drm_hdcp.c, with functions exported for the services for revocation check from drivers (which implements the HDCP authentication) This patch handles the HDCP1.4 and 2.2 versions of SRM table. v2: moved the uAPI to request_firmware_direct() [Daniel] v3: kdoc added. [Daniel] srm_header unified and bit field definitions are removed. [Daniel] locking improved. [Daniel] vrl length violation is fixed. [Daniel] v4: s/__swab16/be16_to_cpu [Daniel] be24_to_cpu is done through a global func [Daniel] Unused variables are removed. [Daniel] unchecked return values are dropped from static funcs [Daniel] Signed-off-by: Ramalingam C Acked-by: Satyeshwar Singh Reviewed-by: Daniel Vetter Acked-by: Dave Airlie Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190507162745.25600-5-ramalingam.c@intel.com --- Documentation/gpu/drm-kms-helpers.rst | 6 + drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_hdcp.c | 333 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_internal.h | 4 + drivers/gpu/drm/drm_sysfs.c | 2 + include/drm/drm_hdcp.h | 24 +++ 6 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/drm_hdcp.c (limited to 'include/drm') diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 14102ae035dc..0fe726a6ee67 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -181,6 +181,12 @@ Panel Helper Reference .. kernel-doc:: drivers/gpu/drm/drm_panel_orientation_quirks.c :export: +HDCP Helper Functions Reference +=============================== + +.. kernel-doc:: drivers/gpu/drm/drm_hdcp.c + :export: + Display Port Helper Functions Reference ======================================= diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 72f5036d9bfa..dd02e9dec810 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -17,7 +17,7 @@ drm-y := drm_auth.o drm_cache.o \ drm_plane.o drm_color_mgmt.o drm_print.o \ drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \ - drm_atomic_uapi.o + drm_atomic_uapi.o drm_hdcp.o drm-$(CONFIG_DRM_LEGACY) += drm_legacy_misc.o drm_bufs.o drm_context.o drm_dma.o drm_scatter.o drm_lock.o drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o diff --git a/drivers/gpu/drm/drm_hdcp.c b/drivers/gpu/drm/drm_hdcp.c new file mode 100644 index 000000000000..5e5409505c31 --- /dev/null +++ b/drivers/gpu/drm/drm_hdcp.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Intel Corporation. + * + * Authors: + * Ramalingam C + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct hdcp_srm { + u32 revoked_ksv_cnt; + u8 *revoked_ksv_list; + + /* Mutex to protect above struct member */ + struct mutex mutex; +} *srm_data; + +static inline void drm_hdcp_print_ksv(const u8 *ksv) +{ + DRM_DEBUG("\t%#02x, %#02x, %#02x, %#02x, %#02x\n", + ksv[0], ksv[1], ksv[2], ksv[3], ksv[4]); +} + +static u32 drm_hdcp_get_revoked_ksv_count(const u8 *buf, u32 vrls_length) +{ + u32 parsed_bytes = 0, ksv_count = 0, vrl_ksv_cnt, vrl_sz; + + while (parsed_bytes < vrls_length) { + vrl_ksv_cnt = *buf; + ksv_count += vrl_ksv_cnt; + + vrl_sz = (vrl_ksv_cnt * DRM_HDCP_KSV_LEN) + 1; + buf += vrl_sz; + parsed_bytes += vrl_sz; + } + + /* + * When vrls are not valid, ksvs are not considered. + * Hence SRM will be discarded. + */ + if (parsed_bytes != vrls_length) + ksv_count = 0; + + return ksv_count; +} + +static u32 drm_hdcp_get_revoked_ksvs(const u8 *buf, u8 *revoked_ksv_list, + u32 vrls_length) +{ + u32 parsed_bytes = 0, ksv_count = 0; + u32 vrl_ksv_cnt, vrl_ksv_sz, vrl_idx = 0; + + do { + vrl_ksv_cnt = *buf; + vrl_ksv_sz = vrl_ksv_cnt * DRM_HDCP_KSV_LEN; + + buf++; + + DRM_DEBUG("vrl: %d, Revoked KSVs: %d\n", vrl_idx++, + vrl_ksv_cnt); + memcpy(revoked_ksv_list, buf, vrl_ksv_sz); + + ksv_count += vrl_ksv_cnt; + revoked_ksv_list += vrl_ksv_sz; + buf += vrl_ksv_sz; + + parsed_bytes += (vrl_ksv_sz + 1); + } while (parsed_bytes < vrls_length); + + return ksv_count; +} + +static inline u32 get_vrl_length(const u8 *buf) +{ + return drm_hdcp_be24_to_cpu(buf); +} + +static int drm_hdcp_parse_hdcp1_srm(const u8 *buf, size_t count) +{ + struct hdcp_srm_header *header; + u32 vrl_length, ksv_count; + + if (count < (sizeof(struct hdcp_srm_header) + + DRM_HDCP_1_4_VRL_LENGTH_SIZE + DRM_HDCP_1_4_DCP_SIG_SIZE)) { + DRM_ERROR("Invalid blob length\n"); + return -EINVAL; + } + + header = (struct hdcp_srm_header *)buf; + DRM_DEBUG("SRM ID: 0x%x, SRM Ver: 0x%x, SRM Gen No: 0x%x\n", + header->srm_id, + be16_to_cpu(header->srm_version), header->srm_gen_no); + + WARN_ON(header->reserved); + + buf = buf + sizeof(*header); + vrl_length = get_vrl_length(buf); + if (count < (sizeof(struct hdcp_srm_header) + vrl_length) || + vrl_length < (DRM_HDCP_1_4_VRL_LENGTH_SIZE + + DRM_HDCP_1_4_DCP_SIG_SIZE)) { + DRM_ERROR("Invalid blob length or vrl length\n"); + return -EINVAL; + } + + /* Length of the all vrls combined */ + vrl_length -= (DRM_HDCP_1_4_VRL_LENGTH_SIZE + + DRM_HDCP_1_4_DCP_SIG_SIZE); + + if (!vrl_length) { + DRM_ERROR("No vrl found\n"); + return -EINVAL; + } + + buf += DRM_HDCP_1_4_VRL_LENGTH_SIZE; + ksv_count = drm_hdcp_get_revoked_ksv_count(buf, vrl_length); + if (!ksv_count) { + DRM_DEBUG("Revoked KSV count is 0\n"); + return count; + } + + kfree(srm_data->revoked_ksv_list); + srm_data->revoked_ksv_list = kcalloc(ksv_count, DRM_HDCP_KSV_LEN, + GFP_KERNEL); + if (!srm_data->revoked_ksv_list) { + DRM_ERROR("Out of Memory\n"); + return -ENOMEM; + } + + if (drm_hdcp_get_revoked_ksvs(buf, srm_data->revoked_ksv_list, + vrl_length) != ksv_count) { + srm_data->revoked_ksv_cnt = 0; + kfree(srm_data->revoked_ksv_list); + return -EINVAL; + } + + srm_data->revoked_ksv_cnt = ksv_count; + return count; +} + +static int drm_hdcp_parse_hdcp2_srm(const u8 *buf, size_t count) +{ + struct hdcp_srm_header *header; + u32 vrl_length, ksv_count, ksv_sz; + + if (count < (sizeof(struct hdcp_srm_header) + + DRM_HDCP_2_VRL_LENGTH_SIZE + DRM_HDCP_2_DCP_SIG_SIZE)) { + DRM_ERROR("Invalid blob length\n"); + return -EINVAL; + } + + header = (struct hdcp_srm_header *)buf; + DRM_DEBUG("SRM ID: 0x%x, SRM Ver: 0x%x, SRM Gen No: 0x%x\n", + header->srm_id & DRM_HDCP_SRM_ID_MASK, + be16_to_cpu(header->srm_version), header->srm_gen_no); + + if (header->reserved) + return -EINVAL; + + buf = buf + sizeof(*header); + vrl_length = get_vrl_length(buf); + + if (count < (sizeof(struct hdcp_srm_header) + vrl_length) || + vrl_length < (DRM_HDCP_2_VRL_LENGTH_SIZE + + DRM_HDCP_2_DCP_SIG_SIZE)) { + DRM_ERROR("Invalid blob length or vrl length\n"); + return -EINVAL; + } + + /* Length of the all vrls combined */ + vrl_length -= (DRM_HDCP_2_VRL_LENGTH_SIZE + + DRM_HDCP_2_DCP_SIG_SIZE); + + if (!vrl_length) { + DRM_ERROR("No vrl found\n"); + return -EINVAL; + } + + buf += DRM_HDCP_2_VRL_LENGTH_SIZE; + ksv_count = (*buf << 2) | DRM_HDCP_2_KSV_COUNT_2_LSBITS(*(buf + 1)); + if (!ksv_count) { + DRM_DEBUG("Revoked KSV count is 0\n"); + return count; + } + + kfree(srm_data->revoked_ksv_list); + srm_data->revoked_ksv_list = kcalloc(ksv_count, DRM_HDCP_KSV_LEN, + GFP_KERNEL); + if (!srm_data->revoked_ksv_list) { + DRM_ERROR("Out of Memory\n"); + return -ENOMEM; + } + + ksv_sz = ksv_count * DRM_HDCP_KSV_LEN; + buf += DRM_HDCP_2_NO_OF_DEV_PLUS_RESERVED_SZ; + + DRM_DEBUG("Revoked KSVs: %d\n", ksv_count); + memcpy(srm_data->revoked_ksv_list, buf, ksv_sz); + + srm_data->revoked_ksv_cnt = ksv_count; + return count; +} + +static inline bool is_srm_version_hdcp1(const u8 *buf) +{ + return *buf == (u8)(DRM_HDCP_1_4_SRM_ID << 4); +} + +static inline bool is_srm_version_hdcp2(const u8 *buf) +{ + return *buf == (u8)(DRM_HDCP_2_SRM_ID << 4 | DRM_HDCP_2_INDICATOR); +} + +static void drm_hdcp_srm_update(const u8 *buf, size_t count) +{ + if (count < sizeof(struct hdcp_srm_header)) + return; + + if (is_srm_version_hdcp1(buf)) + drm_hdcp_parse_hdcp1_srm(buf, count); + else if (is_srm_version_hdcp2(buf)) + drm_hdcp_parse_hdcp2_srm(buf, count); +} + +void drm_hdcp_request_srm(struct drm_device *drm_dev) +{ + char fw_name[36] = "display_hdcp_srm.bin"; + const struct firmware *fw; + + int ret; + + ret = request_firmware_direct(&fw, (const char *)fw_name, + drm_dev->dev); + if (ret < 0) + goto exit; + + if (fw->size && fw->data) + drm_hdcp_srm_update(fw->data, fw->size); + +exit: + release_firmware(fw); +} + +/** + * drm_hdcp_check_ksvs_revoked - Check the revoked status of the IDs + * + * @drm_dev: drm_device for which HDCP revocation check is requested + * @ksvs: List of KSVs (HDCP receiver IDs) + * @ksv_count: KSV count passed in through @ksvs + * + * This function reads the HDCP System renewability Message(SRM Table) + * from userspace as a firmware and parses it for the revoked HDCP + * KSVs(Receiver IDs) detected by DCP LLC. Once the revoked KSVs are known, + * revoked state of the KSVs in the list passed in by display drivers are + * decided and response is sent. + * + * SRM should be presented in the name of "display_hdcp_srm.bin". + * + * Returns: + * TRUE on any of the KSV is revoked, else FALSE. + */ +bool drm_hdcp_check_ksvs_revoked(struct drm_device *drm_dev, u8 *ksvs, + u32 ksv_count) +{ + u32 rev_ksv_cnt, cnt, i, j; + u8 *rev_ksv_list; + + if (!srm_data) + return false; + + mutex_lock(&srm_data->mutex); + drm_hdcp_request_srm(drm_dev); + + rev_ksv_cnt = srm_data->revoked_ksv_cnt; + rev_ksv_list = srm_data->revoked_ksv_list; + + /* If the Revoked ksv list is empty */ + if (!rev_ksv_cnt || !rev_ksv_list) { + mutex_unlock(&srm_data->mutex); + return false; + } + + for (cnt = 0; cnt < ksv_count; cnt++) { + rev_ksv_list = srm_data->revoked_ksv_list; + for (i = 0; i < rev_ksv_cnt; i++) { + for (j = 0; j < DRM_HDCP_KSV_LEN; j++) + if (ksvs[j] != rev_ksv_list[j]) { + break; + } else if (j == (DRM_HDCP_KSV_LEN - 1)) { + DRM_DEBUG("Revoked KSV is "); + drm_hdcp_print_ksv(ksvs); + mutex_unlock(&srm_data->mutex); + return true; + } + /* Move the offset to next KSV in the revoked list */ + rev_ksv_list += DRM_HDCP_KSV_LEN; + } + + /* Iterate to next ksv_offset */ + ksvs += DRM_HDCP_KSV_LEN; + } + mutex_unlock(&srm_data->mutex); + return false; +} +EXPORT_SYMBOL_GPL(drm_hdcp_check_ksvs_revoked); + +int drm_setup_hdcp_srm(struct class *drm_class) +{ + srm_data = kzalloc(sizeof(*srm_data), GFP_KERNEL); + if (!srm_data) + return -ENOMEM; + mutex_init(&srm_data->mutex); + + return 0; +} + +void drm_teardown_hdcp_srm(struct class *drm_class) +{ + if (srm_data) { + kfree(srm_data->revoked_ksv_list); + kfree(srm_data); + } +} diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index e19ac7ca602d..476a422414f6 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -201,3 +201,7 @@ int drm_syncobj_query_ioctl(struct drm_device *dev, void *data, void drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent, const struct drm_framebuffer *fb); int drm_framebuffer_debugfs_init(struct drm_minor *minor); + +/* drm_hdcp.c */ +int drm_setup_hdcp_srm(struct class *drm_class); +void drm_teardown_hdcp_srm(struct class *drm_class); diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index ecb7b33002bb..18b1ac442997 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -78,6 +78,7 @@ int drm_sysfs_init(void) } drm_class->devnode = drm_devnode; + drm_setup_hdcp_srm(drm_class); return 0; } @@ -90,6 +91,7 @@ void drm_sysfs_destroy(void) { if (IS_ERR_OR_NULL(drm_class)) return; + drm_teardown_hdcp_srm(drm_class); class_remove_file(drm_class, &class_attr_version.attr); class_destroy(drm_class); drm_class = NULL; diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h index 1cc66df05a43..2f0335d0a50f 100644 --- a/include/drm/drm_hdcp.h +++ b/include/drm/drm_hdcp.h @@ -265,4 +265,28 @@ void drm_hdcp_cpu_to_be24(u8 seq_num[HDCP_2_2_SEQ_NUM_LEN], u32 val) seq_num[2] = val; } +#define DRM_HDCP_SRM_GEN1_MAX_BYTES (5 * 1024) +#define DRM_HDCP_1_4_SRM_ID 0x8 +#define DRM_HDCP_SRM_ID_MASK (0xF << 4) +#define DRM_HDCP_1_4_VRL_LENGTH_SIZE 3 +#define DRM_HDCP_1_4_DCP_SIG_SIZE 40 +#define DRM_HDCP_2_SRM_ID 0x9 +#define DRM_HDCP_2_INDICATOR 0x1 +#define DRM_HDCP_2_INDICATOR_MASK 0xF +#define DRM_HDCP_2_VRL_LENGTH_SIZE 3 +#define DRM_HDCP_2_DCP_SIG_SIZE 384 +#define DRM_HDCP_2_NO_OF_DEV_PLUS_RESERVED_SZ 4 +#define DRM_HDCP_2_KSV_COUNT_2_LSBITS(byte) (((byte) & 0xC) >> 6) + +struct hdcp_srm_header { + u8 srm_id; + u8 reserved; + __be16 srm_version; + u8 srm_gen_no; +} __packed; + +struct drm_device; + +bool drm_hdcp_check_ksvs_revoked(struct drm_device *dev, + u8 *ksvs, u32 ksv_count); #endif -- cgit v1.2.3 From c16fd9be70faf3c49a61700efd16018dd910e390 Mon Sep 17 00:00:00 2001 From: Ramalingam C Date: Tue, 7 May 2019 21:57:40 +0530 Subject: drm/hdcp: gathering hdcp related code into drm_hdcp.c Considering the significant size of hdcp related code in drm, all hdcp related codes are moved into separate file called drm_hdcp.c. v2: Rebased. v2: Rebased. Signed-off-by: Ramalingam C Suggested-by: Daniel Vetter Reviewed-by: Daniel Vetter Acked-by: Dave Airlie Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190507162745.25600-7-ramalingam.c@intel.com --- drivers/gpu/drm/drm_connector.c | 44 -------------------------------------- drivers/gpu/drm/drm_hdcp.c | 47 +++++++++++++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 2 -- include/drm/drm_hdcp.h | 3 +++ 4 files changed, 50 insertions(+), 46 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 7c0eda9cca60..764c7903edf6 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -823,13 +823,6 @@ static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = { DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, drm_tv_subconnector_enum_list) -static struct drm_prop_enum_list drm_cp_enum_list[] = { - { DRM_MODE_CONTENT_PROTECTION_UNDESIRED, "Undesired" }, - { DRM_MODE_CONTENT_PROTECTION_DESIRED, "Desired" }, - { DRM_MODE_CONTENT_PROTECTION_ENABLED, "Enabled" }, -}; -DRM_ENUM_NAME_FN(drm_get_content_protection_name, drm_cp_enum_list) - static const struct drm_prop_enum_list hdmi_colorspaces[] = { /* For Default case, driver will set the colorspace */ { DRM_MODE_COLORIMETRY_DEFAULT, "Default" }, @@ -1515,43 +1508,6 @@ int drm_connector_attach_scaling_mode_property(struct drm_connector *connector, } EXPORT_SYMBOL(drm_connector_attach_scaling_mode_property); -/** - * drm_connector_attach_content_protection_property - attach content protection - * property - * - * @connector: connector to attach CP property on. - * - * This is used to add support for content protection on select connectors. - * Content Protection is intentionally vague to allow for different underlying - * technologies, however it is most implemented by HDCP. - * - * The content protection will be set to &drm_connector_state.content_protection - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_connector_attach_content_protection_property( - struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - struct drm_property *prop = - dev->mode_config.content_protection_property; - - if (!prop) - prop = drm_property_create_enum(dev, 0, "Content Protection", - drm_cp_enum_list, - ARRAY_SIZE(drm_cp_enum_list)); - if (!prop) - return -ENOMEM; - - drm_object_attach_property(&connector->base, prop, - DRM_MODE_CONTENT_PROTECTION_UNDESIRED); - dev->mode_config.content_protection_property = prop; - - return 0; -} -EXPORT_SYMBOL(drm_connector_attach_content_protection_property); - /** * drm_mode_create_aspect_ratio_property - create aspect ratio property * @dev: DRM device diff --git a/drivers/gpu/drm/drm_hdcp.c b/drivers/gpu/drm/drm_hdcp.c index 5e5409505c31..0da7b3718bad 100644 --- a/drivers/gpu/drm/drm_hdcp.c +++ b/drivers/gpu/drm/drm_hdcp.c @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include struct hdcp_srm { u32 revoked_ksv_cnt; @@ -331,3 +334,47 @@ void drm_teardown_hdcp_srm(struct class *drm_class) kfree(srm_data); } } + +static struct drm_prop_enum_list drm_cp_enum_list[] = { + { DRM_MODE_CONTENT_PROTECTION_UNDESIRED, "Undesired" }, + { DRM_MODE_CONTENT_PROTECTION_DESIRED, "Desired" }, + { DRM_MODE_CONTENT_PROTECTION_ENABLED, "Enabled" }, +}; +DRM_ENUM_NAME_FN(drm_get_content_protection_name, drm_cp_enum_list) + +/** + * drm_connector_attach_content_protection_property - attach content protection + * property + * + * @connector: connector to attach CP property on. + * + * This is used to add support for content protection on select connectors. + * Content Protection is intentionally vague to allow for different underlying + * technologies, however it is most implemented by HDCP. + * + * The content protection will be set to &drm_connector_state.content_protection + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_connector_attach_content_protection_property( + struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_property *prop = + dev->mode_config.content_protection_property; + + if (!prop) + prop = drm_property_create_enum(dev, 0, "Content Protection", + drm_cp_enum_list, + ARRAY_SIZE(drm_cp_enum_list)); + if (!prop) + return -ENOMEM; + + drm_object_attach_property(&connector->base, prop, + DRM_MODE_CONTENT_PROTECTION_UNDESIRED); + dev->mode_config.content_protection_property = prop; + + return 0; +} +EXPORT_SYMBOL(drm_connector_attach_content_protection_property); diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 5e41942e5679..da9e1f087fe4 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1339,8 +1339,6 @@ int drm_connector_attach_scaling_mode_property(struct drm_connector *connector, u32 scaling_mode_mask); int drm_connector_attach_vrr_capable_property( struct drm_connector *connector); -int drm_connector_attach_content_protection_property( - struct drm_connector *connector); int drm_mode_create_aspect_ratio_property(struct drm_device *dev); int drm_mode_create_colorspace_property(struct drm_connector *connector); int drm_mode_create_content_type_property(struct drm_device *dev); diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h index 2f0335d0a50f..13771a496e2b 100644 --- a/include/drm/drm_hdcp.h +++ b/include/drm/drm_hdcp.h @@ -286,7 +286,10 @@ struct hdcp_srm_header { } __packed; struct drm_device; +struct drm_connector; bool drm_hdcp_check_ksvs_revoked(struct drm_device *dev, u8 *ksvs, u32 ksv_count); +int drm_connector_attach_content_protection_property( + struct drm_connector *connector); #endif -- cgit v1.2.3 From 1aa3750885fbcece5a0c9e6bbcd014ac526cea41 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 10 May 2019 17:02:55 +0300 Subject: drm/i915/icl: More workaround for port F detection due to broken VBTs Add another ICL-Y PCIID that proved to have only 5 ports to the corresponding PCIID list. Meanwhile I'm trying to get a complete list of all PCIIDs with less than 6 ports and/or get a VBT fix to mark these ports non-existent, but until then the only way is to go one-by-one. This fixes the following error on machines with less than 6 port: [drm:intel_power_well_enable [i915]] enabling AUX F ------------[ cut here ]------------ WARN_ON(intel_wait_for_register(&dev_priv->uncore, regs->driver, (0x1 << ((pw_idx) * 2)), (0x1 << ((pw_idx) * 2)), 1)) (Internal reference: BSpec/Index/20584/Issues, HSD/1306084116) Cc: Mika Kahola References: https://bugs.freedesktop.org/show_bug.cgi?id=108915 Signed-off-by: Imre Deak Reviewed-by: Mika Kahola Tested-by: Mika Kahola Link: https://patchwork.freedesktop.org/patch/msgid/20190510140255.25215-1-imre.deak@intel.com --- include/drm/i915_pciids.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/drm') diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index 6477da22af28..6d60ea68c171 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -559,7 +559,6 @@ #define INTEL_ICL_PORT_F_IDS(info) \ INTEL_VGA_DEVICE(0x8A50, info), \ INTEL_VGA_DEVICE(0x8A5C, info), \ - INTEL_VGA_DEVICE(0x8A5D, info), \ INTEL_VGA_DEVICE(0x8A59, info), \ INTEL_VGA_DEVICE(0x8A58, info), \ INTEL_VGA_DEVICE(0x8A52, info), \ @@ -573,7 +572,8 @@ #define INTEL_ICL_11_IDS(info) \ INTEL_ICL_PORT_F_IDS(info), \ - INTEL_VGA_DEVICE(0x8A51, info) + INTEL_VGA_DEVICE(0x8A51, info), \ + INTEL_VGA_DEVICE(0x8A5D, info) /* EHL */ #define INTEL_EHL_IDS(info) \ -- cgit v1.2.3 From bc93475587850c097bfbc08f2ffa81171e0a1602 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Mon, 6 May 2019 20:01:31 +0200 Subject: drm/fb-helper: No need to cache rotation and sw_rotations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Getting rotation info is cheap so we can do it on demand. This is done in preparation for the removal of struct drm_fb_helper_crtc. Cc: Hans de Goede Signed-off-by: Noralf Trønnes Acked-by: Daniel Vetter Reviewed-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20190506180139.6913-4-noralf@tronnes.org --- drivers/gpu/drm/drm_fb_helper.c | 131 ++++++++++++++++++++-------------------- include/drm/drm_fb_helper.h | 8 --- 2 files changed, 65 insertions(+), 74 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index c521e7490380..1bfbd16c23de 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -388,6 +388,49 @@ int drm_fb_helper_debug_leave(struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_debug_leave); +/* Check if the plane can hw rotate to match panel orientation */ +static bool drm_fb_helper_panel_rotation(struct drm_mode_set *modeset, + unsigned int *rotation) +{ + struct drm_connector *connector = modeset->connectors[0]; + struct drm_plane *plane = modeset->crtc->primary; + u64 valid_mask = 0; + unsigned int i; + + if (!modeset->num_connectors) + return false; + + switch (connector->display_info.panel_orientation) { + case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP: + *rotation = DRM_MODE_ROTATE_180; + break; + case DRM_MODE_PANEL_ORIENTATION_LEFT_UP: + *rotation = DRM_MODE_ROTATE_90; + break; + case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP: + *rotation = DRM_MODE_ROTATE_270; + break; + default: + *rotation = DRM_MODE_ROTATE_0; + } + + /* + * TODO: support 90 / 270 degree hardware rotation, + * depending on the hardware this may require the framebuffer + * to be in a specific tiling format. + */ + if (*rotation != DRM_MODE_ROTATE_180 || !plane->rotation_property) + return false; + + for (i = 0; i < plane->rotation_property->num_values; i++) + valid_mask |= (1ULL << plane->rotation_property->values[i]); + + if (!(*rotation & valid_mask)) + return false; + + return true; +} + static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool active) { struct drm_device *dev = fb_helper->dev; @@ -428,10 +471,13 @@ retry: for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; struct drm_plane *primary = mode_set->crtc->primary; + unsigned int rotation; - /* Cannot fail as we've already gotten the plane state above */ - plane_state = drm_atomic_get_new_plane_state(state, primary); - plane_state->rotation = fb_helper->crtc_info[i].rotation; + if (drm_fb_helper_panel_rotation(mode_set, &rotation)) { + /* Cannot fail as we've already gotten the plane state above */ + plane_state = drm_atomic_get_new_plane_state(state, primary); + plane_state->rotation = rotation; + } ret = __drm_atomic_helper_set_config(mode_set, state); if (ret != 0) @@ -881,7 +927,6 @@ int drm_fb_helper_init(struct drm_device *dev, if (!fb_helper->crtc_info[i].mode_set.connectors) goto out_free; fb_helper->crtc_info[i].mode_set.num_connectors = 0; - fb_helper->crtc_info[i].rotation = DRM_MODE_ROTATE_0; } i = 0; @@ -2510,62 +2555,6 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, return best_score; } -/* - * This function checks if rotation is necessary because of panel orientation - * and if it is, if it is supported. - * If rotation is necessary and supported, it gets set in fb_crtc.rotation. - * If rotation is necessary but not supported, a DRM_MODE_ROTATE_* flag gets - * or-ed into fb_helper->sw_rotations. In drm_setup_crtcs_fb() we check if only - * one bit is set and then we set fb_info.fbcon_rotate_hint to make fbcon do - * the unsupported rotation. - */ -static void drm_setup_crtc_rotation(struct drm_fb_helper *fb_helper, - struct drm_fb_helper_crtc *fb_crtc, - struct drm_connector *connector) -{ - struct drm_plane *plane = fb_crtc->mode_set.crtc->primary; - uint64_t valid_mask = 0; - int i, rotation; - - fb_crtc->rotation = DRM_MODE_ROTATE_0; - - switch (connector->display_info.panel_orientation) { - case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP: - rotation = DRM_MODE_ROTATE_180; - break; - case DRM_MODE_PANEL_ORIENTATION_LEFT_UP: - rotation = DRM_MODE_ROTATE_90; - break; - case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP: - rotation = DRM_MODE_ROTATE_270; - break; - default: - rotation = DRM_MODE_ROTATE_0; - } - - /* - * TODO: support 90 / 270 degree hardware rotation, - * depending on the hardware this may require the framebuffer - * to be in a specific tiling format. - */ - if (rotation != DRM_MODE_ROTATE_180 || !plane->rotation_property) { - fb_helper->sw_rotations |= rotation; - return; - } - - for (i = 0; i < plane->rotation_property->num_values; i++) - valid_mask |= (1ULL << plane->rotation_property->values[i]); - - if (!(rotation & valid_mask)) { - fb_helper->sw_rotations |= rotation; - return; - } - - fb_crtc->rotation = rotation; - /* Rotating in hardware, fbcon should not rotate */ - fb_helper->sw_rotations |= DRM_MODE_ROTATE_0; -} - static struct drm_fb_helper_crtc * drm_fb_helper_crtc(struct drm_fb_helper *fb_helper, struct drm_crtc *crtc) { @@ -2812,7 +2801,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, drm_fb_helper_modeset_release(fb_helper, &fb_helper->crtc_info[i].mode_set); - fb_helper->sw_rotations = 0; drm_fb_helper_for_each_connector(fb_helper, i) { struct drm_display_mode *mode = modes[i]; struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; @@ -2832,7 +2820,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, modeset->mode = drm_mode_duplicate(dev, fb_crtc->desired_mode); drm_connector_get(connector); - drm_setup_crtc_rotation(fb_helper, fb_crtc, connector); modeset->connectors[modeset->num_connectors++] = connector; modeset->x = offset->x; modeset->y = offset->y; @@ -2855,11 +2842,23 @@ out: static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) { struct fb_info *info = fb_helper->fbdev; + unsigned int rotation, sw_rotations = 0; int i; - for (i = 0; i < fb_helper->crtc_count; i++) - if (fb_helper->crtc_info[i].mode_set.num_connectors) - fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; + for (i = 0; i < fb_helper->crtc_count; i++) { + struct drm_mode_set *modeset = &fb_helper->crtc_info[i].mode_set; + + if (!modeset->num_connectors) + continue; + + modeset->fb = fb_helper->fb; + + if (drm_fb_helper_panel_rotation(modeset, &rotation)) + /* Rotating in hardware, fbcon should not rotate */ + sw_rotations |= DRM_MODE_ROTATE_0; + else + sw_rotations |= rotation; + } mutex_lock(&fb_helper->dev->mode_config.mutex); drm_fb_helper_for_each_connector(fb_helper, i) { @@ -2875,7 +2874,7 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) } mutex_unlock(&fb_helper->dev->mode_config.mutex); - switch (fb_helper->sw_rotations) { + switch (sw_rotations) { case DRM_MODE_ROTATE_0: info->fbcon_rotate_hint = FB_ROTATE_UR; break; diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 40af2866c26a..b0a14aef2e39 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -51,7 +51,6 @@ struct drm_fb_helper_crtc { struct drm_mode_set mode_set; struct drm_display_mode *desired_mode; int x, y; - int rotation; }; /** @@ -151,13 +150,6 @@ struct drm_fb_helper { struct drm_fb_helper_crtc *crtc_info; int connector_count; int connector_info_alloc_count; - /** - * @sw_rotations: - * Bitmask of all rotations requested for panel-orientation which - * could not be handled in hardware. If only one bit is set - * fbdev->fbcon_rotate_hint gets set to the requested rotation. - */ - int sw_rotations; /** * @connector_info: * -- cgit v1.2.3 From c8de0d5ff978a581717a1576e676b68bdc86c996 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Mon, 6 May 2019 20:01:32 +0200 Subject: drm/fb-helper: Remove drm_fb_helper_crtc->{x, y, desired_mode} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The values are already present in the modeset. This is done in preparation for the removal of struct drm_fb_helper_crtc. Signed-off-by: Noralf Trønnes Reviewed-by: Daniel Vetter Reviewed-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20190506180139.6913-5-noralf@tronnes.org --- drivers/gpu/drm/drm_fb_helper.c | 12 ++++-------- include/drm/drm_fb_helper.h | 2 -- 2 files changed, 4 insertions(+), 10 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 1bfbd16c23de..c259a28522f8 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -2031,16 +2031,16 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, */ bool lastv = true, lasth = true; - desired_mode = fb_helper->crtc_info[i].desired_mode; mode_set = &fb_helper->crtc_info[i].mode_set; + desired_mode = mode_set->mode; if (!desired_mode) continue; crtc_count++; - x = fb_helper->crtc_info[i].x; - y = fb_helper->crtc_info[i].y; + x = mode_set->x; + y = mode_set->y; sizes.surface_width = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width); sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height); @@ -2814,11 +2814,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y); - fb_crtc->desired_mode = mode; - fb_crtc->x = offset->x; - fb_crtc->y = offset->y; - modeset->mode = drm_mode_duplicate(dev, - fb_crtc->desired_mode); + modeset->mode = drm_mode_duplicate(dev, mode); drm_connector_get(connector); modeset->connectors[modeset->num_connectors++] = connector; modeset->x = offset->x; diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index b0a14aef2e39..2af1c6d3e147 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -49,8 +49,6 @@ struct drm_fb_offset { struct drm_fb_helper_crtc { struct drm_mode_set mode_set; - struct drm_display_mode *desired_mode; - int x, y; }; /** -- cgit v1.2.3 From 85438a8ddf031f416758ecf8c080ff92075acb01 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 8 May 2019 10:26:11 +0200 Subject: drm: Add |struct drm_gem_vram_object| and helpers The type |struct drm_gem_vram_object| implements a GEM object for simple framebuffer devices with dedicated video memory. The BO is either located in VRAM or system memory. The implementation has been created from the respective code in ast, bochs and mgag200. These drivers copy their implementation from each other; except for the names of several data types. The helpers are currently build with TTM, but this is considered an implementation detail and may change in future updates. v5: * do WARN_ON_ONCE for pin-count mismatches * allocate only 2 entries in placements array v4: * cleanups from checkpatch.pl * removed several fixed-size types from interfaces * DRM_VRAM_HELPER now selects DRM_TTM * remove separate config option for GEM VRAM v2: * rename to |struct drm_gem_vram_object| * move drm_is_gem_ttm() to a later patch in the series * add drm_gem_vram_kmap_at() * return is_iomem from kmap functions * redefine TTM placement flags for public interface * documentation fixes Signed-off-by: Thomas Zimmermann Link: http://patchwork.freedesktop.org/patch/msgid/20190508082630.15116-2-tzimmermann@suse.de Signed-off-by: Gerd Hoffmann --- Documentation/gpu/drm-mm.rst | 15 ++ drivers/gpu/drm/Kconfig | 7 + drivers/gpu/drm/Makefile | 4 + drivers/gpu/drm/drm_gem_vram_helper.c | 412 +++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_vram_helper_common.c | 6 + include/drm/drm_gem_vram_helper.h | 92 +++++++ 6 files changed, 536 insertions(+) create mode 100644 drivers/gpu/drm/drm_gem_vram_helper.c create mode 100644 drivers/gpu/drm/drm_vram_helper_common.c create mode 100644 include/drm/drm_gem_vram_helper.h (limited to 'include/drm') diff --git a/Documentation/gpu/drm-mm.rst b/Documentation/gpu/drm-mm.rst index 54a696d961a7..a3b685089512 100644 --- a/Documentation/gpu/drm-mm.rst +++ b/Documentation/gpu/drm-mm.rst @@ -380,6 +380,21 @@ GEM CMA Helper Functions Reference .. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c :export: +VRAM Helper Function Reference +============================== + +GEM VRAM Helper Functions Reference +----------------------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_gem_vram_helper.c + :doc: overview + +.. kernel-doc:: include/drm/drm_gem_vram_helper.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_gem_vram_helper.c + :export: + VMA Offset Manager ================== diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 2267e84d5cb4..13d7b321db0d 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -160,6 +160,13 @@ config DRM_TTM GPU memory types. Will be enabled automatically if a device driver uses it. +config DRM_VRAM_HELPER + tristate + depends on DRM + select DRM_TTM + help + Helpers for VRAM memory management + config DRM_GEM_CMA_HELPER bool depends on DRM diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 72f5036d9bfa..ed49b2480766 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -32,6 +32,10 @@ drm-$(CONFIG_AGP) += drm_agpsupport.o drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o +drm_vram_helper-y := drm_gem_vram_helper.o \ + drm_vram_helper_common.o +obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o + drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_dsc.o drm_probe_helper.o \ drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \ drm_kms_helper_common.o drm_dp_dual_mode_helper.o \ diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c new file mode 100644 index 000000000000..761a546412f8 --- /dev/null +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -0,0 +1,412 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +/** + * DOC: overview + * + * This library provides a GEM buffer object that is backed by video RAM + * (VRAM). It can be used for framebuffer devices with dedicated memory. + */ + +/* + * Buffer-objects helpers + */ + +static void drm_gem_vram_cleanup(struct drm_gem_vram_object *gbo) +{ + /* We got here via ttm_bo_put(), which means that the + * TTM buffer object in 'bo' has already been cleaned + * up; only release the GEM object. + */ + drm_gem_object_release(&gbo->gem); +} + +static void drm_gem_vram_destroy(struct drm_gem_vram_object *gbo) +{ + drm_gem_vram_cleanup(gbo); + kfree(gbo); +} + +static void ttm_buffer_object_destroy(struct ttm_buffer_object *bo) +{ + struct drm_gem_vram_object *gbo = drm_gem_vram_of_bo(bo); + + drm_gem_vram_destroy(gbo); +} + +static void drm_gem_vram_placement(struct drm_gem_vram_object *gbo, + unsigned long pl_flag) +{ + unsigned int i; + unsigned int c = 0; + + gbo->placement.placement = gbo->placements; + gbo->placement.busy_placement = gbo->placements; + + if (pl_flag & TTM_PL_FLAG_VRAM) + gbo->placements[c++].flags = TTM_PL_FLAG_WC | + TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_VRAM; + + if (pl_flag & TTM_PL_FLAG_SYSTEM) + gbo->placements[c++].flags = TTM_PL_MASK_CACHING | + TTM_PL_FLAG_SYSTEM; + + if (!c) + gbo->placements[c++].flags = TTM_PL_MASK_CACHING | + TTM_PL_FLAG_SYSTEM; + + gbo->placement.num_placement = c; + gbo->placement.num_busy_placement = c; + + for (i = 0; i < c; ++i) { + gbo->placements[i].fpfn = 0; + gbo->placements[i].lpfn = 0; + } +} + +static int drm_gem_vram_init(struct drm_device *dev, + struct ttm_bo_device *bdev, + struct drm_gem_vram_object *gbo, + size_t size, unsigned long pg_align, + bool interruptible) +{ + int ret; + size_t acc_size; + + ret = drm_gem_object_init(dev, &gbo->gem, size); + if (ret) + return ret; + + acc_size = ttm_bo_dma_acc_size(bdev, size, sizeof(*gbo)); + + gbo->bo.bdev = bdev; + drm_gem_vram_placement(gbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); + + ret = ttm_bo_init(bdev, &gbo->bo, size, ttm_bo_type_device, + &gbo->placement, pg_align, interruptible, acc_size, + NULL, NULL, ttm_buffer_object_destroy); + if (ret) + goto err_drm_gem_object_release; + + return 0; + +err_drm_gem_object_release: + drm_gem_object_release(&gbo->gem); + return ret; +} + +/** + * drm_gem_vram_create() - Creates a VRAM-backed GEM object + * @dev: the DRM device + * @bdev: the TTM BO device backing the object + * @size: the buffer size in bytes + * @pg_align: the buffer's alignment in multiples of the page size + * @interruptible: sleep interruptible if waiting for memory + * + * Returns: + * A new instance of &struct drm_gem_vram_object on success, or + * an ERR_PTR()-encoded error code otherwise. + */ +struct drm_gem_vram_object *drm_gem_vram_create(struct drm_device *dev, + struct ttm_bo_device *bdev, + size_t size, + unsigned long pg_align, + bool interruptible) +{ + struct drm_gem_vram_object *gbo; + int ret; + + gbo = kzalloc(sizeof(*gbo), GFP_KERNEL); + if (!gbo) + return ERR_PTR(-ENOMEM); + + ret = drm_gem_vram_init(dev, bdev, gbo, size, pg_align, interruptible); + if (ret < 0) + goto err_kfree; + + return gbo; + +err_kfree: + kfree(gbo); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(drm_gem_vram_create); + +/** + * drm_gem_vram_put() - Releases a reference to a VRAM-backed GEM object + * @gbo: the GEM VRAM object + * + * See ttm_bo_put() for more information. + */ +void drm_gem_vram_put(struct drm_gem_vram_object *gbo) +{ + ttm_bo_put(&gbo->bo); +} +EXPORT_SYMBOL(drm_gem_vram_put); + +/** + * drm_gem_vram_reserve() - Reserves a VRAM-backed GEM object + * @gbo: the GEM VRAM object + * @no_wait: don't wait for buffer object to become available + * + * See ttm_bo_reserve() for more information. + * + * Returns: + * 0 on success, or + * a negative error code otherwise + */ +int drm_gem_vram_reserve(struct drm_gem_vram_object *gbo, bool no_wait) +{ + return ttm_bo_reserve(&gbo->bo, true, no_wait, NULL); +} +EXPORT_SYMBOL(drm_gem_vram_reserve); + +/** + * drm_gem_vram_unreserve() - \ + Release a reservation acquired by drm_gem_vram_reserve() + * @gbo: the GEM VRAM object + * + * See ttm_bo_unreserve() for more information. + */ +void drm_gem_vram_unreserve(struct drm_gem_vram_object *gbo) +{ + ttm_bo_unreserve(&gbo->bo); +} +EXPORT_SYMBOL(drm_gem_vram_unreserve); + +/** + * drm_gem_vram_mmap_offset() - Returns a GEM VRAM object's mmap offset + * @gbo: the GEM VRAM object + * + * See drm_vma_node_offset_addr() for more information. + * + * Returns: + * The buffer object's offset for userspace mappings on success, or + * 0 if no offset is allocated. + */ +u64 drm_gem_vram_mmap_offset(struct drm_gem_vram_object *gbo) +{ + return drm_vma_node_offset_addr(&gbo->bo.vma_node); +} +EXPORT_SYMBOL(drm_gem_vram_mmap_offset); + +/** + * drm_gem_vram_offset() - \ + Returns a GEM VRAM object's offset in video memory + * @gbo: the GEM VRAM object + * + * This function returns the buffer object's offset in the device's video + * memory. The buffer object has to be pinned to %TTM_PL_VRAM. + * + * Returns: + * The buffer object's offset in video memory on success, or + * a negative errno code otherwise. + */ +s64 drm_gem_vram_offset(struct drm_gem_vram_object *gbo) +{ + if (WARN_ON_ONCE(!gbo->pin_count)) + return (s64)-ENODEV; + return gbo->bo.offset; +} +EXPORT_SYMBOL(drm_gem_vram_offset); + +/** + * drm_gem_vram_pin() - Pins a GEM VRAM object in a region. + * @gbo: the GEM VRAM object + * @pl_flag: a bitmask of possible memory regions + * + * Pinning a buffer object ensures that it is not evicted from + * a memory region. A pinned buffer object has to be unpinned before + * it can be pinned to another region. + * + * Returns: + * 0 on success, or + * a negative error code otherwise. + */ +int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag) +{ + int i, ret; + struct ttm_operation_ctx ctx = { false, false }; + + if (gbo->pin_count) { + ++gbo->pin_count; + return 0; + } + + drm_gem_vram_placement(gbo, pl_flag); + for (i = 0; i < gbo->placement.num_placement; ++i) + gbo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx); + if (ret < 0) + return ret; + + gbo->pin_count = 1; + + return 0; +} +EXPORT_SYMBOL(drm_gem_vram_pin); + +/** + * drm_gem_vram_unpin() - Unpins a GEM VRAM object + * @gbo: the GEM VRAM object + * + * Returns: + * 0 on success, or + * a negative error code otherwise. + */ +int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo) +{ + int i, ret; + struct ttm_operation_ctx ctx = { false, false }; + + if (WARN_ON_ONCE(!gbo->pin_count)) + return 0; + + --gbo->pin_count; + if (gbo->pin_count) + return 0; + + for (i = 0; i < gbo->placement.num_placement ; ++i) + gbo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(drm_gem_vram_unpin); + +/** + * drm_gem_vram_push_to_system() - \ + Unpins a GEM VRAM object and moves it to system memory + * @gbo: the GEM VRAM object + * + * This operation only works if the caller holds the final pin on the + * buffer object. + * + * Returns: + * 0 on success, or + * a negative error code otherwise. + */ +int drm_gem_vram_push_to_system(struct drm_gem_vram_object *gbo) +{ + int i, ret; + struct ttm_operation_ctx ctx = { false, false }; + + if (WARN_ON_ONCE(!gbo->pin_count)) + return 0; + + --gbo->pin_count; + if (gbo->pin_count) + return 0; + + if (gbo->kmap.virtual) + ttm_bo_kunmap(&gbo->kmap); + + drm_gem_vram_placement(gbo, TTM_PL_FLAG_SYSTEM); + for (i = 0; i < gbo->placement.num_placement ; ++i) + gbo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(drm_gem_vram_push_to_system); + +/** + * drm_gem_vram_kmap_at() - Maps a GEM VRAM object into kernel address space + * @gbo: the GEM VRAM object + * @map: establish a mapping if necessary + * @is_iomem: returns true if the mapped memory is I/O memory, or false \ + otherwise; can be NULL + * @kmap: the mapping's kmap object + * + * This function maps the buffer object into the kernel's address space + * or returns the current mapping. If the parameter map is false, the + * function only queries the current mapping, but does not establish a + * new one. + * + * Returns: + * The buffers virtual address if mapped, or + * NULL if not mapped, or + * an ERR_PTR()-encoded error code otherwise. + */ +void *drm_gem_vram_kmap_at(struct drm_gem_vram_object *gbo, bool map, + bool *is_iomem, struct ttm_bo_kmap_obj *kmap) +{ + int ret; + + if (kmap->virtual || !map) + goto out; + + ret = ttm_bo_kmap(&gbo->bo, 0, gbo->bo.num_pages, kmap); + if (ret) + return ERR_PTR(ret); + +out: + if (!is_iomem) + return kmap->virtual; + if (!kmap->virtual) { + *is_iomem = false; + return NULL; + } + return ttm_kmap_obj_virtual(kmap, is_iomem); +} +EXPORT_SYMBOL(drm_gem_vram_kmap_at); + +/** + * drm_gem_vram_kmap() - Maps a GEM VRAM object into kernel address space + * @gbo: the GEM VRAM object + * @map: establish a mapping if necessary + * @is_iomem: returns true if the mapped memory is I/O memory, or false \ + otherwise; can be NULL + * + * This function maps the buffer object into the kernel's address space + * or returns the current mapping. If the parameter map is false, the + * function only queries the current mapping, but does not establish a + * new one. + * + * Returns: + * The buffers virtual address if mapped, or + * NULL if not mapped, or + * an ERR_PTR()-encoded error code otherwise. + */ +void *drm_gem_vram_kmap(struct drm_gem_vram_object *gbo, bool map, + bool *is_iomem) +{ + return drm_gem_vram_kmap_at(gbo, map, is_iomem, &gbo->kmap); +} +EXPORT_SYMBOL(drm_gem_vram_kmap); + +/** + * drm_gem_vram_kunmap_at() - Unmaps a GEM VRAM object + * @gbo: the GEM VRAM object + * @kmap: the mapping's kmap object + */ +void drm_gem_vram_kunmap_at(struct drm_gem_vram_object *gbo, + struct ttm_bo_kmap_obj *kmap) +{ + if (!kmap->virtual) + return; + + ttm_bo_kunmap(kmap); + kmap->virtual = NULL; +} +EXPORT_SYMBOL(drm_gem_vram_kunmap_at); + +/** + * drm_gem_vram_kunmap() - Unmaps a GEM VRAM object + * @gbo: the GEM VRAM object + */ +void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo) +{ + drm_gem_vram_kunmap_at(gbo, &gbo->kmap); +} +EXPORT_SYMBOL(drm_gem_vram_kunmap); diff --git a/drivers/gpu/drm/drm_vram_helper_common.c b/drivers/gpu/drm/drm_vram_helper_common.c new file mode 100644 index 000000000000..7c25daca18d0 --- /dev/null +++ b/drivers/gpu/drm/drm_vram_helper_common.c @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +MODULE_DESCRIPTION("DRM VRAM memory-management helpers"); +MODULE_LICENSE("GPL"); diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h new file mode 100644 index 000000000000..5802dd694939 --- /dev/null +++ b/include/drm/drm_gem_vram_helper.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef DRM_GEM_VRAM_HELPER_H +#define DRM_GEM_VRAM_HELPER_H + +#include +#include +#include +#include /* for container_of() */ + +struct filp; + +#define DRM_GEM_VRAM_PL_FLAG_VRAM TTM_PL_FLAG_VRAM +#define DRM_GEM_VRAM_PL_FLAG_SYSTEM TTM_PL_FLAG_SYSTEM + +/* + * Buffer-object helpers + */ + +/** + * struct drm_gem_vram_object - GEM object backed by VRAM + * @gem: GEM object + * @bo: TTM buffer object + * @kmap: Mapping information for @bo + * @placement: TTM placement information. Supported placements are \ + %TTM_PL_VRAM and %TTM_PL_SYSTEM + * @placements: TTM placement information. + * @pin_count: Pin counter + * + * The type struct drm_gem_vram_object represents a GEM object that is + * backed by VRAM. It can be used for simple framebuffer devices with + * dedicated memory. The buffer object can be evicted to system memory if + * video memory becomes scarce. + */ +struct drm_gem_vram_object { + struct drm_gem_object gem; + struct ttm_buffer_object bo; + struct ttm_bo_kmap_obj kmap; + + /* Supported placements are %TTM_PL_VRAM and %TTM_PL_SYSTEM */ + struct ttm_placement placement; + struct ttm_place placements[2]; + + int pin_count; +}; + +/** + * Returns the container of type &struct drm_gem_vram_object + * for field bo. + * @bo: the VRAM buffer object + * Returns: The containing GEM VRAM object + */ +static inline struct drm_gem_vram_object *drm_gem_vram_of_bo( + struct ttm_buffer_object *bo) +{ + return container_of(bo, struct drm_gem_vram_object, bo); +} + +/** + * Returns the container of type &struct drm_gem_vram_object + * for field gem. + * @gem: the GEM object + * Returns: The containing GEM VRAM object + */ +static inline struct drm_gem_vram_object *drm_gem_vram_of_gem( + struct drm_gem_object *gem) +{ + return container_of(gem, struct drm_gem_vram_object, gem); +} + +struct drm_gem_vram_object *drm_gem_vram_create(struct drm_device *dev, + struct ttm_bo_device *bdev, + size_t size, + unsigned long pg_align, + bool interruptible); +void drm_gem_vram_put(struct drm_gem_vram_object *gbo); +int drm_gem_vram_reserve(struct drm_gem_vram_object *gbo, bool no_wait); +void drm_gem_vram_unreserve(struct drm_gem_vram_object *gbo); +u64 drm_gem_vram_mmap_offset(struct drm_gem_vram_object *gbo); +s64 drm_gem_vram_offset(struct drm_gem_vram_object *gbo); +int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag); +int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo); +int drm_gem_vram_push_to_system(struct drm_gem_vram_object *gbo); +void *drm_gem_vram_kmap_at(struct drm_gem_vram_object *gbo, bool map, + bool *is_iomem, struct ttm_bo_kmap_obj *kmap); +void *drm_gem_vram_kmap(struct drm_gem_vram_object *gbo, bool map, + bool *is_iomem); +void drm_gem_vram_kunmap_at(struct drm_gem_vram_object *gbo, + struct ttm_bo_kmap_obj *kmap); +void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo); + +#endif -- cgit v1.2.3 From 6c812bc5074bbc969e78fadfbba7ac41fe61ddeb Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 8 May 2019 10:26:12 +0200 Subject: drm: Add |struct drm_gem_vram_object| callbacks for |struct ttm_bo_driver| The provided helpers can be used for the respective callback functions in |struct ttm_bo_driver|. v2: * drm_is_gem_vram() is now a private function * documentation fixes Signed-off-by: Thomas Zimmermann Link: http://patchwork.freedesktop.org/patch/msgid/20190508082630.15116-3-tzimmermann@suse.de Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/drm_gem_vram_helper.c | 50 +++++++++++++++++++++++++++++++++++ include/drm/drm_gem_vram_helper.h | 10 +++++++ 2 files changed, 60 insertions(+) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index 761a546412f8..d6c4e0d0a1c5 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -410,3 +410,53 @@ void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo) drm_gem_vram_kunmap_at(gbo, &gbo->kmap); } EXPORT_SYMBOL(drm_gem_vram_kunmap); + +/* + * Helpers for struct ttm_bo_driver + */ + +static bool drm_is_gem_vram(struct ttm_buffer_object *bo) +{ + return (bo->destroy == ttm_buffer_object_destroy); +} + +/** + * drm_gem_vram_bo_driver_evict_flags() - \ + Implements &struct ttm_bo_driver.evict_flags + * @bo: TTM buffer object. Refers to &struct drm_gem_vram_object.bo + * @pl: TTM placement information. + */ +void drm_gem_vram_bo_driver_evict_flags(struct ttm_buffer_object *bo, + struct ttm_placement *pl) +{ + struct drm_gem_vram_object *gbo; + + /* TTM may pass BOs that are not GEM VRAM BOs. */ + if (!drm_is_gem_vram(bo)) + return; + + gbo = drm_gem_vram_of_bo(bo); + drm_gem_vram_placement(gbo, TTM_PL_FLAG_SYSTEM); + *pl = gbo->placement; +} +EXPORT_SYMBOL(drm_gem_vram_bo_driver_evict_flags); + +/** + * drm_gem_vram_bo_driver_verify_access() - \ + Implements &struct ttm_bo_driver.verify_access + * @bo: TTM buffer object. Refers to &struct drm_gem_vram_object.bo + * @filp: File pointer. + * + * Returns: + * 0 on success, or + * a negative errno code otherwise. + */ +int drm_gem_vram_bo_driver_verify_access(struct ttm_buffer_object *bo, + struct file *filp) +{ + struct drm_gem_vram_object *gbo = drm_gem_vram_of_bo(bo); + + return drm_vma_node_verify_access(&gbo->gem.vma_node, + filp->private_data); +} +EXPORT_SYMBOL(drm_gem_vram_bo_driver_verify_access); diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index 5802dd694939..1915d3958304 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -89,4 +89,14 @@ void drm_gem_vram_kunmap_at(struct drm_gem_vram_object *gbo, struct ttm_bo_kmap_obj *kmap); void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo); +/* + * Helpers for struct ttm_bo_driver + */ + +void drm_gem_vram_bo_driver_evict_flags(struct ttm_buffer_object *bo, + struct ttm_placement *pl); + +int drm_gem_vram_bo_driver_verify_access(struct ttm_buffer_object *bo, + struct file *filp); + #endif -- cgit v1.2.3 From 737000fd9c7d32dd378733cc4bba17555b241be0 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 8 May 2019 10:26:13 +0200 Subject: drm: Add |struct drm_gem_vram_object| callbacks for |struct drm_driver| The provided helpers can be used for the respective callback functions in |struct drm_driver|. v4: * cleanups from checkpatch.pl v2: * documentation fixes Signed-off-by: Thomas Zimmermann Link: http://patchwork.freedesktop.org/patch/msgid/20190508082630.15116-4-tzimmermann@suse.de Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/drm_gem_vram_helper.c | 49 +++++++++++++++++++++++++++++++++++ include/drm/drm_gem_vram_helper.h | 10 +++++++ 2 files changed, 59 insertions(+) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index d6c4e0d0a1c5..264028374336 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -460,3 +460,52 @@ int drm_gem_vram_bo_driver_verify_access(struct ttm_buffer_object *bo, filp->private_data); } EXPORT_SYMBOL(drm_gem_vram_bo_driver_verify_access); + +/* + * Helpers for struct drm_driver + */ + +/** + * drm_gem_vram_driver_gem_free_object_unlocked() - \ + Implements &struct drm_driver.gem_free_object_unlocked + * @gem: GEM object. Refers to &struct drm_gem_vram_object.gem + */ +void drm_gem_vram_driver_gem_free_object_unlocked(struct drm_gem_object *gem) +{ + struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem); + + drm_gem_vram_put(gbo); +} +EXPORT_SYMBOL(drm_gem_vram_driver_gem_free_object_unlocked); + +/** + * drm_gem_vram_driver_dumb_mmap_offset() - \ + Implements &struct drm_driver.dumb_mmap_offset + * @file: DRM file pointer. + * @dev: DRM device. + * @handle: GEM handle + * @offset: Returns the mapping's memory offset on success + * + * Returns: + * 0 on success, or + * a negative errno code otherwise. + */ +int drm_gem_vram_driver_dumb_mmap_offset(struct drm_file *file, + struct drm_device *dev, + uint32_t handle, uint64_t *offset) +{ + struct drm_gem_object *gem; + struct drm_gem_vram_object *gbo; + + gem = drm_gem_object_lookup(file, handle); + if (!gem) + return -ENOENT; + + gbo = drm_gem_vram_of_gem(gem); + *offset = drm_gem_vram_mmap_offset(gbo); + + drm_gem_object_put_unlocked(gem); + + return 0; +} +EXPORT_SYMBOL(drm_gem_vram_driver_dumb_mmap_offset); diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index 1915d3958304..b87cb2e9d9da 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -99,4 +99,14 @@ void drm_gem_vram_bo_driver_evict_flags(struct ttm_buffer_object *bo, int drm_gem_vram_bo_driver_verify_access(struct ttm_buffer_object *bo, struct file *filp); +/* + * Helpers for struct drm_driver + */ + +void drm_gem_vram_driver_gem_free_object_unlocked(struct drm_gem_object *gem); + +int drm_gem_vram_driver_dumb_mmap_offset(struct drm_file *file, + struct drm_device *dev, + uint32_t handle, uint64_t *offset); + #endif -- cgit v1.2.3 From fed1eec080b9b23f5a80a0c4f19ae49a2d14c69e Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 8 May 2019 10:26:14 +0200 Subject: drm: Add drm_gem_vram_fill_create_dumb() to create dumb buffers The helper function drm_gem_vram_fill_create_dumb() implements most of struct drm_driver.dumb_create() for GEM-VRAM buffer objects. It's not a full implementation of the callback, as several driver-specific parameters are still required. v4: * cleanups from checkpatch.pl v2: * documentation fixes Signed-off-by: Thomas Zimmermann Link: http://patchwork.freedesktop.org/patch/msgid/20190508082630.15116-5-tzimmermann@suse.de Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/drm_gem_vram_helper.c | 62 +++++++++++++++++++++++++++++++++++ include/drm/drm_gem_vram_helper.h | 8 +++++ 2 files changed, 70 insertions(+) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index 264028374336..aba073279ca2 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include /** @@ -411,6 +412,67 @@ void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo) } EXPORT_SYMBOL(drm_gem_vram_kunmap); +/** + * drm_gem_vram_fill_create_dumb() - \ + Helper for implementing &struct drm_driver.dumb_create + * @file: the DRM file + * @dev: the DRM device + * @bdev: the TTM BO device managing the buffer object + * @pg_align: the buffer's alignment in multiples of the page size + * @interruptible: sleep interruptible if waiting for memory + * @args: the arguments as provided to \ + &struct drm_driver.dumb_create + * + * This helper function fills &struct drm_mode_create_dumb, which is used + * by &struct drm_driver.dumb_create. Implementations of this interface + * should forwards their arguments to this helper, plus the driver-specific + * parameters. + * + * Returns: + * 0 on success, or + * a negative error code otherwise. + */ +int drm_gem_vram_fill_create_dumb(struct drm_file *file, + struct drm_device *dev, + struct ttm_bo_device *bdev, + unsigned long pg_align, + bool interruptible, + struct drm_mode_create_dumb *args) +{ + size_t pitch, size; + struct drm_gem_vram_object *gbo; + int ret; + u32 handle; + + pitch = args->width * ((args->bpp + 7) / 8); + size = pitch * args->height; + + size = roundup(size, PAGE_SIZE); + if (!size) + return -EINVAL; + + gbo = drm_gem_vram_create(dev, bdev, size, pg_align, interruptible); + if (IS_ERR(gbo)) + return PTR_ERR(gbo); + + ret = drm_gem_handle_create(file, &gbo->gem, &handle); + if (ret) + goto err_drm_gem_object_put_unlocked; + + drm_gem_object_put_unlocked(&gbo->gem); + + args->pitch = pitch; + args->size = size; + args->handle = handle; + + return 0; + +err_drm_gem_object_put_unlocked: + drm_gem_object_put_unlocked(&gbo->gem); + return ret; +} +EXPORT_SYMBOL(drm_gem_vram_fill_create_dumb); + /* * Helpers for struct ttm_bo_driver */ diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index b87cb2e9d9da..8e54f721c9f8 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -8,6 +8,7 @@ #include #include /* for container_of() */ +struct drm_mode_create_dumb; struct filp; #define DRM_GEM_VRAM_PL_FLAG_VRAM TTM_PL_FLAG_VRAM @@ -89,6 +90,13 @@ void drm_gem_vram_kunmap_at(struct drm_gem_vram_object *gbo, struct ttm_bo_kmap_obj *kmap); void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo); +int drm_gem_vram_fill_create_dumb(struct drm_file *file, + struct drm_device *dev, + struct ttm_bo_device *bdev, + unsigned long pg_align, + bool interruptible, + struct drm_mode_create_dumb *args); + /* * Helpers for struct ttm_bo_driver */ -- cgit v1.2.3 From 1f460b497890d6bf54aba6e41fe24bba2217141a Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 8 May 2019 10:26:15 +0200 Subject: drm: Add simple PRIME helpers for GEM VRAM These basic helper functions for GEM VRAM allow for pinning and mapping GEM VRAM objects via the PRIME interfaces. It's not a full implementation, but complete enough for generic fbcon. Signed-off-by: Thomas Zimmermann Link: http://patchwork.freedesktop.org/patch/msgid/20190508082630.15116-6-tzimmermann@suse.de Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/drm_gem_vram_helper.c | 98 +++++++++++++++++++++++++++++++++++ include/drm/drm_gem_vram_helper.h | 20 +++++++ 2 files changed, 118 insertions(+) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index aba073279ca2..15c6193e4985 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -2,6 +2,7 @@ #include #include +#include #include /** @@ -571,3 +572,100 @@ int drm_gem_vram_driver_dumb_mmap_offset(struct drm_file *file, return 0; } EXPORT_SYMBOL(drm_gem_vram_driver_dumb_mmap_offset); + +/* + * PRIME helpers for struct drm_driver + */ + +/** + * drm_gem_vram_driver_gem_prime_pin() - \ + Implements &struct drm_driver.gem_prime_pin + * @gem: The GEM object to pin + * + * Returns: + * 0 on success, or + * a negative errno code otherwise. + */ +int drm_gem_vram_driver_gem_prime_pin(struct drm_gem_object *gem) +{ + struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem); + + return drm_gem_vram_pin(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM); +} +EXPORT_SYMBOL(drm_gem_vram_driver_gem_prime_pin); + +/** + * drm_gem_vram_driver_gem_prime_unpin() - \ + Implements &struct drm_driver.gem_prime_unpin + * @gem: The GEM object to unpin + */ +void drm_gem_vram_driver_gem_prime_unpin(struct drm_gem_object *gem) +{ + struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem); + + drm_gem_vram_unpin(gbo); +} +EXPORT_SYMBOL(drm_gem_vram_driver_gem_prime_unpin); + +/** + * drm_gem_vram_driver_gem_prime_vmap() - \ + Implements &struct drm_driver.gem_prime_vmap + * @gem: The GEM object to map + * + * Returns: + * The buffers virtual address on success, or + * NULL otherwise. + */ +void *drm_gem_vram_driver_gem_prime_vmap(struct drm_gem_object *gem) +{ + struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem); + int ret; + void *base; + + ret = drm_gem_vram_pin(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM); + if (ret) + return NULL; + base = drm_gem_vram_kmap(gbo, true, NULL); + if (IS_ERR(base)) { + drm_gem_vram_unpin(gbo); + return NULL; + } + return base; +} +EXPORT_SYMBOL(drm_gem_vram_driver_gem_prime_vmap); + +/** + * drm_gem_vram_driver_gem_prime_vunmap() - \ + Implements &struct drm_driver.gem_prime_vunmap + * @gem: The GEM object to unmap + * @vaddr: The mapping's base address + */ +void drm_gem_vram_driver_gem_prime_vunmap(struct drm_gem_object *gem, + void *vaddr) +{ + struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem); + + drm_gem_vram_kunmap(gbo); + drm_gem_vram_unpin(gbo); +} +EXPORT_SYMBOL(drm_gem_vram_driver_gem_prime_vunmap); + +/** + * drm_gem_vram_driver_gem_prime_mmap() - \ + Implements &struct drm_driver.gem_prime_mmap + * @gem: The GEM object to map + * @vma: The VMA describing the mapping + * + * Returns: + * 0 on success, or + * a negative errno code otherwise. + */ +int drm_gem_vram_driver_gem_prime_mmap(struct drm_gem_object *gem, + struct vm_area_struct *vma) +{ + struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem); + + gbo->gem.vma_node.vm_node.start = gbo->bo.vma_node.vm_node.start; + return drm_gem_prime_mmap(gem, vma); +} +EXPORT_SYMBOL(drm_gem_vram_driver_gem_prime_mmap); diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index 8e54f721c9f8..9a8bd98b623f 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -116,5 +116,25 @@ void drm_gem_vram_driver_gem_free_object_unlocked(struct drm_gem_object *gem); int drm_gem_vram_driver_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev, uint32_t handle, uint64_t *offset); +/* + * PRIME helpers for struct drm_driver + */ + +int drm_gem_vram_driver_gem_prime_pin(struct drm_gem_object *obj); +void drm_gem_vram_driver_gem_prime_unpin(struct drm_gem_object *obj); +void *drm_gem_vram_driver_gem_prime_vmap(struct drm_gem_object *obj); +void drm_gem_vram_driver_gem_prime_vunmap(struct drm_gem_object *obj, + void *vaddr); +int drm_gem_vram_driver_gem_prime_mmap(struct drm_gem_object *obj, + struct vm_area_struct *vma); + +#define DRM_GEM_VRAM_DRIVER_PRIME \ + .gem_prime_export = drm_gem_prime_export, \ + .gem_prime_import = drm_gem_prime_import, \ + .gem_prime_pin = drm_gem_vram_driver_gem_prime_pin, \ + .gem_prime_unpin = drm_gem_vram_driver_gem_prime_unpin, \ + .gem_prime_vmap = drm_gem_vram_driver_gem_prime_vmap, \ + .gem_prime_vunmap = drm_gem_vram_driver_gem_prime_vunmap, \ + .gem_prime_mmap = drm_gem_vram_driver_gem_prime_mmap #endif -- cgit v1.2.3 From 96352eca5c7c36c3e0eb97a6d68fb170ce643ea5 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 8 May 2019 10:26:16 +0200 Subject: drm: Add VRAM MM, a simple memory manager for dedicated VRAM The VRAM MM memory manager is a helper library that manages dedicated video memory of simple framebuffer devices. It is supported to be used with struct drm_gem_vram_object, but does not depend on it. The implementation is based on the respective code from ast, bochs, and mgag200. These drivers share the exact same implementation except for type names. The helpers are currently build with TTM. This may change in future revisions. v4: * cleanups from checkpatch.pl v2: * renamed to struct drm_vram_mm * add drm_vram_mm_mmap() helper * documentation fixes Signed-off-by: Thomas Zimmermann Link: http://patchwork.freedesktop.org/patch/msgid/20190508082630.15116-7-tzimmermann@suse.de Signed-off-by: Gerd Hoffmann --- Documentation/gpu/drm-mm.rst | 13 ++- drivers/gpu/drm/Makefile | 3 +- drivers/gpu/drm/drm_vram_mm_helper.c | 210 +++++++++++++++++++++++++++++++++++ include/drm/drm_vram_mm_helper.h | 69 ++++++++++++ 4 files changed, 293 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/drm_vram_mm_helper.c create mode 100644 include/drm/drm_vram_mm_helper.h (limited to 'include/drm') diff --git a/Documentation/gpu/drm-mm.rst b/Documentation/gpu/drm-mm.rst index a3b685089512..eba50afbda42 100644 --- a/Documentation/gpu/drm-mm.rst +++ b/Documentation/gpu/drm-mm.rst @@ -79,7 +79,6 @@ count for the TTM, which will call your initialization function. See the radeon_ttm.c file for an example of usage. - The Graphics Execution Manager (GEM) ==================================== @@ -395,6 +394,18 @@ GEM VRAM Helper Functions Reference .. kernel-doc:: drivers/gpu/drm/drm_gem_vram_helper.c :export: +VRAM MM Helper Functions Reference +---------------------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_vram_mm_helper.c + :doc: overview + +.. kernel-doc:: include/drm/drm_vram_mm_helper.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_vram_mm_helper.c + :export: + VMA Offset Manager ================== diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index ed49b2480766..4c3dc4268b65 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -33,7 +33,8 @@ drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o drm_vram_helper-y := drm_gem_vram_helper.o \ - drm_vram_helper_common.o + drm_vram_helper_common.o \ + drm_vram_mm_helper.o obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_dsc.o drm_probe_helper.o \ diff --git a/drivers/gpu/drm/drm_vram_mm_helper.c b/drivers/gpu/drm/drm_vram_mm_helper.c new file mode 100644 index 000000000000..d17c5169b018 --- /dev/null +++ b/drivers/gpu/drm/drm_vram_mm_helper.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include + +/** + * DOC: overview + * + * The data structure &struct drm_vram_mm and its helpers implement a memory + * manager for simple framebuffer devices with dedicated video memory. Buffer + * objects are either placed in video RAM or evicted to system memory. These + * helper functions work well with &struct drm_gem_vram_object. + */ + +/* + * TTM TT + */ + +static void backend_func_destroy(struct ttm_tt *tt) +{ + ttm_tt_fini(tt); + kfree(tt); +} + +static struct ttm_backend_func backend_func = { + .destroy = backend_func_destroy +}; + +/* + * TTM BO device + */ + +static struct ttm_tt *bo_driver_ttm_tt_create(struct ttm_buffer_object *bo, + uint32_t page_flags) +{ + struct ttm_tt *tt; + int ret; + + tt = kzalloc(sizeof(*tt), GFP_KERNEL); + if (!tt) + return NULL; + + tt->func = &backend_func; + + ret = ttm_tt_init(tt, bo, page_flags); + if (ret < 0) + goto err_ttm_tt_init; + + return tt; + +err_ttm_tt_init: + kfree(tt); + return NULL; +} + +static int bo_driver_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, + struct ttm_mem_type_manager *man) +{ + switch (type) { + case TTM_PL_SYSTEM: + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_VRAM: + man->func = &ttm_bo_manager_func; + man->flags = TTM_MEMTYPE_FLAG_FIXED | + TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + break; + default: + return -EINVAL; + } + return 0; +} + +static void bo_driver_evict_flags(struct ttm_buffer_object *bo, + struct ttm_placement *placement) +{ + struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bo->bdev); + + if (vmm->funcs && vmm->funcs->evict_flags) + vmm->funcs->evict_flags(bo, placement); +} + +static int bo_driver_verify_access(struct ttm_buffer_object *bo, + struct file *filp) +{ + struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bo->bdev); + + if (!vmm->funcs || !vmm->funcs->verify_access) + return 0; + return vmm->funcs->verify_access(bo, filp); +} + +static int bo_driver_io_mem_reserve(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = bdev->man + mem->mem_type; + struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bdev); + + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) + return -EINVAL; + + mem->bus.addr = NULL; + mem->bus.size = mem->num_pages << PAGE_SHIFT; + + switch (mem->mem_type) { + case TTM_PL_SYSTEM: /* nothing to do */ + mem->bus.offset = 0; + mem->bus.base = 0; + mem->bus.is_iomem = false; + break; + case TTM_PL_VRAM: + mem->bus.offset = mem->start << PAGE_SHIFT; + mem->bus.base = vmm->vram_base; + mem->bus.is_iomem = true; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void bo_driver_io_mem_free(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ } + +static struct ttm_bo_driver bo_driver = { + .ttm_tt_create = bo_driver_ttm_tt_create, + .ttm_tt_populate = ttm_pool_populate, + .ttm_tt_unpopulate = ttm_pool_unpopulate, + .init_mem_type = bo_driver_init_mem_type, + .eviction_valuable = ttm_bo_eviction_valuable, + .evict_flags = bo_driver_evict_flags, + .verify_access = bo_driver_verify_access, + .io_mem_reserve = bo_driver_io_mem_reserve, + .io_mem_free = bo_driver_io_mem_free, +}; + +/* + * struct drm_vram_mm + */ + +/** + * drm_vram_mm_init() - Initialize an instance of VRAM MM. + * @vmm: the VRAM MM instance to initialize + * @dev: the DRM device + * @vram_base: the base address of the video memory + * @vram_size: the size of the video memory in bytes + * @funcs: callback functions for buffer objects + * + * Returns: + * 0 on success, or + * a negative error code otherwise. + */ +int drm_vram_mm_init(struct drm_vram_mm *vmm, struct drm_device *dev, + uint64_t vram_base, size_t vram_size, + const struct drm_vram_mm_funcs *funcs) +{ + int ret; + + vmm->vram_base = vram_base; + vmm->vram_size = vram_size; + vmm->funcs = funcs; + + ret = ttm_bo_device_init(&vmm->bdev, &bo_driver, + dev->anon_inode->i_mapping, + true); + if (ret) + return ret; + + ret = ttm_bo_init_mm(&vmm->bdev, TTM_PL_VRAM, vram_size >> PAGE_SHIFT); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(drm_vram_mm_init); + +/** + * drm_vram_mm_cleanup() - Cleans up an initialized instance of VRAM MM. + * @vmm: the VRAM MM instance to clean up + */ +void drm_vram_mm_cleanup(struct drm_vram_mm *vmm) +{ + ttm_bo_device_release(&vmm->bdev); +} +EXPORT_SYMBOL(drm_vram_mm_cleanup); + +/** + * drm_vram_mm_mmap() - Helper for implementing &struct file_operations.mmap() + * @filp: the mapping's file structure + * @vma: the mapping's memory area + * @vmm: the VRAM MM instance + * + * Returns: + * 0 on success, or + * a negative error code otherwise. + */ +int drm_vram_mm_mmap(struct file *filp, struct vm_area_struct *vma, + struct drm_vram_mm *vmm) +{ + return ttm_bo_mmap(filp, vma, &vmm->bdev); +} +EXPORT_SYMBOL(drm_vram_mm_mmap); diff --git a/include/drm/drm_vram_mm_helper.h b/include/drm/drm_vram_mm_helper.h new file mode 100644 index 000000000000..5d45c6447fa4 --- /dev/null +++ b/include/drm/drm_vram_mm_helper.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef DRM_VRAM_MM_HELPER_H +#define DRM_VRAM_MM_HELPER_H + +#include + +struct drm_device; + +/** + * struct drm_vram_mm_funcs - Callback functions for &struct drm_vram_mm + * @evict_flags: Provides an implementation for struct \ + &ttm_bo_driver.evict_flags + * @verify_access: Provides an implementation for \ + struct &ttm_bo_driver.verify_access + * + * These callback function integrate VRAM MM with TTM buffer objects. New + * functions can be added if necessary. + */ +struct drm_vram_mm_funcs { + void (*evict_flags)(struct ttm_buffer_object *bo, + struct ttm_placement *placement); + int (*verify_access)(struct ttm_buffer_object *bo, struct file *filp); +}; + +/** + * struct drm_vram_mm - An instance of VRAM MM + * @vram_base: Base address of the managed video memory + * @vram_size: Size of the managed video memory in bytes + * @bdev: The TTM BO device. + * @funcs: TTM BO functions + * + * The fields &struct drm_vram_mm.vram_base and + * &struct drm_vram_mm.vrm_size are managed by VRAM MM, but are + * available for public read access. Use the field + * &struct drm_vram_mm.bdev to access the TTM BO device. + */ +struct drm_vram_mm { + uint64_t vram_base; + size_t vram_size; + + struct ttm_bo_device bdev; + + const struct drm_vram_mm_funcs *funcs; +}; + +/** + * drm_vram_mm_of_bdev() - \ + Returns the container of type &struct ttm_bo_device for field bdev. + * @bdev: the TTM BO device + * + * Returns: + * The containing instance of &struct drm_vram_mm + */ +static inline struct drm_vram_mm *drm_vram_mm_of_bdev( + struct ttm_bo_device *bdev) +{ + return container_of(bdev, struct drm_vram_mm, bdev); +} + +int drm_vram_mm_init(struct drm_vram_mm *vmm, struct drm_device *dev, + uint64_t vram_base, size_t vram_size, + const struct drm_vram_mm_funcs *funcs); +void drm_vram_mm_cleanup(struct drm_vram_mm *vmm); + +int drm_vram_mm_mmap(struct file *filp, struct vm_area_struct *vma, + struct drm_vram_mm *vmm); + +#endif -- cgit v1.2.3 From 5c9dcacfe56673555540933017c54e8f39e947cb Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 8 May 2019 10:26:17 +0200 Subject: drm: Add default instance for VRAM MM callback functions VRAM MM is most likely be used with GEM VRAM. The latter now provides the required instance of struct drm_vram_mm_funcs for drivers to use. Signed-off-by: Thomas Zimmermann Link: http://patchwork.freedesktop.org/patch/msgid/20190508082630.15116-8-tzimmermann@suse.de Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/drm_gem_vram_helper.c | 14 ++++++++++++++ include/drm/drm_gem_vram_helper.h | 3 +++ 2 files changed, 17 insertions(+) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index 15c6193e4985..c21b1f920e0a 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -3,6 +3,7 @@ #include #include #include +#include #include /** @@ -524,6 +525,19 @@ int drm_gem_vram_bo_driver_verify_access(struct ttm_buffer_object *bo, } EXPORT_SYMBOL(drm_gem_vram_bo_driver_verify_access); +/** + * drm_gem_vram_mm_funcs - Functions for &struct drm_vram_mm + * + * Most users of @struct drm_gem_vram_object will also use + * @struct drm_vram_mm. This instance of &struct drm_vram_mm_funcs + * can be used to connect both. + */ +const struct drm_vram_mm_funcs drm_gem_vram_mm_funcs = { + .evict_flags = drm_gem_vram_bo_driver_evict_flags, + .verify_access = drm_gem_vram_bo_driver_verify_access +}; +EXPORT_SYMBOL(drm_gem_vram_mm_funcs); + /* * Helpers for struct drm_driver */ diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index 9a8bd98b623f..7971656afe87 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -9,6 +9,7 @@ #include /* for container_of() */ struct drm_mode_create_dumb; +struct drm_vram_mm_funcs; struct filp; #define DRM_GEM_VRAM_PL_FLAG_VRAM TTM_PL_FLAG_VRAM @@ -107,6 +108,8 @@ void drm_gem_vram_bo_driver_evict_flags(struct ttm_buffer_object *bo, int drm_gem_vram_bo_driver_verify_access(struct ttm_buffer_object *bo, struct file *filp); +extern const struct drm_vram_mm_funcs drm_gem_vram_mm_funcs; + /* * Helpers for struct drm_driver */ -- cgit v1.2.3 From 59f5989ad42b6edd089b47895986ef15259822dc Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 8 May 2019 10:26:18 +0200 Subject: drm: Integrate VRAM MM into struct drm_device There's now a pointer to struct drm_vram_mm stored in struct drm_device. DRM drivers that use VRAM MM should use this field to refer to their instance of the data structure. Appropriate helpers are now provided as well. Adding struct drm_vram_mm to struct drm_device further avoids wrappers and boilerplate code in drivers. This patch implements default functions for callbacks in struct drm_driver and struct file_operations that use the struct drm_vram_mm stored in struct drm_device. Drivers that need to provide their own implementations can still do so. The patch also adds documentation for the VRAM helper library in general. v5: * set .llseek to no_llseek() from DRM_VRAM_MM_FILE_OPERATIONS v4: * cleanups from checkpatch.pl * document VRAM helper library Signed-off-by: Thomas Zimmermann Link: http://patchwork.freedesktop.org/patch/msgid/20190508082630.15116-9-tzimmermann@suse.de Signed-off-by: Gerd Hoffmann --- Documentation/gpu/drm-mm.rst | 6 +++ drivers/gpu/drm/drm_gem_vram_helper.c | 28 ++++++++++ drivers/gpu/drm/drm_vram_helper_common.c | 92 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_vram_mm_helper.c | 85 +++++++++++++++++++++++++++++ include/drm/drm_device.h | 4 ++ include/drm/drm_gem_vram_helper.h | 19 ++++++- include/drm/drm_vram_mm_helper.h | 33 ++++++++++++ 7 files changed, 266 insertions(+), 1 deletion(-) (limited to 'include/drm') diff --git a/Documentation/gpu/drm-mm.rst b/Documentation/gpu/drm-mm.rst index eba50afbda42..c8ebd4f66a6a 100644 --- a/Documentation/gpu/drm-mm.rst +++ b/Documentation/gpu/drm-mm.rst @@ -382,6 +382,12 @@ GEM CMA Helper Functions Reference VRAM Helper Function Reference ============================== +.. kernel-doc:: drivers/gpu/drm/drm_vram_helper_common.c + :doc: overview + +.. kernel-doc:: include/drm/drm_gem_vram_helper.h + :internal: + GEM VRAM Helper Functions Reference ----------------------------------- diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index c21b1f920e0a..8f142b810eb4 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include @@ -555,6 +556,33 @@ void drm_gem_vram_driver_gem_free_object_unlocked(struct drm_gem_object *gem) } EXPORT_SYMBOL(drm_gem_vram_driver_gem_free_object_unlocked); +/** + * drm_gem_vram_driver_create_dumb() - \ + Implements &struct drm_driver.dumb_create + * @file: the DRM file + * @dev: the DRM device + * @args: the arguments as provided to \ + &struct drm_driver.dumb_create + * + * This function requires the driver to use @drm_device.vram_mm for its + * instance of VRAM MM. + * + * Returns: + * 0 on success, or + * a negative error code otherwise. + */ +int drm_gem_vram_driver_dumb_create(struct drm_file *file, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + if (WARN_ONCE(!dev->vram_mm, "VRAM MM not initialized")) + return -EINVAL; + + return drm_gem_vram_fill_create_dumb(file, dev, &dev->vram_mm->bdev, 0, + false, args); +} +EXPORT_SYMBOL(drm_gem_vram_driver_dumb_create); + /** * drm_gem_vram_driver_dumb_mmap_offset() - \ Implements &struct drm_driver.dumb_mmap_offset diff --git a/drivers/gpu/drm/drm_vram_helper_common.c b/drivers/gpu/drm/drm_vram_helper_common.c index 7c25daca18d0..3b47f7002115 100644 --- a/drivers/gpu/drm/drm_vram_helper_common.c +++ b/drivers/gpu/drm/drm_vram_helper_common.c @@ -2,5 +2,97 @@ #include +/** + * DOC: overview + * + * This library provides &struct drm_gem_vram_object (GEM VRAM), a GEM + * buffer object that is backed by video RAM. It can be used for + * framebuffer devices with dedicated memory. The video RAM can be + * managed with &struct drm_vram_mm (VRAM MM). Both data structures are + * supposed to be used together, but can also be used individually. + * + * With the GEM interface userspace applications create, manage and destroy + * graphics buffers, such as an on-screen framebuffer. GEM does not provide + * an implementation of these interfaces. It's up to the DRM driver to + * provide an implementation that suits the hardware. If the hardware device + * contains dedicated video memory, the DRM driver can use the VRAM helper + * library. Each active buffer object is stored in video RAM. Active + * buffer are used for drawing the current frame, typically something like + * the frame's scanout buffer or the cursor image. If there's no more space + * left in VRAM, inactive GEM objects can be moved to system memory. + * + * The easiest way to use the VRAM helper library is to call + * drm_vram_helper_alloc_mm(). The function allocates and initializes an + * instance of &struct drm_vram_mm in &struct drm_device.vram_mm . Use + * &DRM_GEM_VRAM_DRIVER to initialize &struct drm_driver and + * &DRM_VRAM_MM_FILE_OPERATIONS to initialize &struct file_operations; + * as illustrated below. + * + * .. code-block:: c + * + * struct file_operations fops ={ + * .owner = THIS_MODULE, + * DRM_VRAM_MM_FILE_OPERATION + * }; + * struct drm_driver drv = { + * .driver_feature = DRM_ ... , + * .fops = &fops, + * DRM_GEM_VRAM_DRIVER + * }; + * + * int init_drm_driver() + * { + * struct drm_device *dev; + * uint64_t vram_base; + * unsigned long vram_size; + * int ret; + * + * // setup device, vram base and size + * // ... + * + * ret = drm_vram_helper_alloc_mm(dev, vram_base, vram_size, + * &drm_gem_vram_mm_funcs); + * if (ret) + * return ret; + * return 0; + * } + * + * This creates an instance of &struct drm_vram_mm, exports DRM userspace + * interfaces for GEM buffer management and initializes file operations to + * allow for accessing created GEM buffers. With this setup, the DRM driver + * manages an area of video RAM with VRAM MM and provides GEM VRAM objects + * to userspace. + * + * To clean up the VRAM memory management, call drm_vram_helper_release_mm() + * in the driver's clean-up code. + * + * .. code-block:: c + * + * void fini_drm_driver() + * { + * struct drm_device *dev = ...; + * + * drm_vram_helper_release_mm(dev); + * } + * + * For drawing or scanout operations, buffer object have to be pinned in video + * RAM. Call drm_gem_vram_pin() with &DRM_GEM_VRAM_PL_FLAG_VRAM or + * &DRM_GEM_VRAM_PL_FLAG_SYSTEM to pin a buffer object in video RAM or system + * memory. Call drm_gem_vram_unpin() to release the pinned object afterwards. + * If you have to evict a buffer object from video RAM (e.g., for freeing up + * memory), unpin the buffer and call drm_gem_vram_push_to_system(). + * + * A buffer object that is pinned in video RAM has a fixed address within that + * memory region. Call drm_gem_vram_offset() to retrieve this value. Typically + * it's used to program the hardware's scanout engine for framebuffers, set + * the cursor overlay's image for a mouse cursor, or use it as input to the + * hardware's draing engine. + * + * To access a buffer object's memory from the DRM driver, call + * drm_gem_vram_kmap(). It (optionally) maps the buffer into kernel address + * space and returns the memory address. Use drm_gem_vram_kunmap() to + * release the mapping. + */ + MODULE_DESCRIPTION("DRM VRAM memory-management helpers"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/drm_vram_mm_helper.c b/drivers/gpu/drm/drm_vram_mm_helper.c index d17c5169b018..c94a6dc5ade7 100644 --- a/drivers/gpu/drm/drm_vram_mm_helper.c +++ b/drivers/gpu/drm/drm_vram_mm_helper.c @@ -208,3 +208,88 @@ int drm_vram_mm_mmap(struct file *filp, struct vm_area_struct *vma, return ttm_bo_mmap(filp, vma, &vmm->bdev); } EXPORT_SYMBOL(drm_vram_mm_mmap); + +/* + * Helpers for integration with struct drm_device + */ + +/** + * drm_vram_helper_alloc_mm - Allocates a device's instance of \ + &struct drm_vram_mm + * @dev: the DRM device + * @vram_base: the base address of the video memory + * @vram_size: the size of the video memory in bytes + * @funcs: callback functions for buffer objects + * + * Returns: + * The new instance of &struct drm_vram_mm on success, or + * an ERR_PTR()-encoded errno code otherwise. + */ +struct drm_vram_mm *drm_vram_helper_alloc_mm( + struct drm_device *dev, uint64_t vram_base, size_t vram_size, + const struct drm_vram_mm_funcs *funcs) +{ + int ret; + + if (WARN_ON(dev->vram_mm)) + return dev->vram_mm; + + dev->vram_mm = kzalloc(sizeof(*dev->vram_mm), GFP_KERNEL); + if (!dev->vram_mm) + return ERR_PTR(-ENOMEM); + + ret = drm_vram_mm_init(dev->vram_mm, dev, vram_base, vram_size, funcs); + if (ret) + goto err_kfree; + + return dev->vram_mm; + +err_kfree: + kfree(dev->vram_mm); + dev->vram_mm = NULL; + return ERR_PTR(ret); +} +EXPORT_SYMBOL(drm_vram_helper_alloc_mm); + +/** + * drm_vram_helper_release_mm - Releases a device's instance of \ + &struct drm_vram_mm + * @dev: the DRM device + */ +void drm_vram_helper_release_mm(struct drm_device *dev) +{ + if (!dev->vram_mm) + return; + + drm_vram_mm_cleanup(dev->vram_mm); + kfree(dev->vram_mm); + dev->vram_mm = NULL; +} +EXPORT_SYMBOL(drm_vram_helper_release_mm); + +/* + * Helpers for &struct file_operations + */ + +/** + * drm_vram_mm_file_operations_mmap() - \ + Implements &struct file_operations.mmap() + * @filp: the mapping's file structure + * @vma: the mapping's memory area + * + * Returns: + * 0 on success, or + * a negative error code otherwise. + */ +int drm_vram_mm_file_operations_mmap( + struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv = filp->private_data; + struct drm_device *dev = file_priv->minor->dev; + + if (WARN_ONCE(!dev->vram_mm, "VRAM MM not initialized")) + return -EINVAL; + + return drm_vram_mm_mmap(filp, vma, dev->vram_mm); +} +EXPORT_SYMBOL(drm_vram_mm_file_operations_mmap); diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index 7f9ef709b2b6..1acfc3bbd3fb 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -17,6 +17,7 @@ struct drm_vblank_crtc; struct drm_sg_mem; struct drm_local_map; struct drm_vma_offset_manager; +struct drm_vram_mm; struct drm_fb_helper; struct inode; @@ -286,6 +287,9 @@ struct drm_device { /** @vma_offset_manager: GEM information */ struct drm_vma_offset_manager *vma_offset_manager; + /** @vram_mm: VRAM MM memory manager */ + struct drm_vram_mm *vram_mm; + /** * @switch_power_state: * diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index 7971656afe87..b056f189ba62 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -11,6 +11,7 @@ struct drm_mode_create_dumb; struct drm_vram_mm_funcs; struct filp; +struct vm_area_struct; #define DRM_GEM_VRAM_PL_FLAG_VRAM TTM_PL_FLAG_VRAM #define DRM_GEM_VRAM_PL_FLAG_SYSTEM TTM_PL_FLAG_SYSTEM @@ -115,10 +116,26 @@ extern const struct drm_vram_mm_funcs drm_gem_vram_mm_funcs; */ void drm_gem_vram_driver_gem_free_object_unlocked(struct drm_gem_object *gem); - +int drm_gem_vram_driver_dumb_create(struct drm_file *file, + struct drm_device *dev, + struct drm_mode_create_dumb *args); int drm_gem_vram_driver_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev, uint32_t handle, uint64_t *offset); + +/** + * define DRM_GEM_VRAM_DRIVER - default callback functions for \ + &struct drm_driver + * + * Drivers that use VRAM MM and GEM VRAM can use this macro to initialize + * &struct drm_driver with default functions. + */ +#define DRM_GEM_VRAM_DRIVER \ + .gem_free_object_unlocked = \ + drm_gem_vram_driver_gem_free_object_unlocked, \ + .dumb_create = drm_gem_vram_driver_dumb_create, \ + .dumb_map_offset = drm_gem_vram_driver_dumb_mmap_offset + /* * PRIME helpers for struct drm_driver */ diff --git a/include/drm/drm_vram_mm_helper.h b/include/drm/drm_vram_mm_helper.h index 5d45c6447fa4..a8ffd8599b08 100644 --- a/include/drm/drm_vram_mm_helper.h +++ b/include/drm/drm_vram_mm_helper.h @@ -66,4 +66,37 @@ void drm_vram_mm_cleanup(struct drm_vram_mm *vmm); int drm_vram_mm_mmap(struct file *filp, struct vm_area_struct *vma, struct drm_vram_mm *vmm); +/* + * Helpers for integration with struct drm_device + */ + +struct drm_vram_mm *drm_vram_helper_alloc_mm( + struct drm_device *dev, uint64_t vram_base, size_t vram_size, + const struct drm_vram_mm_funcs *funcs); +void drm_vram_helper_release_mm(struct drm_device *dev); + +/* + * Helpers for &struct file_operations + */ + +int drm_vram_mm_file_operations_mmap( + struct file *filp, struct vm_area_struct *vma); + +/** + * define DRM_VRAM_MM_FILE_OPERATIONS - default callback functions for \ + &struct file_operations + * + * Drivers that use VRAM MM can use this macro to initialize + * &struct file_operations with default functions. + */ +#define DRM_VRAM_MM_FILE_OPERATIONS \ + .llseek = no_llseek, \ + .read = drm_read, \ + .poll = drm_poll, \ + .unlocked_ioctl = drm_ioctl, \ + .compat_ioctl = drm_compat_ioctl, \ + .mmap = drm_vram_mm_file_operations_mmap, \ + .open = drm_open, \ + .release = drm_release \ + #endif -- cgit v1.2.3 From 82ff2fb5d184e95c7877c58359cef4f5d43df9c1 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 16 May 2019 18:27:45 +0200 Subject: drm: Add drm_gem_vram_{pin/unpin}_reserved() and convert mgag200 The new interfaces drm_gem_vram_{pin/unpin}_reserved() are variants of the GEM VRAM pin/unpin functions that do not reserve the BO during validation. The mgag200 driver requires this behavior for its cursor handling. The patch also converts the driver to use the new interfaces. Signed-off-by: Thomas Zimmermann Link: http://patchwork.freedesktop.org/patch/msgid/20190516162746.11636-2-tzimmermann@suse.de Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/drm_gem_vram_helper.c | 75 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/mgag200/mgag200_cursor.c | 18 ++++---- include/drm/drm_gem_vram_helper.h | 3 ++ 3 files changed, 88 insertions(+), 8 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index 8f142b810eb4..a002c03eaf4c 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -254,6 +254,47 @@ int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag) } EXPORT_SYMBOL(drm_gem_vram_pin); +/** + * drm_gem_vram_pin_reserved() - Pins a GEM VRAM object in a region. + * @gbo: the GEM VRAM object + * @pl_flag: a bitmask of possible memory regions + * + * Pinning a buffer object ensures that it is not evicted from + * a memory region. A pinned buffer object has to be unpinned before + * it can be pinned to another region. + * + * This function pins a GEM VRAM object that has already been + * reserved. Use drm_gem_vram_pin() if possible. + * + * Returns: + * 0 on success, or + * a negative error code otherwise. + */ +int drm_gem_vram_pin_reserved(struct drm_gem_vram_object *gbo, + unsigned long pl_flag) +{ + int i, ret; + struct ttm_operation_ctx ctx = { false, false }; + + if (gbo->pin_count) { + ++gbo->pin_count; + return 0; + } + + drm_gem_vram_placement(gbo, pl_flag); + for (i = 0; i < gbo->placement.num_placement; ++i) + gbo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx); + if (ret < 0) + return ret; + + gbo->pin_count = 1; + + return 0; +} +EXPORT_SYMBOL(drm_gem_vram_pin_reserved); + /** * drm_gem_vram_unpin() - Unpins a GEM VRAM object * @gbo: the GEM VRAM object @@ -285,6 +326,40 @@ int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo) } EXPORT_SYMBOL(drm_gem_vram_unpin); +/** + * drm_gem_vram_unpin_reserved() - Unpins a GEM VRAM object + * @gbo: the GEM VRAM object + * + * This function unpins a GEM VRAM object that has already been + * reserved. Use drm_gem_vram_unpin() if possible. + * + * Returns: + * 0 on success, or + * a negative error code otherwise. + */ +int drm_gem_vram_unpin_reserved(struct drm_gem_vram_object *gbo) +{ + int i, ret; + struct ttm_operation_ctx ctx = { false, false }; + + if (WARN_ON_ONCE(!gbo->pin_count)) + return 0; + + --gbo->pin_count; + if (gbo->pin_count) + return 0; + + for (i = 0; i < gbo->placement.num_placement ; ++i) + gbo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(drm_gem_vram_unpin_reserved); + /** * drm_gem_vram_push_to_system() - \ Unpins a GEM VRAM object and moves it to system memory diff --git a/drivers/gpu/drm/mgag200/mgag200_cursor.c b/drivers/gpu/drm/mgag200/mgag200_cursor.c index 6c1a9d724d85..1c4fc85315a0 100644 --- a/drivers/gpu/drm/mgag200/mgag200_cursor.c +++ b/drivers/gpu/drm/mgag200/mgag200_cursor.c @@ -23,9 +23,9 @@ static void mga_hide_cursor(struct mga_device *mdev) WREG8(MGA_CURPOSXL, 0); WREG8(MGA_CURPOSXH, 0); if (mdev->cursor.pixels_1->pin_count) - drm_gem_vram_unpin(mdev->cursor.pixels_1); + drm_gem_vram_unpin_reserved(mdev->cursor.pixels_1); if (mdev->cursor.pixels_2->pin_count) - drm_gem_vram_unpin(mdev->cursor.pixels_2); + drm_gem_vram_unpin_reserved(mdev->cursor.pixels_2); } int mga_crtc_cursor_set(struct drm_crtc *crtc, @@ -96,26 +96,28 @@ int mga_crtc_cursor_set(struct drm_crtc *crtc, /* Move cursor buffers into VRAM if they aren't already */ if (!pixels_1->pin_count) { - ret = drm_gem_vram_pin(pixels_1, DRM_GEM_VRAM_PL_FLAG_VRAM); + ret = drm_gem_vram_pin_reserved(pixels_1, + DRM_GEM_VRAM_PL_FLAG_VRAM); if (ret) goto out1; gpu_addr = drm_gem_vram_offset(pixels_1); if (gpu_addr < 0) { - drm_gem_vram_unpin(pixels_1); + drm_gem_vram_unpin_reserved(pixels_1); goto out1; } mdev->cursor.pixels_1_gpu_addr = gpu_addr; } if (!pixels_2->pin_count) { - ret = drm_gem_vram_pin(pixels_2, DRM_GEM_VRAM_PL_FLAG_VRAM); + ret = drm_gem_vram_pin_reserved(pixels_2, + DRM_GEM_VRAM_PL_FLAG_VRAM); if (ret) { - drm_gem_vram_unpin(pixels_1); + drm_gem_vram_unpin_reserved(pixels_1); goto out1; } gpu_addr = drm_gem_vram_offset(pixels_2); if (gpu_addr < 0) { - drm_gem_vram_unpin(pixels_1); - drm_gem_vram_unpin(pixels_2); + drm_gem_vram_unpin_reserved(pixels_1); + drm_gem_vram_unpin_reserved(pixels_2); goto out1; } mdev->cursor.pixels_2_gpu_addr = gpu_addr; diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index b056f189ba62..ff1a81761543 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -82,7 +82,10 @@ void drm_gem_vram_unreserve(struct drm_gem_vram_object *gbo); u64 drm_gem_vram_mmap_offset(struct drm_gem_vram_object *gbo); s64 drm_gem_vram_offset(struct drm_gem_vram_object *gbo); int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag); +int drm_gem_vram_pin_reserved(struct drm_gem_vram_object *gbo, + unsigned long pl_flag); int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo); +int drm_gem_vram_unpin_reserved(struct drm_gem_vram_object *gbo); int drm_gem_vram_push_to_system(struct drm_gem_vram_object *gbo); void *drm_gem_vram_kmap_at(struct drm_gem_vram_object *gbo, bool map, bool *is_iomem, struct ttm_bo_kmap_obj *kmap); -- cgit v1.2.3 From 05c452c115bffa12f78346723f0282a4264ed200 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 16 May 2019 12:31:47 +0200 Subject: drm: Remove users of drm_format_num_planes drm_format_num_planes() is basically a lookup in the drm_format_info table plus an access to the num_planes field of the appropriate entry. Most drivers are using this function while having access to the entry already, which means that we will perform an unnecessary lookup. Removing the call to drm_format_num_planes is therefore more efficient. Some drivers will not have access to that entry in the function, but in this case the overhead is minimal (we just have to call drm_format_info() to perform the lookup) and we can even avoid multiple, inefficient lookups in some places that need multiple fields from the drm_format_info structure. Reviewed-by: Emil Velikov Reviewed-by: Paul Kocialkowski Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/5ffcec9d14a50ed538e37d565f546802452ee672.1558002671.git-series.maxime.ripard@bootlin.com --- drivers/gpu/drm/arm/malidp_mw.c | 2 +- drivers/gpu/drm/armada/armada_fb.c | 3 ++- drivers/gpu/drm/drm_fourcc.c | 16 ---------------- drivers/gpu/drm/mediatek/mtk_drm_fb.c | 6 ++++-- drivers/gpu/drm/meson/meson_overlay.c | 2 +- drivers/gpu/drm/msm/disp/dpu1/dpu_formats.c | 9 ++++++--- drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c | 3 ++- drivers/gpu/drm/msm/msm_fb.c | 8 ++++++-- drivers/gpu/drm/omapdrm/omap_fb.c | 4 +++- drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 6 +++--- drivers/gpu/drm/tegra/fb.c | 3 ++- drivers/gpu/drm/vc4/vc4_plane.c | 2 +- drivers/gpu/drm/zte/zx_plane.c | 4 +--- include/drm/drm_fourcc.h | 1 - 14 files changed, 32 insertions(+), 37 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c index 5f102bdaf841..2e812525025d 100644 --- a/drivers/gpu/drm/arm/malidp_mw.c +++ b/drivers/gpu/drm/arm/malidp_mw.c @@ -158,7 +158,7 @@ malidp_mw_encoder_atomic_check(struct drm_encoder *encoder, return -EINVAL; } - n_planes = drm_format_num_planes(fb->format->format); + n_planes = fb->format->num_planes; for (i = 0; i < n_planes; i++) { struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, i); /* memory write buffers are never rotated */ diff --git a/drivers/gpu/drm/armada/armada_fb.c b/drivers/gpu/drm/armada/armada_fb.c index 058ac7d9920f..a2f6472eb482 100644 --- a/drivers/gpu/drm/armada/armada_fb.c +++ b/drivers/gpu/drm/armada/armada_fb.c @@ -87,6 +87,7 @@ struct armada_framebuffer *armada_framebuffer_create(struct drm_device *dev, struct drm_framebuffer *armada_fb_create(struct drm_device *dev, struct drm_file *dfile, const struct drm_mode_fb_cmd2 *mode) { + const struct drm_format_info *info = drm_get_format_info(dev, mode); struct armada_gem_object *obj; struct armada_framebuffer *dfb; int ret; @@ -97,7 +98,7 @@ struct drm_framebuffer *armada_fb_create(struct drm_device *dev, mode->pitches[2]); /* We can only handle a single plane at the moment */ - if (drm_format_num_planes(mode->pixel_format) > 1 && + if (info->num_planes > 1 && (mode->handles[0] != mode->handles[1] || mode->handles[0] != mode->handles[2])) { ret = -EINVAL; diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index 6ea55fb4526d..873c0001d8c8 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -332,22 +332,6 @@ drm_get_format_info(struct drm_device *dev, } EXPORT_SYMBOL(drm_get_format_info); -/** - * drm_format_num_planes - get the number of planes for format - * @format: pixel format (DRM_FORMAT_*) - * - * Returns: - * The number of planes used by the specified pixel format. - */ -int drm_format_num_planes(uint32_t format) -{ - const struct drm_format_info *info; - - info = drm_format_info(format); - return info ? info->num_planes : 1; -} -EXPORT_SYMBOL(drm_format_num_planes); - /** * drm_format_plane_cpp - determine the bytes per pixel value * @format: pixel format (DRM_FORMAT_*) diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.c b/drivers/gpu/drm/mediatek/mtk_drm_fb.c index e20fcaef2851..68fdef8b12bd 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_fb.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.c @@ -32,10 +32,11 @@ static struct drm_framebuffer *mtk_drm_framebuffer_init(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode, struct drm_gem_object *obj) { + const struct drm_format_info *info = drm_get_format_info(dev, mode); struct drm_framebuffer *fb; int ret; - if (drm_format_num_planes(mode->pixel_format) != 1) + if (info->num_planes != 1) return ERR_PTR(-EINVAL); fb = kzalloc(sizeof(*fb), GFP_KERNEL); @@ -88,6 +89,7 @@ struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev, struct drm_file *file, const struct drm_mode_fb_cmd2 *cmd) { + const struct drm_format_info *info = drm_get_format_info(dev, cmd); struct drm_framebuffer *fb; struct drm_gem_object *gem; unsigned int width = cmd->width; @@ -95,7 +97,7 @@ struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev, unsigned int size, bpp; int ret; - if (drm_format_num_planes(cmd->pixel_format) != 1) + if (info->num_planes != 1) return ERR_PTR(-EINVAL); gem = drm_gem_object_lookup(file, cmd->handles[0]); diff --git a/drivers/gpu/drm/meson/meson_overlay.c b/drivers/gpu/drm/meson/meson_overlay.c index bdbf925ff3e8..fb8515b2860c 100644 --- a/drivers/gpu/drm/meson/meson_overlay.c +++ b/drivers/gpu/drm/meson/meson_overlay.c @@ -458,7 +458,7 @@ static void meson_overlay_atomic_update(struct drm_plane *plane, } /* Update Canvas with buffer address */ - priv->viu.vd1_planes = drm_format_num_planes(fb->format->format); + priv->viu.vd1_planes = fb->format->num_planes; switch (priv->viu.vd1_planes) { case 3: diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_formats.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_formats.c index f59fe1a9f4b9..c3d491e8d44b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_formats.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_formats.c @@ -1040,10 +1040,11 @@ int dpu_format_check_modified_format( const struct drm_mode_fb_cmd2 *cmd, struct drm_gem_object **bos) { - int ret, i, num_base_fmt_planes; + const struct drm_format_info *info; const struct dpu_format *fmt; struct dpu_hw_fmt_layout layout; uint32_t bos_total_size = 0; + int ret, i; if (!msm_fmt || !cmd || !bos) { DRM_ERROR("invalid arguments\n"); @@ -1051,14 +1052,16 @@ int dpu_format_check_modified_format( } fmt = to_dpu_format(msm_fmt); - num_base_fmt_planes = drm_format_num_planes(fmt->base.pixel_format); + info = drm_format_info(fmt->base.pixel_format); + if (!info) + return -EINVAL; ret = dpu_format_get_plane_sizes(fmt, cmd->width, cmd->height, &layout, cmd->pitches); if (ret) return ret; - for (i = 0; i < num_base_fmt_planes; i++) { + for (i = 0; i < info->num_planes; i++) { if (!bos[i]) { DRM_ERROR("invalid handle for plane %d\n", i); return -EINVAL; diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c index 6153514db04c..72ab8d89efa4 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c @@ -127,13 +127,14 @@ uint32_t mdp5_smp_calculate(struct mdp5_smp *smp, const struct mdp_format *format, u32 width, bool hdecim) { + const struct drm_format_info *info = drm_format_info(format->base.pixel_format); struct mdp5_kms *mdp5_kms = get_kms(smp); int rev = mdp5_cfg_get_hw_rev(mdp5_kms->cfg); int i, hsub, nplanes, nlines; u32 fmt = format->base.pixel_format; uint32_t blkcfg = 0; - nplanes = drm_format_num_planes(fmt); + nplanes = info->num_planes; hsub = drm_format_horz_chroma_subsampling(fmt); /* different if BWC (compressed framebuffer?) enabled: */ diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c index 136058978e0f..432beddafb9e 100644 --- a/drivers/gpu/drm/msm/msm_fb.c +++ b/drivers/gpu/drm/msm/msm_fb.c @@ -106,9 +106,11 @@ const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb) struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev, struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd) { + const struct drm_format_info *info = drm_get_format_info(dev, + mode_cmd); struct drm_gem_object *bos[4] = {0}; struct drm_framebuffer *fb; - int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format); + int ret, i, n = info->num_planes; for (i = 0; i < n; i++) { bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); @@ -135,6 +137,8 @@ out_unref: static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos) { + const struct drm_format_info *info = drm_get_format_info(dev, + mode_cmd); struct msm_drm_private *priv = dev->dev_private; struct msm_kms *kms = priv->kms; struct msm_framebuffer *msm_fb = NULL; @@ -147,7 +151,7 @@ static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, dev, mode_cmd, mode_cmd->width, mode_cmd->height, (char *)&mode_cmd->pixel_format); - n = drm_format_num_planes(mode_cmd->pixel_format); + n = info->num_planes; hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format); vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format); diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c index 4f8eb9d08f99..cfb641363a32 100644 --- a/drivers/gpu/drm/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -298,7 +298,9 @@ void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m) struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd) { - unsigned int num_planes = drm_format_num_planes(mode_cmd->pixel_format); + const struct drm_format_info *info = drm_get_format_info(dev, + mode_cmd); + unsigned int num_planes = info->num_planes; struct drm_gem_object *bos[4]; struct drm_framebuffer *fb; int i; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 97438bbbe389..606d176d5d96 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -74,19 +74,19 @@ static struct drm_framebuffer * rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd) { + const struct drm_format_info *info = drm_get_format_info(dev, + mode_cmd); struct drm_framebuffer *fb; struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER]; struct drm_gem_object *obj; unsigned int hsub; unsigned int vsub; - int num_planes; + int num_planes = min_t(int, info->num_planes, ROCKCHIP_MAX_FB_BUFFER); int ret; int i; hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format); vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format); - num_planes = min(drm_format_num_planes(mode_cmd->pixel_format), - ROCKCHIP_MAX_FB_BUFFER); for (i = 0; i < num_planes; i++) { unsigned int width = mode_cmd->width / (i ? hsub : 1); diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 1dd83a757dba..da0747e317b7 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -131,6 +131,7 @@ struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, struct drm_file *file, const struct drm_mode_fb_cmd2 *cmd) { + const struct drm_format_info *info = drm_get_format_info(drm, cmd); unsigned int hsub, vsub, i; struct tegra_bo *planes[4]; struct drm_gem_object *gem; @@ -140,7 +141,7 @@ struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format); vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format); - for (i = 0; i < drm_format_num_planes(cmd->pixel_format); i++) { + for (i = 0; i < info->num_planes; i++) { unsigned int width = cmd->width / (i ? hsub : 1); unsigned int height = cmd->height / (i ? vsub : 1); unsigned int size, bpp; diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 4d918d3e4858..e3c0a350cb77 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -592,7 +592,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, u32 ctl0_offset = vc4_state->dlist_count; const struct hvs_format *format = vc4_get_hvs_format(fb->format->format); u64 base_format_mod = fourcc_mod_broadcom_mod(fb->modifier); - int num_planes = drm_format_num_planes(format->drm); + int num_planes = fb->format->num_planes; u32 h_subsample, v_subsample; bool mix_plane_alpha; bool covers_screen; diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c index 83d236fd893c..c6a8be444300 100644 --- a/drivers/gpu/drm/zte/zx_plane.c +++ b/drivers/gpu/drm/zte/zx_plane.c @@ -199,7 +199,6 @@ static void zx_vl_plane_atomic_update(struct drm_plane *plane, u32 dst_x, dst_y, dst_w, dst_h; uint32_t format; int fmt; - int num_planes; int i; if (!fb) @@ -218,9 +217,8 @@ static void zx_vl_plane_atomic_update(struct drm_plane *plane, dst_h = drm_rect_height(dst); /* Set up data address registers for Y, Cb and Cr planes */ - num_planes = drm_format_num_planes(format); paddr_reg = layer + VL_Y; - for (i = 0; i < num_planes; i++) { + for (i = 0; i < fb->format->num_planes; i++) { cma_obj = drm_fb_cma_get_gem_obj(fb, i); paddr = cma_obj->paddr + fb->offsets[i]; paddr += src_y * fb->pitches[i]; diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h index b3d9d88ab290..41779b327d91 100644 --- a/include/drm/drm_fourcc.h +++ b/include/drm/drm_fourcc.h @@ -268,7 +268,6 @@ drm_get_format_info(struct drm_device *dev, uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth); uint32_t drm_driver_legacy_fb_format(struct drm_device *dev, uint32_t bpp, uint32_t depth); -int drm_format_num_planes(uint32_t format); int drm_format_plane_cpp(uint32_t format, int plane); int drm_format_horz_chroma_subsampling(uint32_t format); int drm_format_vert_chroma_subsampling(uint32_t format); -- cgit v1.2.3 From f3e9632cb6241a6c098427510ad3ee041365ae64 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 16 May 2019 12:31:48 +0200 Subject: drm: Remove users of drm_format_(horz|vert)_chroma_subsampling drm_format_horz_chroma_subsampling and drm_format_vert_chroma_subsampling are basically a lookup in the drm_format_info table plus an access to the hsub and vsub fields of the appropriate entry. Most drivers are using this function while having access to the entry already, which means that we will perform an unnecessary lookup. Removing the call to these functions is therefore more efficient. Some drivers will not have access to that entry in the function, but in this case the overhead is minimal (we just have to call drm_format_info() to perform the lookup) and we can even avoid multiple, inefficient lookups in some places that need multiple fields from the drm_format_info structure. This is amplified by the fact that most of the time the callers will have to retrieve both the vsub and hsub fields, meaning that they would perform twice the lookup. Reviewed-by: Emil Velikov Reviewed-by: Paul Kocialkowski Reviewed-by: Philipp Zabel Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/6b3cceb8161e2c1d40c2681de99202328b0a8abc.1558002671.git-series.maxime.ripard@bootlin.com --- drivers/gpu/drm/arm/malidp_planes.c | 6 ++--- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 9 ++----- drivers/gpu/drm/drm_fourcc.c | 34 ------------------------- drivers/gpu/drm/imx/ipuv3-plane.c | 15 +++++------ drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 9 ++----- drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c | 24 ++++++++--------- drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c | 2 +- drivers/gpu/drm/msm/msm_fb.c | 8 ++---- drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 9 ++----- drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 10 +++----- drivers/gpu/drm/tegra/fb.c | 9 +++---- drivers/gpu/drm/vc4/vc4_plane.c | 13 +++------- include/drm/drm_fourcc.h | 2 -- 13 files changed, 38 insertions(+), 112 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index d42e0ea9a303..8f89813d08c1 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -233,8 +233,7 @@ bool malidp_format_mod_supported(struct drm_device *drm, } } - if ((drm_format_horz_chroma_subsampling(format) != 1) || - (drm_format_vert_chroma_subsampling(format) != 1)) { + if ((info->hsub != 1) || (info->vsub != 1)) { if (!(format == DRM_FORMAT_YUV420_10BIT && (map->features & MALIDP_DEVICE_AFBC_YUV_420_10_SUPPORT_SPLIT))) { DRM_DEBUG_KMS("Formats which are sub-sampled should never be split\n"); @@ -244,8 +243,7 @@ bool malidp_format_mod_supported(struct drm_device *drm, } if (modifier & AFBC_CBR) { - if ((drm_format_horz_chroma_subsampling(format) == 1) || - (drm_format_vert_chroma_subsampling(format) == 1)) { + if ((info->hsub == 1) || (info->vsub == 1)) { DRM_DEBUG_KMS("Formats which are not sub-sampled should not have CBR set\n"); return false; } diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index e836e2de35ce..fdd607ad27fe 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -603,8 +603,6 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, const struct drm_display_mode *mode; struct drm_crtc_state *crtc_state; unsigned int tmp; - int hsub = 1; - int vsub = 1; int ret; int i; @@ -642,13 +640,10 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, if (state->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES) return -EINVAL; - hsub = drm_format_horz_chroma_subsampling(fb->format->format); - vsub = drm_format_vert_chroma_subsampling(fb->format->format); - for (i = 0; i < state->nplanes; i++) { unsigned int offset = 0; - int xdiv = i ? hsub : 1; - int ydiv = i ? vsub : 1; + int xdiv = i ? fb->format->hsub : 1; + int ydiv = i ? fb->format->vsub : 1; state->bpp[i] = fb->format->cpp[i]; if (!state->bpp[i]) diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index 873c0001d8c8..e4a2c8372c8b 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -352,40 +352,6 @@ int drm_format_plane_cpp(uint32_t format, int plane) } EXPORT_SYMBOL(drm_format_plane_cpp); -/** - * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor - * @format: pixel format (DRM_FORMAT_*) - * - * Returns: - * The horizontal chroma subsampling factor for the - * specified pixel format. - */ -int drm_format_horz_chroma_subsampling(uint32_t format) -{ - const struct drm_format_info *info; - - info = drm_format_info(format); - return info ? info->hsub : 1; -} -EXPORT_SYMBOL(drm_format_horz_chroma_subsampling); - -/** - * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor - * @format: pixel format (DRM_FORMAT_*) - * - * Returns: - * The vertical chroma subsampling factor for the - * specified pixel format. - */ -int drm_format_vert_chroma_subsampling(uint32_t format) -{ - const struct drm_format_info *info; - - info = drm_format_info(format); - return info ? info->vsub : 1; -} -EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); - /** * drm_format_plane_width - width of the plane given the first plane * @width: width of the first plane diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c index d7a727a6e3d7..8f101a0763e3 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3-plane.c @@ -115,8 +115,8 @@ drm_plane_state_to_ubo(struct drm_plane_state *state) cma_obj = drm_fb_cma_get_gem_obj(fb, 1); BUG_ON(!cma_obj); - x /= drm_format_horz_chroma_subsampling(fb->format->format); - y /= drm_format_vert_chroma_subsampling(fb->format->format); + x /= fb->format->hsub; + y /= fb->format->vsub; return cma_obj->paddr + fb->offsets[1] + fb->pitches[1] * y + fb->format->cpp[1] * x - eba; @@ -134,8 +134,8 @@ drm_plane_state_to_vbo(struct drm_plane_state *state) cma_obj = drm_fb_cma_get_gem_obj(fb, 2); BUG_ON(!cma_obj); - x /= drm_format_horz_chroma_subsampling(fb->format->format); - y /= drm_format_vert_chroma_subsampling(fb->format->format); + x /= fb->format->hsub; + y /= fb->format->vsub; return cma_obj->paddr + fb->offsets[2] + fb->pitches[2] * y + fb->format->cpp[2] * x - eba; @@ -352,7 +352,6 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, struct drm_framebuffer *old_fb = old_state->fb; unsigned long eba, ubo, vbo, old_ubo, old_vbo, alpha_eba; bool can_position = (plane->type == DRM_PLANE_TYPE_OVERLAY); - int hsub, vsub; int ret; /* Ok to disable */ @@ -471,10 +470,8 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, * The x/y offsets must be even in case of horizontal/vertical * chroma subsampling. */ - hsub = drm_format_horz_chroma_subsampling(fb->format->format); - vsub = drm_format_vert_chroma_subsampling(fb->format->format); - if (((state->src.x1 >> 16) & (hsub - 1)) || - ((state->src.y1 >> 16) & (vsub - 1))) + if (((state->src.x1 >> 16) & (fb->format->hsub - 1)) || + ((state->src.y1 >> 16) & (fb->format->vsub - 1))) return -EINVAL; break; case DRM_FORMAT_RGB565_A8: diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c index da1f727d7495..7994de952353 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c @@ -557,14 +557,9 @@ static void _dpu_plane_setup_scaler(struct dpu_plane *pdpu, struct dpu_plane_state *pstate, const struct dpu_format *fmt, bool color_fill) { - uint32_t chroma_subsmpl_h, chroma_subsmpl_v; + const struct drm_format_info *info = drm_format_info(fmt->base.pixel_format); /* don't chroma subsample if decimating */ - chroma_subsmpl_h = - drm_format_horz_chroma_subsampling(fmt->base.pixel_format); - chroma_subsmpl_v = - drm_format_vert_chroma_subsampling(fmt->base.pixel_format); - /* update scaler. calculate default config for QSEED3 */ _dpu_plane_setup_scaler3(pdpu, pstate, drm_rect_width(&pdpu->pipe_cfg.src_rect), @@ -572,7 +567,7 @@ static void _dpu_plane_setup_scaler(struct dpu_plane *pdpu, drm_rect_width(&pdpu->pipe_cfg.dst_rect), drm_rect_height(&pdpu->pipe_cfg.dst_rect), &pstate->scaler3_cfg, fmt, - chroma_subsmpl_h, chroma_subsmpl_v); + info->hsub, info->vsub); } /** diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c index be13140967b4..9d9fb6c5fd68 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c @@ -650,10 +650,10 @@ static int calc_scalex_steps(struct drm_plane *plane, uint32_t pixel_format, uint32_t src, uint32_t dest, uint32_t phasex_steps[COMP_MAX]) { + const struct drm_format_info *info = drm_format_info(pixel_format); struct mdp5_kms *mdp5_kms = get_kms(plane); struct device *dev = mdp5_kms->dev->dev; uint32_t phasex_step; - unsigned int hsub; int ret; ret = calc_phase_step(src, dest, &phasex_step); @@ -662,11 +662,9 @@ static int calc_scalex_steps(struct drm_plane *plane, return ret; } - hsub = drm_format_horz_chroma_subsampling(pixel_format); - phasex_steps[COMP_0] = phasex_step; phasex_steps[COMP_3] = phasex_step; - phasex_steps[COMP_1_2] = phasex_step / hsub; + phasex_steps[COMP_1_2] = phasex_step / info->hsub; return 0; } @@ -675,10 +673,10 @@ static int calc_scaley_steps(struct drm_plane *plane, uint32_t pixel_format, uint32_t src, uint32_t dest, uint32_t phasey_steps[COMP_MAX]) { + const struct drm_format_info *info = drm_format_info(pixel_format); struct mdp5_kms *mdp5_kms = get_kms(plane); struct device *dev = mdp5_kms->dev->dev; uint32_t phasey_step; - unsigned int vsub; int ret; ret = calc_phase_step(src, dest, &phasey_step); @@ -687,11 +685,9 @@ static int calc_scaley_steps(struct drm_plane *plane, return ret; } - vsub = drm_format_vert_chroma_subsampling(pixel_format); - phasey_steps[COMP_0] = phasey_step; phasey_steps[COMP_3] = phasey_step; - phasey_steps[COMP_1_2] = phasey_step / vsub; + phasey_steps[COMP_1_2] = phasey_step / info->vsub; return 0; } @@ -699,8 +695,9 @@ static int calc_scaley_steps(struct drm_plane *plane, static uint32_t get_scale_config(const struct mdp_format *format, uint32_t src, uint32_t dst, bool horz) { + const struct drm_format_info *info = drm_format_info(format->base.pixel_format); bool scaling = format->is_yuv ? true : (src != dst); - uint32_t sub, pix_fmt = format->base.pixel_format; + uint32_t sub; uint32_t ya_filter, uv_filter; bool yuv = format->is_yuv; @@ -708,8 +705,7 @@ static uint32_t get_scale_config(const struct mdp_format *format, return 0; if (yuv) { - sub = horz ? drm_format_horz_chroma_subsampling(pix_fmt) : - drm_format_vert_chroma_subsampling(pix_fmt); + sub = horz ? info->hsub : info->vsub; uv_filter = ((src / sub) <= dst) ? SCALE_FILTER_BIL : SCALE_FILTER_PCMN; } @@ -754,7 +750,7 @@ static void mdp5_write_pixel_ext(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe, uint32_t src_w, int pe_left[COMP_MAX], int pe_right[COMP_MAX], uint32_t src_h, int pe_top[COMP_MAX], int pe_bottom[COMP_MAX]) { - uint32_t pix_fmt = format->base.pixel_format; + const struct drm_format_info *info = drm_format_info(format->base.pixel_format); uint32_t lr, tb, req; int i; @@ -763,8 +759,8 @@ static void mdp5_write_pixel_ext(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe, uint32_t roi_h = src_h; if (format->is_yuv && i == COMP_1_2) { - roi_w /= drm_format_horz_chroma_subsampling(pix_fmt); - roi_h /= drm_format_vert_chroma_subsampling(pix_fmt); + roi_w /= info->hsub; + roi_h /= info->vsub; } lr = (pe_left[i] >= 0) ? diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c index 72ab8d89efa4..b30b2f4efc60 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c @@ -135,7 +135,7 @@ uint32_t mdp5_smp_calculate(struct mdp5_smp *smp, uint32_t blkcfg = 0; nplanes = info->num_planes; - hsub = drm_format_horz_chroma_subsampling(fmt); + hsub = info->hsub; /* different if BWC (compressed framebuffer?) enabled: */ nlines = 2; diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c index 432beddafb9e..f69c0afd6ec6 100644 --- a/drivers/gpu/drm/msm/msm_fb.c +++ b/drivers/gpu/drm/msm/msm_fb.c @@ -145,16 +145,12 @@ static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb; const struct msm_format *format; int ret, i, n; - unsigned int hsub, vsub; DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)", dev, mode_cmd, mode_cmd->width, mode_cmd->height, (char *)&mode_cmd->pixel_format); n = info->num_planes; - hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format); - vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format); - format = kms->funcs->get_format(kms, mode_cmd->pixel_format, mode_cmd->modifier[0]); if (!format) { @@ -180,8 +176,8 @@ static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, } for (i = 0; i < n; i++) { - unsigned int width = mode_cmd->width / (i ? hsub : 1); - unsigned int height = mode_cmd->height / (i ? vsub : 1); + unsigned int width = mode_cmd->width / (i ? info->hsub : 1); + unsigned int height = mode_cmd->height / (i ? info->vsub : 1); unsigned int min_size; min_size = (height - 1) * mode_cmd->pitches[i] diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 606d176d5d96..c318fae28581 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -79,18 +79,13 @@ rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, struct drm_framebuffer *fb; struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER]; struct drm_gem_object *obj; - unsigned int hsub; - unsigned int vsub; int num_planes = min_t(int, info->num_planes, ROCKCHIP_MAX_FB_BUFFER); int ret; int i; - hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format); - vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format); - for (i = 0; i < num_planes; i++) { - unsigned int width = mode_cmd->width / (i ? hsub : 1); - unsigned int height = mode_cmd->height / (i ? vsub : 1); + unsigned int width = mode_cmd->width / (i ? info->hsub : 1); + unsigned int height = mode_cmd->height / (i ? info->vsub : 1); unsigned int min_size; obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index c709f64b6f80..4189ca17f381 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -320,11 +320,9 @@ static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win, uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode; uint16_t cbcr_hor_scl_mode = SCALE_NONE; uint16_t cbcr_ver_scl_mode = SCALE_NONE; - int hsub = drm_format_horz_chroma_subsampling(info->format); - int vsub = drm_format_vert_chroma_subsampling(info->format); bool is_yuv = false; - uint16_t cbcr_src_w = src_w / hsub; - uint16_t cbcr_src_h = src_h / vsub; + uint16_t cbcr_src_w = src_w / info->hsub; + uint16_t cbcr_src_h = src_h / info->vsub; uint16_t vsu_mode; uint16_t lb_mode; uint32_t val; @@ -828,8 +826,8 @@ static void vop_plane_atomic_update(struct drm_plane *plane, (state->rotation & DRM_MODE_REFLECT_X) ? 1 : 0); if (is_yuv) { - int hsub = drm_format_horz_chroma_subsampling(fb->format->format); - int vsub = drm_format_vert_chroma_subsampling(fb->format->format); + int hsub = fb->format->hsub; + int vsub = fb->format->vsub; int bpp = fb->format->cpp[1]; uv_obj = fb->obj[1]; diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index da0747e317b7..94fb75089d87 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -132,18 +132,15 @@ struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, const struct drm_mode_fb_cmd2 *cmd) { const struct drm_format_info *info = drm_get_format_info(drm, cmd); - unsigned int hsub, vsub, i; struct tegra_bo *planes[4]; struct drm_gem_object *gem; struct drm_framebuffer *fb; + unsigned int i; int err; - hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format); - vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format); - for (i = 0; i < info->num_planes; i++) { - unsigned int width = cmd->width / (i ? hsub : 1); - unsigned int height = cmd->height / (i ? vsub : 1); + unsigned int width = cmd->width / (i ? info->hsub : 1); + unsigned int height = cmd->height / (i ? info->vsub : 1); unsigned int size, bpp; gem = drm_gem_object_lookup(file, cmd->handles[i]); diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index e3c0a350cb77..be2274924b34 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -310,10 +310,10 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) struct drm_framebuffer *fb = state->fb; struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); u32 subpixel_src_mask = (1 << 16) - 1; - u32 format = fb->format->format; int num_planes = fb->format->num_planes; struct drm_crtc_state *crtc_state; - u32 h_subsample, v_subsample; + u32 h_subsample = fb->format->hsub; + u32 v_subsample = fb->format->vsub; int i, ret; crtc_state = drm_atomic_get_existing_crtc_state(state->state, @@ -328,9 +328,6 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) if (ret) return ret; - h_subsample = drm_format_horz_chroma_subsampling(format); - v_subsample = drm_format_vert_chroma_subsampling(format); - for (i = 0; i < num_planes; i++) vc4_state->offsets[i] = bo->paddr + fb->offsets[i]; @@ -593,7 +590,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, const struct hvs_format *format = vc4_get_hvs_format(fb->format->format); u64 base_format_mod = fourcc_mod_broadcom_mod(fb->modifier); int num_planes = fb->format->num_planes; - u32 h_subsample, v_subsample; + u32 h_subsample = fb->format->hsub; + u32 v_subsample = fb->format->vsub; bool mix_plane_alpha; bool covers_screen; u32 scl0, scl1, pitch0; @@ -623,9 +621,6 @@ static int vc4_plane_mode_set(struct drm_plane *plane, scl1 = vc4_get_scl_field(state, 0); } - h_subsample = drm_format_horz_chroma_subsampling(format->drm); - v_subsample = drm_format_vert_chroma_subsampling(format->drm); - rotation = drm_rotation_simplify(state->rotation, DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_X | diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h index 41779b327d91..eeec449d6c6a 100644 --- a/include/drm/drm_fourcc.h +++ b/include/drm/drm_fourcc.h @@ -269,8 +269,6 @@ uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth); uint32_t drm_driver_legacy_fb_format(struct drm_device *dev, uint32_t bpp, uint32_t depth); int drm_format_plane_cpp(uint32_t format, int plane); -int drm_format_horz_chroma_subsampling(uint32_t format); -int drm_format_vert_chroma_subsampling(uint32_t format); int drm_format_plane_width(int width, uint32_t format, int plane); int drm_format_plane_height(int height, uint32_t format, int plane); unsigned int drm_format_info_block_width(const struct drm_format_info *info, -- cgit v1.2.3 From 24c478ead0bf50a758e9dbecc7356e9eebf20271 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 16 May 2019 12:31:49 +0200 Subject: drm/fourcc: Pass the format_info pointer to drm_format_plane_cpp So far, the drm_format_plane_cpp function was operating on the format's fourcc and was doing a lookup to retrieve the drm_format_info structure and return the cpp. However, this is inefficient since in most cases, we will have the drm_format_info pointer already available so we shouldn't have to perform a new lookup. Some drm_fourcc functions also already operate on the drm_format_info pointer for that reason, so the API is quite inconsistent there. Let's follow the latter pattern and remove the extra lookup while being a bit more consistent. In order to be extra consistent, also rename that function to drm_format_info_plane_cpp and to a static function in the header to match the current policy. Reviewed-by: Emil Velikov Reviewed-by: Paul Kocialkowski Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/32aa13e53dbc98a90207fd290aa8e79f785fb11e.1558002671.git-series.maxime.ripard@bootlin.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c | 4 +++- drivers/gpu/drm/arm/malidp_hw.c | 3 ++- drivers/gpu/drm/arm/malidp_planes.c | 2 +- drivers/gpu/drm/drm_client.c | 3 ++- drivers/gpu/drm/drm_fb_helper.c | 2 +- drivers/gpu/drm/drm_format_helper.c | 4 ++-- drivers/gpu/drm/drm_fourcc.c | 20 -------------------- drivers/gpu/drm/i915/intel_sprite.c | 3 ++- drivers/gpu/drm/mediatek/mtk_drm_fb.c | 2 +- drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c | 3 ++- drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c | 2 +- drivers/gpu/drm/msm/msm_fb.c | 2 +- drivers/gpu/drm/radeon/radeon_fb.c | 4 +++- drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 2 +- drivers/gpu/drm/stm/ltdc.c | 2 +- drivers/gpu/drm/tegra/fb.c | 2 +- drivers/gpu/drm/zte/zx_plane.c | 2 +- include/drm/drm_fourcc.h | 18 +++++++++++++++++- 18 files changed, 42 insertions(+), 38 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c index e47609218839..06e73a343724 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c @@ -121,6 +121,8 @@ static int amdgpufb_create_pinned_object(struct amdgpu_fbdev *rfbdev, struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **gobj_p) { + const struct drm_format_info *info = drm_get_format_info(dev, + mode_cmd); struct amdgpu_device *adev = rfbdev->adev; struct drm_gem_object *gobj = NULL; struct amdgpu_bo *abo = NULL; @@ -131,7 +133,7 @@ static int amdgpufb_create_pinned_object(struct amdgpu_fbdev *rfbdev, int height = mode_cmd->height; u32 cpp; - cpp = drm_format_plane_cpp(mode_cmd->pixel_format, 0); + cpp = drm_format_info_plane_cpp(info, 0); /* need to align pitch with crtc limits */ mode_cmd->pitches[0] = amdgpu_align_pitch(adev, mode_cmd->width, cpp, diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c index 8df12e9a33bb..1c9e869f4c52 100644 --- a/drivers/gpu/drm/arm/malidp_hw.c +++ b/drivers/gpu/drm/arm/malidp_hw.c @@ -382,7 +382,8 @@ static void malidp500_modeset(struct malidp_hw_device *hwdev, struct videomode * int malidp_format_get_bpp(u32 fmt) { - int bpp = drm_format_plane_cpp(fmt, 0) * 8; + const struct drm_format_info *info = drm_format_info(fmt); + int bpp = drm_format_info_plane_cpp(info, 0) * 8; if (bpp == 0) { switch (fmt) { diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index 8f89813d08c1..361c02988375 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -227,7 +227,7 @@ bool malidp_format_mod_supported(struct drm_device *drm, if (modifier & AFBC_SPLIT) { if (!info->is_yuv) { - if (drm_format_plane_cpp(format, 0) <= 2) { + if (drm_format_info_plane_cpp(info, 0) <= 2) { DRM_DEBUG_KMS("RGB formats <= 16bpp are not supported with SPLIT\n"); return false; } diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index f20d1dda3961..169d8eeaa662 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c @@ -243,6 +243,7 @@ static void drm_client_buffer_delete(struct drm_client_buffer *buffer) static struct drm_client_buffer * drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format) { + const struct drm_format_info *info = drm_format_info(format); struct drm_mode_create_dumb dumb_args = { }; struct drm_device *dev = client->dev; struct drm_client_buffer *buffer; @@ -258,7 +259,7 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u dumb_args.width = width; dumb_args.height = height; - dumb_args.bpp = drm_format_plane_cpp(format, 0) * 8; + dumb_args.bpp = drm_format_info_plane_cpp(info, 0) * 8; ret = drm_mode_create_dumb(dev, &dumb_args, client->file); if (ret) goto err_delete; diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index a52b48fafbd7..e2d9a6da14a5 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -813,7 +813,7 @@ static void drm_fb_helper_dirty_blit_real(struct drm_fb_helper *fb_helper, struct drm_clip_rect *clip) { struct drm_framebuffer *fb = fb_helper->fb; - unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0); + unsigned int cpp = drm_format_info_plane_cpp(fb->format, 0); size_t offset = clip->y1 * fb->pitches[0] + clip->x1 * cpp; void *src = fb_helper->fbdev->screen_buffer + offset; void *dst = fb_helper->buffer->vaddr + offset; diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index a18da35145b7..8ad66aa1362a 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -36,7 +36,7 @@ static unsigned int clip_offset(struct drm_rect *clip, void drm_fb_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb, struct drm_rect *clip) { - unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0); + unsigned int cpp = drm_format_info_plane_cpp(fb->format, 0); size_t len = (clip->x2 - clip->x1) * cpp; unsigned int y, lines = clip->y2 - clip->y1; @@ -63,7 +63,7 @@ void drm_fb_memcpy_dstclip(void __iomem *dst, void *vaddr, struct drm_framebuffer *fb, struct drm_rect *clip) { - unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0); + unsigned int cpp = drm_format_info_plane_cpp(fb->format, 0); unsigned int offset = clip_offset(clip, fb->pitches[0], cpp); size_t len = (clip->x2 - clip->x1) * cpp; unsigned int y, lines = clip->y2 - clip->y1; diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index e4a2c8372c8b..5f63fc74e265 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -332,26 +332,6 @@ drm_get_format_info(struct drm_device *dev, } EXPORT_SYMBOL(drm_get_format_info); -/** - * drm_format_plane_cpp - determine the bytes per pixel value - * @format: pixel format (DRM_FORMAT_*) - * @plane: plane index - * - * Returns: - * The bytes per pixel value for the specified plane. - */ -int drm_format_plane_cpp(uint32_t format, int plane) -{ - const struct drm_format_info *info; - - info = drm_format_info(format); - if (!info || plane >= info->num_planes) - return 0; - - return info->cpp[plane]; -} -EXPORT_SYMBOL(drm_format_plane_cpp); - /** * drm_format_plane_width - width of the plane given the first plane * @width: width of the first plane diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 2913e89280d7..e35601b1f878 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -325,7 +325,8 @@ skl_plane_max_stride(struct intel_plane *plane, u32 pixel_format, u64 modifier, unsigned int rotation) { - int cpp = drm_format_plane_cpp(pixel_format, 0); + const struct drm_format_info *info = drm_format_info(pixel_format); + int cpp = drm_format_info_plane_cpp(info, 0); /* * "The stride in bytes must not exceed the diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.c b/drivers/gpu/drm/mediatek/mtk_drm_fb.c index 68fdef8b12bd..0d5334a5a9a7 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_fb.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.c @@ -104,7 +104,7 @@ struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev, if (!gem) return ERR_PTR(-ENOENT); - bpp = drm_format_plane_cpp(cmd->pixel_format, 0); + bpp = drm_format_info_plane_cpp(info, 0); size = (height - 1) * cmd->pitches[0] + width * bpp; size += cmd->offsets[0]; diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c index f33d7007e830..59b24e0c5070 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c @@ -782,6 +782,7 @@ static void get_roi(struct drm_crtc *crtc, uint32_t *roi_w, uint32_t *roi_h) static void mdp5_crtc_restore_cursor(struct drm_crtc *crtc) { + const struct drm_format_info *info = drm_format_info(DRM_FORMAT_ARGB8888); struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct mdp5_kms *mdp5_kms = get_kms(crtc); @@ -800,7 +801,7 @@ static void mdp5_crtc_restore_cursor(struct drm_crtc *crtc) width = mdp5_crtc->cursor.width; height = mdp5_crtc->cursor.height; - stride = width * drm_format_plane_cpp(DRM_FORMAT_ARGB8888, 0); + stride = width * drm_format_info_plane_cpp(info, 0); get_roi(crtc, &roi_w, &roi_h); diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c index b30b2f4efc60..1ca294694597 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c @@ -158,7 +158,7 @@ uint32_t mdp5_smp_calculate(struct mdp5_smp *smp, for (i = 0; i < nplanes; i++) { int n, fetch_stride, cpp; - cpp = drm_format_plane_cpp(fmt, i); + cpp = drm_format_info_plane_cpp(info, i); fetch_stride = width * cpp / (i ? hsub : 1); n = DIV_ROUND_UP(fetch_stride * nlines, smp->blk_size); diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c index f69c0afd6ec6..29e45f2144b5 100644 --- a/drivers/gpu/drm/msm/msm_fb.c +++ b/drivers/gpu/drm/msm/msm_fb.c @@ -181,7 +181,7 @@ static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, unsigned int min_size; min_size = (height - 1) * mode_cmd->pitches[i] - + width * drm_format_plane_cpp(mode_cmd->pixel_format, i) + + width * drm_format_info_plane_cpp(info, i) + mode_cmd->offsets[i]; if (bos[i]->size < min_size) { diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 1298b84cb1c7..dbf596fc4339 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -125,6 +125,7 @@ static int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev, struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **gobj_p) { + const struct drm_format_info *info; struct radeon_device *rdev = rfbdev->rdev; struct drm_gem_object *gobj = NULL; struct radeon_bo *rbo = NULL; @@ -135,7 +136,8 @@ static int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev, int height = mode_cmd->height; u32 cpp; - cpp = drm_format_plane_cpp(mode_cmd->pixel_format, 0); + info = drm_get_format_info(rdev->ddev, mode_cmd); + cpp = drm_format_info_plane_cpp(info, 0); /* need to align pitch with crtc limits */ mode_cmd->pitches[0] = radeon_align_pitch(rdev, mode_cmd->width, cpp, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index c318fae28581..57873c99ae29 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -98,7 +98,7 @@ rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, min_size = (height - 1) * mode_cmd->pitches[i] + mode_cmd->offsets[i] + - width * drm_format_plane_cpp(mode_cmd->pixel_format, i); + width * drm_format_info_plane_cpp(info, i); if (obj->size < min_size) { drm_gem_object_put_unlocked(obj); diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index 97912e2c663d..e7abe54b9746 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -784,7 +784,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane, /* Configures the color frame buffer pitch in bytes & line length */ pitch_in_bytes = fb->pitches[0]; - line_length = drm_format_plane_cpp(fb->format->format, 0) * + line_length = drm_format_info_plane_cpp(fb->format, 0) * (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1; val = ((pitch_in_bytes << 16) | line_length); reg_update_bits(ldev->regs, LTDC_L1CFBLR + lofs, diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 94fb75089d87..d1042196a30f 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -149,7 +149,7 @@ struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, goto unreference; } - bpp = drm_format_plane_cpp(cmd->pixel_format, i); + bpp = drm_format_info_plane_cpp(info, i); size = (height - 1) * cmd->pitches[i] + width * bpp + cmd->offsets[i]; diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c index c6a8be444300..d97a4dff515d 100644 --- a/drivers/gpu/drm/zte/zx_plane.c +++ b/drivers/gpu/drm/zte/zx_plane.c @@ -222,7 +222,7 @@ static void zx_vl_plane_atomic_update(struct drm_plane *plane, cma_obj = drm_fb_cma_get_gem_obj(fb, i); paddr = cma_obj->paddr + fb->offsets[i]; paddr += src_y * fb->pitches[i]; - paddr += src_x * drm_format_plane_cpp(format, i); + paddr += src_x * drm_format_info_plane_cpp(fb->format, i); zx_writel(paddr_reg, paddr); paddr_reg += 4; } diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h index eeec449d6c6a..6b5a82b31bc4 100644 --- a/include/drm/drm_fourcc.h +++ b/include/drm/drm_fourcc.h @@ -260,6 +260,23 @@ drm_format_info_is_yuv_sampling_444(const struct drm_format_info *info) return info->is_yuv && info->hsub == 1 && info->vsub == 1; } +/** + * drm_format_info_plane_cpp - determine the bytes per pixel value + * @format: pixel format info + * @plane: plane index + * + * Returns: + * The bytes per pixel value for the specified plane. + */ +static inline +int drm_format_info_plane_cpp(const struct drm_format_info *info, int plane) +{ + if (!info || plane >= info->num_planes) + return 0; + + return info->cpp[plane]; +} + const struct drm_format_info *__drm_format_info(u32 format); const struct drm_format_info *drm_format_info(u32 format); const struct drm_format_info * @@ -268,7 +285,6 @@ drm_get_format_info(struct drm_device *dev, uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth); uint32_t drm_driver_legacy_fb_format(struct drm_device *dev, uint32_t bpp, uint32_t depth); -int drm_format_plane_cpp(uint32_t format, int plane); int drm_format_plane_width(int width, uint32_t format, int plane); int drm_format_plane_height(int height, uint32_t format, int plane); unsigned int drm_format_info_block_width(const struct drm_format_info *info, -- cgit v1.2.3 From bf39607c16141811d0f5fe67e231364c96a87e09 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 16 May 2019 12:31:50 +0200 Subject: drm/fourcc: Pass the format_info pointer to drm_format_plane_width/height So far, the drm_format_plane_height/width functions were operating on the format's fourcc and was doing a lookup to retrieve the drm_format_info structure and return the cpp. However, this is inefficient since in most cases, we will have the drm_format_info pointer already available so we shouldn't have to perform a new lookup. Some drm_fourcc functions also already operate on the drm_format_info pointer for that reason, so the API is quite inconsistent there. Let's follow the latter pattern and remove the extra lookup while being a bit more consistent. In order to be extra consistent, also rename that function to drm_format_info_plane_cpp and to a static function in the header to match the current policy. The parameters order have also be changed to match the other functions prototype. Reviewed-by: Emil Velikov Reviewed-by: Paul Kocialkowski Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/514af1d489d80b8b1767e3716b663ce5103da6eb.1558002671.git-series.maxime.ripard@bootlin.com --- drivers/gpu/drm/drm_fourcc.c | 48 ----------------------------------- drivers/gpu/drm/meson/meson_overlay.c | 12 ++++----- include/drm/drm_fourcc.h | 46 +++++++++++++++++++++++++++++++-- 3 files changed, 50 insertions(+), 56 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index 5f63fc74e265..35b459d186c5 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -332,54 +332,6 @@ drm_get_format_info(struct drm_device *dev, } EXPORT_SYMBOL(drm_get_format_info); -/** - * drm_format_plane_width - width of the plane given the first plane - * @width: width of the first plane - * @format: pixel format - * @plane: plane index - * - * Returns: - * The width of @plane, given that the width of the first plane is @width. - */ -int drm_format_plane_width(int width, uint32_t format, int plane) -{ - const struct drm_format_info *info; - - info = drm_format_info(format); - if (!info || plane >= info->num_planes) - return 0; - - if (plane == 0) - return width; - - return width / info->hsub; -} -EXPORT_SYMBOL(drm_format_plane_width); - -/** - * drm_format_plane_height - height of the plane given the first plane - * @height: height of the first plane - * @format: pixel format - * @plane: plane index - * - * Returns: - * The height of @plane, given that the height of the first plane is @height. - */ -int drm_format_plane_height(int height, uint32_t format, int plane) -{ - const struct drm_format_info *info; - - info = drm_format_info(format); - if (!info || plane >= info->num_planes) - return 0; - - if (plane == 0) - return height; - - return height / info->vsub; -} -EXPORT_SYMBOL(drm_format_plane_height); - /** * drm_format_info_block_width - width in pixels of block. * @info: pixel format info diff --git a/drivers/gpu/drm/meson/meson_overlay.c b/drivers/gpu/drm/meson/meson_overlay.c index fb8515b2860c..55b3f2f2e608 100644 --- a/drivers/gpu/drm/meson/meson_overlay.c +++ b/drivers/gpu/drm/meson/meson_overlay.c @@ -466,8 +466,8 @@ static void meson_overlay_atomic_update(struct drm_plane *plane, priv->viu.vd1_addr2 = gem->paddr + fb->offsets[2]; priv->viu.vd1_stride2 = fb->pitches[2]; priv->viu.vd1_height2 = - drm_format_plane_height(fb->height, - fb->format->format, 2); + drm_format_info_plane_height(fb->format, + fb->height, 2); DRM_DEBUG("plane 2 addr 0x%x stride %d height %d\n", priv->viu.vd1_addr2, priv->viu.vd1_stride2, @@ -478,8 +478,8 @@ static void meson_overlay_atomic_update(struct drm_plane *plane, priv->viu.vd1_addr1 = gem->paddr + fb->offsets[1]; priv->viu.vd1_stride1 = fb->pitches[1]; priv->viu.vd1_height1 = - drm_format_plane_height(fb->height, - fb->format->format, 1); + drm_format_info_plane_height(fb->format, + fb->height, 1); DRM_DEBUG("plane 1 addr 0x%x stride %d height %d\n", priv->viu.vd1_addr1, priv->viu.vd1_stride1, @@ -490,8 +490,8 @@ static void meson_overlay_atomic_update(struct drm_plane *plane, priv->viu.vd1_addr0 = gem->paddr + fb->offsets[0]; priv->viu.vd1_stride0 = fb->pitches[0]; priv->viu.vd1_height0 = - drm_format_plane_height(fb->height, - fb->format->format, 0); + drm_format_info_plane_height(fb->format, + fb->height, 0); DRM_DEBUG("plane 0 addr 0x%x stride %d height %d\n", priv->viu.vd1_addr0, priv->viu.vd1_stride0, diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h index 6b5a82b31bc4..4ef8ccb5d236 100644 --- a/include/drm/drm_fourcc.h +++ b/include/drm/drm_fourcc.h @@ -277,6 +277,50 @@ int drm_format_info_plane_cpp(const struct drm_format_info *info, int plane) return info->cpp[plane]; } +/** + * drm_format_info_plane_width - width of the plane given the first plane + * @format: pixel format info + * @width: width of the first plane + * @plane: plane index + * + * Returns: + * The width of @plane, given that the width of the first plane is @width. + */ +static inline +int drm_format_info_plane_width(const struct drm_format_info *info, int width, + int plane) +{ + if (!info || plane >= info->num_planes) + return 0; + + if (plane == 0) + return width; + + return width / info->hsub; +} + +/** + * drm_format_info_plane_height - height of the plane given the first plane + * @format: pixel format info + * @height: height of the first plane + * @plane: plane index + * + * Returns: + * The height of @plane, given that the height of the first plane is @height. + */ +static inline +int drm_format_info_plane_height(const struct drm_format_info *info, int height, + int plane) +{ + if (!info || plane >= info->num_planes) + return 0; + + if (plane == 0) + return height; + + return height / info->vsub; +} + const struct drm_format_info *__drm_format_info(u32 format); const struct drm_format_info *drm_format_info(u32 format); const struct drm_format_info * @@ -285,8 +329,6 @@ drm_get_format_info(struct drm_device *dev, uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth); uint32_t drm_driver_legacy_fb_format(struct drm_device *dev, uint32_t bpp, uint32_t depth); -int drm_format_plane_width(int width, uint32_t format, int plane); -int drm_format_plane_height(int height, uint32_t format, int plane); unsigned int drm_format_info_block_width(const struct drm_format_info *info, int plane); unsigned int drm_format_info_block_height(const struct drm_format_info *info, -- cgit v1.2.3 From b0f986b4b025c8036ab2c660460621c1d17656b5 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 16 May 2019 12:31:52 +0200 Subject: drm: Remove users of drm_format_info_plane_cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_format_info_plane_cpp() basically just returns the cpp array content found in the drm_format_info structure. Since it's pretty trivial, let's remove the function and have the users use the array directly Suggested-by: Ville Syrjälä Reviewed-by: Paul Kocialkowski Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/c0a78c87cd0410a1819edad2794ad06543c85bb5.1558002671.git-series.maxime.ripard@bootlin.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c | 2 +- drivers/gpu/drm/arm/malidp_hw.c | 2 +- drivers/gpu/drm/arm/malidp_planes.c | 2 +- drivers/gpu/drm/drm_client.c | 2 +- drivers/gpu/drm/drm_fb_helper.c | 2 +- drivers/gpu/drm/drm_format_helper.c | 4 ++-- drivers/gpu/drm/i915/intel_sprite.c | 2 +- drivers/gpu/drm/mediatek/mtk_drm_fb.c | 2 +- drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c | 2 +- drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c | 2 +- drivers/gpu/drm/msm/msm_fb.c | 2 +- drivers/gpu/drm/radeon/radeon_fb.c | 2 +- drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 2 +- drivers/gpu/drm/stm/ltdc.c | 2 +- drivers/gpu/drm/tegra/fb.c | 2 +- drivers/gpu/drm/zte/zx_plane.c | 2 +- include/drm/drm_fourcc.h | 17 ----------------- 17 files changed, 17 insertions(+), 34 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c index 6edae6458be8..2e2869299a84 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c @@ -133,7 +133,7 @@ static int amdgpufb_create_pinned_object(struct amdgpu_fbdev *rfbdev, u32 cpp; info = drm_get_format_info(adev->ddev, mode_cmd); - cpp = drm_format_info_plane_cpp(info, 0); + cpp = info->cpp[0]; /* need to align pitch with crtc limits */ mode_cmd->pitches[0] = amdgpu_align_pitch(adev, mode_cmd->width, cpp, diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c index 1c9e869f4c52..53391c0f87eb 100644 --- a/drivers/gpu/drm/arm/malidp_hw.c +++ b/drivers/gpu/drm/arm/malidp_hw.c @@ -383,7 +383,7 @@ static void malidp500_modeset(struct malidp_hw_device *hwdev, struct videomode * int malidp_format_get_bpp(u32 fmt) { const struct drm_format_info *info = drm_format_info(fmt); - int bpp = drm_format_info_plane_cpp(info, 0) * 8; + int bpp = info->cpp[0] * 8; if (bpp == 0) { switch (fmt) { diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index 361c02988375..07ceb4ee14e3 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -227,7 +227,7 @@ bool malidp_format_mod_supported(struct drm_device *drm, if (modifier & AFBC_SPLIT) { if (!info->is_yuv) { - if (drm_format_info_plane_cpp(info, 0) <= 2) { + if (info->cpp[0] <= 2) { DRM_DEBUG_KMS("RGB formats <= 16bpp are not supported with SPLIT\n"); return false; } diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index 169d8eeaa662..5abcd83da6a6 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c @@ -259,7 +259,7 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u dumb_args.width = width; dumb_args.height = height; - dumb_args.bpp = drm_format_info_plane_cpp(info, 0) * 8; + dumb_args.bpp = info->cpp[0] * 8; ret = drm_mode_create_dumb(dev, &dumb_args, client->file); if (ret) goto err_delete; diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index e2d9a6da14a5..302cf5f8bcce 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -813,7 +813,7 @@ static void drm_fb_helper_dirty_blit_real(struct drm_fb_helper *fb_helper, struct drm_clip_rect *clip) { struct drm_framebuffer *fb = fb_helper->fb; - unsigned int cpp = drm_format_info_plane_cpp(fb->format, 0); + unsigned int cpp = fb->format->cpp[0]; size_t offset = clip->y1 * fb->pitches[0] + clip->x1 * cpp; void *src = fb_helper->fbdev->screen_buffer + offset; void *dst = fb_helper->buffer->vaddr + offset; diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index 8ad66aa1362a..0897cb9aeaff 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -36,7 +36,7 @@ static unsigned int clip_offset(struct drm_rect *clip, void drm_fb_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb, struct drm_rect *clip) { - unsigned int cpp = drm_format_info_plane_cpp(fb->format, 0); + unsigned int cpp = fb->format->cpp[0]; size_t len = (clip->x2 - clip->x1) * cpp; unsigned int y, lines = clip->y2 - clip->y1; @@ -63,7 +63,7 @@ void drm_fb_memcpy_dstclip(void __iomem *dst, void *vaddr, struct drm_framebuffer *fb, struct drm_rect *clip) { - unsigned int cpp = drm_format_info_plane_cpp(fb->format, 0); + unsigned int cpp = fb->format->cpp[0]; unsigned int offset = clip_offset(clip, fb->pitches[0], cpp); size_t len = (clip->x2 - clip->x1) * cpp; unsigned int y, lines = clip->y2 - clip->y1; diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index e35601b1f878..c1647c0cc217 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -326,7 +326,7 @@ skl_plane_max_stride(struct intel_plane *plane, unsigned int rotation) { const struct drm_format_info *info = drm_format_info(pixel_format); - int cpp = drm_format_info_plane_cpp(info, 0); + int cpp = info->cpp[0]; /* * "The stride in bytes must not exceed the diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.c b/drivers/gpu/drm/mediatek/mtk_drm_fb.c index 0d5334a5a9a7..b5e2f230da00 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_fb.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.c @@ -104,7 +104,7 @@ struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev, if (!gem) return ERR_PTR(-ENOENT); - bpp = drm_format_info_plane_cpp(info, 0); + bpp = info->cpp[0]; size = (height - 1) * cmd->pitches[0] + width * bpp; size += cmd->offsets[0]; diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c index 59b24e0c5070..c3751c95b452 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c @@ -801,7 +801,7 @@ static void mdp5_crtc_restore_cursor(struct drm_crtc *crtc) width = mdp5_crtc->cursor.width; height = mdp5_crtc->cursor.height; - stride = width * drm_format_info_plane_cpp(info, 0); + stride = width * info->cpp[0]; get_roi(crtc, &roi_w, &roi_h); diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c index 1ca294694597..2834837f4d3e 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c @@ -158,7 +158,7 @@ uint32_t mdp5_smp_calculate(struct mdp5_smp *smp, for (i = 0; i < nplanes; i++) { int n, fetch_stride, cpp; - cpp = drm_format_info_plane_cpp(info, i); + cpp = info->cpp[i]; fetch_stride = width * cpp / (i ? hsub : 1); n = DIV_ROUND_UP(fetch_stride * nlines, smp->blk_size); diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c index 29e45f2144b5..68fa2c8f61e6 100644 --- a/drivers/gpu/drm/msm/msm_fb.c +++ b/drivers/gpu/drm/msm/msm_fb.c @@ -181,7 +181,7 @@ static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, unsigned int min_size; min_size = (height - 1) * mode_cmd->pitches[i] - + width * drm_format_info_plane_cpp(info, i) + + width * info->cpp[i] + mode_cmd->offsets[i]; if (bos[i]->size < min_size) { diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index dbf596fc4339..287e3f92102a 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -137,7 +137,7 @@ static int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev, u32 cpp; info = drm_get_format_info(rdev->ddev, mode_cmd); - cpp = drm_format_info_plane_cpp(info, 0); + cpp = info->cpp[0]; /* need to align pitch with crtc limits */ mode_cmd->pitches[0] = radeon_align_pitch(rdev, mode_cmd->width, cpp, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 57873c99ae29..31030cf81bc9 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -98,7 +98,7 @@ rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, min_size = (height - 1) * mode_cmd->pitches[i] + mode_cmd->offsets[i] + - width * drm_format_info_plane_cpp(info, i); + width * info->cpp[i]; if (obj->size < min_size) { drm_gem_object_put_unlocked(obj); diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index e7abe54b9746..14eb8c40953b 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -784,7 +784,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane, /* Configures the color frame buffer pitch in bytes & line length */ pitch_in_bytes = fb->pitches[0]; - line_length = drm_format_info_plane_cpp(fb->format, 0) * + line_length = fb->format->cpp[0] * (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1; val = ((pitch_in_bytes << 16) | line_length); reg_update_bits(ldev->regs, LTDC_L1CFBLR + lofs, diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index d1042196a30f..57cc26e1da01 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -149,7 +149,7 @@ struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, goto unreference; } - bpp = drm_format_info_plane_cpp(info, i); + bpp = info->cpp[i]; size = (height - 1) * cmd->pitches[i] + width * bpp + cmd->offsets[i]; diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c index d97a4dff515d..706452f9b276 100644 --- a/drivers/gpu/drm/zte/zx_plane.c +++ b/drivers/gpu/drm/zte/zx_plane.c @@ -222,7 +222,7 @@ static void zx_vl_plane_atomic_update(struct drm_plane *plane, cma_obj = drm_fb_cma_get_gem_obj(fb, i); paddr = cma_obj->paddr + fb->offsets[i]; paddr += src_y * fb->pitches[i]; - paddr += src_x * drm_format_info_plane_cpp(fb->format, i); + paddr += src_x * fb->format->cpp[i]; zx_writel(paddr_reg, paddr); paddr_reg += 4; } diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h index 4ef8ccb5d236..405466692bd2 100644 --- a/include/drm/drm_fourcc.h +++ b/include/drm/drm_fourcc.h @@ -260,23 +260,6 @@ drm_format_info_is_yuv_sampling_444(const struct drm_format_info *info) return info->is_yuv && info->hsub == 1 && info->vsub == 1; } -/** - * drm_format_info_plane_cpp - determine the bytes per pixel value - * @format: pixel format info - * @plane: plane index - * - * Returns: - * The bytes per pixel value for the specified plane. - */ -static inline -int drm_format_info_plane_cpp(const struct drm_format_info *info, int plane) -{ - if (!info || plane >= info->num_planes) - return 0; - - return info->cpp[plane]; -} - /** * drm_format_info_plane_width - width of the plane given the first plane * @format: pixel format info -- cgit v1.2.3 From 7793a108964924dd2802ee8c5dff2e9f3a9cd54a Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 21 May 2019 13:09:06 +0200 Subject: drm/fourcc: Fix the parameters name in the documentation We introduced new functions in the commit bf39607c1614 ("drm/fourcc: Pass the format_info pointer to drm_format_plane_width/height") based on previous ones but with a slightly different prototype. However, the documentation wasn't changed to reflect that change. Fixes: bf39607c1614 ("drm/fourcc: Pass the format_info pointer to drm_format_plane_width/height") Reported-by: kbuild test robot Reviewed-by: Daniel Vetter Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20190521110906.15268-1-maxime.ripard@bootlin.com --- include/drm/drm_fourcc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h index 405466692bd2..306d1efeb5e0 100644 --- a/include/drm/drm_fourcc.h +++ b/include/drm/drm_fourcc.h @@ -262,7 +262,7 @@ drm_format_info_is_yuv_sampling_444(const struct drm_format_info *info) /** * drm_format_info_plane_width - width of the plane given the first plane - * @format: pixel format info + * @info: pixel format info * @width: width of the first plane * @plane: plane index * @@ -284,7 +284,7 @@ int drm_format_info_plane_width(const struct drm_format_info *info, int width, /** * drm_format_info_plane_height - height of the plane given the first plane - * @format: pixel format info + * @info: pixel format info * @height: height of the first plane * @plane: plane index * -- cgit v1.2.3 From 81da87f63a1edebcf8cbb811d387e353d9f89c7a Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 21 May 2019 13:08:29 +0200 Subject: drm: Replace drm_gem_vram_push_to_system() with kunmap + unpin The push-to-system function forces a buffer out of video RAM. This decision should rather be made by the memory manager. By replacing the function with calls to the kunmap and unpin functions, the buffer's memory becomes available, but the buffer remains in VRAM until it's evicted by a pin operation. This patch replaces the remaining instances of drm_gem_vram_push_to_system() in ast and mgag200, and removes the function from DRM. Signed-off-by: Thomas Zimmermann Acked-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20190521110831.20200-2-tzimmermann@suse.de Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/ast/ast_mode.c | 14 ++++++--- drivers/gpu/drm/drm_gem_vram_helper.c | 50 -------------------------------- drivers/gpu/drm/drm_vram_helper_common.c | 2 -- drivers/gpu/drm/mgag200/mgag200_mode.c | 15 ++++++---- include/drm/drm_gem_vram_helper.h | 1 - 5 files changed, 20 insertions(+), 62 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 9aca9135a5cc..fbafe3994654 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -521,7 +521,6 @@ static void ast_crtc_dpms(struct drm_crtc *crtc, int mode) } } -/* ast is different - we will force move buffers out of VRAM */ static int ast_crtc_do_set_base(struct drm_crtc *crtc, struct drm_framebuffer *fb, int x, int y, int atomic) @@ -534,12 +533,15 @@ static int ast_crtc_do_set_base(struct drm_crtc *crtc, s64 gpu_addr; void *base; - /* push the previous fb to system ram */ if (!atomic && fb) { ast_fb = to_ast_framebuffer(fb); obj = ast_fb->obj; gbo = drm_gem_vram_of_gem(obj); - drm_gem_vram_push_to_system(gbo); + + /* unmap if console */ + if (&ast->fbdev->afb == ast_fb) + drm_gem_vram_kunmap(gbo); + drm_gem_vram_unpin(gbo); } ast_fb = to_ast_framebuffer(crtc->primary->fb); @@ -622,11 +624,15 @@ static void ast_crtc_disable(struct drm_crtc *crtc) DRM_DEBUG_KMS("\n"); ast_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); if (crtc->primary->fb) { + struct ast_private *ast = crtc->dev->dev_private; struct ast_framebuffer *ast_fb = to_ast_framebuffer(crtc->primary->fb); struct drm_gem_object *obj = ast_fb->obj; struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(obj); - drm_gem_vram_push_to_system(gbo); + /* unmap if console */ + if (&ast->fbdev->afb == ast_fb) + drm_gem_vram_kunmap(gbo); + drm_gem_vram_unpin(gbo); } crtc->primary->fb = NULL; } diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index bde8237e8021..b341b70832be 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -379,56 +379,6 @@ int drm_gem_vram_unpin_reserved(struct drm_gem_vram_object *gbo) } EXPORT_SYMBOL(drm_gem_vram_unpin_reserved); -/** - * drm_gem_vram_push_to_system() - \ - Unpins a GEM VRAM object and moves it to system memory - * @gbo: the GEM VRAM object - * - * This operation only works if the caller holds the final pin on the - * buffer object. - * - * Returns: - * 0 on success, or - * a negative error code otherwise. - */ -int drm_gem_vram_push_to_system(struct drm_gem_vram_object *gbo) -{ - int i, ret; - struct ttm_operation_ctx ctx = { false, false }; - - ret = ttm_bo_reserve(&gbo->bo, true, false, NULL); - if (ret < 0) - return ret; - - if (WARN_ON_ONCE(!gbo->pin_count)) - goto out; - - --gbo->pin_count; - if (gbo->pin_count) - goto out; - - if (gbo->kmap.virtual) - ttm_bo_kunmap(&gbo->kmap); - - drm_gem_vram_placement(gbo, TTM_PL_FLAG_SYSTEM); - for (i = 0; i < gbo->placement.num_placement ; ++i) - gbo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; - - ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx); - if (ret) - goto err_ttm_bo_unreserve; - -out: - ttm_bo_unreserve(&gbo->bo); - - return 0; - -err_ttm_bo_unreserve: - ttm_bo_unreserve(&gbo->bo); - return ret; -} -EXPORT_SYMBOL(drm_gem_vram_push_to_system); - /** * drm_gem_vram_kmap_at() - Maps a GEM VRAM object into kernel address space * @gbo: the GEM VRAM object diff --git a/drivers/gpu/drm/drm_vram_helper_common.c b/drivers/gpu/drm/drm_vram_helper_common.c index 3b47f7002115..e9c9f9a80ba3 100644 --- a/drivers/gpu/drm/drm_vram_helper_common.c +++ b/drivers/gpu/drm/drm_vram_helper_common.c @@ -79,8 +79,6 @@ * RAM. Call drm_gem_vram_pin() with &DRM_GEM_VRAM_PL_FLAG_VRAM or * &DRM_GEM_VRAM_PL_FLAG_SYSTEM to pin a buffer object in video RAM or system * memory. Call drm_gem_vram_unpin() to release the pinned object afterwards. - * If you have to evict a buffer object from video RAM (e.g., for freeing up - * memory), unpin the buffer and call drm_gem_vram_push_to_system(). * * A buffer object that is pinned in video RAM has a fixed address within that * memory region. Call drm_gem_vram_offset() to retrieve this value. Typically diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index e79872c968bf..1c8e0bfac015 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -858,8 +858,6 @@ static void mga_set_start_address(struct drm_crtc *crtc, unsigned offset) WREG_ECRT(0x0, ((u8)(addr >> 16) & 0xf) | crtcext0); } - -/* ast is different - we will force move buffers out of VRAM */ static int mga_crtc_do_set_base(struct drm_crtc *crtc, struct drm_framebuffer *fb, int x, int y, int atomic) @@ -872,12 +870,15 @@ static int mga_crtc_do_set_base(struct drm_crtc *crtc, s64 gpu_addr; void *base; - /* push the previous fb to system ram */ if (!atomic && fb) { mga_fb = to_mga_framebuffer(fb); obj = mga_fb->obj; gbo = drm_gem_vram_of_gem(obj); - drm_gem_vram_push_to_system(gbo); + + /* unmap if console */ + if (&mdev->mfbdev->mfb == mga_fb) + drm_gem_vram_kunmap(gbo); + drm_gem_vram_unpin(gbo); } mga_fb = to_mga_framebuffer(crtc->primary->fb); @@ -1425,11 +1426,15 @@ static void mga_crtc_disable(struct drm_crtc *crtc) DRM_DEBUG_KMS("\n"); mga_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); if (crtc->primary->fb) { + struct mga_device *mdev = crtc->dev->dev_private; struct mga_framebuffer *mga_fb = to_mga_framebuffer(crtc->primary->fb); struct drm_gem_object *obj = mga_fb->obj; struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(obj); - drm_gem_vram_push_to_system(gbo); + /* unmap if console */ + if (&mdev->mfbdev->mfb == mga_fb) + drm_gem_vram_kunmap(gbo); + drm_gem_vram_unpin(gbo); } crtc->primary->fb = NULL; } diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index ff1a81761543..22e52d7dfbb7 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -86,7 +86,6 @@ int drm_gem_vram_pin_reserved(struct drm_gem_vram_object *gbo, unsigned long pl_flag); int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo); int drm_gem_vram_unpin_reserved(struct drm_gem_vram_object *gbo); -int drm_gem_vram_push_to_system(struct drm_gem_vram_object *gbo); void *drm_gem_vram_kmap_at(struct drm_gem_vram_object *gbo, bool map, bool *is_iomem, struct ttm_bo_kmap_obj *kmap); void *drm_gem_vram_kmap(struct drm_gem_vram_object *gbo, bool map, -- cgit v1.2.3 From cb1f8814bbfcc2d0d7c499530e3d878ba04e4af2 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 21 May 2019 13:08:30 +0200 Subject: drm: Rename reserve/unreserve to lock/unlock in GEM VRAM helpers To align with the rest of DRM terminology, the GEM VRAM helpers now use lock and unlock in places where reserve and unreserve where used before. All callers have been adapted. Signed-off-by: Thomas Zimmermann Acked-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20190521110831.20200-3-tzimmermann@suse.de Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/ast/ast_fb.c | 11 ++++----- drivers/gpu/drm/ast/ast_mode.c | 12 +++++----- drivers/gpu/drm/drm_gem_vram_helper.c | 32 ++++++++++++------------- drivers/gpu/drm/mgag200/mgag200_cursor.c | 40 ++++++++++++++++---------------- drivers/gpu/drm/mgag200/mgag200_fb.c | 11 ++++----- include/drm/drm_gem_vram_helper.h | 8 +++---- 6 files changed, 56 insertions(+), 58 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index 505e602855c0..05f45222b702 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -62,13 +62,12 @@ static void ast_dirty_update(struct ast_fbdev *afbdev, obj = afbdev->afb.obj; gbo = drm_gem_vram_of_gem(obj); - /* - * try and reserve the BO, if we fail with busy - * then the BO is being moved and we should - * store up the damage until later. + /* Try to lock the BO. If we fail with -EBUSY then + * the BO is being moved and we should store up the + * damage until later. */ if (drm_can_sleep()) - ret = drm_gem_vram_reserve(gbo, true); + ret = drm_gem_vram_lock(gbo, true); if (ret) { if (ret != -EBUSY) return; @@ -127,7 +126,7 @@ static void ast_dirty_update(struct ast_fbdev *afbdev, drm_gem_vram_kunmap(gbo); out: - drm_gem_vram_unreserve(gbo); + drm_gem_vram_unlock(gbo); } static void ast_fillrect(struct fb_info *info, diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index fbafe3994654..fb700d620b64 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -1201,7 +1201,7 @@ static int ast_cursor_set(struct drm_crtc *crtc, } gbo = drm_gem_vram_of_gem(obj); - ret = drm_gem_vram_reserve(gbo, false); + ret = drm_gem_vram_lock(gbo, false); if (ret) goto fail; @@ -1209,7 +1209,7 @@ static int ast_cursor_set(struct drm_crtc *crtc, src = drm_gem_vram_kmap_at(gbo, true, &src_isiomem, &uobj_map); if (IS_ERR(src)) { ret = PTR_ERR(src); - goto fail_unreserve; + goto fail_unlock; } if (src_isiomem == true) DRM_ERROR("src cursor bo should be in main memory\n"); @@ -1218,7 +1218,7 @@ static int ast_cursor_set(struct drm_crtc *crtc, false, &dst_isiomem, &ast->cache_kmap); if (IS_ERR(dst)) { ret = PTR_ERR(dst); - goto fail_unreserve; + goto fail_unlock; } if (dst_isiomem == false) DRM_ERROR("dst bo should be in VRAM\n"); @@ -1229,7 +1229,7 @@ static int ast_cursor_set(struct drm_crtc *crtc, csum = copy_cursor_image(src, dst, width, height); drm_gem_vram_kunmap_at(gbo, &uobj_map); - drm_gem_vram_unreserve(gbo); + drm_gem_vram_unlock(gbo); /* write checksum + signature */ { @@ -1262,8 +1262,8 @@ static int ast_cursor_set(struct drm_crtc *crtc, drm_gem_object_put_unlocked(obj); return 0; -fail_unreserve: - drm_gem_vram_unreserve(gbo); +fail_unlock: + drm_gem_vram_unlock(gbo); fail: drm_gem_object_put_unlocked(obj); return ret; diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index b341b70832be..aefb0c361486 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -152,7 +152,7 @@ void drm_gem_vram_put(struct drm_gem_vram_object *gbo) EXPORT_SYMBOL(drm_gem_vram_put); /** - * drm_gem_vram_reserve() - Reserves a VRAM-backed GEM object + * drm_gem_vram_lock() - Locks a VRAM-backed GEM object * @gbo: the GEM VRAM object * @no_wait: don't wait for buffer object to become available * @@ -162,24 +162,24 @@ EXPORT_SYMBOL(drm_gem_vram_put); * 0 on success, or * a negative error code otherwise */ -int drm_gem_vram_reserve(struct drm_gem_vram_object *gbo, bool no_wait) +int drm_gem_vram_lock(struct drm_gem_vram_object *gbo, bool no_wait) { return ttm_bo_reserve(&gbo->bo, true, no_wait, NULL); } -EXPORT_SYMBOL(drm_gem_vram_reserve); +EXPORT_SYMBOL(drm_gem_vram_lock); /** - * drm_gem_vram_unreserve() - \ - Release a reservation acquired by drm_gem_vram_reserve() + * drm_gem_vram_unlock() - \ + Release a reservation acquired by drm_gem_vram_lock() * @gbo: the GEM VRAM object * * See ttm_bo_unreserve() for more information. */ -void drm_gem_vram_unreserve(struct drm_gem_vram_object *gbo) +void drm_gem_vram_unlock(struct drm_gem_vram_object *gbo) { ttm_bo_unreserve(&gbo->bo); } -EXPORT_SYMBOL(drm_gem_vram_unreserve); +EXPORT_SYMBOL(drm_gem_vram_unlock); /** * drm_gem_vram_mmap_offset() - Returns a GEM VRAM object's mmap offset @@ -263,7 +263,7 @@ err_ttm_bo_unreserve: EXPORT_SYMBOL(drm_gem_vram_pin); /** - * drm_gem_vram_pin_reserved() - Pins a GEM VRAM object in a region. + * drm_gem_vram_pin_locked() - Pins a GEM VRAM object in a region. * @gbo: the GEM VRAM object * @pl_flag: a bitmask of possible memory regions * @@ -272,14 +272,14 @@ EXPORT_SYMBOL(drm_gem_vram_pin); * it can be pinned to another region. * * This function pins a GEM VRAM object that has already been - * reserved. Use drm_gem_vram_pin() if possible. + * locked. Use drm_gem_vram_pin() if possible. * * Returns: * 0 on success, or * a negative error code otherwise. */ -int drm_gem_vram_pin_reserved(struct drm_gem_vram_object *gbo, - unsigned long pl_flag) +int drm_gem_vram_pin_locked(struct drm_gem_vram_object *gbo, + unsigned long pl_flag) { int i, ret; struct ttm_operation_ctx ctx = { false, false }; @@ -301,7 +301,7 @@ int drm_gem_vram_pin_reserved(struct drm_gem_vram_object *gbo, return 0; } -EXPORT_SYMBOL(drm_gem_vram_pin_reserved); +EXPORT_SYMBOL(drm_gem_vram_pin_locked); /** * drm_gem_vram_unpin() - Unpins a GEM VRAM object @@ -346,17 +346,17 @@ err_ttm_bo_unreserve: EXPORT_SYMBOL(drm_gem_vram_unpin); /** - * drm_gem_vram_unpin_reserved() - Unpins a GEM VRAM object + * drm_gem_vram_unpin_locked() - Unpins a GEM VRAM object * @gbo: the GEM VRAM object * * This function unpins a GEM VRAM object that has already been - * reserved. Use drm_gem_vram_unpin() if possible. + * locked. Use drm_gem_vram_unpin() if possible. * * Returns: * 0 on success, or * a negative error code otherwise. */ -int drm_gem_vram_unpin_reserved(struct drm_gem_vram_object *gbo) +int drm_gem_vram_unpin_locked(struct drm_gem_vram_object *gbo) { int i, ret; struct ttm_operation_ctx ctx = { false, false }; @@ -377,7 +377,7 @@ int drm_gem_vram_unpin_reserved(struct drm_gem_vram_object *gbo) return 0; } -EXPORT_SYMBOL(drm_gem_vram_unpin_reserved); +EXPORT_SYMBOL(drm_gem_vram_unpin_locked); /** * drm_gem_vram_kmap_at() - Maps a GEM VRAM object into kernel address space diff --git a/drivers/gpu/drm/mgag200/mgag200_cursor.c b/drivers/gpu/drm/mgag200/mgag200_cursor.c index 1c4fc85315a0..06a8c076cb33 100644 --- a/drivers/gpu/drm/mgag200/mgag200_cursor.c +++ b/drivers/gpu/drm/mgag200/mgag200_cursor.c @@ -23,9 +23,9 @@ static void mga_hide_cursor(struct mga_device *mdev) WREG8(MGA_CURPOSXL, 0); WREG8(MGA_CURPOSXH, 0); if (mdev->cursor.pixels_1->pin_count) - drm_gem_vram_unpin_reserved(mdev->cursor.pixels_1); + drm_gem_vram_unpin_locked(mdev->cursor.pixels_1); if (mdev->cursor.pixels_2->pin_count) - drm_gem_vram_unpin_reserved(mdev->cursor.pixels_2); + drm_gem_vram_unpin_locked(mdev->cursor.pixels_2); } int mga_crtc_cursor_set(struct drm_crtc *crtc, @@ -80,53 +80,53 @@ int mga_crtc_cursor_set(struct drm_crtc *crtc, if (!obj) return -ENOENT; - ret = drm_gem_vram_reserve(pixels_1, true); + ret = drm_gem_vram_lock(pixels_1, true); if (ret) { WREG8(MGA_CURPOSXL, 0); WREG8(MGA_CURPOSXH, 0); goto out_unref; } - ret = drm_gem_vram_reserve(pixels_2, true); + ret = drm_gem_vram_lock(pixels_2, true); if (ret) { WREG8(MGA_CURPOSXL, 0); WREG8(MGA_CURPOSXH, 0); - drm_gem_vram_unreserve(pixels_1); - goto out_unreserve1; + drm_gem_vram_unlock(pixels_1); + goto out_unlock1; } /* Move cursor buffers into VRAM if they aren't already */ if (!pixels_1->pin_count) { - ret = drm_gem_vram_pin_reserved(pixels_1, - DRM_GEM_VRAM_PL_FLAG_VRAM); + ret = drm_gem_vram_pin_locked(pixels_1, + DRM_GEM_VRAM_PL_FLAG_VRAM); if (ret) goto out1; gpu_addr = drm_gem_vram_offset(pixels_1); if (gpu_addr < 0) { - drm_gem_vram_unpin_reserved(pixels_1); + drm_gem_vram_unpin_locked(pixels_1); goto out1; } mdev->cursor.pixels_1_gpu_addr = gpu_addr; } if (!pixels_2->pin_count) { - ret = drm_gem_vram_pin_reserved(pixels_2, - DRM_GEM_VRAM_PL_FLAG_VRAM); + ret = drm_gem_vram_pin_locked(pixels_2, + DRM_GEM_VRAM_PL_FLAG_VRAM); if (ret) { - drm_gem_vram_unpin_reserved(pixels_1); + drm_gem_vram_unpin_locked(pixels_1); goto out1; } gpu_addr = drm_gem_vram_offset(pixels_2); if (gpu_addr < 0) { - drm_gem_vram_unpin_reserved(pixels_1); - drm_gem_vram_unpin_reserved(pixels_2); + drm_gem_vram_unpin_locked(pixels_1); + drm_gem_vram_unpin_locked(pixels_2); goto out1; } mdev->cursor.pixels_2_gpu_addr = gpu_addr; } gbo = drm_gem_vram_of_gem(obj); - ret = drm_gem_vram_reserve(gbo, true); + ret = drm_gem_vram_lock(gbo, true); if (ret) { - dev_err(&dev->pdev->dev, "failed to reserve user bo\n"); + dev_err(&dev->pdev->dev, "failed to lock user bo\n"); goto out1; } src = drm_gem_vram_kmap(gbo, true, NULL); @@ -250,13 +250,13 @@ int mga_crtc_cursor_set(struct drm_crtc *crtc, out3: drm_gem_vram_kunmap(gbo); out2: - drm_gem_vram_unreserve(gbo); + drm_gem_vram_unlock(gbo); out1: if (ret) mga_hide_cursor(mdev); - drm_gem_vram_unreserve(pixels_1); -out_unreserve1: - drm_gem_vram_unreserve(pixels_2); + drm_gem_vram_unlock(pixels_1); +out_unlock1: + drm_gem_vram_unlock(pixels_2); out_unref: drm_gem_object_put_unlocked(obj); diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 87217bdce9f8..97c575a9a86f 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -36,13 +36,12 @@ static void mga_dirty_update(struct mga_fbdev *mfbdev, obj = mfbdev->mfb.obj; gbo = drm_gem_vram_of_gem(obj); - /* - * try and reserve the BO, if we fail with busy - * then the BO is being moved and we should - * store up the damage until later. + /* Try to lock the BO. If we fail with -EBUSY then + * the BO is being moved and we should store up the + * damage until later. */ if (drm_can_sleep()) - ret = drm_gem_vram_reserve(gbo, true); + ret = drm_gem_vram_lock(gbo, true); if (ret) { if (ret != -EBUSY) return; @@ -101,7 +100,7 @@ static void mga_dirty_update(struct mga_fbdev *mfbdev, drm_gem_vram_kunmap(gbo); out: - drm_gem_vram_unreserve(gbo); + drm_gem_vram_unlock(gbo); } static void mga_fillrect(struct fb_info *info, diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index 22e52d7dfbb7..4d1d2c1bf32b 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -77,15 +77,15 @@ struct drm_gem_vram_object *drm_gem_vram_create(struct drm_device *dev, unsigned long pg_align, bool interruptible); void drm_gem_vram_put(struct drm_gem_vram_object *gbo); -int drm_gem_vram_reserve(struct drm_gem_vram_object *gbo, bool no_wait); -void drm_gem_vram_unreserve(struct drm_gem_vram_object *gbo); +int drm_gem_vram_lock(struct drm_gem_vram_object *gbo, bool no_wait); +void drm_gem_vram_unlock(struct drm_gem_vram_object *gbo); u64 drm_gem_vram_mmap_offset(struct drm_gem_vram_object *gbo); s64 drm_gem_vram_offset(struct drm_gem_vram_object *gbo); int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag); -int drm_gem_vram_pin_reserved(struct drm_gem_vram_object *gbo, +int drm_gem_vram_pin_locked(struct drm_gem_vram_object *gbo, unsigned long pl_flag); int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo); -int drm_gem_vram_unpin_reserved(struct drm_gem_vram_object *gbo); +int drm_gem_vram_unpin_locked(struct drm_gem_vram_object *gbo); void *drm_gem_vram_kmap_at(struct drm_gem_vram_object *gbo, bool map, bool *is_iomem, struct ttm_bo_kmap_obj *kmap); void *drm_gem_vram_kmap(struct drm_gem_vram_object *gbo, bool map, -- cgit v1.2.3 From d825c565304f33d5f717a35f74d5109ff4820cae Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sun, 19 May 2019 21:55:22 +0200 Subject: drm/gma500: remove empty gma_drm.h header file The header file gma_drm.h is empty so remove it and drop all uses of the file. Signed-off-by: Sam Ravnborg Cc: Patrik Jakobsson Cc: David Airlie Cc: Daniel Vetter Acked-by: Daniel Vetter Signed-off-by: Patrik Jakobsson Link: https://patchwork.freedesktop.org/patch/msgid/20190519195526.3422-2-sam@ravnborg.org --- drivers/gpu/drm/gma500/cdv_device.c | 2 +- drivers/gpu/drm/gma500/gem.c | 2 +- drivers/gpu/drm/gma500/intel_bios.c | 2 +- drivers/gpu/drm/gma500/intel_gmbus.c | 2 +- drivers/gpu/drm/gma500/mid_bios.c | 2 +- drivers/gpu/drm/gma500/oaktrail_device.c | 2 +- drivers/gpu/drm/gma500/psb_device.c | 2 +- drivers/gpu/drm/gma500/psb_drv.h | 2 +- drivers/gpu/drm/gma500/psb_intel_sdvo.c | 2 +- include/drm/gma_drm.h | 25 ------------------------- 10 files changed, 9 insertions(+), 34 deletions(-) delete mode 100644 include/drm/gma_drm.h (limited to 'include/drm') diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c index 34b85767e4da..6ba605ac7123 100644 --- a/drivers/gpu/drm/gma500/cdv_device.c +++ b/drivers/gpu/drm/gma500/cdv_device.c @@ -20,7 +20,7 @@ #include #include #include -#include + #include "psb_drv.h" #include "psb_reg.h" #include "psb_intel_reg.h" diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c index 576f1b272f23..cfc5b559c77b 100644 --- a/drivers/gpu/drm/gma500/gem.c +++ b/drivers/gpu/drm/gma500/gem.c @@ -25,8 +25,8 @@ #include #include -#include #include + #include "psb_drv.h" void psb_gem_free_object(struct drm_gem_object *obj) diff --git a/drivers/gpu/drm/gma500/intel_bios.c b/drivers/gpu/drm/gma500/intel_bios.c index 63bde4e86c6a..45c17c6f58f8 100644 --- a/drivers/gpu/drm/gma500/intel_bios.c +++ b/drivers/gpu/drm/gma500/intel_bios.c @@ -20,7 +20,7 @@ */ #include #include -#include + #include "psb_drv.h" #include "psb_intel_drv.h" #include "psb_intel_reg.h" diff --git a/drivers/gpu/drm/gma500/intel_gmbus.c b/drivers/gpu/drm/gma500/intel_gmbus.c index e7e22187c539..b1bf314ab150 100644 --- a/drivers/gpu/drm/gma500/intel_gmbus.c +++ b/drivers/gpu/drm/gma500/intel_gmbus.c @@ -30,8 +30,8 @@ #include #include #include + #include "psb_intel_drv.h" -#include #include "psb_drv.h" #include "psb_intel_reg.h" diff --git a/drivers/gpu/drm/gma500/mid_bios.c b/drivers/gpu/drm/gma500/mid_bios.c index 237041a37532..e00778b002ec 100644 --- a/drivers/gpu/drm/gma500/mid_bios.c +++ b/drivers/gpu/drm/gma500/mid_bios.c @@ -25,7 +25,7 @@ #include #include -#include + #include "psb_drv.h" #include "mid_bios.h" diff --git a/drivers/gpu/drm/gma500/oaktrail_device.c b/drivers/gpu/drm/gma500/oaktrail_device.c index ba30b43a3412..f0872e2c22d5 100644 --- a/drivers/gpu/drm/gma500/oaktrail_device.c +++ b/drivers/gpu/drm/gma500/oaktrail_device.c @@ -22,7 +22,7 @@ #include #include #include -#include + #include "psb_drv.h" #include "psb_reg.h" #include "psb_intel_reg.h" diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c index dc0f8527570c..9fd418985d70 100644 --- a/drivers/gpu/drm/gma500/psb_device.c +++ b/drivers/gpu/drm/gma500/psb_device.c @@ -20,7 +20,7 @@ #include #include #include -#include + #include "psb_drv.h" #include "psb_reg.h" #include "psb_intel_reg.h" diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index 941b238bdcc9..fbccf2abdad7 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -24,7 +24,7 @@ #include #include -#include + #include "psb_reg.h" #include "psb_intel_drv.h" #include "gma_display.h" diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index dd3cec0e3190..5090ef5f348d 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -32,8 +32,8 @@ #include #include #include + #include "psb_intel_drv.h" -#include #include "psb_drv.h" #include "psb_intel_sdvo_regs.h" #include "psb_intel_reg.h" diff --git a/include/drm/gma_drm.h b/include/drm/gma_drm.h deleted file mode 100644 index 87ac5e6ca551..000000000000 --- a/include/drm/gma_drm.h +++ /dev/null @@ -1,25 +0,0 @@ -/************************************************************************** - * Copyright (c) 2007-2011, Intel Corporation. - * All Rights Reserved. - * Copyright (c) 2008, Tungsten Graphics Inc. Cedar Park, TX., USA. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -#ifndef _GMA_DRM_H_ -#define _GMA_DRM_H_ - -#endif -- cgit v1.2.3 From fbb5d0353c62d10c3699ec844d2d015a762952d7 Mon Sep 17 00:00:00 2001 From: Uma Shankar Date: Thu, 16 May 2019 19:40:06 +0530 Subject: drm: Add HDR source metadata property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a blob property to get HDR metadata information from userspace. This will be send as part of AVI Infoframe to panel. It also implements get() and set() functions for HDR output metadata property.The blob data is received from userspace and saved in connector state, the same is returned as blob in get property call to userspace. v2: Rebase and modified the metadata structure elements as per Ville's POC changes. v3: No Change v4: Addressed Shashank's review comments v5: Rebase. v6: Addressed Brian Starkey's review comments, defined new structure with header for dynamic metadata scalability. Merge get/set property functions for metadata in this patch. v7: Addressed Jonas Karlman review comments and defined separate structure for infoframe to better align with CTA 861.G spec. Added Shashank's RB. v8: Addressed Ville's review comments. Moved sink metadata structure out of uapi headers as suggested by Jonas Karlman. v9: Rebase and addressed Jonas Karlman review comments. v10: Addressed Ville's review comments, dropped the metdata_changed state variable as its not needed anymore. Signed-off-by: Uma Shankar Reviewed-by: Shashank Sharma Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/1558015817-12025-2-git-send-email-uma.shankar@intel.com --- drivers/gpu/drm/drm_atomic_uapi.c | 12 ++++++++++++ drivers/gpu/drm/drm_connector.c | 6 ++++++ include/drm/drm_connector.h | 10 ++++++++++ include/drm/drm_mode_config.h | 7 +++++++ include/linux/hdmi.h | 26 ++++++++++++++++++++++++++ include/uapi/drm/drm_mode.h | 23 +++++++++++++++++++++++ 6 files changed, 84 insertions(+) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 428d82662dc4..125605ff45af 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -676,6 +676,8 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, { struct drm_device *dev = connector->dev; struct drm_mode_config *config = &dev->mode_config; + bool replaced = false; + int ret; if (property == config->prop_crtc_id) { struct drm_crtc *crtc = drm_crtc_find(dev, file_priv, val); @@ -726,6 +728,13 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, */ if (state->link_status != DRM_LINK_STATUS_GOOD) state->link_status = val; + } else if (property == config->hdr_output_metadata_property) { + ret = drm_atomic_replace_property_blob_from_id(dev, + &state->hdr_output_metadata, + val, + sizeof(struct hdr_output_metadata), -1, + &replaced); + return ret; } else if (property == config->aspect_ratio_property) { state->picture_aspect_ratio = val; } else if (property == config->content_type_property) { @@ -814,6 +823,9 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = state->colorspace; } else if (property == connector->scaling_mode_property) { *val = state->scaling_mode; + } else if (property == config->hdr_output_metadata_property) { + *val = state->hdr_output_metadata ? + state->hdr_output_metadata->base.id : 0; } else if (property == connector->content_protection_property) { *val = state->content_protection; } else if (property == config->writeback_fb_id_property) { diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index b34c3d38bf15..365ace0c0c9e 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1058,6 +1058,12 @@ int drm_connector_create_standard_properties(struct drm_device *dev) return -ENOMEM; dev->mode_config.non_desktop_property = prop; + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, + "HDR_OUTPUT_METADATA", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.hdr_output_metadata_property = prop; + return 0; } diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index f43f40d5888a..f0e987df4c1e 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -603,6 +603,12 @@ struct drm_connector_state { * and the connector bpc limitations obtained from edid. */ u8 max_bpc; + + /** + * @hdr_output_metadata: + * DRM blob property for HDR output metadata + */ + struct drm_property_blob *hdr_output_metadata; }; /** @@ -1243,6 +1249,10 @@ struct drm_connector { * &drm_mode_config.connector_free_work. */ struct llist_node free_node; + + /* HDR metdata */ + struct hdr_output_metadata hdr_output_metadata; + struct hdr_sink_metadata hdr_sink_metadata; }; #define obj_to_connector(x) container_of(x, struct drm_connector, base) diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 7f60e8eb269a..c031b5a9d8d1 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -836,6 +836,13 @@ struct drm_mode_config { */ struct drm_property *writeback_out_fence_ptr_property; + /** + * hdr_output_metadata_property: Connector property containing hdr + * metatda. This will be provided by userspace compositors based + * on HDR content + */ + struct drm_property *hdr_output_metadata_property; + /* dumb ioctl parameters */ uint32_t preferred_depth, prefer_shadow; diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index 927ad6451105..6780476dcbff 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -152,6 +152,16 @@ enum hdmi_content_type { HDMI_CONTENT_TYPE_GAME, }; +enum hdmi_metadata_type { + HDMI_STATIC_METADATA_TYPE1 = 1, +}; + +enum hdmi_eotf { + HDMI_EOTF_TRADITIONAL_GAMMA_SDR, + HDMI_EOTF_TRADITIONAL_GAMMA_HDR, + HDMI_EOTF_SMPTE_ST2084, +}; + struct hdmi_avi_infoframe { enum hdmi_infoframe_type type; unsigned char version; @@ -320,6 +330,22 @@ struct hdmi_vendor_infoframe { unsigned int s3d_ext_data; }; +/* HDR Metadata as per 861.G spec */ +struct hdr_static_metadata { + __u8 eotf; + __u8 metadata_type; + __u16 max_cll; + __u16 max_fall; + __u16 min_cll; +}; + +struct hdr_sink_metadata { + __u32 metadata_type; + union { + struct hdr_static_metadata hdmi_type1; + }; +}; + int hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame); ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, void *buffer, size_t size); diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 83cd1636b9be..997a7e05c0c6 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -630,6 +630,29 @@ struct drm_color_lut { __u16 reserved; }; +/* HDR Metadata Infoframe as per 861.G spec */ +struct hdr_metadata_infoframe { + __u8 eotf; + __u8 metadata_type; + struct { + __u16 x, y; + } display_primaries[3]; + struct { + __u16 x, y; + } white_point; + __u16 max_display_mastering_luminance; + __u16 min_display_mastering_luminance; + __u16 max_cll; + __u16 max_fall; +}; + +struct hdr_output_metadata { + __u32 metadata_type; + union { + struct hdr_metadata_infoframe hdmi_metadata_type1; + }; +}; + #define DRM_MODE_PAGE_FLIP_EVENT 0x01 #define DRM_MODE_PAGE_FLIP_ASYNC 0x02 #define DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE 0x4 -- cgit v1.2.3 From 2cdbfd66a82969770ce1a7032fb1e2155a08cee8 Mon Sep 17 00:00:00 2001 From: Uma Shankar Date: Thu, 16 May 2019 19:40:09 +0530 Subject: drm: Enable HDR infoframe support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable Dynamic Range and Mastering Infoframe for HDR content, which is defined in CEA 861.3 spec. The metadata will be computed based on blending policy in userspace compositors and passed as a connector property blob to driver. The same will be sent as infoframe to panel which support HDR. Added the const version of infoframe for DRM metadata for HDR. v2: Rebase and added Ville's POC changes. v3: No Change v4: Addressed Shashank's review comments and merged the patch making drm infoframe function arguments as constant. v5: Rebase v6: Fixed checkpatch warnings with --strict option. Addressed Shashank's review comments and added his RB. v7: Addressed Brian Starkey's review comments. Merged 2 patches into one. v8: Addressed Jonas Karlman review comments. v9: Addressed Jonas Karlman review comments. v10: Addressed Ville's review comments. v11: Added BUILD_BUG_ON and sizeof instead of magic numbers as per Ville's comments. Signed-off-by: Uma Shankar Reviewed-by: Shashank Sharma Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/1558015817-12025-5-git-send-email-uma.shankar@intel.com --- drivers/gpu/drm/drm_edid.c | 72 +++++++++++++++++ drivers/video/hdmi.c | 190 +++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_edid.h | 5 ++ include/linux/hdmi.h | 28 +++++++ 4 files changed, 295 insertions(+) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a5ef9f45fee0..73560c9437cd 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -4904,6 +4904,78 @@ static bool is_hdmi2_sink(struct drm_connector *connector) connector->display_info.color_formats & DRM_COLOR_FORMAT_YCRCB420; } +static inline bool is_eotf_supported(u8 output_eotf, u8 sink_eotf) +{ + return sink_eotf & BIT(output_eotf); +} + +/** + * drm_hdmi_infoframe_set_hdr_metadata() - fill an HDMI DRM infoframe with + * HDR metadata from userspace + * @frame: HDMI DRM infoframe + * @hdr_metadata: hdr_source_metadata info from userspace + * + * Return: 0 on success or a negative error code on failure. + */ +int +drm_hdmi_infoframe_set_hdr_metadata(struct hdmi_drm_infoframe *frame, + const struct drm_connector_state *conn_state) +{ + struct drm_connector *connector; + struct hdr_output_metadata *hdr_metadata; + int err; + + if (!frame || !conn_state) + return -EINVAL; + + connector = conn_state->connector; + + if (!conn_state->hdr_output_metadata) + return -EINVAL; + + hdr_metadata = conn_state->hdr_output_metadata->data; + + if (!hdr_metadata || !connector) + return -EINVAL; + + /* Sink EOTF is Bit map while infoframe is absolute values */ + if (!is_eotf_supported(hdr_metadata->hdmi_metadata_type1.eotf, + connector->hdr_sink_metadata.hdmi_type1.eotf)) { + DRM_DEBUG_KMS("EOTF Not Supported\n"); + return -EINVAL; + } + + err = hdmi_drm_infoframe_init(frame); + if (err < 0) + return err; + + frame->eotf = hdr_metadata->hdmi_metadata_type1.eotf; + frame->metadata_type = hdr_metadata->hdmi_metadata_type1.metadata_type; + + BUILD_BUG_ON(sizeof(frame->display_primaries) != + sizeof(hdr_metadata->hdmi_metadata_type1.display_primaries)); + BUILD_BUG_ON(sizeof(frame->white_point) != + sizeof(hdr_metadata->hdmi_metadata_type1.white_point)); + + memcpy(&frame->display_primaries, + &hdr_metadata->hdmi_metadata_type1.display_primaries, + sizeof(frame->display_primaries)); + + memcpy(&frame->white_point, + &hdr_metadata->hdmi_metadata_type1.white_point, + sizeof(frame->white_point)); + + frame->max_display_mastering_luminance = + hdr_metadata->hdmi_metadata_type1.max_display_mastering_luminance; + frame->min_display_mastering_luminance = + hdr_metadata->hdmi_metadata_type1.min_display_mastering_luminance; + frame->max_fall = hdr_metadata->hdmi_metadata_type1.max_fall; + frame->max_cll = hdr_metadata->hdmi_metadata_type1.max_cll; + + return 0; +} +EXPORT_SYMBOL(drm_hdmi_infoframe_set_hdr_metadata); + /** * drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with * data from a DRM display mode diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c index 799ae49774f5..481f0367dfd3 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -650,6 +650,150 @@ hdmi_vendor_any_infoframe_check_only(const union hdmi_vendor_any_infoframe *fram return 0; } +/** + * hdmi_drm_infoframe_init() - initialize an HDMI Dynaminc Range and + * mastering infoframe + * @frame: HDMI DRM infoframe + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_drm_infoframe_init(struct hdmi_drm_infoframe *frame) +{ + memset(frame, 0, sizeof(*frame)); + + frame->type = HDMI_INFOFRAME_TYPE_DRM; + frame->version = 1; + frame->length = HDMI_DRM_INFOFRAME_SIZE; + + return 0; +} +EXPORT_SYMBOL(hdmi_drm_infoframe_init); + +static int hdmi_drm_infoframe_check_only(const struct hdmi_drm_infoframe *frame) +{ + if (frame->type != HDMI_INFOFRAME_TYPE_DRM || + frame->version != 1) + return -EINVAL; + + if (frame->length != HDMI_DRM_INFOFRAME_SIZE) + return -EINVAL; + + return 0; +} + +/** + * hdmi_drm_infoframe_check() - check a HDMI DRM infoframe + * @frame: HDMI DRM infoframe + * + * Validates that the infoframe is consistent. + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_drm_infoframe_check(struct hdmi_drm_infoframe *frame) +{ + return hdmi_drm_infoframe_check_only(frame); +} +EXPORT_SYMBOL(hdmi_drm_infoframe_check); + +/** + * hdmi_drm_infoframe_pack_only() - write HDMI DRM infoframe to binary buffer + * @frame: HDMI DRM infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Packs the information contained in the @frame structure into a binary + * representation that can be written into the corresponding controller + * registers. Also computes the checksum as required by section 5.3.5 of + * the HDMI 1.4 specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_drm_infoframe_pack_only(const struct hdmi_drm_infoframe *frame, + void *buffer, size_t size) +{ + u8 *ptr = buffer; + size_t length; + int i; + + length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; + + if (size < length) + return -ENOSPC; + + memset(buffer, 0, size); + + ptr[0] = frame->type; + ptr[1] = frame->version; + ptr[2] = frame->length; + ptr[3] = 0; /* checksum */ + + /* start infoframe payload */ + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + *ptr++ = frame->eotf; + *ptr++ = frame->metadata_type; + + for (i = 0; i < 3; i++) { + *ptr++ = frame->display_primaries[i].x; + *ptr++ = frame->display_primaries[i].x >> 8; + *ptr++ = frame->display_primaries[i].y; + *ptr++ = frame->display_primaries[i].y >> 8; + } + + *ptr++ = frame->white_point.x; + *ptr++ = frame->white_point.x >> 8; + + *ptr++ = frame->white_point.y; + *ptr++ = frame->white_point.y >> 8; + + *ptr++ = frame->max_display_mastering_luminance; + *ptr++ = frame->max_display_mastering_luminance >> 8; + + *ptr++ = frame->min_display_mastering_luminance; + *ptr++ = frame->min_display_mastering_luminance >> 8; + + *ptr++ = frame->max_cll; + *ptr++ = frame->max_cll >> 8; + + *ptr++ = frame->max_fall; + *ptr++ = frame->max_fall >> 8; + + hdmi_infoframe_set_checksum(buffer, length); + + return length; +} +EXPORT_SYMBOL(hdmi_drm_infoframe_pack_only); + +/** + * hdmi_drm_infoframe_pack() - check a HDMI DRM infoframe, + * and write it to binary buffer + * @frame: HDMI DRM infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Validates that the infoframe is consistent and updates derived fields + * (eg. length) based on other fields, after which it packs the information + * contained in the @frame structure into a binary representation that + * can be written into the corresponding controller registers. This function + * also computes the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_drm_infoframe_pack(struct hdmi_drm_infoframe *frame, + void *buffer, size_t size) +{ + int ret; + + ret = hdmi_drm_infoframe_check(frame); + if (ret) + return ret; + + return hdmi_drm_infoframe_pack_only(frame, buffer, size); +} +EXPORT_SYMBOL(hdmi_drm_infoframe_pack); + /* * hdmi_vendor_any_infoframe_check() - check a vendor infoframe */ @@ -758,6 +902,10 @@ hdmi_infoframe_pack_only(const union hdmi_infoframe *frame, void *buffer, size_t length = hdmi_avi_infoframe_pack_only(&frame->avi, buffer, size); break; + case HDMI_INFOFRAME_TYPE_DRM: + length = hdmi_drm_infoframe_pack_only(&frame->drm, + buffer, size); + break; case HDMI_INFOFRAME_TYPE_SPD: length = hdmi_spd_infoframe_pack_only(&frame->spd, buffer, size); @@ -806,6 +954,9 @@ hdmi_infoframe_pack(union hdmi_infoframe *frame, case HDMI_INFOFRAME_TYPE_AVI: length = hdmi_avi_infoframe_pack(&frame->avi, buffer, size); break; + case HDMI_INFOFRAME_TYPE_DRM: + length = hdmi_drm_infoframe_pack(&frame->drm, buffer, size); + break; case HDMI_INFOFRAME_TYPE_SPD: length = hdmi_spd_infoframe_pack(&frame->spd, buffer, size); break; @@ -838,6 +989,8 @@ static const char *hdmi_infoframe_type_get_name(enum hdmi_infoframe_type type) return "Source Product Description (SPD)"; case HDMI_INFOFRAME_TYPE_AUDIO: return "Audio"; + case HDMI_INFOFRAME_TYPE_DRM: + return "Dynamic Range and Mastering"; } return "Reserved"; } @@ -1284,6 +1437,40 @@ static void hdmi_audio_infoframe_log(const char *level, frame->downmix_inhibit ? "Yes" : "No"); } +/** + * hdmi_drm_infoframe_log() - log info of HDMI DRM infoframe + * @level: logging level + * @dev: device + * @frame: HDMI DRM infoframe + */ +static void hdmi_drm_infoframe_log(const char *level, + struct device *dev, + const struct hdmi_drm_infoframe *frame) +{ + int i; + + hdmi_infoframe_log_header(level, dev, + (struct hdmi_any_infoframe *)frame); + hdmi_log("length: %d\n", frame->length); + hdmi_log("metadata type: %d\n", frame->metadata_type); + hdmi_log("eotf: %d\n", frame->eotf); + for (i = 0; i < 3; i++) { + hdmi_log("x[%d]: %d\n", i, frame->display_primaries[i].x); + hdmi_log("y[%d]: %d\n", i, frame->display_primaries[i].y); + } + + hdmi_log("white point x: %d\n", frame->white_point.x); + hdmi_log("white point y: %d\n", frame->white_point.y); + + hdmi_log("max_display_mastering_luminance: %d\n", + frame->max_display_mastering_luminance); + hdmi_log("min_display_mastering_luminance: %d\n", + frame->min_display_mastering_luminance); + + hdmi_log("max_cll: %d\n", frame->max_cll); + hdmi_log("max_fall: %d\n", frame->max_fall); +} + static const char * hdmi_3d_structure_get_name(enum hdmi_3d_structure s3d_struct) { @@ -1372,6 +1559,9 @@ void hdmi_infoframe_log(const char *level, case HDMI_INFOFRAME_TYPE_VENDOR: hdmi_vendor_any_infoframe_log(level, dev, &frame->vendor); break; + case HDMI_INFOFRAME_TYPE_DRM: + hdmi_drm_infoframe_log(level, dev, &frame->drm); + break; } } EXPORT_SYMBOL(hdmi_infoframe_log); diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 9d3b5b93102c..0e21e91c4314 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -25,6 +25,7 @@ #include #include +#include struct drm_device; struct i2c_adapter; @@ -370,6 +371,10 @@ drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe *frame, const struct drm_display_mode *mode, enum hdmi_quantization_range rgb_quant_range); +int +drm_hdmi_infoframe_set_hdr_metadata(struct hdmi_drm_infoframe *frame, + const struct drm_connector_state *conn_state); + /** * drm_eld_mnl - Get ELD monitor name length in bytes. * @eld: pointer to an eld memory structure with mnl set diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index 6780476dcbff..bcf3c6c3499e 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -47,6 +47,7 @@ enum hdmi_infoframe_type { HDMI_INFOFRAME_TYPE_AVI = 0x82, HDMI_INFOFRAME_TYPE_SPD = 0x83, HDMI_INFOFRAME_TYPE_AUDIO = 0x84, + HDMI_INFOFRAME_TYPE_DRM = 0x87, }; #define HDMI_IEEE_OUI 0x000c03 @@ -55,6 +56,7 @@ enum hdmi_infoframe_type { #define HDMI_AVI_INFOFRAME_SIZE 13 #define HDMI_SPD_INFOFRAME_SIZE 25 #define HDMI_AUDIO_INFOFRAME_SIZE 10 +#define HDMI_DRM_INFOFRAME_SIZE 26 #define HDMI_INFOFRAME_SIZE(type) \ (HDMI_INFOFRAME_HEADER_SIZE + HDMI_ ## type ## _INFOFRAME_SIZE) @@ -185,12 +187,37 @@ struct hdmi_avi_infoframe { unsigned short right_bar; }; +/* DRM Infoframe as per CTA 861.G spec */ +struct hdmi_drm_infoframe { + enum hdmi_infoframe_type type; + unsigned char version; + unsigned char length; + enum hdmi_eotf eotf; + enum hdmi_metadata_type metadata_type; + struct { + u16 x, y; + } display_primaries[3]; + struct { + u16 x, y; + } white_point; + u16 max_display_mastering_luminance; + u16 min_display_mastering_luminance; + u16 max_cll; + u16 max_fall; +}; + int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame); ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, size_t size); ssize_t hdmi_avi_infoframe_pack_only(const struct hdmi_avi_infoframe *frame, void *buffer, size_t size); int hdmi_avi_infoframe_check(struct hdmi_avi_infoframe *frame); +int hdmi_drm_infoframe_init(struct hdmi_drm_infoframe *frame); +ssize_t hdmi_drm_infoframe_pack(struct hdmi_drm_infoframe *frame, void *buffer, + size_t size); +ssize_t hdmi_drm_infoframe_pack_only(const struct hdmi_drm_infoframe *frame, + void *buffer, size_t size); +int hdmi_drm_infoframe_check(struct hdmi_drm_infoframe *frame); enum hdmi_spd_sdi { HDMI_SPD_SDI_UNKNOWN, @@ -381,6 +408,7 @@ union hdmi_infoframe { struct hdmi_spd_infoframe spd; union hdmi_vendor_any_infoframe vendor; struct hdmi_audio_infoframe audio; + struct hdmi_drm_infoframe drm; }; ssize_t hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, -- cgit v1.2.3 From 4d432f956d4f3fe5a7082e11ee029d43f6f64457 Mon Sep 17 00:00:00 2001 From: Gwan-gyeong Mun Date: Tue, 21 May 2019 15:17:17 +0300 Subject: drm: Rename struct edp_vsc_psr to struct dp_sdp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VSC SDP Payload for PSR is one of data block type of SDP (Secondaray Data Packet). In order to generalize SDP packet structure name, it renames struct edp_vsc_psr to struct dp_sdp. And each SDP data blocks have different usages, each SDP type has different reserved data blocks and Video_Stream_Configuration Extension VESA SDP might use all of Data Blocks as Extended INFORFRAME Data Byte. so it makes Data Block variables as array type. And it adds comments of details of DB of VSC SDP Payload for Pixel Encoding/Colorimetry Format. This comments follows DP 1.4a spec, section 2.2.5.7.5, chapter "VSC SDP Payload for Pixel Encoding/Colorimetry Format". v7: Addressed review comments from Ville. v9: Rename a member value name DB to db on struct dp_sdp [Laurent] Cc: Ville Syrjälä Signed-off-by: Gwan-gyeong Mun Reviewed-by: Maarten Lankhorst Acked-by: Laurent Pinchart Acked-by: Maarten Lankhorst Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20190521121721.32010-3-gwan-gyeong.mun@intel.com --- drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 12 ++++---- drivers/gpu/drm/bridge/analogix/analogix_dp_core.h | 2 +- drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c | 10 +++---- drivers/gpu/drm/i915/intel_psr.c | 2 +- include/drm/drm_dp_helper.h | 33 +++++++++++++++------- 5 files changed, 36 insertions(+), 23 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 225f5e5dd69b..257d69b21d99 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -115,7 +115,7 @@ EXPORT_SYMBOL_GPL(analogix_dp_psr_enabled); int analogix_dp_enable_psr(struct analogix_dp_device *dp) { - struct edp_vsc_psr psr_vsc; + struct dp_sdp psr_vsc; if (!dp->psr_enable) return 0; @@ -127,8 +127,8 @@ int analogix_dp_enable_psr(struct analogix_dp_device *dp) psr_vsc.sdp_header.HB2 = 0x2; psr_vsc.sdp_header.HB3 = 0x8; - psr_vsc.DB0 = 0; - psr_vsc.DB1 = EDP_VSC_PSR_STATE_ACTIVE | EDP_VSC_PSR_CRC_VALUES_VALID; + psr_vsc.db[0] = 0; + psr_vsc.db[1] = EDP_VSC_PSR_STATE_ACTIVE | EDP_VSC_PSR_CRC_VALUES_VALID; return analogix_dp_send_psr_spd(dp, &psr_vsc, true); } @@ -136,7 +136,7 @@ EXPORT_SYMBOL_GPL(analogix_dp_enable_psr); int analogix_dp_disable_psr(struct analogix_dp_device *dp) { - struct edp_vsc_psr psr_vsc; + struct dp_sdp psr_vsc; int ret; if (!dp->psr_enable) @@ -149,8 +149,8 @@ int analogix_dp_disable_psr(struct analogix_dp_device *dp) psr_vsc.sdp_header.HB2 = 0x2; psr_vsc.sdp_header.HB3 = 0x8; - psr_vsc.DB0 = 0; - psr_vsc.DB1 = 0; + psr_vsc.db[0] = 0; + psr_vsc.db[1] = 0; ret = drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D0); if (ret != 1) { diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h index 769255dc6e99..3e5fe90edf71 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h @@ -254,7 +254,7 @@ void analogix_dp_enable_scrambling(struct analogix_dp_device *dp); void analogix_dp_disable_scrambling(struct analogix_dp_device *dp); void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp); int analogix_dp_send_psr_spd(struct analogix_dp_device *dp, - struct edp_vsc_psr *vsc, bool blocking); + struct dp_sdp *vsc, bool blocking); ssize_t analogix_dp_transfer(struct analogix_dp_device *dp, struct drm_dp_aux_msg *msg); diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c index a5f2763d72e4..cf17e2e21b15 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c @@ -1041,7 +1041,7 @@ static ssize_t analogix_dp_get_psr_status(struct analogix_dp_device *dp) } int analogix_dp_send_psr_spd(struct analogix_dp_device *dp, - struct edp_vsc_psr *vsc, bool blocking) + struct dp_sdp *vsc, bool blocking) { unsigned int val; int ret; @@ -1069,8 +1069,8 @@ int analogix_dp_send_psr_spd(struct analogix_dp_device *dp, writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3); /* configure DB0 / DB1 values */ - writel(vsc->DB0, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0); - writel(vsc->DB1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1); + writel(vsc->db[0], dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0); + writel(vsc->db[1], dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1); /* set reuse spd inforframe */ val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3); @@ -1092,8 +1092,8 @@ int analogix_dp_send_psr_spd(struct analogix_dp_device *dp, ret = readx_poll_timeout(analogix_dp_get_psr_status, dp, psr_status, psr_status >= 0 && - ((vsc->DB1 && psr_status == DP_PSR_SINK_ACTIVE_RFB) || - (!vsc->DB1 && psr_status == DP_PSR_SINK_INACTIVE)), 1500, + ((vsc->db[1] && psr_status == DP_PSR_SINK_ACTIVE_RFB) || + (!vsc->db[1] && psr_status == DP_PSR_SINK_INACTIVE)), 1500, DP_TIMEOUT_PSR_LOOP_MS * 1000); if (ret) { dev_warn(dp->dev, "Failed to apply PSR %d\n", ret); diff --git a/drivers/gpu/drm/i915/intel_psr.c b/drivers/gpu/drm/i915/intel_psr.c index 2a547a128a37..01ca502099df 100644 --- a/drivers/gpu/drm/i915/intel_psr.c +++ b/drivers/gpu/drm/i915/intel_psr.c @@ -342,7 +342,7 @@ static void intel_psr_setup_vsc(struct intel_dp *intel_dp, { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - struct edp_vsc_psr psr_vsc; + struct dp_sdp psr_vsc; if (dev_priv->psr.psr2_enabled) { /* Prepare VSC Header for SU as per EDP 1.4 spec, Table 6.11 */ diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 97ce790a5b5a..3fc534ee8174 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1083,17 +1083,30 @@ struct dp_sdp_header { #define EDP_SDP_HEADER_VALID_PAYLOAD_BYTES 0x1F #define DP_SDP_PPS_HEADER_PAYLOAD_BYTES_MINUS_1 0x7F -struct edp_vsc_psr { +/** + * struct dp_sdp - DP secondary data packet + * @sdp_header: DP secondary data packet header + * @db: DP secondaray data packet data blocks + * VSC SDP Payload for PSR + * db[0]: Stereo Interface + * db[1]: 0 - PSR State; 1 - Update RFB; 2 - CRC Valid + * db[2]: CRC value bits 7:0 of the R or Cr component + * db[3]: CRC value bits 15:8 of the R or Cr component + * db[4]: CRC value bits 7:0 of the G or Y component + * db[5]: CRC value bits 15:8 of the G or Y component + * db[6]: CRC value bits 7:0 of the B or Cb component + * db[7]: CRC value bits 15:8 of the B or Cb component + * db[8] - db[31]: Reserved + * VSC SDP Payload for Pixel Encoding/Colorimetry Format + * db[0] - db[15]: Reserved + * db[16]: Pixel Encoding and Colorimetry Formats + * db[17]: Dynamic Range and Component Bit Depth + * db[18]: Content Type + * db[19] - db[31]: Reserved + */ +struct dp_sdp { struct dp_sdp_header sdp_header; - u8 DB0; /* Stereo Interface */ - u8 DB1; /* 0 - PSR State; 1 - Update RFB; 2 - CRC Valid */ - u8 DB2; /* CRC value bits 7:0 of the R or Cr component */ - u8 DB3; /* CRC value bits 15:8 of the R or Cr component */ - u8 DB4; /* CRC value bits 7:0 of the G or Y component */ - u8 DB5; /* CRC value bits 15:8 of the G or Y component */ - u8 DB6; /* CRC value bits 7:0 of the B or Cb component */ - u8 DB7; /* CRC value bits 15:8 of the B or Cb component */ - u8 DB8_31[24]; /* Reserved */ + u8 db[32]; } __packed; #define EDP_VSC_PSR_STATE_ACTIVE (1<<0) -- cgit v1.2.3 From 54e6a745aad30ba82bbea5b0e9869e42a39cbbc3 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sun, 19 May 2019 20:36:35 +0200 Subject: drm/bridge: make dw_mipi_dsi.h self-contained To allow users to include dw_mipi_dsi.h without pulling in dependencies make dw_mipi_dsi.h self-contained. Use forward declarations when possible. v2: - Drop forward declarations of local structs (Laurent) - Add include of drm_modes.h (Laurent) Signed-off-by: Sam Ravnborg Reviewed-by: Laurent Pinchart Cc: Andrzej Hajda Cc: David Airlie Cc: Daniel Vetter Cc: Peter Senna Tschudin Cc: Martin Donnelly Cc: Martyn Welch Link: https://patchwork.freedesktop.org/patch/msgid/20190519183636.19588-2-sam@ravnborg.org --- include/drm/bridge/dw_mipi_dsi.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include/drm') diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h index 7d3dd69a5caa..92cf9d1e1655 100644 --- a/include/drm/bridge/dw_mipi_dsi.h +++ b/include/drm/bridge/dw_mipi_dsi.h @@ -10,7 +10,15 @@ #ifndef __DW_MIPI_DSI__ #define __DW_MIPI_DSI__ +#include + +#include + +struct drm_display_mode; +struct drm_encoder; struct dw_mipi_dsi; +struct mipi_dsi_device; +struct platform_device; struct dw_mipi_dsi_phy_ops { int (*init)(void *priv_data); -- cgit v1.2.3 From 2197f55f462dc776312fada027d45d52bd12749c Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sun, 26 May 2019 19:35:29 +0200 Subject: drm: make drm/drm_auth.h self contained Do not require users of include/drm/drm_auth.h to include other files just to let it build. Signed-off-by: Sam Ravnborg Acked-by: Daniel Vetter Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Sean Paul Cc: David Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20190526173535.32701-2-sam@ravnborg.org --- include/drm/drm_auth.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_auth.h b/include/drm/drm_auth.h index 871008118bab..6bf8b2b78991 100644 --- a/include/drm/drm_auth.h +++ b/include/drm/drm_auth.h @@ -1,3 +1,6 @@ +#ifndef _DRM_AUTH_H_ +#define _DRM_AUTH_H_ + /* * Internal Header for the Direct Rendering Manager * @@ -25,8 +28,12 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef _DRM_AUTH_H_ -#define _DRM_AUTH_H_ +#include +#include +#include + +struct drm_file; +struct drm_hw_lock; /* * Legacy DRI1 locking data structure. Only here instead of in drm_legacy.h for -- cgit v1.2.3 From cbe932a38dc67f4c5c2fa6ffc4e7f4efa27c06f7 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sun, 26 May 2019 19:35:30 +0200 Subject: drm: make drm/drm_legacy.h self-contained Do not require users of include/drm/drm_legacy.h to include other files just to let it build. Signed-off-by: Sam Ravnborg Acked-by: Daniel Vetter Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Sean Paul Cc: David Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20190526173535.32701-3-sam@ravnborg.org --- include/drm/drm_legacy.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_legacy.h b/include/drm/drm_legacy.h index 2182a56ac421..58dc0c04bf99 100644 --- a/include/drm/drm_legacy.h +++ b/include/drm/drm_legacy.h @@ -1,11 +1,5 @@ #ifndef __DRM_DRM_LEGACY_H__ #define __DRM_DRM_LEGACY_H__ - -#include -#include - -struct drm_device; - /* * Legacy driver interfaces for the Direct Rendering Manager * @@ -39,6 +33,12 @@ struct drm_device; * OTHER DEALINGS IN THE SOFTWARE. */ +#include +#include +#include + +struct drm_device; +struct file; /* * Legacy Support for palateontologic DRM drivers -- cgit v1.2.3 From 6e58ab7ac7fac61acd7705a8abf1632462c1512a Mon Sep 17 00:00:00 2001 From: Christian König Date: Fri, 10 May 2019 14:15:08 +0200 Subject: drm/ttm: Make LRU removal optional v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are already doing this for DMA-buf imports and also for amdgpu VM BOs for quite a while now. If this doesn't run into any problems we are probably going to stop removing BOs from the LRU altogether. v2: drop BUG_ON from ttm_bo_add_to_lru Signed-off-by: Christian König Reviewed-by: Chunming Zhou Tested-by: Pierre-Eric Pelloux-Prayer Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 9 +++++---- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_csa.c | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 4 ++-- drivers/gpu/drm/qxl/qxl_release.c | 2 +- drivers/gpu/drm/radeon/radeon_gem.c | 2 +- drivers/gpu/drm/radeon/radeon_object.c | 2 +- drivers/gpu/drm/ttm/ttm_bo.c | 23 ++++++++++++----------- drivers/gpu/drm/ttm/ttm_execbuf_util.c | 20 ++++++++++++-------- drivers/gpu/drm/virtio/virtgpu_ioctl.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 3 ++- drivers/gpu/drm/vmwgfx/vmwgfx_validation.h | 2 +- include/drm/ttm/ttm_bo_driver.h | 5 ++++- include/drm/ttm/ttm_execbuf_util.h | 3 ++- 14 files changed, 46 insertions(+), 35 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index e30427161295..81e0e758cc54 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -585,7 +585,7 @@ static int reserve_bo_and_vm(struct kgd_mem *mem, amdgpu_vm_get_pd_bo(vm, &ctx->list, &ctx->vm_pd[0]); ret = ttm_eu_reserve_buffers(&ctx->ticket, &ctx->list, - false, &ctx->duplicates); + false, &ctx->duplicates, true); if (!ret) ctx->reserved = true; else { @@ -658,7 +658,7 @@ static int reserve_bo_and_cond_vms(struct kgd_mem *mem, } ret = ttm_eu_reserve_buffers(&ctx->ticket, &ctx->list, - false, &ctx->duplicates); + false, &ctx->duplicates, true); if (!ret) ctx->reserved = true; else @@ -1808,7 +1808,8 @@ static int validate_invalid_user_pages(struct amdkfd_process_info *process_info) } /* Reserve all BOs and page tables for validation */ - ret = ttm_eu_reserve_buffers(&ticket, &resv_list, false, &duplicates); + ret = ttm_eu_reserve_buffers(&ticket, &resv_list, false, &duplicates, + true); WARN(!list_empty(&duplicates), "Duplicates should be empty"); if (ret) goto out_free; @@ -2014,7 +2015,7 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef) } ret = ttm_eu_reserve_buffers(&ctx.ticket, &ctx.list, - false, &duplicate_save); + false, &duplicate_save, true); if (ret) { pr_debug("Memory eviction: TTM Reserve Failed. Try again\n"); goto ttm_reserve_fail; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index d72cc583ebd1..fff558cf385b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -648,7 +648,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, } r = ttm_eu_reserve_buffers(&p->ticket, &p->validated, true, - &duplicates); + &duplicates, true); if (unlikely(r != 0)) { if (r != -ERESTARTSYS) DRM_ERROR("ttm_eu_reserve_buffers failed.\n"); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_csa.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_csa.c index 54dd02a898b9..06f83cac0d3a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_csa.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_csa.c @@ -79,7 +79,7 @@ int amdgpu_map_static_csa(struct amdgpu_device *adev, struct amdgpu_vm *vm, list_add(&csa_tv.head, &list); amdgpu_vm_get_pd_bo(vm, &list, &pd); - r = ttm_eu_reserve_buffers(&ticket, &list, true, NULL); + r = ttm_eu_reserve_buffers(&ticket, &list, true, NULL, true); if (r) { DRM_ERROR("failed to reserve CSA,PD BOs: err=%d\n", r); return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 7b840367004c..d513a5ad03dd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -171,7 +171,7 @@ void amdgpu_gem_object_close(struct drm_gem_object *obj, amdgpu_vm_get_pd_bo(vm, &list, &vm_pd); - r = ttm_eu_reserve_buffers(&ticket, &list, false, &duplicates); + r = ttm_eu_reserve_buffers(&ticket, &list, false, &duplicates, true); if (r) { dev_err(adev->dev, "leaking bo va because " "we fail to reserve bo (%d)\n", r); @@ -608,7 +608,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, amdgpu_vm_get_pd_bo(&fpriv->vm, &list, &vm_pd); - r = ttm_eu_reserve_buffers(&ticket, &list, true, &duplicates); + r = ttm_eu_reserve_buffers(&ticket, &list, true, &duplicates, true); if (r) goto error_unref; diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c index 30f85f0130cb..49f9a9385393 100644 --- a/drivers/gpu/drm/qxl/qxl_release.c +++ b/drivers/gpu/drm/qxl/qxl_release.c @@ -256,7 +256,7 @@ int qxl_release_reserve_list(struct qxl_release *release, bool no_intr) return 0; ret = ttm_eu_reserve_buffers(&release->ticket, &release->bos, - !no_intr, NULL); + !no_intr, NULL, true); if (ret) return ret; diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index 44617dec8183..7411e69e2712 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -559,7 +559,7 @@ static void radeon_gem_va_update_vm(struct radeon_device *rdev, if (!vm_bos) return; - r = ttm_eu_reserve_buffers(&ticket, &list, true, NULL); + r = ttm_eu_reserve_buffers(&ticket, &list, true, NULL, true); if (r) goto error_free; diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 833e909706a9..36683de0300b 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -539,7 +539,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev, u64 bytes_moved_threshold = radeon_bo_get_threshold_for_moves(rdev); INIT_LIST_HEAD(&duplicates); - r = ttm_eu_reserve_buffers(ticket, head, true, &duplicates); + r = ttm_eu_reserve_buffers(ticket, head, true, &duplicates, true); if (unlikely(r != 0)) { return r; } diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 2845fceb2fbd..06bbcd2679b2 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -173,19 +173,20 @@ void ttm_bo_add_to_lru(struct ttm_buffer_object *bo) reservation_object_assert_held(bo->resv); - if (!(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) { - BUG_ON(!list_empty(&bo->lru)); + if (!list_empty(&bo->lru)) + return; - man = &bdev->man[bo->mem.mem_type]; - list_add_tail(&bo->lru, &man->lru[bo->priority]); - kref_get(&bo->list_kref); + if (bo->mem.placement & TTM_PL_FLAG_NO_EVICT) + return; - if (bo->ttm && !(bo->ttm->page_flags & - (TTM_PAGE_FLAG_SG | TTM_PAGE_FLAG_SWAPPED))) { - list_add_tail(&bo->swap, - &bdev->glob->swap_lru[bo->priority]); - kref_get(&bo->list_kref); - } + man = &bdev->man[bo->mem.mem_type]; + list_add_tail(&bo->lru, &man->lru[bo->priority]); + kref_get(&bo->list_kref); + + if (bo->ttm && !(bo->ttm->page_flags & + (TTM_PAGE_FLAG_SG | TTM_PAGE_FLAG_SWAPPED))) { + list_add_tail(&bo->swap, &bdev->glob->swap_lru[bo->priority]); + kref_get(&bo->list_kref); } } EXPORT_SYMBOL(ttm_bo_add_to_lru); diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c index 0075eb9a0b52..957ec375a4ba 100644 --- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c +++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c @@ -69,7 +69,8 @@ void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket, list_for_each_entry(entry, list, head) { struct ttm_buffer_object *bo = entry->bo; - ttm_bo_add_to_lru(bo); + if (list_empty(&bo->lru)) + ttm_bo_add_to_lru(bo); reservation_object_unlock(bo->resv); } spin_unlock(&glob->lru_lock); @@ -93,7 +94,7 @@ EXPORT_SYMBOL(ttm_eu_backoff_reservation); int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket, struct list_head *list, bool intr, - struct list_head *dups) + struct list_head *dups, bool del_lru) { struct ttm_bo_global *glob; struct ttm_validate_buffer *entry; @@ -172,11 +173,11 @@ int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket, list_add(&entry->head, list); } - if (ticket) - ww_acquire_done(ticket); - spin_lock(&glob->lru_lock); - ttm_eu_del_from_lru_locked(list); - spin_unlock(&glob->lru_lock); + if (del_lru) { + spin_lock(&glob->lru_lock); + ttm_eu_del_from_lru_locked(list); + spin_unlock(&glob->lru_lock); + } return 0; } EXPORT_SYMBOL(ttm_eu_reserve_buffers); @@ -203,7 +204,10 @@ void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket, reservation_object_add_shared_fence(bo->resv, fence); else reservation_object_add_excl_fence(bo->resv, fence); - ttm_bo_add_to_lru(bo); + if (list_empty(&bo->lru)) + ttm_bo_add_to_lru(bo); + else + ttm_bo_move_to_lru_tail(bo, NULL); reservation_object_unlock(bo->resv); } spin_unlock(&glob->lru_lock); diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c index b7f9dfe61d1c..fe0bd6274db6 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c @@ -63,7 +63,7 @@ int virtio_gpu_object_list_validate(struct ww_acquire_ctx *ticket, struct virtio_gpu_object *qobj; int ret; - ret = ttm_eu_reserve_buffers(ticket, head, true, NULL); + ret = ttm_eu_reserve_buffers(ticket, head, true, NULL, true); if (ret != 0) return ret; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 711f8fd0dd45..1d38a8b2f2ec 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -464,7 +464,8 @@ vmw_resource_check_buffer(struct ww_acquire_ctx *ticket, val_buf->bo = &res->backup->base; val_buf->num_shared = 0; list_add_tail(&val_buf->head, &val_list); - ret = ttm_eu_reserve_buffers(ticket, &val_list, interruptible, NULL); + ret = ttm_eu_reserve_buffers(ticket, &val_list, interruptible, NULL, + true); if (unlikely(ret != 0)) goto out_no_reserve; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h index 523f6ac5c335..1d2322ad6fd5 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h @@ -169,7 +169,7 @@ vmw_validation_bo_reserve(struct vmw_validation_context *ctx, bool intr) { return ttm_eu_reserve_buffers(&ctx->ticket, &ctx->bo_list, intr, - NULL); + NULL, true); } /** diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 129dabbc002d..9f54cf9c60df 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -769,7 +769,10 @@ static inline void ttm_bo_unreserve(struct ttm_buffer_object *bo) { if (!(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) { spin_lock(&bo->bdev->glob->lru_lock); - ttm_bo_add_to_lru(bo); + if (list_empty(&bo->lru)) + ttm_bo_add_to_lru(bo); + else + ttm_bo_move_to_lru_tail(bo, NULL); spin_unlock(&bo->bdev->glob->lru_lock); } reservation_object_unlock(bo->resv); diff --git a/include/drm/ttm/ttm_execbuf_util.h b/include/drm/ttm/ttm_execbuf_util.h index 621615fa7728..7e46cc678e7e 100644 --- a/include/drm/ttm/ttm_execbuf_util.h +++ b/include/drm/ttm/ttm_execbuf_util.h @@ -70,6 +70,7 @@ extern void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket, * @list: thread private list of ttm_validate_buffer structs. * @intr: should the wait be interruptible * @dups: [out] optional list of duplicates. + * @del_lru: true if BOs should be removed from the LRU. * * Tries to reserve bos pointed to by the list entries for validation. * If the function returns 0, all buffers are marked as "unfenced", @@ -98,7 +99,7 @@ extern void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket, extern int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket, struct list_head *list, bool intr, - struct list_head *dups); + struct list_head *dups, bool del_lru); /** * function ttm_eu_fence_buffer_objects. -- cgit v1.2.3 From 848d56dddad8a3a052a983d2f47b739d0492b8b1 Mon Sep 17 00:00:00 2001 From: Uma Shankar Date: Thu, 30 May 2019 01:29:01 +0530 Subject: drm: Drop a redundant unused variable Drop a redundant and unused variable "hdr_output_metadata" from drm_connector. Suggested-by: Daniel Vetter Signed-off-by: Uma Shankar Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/1559159944-21103-2-git-send-email-uma.shankar@intel.com --- include/drm/drm_connector.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index f8f4003e2758..547656173c74 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1244,8 +1244,6 @@ struct drm_connector { */ struct llist_node free_node; - /* HDR metdata */ - struct hdr_output_metadata hdr_output_metadata; struct hdr_sink_metadata hdr_sink_metadata; }; -- cgit v1.2.3 From 1b94f47793b1b1c4149d8192fa7b859fffd0e7ea Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 3 Jun 2019 16:28:48 +0200 Subject: drm/docs: More links for implicit/explicit fencing. drm_atomic_set_fence_for_plane() contains the main discussion from a driver pov, link to that from more places. Cc: Pekka Paalanen Reviewed-by: Pekka Paalanen Signed-off-by: Daniel Vetter Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Sean Paul Cc: David Airlie Cc: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190603142848.26487-1-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_gem_framebuffer_helper.c | 6 ++++++ include/drm/drm_plane.h | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 6fd48efe288c..6791245963c3 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -284,6 +284,9 @@ EXPORT_SYMBOL_GPL(drm_gem_fb_create_with_dirty); * There is no need for &drm_plane_helper_funcs.cleanup_fb hook for simple * gem based framebuffer drivers which have their buffers always pinned in * memory. + * + * See drm_atomic_set_fence_for_plane() for a discussion of implicit and + * explicit fencing in atomic modeset updates. */ int drm_gem_fb_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state) @@ -314,6 +317,9 @@ EXPORT_SYMBOL_GPL(drm_gem_fb_prepare_fb); * &dma_buf attached, extracts the exclusive fence and attaches it to plane * state for the atomic helper to wait on. Drivers can use this as their * &drm_simple_display_pipe_funcs.prepare_fb callback. + * + * See drm_atomic_set_fence_for_plane() for a discussion of implicit and + * explicit fencing in atomic modeset updates. */ int drm_gem_fb_simple_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe, struct drm_plane_state *plane_state) diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h index 6078c700d9ba..cd5903ad33f7 100644 --- a/include/drm/drm_plane.h +++ b/include/drm/drm_plane.h @@ -69,7 +69,7 @@ struct drm_plane_state { * * Optional fence to wait for before scanning out @fb. The core atomic * code will set this when userspace is using explicit fencing. Do not - * write this directly for a driver's implicit fence, use + * write this field directly for a driver's implicit fence, use * drm_atomic_set_fence_for_plane() to ensure that an explicit fence is * preserved. * -- cgit v1.2.3 From d81294afeecdacc8d84804ba0bcb3d39e64d0f27 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Fri, 31 May 2019 16:01:11 +0200 Subject: drm/fb-helper: Remove drm_fb_helper_crtc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct drm_fb_helper_crtc is now just a wrapper around drm_mode_set so use that directly instead and attach it as a modeset array onto drm_client_dev. drm_fb_helper will use this array to store its modesets which means it will always initialize a drm_client, but it will not register the client (callbacks) unless it's the generic fbdev emulation. Code will later be moved to drm_client, so add code there in a new file drm_client_modeset.c with MIT license to match drm_fb_helper.c. The modeset connector array size is hardcoded for the cloned case to avoid having to pass in a value from the driver. A value of 8 is chosen to err on the safe side. This means that the max connector argument for drm_fb_helper_init() and drm_fb_helper_fbdev_setup() isn't used anymore, a todo entry for this is added. In pan_display_atomic() restore_fbdev_mode_force() is used instead of restore_fbdev_mode_atomic() because that one will later become internal to drm_client_modeset. Locking order: 1. drm_fb_helper->lock 2. drm_master_internal_acquire 3. drm_client_dev->modeset_mutex v6: Improve commit message (Sam Ravnborg) v3: - Use full drm_client_init/release for the modesets (Daniel Vetter) - drm_client_for_each_modeset: use lockdep_assert_held (Daniel Vetter) - Hook up to Documentation/gpu/drm-client.rst (Daniel Vetter) v2: - Add modesets array to drm_client (Daniel Vetter) - Use a new file for the modeset code (Daniel Vetter) - File has to be MIT licensed (Emmanuel Vadot) - Add copyrights from drm_fb_helper.c Signed-off-by: Noralf Trønnes Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20190531140117.37751-3-noralf@tronnes.org --- Documentation/gpu/drm-client.rst | 3 + Documentation/gpu/todo.rst | 3 + drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_client.c | 10 +- drivers/gpu/drm/drm_client_modeset.c | 104 ++++++++++++ drivers/gpu/drm/drm_fb_helper.c | 299 +++++++++++++++-------------------- include/drm/drm_client.h | 30 ++++ include/drm/drm_fb_helper.h | 8 - 8 files changed, 273 insertions(+), 186 deletions(-) create mode 100644 drivers/gpu/drm/drm_client_modeset.c (limited to 'include/drm') diff --git a/Documentation/gpu/drm-client.rst b/Documentation/gpu/drm-client.rst index 7e672063e7eb..58b5a1d1219d 100644 --- a/Documentation/gpu/drm-client.rst +++ b/Documentation/gpu/drm-client.rst @@ -10,3 +10,6 @@ Kernel clients .. kernel-doc:: drivers/gpu/drm/drm_client.c :export: + +.. kernel-doc:: drivers/gpu/drm/drm_client_modeset.c + :export: diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 66f05f4e469f..9d4038c50013 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -289,6 +289,9 @@ drm_fb_helper tasks these igt tests need to be fixed: kms_fbcon_fbt@psr and kms_fbcon_fbt@psr-suspend. +- The max connector argument for drm_fb_helper_init() and + drm_fb_helper_fbdev_setup() isn't used anymore and can be removed. + Core refactorings ================= diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 4a63c1fcf389..d36feb4a6233 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -17,7 +17,7 @@ drm-y := drm_auth.o drm_cache.o \ drm_plane.o drm_color_mgmt.o drm_print.o \ drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \ - drm_atomic_uapi.o drm_hdcp.o + drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o drm-$(CONFIG_DRM_LEGACY) += drm_legacy_misc.o drm_bufs.o drm_context.o drm_dma.o drm_scatter.o drm_lock.o drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index e7870a54f498..410572f14257 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c @@ -27,7 +27,6 @@ * DOC: overview * * This library provides support for clients running in the kernel like fbdev and bootsplash. - * Currently it's only partially implemented, just enough to support fbdev. * * GEM drivers which provide a GEM based dumb buffer with a virtual address are supported. */ @@ -92,14 +91,20 @@ int drm_client_init(struct drm_device *dev, struct drm_client_dev *client, client->name = name; client->funcs = funcs; - ret = drm_client_open(client); + ret = drm_client_modeset_create(client); if (ret) goto err_put_module; + ret = drm_client_open(client); + if (ret) + goto err_free; + drm_dev_get(dev); return 0; +err_free: + drm_client_modeset_free(client); err_put_module: if (funcs) module_put(funcs->owner); @@ -148,6 +153,7 @@ void drm_client_release(struct drm_client_dev *client) DRM_DEV_DEBUG_KMS(dev->dev, "%s\n", client->name); + drm_client_modeset_free(client); drm_client_close(client); drm_dev_put(dev); if (client->funcs) diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c new file mode 100644 index 000000000000..66770ed3299e --- /dev/null +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2018 Noralf Trønnes + * Copyright (c) 2006-2009 Red Hat Inc. + * Copyright (c) 2006-2008 Intel Corporation + * Jesse Barnes + * Copyright (c) 2007 Dave Airlie + */ + +#include +#include +#include + +#include +#include +#include + +int drm_client_modeset_create(struct drm_client_dev *client) +{ + struct drm_device *dev = client->dev; + unsigned int num_crtc = dev->mode_config.num_crtc; + unsigned int max_connector_count = 1; + struct drm_mode_set *modeset; + struct drm_crtc *crtc; + unsigned int i = 0; + + /* Add terminating zero entry to enable index less iteration */ + client->modesets = kcalloc(num_crtc + 1, sizeof(*client->modesets), GFP_KERNEL); + if (!client->modesets) + return -ENOMEM; + + mutex_init(&client->modeset_mutex); + + drm_for_each_crtc(crtc, dev) + client->modesets[i++].crtc = crtc; + + /* Cloning is only supported in the single crtc case. */ + if (num_crtc == 1) + max_connector_count = DRM_CLIENT_MAX_CLONED_CONNECTORS; + + for (modeset = client->modesets; modeset->crtc; modeset++) { + modeset->connectors = kcalloc(max_connector_count, + sizeof(*modeset->connectors), GFP_KERNEL); + if (!modeset->connectors) + goto err_free; + } + + return 0; + +err_free: + drm_client_modeset_free(client); + + return -ENOMEM; +} + +void drm_client_modeset_release(struct drm_client_dev *client) +{ + struct drm_mode_set *modeset; + unsigned int i; + + drm_client_for_each_modeset(modeset, client) { + drm_mode_destroy(client->dev, modeset->mode); + modeset->mode = NULL; + modeset->fb = NULL; + + for (i = 0; i < modeset->num_connectors; i++) { + drm_connector_put(modeset->connectors[i]); + modeset->connectors[i] = NULL; + } + modeset->num_connectors = 0; + } +} +/* TODO: Remove export when modeset code has been moved over */ +EXPORT_SYMBOL(drm_client_modeset_release); + +void drm_client_modeset_free(struct drm_client_dev *client) +{ + struct drm_mode_set *modeset; + + mutex_lock(&client->modeset_mutex); + + drm_client_modeset_release(client); + + drm_client_for_each_modeset(modeset, client) + kfree(modeset->connectors); + + mutex_unlock(&client->modeset_mutex); + + mutex_destroy(&client->modeset_mutex); + kfree(client->modesets); +} + +struct drm_mode_set *drm_client_find_modeset(struct drm_client_dev *client, struct drm_crtc *crtc) +{ + struct drm_mode_set *modeset; + + drm_client_for_each_modeset(modeset, client) + if (modeset->crtc == crtc) + return modeset; + + return NULL; +} +/* TODO: Remove export when modeset code has been moved over */ +EXPORT_SYMBOL(drm_client_find_modeset); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 3f4cb66c7d65..b9b7c06cbc4f 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -322,13 +322,11 @@ int drm_fb_helper_debug_enter(struct fb_info *info) { struct drm_fb_helper *helper = info->par; const struct drm_crtc_helper_funcs *funcs; - int i; + struct drm_mode_set *mode_set; list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { - for (i = 0; i < helper->crtc_count; i++) { - struct drm_mode_set *mode_set = - &helper->crtc_info[i].mode_set; - + mutex_lock(&helper->client.modeset_mutex); + drm_client_for_each_modeset(mode_set, &helper->client) { if (!mode_set->crtc->enabled) continue; @@ -345,6 +343,7 @@ int drm_fb_helper_debug_enter(struct fb_info *info) mode_set->y, ENTER_ATOMIC_MODE_SET); } + mutex_unlock(&helper->client.modeset_mutex); } return 0; @@ -358,14 +357,14 @@ EXPORT_SYMBOL(drm_fb_helper_debug_enter); int drm_fb_helper_debug_leave(struct fb_info *info) { struct drm_fb_helper *helper = info->par; + struct drm_client_dev *client = &helper->client; struct drm_crtc *crtc; const struct drm_crtc_helper_funcs *funcs; + struct drm_mode_set *mode_set; struct drm_framebuffer *fb; - int i; - - for (i = 0; i < helper->crtc_count; i++) { - struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; + mutex_lock(&client->modeset_mutex); + drm_client_for_each_modeset(mode_set, client) { crtc = mode_set->crtc; if (drm_drv_uses_atomic_modeset(crtc->dev)) continue; @@ -388,6 +387,7 @@ int drm_fb_helper_debug_leave(struct fb_info *info) funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x, crtc->y, LEAVE_ATOMIC_MODE_SET); } + mutex_unlock(&client->modeset_mutex); return 0; } @@ -438,12 +438,14 @@ static bool drm_fb_helper_panel_rotation(struct drm_mode_set *modeset, static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool active) { + struct drm_client_dev *client = &fb_helper->client; struct drm_device *dev = fb_helper->dev; struct drm_plane_state *plane_state; struct drm_plane *plane; struct drm_atomic_state *state; - int i, ret; struct drm_modeset_acquire_ctx ctx; + struct drm_mode_set *mode_set; + int ret; drm_modeset_acquire_init(&ctx, 0); @@ -473,8 +475,7 @@ retry: goto out_state; } - for (i = 0; i < fb_helper->crtc_count; i++) { - struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; + drm_client_for_each_modeset(mode_set, client) { struct drm_plane *primary = mode_set->crtc->primary; unsigned int rotation; @@ -522,9 +523,11 @@ backoff: static int restore_fbdev_mode_legacy(struct drm_fb_helper *fb_helper) { + struct drm_client_dev *client = &fb_helper->client; struct drm_device *dev = fb_helper->dev; + struct drm_mode_set *mode_set; struct drm_plane *plane; - int i, ret = 0; + int ret = 0; drm_modeset_lock_all(fb_helper->dev); drm_for_each_plane(plane, dev) { @@ -537,8 +540,7 @@ static int restore_fbdev_mode_legacy(struct drm_fb_helper *fb_helper) DRM_MODE_ROTATE_0); } - for (i = 0; i < fb_helper->crtc_count; i++) { - struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; + drm_client_for_each_modeset(mode_set, client) { struct drm_crtc *crtc = mode_set->crtc; if (crtc->funcs->cursor_set2) { @@ -564,11 +566,16 @@ out: static int restore_fbdev_mode_force(struct drm_fb_helper *fb_helper) { struct drm_device *dev = fb_helper->dev; + int ret; + mutex_lock(&fb_helper->client.modeset_mutex); if (drm_drv_uses_atomic_modeset(dev)) - return restore_fbdev_mode_atomic(fb_helper, true); + ret = restore_fbdev_mode_atomic(fb_helper, true); else - return restore_fbdev_mode_legacy(fb_helper); + ret = restore_fbdev_mode_legacy(fb_helper); + mutex_unlock(&fb_helper->client.modeset_mutex); + + return ret; } static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) @@ -687,15 +694,14 @@ static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { }; static void dpms_legacy(struct drm_fb_helper *fb_helper, int dpms_mode) { + struct drm_client_dev *client = &fb_helper->client; struct drm_device *dev = fb_helper->dev; struct drm_connector *connector; struct drm_mode_set *modeset; - int i, j; + int j; drm_modeset_lock_all(dev); - for (i = 0; i < fb_helper->crtc_count; i++) { - modeset = &fb_helper->crtc_info[i].mode_set; - + drm_client_for_each_modeset(modeset, client) { if (!modeset->crtc->enabled) continue; @@ -712,6 +718,7 @@ static void dpms_legacy(struct drm_fb_helper *fb_helper, int dpms_mode) static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) { struct drm_fb_helper *fb_helper = info->par; + struct drm_client_dev *client = &fb_helper->client; struct drm_device *dev = fb_helper->dev; /* @@ -721,10 +728,12 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) if (!drm_master_internal_acquire(dev)) goto unlock; + mutex_lock(&client->modeset_mutex); if (drm_drv_uses_atomic_modeset(dev)) restore_fbdev_mode_atomic(fb_helper, dpms_mode == DRM_MODE_DPMS_ON); else dpms_legacy(fb_helper, dpms_mode); + mutex_unlock(&client->modeset_mutex); drm_master_internal_release(dev); unlock: @@ -767,43 +776,6 @@ int drm_fb_helper_blank(int blank, struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_blank); -static void drm_fb_helper_modeset_release(struct drm_fb_helper *helper, - struct drm_mode_set *modeset) -{ - int i; - - for (i = 0; i < modeset->num_connectors; i++) { - drm_connector_put(modeset->connectors[i]); - modeset->connectors[i] = NULL; - } - modeset->num_connectors = 0; - - drm_mode_destroy(helper->dev, modeset->mode); - modeset->mode = NULL; - - /* FIXME should hold a ref? */ - modeset->fb = NULL; -} - -static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) -{ - int i; - - for (i = 0; i < helper->connector_count; i++) { - drm_connector_put(helper->connector_info[i]->connector); - kfree(helper->connector_info[i]); - } - kfree(helper->connector_info); - - for (i = 0; i < helper->crtc_count; i++) { - struct drm_mode_set *modeset = &helper->crtc_info[i].mode_set; - - drm_fb_helper_modeset_release(helper, modeset); - kfree(modeset->connectors); - } - kfree(helper->crtc_info); -} - static void drm_fb_helper_resume_worker(struct work_struct *work) { struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper, @@ -882,7 +854,7 @@ EXPORT_SYMBOL(drm_fb_helper_prepare); * drm_fb_helper_init - initialize a &struct drm_fb_helper * @dev: drm device * @fb_helper: driver-allocated fbdev helper structure to initialize - * @max_conn_count: max connector count + * @max_conn_count: max connector count (not used) * * This allocates the structures for the fbdev helper with the given limits. * Note that this won't yet touch the hardware (through the driver interfaces) @@ -898,53 +870,36 @@ int drm_fb_helper_init(struct drm_device *dev, struct drm_fb_helper *fb_helper, int max_conn_count) { - struct drm_crtc *crtc; - struct drm_mode_config *config = &dev->mode_config; - int i; + int ret; if (!drm_fbdev_emulation) { dev->fb_helper = fb_helper; return 0; } - if (!max_conn_count) - return -EINVAL; - - fb_helper->crtc_info = kcalloc(config->num_crtc, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); - if (!fb_helper->crtc_info) - return -ENOMEM; + /* + * If this is not the generic fbdev client, initialize a drm_client + * without callbacks so we can use the modesets. + */ + if (!fb_helper->client.funcs) { + ret = drm_client_init(dev, &fb_helper->client, "drm_fb_helper", NULL); + if (ret) + return ret; + } - fb_helper->crtc_count = config->num_crtc; fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL); - if (!fb_helper->connector_info) { - kfree(fb_helper->crtc_info); - return -ENOMEM; - } + if (!fb_helper->connector_info) + goto out_free; + fb_helper->connector_info_alloc_count = dev->mode_config.num_connector; fb_helper->connector_count = 0; - for (i = 0; i < fb_helper->crtc_count; i++) { - fb_helper->crtc_info[i].mode_set.connectors = - kcalloc(max_conn_count, - sizeof(struct drm_connector *), - GFP_KERNEL); - - if (!fb_helper->crtc_info[i].mode_set.connectors) - goto out_free; - fb_helper->crtc_info[i].mode_set.num_connectors = 0; - } - - i = 0; - drm_for_each_crtc(crtc, dev) { - fb_helper->crtc_info[i].mode_set.crtc = crtc; - i++; - } - dev->fb_helper = fb_helper; return 0; out_free: - drm_fb_helper_crtc_free(fb_helper); + drm_client_release(&fb_helper->client); + return -ENOMEM; } EXPORT_SYMBOL(drm_fb_helper_init); @@ -1020,6 +975,7 @@ EXPORT_SYMBOL(drm_fb_helper_unregister_fbi); void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) { struct fb_info *info; + int i; if (!fb_helper) return; @@ -1049,8 +1005,15 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) mutex_unlock(&kernel_fb_helper_lock); mutex_destroy(&fb_helper->lock); - drm_fb_helper_crtc_free(fb_helper); + if (!fb_helper->client.funcs) + drm_client_release(&fb_helper->client); + + for (i = 0; i < fb_helper->connector_count; i++) { + drm_connector_put(fb_helper->connector_info[i]->connector); + kfree(fb_helper->connector_info[i]); + } + kfree(fb_helper->connector_info); } EXPORT_SYMBOL(drm_fb_helper_fini); @@ -1395,13 +1358,14 @@ static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info) static int setcmap_legacy(struct fb_cmap *cmap, struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; + struct drm_mode_set *modeset; struct drm_crtc *crtc; u16 *r, *g, *b; - int i, ret = 0; + int ret = 0; drm_modeset_lock_all(fb_helper->dev); - for (i = 0; i < fb_helper->crtc_count; i++) { - crtc = fb_helper->crtc_info[i].mode_set.crtc; + drm_client_for_each_modeset(modeset, &fb_helper->client) { + crtc = modeset->crtc; if (!crtc->funcs->gamma_set || !crtc->gamma_size) return -EINVAL; @@ -1477,10 +1441,11 @@ static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info) struct drm_modeset_acquire_ctx ctx; struct drm_crtc_state *crtc_state; struct drm_atomic_state *state; + struct drm_mode_set *modeset; struct drm_crtc *crtc; u16 *r, *g, *b; - int i, ret = 0; bool replaced; + int ret = 0; drm_modeset_acquire_init(&ctx, 0); @@ -1492,8 +1457,8 @@ static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info) state->acquire_ctx = &ctx; retry: - for (i = 0; i < fb_helper->crtc_count; i++) { - crtc = fb_helper->crtc_info[i].mode_set.crtc; + drm_client_for_each_modeset(modeset, &fb_helper->client) { + crtc = modeset->crtc; if (!gamma_lut) gamma_lut = setcmap_new_gamma_lut(crtc, cmap); @@ -1521,8 +1486,8 @@ retry: if (ret) goto out_state; - for (i = 0; i < fb_helper->crtc_count; i++) { - crtc = fb_helper->crtc_info[i].mode_set.crtc; + drm_client_for_each_modeset(modeset, &fb_helper->client) { + crtc = modeset->crtc; r = crtc->gamma_store; g = r + crtc->gamma_size; @@ -1572,12 +1537,14 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) goto unlock; } + mutex_lock(&fb_helper->client.modeset_mutex); if (info->fix.visual == FB_VISUAL_TRUECOLOR) ret = setcmap_pseudo_palette(cmap, info); else if (drm_drv_uses_atomic_modeset(fb_helper->dev)) ret = setcmap_atomic(cmap, info); else ret = setcmap_legacy(cmap, info); + mutex_unlock(&fb_helper->client.modeset_mutex); drm_master_internal_release(dev); unlock: @@ -1601,7 +1568,6 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, { struct drm_fb_helper *fb_helper = info->par; struct drm_device *dev = fb_helper->dev; - struct drm_mode_set *mode_set; struct drm_crtc *crtc; int ret = 0; @@ -1629,8 +1595,7 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, * make. If we're not smart enough here, one should * just consider switch the userspace to KMS. */ - mode_set = &fb_helper->crtc_info[0].mode_set; - crtc = mode_set->crtc; + crtc = fb_helper->client.modesets[0].crtc; /* * Only wait for a vblank event if the CRTC is @@ -1827,16 +1792,14 @@ EXPORT_SYMBOL(drm_fb_helper_set_par); static void pan_set(struct drm_fb_helper *fb_helper, int x, int y) { - int i; - - for (i = 0; i < fb_helper->crtc_count; i++) { - struct drm_mode_set *mode_set; - - mode_set = &fb_helper->crtc_info[i].mode_set; + struct drm_mode_set *mode_set; + mutex_lock(&fb_helper->client.modeset_mutex); + drm_client_for_each_modeset(mode_set, &fb_helper->client) { mode_set->x = x; mode_set->y = y; } + mutex_unlock(&fb_helper->client.modeset_mutex); } static int pan_display_atomic(struct fb_var_screeninfo *var, @@ -1847,7 +1810,7 @@ static int pan_display_atomic(struct fb_var_screeninfo *var, pan_set(fb_helper, var->xoffset, var->yoffset); - ret = restore_fbdev_mode_atomic(fb_helper, true); + ret = restore_fbdev_mode_force(fb_helper); if (!ret) { info->var.xoffset = var->xoffset; info->var.yoffset = var->yoffset; @@ -1861,14 +1824,13 @@ static int pan_display_legacy(struct fb_var_screeninfo *var, struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; + struct drm_client_dev *client = &fb_helper->client; struct drm_mode_set *modeset; int ret = 0; - int i; drm_modeset_lock_all(fb_helper->dev); - for (i = 0; i < fb_helper->crtc_count; i++) { - modeset = &fb_helper->crtc_info[i].mode_set; - + mutex_lock(&client->modeset_mutex); + drm_client_for_each_modeset(modeset, client) { modeset->x = var->xoffset; modeset->y = var->yoffset; @@ -1880,6 +1842,7 @@ static int pan_display_legacy(struct fb_var_screeninfo *var, } } } + mutex_unlock(&client->modeset_mutex); drm_modeset_unlock_all(fb_helper->dev); return ret; @@ -1926,10 +1889,12 @@ EXPORT_SYMBOL(drm_fb_helper_pan_display); static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, int preferred_bpp) { + struct drm_client_dev *client = &fb_helper->client; int ret = 0; int crtc_count = 0; int i; struct drm_fb_helper_surface_size sizes; + struct drm_mode_set *mode_set; int best_depth = 0; memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size)); @@ -1980,13 +1945,13 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, * supports RGBA5551 (16 bpp, depth 15) but not RGB565 (16 bpp, depth * 16) we need to scale down the depth of the sizes we request. */ - for (i = 0; i < fb_helper->crtc_count; i++) { - struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; + mutex_lock(&client->modeset_mutex); + drm_client_for_each_modeset(mode_set, client) { struct drm_crtc *crtc = mode_set->crtc; struct drm_plane *plane = crtc->primary; int j; - DRM_DEBUG("test CRTC %d primary plane\n", i); + DRM_DEBUG("test CRTC %u primary plane\n", drm_crtc_index(crtc)); for (j = 0; j < plane->format_count; j++) { const struct drm_format_info *fmt; @@ -2026,9 +1991,8 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, /* first up get a count of crtcs now in use and new min/maxes width/heights */ crtc_count = 0; - for (i = 0; i < fb_helper->crtc_count; i++) { + drm_client_for_each_modeset(mode_set, client) { struct drm_display_mode *desired_mode; - struct drm_mode_set *mode_set; int x, y, j; /* in case of tile group, are we the last tile vert or horiz? * If no tile group you are always the last one both vertically @@ -2036,7 +2000,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, */ bool lastv = true, lasth = true; - mode_set = &fb_helper->crtc_info[i].mode_set; desired_mode = mode_set->mode; if (!desired_mode) @@ -2066,6 +2029,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, if (lastv) sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height); } + mutex_unlock(&client->modeset_mutex); if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) { DRM_INFO("Cannot find any crtc or sizes\n"); @@ -2297,7 +2261,7 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper, struct drm_display_mode *dmt_mode, *mode; /* only contemplate cloning in the single crtc case */ - if (fb_helper->crtc_count > 1) + if (fb_helper->dev->mode_config.num_crtc > 1) return false; count = 0; @@ -2486,15 +2450,17 @@ static bool connector_has_possible_crtc(struct drm_connector *connector, } static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, - struct drm_fb_helper_crtc **best_crtcs, + struct drm_crtc **best_crtcs, struct drm_display_mode **modes, int n, int width, int height) { - int c, o; + struct drm_client_dev *client = &fb_helper->client; struct drm_connector *connector; int my_score, best_score, score; - struct drm_fb_helper_crtc **crtcs, *crtc; + struct drm_crtc **crtcs, *crtc; + struct drm_mode_set *modeset; struct drm_fb_helper_connector *fb_helper_conn; + int o; if (n == fb_helper->connector_count) return 0; @@ -2507,8 +2473,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, if (modes[n] == NULL) return best_score; - crtcs = kcalloc(fb_helper->connector_count, - sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); + crtcs = kcalloc(fb_helper->connector_count, sizeof(*crtcs), GFP_KERNEL); if (!crtcs) return best_score; @@ -2524,11 +2489,10 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, * select a crtc for this connector and then attempt to configure * remaining connectors */ - for (c = 0; c < fb_helper->crtc_count; c++) { - crtc = &fb_helper->crtc_info[c]; + drm_client_for_each_modeset(modeset, client) { + crtc = modeset->crtc; - if (!connector_has_possible_crtc(connector, - crtc->mode_set.crtc)) + if (!connector_has_possible_crtc(connector, crtc)) continue; for (o = 0; o < n; o++) @@ -2537,7 +2501,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, if (o < n) { /* ignore cloning unless only a single crtc */ - if (fb_helper->crtc_count > 1) + if (fb_helper->dev->mode_config.num_crtc > 1) continue; if (!drm_mode_equal(modes[o], modes[n])) @@ -2545,14 +2509,13 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, } crtcs[n] = crtc; - memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *)); + memcpy(crtcs, best_crtcs, n * sizeof(*crtcs)); score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1, width, height); if (score > best_score) { best_score = score; memcpy(best_crtcs, crtcs, - fb_helper->connector_count * - sizeof(struct drm_fb_helper_crtc *)); + fb_helper->connector_count * sizeof(*crtcs)); } } @@ -2560,21 +2523,9 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, return best_score; } -static struct drm_fb_helper_crtc * -drm_fb_helper_crtc(struct drm_fb_helper *fb_helper, struct drm_crtc *crtc) -{ - int i; - - for (i = 0; i < fb_helper->crtc_count; i++) - if (fb_helper->crtc_info[i].mode_set.crtc == crtc) - return &fb_helper->crtc_info[i]; - - return NULL; -} - /* Try to read the BIOS display configuration and use it for the initial config */ static bool drm_fb_helper_firmware_config(struct drm_fb_helper *fb_helper, - struct drm_fb_helper_crtc **crtcs, + struct drm_crtc **crtcs, struct drm_display_mode **modes, struct drm_fb_offset *offsets, bool *enabled, int width, int height) @@ -2610,7 +2561,7 @@ retry: struct drm_fb_helper_connector *fb_conn; struct drm_connector *connector; struct drm_encoder *encoder; - struct drm_fb_helper_crtc *new_crtc; + struct drm_crtc *new_crtc; fb_conn = fb_helper->connector_info[i]; connector = fb_conn->connector; @@ -2652,7 +2603,7 @@ retry: num_connectors_enabled++; - new_crtc = drm_fb_helper_crtc(fb_helper, connector->state->crtc); + new_crtc = connector->state->crtc; /* * Make sure we're not trying to drive multiple connectors @@ -2752,10 +2703,11 @@ bail: static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, u32 width, u32 height) { + struct drm_client_dev *client = &fb_helper->client; struct drm_device *dev = fb_helper->dev; - struct drm_fb_helper_crtc **crtcs; struct drm_display_mode **modes; struct drm_fb_offset *offsets; + struct drm_crtc **crtcs; bool *enabled; int i; @@ -2763,8 +2715,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, /* prevent concurrent modification of connector_count by hotplug */ lockdep_assert_held(&fb_helper->lock); - crtcs = kcalloc(fb_helper->connector_count, - sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); + crtcs = kcalloc(fb_helper->connector_count, sizeof(*crtcs), GFP_KERNEL); modes = kcalloc(fb_helper->connector_count, sizeof(struct drm_display_mode *), GFP_KERNEL); offsets = kcalloc(fb_helper->connector_count, @@ -2776,6 +2727,8 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, goto out; } + mutex_lock(&client->modeset_mutex); + mutex_lock(&fb_helper->dev->mode_config.mutex); if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0) DRM_DEBUG_KMS("No connectors reported connected with modes\n"); @@ -2800,24 +2753,24 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, } mutex_unlock(&fb_helper->dev->mode_config.mutex); - /* need to set the modesets up here for use later */ - /* fill out the connector<->crtc mappings into the modesets */ - for (i = 0; i < fb_helper->crtc_count; i++) - drm_fb_helper_modeset_release(fb_helper, - &fb_helper->crtc_info[i].mode_set); + drm_client_modeset_release(client); drm_fb_helper_for_each_connector(fb_helper, i) { struct drm_display_mode *mode = modes[i]; - struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; + struct drm_crtc *crtc = crtcs[i]; struct drm_fb_offset *offset = &offsets[i]; - if (mode && fb_crtc) { - struct drm_mode_set *modeset = &fb_crtc->mode_set; + if (mode && crtc) { + struct drm_mode_set *modeset = drm_client_find_modeset(client, crtc); struct drm_connector *connector = fb_helper->connector_info[i]->connector; DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", - mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y); + mode->name, crtc->base.id, offset->x, offset->y); + + if (WARN_ON_ONCE(modeset->num_connectors == DRM_CLIENT_MAX_CLONED_CONNECTORS || + (dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1))) + break; modeset->mode = drm_mode_duplicate(dev, mode); drm_connector_get(connector); @@ -2826,6 +2779,8 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, modeset->y = offset->y; } } + + mutex_unlock(&client->modeset_mutex); out: kfree(crtcs); kfree(modes); @@ -2842,13 +2797,14 @@ out: */ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) { + struct drm_client_dev *client = &fb_helper->client; struct fb_info *info = fb_helper->fbdev; unsigned int rotation, sw_rotations = 0; + struct drm_mode_set *modeset; int i; - for (i = 0; i < fb_helper->crtc_count; i++) { - struct drm_mode_set *modeset = &fb_helper->crtc_info[i].mode_set; - + mutex_lock(&client->modeset_mutex); + drm_client_for_each_modeset(modeset, client) { if (!modeset->num_connectors) continue; @@ -2860,6 +2816,7 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) else sw_rotations |= rotation; } + mutex_unlock(&client->modeset_mutex); mutex_lock(&fb_helper->dev->mode_config.mutex); drm_fb_helper_for_each_connector(fb_helper, i) { @@ -3075,8 +3032,7 @@ EXPORT_SYMBOL(drm_fb_helper_hotplug_event); * @funcs: fbdev helper functions * @preferred_bpp: Preferred bits per pixel for the device. * @dev->mode_config.preferred_depth is used if this is zero. - * @max_conn_count: Maximum number of connectors. - * @dev->mode_config.num_connector is used if this is zero. + * @max_conn_count: Maximum number of connectors (not used) * * This function sets up fbdev emulation and registers fbdev for access by * userspace. If all connectors are disconnected, setup is deferred to the next @@ -3104,16 +3060,9 @@ int drm_fb_helper_fbdev_setup(struct drm_device *dev, if (!preferred_bpp) preferred_bpp = 32; - if (!max_conn_count) - max_conn_count = dev->mode_config.num_connector; - if (!max_conn_count) { - DRM_DEV_ERROR(dev->dev, "fbdev: No connectors\n"); - return -EINVAL; - } - drm_fb_helper_prepare(dev, fb_helper, funcs); - ret = drm_fb_helper_init(dev, fb_helper, max_conn_count); + ret = drm_fb_helper_init(dev, fb_helper, 0); if (ret < 0) { DRM_DEV_ERROR(dev->dev, "fbdev: Failed to initialize (ret=%d)\n", ret); return ret; @@ -3426,7 +3375,7 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client) drm_fb_helper_prepare(dev, fb_helper, &drm_fb_helper_generic_funcs); - ret = drm_fb_helper_init(dev, fb_helper, dev->mode_config.num_connector); + ret = drm_fb_helper_init(dev, fb_helper, 0); if (ret) goto err; diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index 268b2cf0052a..87be9aeb1fe0 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -3,8 +3,12 @@ #ifndef _DRM_CLIENT_H_ #define _DRM_CLIENT_H_ +#include +#include #include +#include + struct drm_client_dev; struct drm_device; struct drm_file; @@ -13,6 +17,8 @@ struct drm_gem_object; struct drm_minor; struct module; +#define DRM_CLIENT_MAX_CLONED_CONNECTORS 8 + /** * struct drm_client_funcs - DRM client callbacks */ @@ -85,6 +91,16 @@ struct drm_client_dev { * @file: DRM file */ struct drm_file *file; + + /** + * @modeset_mutex: Protects @modesets. + */ + struct mutex modeset_mutex; + + /** + * @modesets: CRTC configurations + */ + struct drm_mode_set *modesets; }; int drm_client_init(struct drm_device *dev, struct drm_client_dev *client, @@ -135,6 +151,20 @@ struct drm_client_buffer * drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format); void drm_client_framebuffer_delete(struct drm_client_buffer *buffer); +int drm_client_modeset_create(struct drm_client_dev *client); +void drm_client_modeset_free(struct drm_client_dev *client); +void drm_client_modeset_release(struct drm_client_dev *client); +struct drm_mode_set *drm_client_find_modeset(struct drm_client_dev *client, struct drm_crtc *crtc); + +/** + * drm_client_for_each_modeset() - Iterate over client modesets + * @modeset: &drm_mode_set loop cursor + * @client: DRM client + */ +#define drm_client_for_each_modeset(modeset, client) \ + for (({ lockdep_assert_held(&(client)->modeset_mutex); }), \ + modeset = (client)->modesets; modeset->crtc; modeset++) + int drm_client_debugfs_init(struct drm_minor *minor); #endif diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 2af1c6d3e147..6b334f4d8a22 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -47,10 +47,6 @@ struct drm_fb_offset { int x, y; }; -struct drm_fb_helper_crtc { - struct drm_mode_set mode_set; -}; - /** * struct drm_fb_helper_surface_size - describes fbdev size and scanout surface size * @fb_width: fbdev width @@ -109,8 +105,6 @@ struct drm_fb_helper_connector { * struct drm_fb_helper - main structure to emulate fbdev on top of KMS * @fb: Scanout framebuffer object * @dev: DRM device - * @crtc_count: number of possible CRTCs - * @crtc_info: per-CRTC helper state (mode, x/y offset, etc) * @connector_count: number of connected connectors * @connector_info_alloc_count: size of connector_info * @funcs: driver callbacks for fb helper @@ -144,8 +138,6 @@ struct drm_fb_helper { struct drm_framebuffer *fb; struct drm_device *dev; - int crtc_count; - struct drm_fb_helper_crtc *crtc_info; int connector_count; int connector_info_alloc_count; /** -- cgit v1.2.3 From a09db883e5d938b525a86a4630fc04f98ff1063d Mon Sep 17 00:00:00 2001 From: Uma Shankar Date: Tue, 4 Jun 2019 16:47:02 +0530 Subject: drm: Fix docbook warnings in hdr metadata helper structures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the following warnings: ./include/drm/drm_mode_config.h:841: warning: Incorrect use of kernel-doc format: * hdr_output_metadata_property: Connector property containing hdr ./include/drm/drm_mode_config.h:918: warning: Function parameter or member 'hdr_output_metadata_property' not described in 'drm_mode_config' ./include/drm/drm_connector.h:1251: warning: Function parameter or member 'hdr_output_metadata' not described in 'drm_connector' ./include/drm/drm_connector.h:1251: warning: Function parameter or member 'hdr_sink_metadata' not described in 'drm_connector' Also adds some property documentation for HDR Metadata Connector Property in connector property create function. v2: Fixed Sean Paul's review comments. v3: Fixed Daniel Vetter's review comments, added the UAPI structure definition section in kernel docs. v4: Fixed Daniel Vetter's review comments. v5: Added structure member references as per Daniel's suggestion. Cc: Shashank Sharma Cc: Ville Syrjä Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Sean Paul Cc: David Airlie Cc: Daniel Vetter Cc: Bartlomiej Zolnierkiewicz Cc: "Ville Syrjä" Cc: Hans Verkuil Cc: dri-devel@lists.freedesktop.org Cc: linux-fbdev@vger.kernel.org Reviewed-by: Sean Paul (v1) Signed-off-by: Uma Shankar [danvet: Fix up markup: () for functions, & for structs. Style guide also recommends to prepend struct for structures.] Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/1559647022-7336-1-git-send-email-uma.shankar@intel.com --- drivers/gpu/drm/drm_connector.c | 40 ++++++++++++++++++++++ include/drm/drm_connector.h | 1 + include/drm/drm_mode_config.h | 4 +-- include/linux/hdmi.h | 12 +++++++ include/uapi/drm/drm_mode.h | 74 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 128 insertions(+), 3 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index c9ac8b9e83ea..e17586aaa80f 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -956,6 +956,46 @@ static const struct drm_prop_enum_list hdmi_colorspaces[] = { * is no longer protected and userspace should take appropriate action * (whatever that might be). * + * HDR_OUTPUT_METADATA: + * Connector property to enable userspace to send HDR Metadata to + * driver. This metadata is based on the composition and blending + * policies decided by user, taking into account the hardware and + * sink capabilities. The driver gets this metadata and creates a + * Dynamic Range and Mastering Infoframe (DRM) in case of HDMI, + * SDP packet (Non-audio INFOFRAME SDP v1.3) for DP. This is then + * sent to sink. This notifies the sink of the upcoming frame's Color + * Encoding and Luminance parameters. + * + * Userspace first need to detect the HDR capabilities of sink by + * reading and parsing the EDID. Details of HDR metadata for HDMI + * are added in CTA 861.G spec. For DP , its defined in VESA DP + * Standard v1.4. It needs to then get the metadata information + * of the video/game/app content which are encoded in HDR (basically + * using HDR transfer functions). With this information it needs to + * decide on a blending policy and compose the relevant + * layers/overlays into a common format. Once this blending is done, + * userspace will be aware of the metadata of the composed frame to + * be send to sink. It then uses this property to communicate this + * metadata to driver which then make a Infoframe packet and sends + * to sink based on the type of encoder connected. + * + * Userspace will be responsible to do Tone mapping operation in case: + * - Some layers are HDR and others are SDR + * - HDR layers luminance is not same as sink + * It will even need to do colorspace conversion and get all layers + * to one common colorspace for blending. It can use either GL, Media + * or display engine to get this done based on the capabilties of the + * associated hardware. + * + * Driver expects metadata to be put in &struct hdr_output_metadata + * structure from userspace. This is received as blob and stored in + * &drm_connector_state.hdr_output_metadata. It parses EDID and saves the + * sink metadata in &struct hdr_sink_metadata, as + * &drm_connector.hdr_sink_metadata. Driver uses + * drm_hdmi_infoframe_set_hdr_metadata() helper to set the HDR metadata, + * hdmi_drm_infoframe_pack() to pack the infoframe as per spec, in case of + * HDMI encoder. + * * max bpc: * This range property is used by userspace to limit the bit depth. When * used the driver would limit the bpc in accordance with the valid range diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 547656173c74..47e749b74e5f 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1244,6 +1244,7 @@ struct drm_connector { */ struct llist_node free_node; + /** @hdr_sink_metadata: HDR Metadata Information read from sink */ struct hdr_sink_metadata hdr_sink_metadata; }; diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 4f88cc972407..759d462d028b 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -837,8 +837,8 @@ struct drm_mode_config { struct drm_property *writeback_out_fence_ptr_property; /** - * hdr_output_metadata_property: Connector property containing hdr - * metatda. This will be provided by userspace compositors based + * @hdr_output_metadata_property: Connector property containing hdr + * metatada. This will be provided by userspace compositors based * on HDR content */ struct drm_property *hdr_output_metadata_property; diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index ee55ba589cdc..9918a6c910c5 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -367,8 +367,19 @@ struct hdr_static_metadata { __u16 min_cll; }; +/** + * struct hdr_sink_metadata - HDR sink metadata + * + * Metadata Information read from Sink's EDID + */ struct hdr_sink_metadata { + /** + * @metadata_type: Static_Metadata_Descriptor_ID. + */ __u32 metadata_type; + /** + * @hdmi_type1: HDR Metadata Infoframe. + */ union { struct hdr_static_metadata hdmi_type1; }; @@ -398,6 +409,7 @@ union hdmi_vendor_any_infoframe { * @spd: spd infoframe * @vendor: union of all vendor infoframes * @audio: audio infoframe + * @drm: Dynamic Range and Mastering infoframe * * This is used by the generic pack function. This works since all infoframes * have the same header which also indicates which type of infoframe should be diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 19b5cf368cff..5ab331e5dc23 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -33,6 +33,15 @@ extern "C" { #endif +/** + * DOC: overview + * + * DRM exposes many UAPI and structure definition to have a consistent + * and standardized interface with user. + * Userspace can refer to these structure definitions and UAPI formats + * to communicate to driver + */ + #define DRM_CONNECTOR_NAME_LEN 32 #define DRM_DISPLAY_MODE_LEN 32 #define DRM_PROP_NAME_LEN 32 @@ -630,24 +639,87 @@ struct drm_color_lut { __u16 reserved; }; -/* HDR Metadata Infoframe as per 861.G spec */ +/** + * struct hdr_metadata_infoframe - HDR Metadata Infoframe Data. + * + * HDR Metadata Infoframe as per CTA 861.G spec. This is expected + * to match exactly with the spec. + * + * Userspace is expected to pass the metadata information as per + * the format described in this structure. + */ struct hdr_metadata_infoframe { + /** + * @eotf: Electro-Optical Transfer Function (EOTF) + * used in the stream. + */ __u8 eotf; + /** + * @metadata_type: Static_Metadata_Descriptor_ID. + */ __u8 metadata_type; + /** + * @display_primaries: Color Primaries of the Data. + * These are coded as unsigned 16-bit values in units of + * 0.00002, where 0x0000 represents zero and 0xC350 + * represents 1.0000. + * @display_primaries.x: X cordinate of color primary. + * @display_primaries.y: Y cordinate of color primary. + */ struct { __u16 x, y; } display_primaries[3]; + /** + * @white_point: White Point of Colorspace Data. + * These are coded as unsigned 16-bit values in units of + * 0.00002, where 0x0000 represents zero and 0xC350 + * represents 1.0000. + * @white_point.x: X cordinate of whitepoint of color primary. + * @white_point.y: Y cordinate of whitepoint of color primary. + */ struct { __u16 x, y; } white_point; + /** + * @max_display_mastering_luminance: Max Mastering Display Luminance. + * This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + * where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + */ __u16 max_display_mastering_luminance; + /** + * @min_display_mastering_luminance: Min Mastering Display Luminance. + * This value is coded as an unsigned 16-bit value in units of + * 0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF + * represents 6.5535 cd/m2. + */ __u16 min_display_mastering_luminance; + /** + * @max_cll: Max Content Light Level. + * This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + * where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + */ __u16 max_cll; + /** + * @max_fall: Max Frame Average Light Level. + * This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + * where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + */ __u16 max_fall; }; +/** + * struct hdr_output_metadata - HDR output metadata + * + * Metadata Information to be passed from userspace + */ struct hdr_output_metadata { + /** + * @metadata_type: Static_Metadata_Descriptor_ID. + */ __u32 metadata_type; + /** + * @hdmi_metadata_type1: HDR Metadata Infoframe. + */ union { struct hdr_metadata_infoframe hdmi_metadata_type1; }; -- cgit v1.2.3 From ae96e8d7b6f437a0cece42c2e834d64c8f22234c Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 4 Jun 2019 17:07:10 +0200 Subject: drm/ttm: fix ttm_bo_unreserve MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we now keep BOs on the LRU we need to explicitely remove them from the LRU now after they are pinned. Signed-off-by: Christian König Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher --- include/drm/ttm/ttm_bo_driver.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'include/drm') diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 9f54cf9c60df..c9b8ba492f24 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -767,14 +767,12 @@ static inline int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo, */ static inline void ttm_bo_unreserve(struct ttm_buffer_object *bo) { - if (!(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) { - spin_lock(&bo->bdev->glob->lru_lock); - if (list_empty(&bo->lru)) - ttm_bo_add_to_lru(bo); - else - ttm_bo_move_to_lru_tail(bo, NULL); - spin_unlock(&bo->bdev->glob->lru_lock); - } + spin_lock(&bo->bdev->glob->lru_lock); + if (list_empty(&bo->lru)) + ttm_bo_add_to_lru(bo); + else + ttm_bo_move_to_lru_tail(bo, NULL); + spin_unlock(&bo->bdev->glob->lru_lock); reservation_object_unlock(bo->resv); } -- cgit v1.2.3 From 382d2af64e7d7083eefcc8c7e6c780634174ce87 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 29 May 2019 14:02:03 +0300 Subject: drm/edid: Clean up DRM_EDID_DIGITAL_* flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Give the "DFP 1.x" bit a proper name, and clean up the rest of the bits defines as well. Cc: Mario Kleiner Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20190529110204.2384-1-ville.syrjala@linux.intel.com Reviewed-by: Alex Deucher Reviewed-by: Mario Kleiner --- drivers/gpu/drm/drm_edid.c | 2 +- include/drm/drm_edid.h | 32 +++++++++++++++++--------------- 2 files changed, 18 insertions(+), 16 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index d87f574feeca..dd601ed6a30e 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -4570,7 +4570,7 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi * extensions which tell otherwise. */ if ((info->bpc == 0) && (edid->revision < 4) && - (edid->input & DRM_EDID_DIGITAL_TYPE_DVI)) { + (edid->input & DRM_EDID_DIGITAL_DFP_1_X)) { info->bpc = 8; DRM_DEBUG("%s: Assigning DFP sink color depth as %d bpc.\n", connector->name, info->bpc); diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 0e21e91c4314..88b63801f9db 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -177,21 +177,23 @@ struct detailed_timing { #define DRM_EDID_INPUT_BLANK_TO_BLACK (1 << 4) #define DRM_EDID_INPUT_VIDEO_LEVEL (3 << 5) #define DRM_EDID_INPUT_DIGITAL (1 << 7) -#define DRM_EDID_DIGITAL_DEPTH_MASK (7 << 4) -#define DRM_EDID_DIGITAL_DEPTH_UNDEF (0 << 4) -#define DRM_EDID_DIGITAL_DEPTH_6 (1 << 4) -#define DRM_EDID_DIGITAL_DEPTH_8 (2 << 4) -#define DRM_EDID_DIGITAL_DEPTH_10 (3 << 4) -#define DRM_EDID_DIGITAL_DEPTH_12 (4 << 4) -#define DRM_EDID_DIGITAL_DEPTH_14 (5 << 4) -#define DRM_EDID_DIGITAL_DEPTH_16 (6 << 4) -#define DRM_EDID_DIGITAL_DEPTH_RSVD (7 << 4) -#define DRM_EDID_DIGITAL_TYPE_UNDEF (0) -#define DRM_EDID_DIGITAL_TYPE_DVI (1) -#define DRM_EDID_DIGITAL_TYPE_HDMI_A (2) -#define DRM_EDID_DIGITAL_TYPE_HDMI_B (3) -#define DRM_EDID_DIGITAL_TYPE_MDDI (4) -#define DRM_EDID_DIGITAL_TYPE_DP (5) +#define DRM_EDID_DIGITAL_DEPTH_MASK (7 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_UNDEF (0 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_6 (1 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_8 (2 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_10 (3 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_12 (4 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_14 (5 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_16 (6 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_DEPTH_RSVD (7 << 4) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_MASK (7 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_UNDEF (0 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_DVI (1 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_HDMI_A (2 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_HDMI_B (3 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_MDDI (4 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_TYPE_DP (5 << 0) /* 1.4 */ +#define DRM_EDID_DIGITAL_DFP_1_X (1 << 0) /* 1.3 */ #define DRM_EDID_FEATURE_DEFAULT_GTF (1 << 0) #define DRM_EDID_FEATURE_PREFERRED_TIMING (1 << 1) -- cgit v1.2.3 From b0a6b94027c85857f768a586cbcf1e96ee1d04ae Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 4 Mar 2019 23:05:34 +0200 Subject: drm: bridge: Add dual_link field to the drm_bridge_timings structure Extend the drm_bridge_timings structure with a new dual_link field to indicate that the bridge's input bus carries data on two separate physical links. The first use case is LVDS dual-link mode where even- and odd-numbered pixels are transferred on separate LVDS links. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham Tested-by: Jacopo Mondi Acked-by: Sam Ravnborg --- include/drm/drm_bridge.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include/drm') diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index d4428913a4e1..aea1fcfd92a7 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -265,6 +265,14 @@ struct drm_bridge_timings { * input signal after the clock edge. */ u32 hold_time_ps; + /** + * @dual_link: + * + * True if the bus operates in dual-link mode. The exact meaning is + * dependent on the bus type. For LVDS buses, this indicates that even- + * and odd-numbered pixels are received on separate links. + */ + bool dual_link; }; /** -- cgit v1.2.3 From df73789514554761ebdd87f2426938696a2442a2 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Fri, 31 May 2019 16:01:10 +0200 Subject: drm/atomic: Move __drm_atomic_helper_disable_plane/set_config() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for moving drm_fb_helper modesetting code to drm_client. drm_client will be linked to drm.ko, so move __drm_atomic_helper_disable_plane() and __drm_atomic_helper_set_config() out of drm_kms_helper.ko. While at it, fix two checkpatch complaints: - WARNING: Block comments use a trailing */ on a separate line - CHECK: Alignment should match open parenthesis v7: Declare drm_mode_set and drm_plane_state Signed-off-by: Noralf Trønnes Reviewed-by: Daniel Vetter Reviewed-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20190531140117.37751-2-noralf@tronnes.org --- drivers/gpu/drm/drm_atomic.c | 168 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_atomic_helper.c | 164 ----------------------------------- drivers/gpu/drm/drm_crtc_internal.h | 7 ++ include/drm/drm_atomic_helper.h | 4 - 4 files changed, 175 insertions(+), 168 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index b640e77184e5..169f06d7c5ce 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1179,6 +1179,174 @@ int drm_atomic_nonblocking_commit(struct drm_atomic_state *state) } EXPORT_SYMBOL(drm_atomic_nonblocking_commit); +/* just used from drm-client and atomic-helper: */ +int __drm_atomic_helper_disable_plane(struct drm_plane *plane, + struct drm_plane_state *plane_state) +{ + int ret; + + ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); + if (ret != 0) + return ret; + + drm_atomic_set_fb_for_plane(plane_state, NULL); + plane_state->crtc_x = 0; + plane_state->crtc_y = 0; + plane_state->crtc_w = 0; + plane_state->crtc_h = 0; + plane_state->src_x = 0; + plane_state->src_y = 0; + plane_state->src_w = 0; + plane_state->src_h = 0; + + return 0; +} +EXPORT_SYMBOL(__drm_atomic_helper_disable_plane); + +static int update_output_state(struct drm_atomic_state *state, + struct drm_mode_set *set) +{ + struct drm_device *dev = set->crtc->dev; + struct drm_crtc *crtc; + struct drm_crtc_state *new_crtc_state; + struct drm_connector *connector; + struct drm_connector_state *new_conn_state; + int ret, i; + + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, + state->acquire_ctx); + if (ret) + return ret; + + /* First disable all connectors on the target crtc. */ + ret = drm_atomic_add_affected_connectors(state, set->crtc); + if (ret) + return ret; + + for_each_new_connector_in_state(state, connector, new_conn_state, i) { + if (new_conn_state->crtc == set->crtc) { + ret = drm_atomic_set_crtc_for_connector(new_conn_state, + NULL); + if (ret) + return ret; + + /* Make sure legacy setCrtc always re-trains */ + new_conn_state->link_status = DRM_LINK_STATUS_GOOD; + } + } + + /* Then set all connectors from set->connectors on the target crtc */ + for (i = 0; i < set->num_connectors; i++) { + new_conn_state = drm_atomic_get_connector_state(state, + set->connectors[i]); + if (IS_ERR(new_conn_state)) + return PTR_ERR(new_conn_state); + + ret = drm_atomic_set_crtc_for_connector(new_conn_state, + set->crtc); + if (ret) + return ret; + } + + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { + /* + * Don't update ->enable for the CRTC in the set_config request, + * since a mismatch would indicate a bug in the upper layers. + * The actual modeset code later on will catch any + * inconsistencies here. + */ + if (crtc == set->crtc) + continue; + + if (!new_crtc_state->connector_mask) { + ret = drm_atomic_set_mode_prop_for_crtc(new_crtc_state, + NULL); + if (ret < 0) + return ret; + + new_crtc_state->active = false; + } + } + + return 0; +} + +/* just used from drm-client and atomic-helper: */ +int __drm_atomic_helper_set_config(struct drm_mode_set *set, + struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state; + struct drm_plane_state *primary_state; + struct drm_crtc *crtc = set->crtc; + int hdisplay, vdisplay; + int ret; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + primary_state = drm_atomic_get_plane_state(state, crtc->primary); + if (IS_ERR(primary_state)) + return PTR_ERR(primary_state); + + if (!set->mode) { + WARN_ON(set->fb); + WARN_ON(set->num_connectors); + + ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL); + if (ret != 0) + return ret; + + crtc_state->active = false; + + ret = drm_atomic_set_crtc_for_plane(primary_state, NULL); + if (ret != 0) + return ret; + + drm_atomic_set_fb_for_plane(primary_state, NULL); + + goto commit; + } + + WARN_ON(!set->fb); + WARN_ON(!set->num_connectors); + + ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode); + if (ret != 0) + return ret; + + crtc_state->active = true; + + ret = drm_atomic_set_crtc_for_plane(primary_state, crtc); + if (ret != 0) + return ret; + + drm_mode_get_hv_timing(set->mode, &hdisplay, &vdisplay); + + drm_atomic_set_fb_for_plane(primary_state, set->fb); + primary_state->crtc_x = 0; + primary_state->crtc_y = 0; + primary_state->crtc_w = hdisplay; + primary_state->crtc_h = vdisplay; + primary_state->src_x = set->x << 16; + primary_state->src_y = set->y << 16; + if (drm_rotation_90_or_270(primary_state->rotation)) { + primary_state->src_w = vdisplay << 16; + primary_state->src_h = hdisplay << 16; + } else { + primary_state->src_w = hdisplay << 16; + primary_state->src_h = vdisplay << 16; + } + +commit: + ret = update_output_state(state, set); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(__drm_atomic_helper_set_config); + void drm_atomic_print_state(const struct drm_atomic_state *state) { struct drm_printer p = drm_info_printer(state->dev->dev); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index acf993cb8e52..0fc63d682245 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2844,95 +2844,6 @@ fail: } EXPORT_SYMBOL(drm_atomic_helper_disable_plane); -/* just used from fb-helper and atomic-helper: */ -int __drm_atomic_helper_disable_plane(struct drm_plane *plane, - struct drm_plane_state *plane_state) -{ - int ret; - - ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); - if (ret != 0) - return ret; - - drm_atomic_set_fb_for_plane(plane_state, NULL); - plane_state->crtc_x = 0; - plane_state->crtc_y = 0; - plane_state->crtc_w = 0; - plane_state->crtc_h = 0; - plane_state->src_x = 0; - plane_state->src_y = 0; - plane_state->src_w = 0; - plane_state->src_h = 0; - - return 0; -} - -static int update_output_state(struct drm_atomic_state *state, - struct drm_mode_set *set) -{ - struct drm_device *dev = set->crtc->dev; - struct drm_crtc *crtc; - struct drm_crtc_state *new_crtc_state; - struct drm_connector *connector; - struct drm_connector_state *new_conn_state; - int ret, i; - - ret = drm_modeset_lock(&dev->mode_config.connection_mutex, - state->acquire_ctx); - if (ret) - return ret; - - /* First disable all connectors on the target crtc. */ - ret = drm_atomic_add_affected_connectors(state, set->crtc); - if (ret) - return ret; - - for_each_new_connector_in_state(state, connector, new_conn_state, i) { - if (new_conn_state->crtc == set->crtc) { - ret = drm_atomic_set_crtc_for_connector(new_conn_state, - NULL); - if (ret) - return ret; - - /* Make sure legacy setCrtc always re-trains */ - new_conn_state->link_status = DRM_LINK_STATUS_GOOD; - } - } - - /* Then set all connectors from set->connectors on the target crtc */ - for (i = 0; i < set->num_connectors; i++) { - new_conn_state = drm_atomic_get_connector_state(state, - set->connectors[i]); - if (IS_ERR(new_conn_state)) - return PTR_ERR(new_conn_state); - - ret = drm_atomic_set_crtc_for_connector(new_conn_state, - set->crtc); - if (ret) - return ret; - } - - for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { - /* Don't update ->enable for the CRTC in the set_config request, - * since a mismatch would indicate a bug in the upper layers. - * The actual modeset code later on will catch any - * inconsistencies here. */ - if (crtc == set->crtc) - continue; - - if (!new_crtc_state->connector_mask) { - ret = drm_atomic_set_mode_prop_for_crtc(new_crtc_state, - NULL); - if (ret < 0) - return ret; - - new_crtc_state->active = false; - } - } - - return 0; -} - /** * drm_atomic_helper_set_config - set a new config from userspace * @set: mode set configuration @@ -2977,81 +2888,6 @@ fail: } EXPORT_SYMBOL(drm_atomic_helper_set_config); -/* just used from fb-helper and atomic-helper: */ -int __drm_atomic_helper_set_config(struct drm_mode_set *set, - struct drm_atomic_state *state) -{ - struct drm_crtc_state *crtc_state; - struct drm_plane_state *primary_state; - struct drm_crtc *crtc = set->crtc; - int hdisplay, vdisplay; - int ret; - - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); - - primary_state = drm_atomic_get_plane_state(state, crtc->primary); - if (IS_ERR(primary_state)) - return PTR_ERR(primary_state); - - if (!set->mode) { - WARN_ON(set->fb); - WARN_ON(set->num_connectors); - - ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL); - if (ret != 0) - return ret; - - crtc_state->active = false; - - ret = drm_atomic_set_crtc_for_plane(primary_state, NULL); - if (ret != 0) - return ret; - - drm_atomic_set_fb_for_plane(primary_state, NULL); - - goto commit; - } - - WARN_ON(!set->fb); - WARN_ON(!set->num_connectors); - - ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode); - if (ret != 0) - return ret; - - crtc_state->active = true; - - ret = drm_atomic_set_crtc_for_plane(primary_state, crtc); - if (ret != 0) - return ret; - - drm_mode_get_hv_timing(set->mode, &hdisplay, &vdisplay); - - drm_atomic_set_fb_for_plane(primary_state, set->fb); - primary_state->crtc_x = 0; - primary_state->crtc_y = 0; - primary_state->crtc_w = hdisplay; - primary_state->crtc_h = vdisplay; - primary_state->src_x = set->x << 16; - primary_state->src_y = set->y << 16; - if (drm_rotation_90_or_270(primary_state->rotation)) { - primary_state->src_w = vdisplay << 16; - primary_state->src_h = hdisplay << 16; - } else { - primary_state->src_w = hdisplay << 16; - primary_state->src_h = vdisplay << 16; - } - -commit: - ret = update_output_state(state, set); - if (ret) - return ret; - - return 0; -} - /** * drm_atomic_helper_disable_all - disable all currently active outputs * @dev: DRM device diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index c78a44fad13d..c7d5e4c21423 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -50,7 +50,9 @@ struct drm_mode_create_dumb; struct drm_mode_fb_cmd2; struct drm_mode_fb_cmd; struct drm_mode_object; +struct drm_mode_set; struct drm_plane; +struct drm_plane_state; struct drm_property; struct edid; struct kref; @@ -223,6 +225,11 @@ struct drm_minor; int drm_atomic_debugfs_init(struct drm_minor *minor); #endif +int __drm_atomic_helper_disable_plane(struct drm_plane *plane, + struct drm_plane_state *plane_state); +int __drm_atomic_helper_set_config(struct drm_mode_set *set, + struct drm_atomic_state *state); + void drm_atomic_print_state(const struct drm_atomic_state *state); /* drm_atomic_uapi.c */ diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 58214be3bf3d..bf4e07141d81 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -117,12 +117,8 @@ int drm_atomic_helper_update_plane(struct drm_plane *plane, struct drm_modeset_acquire_ctx *ctx); int drm_atomic_helper_disable_plane(struct drm_plane *plane, struct drm_modeset_acquire_ctx *ctx); -int __drm_atomic_helper_disable_plane(struct drm_plane *plane, - struct drm_plane_state *plane_state); int drm_atomic_helper_set_config(struct drm_mode_set *set, struct drm_modeset_acquire_ctx *ctx); -int __drm_atomic_helper_set_config(struct drm_mode_set *set, - struct drm_atomic_state *state); int drm_atomic_helper_disable_all(struct drm_device *dev, struct drm_modeset_acquire_ctx *ctx); -- cgit v1.2.3 From aec3925f093d3ac880c53ec03ea28ef8608b0a52 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Fri, 31 May 2019 16:01:13 +0200 Subject: drm/fb-helper: Move out commit code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the modeset commit code to drm_client_modeset. No changes except exporting API. v7: Export drm_client_panel_rotation() (Gerd Hoffmann) v2: Move to drm_client_modeset.c instead of drm_client.c Signed-off-by: Noralf Trønnes Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20190531140117.37751-5-noralf@tronnes.org --- drivers/gpu/drm/drm_client_modeset.c | 288 +++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_fb_helper.c | 282 ---------------------------------- include/drm/drm_client.h | 4 + 3 files changed, 292 insertions(+), 282 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index 66770ed3299e..b1984f901a6d 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -11,9 +11,14 @@ #include #include +#include #include #include #include +#include + +#include "drm_crtc_internal.h" +#include "drm_internal.h" int drm_client_modeset_create(struct drm_client_dev *client) { @@ -102,3 +107,286 @@ struct drm_mode_set *drm_client_find_modeset(struct drm_client_dev *client, stru } /* TODO: Remove export when modeset code has been moved over */ EXPORT_SYMBOL(drm_client_find_modeset); + +/** + * drm_client_panel_rotation() - Check panel orientation + * @modeset: DRM modeset + * @rotation: Returned rotation value + * + * This function checks if the primary plane in @modeset can hw rotate to match + * the panel orientation on its connector. + * + * Note: Currently only 0 and 180 degrees are supported. + * + * Return: + * True if the plane can do the rotation, false otherwise. + */ +bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotation) +{ + struct drm_connector *connector = modeset->connectors[0]; + struct drm_plane *plane = modeset->crtc->primary; + u64 valid_mask = 0; + unsigned int i; + + if (!modeset->num_connectors) + return false; + + switch (connector->display_info.panel_orientation) { + case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP: + *rotation = DRM_MODE_ROTATE_180; + break; + case DRM_MODE_PANEL_ORIENTATION_LEFT_UP: + *rotation = DRM_MODE_ROTATE_90; + break; + case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP: + *rotation = DRM_MODE_ROTATE_270; + break; + default: + *rotation = DRM_MODE_ROTATE_0; + } + + /* + * TODO: support 90 / 270 degree hardware rotation, + * depending on the hardware this may require the framebuffer + * to be in a specific tiling format. + */ + if (*rotation != DRM_MODE_ROTATE_180 || !plane->rotation_property) + return false; + + for (i = 0; i < plane->rotation_property->num_values; i++) + valid_mask |= (1ULL << plane->rotation_property->values[i]); + + if (!(*rotation & valid_mask)) + return false; + + return true; +} +EXPORT_SYMBOL(drm_client_panel_rotation); + +static int drm_client_modeset_commit_atomic(struct drm_client_dev *client, bool active) +{ + struct drm_device *dev = client->dev; + struct drm_plane_state *plane_state; + struct drm_plane *plane; + struct drm_atomic_state *state; + struct drm_modeset_acquire_ctx ctx; + struct drm_mode_set *mode_set; + int ret; + + drm_modeset_acquire_init(&ctx, 0); + + state = drm_atomic_state_alloc(dev); + if (!state) { + ret = -ENOMEM; + goto out_ctx; + } + + state->acquire_ctx = &ctx; +retry: + drm_for_each_plane(plane, dev) { + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + goto out_state; + } + + plane_state->rotation = DRM_MODE_ROTATE_0; + + /* disable non-primary: */ + if (plane->type == DRM_PLANE_TYPE_PRIMARY) + continue; + + ret = __drm_atomic_helper_disable_plane(plane, plane_state); + if (ret != 0) + goto out_state; + } + + drm_client_for_each_modeset(mode_set, client) { + struct drm_plane *primary = mode_set->crtc->primary; + unsigned int rotation; + + if (drm_client_panel_rotation(mode_set, &rotation)) { + /* Cannot fail as we've already gotten the plane state above */ + plane_state = drm_atomic_get_new_plane_state(state, primary); + plane_state->rotation = rotation; + } + + ret = __drm_atomic_helper_set_config(mode_set, state); + if (ret != 0) + goto out_state; + + /* + * __drm_atomic_helper_set_config() sets active when a + * mode is set, unconditionally clear it if we force DPMS off + */ + if (!active) { + struct drm_crtc *crtc = mode_set->crtc; + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + crtc_state->active = false; + } + } + + ret = drm_atomic_commit(state); + +out_state: + if (ret == -EDEADLK) + goto backoff; + + drm_atomic_state_put(state); +out_ctx: + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + return ret; + +backoff: + drm_atomic_state_clear(state); + drm_modeset_backoff(&ctx); + + goto retry; +} + +static int drm_client_modeset_commit_legacy(struct drm_client_dev *client) +{ + struct drm_device *dev = client->dev; + struct drm_mode_set *mode_set; + struct drm_plane *plane; + int ret = 0; + + drm_modeset_lock_all(dev); + drm_for_each_plane(plane, dev) { + if (plane->type != DRM_PLANE_TYPE_PRIMARY) + drm_plane_force_disable(plane); + + if (plane->rotation_property) + drm_mode_plane_set_obj_prop(plane, + plane->rotation_property, + DRM_MODE_ROTATE_0); + } + + drm_client_for_each_modeset(mode_set, client) { + struct drm_crtc *crtc = mode_set->crtc; + + if (crtc->funcs->cursor_set2) { + ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0); + if (ret) + goto out; + } else if (crtc->funcs->cursor_set) { + ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0); + if (ret) + goto out; + } + + ret = drm_mode_set_config_internal(mode_set); + if (ret) + goto out; + } +out: + drm_modeset_unlock_all(dev); + + return ret; +} + +/** + * drm_client_modeset_commit_force() - Force commit CRTC configuration + * @client: DRM client + * + * Commit modeset configuration to crtcs without checking if there is a DRM master. + * + * Returns: + * Zero on success or negative error code on failure. + */ +int drm_client_modeset_commit_force(struct drm_client_dev *client) +{ + struct drm_device *dev = client->dev; + int ret; + + mutex_lock(&client->modeset_mutex); + if (drm_drv_uses_atomic_modeset(dev)) + ret = drm_client_modeset_commit_atomic(client, true); + else + ret = drm_client_modeset_commit_legacy(client); + mutex_unlock(&client->modeset_mutex); + + return ret; +} +EXPORT_SYMBOL(drm_client_modeset_commit_force); + +/** + * drm_client_modeset_commit() - Commit CRTC configuration + * @client: DRM client + * + * Commit modeset configuration to crtcs. + * + * Returns: + * Zero on success or negative error code on failure. + */ +int drm_client_modeset_commit(struct drm_client_dev *client) +{ + struct drm_device *dev = client->dev; + int ret; + + if (!drm_master_internal_acquire(dev)) + return -EBUSY; + + ret = drm_client_modeset_commit_force(client); + + drm_master_internal_release(dev); + + return ret; +} +EXPORT_SYMBOL(drm_client_modeset_commit); + +static void drm_client_modeset_dpms_legacy(struct drm_client_dev *client, int dpms_mode) +{ + struct drm_device *dev = client->dev; + struct drm_connector *connector; + struct drm_mode_set *modeset; + int j; + + drm_modeset_lock_all(dev); + drm_client_for_each_modeset(modeset, client) { + if (!modeset->crtc->enabled) + continue; + + for (j = 0; j < modeset->num_connectors; j++) { + connector = modeset->connectors[j]; + connector->funcs->dpms(connector, dpms_mode); + drm_object_property_set_value(&connector->base, + dev->mode_config.dpms_property, dpms_mode); + } + } + drm_modeset_unlock_all(dev); +} + +/** + * drm_client_modeset_dpms() - Set DPMS mode + * @client: DRM client + * @mode: DPMS mode + * + * Note: For atomic drivers @mode is reduced to on/off. + * + * Returns: + * Zero on success or negative error code on failure. + */ +int drm_client_modeset_dpms(struct drm_client_dev *client, int mode) +{ + struct drm_device *dev = client->dev; + int ret = 0; + + if (!drm_master_internal_acquire(dev)) + return -EBUSY; + + mutex_lock(&client->modeset_mutex); + if (drm_drv_uses_atomic_modeset(dev)) + ret = drm_client_modeset_commit_atomic(client, mode == DRM_MODE_DPMS_ON); + else + drm_client_modeset_dpms_legacy(client, mode); + mutex_unlock(&client->modeset_mutex); + + drm_master_internal_release(dev); + + return ret; +} +EXPORT_SYMBOL(drm_client_modeset_dpms); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 64800d43837d..7b388674a456 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -38,7 +38,6 @@ #include #include -#include #include #include #include @@ -47,8 +46,6 @@ #include #include -#include "drm_crtc_helper_internal.h" -#include "drm_crtc_internal.h" #include "drm_internal.h" static bool drm_fbdev_emulation = true; @@ -393,233 +390,6 @@ int drm_fb_helper_debug_leave(struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_debug_leave); -/** - * drm_client_panel_rotation() - Check panel orientation - * @modeset: DRM modeset - * @rotation: Returned rotation value - * - * This function checks if the primary plane in @modeset can hw rotate to match - * the panel orientation on its connector. - * - * Note: Currently only 0 and 180 degrees are supported. - * - * Return: - * True if the plane can do the rotation, false otherwise. - */ -bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotation) -{ - struct drm_connector *connector = modeset->connectors[0]; - struct drm_plane *plane = modeset->crtc->primary; - u64 valid_mask = 0; - unsigned int i; - - if (!modeset->num_connectors) - return false; - - switch (connector->display_info.panel_orientation) { - case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP: - *rotation = DRM_MODE_ROTATE_180; - break; - case DRM_MODE_PANEL_ORIENTATION_LEFT_UP: - *rotation = DRM_MODE_ROTATE_90; - break; - case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP: - *rotation = DRM_MODE_ROTATE_270; - break; - default: - *rotation = DRM_MODE_ROTATE_0; - } - - /* - * TODO: support 90 / 270 degree hardware rotation, - * depending on the hardware this may require the framebuffer - * to be in a specific tiling format. - */ - if (*rotation != DRM_MODE_ROTATE_180 || !plane->rotation_property) - return false; - - for (i = 0; i < plane->rotation_property->num_values; i++) - valid_mask |= (1ULL << plane->rotation_property->values[i]); - - if (!(*rotation & valid_mask)) - return false; - - return true; -} - -static int drm_client_modeset_commit_atomic(struct drm_client_dev *client, bool active) -{ - struct drm_device *dev = client->dev; - struct drm_plane_state *plane_state; - struct drm_plane *plane; - struct drm_atomic_state *state; - struct drm_modeset_acquire_ctx ctx; - struct drm_mode_set *mode_set; - int ret; - - drm_modeset_acquire_init(&ctx, 0); - - state = drm_atomic_state_alloc(dev); - if (!state) { - ret = -ENOMEM; - goto out_ctx; - } - - state->acquire_ctx = &ctx; -retry: - drm_for_each_plane(plane, dev) { - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - ret = PTR_ERR(plane_state); - goto out_state; - } - - plane_state->rotation = DRM_MODE_ROTATE_0; - - /* disable non-primary: */ - if (plane->type == DRM_PLANE_TYPE_PRIMARY) - continue; - - ret = __drm_atomic_helper_disable_plane(plane, plane_state); - if (ret != 0) - goto out_state; - } - - drm_client_for_each_modeset(mode_set, client) { - struct drm_plane *primary = mode_set->crtc->primary; - unsigned int rotation; - - if (drm_client_panel_rotation(mode_set, &rotation)) { - /* Cannot fail as we've already gotten the plane state above */ - plane_state = drm_atomic_get_new_plane_state(state, primary); - plane_state->rotation = rotation; - } - - ret = __drm_atomic_helper_set_config(mode_set, state); - if (ret != 0) - goto out_state; - - /* - * __drm_atomic_helper_set_config() sets active when a - * mode is set, unconditionally clear it if we force DPMS off - */ - if (!active) { - struct drm_crtc *crtc = mode_set->crtc; - struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); - - crtc_state->active = false; - } - } - - ret = drm_atomic_commit(state); - -out_state: - if (ret == -EDEADLK) - goto backoff; - - drm_atomic_state_put(state); -out_ctx: - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); - - return ret; - -backoff: - drm_atomic_state_clear(state); - drm_modeset_backoff(&ctx); - - goto retry; -} - -static int drm_client_modeset_commit_legacy(struct drm_client_dev *client) -{ - struct drm_device *dev = client->dev; - struct drm_mode_set *mode_set; - struct drm_plane *plane; - int ret = 0; - - drm_modeset_lock_all(dev); - drm_for_each_plane(plane, dev) { - if (plane->type != DRM_PLANE_TYPE_PRIMARY) - drm_plane_force_disable(plane); - - if (plane->rotation_property) - drm_mode_plane_set_obj_prop(plane, - plane->rotation_property, - DRM_MODE_ROTATE_0); - } - - drm_client_for_each_modeset(mode_set, client) { - struct drm_crtc *crtc = mode_set->crtc; - - if (crtc->funcs->cursor_set2) { - ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0); - if (ret) - goto out; - } else if (crtc->funcs->cursor_set) { - ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0); - if (ret) - goto out; - } - - ret = drm_mode_set_config_internal(mode_set); - if (ret) - goto out; - } -out: - drm_modeset_unlock_all(dev); - - return ret; -} - -/** - * drm_client_modeset_commit_force() - Force commit CRTC configuration - * @client: DRM client - * - * Commit modeset configuration to crtcs without checking if there is a DRM master. - * - * Returns: - * Zero on success or negative error code on failure. - */ -int drm_client_modeset_commit_force(struct drm_client_dev *client) -{ - struct drm_device *dev = client->dev; - int ret; - - mutex_lock(&client->modeset_mutex); - if (drm_drv_uses_atomic_modeset(dev)) - ret = drm_client_modeset_commit_atomic(client, true); - else - ret = drm_client_modeset_commit_legacy(client); - mutex_unlock(&client->modeset_mutex); - - return ret; -} - -/** - * drm_client_modeset_commit() - Commit CRTC configuration - * @client: DRM client - * - * Commit modeset configuration to crtcs. - * - * Returns: - * Zero on success or negative error code on failure. - */ -int drm_client_modeset_commit(struct drm_client_dev *client) -{ - struct drm_device *dev = client->dev; - int ret; - - if (!drm_master_internal_acquire(dev)) - return -EBUSY; - - ret = drm_client_modeset_commit_force(client); - - drm_master_internal_release(dev); - - return ret; -} - /** * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration * @fb_helper: driver-allocated fbdev helper, can be NULL @@ -719,58 +489,6 @@ static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { }; #endif -static void drm_client_modeset_dpms_legacy(struct drm_client_dev *client, int dpms_mode) -{ - struct drm_device *dev = client->dev; - struct drm_connector *connector; - struct drm_mode_set *modeset; - int j; - - drm_modeset_lock_all(dev); - drm_client_for_each_modeset(modeset, client) { - if (!modeset->crtc->enabled) - continue; - - for (j = 0; j < modeset->num_connectors; j++) { - connector = modeset->connectors[j]; - connector->funcs->dpms(connector, dpms_mode); - drm_object_property_set_value(&connector->base, - dev->mode_config.dpms_property, dpms_mode); - } - } - drm_modeset_unlock_all(dev); -} - -/** - * drm_client_modeset_dpms() - Set DPMS mode - * @client: DRM client - * @mode: DPMS mode - * - * Note: For atomic drivers @mode is reduced to on/off. - * - * Returns: - * Zero on success or negative error code on failure. - */ -int drm_client_modeset_dpms(struct drm_client_dev *client, int mode) -{ - struct drm_device *dev = client->dev; - int ret = 0; - - if (!drm_master_internal_acquire(dev)) - return -EBUSY; - - mutex_lock(&client->modeset_mutex); - if (drm_drv_uses_atomic_modeset(dev)) - ret = drm_client_modeset_commit_atomic(client, mode == DRM_MODE_DPMS_ON); - else - drm_client_modeset_dpms_legacy(client, mode); - mutex_unlock(&client->modeset_mutex); - - drm_master_internal_release(dev); - - return ret; -} - static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) { struct drm_fb_helper *fb_helper = info->par; diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index 87be9aeb1fe0..6cf48419f77f 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -155,6 +155,10 @@ int drm_client_modeset_create(struct drm_client_dev *client); void drm_client_modeset_free(struct drm_client_dev *client); void drm_client_modeset_release(struct drm_client_dev *client); struct drm_mode_set *drm_client_find_modeset(struct drm_client_dev *client, struct drm_crtc *crtc); +bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotation); +int drm_client_modeset_commit_force(struct drm_client_dev *client); +int drm_client_modeset_commit(struct drm_client_dev *client); +int drm_client_modeset_dpms(struct drm_client_dev *client, int mode); /** * drm_client_for_each_modeset() - Iterate over client modesets -- cgit v1.2.3 From 75f3f70f0462f443283e66d8448bed9330073178 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 8 Jun 2019 10:02:35 +0200 Subject: drm: drm_crtc.h self-contained While removing drmP.h from drm/radeon a few files ended up including drm_crtc.h as the first file. This failed build due to a missing dependency in drm_crtc.h. Add the missing include file. Signed-off-by: Sam Ravnborg Reviewed-by: Alex Deucher Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Sean Paul Cc: David Airlie Cc: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190608080241.4958-2-sam@ravnborg.org --- include/drm/drm_crtc.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/drm') diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 58ad983d7cd6..dc42b9e35333 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From a7d469cc99b16cfda51d1c8a5ea250119c3e220a Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 8 Jun 2019 10:02:36 +0200 Subject: drm: drm_debugfs.h self-contained While removing drmP.h from drm/radeon a few files ended up including drm_debugfs.h as the first file. This failed build due to missing dependencies in drm_debugfs.h. Add the missing include files. Signed-off-by: Sam Ravnborg Reviewed-by: Alex Deucher Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Sean Paul Cc: David Airlie Cc: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190608080241.4958-3-sam@ravnborg.org --- include/drm/drm_debugfs.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/drm') diff --git a/include/drm/drm_debugfs.h b/include/drm/drm_debugfs.h index ac0f75df1ac9..7501e323d383 100644 --- a/include/drm/drm_debugfs.h +++ b/include/drm/drm_debugfs.h @@ -32,6 +32,8 @@ #ifndef _DRM_DEBUGFS_H_ #define _DRM_DEBUGFS_H_ +#include +#include /** * struct drm_info_list - debugfs info list entry * -- cgit v1.2.3 From 656600efd67b9b185bc1c85ec68d9420c2bbb812 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Mon, 10 Jun 2019 00:07:48 +0200 Subject: drm: fix build errors with drm_print.h drm_print.h requires to fix build when macros are used. Pull in the header file in drm_print.h so users do not have to do it. Signed-off-by: Sam Ravnborg Reviewed-by: Alex Deucher Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Sean Paul Cc: David Airlie Cc: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190609220757.10862-2-sam@ravnborg.org --- include/drm/drm_print.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/drm') diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index 3a4247319e63..a5d6f2f3e430 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -32,6 +32,8 @@ #include #include +#include + /** * DOC: print * -- cgit v1.2.3 From e5852bee90d6cb7d9bd2c635c056e7746f137e06 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Sat, 8 Jun 2019 17:26:53 +0200 Subject: drm/fb-helper: Remove drm_fb_helper_connector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All drivers add all their connectors so there's no need to keep around an array of available connectors. Instead we just put the useable (not writeback) connectors in a temporary array using drm_client_for_each_connector_iter() everytime we probe the outputs. Other places where it's necessary to look at the connectors, we just iterate over them using the same iterator function. Rename functions which signature is changed since they will be moved to drm_client in a later patch. v6: Improve commit message (Sam Ravnborg) Signed-off-by: Noralf Trønnes Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20190608152657.36613-2-noralf@tronnes.org --- Documentation/gpu/todo.rst | 4 + drivers/gpu/drm/drm_fb_helper.c | 506 +++++++++++++--------------------------- include/drm/drm_client.h | 15 ++ include/drm/drm_fb_helper.h | 80 ++----- 4 files changed, 197 insertions(+), 408 deletions(-) (limited to 'include/drm') diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 9d4038c50013..ab96ba0600a9 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -292,6 +292,10 @@ drm_fb_helper tasks - The max connector argument for drm_fb_helper_init() and drm_fb_helper_fbdev_setup() isn't used anymore and can be removed. +- The helper doesn't keep an array of connectors anymore so these can be + removed: drm_fb_helper_single_add_all_connectors(), + drm_fb_helper_add_one_connector() and drm_fb_helper_remove_one_connector(). + Core refactorings ================= diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 7b388674a456..b936db6280ac 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -95,12 +95,6 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * Setup fbdev emulation by calling drm_fb_helper_fbdev_setup() and tear it * down by calling drm_fb_helper_fbdev_teardown(). * - * Drivers that need to handle connector hotplugging (e.g. dp mst) can't use - * the setup helper and will need to do the whole four-step setup process with - * drm_fb_helper_prepare(), drm_fb_helper_init(), - * drm_fb_helper_single_add_all_connectors(), enable hotplugging and - * drm_fb_helper_initial_config() to avoid a possible race window. - * * At runtime drivers should restore the fbdev console by using * drm_fb_helper_lastclose() as their &drm_driver.lastclose callback. * They should also notify the fb helper code from updates to the output @@ -123,8 +117,7 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * encoders and connectors. To finish up the fbdev helper initialization, the * drm_fb_helper_init() function is called. To probe for all attached displays * and set up an initial configuration using the detected hardware, drivers - * should call drm_fb_helper_single_add_all_connectors() followed by - * drm_fb_helper_initial_config(). + * should call drm_fb_helper_initial_config(). * * If &drm_framebuffer_funcs.dirty is set, the * drm_fb_helper_{cfb,sys}_{write,fillrect,copyarea,imageblit} functions will @@ -137,165 +130,6 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * deferred I/O (coupled with drm_fb_helper_fbdev_teardown()). */ -#define drm_fb_helper_for_each_connector(fbh, i__) \ - for (({ lockdep_assert_held(&(fbh)->lock); }), \ - i__ = 0; i__ < (fbh)->connector_count; i__++) - -static int __drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - struct drm_fb_helper_connector *fb_conn; - struct drm_fb_helper_connector **temp; - unsigned int count; - - if (!drm_fbdev_emulation) - return 0; - - lockdep_assert_held(&fb_helper->lock); - - count = fb_helper->connector_count + 1; - - if (count > fb_helper->connector_info_alloc_count) { - size_t size = count * sizeof(fb_conn); - - temp = krealloc(fb_helper->connector_info, size, GFP_KERNEL); - if (!temp) - return -ENOMEM; - - fb_helper->connector_info_alloc_count = count; - fb_helper->connector_info = temp; - } - - fb_conn = kzalloc(sizeof(*fb_conn), GFP_KERNEL); - if (!fb_conn) - return -ENOMEM; - - drm_connector_get(connector); - fb_conn->connector = connector; - fb_helper->connector_info[fb_helper->connector_count++] = fb_conn; - - return 0; -} - -int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - int err; - - if (!fb_helper) - return 0; - - mutex_lock(&fb_helper->lock); - err = __drm_fb_helper_add_one_connector(fb_helper, connector); - mutex_unlock(&fb_helper->lock); - - return err; -} -EXPORT_SYMBOL(drm_fb_helper_add_one_connector); - -/** - * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev - * emulation helper - * @fb_helper: fbdev initialized with drm_fb_helper_init, can be NULL - * - * This functions adds all the available connectors for use with the given - * fb_helper. This is a separate step to allow drivers to freely assign - * connectors to the fbdev, e.g. if some are reserved for special purposes or - * not adequate to be used for the fbcon. - * - * This function is protected against concurrent connector hotadds/removals - * using drm_fb_helper_add_one_connector() and - * drm_fb_helper_remove_one_connector(). - */ -int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) -{ - struct drm_device *dev; - struct drm_connector *connector; - struct drm_connector_list_iter conn_iter; - int i, ret = 0; - - if (!drm_fbdev_emulation || !fb_helper) - return 0; - - dev = fb_helper->dev; - - mutex_lock(&fb_helper->lock); - drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(connector, &conn_iter) { - if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) - continue; - - ret = __drm_fb_helper_add_one_connector(fb_helper, connector); - if (ret) - goto fail; - } - goto out; - -fail: - drm_fb_helper_for_each_connector(fb_helper, i) { - struct drm_fb_helper_connector *fb_helper_connector = - fb_helper->connector_info[i]; - - drm_connector_put(fb_helper_connector->connector); - - kfree(fb_helper_connector); - fb_helper->connector_info[i] = NULL; - } - fb_helper->connector_count = 0; -out: - drm_connector_list_iter_end(&conn_iter); - mutex_unlock(&fb_helper->lock); - - return ret; -} -EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); - -static int __drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - struct drm_fb_helper_connector *fb_helper_connector; - int i, j; - - if (!drm_fbdev_emulation) - return 0; - - lockdep_assert_held(&fb_helper->lock); - - drm_fb_helper_for_each_connector(fb_helper, i) { - if (fb_helper->connector_info[i]->connector == connector) - break; - } - - if (i == fb_helper->connector_count) - return -EINVAL; - fb_helper_connector = fb_helper->connector_info[i]; - drm_connector_put(fb_helper_connector->connector); - - for (j = i + 1; j < fb_helper->connector_count; j++) - fb_helper->connector_info[j - 1] = fb_helper->connector_info[j]; - - fb_helper->connector_count--; - kfree(fb_helper_connector); - - return 0; -} - -int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - int err; - - if (!fb_helper) - return 0; - - mutex_lock(&fb_helper->lock); - err = __drm_fb_helper_remove_one_connector(fb_helper, connector); - mutex_unlock(&fb_helper->lock); - - return err; -} -EXPORT_SYMBOL(drm_fb_helper_remove_one_connector); - static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) { uint16_t *r_base, *g_base, *b_base; @@ -645,20 +479,9 @@ int drm_fb_helper_init(struct drm_device *dev, return ret; } - fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL); - if (!fb_helper->connector_info) - goto out_free; - - fb_helper->connector_info_alloc_count = dev->mode_config.num_connector; - fb_helper->connector_count = 0; - dev->fb_helper = fb_helper; return 0; -out_free: - drm_client_release(&fb_helper->client); - - return -ENOMEM; } EXPORT_SYMBOL(drm_fb_helper_init); @@ -733,7 +556,6 @@ EXPORT_SYMBOL(drm_fb_helper_unregister_fbi); void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) { struct fb_info *info; - int i; if (!fb_helper) return; @@ -766,12 +588,6 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) if (!fb_helper->client.funcs) drm_client_release(&fb_helper->client); - - for (i = 0; i < fb_helper->connector_count; i++) { - drm_connector_put(fb_helper->connector_info[i]->connector); - kfree(fb_helper->connector_info[i]); - } - kfree(fb_helper->connector_info); } EXPORT_SYMBOL(drm_fb_helper_fini); @@ -1650,8 +1466,9 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, struct drm_client_dev *client = &fb_helper->client; int ret = 0; int crtc_count = 0; - int i; + struct drm_connector_list_iter conn_iter; struct drm_fb_helper_surface_size sizes; + struct drm_connector *connector; struct drm_mode_set *mode_set; int best_depth = 0; @@ -1668,11 +1485,11 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, if (preferred_bpp != sizes.surface_bpp) sizes.surface_depth = sizes.surface_bpp = preferred_bpp; - drm_fb_helper_for_each_connector(fb_helper, i) { - struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i]; + drm_connector_list_iter_begin(fb_helper->dev, &conn_iter); + drm_client_for_each_connector_iter(connector, &conn_iter) { struct drm_cmdline_mode *cmdline_mode; - cmdline_mode = &fb_helper_conn->connector->cmdline_mode; + cmdline_mode = &connector->cmdline_mode; if (cmdline_mode->bpp_specified) { switch (cmdline_mode->bpp) { @@ -1697,6 +1514,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, break; } } + drm_connector_list_iter_end(&conn_iter); /* * If we run into a situation where, for example, the primary plane @@ -1881,26 +1699,12 @@ void drm_fb_helper_fill_info(struct fb_info *info, } EXPORT_SYMBOL(drm_fb_helper_fill_info); -static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper, - uint32_t maxX, - uint32_t maxY) -{ - struct drm_connector *connector; - int i, count = 0; - - drm_fb_helper_for_each_connector(fb_helper, i) { - connector = fb_helper->connector_info[i]->connector; - count += connector->funcs->fill_modes(connector, maxX, maxY); - } - - return count; -} - -struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height) +static struct drm_display_mode * +drm_connector_has_preferred_mode(struct drm_connector *connector, int width, int height) { struct drm_display_mode *mode; - list_for_each_entry(mode, &fb_connector->connector->modes, head) { + list_for_each_entry(mode, &connector->modes, head) { if (mode->hdisplay > width || mode->vdisplay > height) continue; @@ -1909,20 +1713,15 @@ struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector * } return NULL; } -EXPORT_SYMBOL(drm_has_preferred_mode); - -static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) -{ - return fb_connector->connector->cmdline_mode.specified; -} -struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn) +static struct drm_display_mode * +drm_connector_pick_cmdline_mode(struct drm_connector *connector) { struct drm_cmdline_mode *cmdline_mode; struct drm_display_mode *mode; bool prefer_non_interlace; - cmdline_mode = &fb_helper_conn->connector->cmdline_mode; + cmdline_mode = &connector->cmdline_mode; if (cmdline_mode->specified == false) return NULL; @@ -1934,7 +1733,7 @@ struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *f prefer_non_interlace = !cmdline_mode->interlace; again: - list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { + list_for_each_entry(mode, &connector->modes, head) { /* check width/height */ if (mode->hdisplay != cmdline_mode->xres || mode->vdisplay != cmdline_mode->yres) @@ -1961,12 +1760,11 @@ again: } create_mode: - mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev, - cmdline_mode); - list_add(&mode->head, &fb_helper_conn->connector->modes); + mode = drm_mode_create_from_cmdline_mode(connector->dev, cmdline_mode); + list_add(&mode->head, &connector->modes); + return mode; } -EXPORT_SYMBOL(drm_pick_cmdline_mode); static bool drm_connector_enabled(struct drm_connector *connector, bool strict) { @@ -1983,15 +1781,16 @@ static bool drm_connector_enabled(struct drm_connector *connector, bool strict) return enable; } -static void drm_enable_connectors(struct drm_fb_helper *fb_helper, - bool *enabled) +static void drm_client_connectors_enabled(struct drm_connector **connectors, + unsigned int connector_count, + bool *enabled) { bool any_enabled = false; struct drm_connector *connector; int i = 0; - drm_fb_helper_for_each_connector(fb_helper, i) { - connector = fb_helper->connector_info[i]->connector; + for (i = 0; i < connector_count; i++) { + connector = connectors[i]; enabled[i] = drm_connector_enabled(connector, true); DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, connector->display_info.non_desktop ? "non desktop" : enabled[i] ? "yes" : "no"); @@ -2002,28 +1801,27 @@ static void drm_enable_connectors(struct drm_fb_helper *fb_helper, if (any_enabled) return; - drm_fb_helper_for_each_connector(fb_helper, i) { - connector = fb_helper->connector_info[i]->connector; - enabled[i] = drm_connector_enabled(connector, false); - } + for (i = 0; i < connector_count; i++) + enabled[i] = drm_connector_enabled(connectors[i], false); } -static bool drm_target_cloned(struct drm_fb_helper *fb_helper, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - bool *enabled, int width, int height) +static bool drm_client_target_cloned(struct drm_device *dev, + struct drm_connector **connectors, + unsigned int connector_count, + struct drm_display_mode **modes, + struct drm_fb_offset *offsets, + bool *enabled, int width, int height) { int count, i, j; bool can_clone = false; - struct drm_fb_helper_connector *fb_helper_conn; struct drm_display_mode *dmt_mode, *mode; /* only contemplate cloning in the single crtc case */ - if (fb_helper->dev->mode_config.num_crtc > 1) + if (dev->mode_config.num_crtc > 1) return false; count = 0; - drm_fb_helper_for_each_connector(fb_helper, i) { + for (i = 0; i < connector_count; i++) { if (enabled[i]) count++; } @@ -2034,11 +1832,10 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper, /* check the command line or if nothing common pick 1024x768 */ can_clone = true; - drm_fb_helper_for_each_connector(fb_helper, i) { + for (i = 0; i < connector_count; i++) { if (!enabled[i]) continue; - fb_helper_conn = fb_helper->connector_info[i]; - modes[i] = drm_pick_cmdline_mode(fb_helper_conn); + modes[i] = drm_connector_pick_cmdline_mode(connectors[i]); if (!modes[i]) { can_clone = false; break; @@ -2062,14 +1859,13 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper, /* try and find a 1024x768 mode on each connector */ can_clone = true; - dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false); + dmt_mode = drm_mode_find_dmt(dev, 1024, 768, 60, false); - drm_fb_helper_for_each_connector(fb_helper, i) { + for (i = 0; i < connector_count; i++) { if (!enabled[i]) continue; - fb_helper_conn = fb_helper->connector_info[i]; - list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { + list_for_each_entry(mode, &connectors[i]->modes, head) { if (drm_mode_match(mode, dmt_mode, DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_CLOCK | @@ -2089,30 +1885,31 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper, return false; } -static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - int idx, - int h_idx, int v_idx) +static int drm_client_get_tile_offsets(struct drm_connector **connectors, + unsigned int connector_count, + struct drm_display_mode **modes, + struct drm_fb_offset *offsets, + int idx, + int h_idx, int v_idx) { - struct drm_fb_helper_connector *fb_helper_conn; + struct drm_connector *connector; int i; int hoffset = 0, voffset = 0; - drm_fb_helper_for_each_connector(fb_helper, i) { - fb_helper_conn = fb_helper->connector_info[i]; - if (!fb_helper_conn->connector->has_tile) + for (i = 0; i < connector_count; i++) { + connector = connectors[i]; + if (!connector->has_tile) continue; if (!modes[i] && (h_idx || v_idx)) { DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i, - fb_helper_conn->connector->base.id); + connector->base.id); continue; } - if (fb_helper_conn->connector->tile_h_loc < h_idx) + if (connector->tile_h_loc < h_idx) hoffset += modes[i]->hdisplay; - if (fb_helper_conn->connector->tile_v_loc < v_idx) + if (connector->tile_v_loc < v_idx) voffset += modes[i]->vdisplay; } offsets[idx].x = hoffset; @@ -2121,20 +1918,21 @@ static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper, return 0; } -static bool drm_target_preferred(struct drm_fb_helper *fb_helper, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - bool *enabled, int width, int height) +static bool drm_client_target_preferred(struct drm_connector **connectors, + unsigned int connector_count, + struct drm_display_mode **modes, + struct drm_fb_offset *offsets, + bool *enabled, int width, int height) { - struct drm_fb_helper_connector *fb_helper_conn; - const u64 mask = BIT_ULL(fb_helper->connector_count) - 1; + const u64 mask = BIT_ULL(connector_count) - 1; + struct drm_connector *connector; u64 conn_configured = 0; int tile_pass = 0; int i; retry: - drm_fb_helper_for_each_connector(fb_helper, i) { - fb_helper_conn = fb_helper->connector_info[i]; + for (i = 0; i < connector_count; i++) { + connector = connectors[i]; if (conn_configured & BIT_ULL(i)) continue; @@ -2145,17 +1943,17 @@ retry: } /* first pass over all the untiled connectors */ - if (tile_pass == 0 && fb_helper_conn->connector->has_tile) + if (tile_pass == 0 && connector->has_tile) continue; if (tile_pass == 1) { - if (fb_helper_conn->connector->tile_h_loc != 0 || - fb_helper_conn->connector->tile_v_loc != 0) + if (connector->tile_h_loc != 0 || + connector->tile_v_loc != 0) continue; } else { - if (fb_helper_conn->connector->tile_h_loc != tile_pass - 1 && - fb_helper_conn->connector->tile_v_loc != tile_pass - 1) + if (connector->tile_h_loc != tile_pass - 1 && + connector->tile_v_loc != tile_pass - 1) /* if this tile_pass doesn't cover any of the tiles - keep going */ continue; @@ -2163,22 +1961,22 @@ retry: * find the tile offsets for this pass - need to find * all tiles left and above */ - drm_get_tile_offsets(fb_helper, modes, offsets, - i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc); + drm_client_get_tile_offsets(connectors, connector_count, modes, offsets, i, + connector->tile_h_loc, connector->tile_v_loc); } DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", - fb_helper_conn->connector->base.id); + connector->base.id); /* got for command line mode first */ - modes[i] = drm_pick_cmdline_mode(fb_helper_conn); + modes[i] = drm_connector_pick_cmdline_mode(connector); if (!modes[i]) { DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n", - fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0); - modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height); + connector->base.id, connector->tile_group ? connector->tile_group->id : 0); + modes[i] = drm_connector_has_preferred_mode(connector, width, height); } /* No preferred modes, pick one off the list */ - if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) { - list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head) + if (!modes[i] && !list_empty(&connector->modes)) { + list_for_each_entry(modes[i], &connector->modes, head) break; } DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : @@ -2207,40 +2005,41 @@ static bool connector_has_possible_crtc(struct drm_connector *connector, return false; } -static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, - struct drm_crtc **best_crtcs, - struct drm_display_mode **modes, - int n, int width, int height) +static int drm_client_pick_crtcs(struct drm_client_dev *client, + struct drm_connector **connectors, + unsigned int connector_count, + struct drm_crtc **best_crtcs, + struct drm_display_mode **modes, + int n, int width, int height) { - struct drm_client_dev *client = &fb_helper->client; + struct drm_device *dev = client->dev; struct drm_connector *connector; int my_score, best_score, score; struct drm_crtc **crtcs, *crtc; struct drm_mode_set *modeset; - struct drm_fb_helper_connector *fb_helper_conn; int o; - if (n == fb_helper->connector_count) + if (n == connector_count) return 0; - fb_helper_conn = fb_helper->connector_info[n]; - connector = fb_helper_conn->connector; + connector = connectors[n]; best_crtcs[n] = NULL; - best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height); + best_score = drm_client_pick_crtcs(client, connectors, connector_count, + best_crtcs, modes, n + 1, width, height); if (modes[n] == NULL) return best_score; - crtcs = kcalloc(fb_helper->connector_count, sizeof(*crtcs), GFP_KERNEL); + crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); if (!crtcs) return best_score; my_score = 1; if (connector->status == connector_status_connected) my_score++; - if (drm_has_cmdline_mode(fb_helper_conn)) + if (connector->cmdline_mode.specified) my_score++; - if (drm_has_preferred_mode(fb_helper_conn, width, height)) + if (drm_connector_has_preferred_mode(connector, width, height)) my_score++; /* @@ -2259,7 +2058,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, if (o < n) { /* ignore cloning unless only a single crtc */ - if (fb_helper->dev->mode_config.num_crtc > 1) + if (dev->mode_config.num_crtc > 1) continue; if (!drm_mode_equal(modes[o], modes[n])) @@ -2268,12 +2067,11 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, crtcs[n] = crtc; memcpy(crtcs, best_crtcs, n * sizeof(*crtcs)); - score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1, - width, height); + score = my_score + drm_client_pick_crtcs(client, connectors, connector_count, + crtcs, modes, n + 1, width, height); if (score > best_score) { best_score = score; - memcpy(best_crtcs, crtcs, - fb_helper->connector_count * sizeof(*crtcs)); + memcpy(best_crtcs, crtcs, connector_count * sizeof(*crtcs)); } } @@ -2282,15 +2080,17 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, } /* Try to read the BIOS display configuration and use it for the initial config */ -static bool drm_fb_helper_firmware_config(struct drm_fb_helper *fb_helper, - struct drm_crtc **crtcs, - struct drm_display_mode **modes, - struct drm_fb_offset *offsets, - bool *enabled, int width, int height) -{ - struct drm_device *dev = fb_helper->dev; - unsigned int count = min(fb_helper->connector_count, BITS_PER_LONG); +static bool drm_client_firmware_config(struct drm_client_dev *client, + struct drm_connector **connectors, + unsigned int connector_count, + struct drm_crtc **crtcs, + struct drm_display_mode **modes, + struct drm_fb_offset *offsets, + bool *enabled, int width, int height) +{ + unsigned int count = min_t(unsigned int, connector_count, BITS_PER_LONG); unsigned long conn_configured, conn_seq, mask; + struct drm_device *dev = client->dev; int i, j; bool *save_enabled; bool fallback = true, ret = true; @@ -2316,13 +2116,11 @@ static bool drm_fb_helper_firmware_config(struct drm_fb_helper *fb_helper, retry: conn_seq = conn_configured; for (i = 0; i < count; i++) { - struct drm_fb_helper_connector *fb_conn; struct drm_connector *connector; struct drm_encoder *encoder; struct drm_crtc *new_crtc; - fb_conn = fb_helper->connector_info[i]; - connector = fb_conn->connector; + connector = connectors[i]; if (conn_configured & BIT(i)) continue; @@ -2379,14 +2177,13 @@ retry: connector->name); /* go for command line mode first */ - modes[i] = drm_pick_cmdline_mode(fb_conn); + modes[i] = drm_connector_pick_cmdline_mode(connector); /* try for preferred next */ if (!modes[i]) { DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n", connector->name, connector->has_tile); - modes[i] = drm_has_preferred_mode(fb_conn, width, - height); + modes[i] = drm_connector_has_preferred_mode(connector, width, height); } /* No preferred mode marked by the EDID? Are there any modes? */ @@ -2461,8 +2258,12 @@ bail: static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, u32 width, u32 height) { + struct drm_connector *connector, **connectors = NULL; struct drm_client_dev *client = &fb_helper->client; + struct drm_connector_list_iter conn_iter; struct drm_device *dev = fb_helper->dev; + unsigned int total_modes_count = 0; + unsigned int connector_count = 0; struct drm_display_mode **modes; struct drm_fb_offset *offsets; struct drm_crtc **crtcs; @@ -2470,16 +2271,28 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, int i; DRM_DEBUG_KMS("\n"); - /* prevent concurrent modification of connector_count by hotplug */ - lockdep_assert_held(&fb_helper->lock); - - crtcs = kcalloc(fb_helper->connector_count, sizeof(*crtcs), GFP_KERNEL); - modes = kcalloc(fb_helper->connector_count, - sizeof(struct drm_display_mode *), GFP_KERNEL); - offsets = kcalloc(fb_helper->connector_count, - sizeof(struct drm_fb_offset), GFP_KERNEL); - enabled = kcalloc(fb_helper->connector_count, - sizeof(bool), GFP_KERNEL); + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_client_for_each_connector_iter(connector, &conn_iter) { + struct drm_connector **tmp; + + tmp = krealloc(connectors, (connector_count + 1) * sizeof(*connectors), GFP_KERNEL); + if (!tmp) + goto free_connectors; + + connectors = tmp; + drm_connector_get(connector); + connectors[connector_count++] = connector; + } + drm_connector_list_iter_end(&conn_iter); + + if (!connector_count) + return; + + crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); + modes = kcalloc(connector_count, sizeof(*modes), GFP_KERNEL); + offsets = kcalloc(connector_count, sizeof(*offsets), GFP_KERNEL); + enabled = kcalloc(connector_count, sizeof(bool), GFP_KERNEL); if (!crtcs || !modes || !enabled || !offsets) { DRM_ERROR("Memory allocation failed\n"); goto out; @@ -2488,40 +2301,42 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, mutex_lock(&client->modeset_mutex); mutex_lock(&fb_helper->dev->mode_config.mutex); - if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0) + for (i = 0; i < connector_count; i++) + total_modes_count += connectors[i]->funcs->fill_modes(connectors[i], width, height); + if (!total_modes_count) DRM_DEBUG_KMS("No connectors reported connected with modes\n"); - drm_enable_connectors(fb_helper, enabled); - - if (!drm_fb_helper_firmware_config(fb_helper, crtcs, modes, offsets, - enabled, width, height)) { - memset(modes, 0, fb_helper->connector_count*sizeof(modes[0])); - memset(crtcs, 0, fb_helper->connector_count*sizeof(crtcs[0])); - memset(offsets, 0, fb_helper->connector_count*sizeof(offsets[0])); - - if (!drm_target_cloned(fb_helper, modes, offsets, - enabled, width, height) && - !drm_target_preferred(fb_helper, modes, offsets, - enabled, width, height)) + drm_client_connectors_enabled(connectors, connector_count, enabled); + + if (!drm_client_firmware_config(client, connectors, connector_count, crtcs, + modes, offsets, enabled, width, height)) { + memset(modes, 0, connector_count * sizeof(*modes)); + memset(crtcs, 0, connector_count * sizeof(*crtcs)); + memset(offsets, 0, connector_count * sizeof(*offsets)); + + if (!drm_client_target_cloned(dev, connectors, connector_count, modes, + offsets, enabled, width, height) && + !drm_client_target_preferred(connectors, connector_count, modes, + offsets, enabled, width, height)) DRM_ERROR("Unable to find initial modes\n"); DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); - drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height); + drm_client_pick_crtcs(client, connectors, connector_count, + crtcs, modes, 0, width, height); } mutex_unlock(&fb_helper->dev->mode_config.mutex); drm_client_modeset_release(client); - drm_fb_helper_for_each_connector(fb_helper, i) { + for (i = 0; i < connector_count; i++) { struct drm_display_mode *mode = modes[i]; struct drm_crtc *crtc = crtcs[i]; struct drm_fb_offset *offset = &offsets[i]; if (mode && crtc) { struct drm_mode_set *modeset = drm_client_find_modeset(client, crtc); - struct drm_connector *connector = - fb_helper->connector_info[i]->connector; + struct drm_connector *connector = connectors[i]; DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", mode->name, crtc->base.id, offset->x, offset->y); @@ -2544,6 +2359,10 @@ out: kfree(modes); kfree(offsets); kfree(enabled); +free_connectors: + for (i = 0; i < connector_count; i++) + drm_connector_put(connectors[i]); + kfree(connectors); } /* @@ -2556,10 +2375,11 @@ out: static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) { struct drm_client_dev *client = &fb_helper->client; + struct drm_connector_list_iter conn_iter; struct fb_info *info = fb_helper->fbdev; unsigned int rotation, sw_rotations = 0; + struct drm_connector *connector; struct drm_mode_set *modeset; - int i; mutex_lock(&client->modeset_mutex); drm_client_for_each_modeset(modeset, client) { @@ -2576,10 +2396,8 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) } mutex_unlock(&client->modeset_mutex); - mutex_lock(&fb_helper->dev->mode_config.mutex); - drm_fb_helper_for_each_connector(fb_helper, i) { - struct drm_connector *connector = - fb_helper->connector_info[i]->connector; + drm_connector_list_iter_begin(fb_helper->dev, &conn_iter); + drm_client_for_each_connector_iter(connector, &conn_iter) { /* use first connected connector for the physical dimensions */ if (connector->status == connector_status_connected) { @@ -2588,7 +2406,7 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) break; } } - mutex_unlock(&fb_helper->dev->mode_config.mutex); + drm_connector_list_iter_end(&conn_iter); switch (sw_rotations) { case DRM_MODE_ROTATE_0: @@ -2826,12 +2644,6 @@ int drm_fb_helper_fbdev_setup(struct drm_device *dev, return ret; } - ret = drm_fb_helper_single_add_all_connectors(fb_helper); - if (ret < 0) { - DRM_DEV_ERROR(dev->dev, "fbdev: Failed to add connectors (ret=%d)\n", ret); - goto err_drm_fb_helper_fini; - } - if (!drm_drv_uses_atomic_modeset(dev)) drm_helper_disable_unused_functions(dev); @@ -3137,10 +2949,6 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client) if (ret) goto err; - ret = drm_fb_helper_single_add_all_connectors(fb_helper); - if (ret) - goto err_cleanup; - if (!drm_drv_uses_atomic_modeset(dev)) drm_helper_disable_unused_functions(dev); diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index 6cf48419f77f..8d94880bbe25 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -7,6 +7,7 @@ #include #include +#include #include struct drm_client_dev; @@ -169,6 +170,20 @@ int drm_client_modeset_dpms(struct drm_client_dev *client, int mode); for (({ lockdep_assert_held(&(client)->modeset_mutex); }), \ modeset = (client)->modesets; modeset->crtc; modeset++) +/** + * drm_client_for_each_connector_iter - connector_list iterator macro + * @connector: &struct drm_connector pointer used as cursor + * @iter: &struct drm_connector_list_iter + * + * This iterates the connectors that are useable for internal clients (excludes + * writeback connectors). + * + * For more info see drm_for_each_connector_iter(). + */ +#define drm_client_for_each_connector_iter(connector, iter) \ + drm_for_each_connector_iter(connector, iter) \ + if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) + int drm_client_debugfs_init(struct drm_minor *minor); #endif diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 6b334f4d8a22..e14d52ef9638 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -97,16 +97,10 @@ struct drm_fb_helper_funcs { struct drm_fb_helper_surface_size *sizes); }; -struct drm_fb_helper_connector { - struct drm_connector *connector; -}; - /** * struct drm_fb_helper - main structure to emulate fbdev on top of KMS * @fb: Scanout framebuffer object * @dev: DRM device - * @connector_count: number of connected connectors - * @connector_info_alloc_count: size of connector_info * @funcs: driver callbacks for fb helper * @fbdev: emulated fbdev device info struct * @pseudo_palette: fake palette of 16 colors @@ -138,15 +132,6 @@ struct drm_fb_helper { struct drm_framebuffer *fb; struct drm_device *dev; - int connector_count; - int connector_info_alloc_count; - /** - * @connector_info: - * - * Array of per-connector information. Do not iterate directly, but use - * drm_fb_helper_for_each_connector. - */ - struct drm_fb_helper_connector **connector_info; const struct drm_fb_helper_funcs *funcs; struct fb_info *fbdev; u32 pseudo_palette[17]; @@ -286,18 +271,8 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper); int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel); -int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper); int drm_fb_helper_debug_enter(struct fb_info *info); int drm_fb_helper_debug_leave(struct fb_info *info); -struct drm_display_mode * -drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, - int width, int height); -struct drm_display_mode * -drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn); - -int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector); -int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector); int drm_fb_helper_fbdev_setup(struct drm_device *dev, struct drm_fb_helper *fb_helper, @@ -472,12 +447,6 @@ static inline int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, return 0; } -static inline int -drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) -{ - return 0; -} - static inline int drm_fb_helper_debug_enter(struct fb_info *info) { return 0; @@ -488,34 +457,6 @@ static inline int drm_fb_helper_debug_leave(struct fb_info *info) return 0; } -static inline struct drm_display_mode * -drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, - int width, int height) -{ - return NULL; -} - -static inline struct drm_display_mode * -drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, - int width, int height) -{ - return NULL; -} - -static inline int -drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - return 0; -} - -static inline int -drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, - struct drm_connector *connector) -{ - return 0; -} - static inline int drm_fb_helper_fbdev_setup(struct drm_device *dev, struct drm_fb_helper *fb_helper, @@ -557,6 +498,27 @@ drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) #endif +/* TODO: There's a todo entry to remove these three */ +static inline int +drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) +{ + return 0; +} + +static inline int +drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, + struct drm_connector *connector) +{ + return 0; +} + +static inline int +drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, + struct drm_connector *connector) +{ + return 0; +} + /** * drm_fb_helper_remove_conflicting_framebuffers - remove firmware-configured framebuffers * @a: memory range, users of which are to be removed -- cgit v1.2.3 From aafa9e0668720a663eb47f25d863a84160eead7b Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Sat, 8 Jun 2019 17:26:54 +0200 Subject: drm/fb-helper: Prepare to move out modeset config code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prepares the modeset code so it can be moved out as-is in the next patch. v3: Remove stray newline Signed-off-by: Noralf Trønnes Reviewed-by: Maxime Ripard Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20190608152657.36613-3-noralf@tronnes.org --- drivers/gpu/drm/drm_fb_helper.c | 62 +++++++++++++++++++++++++++++------------ include/drm/drm_fb_helper.h | 4 --- 2 files changed, 44 insertions(+), 22 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index b936db6280ac..e7f41e5d924c 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -48,6 +48,10 @@ #include "drm_internal.h" +struct drm_client_offset { + int x, y; +}; + static bool drm_fbdev_emulation = true; module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600); MODULE_PARM_DESC(fbdev_emulation, @@ -1809,7 +1813,7 @@ static bool drm_client_target_cloned(struct drm_device *dev, struct drm_connector **connectors, unsigned int connector_count, struct drm_display_mode **modes, - struct drm_fb_offset *offsets, + struct drm_client_offset *offsets, bool *enabled, int width, int height) { int count, i, j; @@ -1888,7 +1892,7 @@ static bool drm_client_target_cloned(struct drm_device *dev, static int drm_client_get_tile_offsets(struct drm_connector **connectors, unsigned int connector_count, struct drm_display_mode **modes, - struct drm_fb_offset *offsets, + struct drm_client_offset *offsets, int idx, int h_idx, int v_idx) { @@ -1921,7 +1925,7 @@ static int drm_client_get_tile_offsets(struct drm_connector **connectors, static bool drm_client_target_preferred(struct drm_connector **connectors, unsigned int connector_count, struct drm_display_mode **modes, - struct drm_fb_offset *offsets, + struct drm_client_offset *offsets, bool *enabled, int width, int height) { const u64 mask = BIT_ULL(connector_count) - 1; @@ -2085,7 +2089,7 @@ static bool drm_client_firmware_config(struct drm_client_dev *client, unsigned int connector_count, struct drm_crtc **crtcs, struct drm_display_mode **modes, - struct drm_fb_offset *offsets, + struct drm_client_offset *offsets, bool *enabled, int width, int height) { unsigned int count = min_t(unsigned int, connector_count, BITS_PER_LONG); @@ -2255,30 +2259,47 @@ bail: return ret; } -static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, - u32 width, u32 height) +/** + * drm_client_modeset_probe() - Probe for displays + * @client: DRM client + * @width: Maximum display mode width (optional) + * @height: Maximum display mode height (optional) + * + * This function sets up display pipelines for enabled connectors and stores the + * config in the client's modeset array. + * + * Returns: + * Zero on success or negative error code on failure. + */ +int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, unsigned int height) { struct drm_connector *connector, **connectors = NULL; - struct drm_client_dev *client = &fb_helper->client; struct drm_connector_list_iter conn_iter; - struct drm_device *dev = fb_helper->dev; + struct drm_device *dev = client->dev; unsigned int total_modes_count = 0; + struct drm_client_offset *offsets; unsigned int connector_count = 0; struct drm_display_mode **modes; - struct drm_fb_offset *offsets; struct drm_crtc **crtcs; + int i, ret = 0; bool *enabled; - int i; DRM_DEBUG_KMS("\n"); + if (!width) + width = dev->mode_config.max_width; + if (!height) + height = dev->mode_config.max_height; + drm_connector_list_iter_begin(dev, &conn_iter); drm_client_for_each_connector_iter(connector, &conn_iter) { struct drm_connector **tmp; tmp = krealloc(connectors, (connector_count + 1) * sizeof(*connectors), GFP_KERNEL); - if (!tmp) + if (!tmp) { + ret = -ENOMEM; goto free_connectors; + } connectors = tmp; drm_connector_get(connector); @@ -2287,7 +2308,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, drm_connector_list_iter_end(&conn_iter); if (!connector_count) - return; + return 0; crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); modes = kcalloc(connector_count, sizeof(*modes), GFP_KERNEL); @@ -2295,12 +2316,13 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, enabled = kcalloc(connector_count, sizeof(bool), GFP_KERNEL); if (!crtcs || !modes || !enabled || !offsets) { DRM_ERROR("Memory allocation failed\n"); + ret = -ENOMEM; goto out; } mutex_lock(&client->modeset_mutex); - mutex_lock(&fb_helper->dev->mode_config.mutex); + mutex_lock(&dev->mode_config.mutex); for (i = 0; i < connector_count; i++) total_modes_count += connectors[i]->funcs->fill_modes(connectors[i], width, height); if (!total_modes_count) @@ -2325,14 +2347,14 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, drm_client_pick_crtcs(client, connectors, connector_count, crtcs, modes, 0, width, height); } - mutex_unlock(&fb_helper->dev->mode_config.mutex); + mutex_unlock(&dev->mode_config.mutex); drm_client_modeset_release(client); for (i = 0; i < connector_count; i++) { struct drm_display_mode *mode = modes[i]; struct drm_crtc *crtc = crtcs[i]; - struct drm_fb_offset *offset = &offsets[i]; + struct drm_client_offset *offset = &offsets[i]; if (mode && crtc) { struct drm_mode_set *modeset = drm_client_find_modeset(client, crtc); @@ -2342,8 +2364,10 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, mode->name, crtc->base.id, offset->x, offset->y); if (WARN_ON_ONCE(modeset->num_connectors == DRM_CLIENT_MAX_CLONED_CONNECTORS || - (dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1))) + (dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1))) { + ret = -EINVAL; break; + } modeset->mode = drm_mode_duplicate(dev, mode); drm_connector_get(connector); @@ -2363,6 +2387,8 @@ free_connectors: for (i = 0; i < connector_count; i++) drm_connector_put(connectors[i]); kfree(connectors); + + return ret; } /* @@ -2444,7 +2470,7 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper, width = dev->mode_config.max_width; height = dev->mode_config.max_height; - drm_setup_crtcs(fb_helper, width, height); + drm_client_modeset_probe(&fb_helper->client, width, height); ret = drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); if (ret < 0) { if (ret == -EAGAIN) { @@ -2591,7 +2617,7 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) DRM_DEBUG_KMS("\n"); - drm_setup_crtcs(fb_helper, fb_helper->fb->width, fb_helper->fb->height); + drm_client_modeset_probe(&fb_helper->client, fb_helper->fb->width, fb_helper->fb->height); drm_setup_crtcs_fb(fb_helper); mutex_unlock(&fb_helper->lock); diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index e14d52ef9638..c8a8ae2a678a 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -43,10 +43,6 @@ enum mode_set_atomic { ENTER_ATOMIC_MODE_SET, }; -struct drm_fb_offset { - int x, y; -}; - /** * struct drm_fb_helper_surface_size - describes fbdev size and scanout surface size * @fb_width: fbdev width -- cgit v1.2.3 From cf13909aee054f5aa667d4b9da0ac7df4f6c1327 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Sat, 8 Jun 2019 17:26:55 +0200 Subject: drm/fb-helper: Move out modeset config code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No functional changes, just moving code as-is and fixing includes. Signed-off-by: Noralf Trønnes Reviewed-by: Maxime Ripard Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20190608152657.36613-4-noralf@tronnes.org --- drivers/gpu/drm/drm_client_modeset.c | 707 ++++++++++++++++++++++++++++++++++- drivers/gpu/drm/drm_fb_helper.c | 692 ---------------------------------- include/drm/drm_client.h | 5 +- 3 files changed, 702 insertions(+), 702 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index b1984f901a6d..006bf7390e7d 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -13,13 +13,22 @@ #include #include +#include #include #include #include +#include +#include #include "drm_crtc_internal.h" #include "drm_internal.h" +#define DRM_CLIENT_MAX_CLONED_CONNECTORS 8 + +struct drm_client_offset { + int x, y; +}; + int drm_client_modeset_create(struct drm_client_dev *client) { struct drm_device *dev = client->dev; @@ -58,7 +67,7 @@ err_free: return -ENOMEM; } -void drm_client_modeset_release(struct drm_client_dev *client) +static void drm_client_modeset_release(struct drm_client_dev *client) { struct drm_mode_set *modeset; unsigned int i; @@ -75,8 +84,6 @@ void drm_client_modeset_release(struct drm_client_dev *client) modeset->num_connectors = 0; } } -/* TODO: Remove export when modeset code has been moved over */ -EXPORT_SYMBOL(drm_client_modeset_release); void drm_client_modeset_free(struct drm_client_dev *client) { @@ -95,7 +102,8 @@ void drm_client_modeset_free(struct drm_client_dev *client) kfree(client->modesets); } -struct drm_mode_set *drm_client_find_modeset(struct drm_client_dev *client, struct drm_crtc *crtc) +static struct drm_mode_set * +drm_client_find_modeset(struct drm_client_dev *client, struct drm_crtc *crtc) { struct drm_mode_set *modeset; @@ -105,8 +113,695 @@ struct drm_mode_set *drm_client_find_modeset(struct drm_client_dev *client, stru return NULL; } -/* TODO: Remove export when modeset code has been moved over */ -EXPORT_SYMBOL(drm_client_find_modeset); + +static struct drm_display_mode * +drm_connector_has_preferred_mode(struct drm_connector *connector, int width, int height) +{ + struct drm_display_mode *mode; + + list_for_each_entry(mode, &connector->modes, head) { + if (mode->hdisplay > width || + mode->vdisplay > height) + continue; + if (mode->type & DRM_MODE_TYPE_PREFERRED) + return mode; + } + return NULL; +} + +static struct drm_display_mode * +drm_connector_pick_cmdline_mode(struct drm_connector *connector) +{ + struct drm_cmdline_mode *cmdline_mode; + struct drm_display_mode *mode; + bool prefer_non_interlace; + + cmdline_mode = &connector->cmdline_mode; + if (cmdline_mode->specified == false) + return NULL; + + /* attempt to find a matching mode in the list of modes + * we have gotten so far, if not add a CVT mode that conforms + */ + if (cmdline_mode->rb || cmdline_mode->margins) + goto create_mode; + + prefer_non_interlace = !cmdline_mode->interlace; +again: + list_for_each_entry(mode, &connector->modes, head) { + /* check width/height */ + if (mode->hdisplay != cmdline_mode->xres || + mode->vdisplay != cmdline_mode->yres) + continue; + + if (cmdline_mode->refresh_specified) { + if (mode->vrefresh != cmdline_mode->refresh) + continue; + } + + if (cmdline_mode->interlace) { + if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) + continue; + } else if (prefer_non_interlace) { + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + continue; + } + return mode; + } + + if (prefer_non_interlace) { + prefer_non_interlace = false; + goto again; + } + +create_mode: + mode = drm_mode_create_from_cmdline_mode(connector->dev, cmdline_mode); + list_add(&mode->head, &connector->modes); + + return mode; +} + +static bool drm_connector_enabled(struct drm_connector *connector, bool strict) +{ + bool enable; + + if (connector->display_info.non_desktop) + return false; + + if (strict) + enable = connector->status == connector_status_connected; + else + enable = connector->status != connector_status_disconnected; + + return enable; +} + +static void drm_client_connectors_enabled(struct drm_connector **connectors, + unsigned int connector_count, + bool *enabled) +{ + bool any_enabled = false; + struct drm_connector *connector; + int i = 0; + + for (i = 0; i < connector_count; i++) { + connector = connectors[i]; + enabled[i] = drm_connector_enabled(connector, true); + DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, + connector->display_info.non_desktop ? "non desktop" : enabled[i] ? "yes" : "no"); + + any_enabled |= enabled[i]; + } + + if (any_enabled) + return; + + for (i = 0; i < connector_count; i++) + enabled[i] = drm_connector_enabled(connectors[i], false); +} + +static bool drm_client_target_cloned(struct drm_device *dev, + struct drm_connector **connectors, + unsigned int connector_count, + struct drm_display_mode **modes, + struct drm_client_offset *offsets, + bool *enabled, int width, int height) +{ + int count, i, j; + bool can_clone = false; + struct drm_display_mode *dmt_mode, *mode; + + /* only contemplate cloning in the single crtc case */ + if (dev->mode_config.num_crtc > 1) + return false; + + count = 0; + for (i = 0; i < connector_count; i++) { + if (enabled[i]) + count++; + } + + /* only contemplate cloning if more than one connector is enabled */ + if (count <= 1) + return false; + + /* check the command line or if nothing common pick 1024x768 */ + can_clone = true; + for (i = 0; i < connector_count; i++) { + if (!enabled[i]) + continue; + modes[i] = drm_connector_pick_cmdline_mode(connectors[i]); + if (!modes[i]) { + can_clone = false; + break; + } + for (j = 0; j < i; j++) { + if (!enabled[j]) + continue; + if (!drm_mode_match(modes[j], modes[i], + DRM_MODE_MATCH_TIMINGS | + DRM_MODE_MATCH_CLOCK | + DRM_MODE_MATCH_FLAGS | + DRM_MODE_MATCH_3D_FLAGS)) + can_clone = false; + } + } + + if (can_clone) { + DRM_DEBUG_KMS("can clone using command line\n"); + return true; + } + + /* try and find a 1024x768 mode on each connector */ + can_clone = true; + dmt_mode = drm_mode_find_dmt(dev, 1024, 768, 60, false); + + for (i = 0; i < connector_count; i++) { + if (!enabled[i]) + continue; + + list_for_each_entry(mode, &connectors[i]->modes, head) { + if (drm_mode_match(mode, dmt_mode, + DRM_MODE_MATCH_TIMINGS | + DRM_MODE_MATCH_CLOCK | + DRM_MODE_MATCH_FLAGS | + DRM_MODE_MATCH_3D_FLAGS)) + modes[i] = mode; + } + if (!modes[i]) + can_clone = false; + } + + if (can_clone) { + DRM_DEBUG_KMS("can clone using 1024x768\n"); + return true; + } + DRM_INFO("kms: can't enable cloning when we probably wanted to.\n"); + return false; +} + +static int drm_client_get_tile_offsets(struct drm_connector **connectors, + unsigned int connector_count, + struct drm_display_mode **modes, + struct drm_client_offset *offsets, + int idx, + int h_idx, int v_idx) +{ + struct drm_connector *connector; + int i; + int hoffset = 0, voffset = 0; + + for (i = 0; i < connector_count; i++) { + connector = connectors[i]; + if (!connector->has_tile) + continue; + + if (!modes[i] && (h_idx || v_idx)) { + DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i, + connector->base.id); + continue; + } + if (connector->tile_h_loc < h_idx) + hoffset += modes[i]->hdisplay; + + if (connector->tile_v_loc < v_idx) + voffset += modes[i]->vdisplay; + } + offsets[idx].x = hoffset; + offsets[idx].y = voffset; + DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx); + return 0; +} + +static bool drm_client_target_preferred(struct drm_connector **connectors, + unsigned int connector_count, + struct drm_display_mode **modes, + struct drm_client_offset *offsets, + bool *enabled, int width, int height) +{ + const u64 mask = BIT_ULL(connector_count) - 1; + struct drm_connector *connector; + u64 conn_configured = 0; + int tile_pass = 0; + int i; + +retry: + for (i = 0; i < connector_count; i++) { + connector = connectors[i]; + + if (conn_configured & BIT_ULL(i)) + continue; + + if (enabled[i] == false) { + conn_configured |= BIT_ULL(i); + continue; + } + + /* first pass over all the untiled connectors */ + if (tile_pass == 0 && connector->has_tile) + continue; + + if (tile_pass == 1) { + if (connector->tile_h_loc != 0 || + connector->tile_v_loc != 0) + continue; + + } else { + if (connector->tile_h_loc != tile_pass - 1 && + connector->tile_v_loc != tile_pass - 1) + /* if this tile_pass doesn't cover any of the tiles - keep going */ + continue; + + /* + * find the tile offsets for this pass - need to find + * all tiles left and above + */ + drm_client_get_tile_offsets(connectors, connector_count, modes, offsets, i, + connector->tile_h_loc, connector->tile_v_loc); + } + DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", + connector->base.id); + + /* got for command line mode first */ + modes[i] = drm_connector_pick_cmdline_mode(connector); + if (!modes[i]) { + DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n", + connector->base.id, connector->tile_group ? connector->tile_group->id : 0); + modes[i] = drm_connector_has_preferred_mode(connector, width, height); + } + /* No preferred modes, pick one off the list */ + if (!modes[i] && !list_empty(&connector->modes)) { + list_for_each_entry(modes[i], &connector->modes, head) + break; + } + DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : + "none"); + conn_configured |= BIT_ULL(i); + } + + if ((conn_configured & mask) != mask) { + tile_pass++; + goto retry; + } + return true; +} + +static bool connector_has_possible_crtc(struct drm_connector *connector, + struct drm_crtc *crtc) +{ + struct drm_encoder *encoder; + int i; + + drm_connector_for_each_possible_encoder(connector, encoder, i) { + if (encoder->possible_crtcs & drm_crtc_mask(crtc)) + return true; + } + + return false; +} + +static int drm_client_pick_crtcs(struct drm_client_dev *client, + struct drm_connector **connectors, + unsigned int connector_count, + struct drm_crtc **best_crtcs, + struct drm_display_mode **modes, + int n, int width, int height) +{ + struct drm_device *dev = client->dev; + struct drm_connector *connector; + int my_score, best_score, score; + struct drm_crtc **crtcs, *crtc; + struct drm_mode_set *modeset; + int o; + + if (n == connector_count) + return 0; + + connector = connectors[n]; + + best_crtcs[n] = NULL; + best_score = drm_client_pick_crtcs(client, connectors, connector_count, + best_crtcs, modes, n + 1, width, height); + if (modes[n] == NULL) + return best_score; + + crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); + if (!crtcs) + return best_score; + + my_score = 1; + if (connector->status == connector_status_connected) + my_score++; + if (connector->cmdline_mode.specified) + my_score++; + if (drm_connector_has_preferred_mode(connector, width, height)) + my_score++; + + /* + * select a crtc for this connector and then attempt to configure + * remaining connectors + */ + drm_client_for_each_modeset(modeset, client) { + crtc = modeset->crtc; + + if (!connector_has_possible_crtc(connector, crtc)) + continue; + + for (o = 0; o < n; o++) + if (best_crtcs[o] == crtc) + break; + + if (o < n) { + /* ignore cloning unless only a single crtc */ + if (dev->mode_config.num_crtc > 1) + continue; + + if (!drm_mode_equal(modes[o], modes[n])) + continue; + } + + crtcs[n] = crtc; + memcpy(crtcs, best_crtcs, n * sizeof(*crtcs)); + score = my_score + drm_client_pick_crtcs(client, connectors, connector_count, + crtcs, modes, n + 1, width, height); + if (score > best_score) { + best_score = score; + memcpy(best_crtcs, crtcs, connector_count * sizeof(*crtcs)); + } + } + + kfree(crtcs); + return best_score; +} + +/* Try to read the BIOS display configuration and use it for the initial config */ +static bool drm_client_firmware_config(struct drm_client_dev *client, + struct drm_connector **connectors, + unsigned int connector_count, + struct drm_crtc **crtcs, + struct drm_display_mode **modes, + struct drm_client_offset *offsets, + bool *enabled, int width, int height) +{ + unsigned int count = min_t(unsigned int, connector_count, BITS_PER_LONG); + unsigned long conn_configured, conn_seq, mask; + struct drm_device *dev = client->dev; + int i, j; + bool *save_enabled; + bool fallback = true, ret = true; + int num_connectors_enabled = 0; + int num_connectors_detected = 0; + struct drm_modeset_acquire_ctx ctx; + + if (!drm_drv_uses_atomic_modeset(dev)) + return false; + + save_enabled = kcalloc(count, sizeof(bool), GFP_KERNEL); + if (!save_enabled) + return false; + + drm_modeset_acquire_init(&ctx, 0); + + while (drm_modeset_lock_all_ctx(dev, &ctx) != 0) + drm_modeset_backoff(&ctx); + + memcpy(save_enabled, enabled, count); + mask = GENMASK(count - 1, 0); + conn_configured = 0; +retry: + conn_seq = conn_configured; + for (i = 0; i < count; i++) { + struct drm_connector *connector; + struct drm_encoder *encoder; + struct drm_crtc *new_crtc; + + connector = connectors[i]; + + if (conn_configured & BIT(i)) + continue; + + if (conn_seq == 0 && !connector->has_tile) + continue; + + if (connector->status == connector_status_connected) + num_connectors_detected++; + + if (!enabled[i]) { + DRM_DEBUG_KMS("connector %s not enabled, skipping\n", + connector->name); + conn_configured |= BIT(i); + continue; + } + + if (connector->force == DRM_FORCE_OFF) { + DRM_DEBUG_KMS("connector %s is disabled by user, skipping\n", + connector->name); + enabled[i] = false; + continue; + } + + encoder = connector->state->best_encoder; + if (!encoder || WARN_ON(!connector->state->crtc)) { + if (connector->force > DRM_FORCE_OFF) + goto bail; + + DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", + connector->name); + enabled[i] = false; + conn_configured |= BIT(i); + continue; + } + + num_connectors_enabled++; + + new_crtc = connector->state->crtc; + + /* + * Make sure we're not trying to drive multiple connectors + * with a single CRTC, since our cloning support may not + * match the BIOS. + */ + for (j = 0; j < count; j++) { + if (crtcs[j] == new_crtc) { + DRM_DEBUG_KMS("fallback: cloned configuration\n"); + goto bail; + } + } + + DRM_DEBUG_KMS("looking for cmdline mode on connector %s\n", + connector->name); + + /* go for command line mode first */ + modes[i] = drm_connector_pick_cmdline_mode(connector); + + /* try for preferred next */ + if (!modes[i]) { + DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n", + connector->name, connector->has_tile); + modes[i] = drm_connector_has_preferred_mode(connector, width, height); + } + + /* No preferred mode marked by the EDID? Are there any modes? */ + if (!modes[i] && !list_empty(&connector->modes)) { + DRM_DEBUG_KMS("using first mode listed on connector %s\n", + connector->name); + modes[i] = list_first_entry(&connector->modes, + struct drm_display_mode, + head); + } + + /* last resort: use current mode */ + if (!modes[i]) { + /* + * IMPORTANT: We want to use the adjusted mode (i.e. + * after the panel fitter upscaling) as the initial + * config, not the input mode, which is what crtc->mode + * usually contains. But since our current + * code puts a mode derived from the post-pfit timings + * into crtc->mode this works out correctly. + * + * This is crtc->mode and not crtc->state->mode for the + * fastboot check to work correctly. + */ + DRM_DEBUG_KMS("looking for current mode on connector %s\n", + connector->name); + modes[i] = &connector->state->crtc->mode; + } + crtcs[i] = new_crtc; + + DRM_DEBUG_KMS("connector %s on [CRTC:%d:%s]: %dx%d%s\n", + connector->name, + connector->state->crtc->base.id, + connector->state->crtc->name, + modes[i]->hdisplay, modes[i]->vdisplay, + modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" : ""); + + fallback = false; + conn_configured |= BIT(i); + } + + if ((conn_configured & mask) != mask && conn_configured != conn_seq) + goto retry; + + /* + * If the BIOS didn't enable everything it could, fall back to have the + * same user experiencing of lighting up as much as possible like the + * fbdev helper library. + */ + if (num_connectors_enabled != num_connectors_detected && + num_connectors_enabled < dev->mode_config.num_crtc) { + DRM_DEBUG_KMS("fallback: Not all outputs enabled\n"); + DRM_DEBUG_KMS("Enabled: %i, detected: %i\n", num_connectors_enabled, + num_connectors_detected); + fallback = true; + } + + if (fallback) { +bail: + DRM_DEBUG_KMS("Not using firmware configuration\n"); + memcpy(enabled, save_enabled, count); + ret = false; + } + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + kfree(save_enabled); + return ret; +} + +/** + * drm_client_modeset_probe() - Probe for displays + * @client: DRM client + * @width: Maximum display mode width (optional) + * @height: Maximum display mode height (optional) + * + * This function sets up display pipelines for enabled connectors and stores the + * config in the client's modeset array. + * + * Returns: + * Zero on success or negative error code on failure. + */ +int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, unsigned int height) +{ + struct drm_connector *connector, **connectors = NULL; + struct drm_connector_list_iter conn_iter; + struct drm_device *dev = client->dev; + unsigned int total_modes_count = 0; + struct drm_client_offset *offsets; + unsigned int connector_count = 0; + struct drm_display_mode **modes; + struct drm_crtc **crtcs; + int i, ret = 0; + bool *enabled; + + DRM_DEBUG_KMS("\n"); + + if (!width) + width = dev->mode_config.max_width; + if (!height) + height = dev->mode_config.max_height; + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_client_for_each_connector_iter(connector, &conn_iter) { + struct drm_connector **tmp; + + tmp = krealloc(connectors, (connector_count + 1) * sizeof(*connectors), GFP_KERNEL); + if (!tmp) { + ret = -ENOMEM; + goto free_connectors; + } + + connectors = tmp; + drm_connector_get(connector); + connectors[connector_count++] = connector; + } + drm_connector_list_iter_end(&conn_iter); + + if (!connector_count) + return 0; + + crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); + modes = kcalloc(connector_count, sizeof(*modes), GFP_KERNEL); + offsets = kcalloc(connector_count, sizeof(*offsets), GFP_KERNEL); + enabled = kcalloc(connector_count, sizeof(bool), GFP_KERNEL); + if (!crtcs || !modes || !enabled || !offsets) { + DRM_ERROR("Memory allocation failed\n"); + ret = -ENOMEM; + goto out; + } + + mutex_lock(&client->modeset_mutex); + + mutex_lock(&dev->mode_config.mutex); + for (i = 0; i < connector_count; i++) + total_modes_count += connectors[i]->funcs->fill_modes(connectors[i], width, height); + if (!total_modes_count) + DRM_DEBUG_KMS("No connectors reported connected with modes\n"); + drm_client_connectors_enabled(connectors, connector_count, enabled); + + if (!drm_client_firmware_config(client, connectors, connector_count, crtcs, + modes, offsets, enabled, width, height)) { + memset(modes, 0, connector_count * sizeof(*modes)); + memset(crtcs, 0, connector_count * sizeof(*crtcs)); + memset(offsets, 0, connector_count * sizeof(*offsets)); + + if (!drm_client_target_cloned(dev, connectors, connector_count, modes, + offsets, enabled, width, height) && + !drm_client_target_preferred(connectors, connector_count, modes, + offsets, enabled, width, height)) + DRM_ERROR("Unable to find initial modes\n"); + + DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", + width, height); + + drm_client_pick_crtcs(client, connectors, connector_count, + crtcs, modes, 0, width, height); + } + mutex_unlock(&dev->mode_config.mutex); + + drm_client_modeset_release(client); + + for (i = 0; i < connector_count; i++) { + struct drm_display_mode *mode = modes[i]; + struct drm_crtc *crtc = crtcs[i]; + struct drm_client_offset *offset = &offsets[i]; + + if (mode && crtc) { + struct drm_mode_set *modeset = drm_client_find_modeset(client, crtc); + struct drm_connector *connector = connectors[i]; + + DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", + mode->name, crtc->base.id, offset->x, offset->y); + + if (WARN_ON_ONCE(modeset->num_connectors == DRM_CLIENT_MAX_CLONED_CONNECTORS || + (dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1))) { + ret = -EINVAL; + break; + } + + modeset->mode = drm_mode_duplicate(dev, mode); + drm_connector_get(connector); + modeset->connectors[modeset->num_connectors++] = connector; + modeset->x = offset->x; + modeset->y = offset->y; + } + } + + mutex_unlock(&client->modeset_mutex); +out: + kfree(crtcs); + kfree(modes); + kfree(offsets); + kfree(enabled); +free_connectors: + for (i = 0; i < connector_count; i++) + drm_connector_put(connectors[i]); + kfree(connectors); + + return ret; +} +EXPORT_SYMBOL(drm_client_modeset_probe); /** * drm_client_panel_rotation() - Check panel orientation diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index e7f41e5d924c..946cae196475 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -48,10 +48,6 @@ #include "drm_internal.h" -struct drm_client_offset { - int x, y; -}; - static bool drm_fbdev_emulation = true; module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600); MODULE_PARM_DESC(fbdev_emulation, @@ -1703,694 +1699,6 @@ void drm_fb_helper_fill_info(struct fb_info *info, } EXPORT_SYMBOL(drm_fb_helper_fill_info); -static struct drm_display_mode * -drm_connector_has_preferred_mode(struct drm_connector *connector, int width, int height) -{ - struct drm_display_mode *mode; - - list_for_each_entry(mode, &connector->modes, head) { - if (mode->hdisplay > width || - mode->vdisplay > height) - continue; - if (mode->type & DRM_MODE_TYPE_PREFERRED) - return mode; - } - return NULL; -} - -static struct drm_display_mode * -drm_connector_pick_cmdline_mode(struct drm_connector *connector) -{ - struct drm_cmdline_mode *cmdline_mode; - struct drm_display_mode *mode; - bool prefer_non_interlace; - - cmdline_mode = &connector->cmdline_mode; - if (cmdline_mode->specified == false) - return NULL; - - /* attempt to find a matching mode in the list of modes - * we have gotten so far, if not add a CVT mode that conforms - */ - if (cmdline_mode->rb || cmdline_mode->margins) - goto create_mode; - - prefer_non_interlace = !cmdline_mode->interlace; -again: - list_for_each_entry(mode, &connector->modes, head) { - /* check width/height */ - if (mode->hdisplay != cmdline_mode->xres || - mode->vdisplay != cmdline_mode->yres) - continue; - - if (cmdline_mode->refresh_specified) { - if (mode->vrefresh != cmdline_mode->refresh) - continue; - } - - if (cmdline_mode->interlace) { - if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) - continue; - } else if (prefer_non_interlace) { - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - continue; - } - return mode; - } - - if (prefer_non_interlace) { - prefer_non_interlace = false; - goto again; - } - -create_mode: - mode = drm_mode_create_from_cmdline_mode(connector->dev, cmdline_mode); - list_add(&mode->head, &connector->modes); - - return mode; -} - -static bool drm_connector_enabled(struct drm_connector *connector, bool strict) -{ - bool enable; - - if (connector->display_info.non_desktop) - return false; - - if (strict) - enable = connector->status == connector_status_connected; - else - enable = connector->status != connector_status_disconnected; - - return enable; -} - -static void drm_client_connectors_enabled(struct drm_connector **connectors, - unsigned int connector_count, - bool *enabled) -{ - bool any_enabled = false; - struct drm_connector *connector; - int i = 0; - - for (i = 0; i < connector_count; i++) { - connector = connectors[i]; - enabled[i] = drm_connector_enabled(connector, true); - DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, - connector->display_info.non_desktop ? "non desktop" : enabled[i] ? "yes" : "no"); - - any_enabled |= enabled[i]; - } - - if (any_enabled) - return; - - for (i = 0; i < connector_count; i++) - enabled[i] = drm_connector_enabled(connectors[i], false); -} - -static bool drm_client_target_cloned(struct drm_device *dev, - struct drm_connector **connectors, - unsigned int connector_count, - struct drm_display_mode **modes, - struct drm_client_offset *offsets, - bool *enabled, int width, int height) -{ - int count, i, j; - bool can_clone = false; - struct drm_display_mode *dmt_mode, *mode; - - /* only contemplate cloning in the single crtc case */ - if (dev->mode_config.num_crtc > 1) - return false; - - count = 0; - for (i = 0; i < connector_count; i++) { - if (enabled[i]) - count++; - } - - /* only contemplate cloning if more than one connector is enabled */ - if (count <= 1) - return false; - - /* check the command line or if nothing common pick 1024x768 */ - can_clone = true; - for (i = 0; i < connector_count; i++) { - if (!enabled[i]) - continue; - modes[i] = drm_connector_pick_cmdline_mode(connectors[i]); - if (!modes[i]) { - can_clone = false; - break; - } - for (j = 0; j < i; j++) { - if (!enabled[j]) - continue; - if (!drm_mode_match(modes[j], modes[i], - DRM_MODE_MATCH_TIMINGS | - DRM_MODE_MATCH_CLOCK | - DRM_MODE_MATCH_FLAGS | - DRM_MODE_MATCH_3D_FLAGS)) - can_clone = false; - } - } - - if (can_clone) { - DRM_DEBUG_KMS("can clone using command line\n"); - return true; - } - - /* try and find a 1024x768 mode on each connector */ - can_clone = true; - dmt_mode = drm_mode_find_dmt(dev, 1024, 768, 60, false); - - for (i = 0; i < connector_count; i++) { - if (!enabled[i]) - continue; - - list_for_each_entry(mode, &connectors[i]->modes, head) { - if (drm_mode_match(mode, dmt_mode, - DRM_MODE_MATCH_TIMINGS | - DRM_MODE_MATCH_CLOCK | - DRM_MODE_MATCH_FLAGS | - DRM_MODE_MATCH_3D_FLAGS)) - modes[i] = mode; - } - if (!modes[i]) - can_clone = false; - } - - if (can_clone) { - DRM_DEBUG_KMS("can clone using 1024x768\n"); - return true; - } - DRM_INFO("kms: can't enable cloning when we probably wanted to.\n"); - return false; -} - -static int drm_client_get_tile_offsets(struct drm_connector **connectors, - unsigned int connector_count, - struct drm_display_mode **modes, - struct drm_client_offset *offsets, - int idx, - int h_idx, int v_idx) -{ - struct drm_connector *connector; - int i; - int hoffset = 0, voffset = 0; - - for (i = 0; i < connector_count; i++) { - connector = connectors[i]; - if (!connector->has_tile) - continue; - - if (!modes[i] && (h_idx || v_idx)) { - DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i, - connector->base.id); - continue; - } - if (connector->tile_h_loc < h_idx) - hoffset += modes[i]->hdisplay; - - if (connector->tile_v_loc < v_idx) - voffset += modes[i]->vdisplay; - } - offsets[idx].x = hoffset; - offsets[idx].y = voffset; - DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx); - return 0; -} - -static bool drm_client_target_preferred(struct drm_connector **connectors, - unsigned int connector_count, - struct drm_display_mode **modes, - struct drm_client_offset *offsets, - bool *enabled, int width, int height) -{ - const u64 mask = BIT_ULL(connector_count) - 1; - struct drm_connector *connector; - u64 conn_configured = 0; - int tile_pass = 0; - int i; - -retry: - for (i = 0; i < connector_count; i++) { - connector = connectors[i]; - - if (conn_configured & BIT_ULL(i)) - continue; - - if (enabled[i] == false) { - conn_configured |= BIT_ULL(i); - continue; - } - - /* first pass over all the untiled connectors */ - if (tile_pass == 0 && connector->has_tile) - continue; - - if (tile_pass == 1) { - if (connector->tile_h_loc != 0 || - connector->tile_v_loc != 0) - continue; - - } else { - if (connector->tile_h_loc != tile_pass - 1 && - connector->tile_v_loc != tile_pass - 1) - /* if this tile_pass doesn't cover any of the tiles - keep going */ - continue; - - /* - * find the tile offsets for this pass - need to find - * all tiles left and above - */ - drm_client_get_tile_offsets(connectors, connector_count, modes, offsets, i, - connector->tile_h_loc, connector->tile_v_loc); - } - DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", - connector->base.id); - - /* got for command line mode first */ - modes[i] = drm_connector_pick_cmdline_mode(connector); - if (!modes[i]) { - DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n", - connector->base.id, connector->tile_group ? connector->tile_group->id : 0); - modes[i] = drm_connector_has_preferred_mode(connector, width, height); - } - /* No preferred modes, pick one off the list */ - if (!modes[i] && !list_empty(&connector->modes)) { - list_for_each_entry(modes[i], &connector->modes, head) - break; - } - DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : - "none"); - conn_configured |= BIT_ULL(i); - } - - if ((conn_configured & mask) != mask) { - tile_pass++; - goto retry; - } - return true; -} - -static bool connector_has_possible_crtc(struct drm_connector *connector, - struct drm_crtc *crtc) -{ - struct drm_encoder *encoder; - int i; - - drm_connector_for_each_possible_encoder(connector, encoder, i) { - if (encoder->possible_crtcs & drm_crtc_mask(crtc)) - return true; - } - - return false; -} - -static int drm_client_pick_crtcs(struct drm_client_dev *client, - struct drm_connector **connectors, - unsigned int connector_count, - struct drm_crtc **best_crtcs, - struct drm_display_mode **modes, - int n, int width, int height) -{ - struct drm_device *dev = client->dev; - struct drm_connector *connector; - int my_score, best_score, score; - struct drm_crtc **crtcs, *crtc; - struct drm_mode_set *modeset; - int o; - - if (n == connector_count) - return 0; - - connector = connectors[n]; - - best_crtcs[n] = NULL; - best_score = drm_client_pick_crtcs(client, connectors, connector_count, - best_crtcs, modes, n + 1, width, height); - if (modes[n] == NULL) - return best_score; - - crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); - if (!crtcs) - return best_score; - - my_score = 1; - if (connector->status == connector_status_connected) - my_score++; - if (connector->cmdline_mode.specified) - my_score++; - if (drm_connector_has_preferred_mode(connector, width, height)) - my_score++; - - /* - * select a crtc for this connector and then attempt to configure - * remaining connectors - */ - drm_client_for_each_modeset(modeset, client) { - crtc = modeset->crtc; - - if (!connector_has_possible_crtc(connector, crtc)) - continue; - - for (o = 0; o < n; o++) - if (best_crtcs[o] == crtc) - break; - - if (o < n) { - /* ignore cloning unless only a single crtc */ - if (dev->mode_config.num_crtc > 1) - continue; - - if (!drm_mode_equal(modes[o], modes[n])) - continue; - } - - crtcs[n] = crtc; - memcpy(crtcs, best_crtcs, n * sizeof(*crtcs)); - score = my_score + drm_client_pick_crtcs(client, connectors, connector_count, - crtcs, modes, n + 1, width, height); - if (score > best_score) { - best_score = score; - memcpy(best_crtcs, crtcs, connector_count * sizeof(*crtcs)); - } - } - - kfree(crtcs); - return best_score; -} - -/* Try to read the BIOS display configuration and use it for the initial config */ -static bool drm_client_firmware_config(struct drm_client_dev *client, - struct drm_connector **connectors, - unsigned int connector_count, - struct drm_crtc **crtcs, - struct drm_display_mode **modes, - struct drm_client_offset *offsets, - bool *enabled, int width, int height) -{ - unsigned int count = min_t(unsigned int, connector_count, BITS_PER_LONG); - unsigned long conn_configured, conn_seq, mask; - struct drm_device *dev = client->dev; - int i, j; - bool *save_enabled; - bool fallback = true, ret = true; - int num_connectors_enabled = 0; - int num_connectors_detected = 0; - struct drm_modeset_acquire_ctx ctx; - - if (!drm_drv_uses_atomic_modeset(dev)) - return false; - - save_enabled = kcalloc(count, sizeof(bool), GFP_KERNEL); - if (!save_enabled) - return false; - - drm_modeset_acquire_init(&ctx, 0); - - while (drm_modeset_lock_all_ctx(dev, &ctx) != 0) - drm_modeset_backoff(&ctx); - - memcpy(save_enabled, enabled, count); - mask = GENMASK(count - 1, 0); - conn_configured = 0; -retry: - conn_seq = conn_configured; - for (i = 0; i < count; i++) { - struct drm_connector *connector; - struct drm_encoder *encoder; - struct drm_crtc *new_crtc; - - connector = connectors[i]; - - if (conn_configured & BIT(i)) - continue; - - if (conn_seq == 0 && !connector->has_tile) - continue; - - if (connector->status == connector_status_connected) - num_connectors_detected++; - - if (!enabled[i]) { - DRM_DEBUG_KMS("connector %s not enabled, skipping\n", - connector->name); - conn_configured |= BIT(i); - continue; - } - - if (connector->force == DRM_FORCE_OFF) { - DRM_DEBUG_KMS("connector %s is disabled by user, skipping\n", - connector->name); - enabled[i] = false; - continue; - } - - encoder = connector->state->best_encoder; - if (!encoder || WARN_ON(!connector->state->crtc)) { - if (connector->force > DRM_FORCE_OFF) - goto bail; - - DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", - connector->name); - enabled[i] = false; - conn_configured |= BIT(i); - continue; - } - - num_connectors_enabled++; - - new_crtc = connector->state->crtc; - - /* - * Make sure we're not trying to drive multiple connectors - * with a single CRTC, since our cloning support may not - * match the BIOS. - */ - for (j = 0; j < count; j++) { - if (crtcs[j] == new_crtc) { - DRM_DEBUG_KMS("fallback: cloned configuration\n"); - goto bail; - } - } - - DRM_DEBUG_KMS("looking for cmdline mode on connector %s\n", - connector->name); - - /* go for command line mode first */ - modes[i] = drm_connector_pick_cmdline_mode(connector); - - /* try for preferred next */ - if (!modes[i]) { - DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n", - connector->name, connector->has_tile); - modes[i] = drm_connector_has_preferred_mode(connector, width, height); - } - - /* No preferred mode marked by the EDID? Are there any modes? */ - if (!modes[i] && !list_empty(&connector->modes)) { - DRM_DEBUG_KMS("using first mode listed on connector %s\n", - connector->name); - modes[i] = list_first_entry(&connector->modes, - struct drm_display_mode, - head); - } - - /* last resort: use current mode */ - if (!modes[i]) { - /* - * IMPORTANT: We want to use the adjusted mode (i.e. - * after the panel fitter upscaling) as the initial - * config, not the input mode, which is what crtc->mode - * usually contains. But since our current - * code puts a mode derived from the post-pfit timings - * into crtc->mode this works out correctly. - * - * This is crtc->mode and not crtc->state->mode for the - * fastboot check to work correctly. - */ - DRM_DEBUG_KMS("looking for current mode on connector %s\n", - connector->name); - modes[i] = &connector->state->crtc->mode; - } - crtcs[i] = new_crtc; - - DRM_DEBUG_KMS("connector %s on [CRTC:%d:%s]: %dx%d%s\n", - connector->name, - connector->state->crtc->base.id, - connector->state->crtc->name, - modes[i]->hdisplay, modes[i]->vdisplay, - modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" : ""); - - fallback = false; - conn_configured |= BIT(i); - } - - if ((conn_configured & mask) != mask && conn_configured != conn_seq) - goto retry; - - /* - * If the BIOS didn't enable everything it could, fall back to have the - * same user experiencing of lighting up as much as possible like the - * fbdev helper library. - */ - if (num_connectors_enabled != num_connectors_detected && - num_connectors_enabled < dev->mode_config.num_crtc) { - DRM_DEBUG_KMS("fallback: Not all outputs enabled\n"); - DRM_DEBUG_KMS("Enabled: %i, detected: %i\n", num_connectors_enabled, - num_connectors_detected); - fallback = true; - } - - if (fallback) { -bail: - DRM_DEBUG_KMS("Not using firmware configuration\n"); - memcpy(enabled, save_enabled, count); - ret = false; - } - - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); - - kfree(save_enabled); - return ret; -} - -/** - * drm_client_modeset_probe() - Probe for displays - * @client: DRM client - * @width: Maximum display mode width (optional) - * @height: Maximum display mode height (optional) - * - * This function sets up display pipelines for enabled connectors and stores the - * config in the client's modeset array. - * - * Returns: - * Zero on success or negative error code on failure. - */ -int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, unsigned int height) -{ - struct drm_connector *connector, **connectors = NULL; - struct drm_connector_list_iter conn_iter; - struct drm_device *dev = client->dev; - unsigned int total_modes_count = 0; - struct drm_client_offset *offsets; - unsigned int connector_count = 0; - struct drm_display_mode **modes; - struct drm_crtc **crtcs; - int i, ret = 0; - bool *enabled; - - DRM_DEBUG_KMS("\n"); - - if (!width) - width = dev->mode_config.max_width; - if (!height) - height = dev->mode_config.max_height; - - drm_connector_list_iter_begin(dev, &conn_iter); - drm_client_for_each_connector_iter(connector, &conn_iter) { - struct drm_connector **tmp; - - tmp = krealloc(connectors, (connector_count + 1) * sizeof(*connectors), GFP_KERNEL); - if (!tmp) { - ret = -ENOMEM; - goto free_connectors; - } - - connectors = tmp; - drm_connector_get(connector); - connectors[connector_count++] = connector; - } - drm_connector_list_iter_end(&conn_iter); - - if (!connector_count) - return 0; - - crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL); - modes = kcalloc(connector_count, sizeof(*modes), GFP_KERNEL); - offsets = kcalloc(connector_count, sizeof(*offsets), GFP_KERNEL); - enabled = kcalloc(connector_count, sizeof(bool), GFP_KERNEL); - if (!crtcs || !modes || !enabled || !offsets) { - DRM_ERROR("Memory allocation failed\n"); - ret = -ENOMEM; - goto out; - } - - mutex_lock(&client->modeset_mutex); - - mutex_lock(&dev->mode_config.mutex); - for (i = 0; i < connector_count; i++) - total_modes_count += connectors[i]->funcs->fill_modes(connectors[i], width, height); - if (!total_modes_count) - DRM_DEBUG_KMS("No connectors reported connected with modes\n"); - drm_client_connectors_enabled(connectors, connector_count, enabled); - - if (!drm_client_firmware_config(client, connectors, connector_count, crtcs, - modes, offsets, enabled, width, height)) { - memset(modes, 0, connector_count * sizeof(*modes)); - memset(crtcs, 0, connector_count * sizeof(*crtcs)); - memset(offsets, 0, connector_count * sizeof(*offsets)); - - if (!drm_client_target_cloned(dev, connectors, connector_count, modes, - offsets, enabled, width, height) && - !drm_client_target_preferred(connectors, connector_count, modes, - offsets, enabled, width, height)) - DRM_ERROR("Unable to find initial modes\n"); - - DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", - width, height); - - drm_client_pick_crtcs(client, connectors, connector_count, - crtcs, modes, 0, width, height); - } - mutex_unlock(&dev->mode_config.mutex); - - drm_client_modeset_release(client); - - for (i = 0; i < connector_count; i++) { - struct drm_display_mode *mode = modes[i]; - struct drm_crtc *crtc = crtcs[i]; - struct drm_client_offset *offset = &offsets[i]; - - if (mode && crtc) { - struct drm_mode_set *modeset = drm_client_find_modeset(client, crtc); - struct drm_connector *connector = connectors[i]; - - DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", - mode->name, crtc->base.id, offset->x, offset->y); - - if (WARN_ON_ONCE(modeset->num_connectors == DRM_CLIENT_MAX_CLONED_CONNECTORS || - (dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1))) { - ret = -EINVAL; - break; - } - - modeset->mode = drm_mode_duplicate(dev, mode); - drm_connector_get(connector); - modeset->connectors[modeset->num_connectors++] = connector; - modeset->x = offset->x; - modeset->y = offset->y; - } - } - - mutex_unlock(&client->modeset_mutex); -out: - kfree(crtcs); - kfree(modes); - kfree(offsets); - kfree(enabled); -free_connectors: - for (i = 0; i < connector_count; i++) - drm_connector_put(connectors[i]); - kfree(connectors); - - return ret; -} - /* * This is a continuation of drm_setup_crtcs() that sets up anything related * to the framebuffer. During initialization, drm_setup_crtcs() is called before diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index 8d94880bbe25..f2d5ed745733 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -18,8 +18,6 @@ struct drm_gem_object; struct drm_minor; struct module; -#define DRM_CLIENT_MAX_CLONED_CONNECTORS 8 - /** * struct drm_client_funcs - DRM client callbacks */ @@ -154,8 +152,7 @@ void drm_client_framebuffer_delete(struct drm_client_buffer *buffer); int drm_client_modeset_create(struct drm_client_dev *client); void drm_client_modeset_free(struct drm_client_dev *client); -void drm_client_modeset_release(struct drm_client_dev *client); -struct drm_mode_set *drm_client_find_modeset(struct drm_client_dev *client, struct drm_crtc *crtc); +int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, unsigned int height); bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotation); int drm_client_modeset_commit_force(struct drm_client_dev *client); int drm_client_modeset_commit(struct drm_client_dev *client); -- cgit v1.2.3 From ecf79e7ca1565e4d36482aa4d2950bc397313425 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Jun 2019 13:28:59 +0200 Subject: drm/fb: document dirty helper better MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently little known fact that there's no need to hand-roll your own anymore. Cc'ing a bunch of driver people who might want to know this too. v2: s/none/known/ (Chris Wilson) Cc: Rob Clark Cc: Sebastian Reichel Cc: Tomi Valkeinen Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Sean Paul Cc: Gerd Hoffmann Cc: David Lechner Cc: Noralf Trønnes Cc: Hans de Goede Reviewed-by: Noralf Trønnes Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190611112859.16375-1-daniel.vetter@ffwll.ch --- include/drm/drm_framebuffer.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/drm') diff --git a/include/drm/drm_framebuffer.h b/include/drm/drm_framebuffer.h index c23016748e3f..c0e0256e3e98 100644 --- a/include/drm/drm_framebuffer.h +++ b/include/drm/drm_framebuffer.h @@ -87,6 +87,9 @@ struct drm_framebuffer_funcs { * for more information as all the semantics and arguments have a one to * one mapping on this function. * + * Atomic drivers should use drm_atomic_helper_dirtyfb() to implement + * this hook. + * * RETURNS: * * 0 on success or a negative error code on failure. -- cgit v1.2.3 From 99d02ed523dcaf5be0c6a778708df2779cfb3ed6 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Tue, 4 Jun 2019 13:42:06 -0700 Subject: drm: bridge: dw-hdmi: Add hook for resume On Rockchip rk3288-based Chromebooks when you do a suspend/resume cycle: 1. You lose the ability to detect an HDMI device being plugged in. 2. If you're using the i2c bus built in to dw_hdmi then it stops working. Let's add a hook to the core dw-hdmi driver so that we can call it in dw_hdmi-rockchip in the next commit. NOTE: the exact set of steps I've done here in resume come from looking at the normal dw_hdmi init sequence in upstream Linux plus the sequence that we did in downstream Chrome OS 3.14. Testing show that it seems to work, but if an extra step is needed or something here is not needed we could improve it. As part of this change we'll refactor the hardware init bits of dw-hdmi to happen all in one function and all at the same time. Since we need to init the interrupt mutes before we request the IRQ, this means moving the hardware init earlier in the function, but there should be no problems with that. Also as part of this we now unconditionally init the "i2c" parts of dw-hdmi, but again that ought to be fine. Signed-off-by: Douglas Anderson Reviewed-by: Laurent Pinchart Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190604204207.168085-1-dianders@chromium.org --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 48 +++++++++++++++++++------------ include/drm/bridge/dw_hdmi.h | 2 ++ 2 files changed, 31 insertions(+), 19 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 3a6320108575..f6c89007d3bb 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -233,6 +233,13 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg, static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi) { + hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, + HDMI_PHY_I2CM_INT_ADDR); + + hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL | + HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL, + HDMI_PHY_I2CM_CTLINT_ADDR); + /* Software reset */ hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ); @@ -1994,16 +2001,6 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) return 0; } -static void dw_hdmi_setup_i2c(struct dw_hdmi *hdmi) -{ - hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, - HDMI_PHY_I2CM_INT_ADDR); - - hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL | - HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL, - HDMI_PHY_I2CM_CTLINT_ADDR); -} - static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi) { u8 ih_mute; @@ -2504,6 +2501,21 @@ static const struct regmap_config hdmi_regmap_32bit_config = { .max_register = HDMI_I2CM_FS_SCL_LCNT_0_ADDR << 2, }; +static void dw_hdmi_init_hw(struct dw_hdmi *hdmi) +{ + initialize_hdmi_ih_mutes(hdmi); + + /* + * Reset HDMI DDC I2C master controller and mute I2CM interrupts. + * Even if we are using a separate i2c adapter doing this doesn't + * hurt. + */ + dw_hdmi_i2c_init(hdmi); + + if (hdmi->phy.ops->setup_hpd) + hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); +} + static struct dw_hdmi * __dw_hdmi_probe(struct platform_device *pdev, const struct dw_hdmi_plat_data *plat_data) @@ -2655,7 +2667,7 @@ __dw_hdmi_probe(struct platform_device *pdev, prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without", hdmi->phy.name); - initialize_hdmi_ih_mutes(hdmi); + dw_hdmi_init_hw(hdmi); irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -2712,10 +2724,6 @@ __dw_hdmi_probe(struct platform_device *pdev, hdmi->bridge.of_node = pdev->dev.of_node; #endif - dw_hdmi_setup_i2c(hdmi); - if (hdmi->phy.ops->setup_hpd) - hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); - memset(&pdevinfo, 0, sizeof(pdevinfo)); pdevinfo.parent = dev; pdevinfo.id = PLATFORM_DEVID_AUTO; @@ -2768,10 +2776,6 @@ __dw_hdmi_probe(struct platform_device *pdev, hdmi->cec = platform_device_register_full(&pdevinfo); } - /* Reset HDMI DDC I2C master controller and mute I2CM interrupts */ - if (hdmi->i2c) - dw_hdmi_i2c_init(hdmi); - return hdmi; err_iahb: @@ -2875,6 +2879,12 @@ void dw_hdmi_unbind(struct dw_hdmi *hdmi) } EXPORT_SYMBOL_GPL(dw_hdmi_unbind); +void dw_hdmi_resume(struct dw_hdmi *hdmi) +{ + dw_hdmi_init_hw(hdmi); +} +EXPORT_SYMBOL_GPL(dw_hdmi_resume); + MODULE_AUTHOR("Sascha Hauer "); MODULE_AUTHOR("Andy Yan "); MODULE_AUTHOR("Yakir Yang "); diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index 66e70770cce5..601243b56b69 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -154,6 +154,8 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, const struct dw_hdmi_plat_data *plat_data); +void dw_hdmi_resume(struct dw_hdmi *hdmi); + void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense); void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate); -- cgit v1.2.3 From a3e69b86cf04b7c0b624572c181678ea74e83400 Mon Sep 17 00:00:00 2001 From: Yannick Fertré Date: Mon, 27 May 2019 12:21:38 +0200 Subject: drm/bridge/synopsys: dsi: add power on/off optional phy ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add power on & off optional physical operation functions, helpful to program specific registers of the DSI physical part. Signed-off-by: Yannick Fertré Reviewed-by: Philippe Cornu Tested-by: Philippe Cornu Reviewed-by: Andrzej Hajda Signed-off-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/1558952499-15418-2-git-send-email-yannick.fertre@st.com --- drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 8 ++++++++ include/drm/bridge/dw_mipi_dsi.h | 2 ++ 2 files changed, 10 insertions(+) (limited to 'include/drm') diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index a79c87bd0147..281c58bab1a1 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -778,6 +778,10 @@ static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi) static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge) { struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); + const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops; + + if (phy_ops->power_off) + phy_ops->power_off(dsi->plat_data->priv_data); /* * Switch to command mode before panel-bridge post_disable & @@ -877,11 +881,15 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge, static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge) { struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); + const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops; /* Switch to video mode for panel-bridge enable & panel enable */ dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO); if (dsi->slave) dw_mipi_dsi_set_mode(dsi->slave, MIPI_DSI_MODE_VIDEO); + + if (phy_ops->power_on) + phy_ops->power_on(dsi->plat_data->priv_data); } static enum drm_mode_status diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h index 92cf9d1e1655..be8cdc2bf67d 100644 --- a/include/drm/bridge/dw_mipi_dsi.h +++ b/include/drm/bridge/dw_mipi_dsi.h @@ -22,6 +22,8 @@ struct platform_device; struct dw_mipi_dsi_phy_ops { int (*init)(void *priv_data); + void (*power_on)(void *priv_data); + void (*power_off)(void *priv_data); int (*get_lane_mbps)(void *priv_data, const struct drm_display_mode *mode, unsigned long mode_flags, u32 lanes, u32 format, -- cgit v1.2.3 From 09cc560951dbcc74c7f9419f7cf703b57472b7a2 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Wed, 12 Jun 2019 11:00:34 -0400 Subject: drm: Tweak drm_encoder_helper_funcs.enable kerneldoc I copied the kerneldoc for encoder_funcs.atomic_enable from encoder_funcs.enable in a recent patch [1]. Sam rightly pointed out in the review that "for symmetry with" text is awkward [2]. So here's a patch to fix up the source of the awkward language. [1] https://patchwork.freedesktop.org/patch/msgid/20190611160844.257498-2-sean@poorly.run [2] https://patchwork.freedesktop.org/patch/msgid/20190611185352.GA16305@ravnborg.org Suggested-by: Sam Ravnborg Reviewed-by: Sam Ravnborg Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190612150038.194843-1-sean@poorly.run --- include/drm/drm_modeset_helper_vtables.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index f9c94c2a1364..df80131bb10f 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -719,11 +719,11 @@ struct drm_encoder_helper_funcs { * hooks and call them from CRTC's callback by looping over all encoders * connected to it using for_each_encoder_on_crtc(). * - * This hook is used only by atomic helpers, for symmetry with @disable. - * Atomic drivers don't need to implement it if there's no need to - * enable anything at the encoder level. To ensure that runtime PM handling - * (using either DPMS or the new "ACTIVE" property) works - * @enable must be the inverse of @disable for atomic drivers. + * This hook is only used by atomic helpers, it is the opposite of + * @disable. Atomic drivers don't need to implement it if there's no + * need to enable anything at the encoder level. To ensure that + * runtime PM handling (using either DPMS or the new "ACTIVE" property) + * works @enable must be the inverse of @disable for atomic drivers. */ void (*enable)(struct drm_encoder *encoder); -- cgit v1.2.3 From 57c84d5c9348bda5e9129bc4e4e567546915ad8c Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 13 Jun 2019 09:30:40 +0200 Subject: drm: Remove lock interfaces from GEM VRAM helpers The lock functions and the locked-pin/unpin functions of GEM VRAM are not required any longer. Remove them. Signed-off-by: Thomas Zimmermann Acked-by: Gerd Hoffmann Link: https://patchwork.freedesktop.org/patch/msgid/20190613073041.29350-9-tzimmermann@suse.de --- drivers/gpu/drm/drm_gem_vram_helper.c | 109 ---------------------------------- include/drm/drm_gem_vram_helper.h | 5 -- 2 files changed, 114 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index f3e5803affb0..4cae52054e61 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -151,36 +151,6 @@ void drm_gem_vram_put(struct drm_gem_vram_object *gbo) } EXPORT_SYMBOL(drm_gem_vram_put); -/** - * drm_gem_vram_lock() - Locks a VRAM-backed GEM object - * @gbo: the GEM VRAM object - * @no_wait: don't wait for buffer object to become available - * - * See ttm_bo_reserve() for more information. - * - * Returns: - * 0 on success, or - * a negative error code otherwise - */ -int drm_gem_vram_lock(struct drm_gem_vram_object *gbo, bool no_wait) -{ - return ttm_bo_reserve(&gbo->bo, true, no_wait, NULL); -} -EXPORT_SYMBOL(drm_gem_vram_lock); - -/** - * drm_gem_vram_unlock() - \ - Release a reservation acquired by drm_gem_vram_lock() - * @gbo: the GEM VRAM object - * - * See ttm_bo_unreserve() for more information. - */ -void drm_gem_vram_unlock(struct drm_gem_vram_object *gbo) -{ - ttm_bo_unreserve(&gbo->bo); -} -EXPORT_SYMBOL(drm_gem_vram_unlock); - /** * drm_gem_vram_mmap_offset() - Returns a GEM VRAM object's mmap offset * @gbo: the GEM VRAM object @@ -266,49 +236,6 @@ err_ttm_bo_unreserve: } EXPORT_SYMBOL(drm_gem_vram_pin); -/** - * drm_gem_vram_pin_locked() - Pins a GEM VRAM object in a region. - * @gbo: the GEM VRAM object - * @pl_flag: a bitmask of possible memory regions - * - * Pinning a buffer object ensures that it is not evicted from - * a memory region. A pinned buffer object has to be unpinned before - * it can be pinned to another region. - * - * This function pins a GEM VRAM object that has already been - * locked. Use drm_gem_vram_pin() if possible. - * - * Returns: - * 0 on success, or - * a negative error code otherwise. - */ -int drm_gem_vram_pin_locked(struct drm_gem_vram_object *gbo, - unsigned long pl_flag) -{ - int i, ret; - struct ttm_operation_ctx ctx = { false, false }; - - lockdep_assert_held(&gbo->bo.resv->lock.base); - - if (gbo->pin_count) { - ++gbo->pin_count; - return 0; - } - - drm_gem_vram_placement(gbo, pl_flag); - for (i = 0; i < gbo->placement.num_placement; ++i) - gbo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; - - ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx); - if (ret < 0) - return ret; - - gbo->pin_count = 1; - - return 0; -} -EXPORT_SYMBOL(drm_gem_vram_pin_locked); - /** * drm_gem_vram_unpin() - Unpins a GEM VRAM object * @gbo: the GEM VRAM object @@ -351,42 +278,6 @@ err_ttm_bo_unreserve: } EXPORT_SYMBOL(drm_gem_vram_unpin); -/** - * drm_gem_vram_unpin_locked() - Unpins a GEM VRAM object - * @gbo: the GEM VRAM object - * - * This function unpins a GEM VRAM object that has already been - * locked. Use drm_gem_vram_unpin() if possible. - * - * Returns: - * 0 on success, or - * a negative error code otherwise. - */ -int drm_gem_vram_unpin_locked(struct drm_gem_vram_object *gbo) -{ - int i, ret; - struct ttm_operation_ctx ctx = { false, false }; - - lockdep_assert_held(&gbo->bo.resv->lock.base); - - if (WARN_ON_ONCE(!gbo->pin_count)) - return 0; - - --gbo->pin_count; - if (gbo->pin_count) - return 0; - - for (i = 0; i < gbo->placement.num_placement ; ++i) - gbo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; - - ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx); - if (ret < 0) - return ret; - - return 0; -} -EXPORT_SYMBOL(drm_gem_vram_unpin_locked); - /** * drm_gem_vram_kmap_at() - Maps a GEM VRAM object into kernel address space * @gbo: the GEM VRAM object diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index 4d1d2c1bf32b..1d4aa87f8dfa 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -77,15 +77,10 @@ struct drm_gem_vram_object *drm_gem_vram_create(struct drm_device *dev, unsigned long pg_align, bool interruptible); void drm_gem_vram_put(struct drm_gem_vram_object *gbo); -int drm_gem_vram_lock(struct drm_gem_vram_object *gbo, bool no_wait); -void drm_gem_vram_unlock(struct drm_gem_vram_object *gbo); u64 drm_gem_vram_mmap_offset(struct drm_gem_vram_object *gbo); s64 drm_gem_vram_offset(struct drm_gem_vram_object *gbo); int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag); -int drm_gem_vram_pin_locked(struct drm_gem_vram_object *gbo, - unsigned long pl_flag); int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo); -int drm_gem_vram_unpin_locked(struct drm_gem_vram_object *gbo); void *drm_gem_vram_kmap_at(struct drm_gem_vram_object *gbo, bool map, bool *is_iomem, struct ttm_bo_kmap_obj *kmap); void *drm_gem_vram_kmap(struct drm_gem_vram_object *gbo, bool map, -- cgit v1.2.3 From 921721738f4994b54f916f9a3bdd5dfdf26f753c Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 13 Jun 2019 09:30:41 +0200 Subject: drm: Remove functions with kmap-object argument from GEM VRAM helpers The GEM VRAM functions with kmap-object argument are not required any longer. Remove them. Signed-off-by: Thomas Zimmermann Acked-by: Gerd Hoffmann Link: https://patchwork.freedesktop.org/patch/msgid/20190613073041.29350-10-tzimmermann@suse.de --- drivers/gpu/drm/drm_gem_vram_helper.c | 50 ++++++----------------------------- include/drm/drm_gem_vram_helper.h | 4 --- 2 files changed, 8 insertions(+), 46 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index 4cae52054e61..4de782ca26b2 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -279,12 +279,11 @@ err_ttm_bo_unreserve: EXPORT_SYMBOL(drm_gem_vram_unpin); /** - * drm_gem_vram_kmap_at() - Maps a GEM VRAM object into kernel address space + * drm_gem_vram_kmap() - Maps a GEM VRAM object into kernel address space * @gbo: the GEM VRAM object * @map: establish a mapping if necessary * @is_iomem: returns true if the mapped memory is I/O memory, or false \ otherwise; can be NULL - * @kmap: the mapping's kmap object * * This function maps the buffer object into the kernel's address space * or returns the current mapping. If the parameter map is false, the @@ -296,10 +295,11 @@ EXPORT_SYMBOL(drm_gem_vram_unpin); * NULL if not mapped, or * an ERR_PTR()-encoded error code otherwise. */ -void *drm_gem_vram_kmap_at(struct drm_gem_vram_object *gbo, bool map, - bool *is_iomem, struct ttm_bo_kmap_obj *kmap) +void *drm_gem_vram_kmap(struct drm_gem_vram_object *gbo, bool map, + bool *is_iomem) { int ret; + struct ttm_bo_kmap_obj *kmap = &gbo->kmap; if (kmap->virtual || !map) goto out; @@ -317,56 +317,22 @@ out: } return ttm_kmap_obj_virtual(kmap, is_iomem); } -EXPORT_SYMBOL(drm_gem_vram_kmap_at); - -/** - * drm_gem_vram_kmap() - Maps a GEM VRAM object into kernel address space - * @gbo: the GEM VRAM object - * @map: establish a mapping if necessary - * @is_iomem: returns true if the mapped memory is I/O memory, or false \ - otherwise; can be NULL - * - * This function maps the buffer object into the kernel's address space - * or returns the current mapping. If the parameter map is false, the - * function only queries the current mapping, but does not establish a - * new one. - * - * Returns: - * The buffers virtual address if mapped, or - * NULL if not mapped, or - * an ERR_PTR()-encoded error code otherwise. - */ -void *drm_gem_vram_kmap(struct drm_gem_vram_object *gbo, bool map, - bool *is_iomem) -{ - return drm_gem_vram_kmap_at(gbo, map, is_iomem, &gbo->kmap); -} EXPORT_SYMBOL(drm_gem_vram_kmap); /** - * drm_gem_vram_kunmap_at() - Unmaps a GEM VRAM object + * drm_gem_vram_kunmap() - Unmaps a GEM VRAM object * @gbo: the GEM VRAM object - * @kmap: the mapping's kmap object */ -void drm_gem_vram_kunmap_at(struct drm_gem_vram_object *gbo, - struct ttm_bo_kmap_obj *kmap) +void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo) { + struct ttm_bo_kmap_obj *kmap = &gbo->kmap; + if (!kmap->virtual) return; ttm_bo_kunmap(kmap); kmap->virtual = NULL; } -EXPORT_SYMBOL(drm_gem_vram_kunmap_at); - -/** - * drm_gem_vram_kunmap() - Unmaps a GEM VRAM object - * @gbo: the GEM VRAM object - */ -void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo) -{ - drm_gem_vram_kunmap_at(gbo, &gbo->kmap); -} EXPORT_SYMBOL(drm_gem_vram_kunmap); /** diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index 1d4aa87f8dfa..9581ea0a4f7e 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -81,12 +81,8 @@ u64 drm_gem_vram_mmap_offset(struct drm_gem_vram_object *gbo); s64 drm_gem_vram_offset(struct drm_gem_vram_object *gbo); int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag); int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo); -void *drm_gem_vram_kmap_at(struct drm_gem_vram_object *gbo, bool map, - bool *is_iomem, struct ttm_bo_kmap_obj *kmap); void *drm_gem_vram_kmap(struct drm_gem_vram_object *gbo, bool map, bool *is_iomem); -void drm_gem_vram_kunmap_at(struct drm_gem_vram_object *gbo, - struct ttm_bo_kmap_obj *kmap); void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo); int drm_gem_vram_fill_create_dumb(struct drm_file *file, -- cgit v1.2.3 From 43c76d72ea853ccaeb22a497c13d3cd946869ec6 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Tue, 11 Jun 2019 16:49:53 -0400 Subject: drm: Add atomic variants of enable/disable to encoder helper funcs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds atomic_enable and atomic_disable callbacks to the encoder helpers. This will allow encoders to make informed decisions in their start-up/shutdown based on the committed state. Aside from the new hooks, this patch also introduces the new signature for .atomic_* functions going forward. Instead of passing object state (well, encoders don't have atomic state, but let's ignore that), we pass the entire atomic state so the driver can inspect more than what's happening locally. This is particularly important for the upcoming self refresh helpers. Changes in v3: - Added patch to the set Changes in v4: - Move atomic_disable above prepare (Daniel) - Add breadcrumb to .enable() docbook (Daniel) Changes in v5: - None Changes in v6: - Tweak kerneldoc some more (Sam) Link to v3: https://patchwork.freedesktop.org/patch/msgid/20190502194956.218441-2-sean@poorly.run Link to v4: https://patchwork.freedesktop.org/patch/msgid/20190508160920.144739-2-sean@poorly.run Link to v5: https://patchwork.freedesktop.org/patch/msgid/20190611160844.257498-2-sean@poorly.run Cc: Daniel Vetter Cc: Ville Syrjälä Cc: Sam Ravnborg Tested-by: Heiko Stuebner Reviewed-by: Daniel Vetter Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190611204959.180855-1-sean@poorly.run --- drivers/gpu/drm/drm_atomic_helper.c | 8 ++++-- include/drm/drm_modeset_helper_vtables.h | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 0fc63d682245..62e29b5ebb6e 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1002,7 +1002,9 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) /* Right function depends upon target state. */ if (funcs) { - if (new_conn_state->crtc && funcs->prepare) + if (funcs->atomic_disable) + funcs->atomic_disable(encoder, old_state); + else if (new_conn_state->crtc && funcs->prepare) funcs->prepare(encoder); else if (funcs->disable) funcs->disable(encoder); @@ -1311,7 +1313,9 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, drm_bridge_pre_enable(encoder->bridge); if (funcs) { - if (funcs->enable) + if (funcs->atomic_enable) + funcs->atomic_enable(encoder, old_state); + else if (funcs->enable) funcs->enable(encoder); else if (funcs->commit) funcs->commit(encoder); diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index df80131bb10f..13f0e3ca0a29 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -679,6 +679,52 @@ struct drm_encoder_helper_funcs { enum drm_connector_status (*detect)(struct drm_encoder *encoder, struct drm_connector *connector); + /** + * @atomic_disable: + * + * This callback should be used to disable the encoder. With the atomic + * drivers it is called before this encoder's CRTC has been shut off + * using their own &drm_crtc_helper_funcs.atomic_disable hook. If that + * sequence is too simple drivers can just add their own driver private + * encoder hooks and call them from CRTC's callback by looping over all + * encoders connected to it using for_each_encoder_on_crtc(). + * + * This callback is a variant of @disable that provides the atomic state + * to the driver. If @atomic_disable is implemented, @disable is not + * called by the helpers. + * + * This hook is only used by atomic helpers. Atomic drivers don't need + * to implement it if there's no need to disable anything at the encoder + * level. To ensure that runtime PM handling (using either DPMS or the + * new "ACTIVE" property) works @atomic_disable must be the inverse of + * @atomic_enable. + */ + void (*atomic_disable)(struct drm_encoder *encoder, + struct drm_atomic_state *state); + + /** + * @atomic_enable: + * + * This callback should be used to enable the encoder. It is called + * after this encoder's CRTC has been enabled using their own + * &drm_crtc_helper_funcs.atomic_enable hook. If that sequence is + * too simple drivers can just add their own driver private encoder + * hooks and call them from CRTC's callback by looping over all encoders + * connected to it using for_each_encoder_on_crtc(). + * + * This callback is a variant of @enable that provides the atomic state + * to the driver. If @atomic_enable is implemented, @enable is not + * called by the helpers. + * + * This hook is only used by atomic helpers, it is the opposite of + * @atomic_disable. Atomic drivers don't need to implement it if there's + * no need to enable anything at the encoder level. To ensure that + * runtime PM handling works @atomic_enable must be the inverse of + * @atomic_disable. + */ + void (*atomic_enable)(struct drm_encoder *encoder, + struct drm_atomic_state *state); + /** * @disable: * @@ -695,6 +741,9 @@ struct drm_encoder_helper_funcs { * handling (using either DPMS or the new "ACTIVE" property) works * @disable must be the inverse of @enable for atomic drivers. * + * For atomic drivers also consider @atomic_disable and save yourself + * from having to read the NOTE below! + * * NOTE: * * With legacy CRTC helpers there's a big semantic difference between -- cgit v1.2.3 From 1b27fbdde1df172dba604855c45078d741f8c858 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 11 Jun 2019 16:51:43 -0400 Subject: drm: Add drm_atomic_get_(old|new)_connector_for_encoder() helpers Add functions to the atomic core to retrieve the old and new connectors associated with an encoder in a drm_atomic_state. This is useful for encoders and bridges that need to access the connector, for instance for the drm_display_info. The CRTC associated with the encoder can also be retrieved through the connector state, and from it, the old and new CRTC states. Changed in v4: - Added to the set Changed in v5: - Fix up docbook (Daniel & Laurent) Changed in v6: - Updated commit subject (Sam) Link to v4: https://patchwork.freedesktop.org/patch/msgid/20190508160920.144739-3-sean@poorly.run Link to v5: https://patchwork.freedesktop.org/patch/msgid/20190611160844.257498-3-sean@poorly.run Cc: Daniel Vetter Cc: Sam Ravnborg Tested-by: Heiko Stuebner Reviewed-by: Daniel Vetter Signed-off-by: Laurent Pinchart [seanpaul removed WARNs from helpers and added docs to explain why returning NULL might be valid] Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190611205147.181298-1-sean@poorly.run --- drivers/gpu/drm/drm_atomic.c | 69 ++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_atomic.h | 7 +++++ include/drm/drm_connector.h | 5 ++++ 3 files changed, 81 insertions(+) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 169f06d7c5ce..a4e779deab0f 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -846,6 +846,75 @@ drm_atomic_get_new_private_obj_state(struct drm_atomic_state *state, } EXPORT_SYMBOL(drm_atomic_get_new_private_obj_state); +/** + * drm_atomic_get_old_connector_for_encoder - Get old connector for an encoder + * @state: Atomic state + * @encoder: The encoder to fetch the connector state for + * + * This function finds and returns the connector that was connected to @encoder + * as specified by the @state. + * + * If there is no connector in @state which previously had @encoder connected to + * it, this function will return NULL. While this may seem like an invalid use + * case, it is sometimes useful to differentiate commits which had no prior + * connectors attached to @encoder vs ones that did (and to inspect their + * state). This is especially true in enable hooks because the pipeline has + * changed. + * + * Returns: The old connector connected to @encoder, or NULL if the encoder is + * not connected. + */ +struct drm_connector * +drm_atomic_get_old_connector_for_encoder(struct drm_atomic_state *state, + struct drm_encoder *encoder) +{ + struct drm_connector_state *conn_state; + struct drm_connector *connector; + unsigned int i; + + for_each_old_connector_in_state(state, connector, conn_state, i) { + if (conn_state->best_encoder == encoder) + return connector; + } + + return NULL; +} +EXPORT_SYMBOL(drm_atomic_get_old_connector_for_encoder); + +/** + * drm_atomic_get_new_connector_for_encoder - Get new connector for an encoder + * @state: Atomic state + * @encoder: The encoder to fetch the connector state for + * + * This function finds and returns the connector that will be connected to + * @encoder as specified by the @state. + * + * If there is no connector in @state which will have @encoder connected to it, + * this function will return NULL. While this may seem like an invalid use case, + * it is sometimes useful to differentiate commits which have no connectors + * attached to @encoder vs ones that do (and to inspect their state). This is + * especially true in disable hooks because the pipeline will change. + * + * Returns: The new connector connected to @encoder, or NULL if the encoder is + * not connected. + */ +struct drm_connector * +drm_atomic_get_new_connector_for_encoder(struct drm_atomic_state *state, + struct drm_encoder *encoder) +{ + struct drm_connector_state *conn_state; + struct drm_connector *connector; + unsigned int i; + + for_each_new_connector_in_state(state, connector, conn_state, i) { + if (conn_state->best_encoder == encoder) + return connector; + } + + return NULL; +} +EXPORT_SYMBOL(drm_atomic_get_new_connector_for_encoder); + /** * drm_atomic_get_connector_state - get connector state * @state: global atomic state object diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index e937ff2beb04..f12215647801 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -459,6 +459,13 @@ struct drm_private_state * drm_atomic_get_new_private_obj_state(struct drm_atomic_state *state, struct drm_private_obj *obj); +struct drm_connector * +drm_atomic_get_old_connector_for_encoder(struct drm_atomic_state *state, + struct drm_encoder *encoder); +struct drm_connector * +drm_atomic_get_new_connector_for_encoder(struct drm_atomic_state *state, + struct drm_encoder *encoder); + /** * drm_atomic_get_existing_crtc_state - get crtc state, if it exists * @state: global atomic state object diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 47e749b74e5f..071143bc0ebd 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -518,6 +518,11 @@ struct drm_connector_state { * &drm_connector_helper_funcs.atomic_best_encoder or * &drm_connector_helper_funcs.best_encoder callbacks. * + * This is also used in the atomic helpers to map encoders to their + * current and previous connectors, see + * &drm_atomic_get_old_connector_for_encoder() and + * &drm_atomic_get_new_connector_for_encoder(). + * * NOTE: Atomic drivers must fill this out (either themselves or through * helpers), for otherwise the GETCONNECTOR and GETENCODER IOCTLs will * not return correct data to userspace. -- cgit v1.2.3 From 5ade071ba13e3bb24e3a9d5f8d6a3cf145deeb18 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Tue, 11 Jun 2019 12:08:17 -0400 Subject: drm: Add atomic variants for bridge enable/disable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds atomic variants for all of pre_enable/enable/disable/post_disable bridge functions. These will be called from the appropriate atomic helper functions. If the bridge driver doesn't implement the atomic version of the function, we will fall back to the vanilla implementation. Note that some drivers call drm_bridge_disable directly, and these cases are not covered. It's up to the driver to decide whether to implement both atomic_disable and disable, or if it's not necessary. Changes in v3: - Added to the patchset Changes in v4: - Fix up docbook references (Daniel) Changes in v5: - None Link to v3: https://patchwork.freedesktop.org/patch/msgid/20190502194956.218441-4-sean@poorly.run Link to v4: https://patchwork.freedesktop.org/patch/msgid/20190508160920.144739-4-sean@poorly.run Cc: Daniel Vetter Cc: Ville Syrjälä Tested-by: Heiko Stuebner Reviewed-by: Daniel Vetter Reviewed-by: Andrzej Hajda Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190611160844.257498-4-sean@poorly.run --- drivers/gpu/drm/drm_atomic_helper.c | 8 +-- drivers/gpu/drm/drm_bridge.c | 110 ++++++++++++++++++++++++++++++++++++ include/drm/drm_bridge.h | 106 ++++++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 4 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 62e29b5ebb6e..2133f6253917 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -998,7 +998,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) * Each encoder has at most one connector (since we always steal * it away), so we won't call disable hooks twice. */ - drm_bridge_disable(encoder->bridge); + drm_atomic_bridge_disable(encoder->bridge, old_state); /* Right function depends upon target state. */ if (funcs) { @@ -1012,7 +1012,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) funcs->dpms(encoder, DRM_MODE_DPMS_OFF); } - drm_bridge_post_disable(encoder->bridge); + drm_atomic_bridge_post_disable(encoder->bridge, old_state); } for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { @@ -1310,7 +1310,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, * Each encoder has at most one connector (since we always steal * it away), so we won't call enable hooks twice. */ - drm_bridge_pre_enable(encoder->bridge); + drm_atomic_bridge_pre_enable(encoder->bridge, old_state); if (funcs) { if (funcs->atomic_enable) @@ -1321,7 +1321,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, funcs->commit(encoder); } - drm_bridge_enable(encoder->bridge); + drm_atomic_bridge_enable(encoder->bridge, old_state); } drm_atomic_helper_commit_writebacks(dev, old_state); diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 138b2711d389..cba537c99e43 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -352,6 +352,116 @@ void drm_bridge_enable(struct drm_bridge *bridge) } EXPORT_SYMBOL(drm_bridge_enable); +/** + * drm_atomic_bridge_disable - disables all bridges in the encoder chain + * @bridge: bridge control structure + * @state: atomic state being committed + * + * Calls &drm_bridge_funcs.atomic_disable (falls back on + * &drm_bridge_funcs.disable) op for all the bridges in the encoder chain, + * starting from the last bridge to the first. These are called before calling + * &drm_encoder_helper_funcs.atomic_disable + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_atomic_bridge_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) +{ + if (!bridge) + return; + + drm_atomic_bridge_disable(bridge->next, state); + + if (bridge->funcs->atomic_disable) + bridge->funcs->atomic_disable(bridge, state); + else if (bridge->funcs->disable) + bridge->funcs->disable(bridge); +} +EXPORT_SYMBOL(drm_atomic_bridge_disable); + +/** + * drm_atomic_bridge_post_disable - cleans up after disabling all bridges in the + * encoder chain + * @bridge: bridge control structure + * @state: atomic state being committed + * + * Calls &drm_bridge_funcs.atomic_post_disable (falls back on + * &drm_bridge_funcs.post_disable) op for all the bridges in the encoder chain, + * starting from the first bridge to the last. These are called after completing + * &drm_encoder_helper_funcs.atomic_disable + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_atomic_bridge_post_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) +{ + if (!bridge) + return; + + if (bridge->funcs->atomic_post_disable) + bridge->funcs->atomic_post_disable(bridge, state); + else if (bridge->funcs->post_disable) + bridge->funcs->post_disable(bridge); + + drm_atomic_bridge_post_disable(bridge->next, state); +} +EXPORT_SYMBOL(drm_atomic_bridge_post_disable); + +/** + * drm_atomic_bridge_pre_enable - prepares for enabling all bridges in the + * encoder chain + * @bridge: bridge control structure + * @state: atomic state being committed + * + * Calls &drm_bridge_funcs.atomic_pre_enable (falls back on + * &drm_bridge_funcs.pre_enable) op for all the bridges in the encoder chain, + * starting from the last bridge to the first. These are called before calling + * &drm_encoder_helper_funcs.atomic_enable + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_atomic_bridge_pre_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) +{ + if (!bridge) + return; + + drm_atomic_bridge_pre_enable(bridge->next, state); + + if (bridge->funcs->atomic_pre_enable) + bridge->funcs->atomic_pre_enable(bridge, state); + else if (bridge->funcs->pre_enable) + bridge->funcs->pre_enable(bridge); +} +EXPORT_SYMBOL(drm_atomic_bridge_pre_enable); + +/** + * drm_atomic_bridge_enable - enables all bridges in the encoder chain + * @bridge: bridge control structure + * @state: atomic state being committed + * + * Calls &drm_bridge_funcs.atomic_enable (falls back on + * &drm_bridge_funcs.enable) op for all the bridges in the encoder chain, + * starting from the first bridge to the last. These are called after completing + * &drm_encoder_helper_funcs.atomic_enable + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_atomic_bridge_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) +{ + if (!bridge) + return; + + if (bridge->funcs->atomic_enable) + bridge->funcs->atomic_enable(bridge, state); + else if (bridge->funcs->enable) + bridge->funcs->enable(bridge); + + drm_atomic_bridge_enable(bridge->next, state); +} +EXPORT_SYMBOL(drm_atomic_bridge_enable); + #ifdef CONFIG_OF /** * of_drm_find_bridge - find the bridge corresponding to the device node in diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index d4428913a4e1..322801884814 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -237,6 +237,103 @@ struct drm_bridge_funcs { * The enable callback is optional. */ void (*enable)(struct drm_bridge *bridge); + + /** + * @atomic_pre_enable: + * + * This callback should enable the bridge. It is called right before + * the preceding element in the display pipe is enabled. If the + * preceding element is a bridge this means it's called before that + * bridge's @atomic_pre_enable or @pre_enable function. If the preceding + * element is a &drm_encoder it's called right before the encoder's + * &drm_encoder_helper_funcs.atomic_enable hook. + * + * The display pipe (i.e. clocks and timing signals) feeding this bridge + * will not yet be running when this callback is called. The bridge must + * not enable the display link feeding the next bridge in the chain (if + * there is one) when this callback is called. + * + * Note that this function will only be invoked in the context of an + * atomic commit. It will not be invoked from &drm_bridge_pre_enable. It + * would be prudent to also provide an implementation of @pre_enable if + * you are expecting driver calls into &drm_bridge_pre_enable. + * + * The @atomic_pre_enable callback is optional. + */ + void (*atomic_pre_enable)(struct drm_bridge *bridge, + struct drm_atomic_state *state); + + /** + * @atomic_enable: + * + * This callback should enable the bridge. It is called right after + * the preceding element in the display pipe is enabled. If the + * preceding element is a bridge this means it's called after that + * bridge's @atomic_enable or @enable function. If the preceding element + * is a &drm_encoder it's called right after the encoder's + * &drm_encoder_helper_funcs.atomic_enable hook. + * + * The bridge can assume that the display pipe (i.e. clocks and timing + * signals) feeding it is running when this callback is called. This + * callback must enable the display link feeding the next bridge in the + * chain if there is one. + * + * Note that this function will only be invoked in the context of an + * atomic commit. It will not be invoked from &drm_bridge_enable. It + * would be prudent to also provide an implementation of @enable if + * you are expecting driver calls into &drm_bridge_enable. + * + * The enable callback is optional. + */ + void (*atomic_enable)(struct drm_bridge *bridge, + struct drm_atomic_state *state); + /** + * @atomic_disable: + * + * This callback should disable the bridge. It is called right before + * the preceding element in the display pipe is disabled. If the + * preceding element is a bridge this means it's called before that + * bridge's @atomic_disable or @disable vfunc. If the preceding element + * is a &drm_encoder it's called right before the + * &drm_encoder_helper_funcs.atomic_disable hook. + * + * The bridge can assume that the display pipe (i.e. clocks and timing + * signals) feeding it is still running when this callback is called. + * + * Note that this function will only be invoked in the context of an + * atomic commit. It will not be invoked from &drm_bridge_disable. It + * would be prudent to also provide an implementation of @disable if + * you are expecting driver calls into &drm_bridge_disable. + * + * The disable callback is optional. + */ + void (*atomic_disable)(struct drm_bridge *bridge, + struct drm_atomic_state *state); + + /** + * @atomic_post_disable: + * + * This callback should disable the bridge. It is called right after the + * preceding element in the display pipe is disabled. If the preceding + * element is a bridge this means it's called after that bridge's + * @atomic_post_disable or @post_disable function. If the preceding + * element is a &drm_encoder it's called right after the encoder's + * &drm_encoder_helper_funcs.atomic_disable hook. + * + * The bridge must assume that the display pipe (i.e. clocks and timing + * signals) feeding it is no longer running when this callback is + * called. + * + * Note that this function will only be invoked in the context of an + * atomic commit. It will not be invoked from &drm_bridge_post_disable. + * It would be prudent to also provide an implementation of + * @post_disable if you are expecting driver calls into + * &drm_bridge_post_disable. + * + * The post_disable callback is optional. + */ + void (*atomic_post_disable)(struct drm_bridge *bridge, + struct drm_atomic_state *state); }; /** @@ -314,6 +411,15 @@ void drm_bridge_mode_set(struct drm_bridge *bridge, void drm_bridge_pre_enable(struct drm_bridge *bridge); void drm_bridge_enable(struct drm_bridge *bridge); +void drm_atomic_bridge_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state); +void drm_atomic_bridge_post_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state); +void drm_atomic_bridge_pre_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state); +void drm_atomic_bridge_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state); + #ifdef CONFIG_DRM_PANEL_BRIDGE struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel, u32 connector_type); -- cgit v1.2.3 From 6f3b62781bbd2670756a4847113d410a827a2593 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Tue, 11 Jun 2019 12:08:18 -0400 Subject: drm: Convert connector_helper_funcs->atomic_check to accept drm_atomic_state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Everyone who implements connector_helper_funcs->atomic_check reaches into the connector state to get the atomic state. Instead of continuing this pattern, change the callback signature to just give atomic state and let the driver determine what it does and does not need from it. Eventually all atomic functions should do this, but that's just too much busy work for me. Changes in v3: - Added to the set Changes in v4: - None Changes in v5: - intel_digital_connector_atomic_check declaration moved to i915_atomic.h Link to v3: https://patchwork.freedesktop.org/patch/msgid/20190502194956.218441-5-sean@poorly.run Link to v4: https://patchwork.freedesktop.org/patch/msgid/20190508160920.144739-5-sean@poorly.run Cc: Daniel Vetter Cc: Ville Syrjälä Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Ben Skeggs Cc: Laurent Pinchart Cc: Kieran Bingham Cc: Eric Anholt Tested-by: Heiko Stuebner Reviewed-by: Laurent Pinchart [for rcar lvds] Acked-by: Daniel Vetter Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190611160844.257498-5-sean@poorly.run --- drivers/gpu/drm/drm_atomic_helper.c | 4 ++-- drivers/gpu/drm/i915/intel_atomic.c | 8 +++++--- drivers/gpu/drm/i915/intel_atomic.h | 2 +- drivers/gpu/drm/i915/intel_dp_mst.c | 7 ++++--- drivers/gpu/drm/i915/intel_sdvo.c | 9 +++++---- drivers/gpu/drm/i915/intel_tv.c | 8 +++++--- drivers/gpu/drm/nouveau/dispnv50/disp.c | 5 +++-- drivers/gpu/drm/rcar-du/rcar_lvds.c | 12 +++++++----- drivers/gpu/drm/vc4/vc4_txp.c | 7 ++++--- include/drm/drm_modeset_helper_vtables.h | 2 +- 10 files changed, 37 insertions(+), 27 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 2133f6253917..e58be6996069 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -686,7 +686,7 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, } if (funcs->atomic_check) - ret = funcs->atomic_check(connector, new_connector_state); + ret = funcs->atomic_check(connector, state); if (ret) return ret; @@ -728,7 +728,7 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, continue; if (funcs->atomic_check) - ret = funcs->atomic_check(connector, new_connector_state); + ret = funcs->atomic_check(connector, state); if (ret) return ret; } diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 58b8049649a0..ab40448a19d5 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -106,12 +106,14 @@ int intel_digital_connector_atomic_set_property(struct drm_connector *connector, } int intel_digital_connector_atomic_check(struct drm_connector *conn, - struct drm_connector_state *new_state) + struct drm_atomic_state *state) { + struct drm_connector_state *new_state = + drm_atomic_get_new_connector_state(state, conn); struct intel_digital_connector_state *new_conn_state = to_intel_digital_connector_state(new_state); struct drm_connector_state *old_state = - drm_atomic_get_old_connector_state(new_state->state, conn); + drm_atomic_get_old_connector_state(state, conn); struct intel_digital_connector_state *old_conn_state = to_intel_digital_connector_state(old_state); struct drm_crtc_state *crtc_state; @@ -121,7 +123,7 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn, if (!new_state->crtc) return 0; - crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc); /* * These properties are handled by fastset, and might not end diff --git a/drivers/gpu/drm/i915/intel_atomic.h b/drivers/gpu/drm/i915/intel_atomic.h index 1c8507da1a69..58065d3161a3 100644 --- a/drivers/gpu/drm/i915/intel_atomic.h +++ b/drivers/gpu/drm/i915/intel_atomic.h @@ -28,7 +28,7 @@ int intel_digital_connector_atomic_set_property(struct drm_connector *connector, struct drm_property *property, u64 val); int intel_digital_connector_atomic_check(struct drm_connector *conn, - struct drm_connector_state *new_state); + struct drm_atomic_state *state); struct drm_connector_state * intel_digital_connector_duplicate_state(struct drm_connector *connector); diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c index 0caf645fbbb8..60652ebbdf61 100644 --- a/drivers/gpu/drm/i915/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/intel_dp_mst.c @@ -151,9 +151,10 @@ static int intel_dp_mst_compute_config(struct intel_encoder *encoder, static int intel_dp_mst_atomic_check(struct drm_connector *connector, - struct drm_connector_state *new_conn_state) + struct drm_atomic_state *state) { - struct drm_atomic_state *state = new_conn_state->state; + struct drm_connector_state *new_conn_state = + drm_atomic_get_new_connector_state(state, connector); struct drm_connector_state *old_conn_state = drm_atomic_get_old_connector_state(state, connector); struct intel_connector *intel_connector = @@ -163,7 +164,7 @@ intel_dp_mst_atomic_check(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr; int ret; - ret = intel_digital_connector_atomic_check(connector, new_conn_state); + ret = intel_digital_connector_atomic_check(connector, state); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 6c98b35790db..368b99f6f7fb 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2353,9 +2353,10 @@ static const struct drm_connector_funcs intel_sdvo_connector_funcs = { }; static int intel_sdvo_atomic_check(struct drm_connector *conn, - struct drm_connector_state *new_conn_state) + struct drm_atomic_state *state) { - struct drm_atomic_state *state = new_conn_state->state; + struct drm_connector_state *new_conn_state = + drm_atomic_get_new_connector_state(state, conn); struct drm_connector_state *old_conn_state = drm_atomic_get_old_connector_state(state, conn); struct intel_sdvo_connector_state *old_state = @@ -2367,13 +2368,13 @@ static int intel_sdvo_atomic_check(struct drm_connector *conn, (memcmp(&old_state->tv, &new_state->tv, sizeof(old_state->tv)) || memcmp(&old_conn_state->tv, &new_conn_state->tv, sizeof(old_conn_state->tv)))) { struct drm_crtc_state *crtc_state = - drm_atomic_get_new_crtc_state(new_conn_state->state, + drm_atomic_get_new_crtc_state(state, new_conn_state->crtc); crtc_state->connectors_changed = true; } - return intel_digital_connector_atomic_check(conn, new_conn_state); + return intel_digital_connector_atomic_check(conn, state); } static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs = { diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 5dc594eafaf2..0a95df6c6a57 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1821,16 +1821,18 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = { }; static int intel_tv_atomic_check(struct drm_connector *connector, - struct drm_connector_state *new_state) + struct drm_atomic_state *state) { + struct drm_connector_state *new_state; struct drm_crtc_state *new_crtc_state; struct drm_connector_state *old_state; + new_state = drm_atomic_get_new_connector_state(state, connector); if (!new_state->crtc) return 0; - old_state = drm_atomic_get_old_connector_state(new_state->state, connector); - new_crtc_state = drm_atomic_get_new_crtc_state(new_state->state, new_state->crtc); + old_state = drm_atomic_get_old_connector_state(state, connector); + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc); if (old_state->tv.mode != new_state->tv.mode || old_state->tv.margins.left != new_state->tv.margins.left || diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 4b1650f51955..7ba373f493b2 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -948,11 +948,12 @@ nv50_mstc_get_modes(struct drm_connector *connector) static int nv50_mstc_atomic_check(struct drm_connector *connector, - struct drm_connector_state *new_conn_state) + struct drm_atomic_state *state) { - struct drm_atomic_state *state = new_conn_state->state; struct nv50_mstc *mstc = nv50_mstc(connector); struct drm_dp_mst_topology_mgr *mgr = &mstc->mstm->mgr; + struct drm_connector_state *new_conn_state = + drm_atomic_get_new_connector_state(state, connector); struct drm_connector_state *old_conn_state = drm_atomic_get_old_connector_state(state, connector); struct drm_crtc_state *crtc_state; diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index 620b51aab291..5b81ba2a7f27 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -92,13 +92,15 @@ static int rcar_lvds_connector_get_modes(struct drm_connector *connector) } static int rcar_lvds_connector_atomic_check(struct drm_connector *connector, - struct drm_connector_state *state) + struct drm_atomic_state *state) { struct rcar_lvds *lvds = connector_to_rcar_lvds(connector); const struct drm_display_mode *panel_mode; + struct drm_connector_state *conn_state; struct drm_crtc_state *crtc_state; - if (!state->crtc) + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (!conn_state->crtc) return 0; if (list_empty(&connector->modes)) { @@ -110,9 +112,9 @@ static int rcar_lvds_connector_atomic_check(struct drm_connector *connector, struct drm_display_mode, head); /* We're not allowed to modify the resolution. */ - crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); + crtc_state = drm_atomic_get_crtc_state(state, conn_state->crtc); + if (!crtc_state) + return -EINVAL; if (crtc_state->mode.hdisplay != panel_mode->hdisplay || crtc_state->mode.vdisplay != panel_mode->vdisplay) diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index c8b89a78f9f4..96f91c1b4b6e 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -221,17 +221,18 @@ static const u32 txp_fmts[] = { }; static int vc4_txp_connector_atomic_check(struct drm_connector *conn, - struct drm_connector_state *conn_state) + struct drm_atomic_state *state) { + struct drm_connector_state *conn_state; struct drm_crtc_state *crtc_state; struct drm_framebuffer *fb; int i; + conn_state = drm_atomic_get_new_connector_state(state, conn); if (!conn_state->writeback_job || !conn_state->writeback_job->fb) return 0; - crtc_state = drm_atomic_get_new_crtc_state(conn_state->state, - conn_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); fb = conn_state->writeback_job->fb; if (fb->width != crtc_state->mode.hdisplay || diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index 13f0e3ca0a29..f36be43bd052 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -1028,7 +1028,7 @@ struct drm_connector_helper_funcs { * deadlock. */ int (*atomic_check)(struct drm_connector *connector, - struct drm_connector_state *state); + struct drm_atomic_state *state); /** * @atomic_commit: -- cgit v1.2.3 From 1452c25b0e60278820f3d2155c65f1bfcce5ee79 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Wed, 12 Jun 2019 10:50:19 -0400 Subject: drm: Add helpers to kick off self refresh mode in drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a new drm helper library to help drivers implement self refresh. Drivers choosing to use it will register crtcs and will receive callbacks when it's time to enter or exit self refresh mode. In its current form, it has a timer which will trigger after a driver-specified amount of inactivity. When the timer triggers, the helpers will submit a new atomic commit to shut the refreshing pipe off. On the next atomic commit, the drm core will revert the self refresh state and bring everything back up to be actively driven. From the driver's perspective, this works like a regular disable/enable cycle. The driver need only check the 'self_refresh_active' state in crtc_state. It should initiate self refresh mode on the panel and enter an off or low-power state. Changes in v2: - s/psr/self_refresh/ (Daniel) - integrated the psr exit into the commit that wakes it up (Jose/Daniel) - made the psr state per-crtc (Jose/Daniel) Changes in v3: - Remove the self_refresh_(active|changed) from connector state (Daniel) - Simplify loop in drm_self_refresh_helper_alter_state (Daniel) - Improve self_refresh_aware comment (Daniel) - s/self_refresh_state/self_refresh_data/ (Daniel) Changes in v4: - Move docbook location below panel (Daniel) - Improve docbook with references and more detailed explanation (Daniel) - Instead of register/unregister, use init/cleanup (Daniel) Changes in v5: - Resolved conflict in drm_atomic_helper.c #include block - Resolved conflict in rst with HDCP helper docs Changes in v6: - Fix include ordering, clean up forward declarations (Sam) Link to v1: https://patchwork.freedesktop.org/patch/msgid/20190228210939.83386-2-sean@poorly.run Link to v2: https://patchwork.freedesktop.org/patch/msgid/20190326204509.96515-1-sean@poorly.run Link to v3: https://patchwork.freedesktop.org/patch/msgid/20190502194956.218441-6-sean@poorly.run Link to v4: https://patchwork.freedesktop.org/patch/msgid/20190508160920.144739-6-sean@poorly.run Link to v5: https://patchwork.freedesktop.org/patch/msgid/20190611160844.257498-6-sean@poorly.run Cc: Daniel Vetter Cc: Jose Souza Cc: Zain Wang Cc: Tomasz Figa Cc: Ville Syrjälä Cc: Sam Ravnborg Tested-by: Heiko Stuebner Reviewed-by: Daniel Vetter Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190612145026.191846-1-sean@poorly.run --- Documentation/gpu/drm-kms-helpers.rst | 9 ++ drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_atomic.c | 2 + drivers/gpu/drm/drm_atomic_helper.c | 35 ++++- drivers/gpu/drm/drm_atomic_state_helper.c | 4 + drivers/gpu/drm/drm_atomic_uapi.c | 7 +- drivers/gpu/drm/drm_self_refresh_helper.c | 216 ++++++++++++++++++++++++++++++ include/drm/drm_atomic.h | 15 +++ include/drm/drm_connector.h | 14 ++ include/drm/drm_crtc.h | 19 +++ include/drm/drm_self_refresh_helper.h | 20 +++ 11 files changed, 338 insertions(+), 5 deletions(-) create mode 100644 drivers/gpu/drm/drm_self_refresh_helper.c create mode 100644 include/drm/drm_self_refresh_helper.h (limited to 'include/drm') diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 0fe726a6ee67..b327bbc11182 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -181,6 +181,15 @@ Panel Helper Reference .. kernel-doc:: drivers/gpu/drm/drm_panel_orientation_quirks.c :export: +Panel Self Refresh Helper Reference +=================================== + +.. kernel-doc:: drivers/gpu/drm/drm_self_refresh_helper.c + :doc: overview + +.. kernel-doc:: drivers/gpu/drm/drm_self_refresh_helper.c + :export: + HDCP Helper Functions Reference =============================== diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index d36feb4a6233..9d630a28a788 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -43,7 +43,7 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_dsc.o drm_probe_helper drm_simple_kms_helper.o drm_modeset_helper.o \ drm_scdc_helper.o drm_gem_framebuffer_helper.o \ drm_atomic_state_helper.o drm_damage_helper.o \ - drm_format_helper.o + drm_format_helper.o drm_self_refresh_helper.o drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index a4e779deab0f..419381abbdd1 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -384,6 +384,7 @@ static void drm_atomic_crtc_print_state(struct drm_printer *p, drm_printf(p, "crtc[%u]: %s\n", crtc->base.id, crtc->name); drm_printf(p, "\tenable=%d\n", state->enable); drm_printf(p, "\tactive=%d\n", state->active); + drm_printf(p, "\tself_refresh_active=%d\n", state->self_refresh_active); drm_printf(p, "\tplanes_changed=%d\n", state->planes_changed); drm_printf(p, "\tmode_changed=%d\n", state->mode_changed); drm_printf(p, "\tactive_changed=%d\n", state->active_changed); @@ -999,6 +1000,7 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, drm_printf(p, "connector[%u]: %s\n", connector->base.id, connector->name); drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)"); + drm_printf(p, "\tself_refresh_aware=%d\n", state->self_refresh_aware); if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) if (state->writeback_job && state->writeback_job->fb) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index e58be6996069..d9ea7e51ffdc 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -953,10 +954,33 @@ int drm_atomic_helper_check(struct drm_device *dev, if (state->legacy_cursor_update) state->async_update = !drm_atomic_helper_async_check(dev, state); + drm_self_refresh_helper_alter_state(state); + return ret; } EXPORT_SYMBOL(drm_atomic_helper_check); +static bool +crtc_needs_disable(struct drm_crtc_state *old_state, + struct drm_crtc_state *new_state) +{ + /* + * No new_state means the crtc is off, so the only criteria is whether + * it's currently active or in self refresh mode. + */ + if (!new_state) + return drm_atomic_crtc_effectively_active(old_state); + + /* + * We need to run through the crtc_funcs->disable() function if the crtc + * is currently on, if it's transitioning to self refresh mode, or if + * it's in self refresh mode and needs to be fully disabled. + */ + return old_state->active || + (old_state->self_refresh_active && !new_state->enable) || + new_state->self_refresh_active; +} + static void disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) { @@ -977,7 +1001,14 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) old_crtc_state = drm_atomic_get_old_crtc_state(old_state, old_conn_state->crtc); - if (!old_crtc_state->active || + if (new_conn_state->crtc) + new_crtc_state = drm_atomic_get_new_crtc_state( + old_state, + new_conn_state->crtc); + else + new_crtc_state = NULL; + + if (!crtc_needs_disable(old_crtc_state, new_crtc_state) || !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state)) continue; @@ -1023,7 +1054,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) continue; - if (!old_crtc_state->active) + if (!crtc_needs_disable(old_crtc_state, new_crtc_state)) continue; funcs = crtc->helper_private; diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 97ab26679b96..7d7347a6f194 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -129,6 +129,10 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, state->commit = NULL; state->event = NULL; state->pageflip_flags = 0; + + /* Self refresh should be canceled when a new update is available */ + state->active = drm_atomic_crtc_effectively_active(state); + state->self_refresh_active = false; } EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state); diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index eb22e8bdd853..abe38bdf85ae 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -490,7 +490,7 @@ drm_atomic_crtc_get_property(struct drm_crtc *crtc, struct drm_mode_config *config = &dev->mode_config; if (property == config->prop_active) - *val = state->active; + *val = drm_atomic_crtc_effectively_active(state); else if (property == config->prop_mode_id) *val = (state->mode_blob) ? state->mode_blob->base.id : 0; else if (property == config->prop_vrr_enabled) @@ -788,7 +788,10 @@ drm_atomic_connector_get_property(struct drm_connector *connector, if (property == config->prop_crtc_id) { *val = (state->crtc) ? state->crtc->base.id : 0; } else if (property == config->dpms_property) { - *val = connector->dpms; + if (state->crtc && state->crtc->state->self_refresh_active) + *val = DRM_MODE_DPMS_ON; + else + *val = connector->dpms; } else if (property == config->tv_select_subconnector_property) { *val = state->tv.subconnector; } else if (property == config->tv_left_margin_property) { diff --git a/drivers/gpu/drm/drm_self_refresh_helper.c b/drivers/gpu/drm/drm_self_refresh_helper.c new file mode 100644 index 000000000000..2b3daaf77841 --- /dev/null +++ b/drivers/gpu/drm/drm_self_refresh_helper.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2019 Google, Inc. + * + * Authors: + * Sean Paul + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * DOC: overview + * + * This helper library provides an easy way for drivers to leverage the atomic + * framework to implement panel self refresh (SR) support. Drivers are + * responsible for initializing and cleaning up the SR helpers on load/unload + * (see &drm_self_refresh_helper_init/&drm_self_refresh_helper_cleanup). + * The connector is responsible for setting + * &drm_connector_state.self_refresh_aware to true at runtime if it is SR-aware + * (meaning it knows how to initiate self refresh on the panel). + * + * Once a crtc has enabled SR using &drm_self_refresh_helper_init, the + * helpers will monitor activity and call back into the driver to enable/disable + * SR as appropriate. The best way to think about this is that it's a DPMS + * on/off request with &drm_crtc_state.self_refresh_active set in crtc state + * that tells you to disable/enable SR on the panel instead of power-cycling it. + * + * During SR, drivers may choose to fully disable their crtc/encoder/bridge + * hardware (in which case no driver changes are necessary), or they can inspect + * &drm_crtc_state.self_refresh_active if they want to enter low power mode + * without full disable (in case full disable/enable is too slow). + * + * SR will be deactivated if there are any atomic updates affecting the + * pipe that is in SR mode. If a crtc is driving multiple connectors, all + * connectors must be SR aware and all will enter/exit SR mode at the same time. + * + * If the crtc and connector are SR aware, but the panel connected does not + * support it (or is otherwise unable to enter SR), the driver should fail + * atomic_check when &drm_crtc_state.self_refresh_active is true. + */ + +struct drm_self_refresh_data { + struct drm_crtc *crtc; + struct delayed_work entry_work; + struct drm_atomic_state *save_state; + unsigned int entry_delay_ms; +}; + +static void drm_self_refresh_helper_entry_work(struct work_struct *work) +{ + struct drm_self_refresh_data *sr_data = container_of( + to_delayed_work(work), + struct drm_self_refresh_data, entry_work); + struct drm_crtc *crtc = sr_data->crtc; + struct drm_device *dev = crtc->dev; + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_state *state; + struct drm_connector *conn; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + int i, ret; + + drm_modeset_acquire_init(&ctx, 0); + + state = drm_atomic_state_alloc(dev); + if (!state) { + ret = -ENOMEM; + goto out; + } + +retry: + state->acquire_ctx = &ctx; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto out; + } + + if (!crtc_state->enable) + goto out; + + ret = drm_atomic_add_affected_connectors(state, crtc); + if (ret) + goto out; + + for_each_new_connector_in_state(state, conn, conn_state, i) { + if (!conn_state->self_refresh_aware) + goto out; + } + + crtc_state->active = false; + crtc_state->self_refresh_active = true; + + ret = drm_atomic_commit(state); + if (ret) + goto out; + +out: + if (ret == -EDEADLK) { + drm_atomic_state_clear(state); + ret = drm_modeset_backoff(&ctx); + if (!ret) + goto retry; + } + + drm_atomic_state_put(state); + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +/** + * drm_self_refresh_helper_alter_state - Alters the atomic state for SR exit + * @state: the state currently being checked + * + * Called at the end of atomic check. This function checks the state for flags + * incompatible with self refresh exit and changes them. This is a bit + * disingenuous since userspace is expecting one thing and we're giving it + * another. However in order to keep self refresh entirely hidden from + * userspace, this is required. + * + * At the end, we queue up the self refresh entry work so we can enter PSR after + * the desired delay. + */ +void drm_self_refresh_helper_alter_state(struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + int i; + + if (state->async_update || !state->allow_modeset) { + for_each_old_crtc_in_state(state, crtc, crtc_state, i) { + if (crtc_state->self_refresh_active) { + state->async_update = false; + state->allow_modeset = true; + break; + } + } + } + + for_each_new_crtc_in_state(state, crtc, crtc_state, i) { + struct drm_self_refresh_data *sr_data; + + /* Don't trigger the entry timer when we're already in SR */ + if (crtc_state->self_refresh_active) + continue; + + sr_data = crtc->self_refresh_data; + if (!sr_data) + continue; + + mod_delayed_work(system_wq, &sr_data->entry_work, + msecs_to_jiffies(sr_data->entry_delay_ms)); + } +} +EXPORT_SYMBOL(drm_self_refresh_helper_alter_state); + +/** + * drm_self_refresh_helper_init - Initializes self refresh helpers for a crtc + * @crtc: the crtc which supports self refresh supported displays + * @entry_delay_ms: amount of inactivity to wait before entering self refresh + * + * Returns zero if successful or -errno on failure + */ +int drm_self_refresh_helper_init(struct drm_crtc *crtc, + unsigned int entry_delay_ms) +{ + struct drm_self_refresh_data *sr_data = crtc->self_refresh_data; + + /* Helper is already initialized */ + if (WARN_ON(sr_data)) + return -EINVAL; + + sr_data = kzalloc(sizeof(*sr_data), GFP_KERNEL); + if (!sr_data) + return -ENOMEM; + + INIT_DELAYED_WORK(&sr_data->entry_work, + drm_self_refresh_helper_entry_work); + sr_data->entry_delay_ms = entry_delay_ms; + sr_data->crtc = crtc; + + crtc->self_refresh_data = sr_data; + return 0; +} +EXPORT_SYMBOL(drm_self_refresh_helper_init); + +/** + * drm_self_refresh_helper_cleanup - Cleans up self refresh helpers for a crtc + * @crtc: the crtc to cleanup + */ +void drm_self_refresh_helper_cleanup(struct drm_crtc *crtc) +{ + struct drm_self_refresh_data *sr_data = crtc->self_refresh_data; + + /* Helper is already uninitialized */ + if (sr_data) + return; + + crtc->self_refresh_data = NULL; + + cancel_delayed_work_sync(&sr_data->entry_work); + kfree(sr_data); +} +EXPORT_SYMBOL(drm_self_refresh_helper_cleanup); diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index f12215647801..927e1205d7aa 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -957,4 +957,19 @@ drm_atomic_crtc_needs_modeset(const struct drm_crtc_state *state) state->connectors_changed; } +/** + * drm_atomic_crtc_effectively_active - compute whether crtc is actually active + * @state: &drm_crtc_state for the CRTC + * + * When in self refresh mode, the crtc_state->active value will be false, since + * the crtc is off. However in some cases we're interested in whether the crtc + * is active, or effectively active (ie: it's connected to an active display). + * In these cases, use this function instead of just checking active. + */ +static inline bool +drm_atomic_crtc_effectively_active(const struct drm_crtc_state *state) +{ + return state->active || state->self_refresh_active; +} + #endif /* DRM_ATOMIC_H_ */ diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 071143bc0ebd..c6f8486d8b8f 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -548,6 +548,20 @@ struct drm_connector_state { /** @tv: TV connector state */ struct drm_tv_connector_state tv; + /** + * @self_refresh_aware: + * + * This tracks whether a connector is aware of the self refresh state. + * It should be set to true for those connector implementations which + * understand the self refresh state. This is needed since the crtc + * registers the self refresh helpers and it doesn't know if the + * connectors downstream have implemented self refresh entry/exit. + * + * Drivers should set this to true in atomic_check if they know how to + * handle self_refresh requests. + */ + bool self_refresh_aware; + /** * @picture_aspect_ratio: Connector property to control the * HDMI infoframe aspect ratio setting. diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index dc42b9e35333..128d8b210621 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -54,6 +54,7 @@ struct drm_mode_set; struct drm_file; struct drm_clip_rect; struct drm_printer; +struct drm_self_refresh_data; struct device_node; struct dma_fence; struct edid; @@ -300,6 +301,17 @@ struct drm_crtc_state { */ bool vrr_enabled; + /** + * @self_refresh_active: + * + * Used by the self refresh helpers to denote when a self refresh + * transition is occurring. This will be set on enable/disable callbacks + * when self refresh is being enabled or disabled. In some cases, it may + * not be desirable to fully shut off the crtc during self refresh. + * CRTC's can inspect this flag and determine the best course of action. + */ + bool self_refresh_active; + /** * @event: * @@ -1088,6 +1100,13 @@ struct drm_crtc { * The name of the CRTC's fence timeline. */ char timeline_name[32]; + + /** + * @self_refresh_data: Holds the state for the self refresh helpers + * + * Initialized via drm_self_refresh_helper_register(). + */ + struct drm_self_refresh_data *self_refresh_data; }; /** diff --git a/include/drm/drm_self_refresh_helper.h b/include/drm/drm_self_refresh_helper.h new file mode 100644 index 000000000000..397a583ccca7 --- /dev/null +++ b/include/drm/drm_self_refresh_helper.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (C) 2019 Google, Inc. + * + * Authors: + * Sean Paul + */ +#ifndef DRM_SELF_REFRESH_HELPER_H_ +#define DRM_SELF_REFRESH_HELPER_H_ + +struct drm_atomic_state; +struct drm_crtc; + +void drm_self_refresh_helper_alter_state(struct drm_atomic_state *state); + +int drm_self_refresh_helper_init(struct drm_crtc *crtc, + unsigned int entry_delay_ms); + +void drm_self_refresh_helper_cleanup(struct drm_crtc *crtc); +#endif -- cgit v1.2.3 From 7974033e527a5dd12d96126d09d4cff4f9b65c69 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 28 May 2019 17:06:49 +0300 Subject: drm/dp: Add DP_DPCD_QUIRK_NO_SINK_COUNT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CH7511 eDP->LVDS bridge doesn't seem to set SINK_COUNT properly causing i915 to detect it as disconnected. Add a quirk to ignore SINK_COUNT on these devices. Cc: David S. Cc: Peteris Rudzusiks Tested-by: Peteris Rudzusiks Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=105406 Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20190528140650.19230-1-ville.syrjala@linux.intel.com Acked-by: Jani Nikula #irc --- drivers/gpu/drm/drm_dp_helper.c | 4 +++- include/drm/drm_dp_helper.h | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index e6af758a7d22..0b994d083a89 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -1280,7 +1280,9 @@ static const struct dpcd_quirk dpcd_quirk_list[] = { /* LG LP140WF6-SPM1 eDP panel */ { OUI(0x00, 0x22, 0xb9), DEVICE_ID('s', 'i', 'v', 'a', 'r', 'T'), false, BIT(DP_DPCD_QUIRK_CONSTANT_N) }, /* Apple panels need some additional handling to support PSR */ - { OUI(0x00, 0x10, 0xfa), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_NO_PSR) } + { OUI(0x00, 0x10, 0xfa), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_NO_PSR) }, + /* CH7511 seems to leave SINK_COUNT zeroed */ + { OUI(0x00, 0x00, 0x00), DEVICE_ID('C', 'H', '7', '5', '1', '1'), false, BIT(DP_DPCD_QUIRK_NO_SINK_COUNT) }, }; #undef OUI diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 3fc534ee8174..7e52eb81284a 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1414,6 +1414,13 @@ enum drm_dp_quirk { * driver still need to implement proper handling for such device. */ DP_DPCD_QUIRK_NO_PSR, + /** + * @DP_DPCD_QUIRK_NO_SINK_COUNT: + * + * The device does not set SINK_COUNT to a non-zero value. + * The driver should ignore SINK_COUNT during detection. + */ + DP_DPCD_QUIRK_NO_SINK_COUNT, }; /** -- cgit v1.2.3 From eb69c8a4bf5e26ae112793bb04a6cd6c27114900 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 14 Jun 2019 22:35:18 +0200 Subject: drm/gem: Unexport drm_gem_(un)pin/v(un)map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They're purely for internal use, not for drivers. Cc: Noralf Trønnes Cc: Christian König Reviewed-by: Eric Anholt Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20190614203615.12639-3-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_gem.c | 32 -------------------------------- drivers/gpu/drm/drm_internal.h | 5 +++++ include/drm/drm_gem.h | 5 ----- 3 files changed, 5 insertions(+), 37 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 8a55f71325b1..a8c4468f03d9 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1216,15 +1216,6 @@ void drm_gem_print_info(struct drm_printer *p, unsigned int indent, obj->dev->driver->gem_print_info(p, indent, obj); } -/** - * drm_gem_pin - Pin backing buffer in memory - * @obj: GEM object - * - * Make sure the backing buffer is pinned in memory. - * - * Returns: - * 0 on success or a negative error code on failure. - */ int drm_gem_pin(struct drm_gem_object *obj) { if (obj->funcs && obj->funcs->pin) @@ -1234,14 +1225,7 @@ int drm_gem_pin(struct drm_gem_object *obj) else return 0; } -EXPORT_SYMBOL(drm_gem_pin); -/** - * drm_gem_unpin - Unpin backing buffer from memory - * @obj: GEM object - * - * Relax the requirement that the backing buffer is pinned in memory. - */ void drm_gem_unpin(struct drm_gem_object *obj) { if (obj->funcs && obj->funcs->unpin) @@ -1249,16 +1233,7 @@ void drm_gem_unpin(struct drm_gem_object *obj) else if (obj->dev->driver->gem_prime_unpin) obj->dev->driver->gem_prime_unpin(obj); } -EXPORT_SYMBOL(drm_gem_unpin); -/** - * drm_gem_vmap - Map buffer into kernel virtual address space - * @obj: GEM object - * - * Returns: - * A virtual pointer to a newly created GEM object or an ERR_PTR-encoded negative - * error code on failure. - */ void *drm_gem_vmap(struct drm_gem_object *obj) { void *vaddr; @@ -1275,13 +1250,7 @@ void *drm_gem_vmap(struct drm_gem_object *obj) return vaddr; } -EXPORT_SYMBOL(drm_gem_vmap); -/** - * drm_gem_vunmap - Remove buffer mapping from kernel virtual address space - * @obj: GEM object - * @vaddr: Virtual address (can be NULL) - */ void drm_gem_vunmap(struct drm_gem_object *obj, void *vaddr) { if (!vaddr) @@ -1292,7 +1261,6 @@ void drm_gem_vunmap(struct drm_gem_object *obj, void *vaddr) else if (obj->dev->driver->gem_prime_vunmap) obj->dev->driver->gem_prime_vunmap(obj, vaddr); } -EXPORT_SYMBOL(drm_gem_vunmap); /** * drm_gem_lock_reservations - Sets up the ww context and acquires diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index d18c7b91a1a8..51a2055c8f18 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -133,6 +133,11 @@ void drm_gem_release(struct drm_device *dev, struct drm_file *file_private); void drm_gem_print_info(struct drm_printer *p, unsigned int indent, const struct drm_gem_object *obj); +int drm_gem_pin(struct drm_gem_object *obj); +void drm_gem_unpin(struct drm_gem_object *obj); +void *drm_gem_vmap(struct drm_gem_object *obj); +void drm_gem_vunmap(struct drm_gem_object *obj, void *vaddr); + /* drm_debugfs.c drm_debugfs_crc.c */ #if defined(CONFIG_DEBUG_FS) int drm_debugfs_init(struct drm_minor *minor, int minor_id, diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h index 5047c7ee25f5..a9121fe66ea2 100644 --- a/include/drm/drm_gem.h +++ b/include/drm/drm_gem.h @@ -401,9 +401,4 @@ int drm_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, uint32_t handle); -int drm_gem_pin(struct drm_gem_object *obj); -void drm_gem_unpin(struct drm_gem_object *obj); -void *drm_gem_vmap(struct drm_gem_object *obj); -void drm_gem_vunmap(struct drm_gem_object *obj, void *vaddr); - #endif /* __DRM_GEM_H__ */ -- cgit v1.2.3 From 32d1f6985ceb6b9099bc9e02dc04e58660f28c16 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Fri, 12 Oct 2018 17:16:39 +0200 Subject: drm/ttm: Allow the driver to provide the ttm struct vm_operations_struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a pointer to the struct vm_operations_struct in the bo_device, and assign that pointer to the default value currently used. The driver can then optionally modify that pointer and the new value can be used for each new vma created. Cc: "Christian König" Signed-off-by: Thomas Hellstrom Reviewed-by: Christian König --- drivers/gpu/drm/ttm/ttm_bo.c | 1 + drivers/gpu/drm/ttm/ttm_bo_vm.c | 6 +++--- include/drm/ttm/ttm_bo_driver.h | 6 ++++++ 3 files changed, 10 insertions(+), 3 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index c7de667d482a..6953dd264172 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -1739,6 +1739,7 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev, mutex_lock(&ttm_global_mutex); list_add_tail(&bdev->device_list, &glob->device_list); mutex_unlock(&ttm_global_mutex); + bdev->vm_ops = &ttm_bo_vm_ops; return 0; out_no_sys: diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 6dacff49c1cc..196e13a0adad 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -395,7 +395,7 @@ static int ttm_bo_vm_access(struct vm_area_struct *vma, unsigned long addr, return ret; } -static const struct vm_operations_struct ttm_bo_vm_ops = { +const struct vm_operations_struct ttm_bo_vm_ops = { .fault = ttm_bo_vm_fault, .open = ttm_bo_vm_open, .close = ttm_bo_vm_close, @@ -448,7 +448,7 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma, if (unlikely(ret != 0)) goto out_unref; - vma->vm_ops = &ttm_bo_vm_ops; + vma->vm_ops = bdev->vm_ops; /* * Note: We're transferring the bo reference to @@ -480,7 +480,7 @@ int ttm_fbdev_mmap(struct vm_area_struct *vma, struct ttm_buffer_object *bo) ttm_bo_get(bo); - vma->vm_ops = &ttm_bo_vm_ops; + vma->vm_ops = bo->bdev->vm_ops; vma->vm_private_data = bo; vma->vm_flags |= VM_MIXEDMAP; vma->vm_flags |= VM_IO | VM_DONTEXPAND; diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index c9b8ba492f24..a2d810a2504d 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -442,6 +442,9 @@ extern struct ttm_bo_global { * @driver: Pointer to a struct ttm_bo_driver struct setup by the driver. * @man: An array of mem_type_managers. * @vma_manager: Address space manager + * @vm_ops: Pointer to the struct vm_operations_struct used for this + * device's VM operations. The driver may override this before the first + * mmap() call. * lru_lock: Spinlock that protects the buffer+device lru lists and * ddestroy lists. * @dev_mapping: A pointer to the struct address_space representing the @@ -460,6 +463,7 @@ struct ttm_bo_device { struct ttm_bo_global *glob; struct ttm_bo_driver *driver; struct ttm_mem_type_manager man[TTM_NUM_MEM_TYPES]; + const struct vm_operations_struct *vm_ops; /* * Protected by internal locks. @@ -488,6 +492,8 @@ struct ttm_bo_device { bool no_retry; }; +extern const struct vm_operations_struct ttm_bo_vm_ops; + /** * struct ttm_lru_bulk_move_pos * -- cgit v1.2.3 From 7a39f35ce43f96ff293cc90f845416d03566b14b Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Wed, 6 Feb 2019 12:55:08 +0100 Subject: drm/ttm: TTM fault handler helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the vmwgfx dirty tracking, the default TTM fault handler is not completely sufficient (vmwgfx need to modify the vma->vm_flags member, and also needs to restrict the number of prefaults). We also want to replicate the new ttm_bo_vm_reserve() functionality So start turning the TTM vm code into helpers: ttm_bo_vm_fault_reserved() and ttm_bo_vm_reserve(), and provide a default TTM fault handler for other drivers to use. Cc: "Christian König" Signed-off-by: Thomas Hellstrom Reviewed-by: "Christian König" #v1 --- drivers/gpu/drm/ttm/ttm_bo_vm.c | 163 +++++++++++++++++++++++++--------------- include/drm/ttm/ttm_bo_api.h | 10 +++ 2 files changed, 111 insertions(+), 62 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 196e13a0adad..0c4576cbafcf 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -42,8 +42,6 @@ #include #include -#define TTM_BO_VM_NUM_PREFAULT 16 - static vm_fault_t ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo, struct vm_fault *vmf) { @@ -106,25 +104,30 @@ static unsigned long ttm_bo_io_mem_pfn(struct ttm_buffer_object *bo, + page_offset; } -static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) +/** + * ttm_bo_vm_reserve - Reserve a buffer object in a retryable vm callback + * @bo: The buffer object + * @vmf: The fault structure handed to the callback + * + * vm callbacks like fault() and *_mkwrite() allow for the mm_sem to be dropped + * during long waits, and after the wait the callback will be restarted. This + * is to allow other threads using the same virtual memory space concurrent + * access to map(), unmap() completely unrelated buffer objects. TTM buffer + * object reservations sometimes wait for GPU and should therefore be + * considered long waits. This function reserves the buffer object interruptibly + * taking this into account. Starvation is avoided by the vm system not + * allowing too many repeated restarts. + * This function is intended to be used in customized fault() and _mkwrite() + * handlers. + * + * Return: + * 0 on success and the bo was reserved. + * VM_FAULT_RETRY if blocking wait. + * VM_FAULT_NOPAGE if blocking wait and retrying was not allowed. + */ +vm_fault_t ttm_bo_vm_reserve(struct ttm_buffer_object *bo, + struct vm_fault *vmf) { - struct vm_area_struct *vma = vmf->vma; - struct ttm_buffer_object *bo = (struct ttm_buffer_object *) - vma->vm_private_data; - struct ttm_bo_device *bdev = bo->bdev; - unsigned long page_offset; - unsigned long page_last; - unsigned long pfn; - struct ttm_tt *ttm = NULL; - struct page *page; - int err; - int i; - vm_fault_t ret = VM_FAULT_NOPAGE; - unsigned long address = vmf->address; - struct ttm_mem_type_manager *man = - &bdev->man[bo->mem.mem_type]; - struct vm_area_struct cvma; - /* * Work around locking order reversal in fault / nopfn * between mmap_sem and bo_reserve: Perform a trylock operation @@ -151,14 +154,55 @@ static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) return VM_FAULT_NOPAGE; } + return 0; +} +EXPORT_SYMBOL(ttm_bo_vm_reserve); + +/** + * ttm_bo_vm_fault_reserved - TTM fault helper + * @vmf: The struct vm_fault given as argument to the fault callback + * @prot: The page protection to be used for this memory area. + * @num_prefault: Maximum number of prefault pages. The caller may want to + * specify this based on madvice settings and the size of the GPU object + * backed by the memory. + * + * This function inserts one or more page table entries pointing to the + * memory backing the buffer object, and then returns a return code + * instructing the caller to retry the page access. + * + * Return: + * VM_FAULT_NOPAGE on success or pending signal + * VM_FAULT_SIGBUS on unspecified error + * VM_FAULT_OOM on out-of-memory + * VM_FAULT_RETRY if retryable wait + */ +vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf, + pgprot_t prot, + pgoff_t num_prefault) +{ + struct vm_area_struct *vma = vmf->vma; + struct vm_area_struct cvma = *vma; + struct ttm_buffer_object *bo = (struct ttm_buffer_object *) + vma->vm_private_data; + struct ttm_bo_device *bdev = bo->bdev; + unsigned long page_offset; + unsigned long page_last; + unsigned long pfn; + struct ttm_tt *ttm = NULL; + struct page *page; + int err; + pgoff_t i; + vm_fault_t ret = VM_FAULT_NOPAGE; + unsigned long address = vmf->address; + struct ttm_mem_type_manager *man = + &bdev->man[bo->mem.mem_type]; + /* * Refuse to fault imported pages. This should be handled * (if at all) by redirecting mmap to the exporter. */ - if (bo->ttm && (bo->ttm->page_flags & TTM_PAGE_FLAG_SG)) { - ret = VM_FAULT_SIGBUS; - goto out_unlock; - } + if (bo->ttm && (bo->ttm->page_flags & TTM_PAGE_FLAG_SG)) + return VM_FAULT_SIGBUS; if (bdev->driver->fault_reserve_notify) { struct dma_fence *moving = dma_fence_get(bo->moving); @@ -169,11 +213,9 @@ static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) break; case -EBUSY: case -ERESTARTSYS: - ret = VM_FAULT_NOPAGE; - goto out_unlock; + return VM_FAULT_NOPAGE; default: - ret = VM_FAULT_SIGBUS; - goto out_unlock; + return VM_FAULT_SIGBUS; } if (bo->moving != moving) { @@ -189,21 +231,12 @@ static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) * move. */ ret = ttm_bo_vm_fault_idle(bo, vmf); - if (unlikely(ret != 0)) { - if (ret == VM_FAULT_RETRY && - !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) { - /* The BO has already been unreserved. */ - return ret; - } - - goto out_unlock; - } + if (unlikely(ret != 0)) + return ret; err = ttm_mem_io_lock(man, true); - if (unlikely(err != 0)) { - ret = VM_FAULT_NOPAGE; - goto out_unlock; - } + if (unlikely(err != 0)) + return VM_FAULT_NOPAGE; err = ttm_mem_io_reserve_vm(bo); if (unlikely(err != 0)) { ret = VM_FAULT_SIGBUS; @@ -220,18 +253,8 @@ static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) goto out_io_unlock; } - /* - * Make a local vma copy to modify the page_prot member - * and vm_flags if necessary. The vma parameter is protected - * by mmap_sem in write mode. - */ - cvma = *vma; - cvma.vm_page_prot = vm_get_page_prot(cvma.vm_flags); - - if (bo->mem.bus.is_iomem) { - cvma.vm_page_prot = ttm_io_prot(bo->mem.placement, - cvma.vm_page_prot); - } else { + cvma.vm_page_prot = ttm_io_prot(bo->mem.placement, prot); + if (!bo->mem.bus.is_iomem) { struct ttm_operation_ctx ctx = { .interruptible = false, .no_wait_gpu = false, @@ -240,24 +263,21 @@ static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) }; ttm = bo->ttm; - cvma.vm_page_prot = ttm_io_prot(bo->mem.placement, - cvma.vm_page_prot); - - /* Allocate all page at once, most common usage */ - if (ttm_tt_populate(ttm, &ctx)) { + if (ttm_tt_populate(bo->ttm, &ctx)) { ret = VM_FAULT_OOM; goto out_io_unlock; } + } else { + /* Iomem should not be marked encrypted */ + cvma.vm_page_prot = pgprot_decrypted(cvma.vm_page_prot); } /* * Speculatively prefault a number of pages. Only error on * first page. */ - for (i = 0; i < TTM_BO_VM_NUM_PREFAULT; ++i) { + for (i = 0; i < num_prefault; ++i) { if (bo->mem.bus.is_iomem) { - /* Iomem should not be marked encrypted */ - cvma.vm_page_prot = pgprot_decrypted(cvma.vm_page_prot); pfn = ttm_bo_io_mem_pfn(bo, page_offset); } else { page = ttm->pages[page_offset]; @@ -295,7 +315,26 @@ static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) ret = VM_FAULT_NOPAGE; out_io_unlock: ttm_mem_io_unlock(man); -out_unlock: + return ret; +} +EXPORT_SYMBOL(ttm_bo_vm_fault_reserved); + +static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) +{ + struct vm_area_struct *vma = vmf->vma; + pgprot_t prot; + struct ttm_buffer_object *bo = vma->vm_private_data; + vm_fault_t ret; + + ret = ttm_bo_vm_reserve(bo, vmf); + if (ret) + return ret; + + prot = vm_get_page_prot(vma->vm_flags); + ret = ttm_bo_vm_fault_reserved(vmf, prot, TTM_BO_VM_NUM_PREFAULT); + if (ret == VM_FAULT_RETRY && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) + return ret; + reservation_object_unlock(bo->resv); return ret; } diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h index 49d9cdfc58f2..435d02f719a8 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo_api.h @@ -768,4 +768,14 @@ int ttm_bo_swapout(struct ttm_bo_global *glob, struct ttm_operation_ctx *ctx); void ttm_bo_swapout_all(struct ttm_bo_device *bdev); int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo); + +/* Default number of pre-faulted pages in the TTM fault handler */ +#define TTM_BO_VM_NUM_PREFAULT 16 + +vm_fault_t ttm_bo_vm_reserve(struct ttm_buffer_object *bo, + struct vm_fault *vmf); + +vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf, + pgprot_t prot, + pgoff_t num_prefault); #endif -- cgit v1.2.3 From 772cd52c5574b04b00a97d638b2cfe94c0c1a9b6 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 19 Jun 2019 12:17:48 +0200 Subject: drm/connector: Add documentation for drm_cmdline_mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The struct drm_cmdline_mode holds the result of the command line parsers. However, it wasn't documented so far, so let's do that. Reviewed-by: Noralf Trønnes Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/963c893c16c6a25fc469b53c726f493d99bdc578.1560783090.git-series.maxime.ripard@bootlin.com --- include/drm/drm_connector.h | 86 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 2 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index c6f8486d8b8f..c802780b0bfc 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -923,18 +923,100 @@ struct drm_connector_funcs { const struct drm_connector_state *state); }; -/* mode specified on the command line */ +/** + * struct drm_cmdline_mode - DRM Mode passed through the kernel command-line + * + * Each connector can have an initial mode with additional options + * passed through the kernel command line. This structure allows to + * express those parameters and will be filled by the command-line + * parser. + */ struct drm_cmdline_mode { + /** + * @specified: + * + * Has a mode been read from the command-line? + */ bool specified; + + /** + * @refresh_specified: + * + * Did the mode have a preferred refresh rate? + */ bool refresh_specified; + + /** + * @bpp_specified: + * + * Did the mode have a preferred BPP? + */ bool bpp_specified; - int xres, yres; + + /** + * @xres: + * + * Active resolution on the X axis, in pixels. + */ + int xres; + + /** + * @yres: + * + * Active resolution on the Y axis, in pixels. + */ + int yres; + + /** + * @bpp: + * + * Bits per pixels for the mode. + */ int bpp; + + /** + * @refresh: + * + * Refresh rate, in Hertz. + */ int refresh; + + /** + * @rb: + * + * Do we need to use reduced blanking? + */ bool rb; + + /** + * @interlace: + * + * The mode is interlaced. + */ bool interlace; + + /** + * @cvt: + * + * The timings will be calculated using the VESA Coordinated + * Video Timings instead of looking up the mode from a table. + */ bool cvt; + + /** + * @margins: + * + * Add margins to the mode calculation (1.8% of xres rounded + * down to 8 pixels and 1.8% of yres). + */ bool margins; + + /** + * @force: + * + * Ignore the hotplug state of the connector, and force its + * state to one of the DRM_FORCE_* values. + */ enum drm_connector_force force; }; -- cgit v1.2.3 From a99076e87e0644088b09182bba147754ba7238ad Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 19 Jun 2019 12:17:49 +0200 Subject: drm/client: Change drm_client_panel_rotation name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm_client_panel_rotation function has been used so far to set the default rotation based on the panel orientation. However, we can have more sources of information to make that decision, starting with the command line that we will introduce later in this series. Change the name to remove the panel mention. Reviewed-by: Noralf Trønnes Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/8cb0f0d9569d41685bbf30a1538da6578cd2769b.1560783090.git-series.maxime.ripard@bootlin.com --- drivers/gpu/drm/drm_client_modeset.c | 12 ++++++------ drivers/gpu/drm/drm_fb_helper.c | 2 +- include/drm/drm_client.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index b4e5fb0a17cf..4869a0170bec 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -804,19 +804,19 @@ free_connectors: EXPORT_SYMBOL(drm_client_modeset_probe); /** - * drm_client_panel_rotation() - Check panel orientation + * drm_client_rotation() - Check the initial rotation value * @modeset: DRM modeset * @rotation: Returned rotation value * - * This function checks if the primary plane in @modeset can hw rotate to match - * the panel orientation on its connector. + * This function checks if the primary plane in @modeset can hw rotate + * to match the rotation needed on its connector. * * Note: Currently only 0 and 180 degrees are supported. * * Return: * True if the plane can do the rotation, false otherwise. */ -bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotation) +bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation) { struct drm_connector *connector = modeset->connectors[0]; struct drm_plane *plane = modeset->crtc->primary; @@ -857,7 +857,7 @@ bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotat return true; } -EXPORT_SYMBOL(drm_client_panel_rotation); +EXPORT_SYMBOL(drm_client_rotation); static int drm_client_modeset_commit_atomic(struct drm_client_dev *client, bool active) { @@ -902,7 +902,7 @@ retry: struct drm_plane *primary = mode_set->crtc->primary; unsigned int rotation; - if (drm_client_panel_rotation(mode_set, &rotation)) { + if (drm_client_rotation(mode_set, &rotation)) { struct drm_plane_state *plane_state; /* Cannot fail as we've already gotten the plane state above */ diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 42852cae749b..1984e5c54d58 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1722,7 +1722,7 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) modeset->fb = fb_helper->fb; - if (drm_client_panel_rotation(modeset, &rotation)) + if (drm_client_rotation(modeset, &rotation)) /* Rotating in hardware, fbcon should not rotate */ sw_rotations |= DRM_MODE_ROTATE_0; else diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index f2d5ed745733..72d51d1e9dd9 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -153,7 +153,7 @@ void drm_client_framebuffer_delete(struct drm_client_buffer *buffer); int drm_client_modeset_create(struct drm_client_dev *client); void drm_client_modeset_free(struct drm_client_dev *client); int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, unsigned int height); -bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotation); +bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation); int drm_client_modeset_commit_force(struct drm_client_dev *client); int drm_client_modeset_commit(struct drm_client_dev *client); int drm_client_modeset_dpms(struct drm_client_dev *client, int mode); -- cgit v1.2.3 From 3aeeb13d899627fe2b86bdbdcd0927cf7192234f Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 19 Jun 2019 12:17:50 +0200 Subject: drm/modes: Support modes names on the command line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm subsystem also uses the video= kernel parameter, and in the documentation refers to the fbdev documentation for that parameter. However, that documentation also says that instead of giving the mode using its resolution we can also give a name. However, DRM doesn't handle that case at the moment. Even though in most case it shouldn't make any difference, it might be useful for analog modes, where different standards might have the same resolution, but still have a few different parameters that are not encoded in the modes (NTSC vs NTSC-J vs PAL-M for example). Reviewed-by: Noralf Trønnes Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/18443e0c3bdbbd16cea4ec63bc7f2079b820b43b.1560783090.git-series.maxime.ripard@bootlin.com --- drivers/gpu/drm/drm_client_modeset.c | 4 +++ drivers/gpu/drm/drm_connector.c | 3 +- drivers/gpu/drm/drm_modes.c | 62 ++++++++++++++++++++++++++---------- include/drm/drm_connector.h | 7 ++++ 4 files changed, 59 insertions(+), 17 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index 4869a0170bec..33d4988f22ae 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -149,6 +149,10 @@ drm_connector_pick_cmdline_mode(struct drm_connector *connector) prefer_non_interlace = !cmdline_mode->interlace; again: list_for_each_entry(mode, &connector->modes, head) { + /* Check (optional) mode name first */ + if (!strcmp(mode->name, cmdline_mode->name)) + return mode; + /* check width/height */ if (mode->hdisplay != cmdline_mode->xres || mode->vdisplay != cmdline_mode->yres) diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 3ccdcf3dfcde..3afed5677946 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -139,8 +139,9 @@ static void drm_connector_get_cmdline_mode(struct drm_connector *connector) connector->force = mode->force; } - DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", + DRM_DEBUG_KMS("cmdline mode for connector %s %s %dx%d@%dHz%s%s%s\n", connector->name, + mode->name ? mode->name : "", mode->xres, mode->yres, mode->refresh_specified ? mode->refresh : 60, mode->rb ? " reduced blanking" : "", diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 6debbd6c1763..429d3be17800 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1580,7 +1580,7 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, struct drm_cmdline_mode *mode) { const char *name; - bool parse_extras = false; + bool named_mode = false, parse_extras = false; unsigned int bpp_off = 0, refresh_off = 0; unsigned int mode_end = 0; char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL; @@ -1599,8 +1599,22 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, name = mode_option; - if (!isdigit(name[0])) - return false; + /* + * This is a bit convoluted. To differentiate between the + * named modes and poorly formatted resolutions, we need a + * bunch of things: + * - We need to make sure that the first character (which + * would be our resolution in X) is a digit. + * - However, if the X resolution is missing, then we end up + * with something like x, with our first character + * being an alpha-numerical character, which would be + * considered a named mode. + * + * If this isn't enough, we should add more heuristics here, + * and matching unit-tests. + */ + if (!isdigit(name[0]) && name[0] != 'x') + named_mode = true; /* Try to locate the bpp and refresh specifiers, if any */ bpp_ptr = strchr(name, '-'); @@ -1611,6 +1625,9 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, refresh_ptr = strchr(name, '@'); if (refresh_ptr) { + if (named_mode) + return false; + refresh_off = refresh_ptr - name; mode->refresh_specified = true; } @@ -1627,12 +1644,16 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, parse_extras = true; } - ret = drm_mode_parse_cmdline_res_mode(name, mode_end, - parse_extras, - connector, - mode); - if (ret) - return false; + if (named_mode) { + strncpy(mode->name, name, mode_end); + } else { + ret = drm_mode_parse_cmdline_res_mode(name, mode_end, + parse_extras, + connector, + mode); + if (ret) + return false; + } mode->specified = true; if (bpp_ptr) { @@ -1660,14 +1681,23 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, extra_ptr = refresh_end_ptr; if (extra_ptr) { - int remaining = strlen(name) - (extra_ptr - name); + if (!named_mode) { + int len = strlen(name) - (extra_ptr - name); - /* - * We still have characters to process, while - * we shouldn't have any - */ - if (remaining > 0) - return false; + ret = drm_mode_parse_cmdline_extra(extra_ptr, len, + connector, mode); + if (ret) + return false; + } else { + int remaining = strlen(name) - (extra_ptr - name); + + /* + * We still have characters to process, while + * we shouldn't have any + */ + if (remaining > 0) + return false; + } } return true; diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index c802780b0bfc..cdf2fb910010 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -932,6 +932,13 @@ struct drm_connector_funcs { * parser. */ struct drm_cmdline_mode { + /** + * @name: + * + * Name of the mode. + */ + char name[DRM_DISPLAY_MODE_LEN]; + /** * @specified: * -- cgit v1.2.3 From 1bf4e09227c345e246062285eba4b8fe660e512e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 19 Jun 2019 12:17:51 +0200 Subject: drm/modes: Allow to specify rotation and reflection on the commandline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rotations and reflections setup are needed in some scenarios to initialise properly the initial framebuffer. Some drivers already had a bunch of quirks to deal with this, such as either a private kernel command line parameter (omapdss) or on the device tree (various panels). In order to accomodate this, let's create a video mode parameter to deal with the rotation and reflexion. Reviewed-by: Noralf Trønnes Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/777da16e42db757c1f5b414b5ca34507097fed5c.1560783090.git-series.maxime.ripard@bootlin.com --- Documentation/fb/modedb.txt | 12 ++++ drivers/gpu/drm/drm_client_modeset.c | 30 +++++++++ drivers/gpu/drm/drm_modes.c | 114 +++++++++++++++++++++++++++++------ include/drm/drm_connector.h | 10 +++ 4 files changed, 146 insertions(+), 20 deletions(-) (limited to 'include/drm') diff --git a/Documentation/fb/modedb.txt b/Documentation/fb/modedb.txt index 16aa08453911..52418c6dbfc4 100644 --- a/Documentation/fb/modedb.txt +++ b/Documentation/fb/modedb.txt @@ -51,6 +51,18 @@ To force the VGA output to be enabled and drive a specific mode say: Specifying the option multiple times for different ports is possible, e.g.: video=LVDS-1:d video=HDMI-1:D +Options can also be passed after the mode, using commas as separator. + + Sample usage: 720x480,rotate=180 - 720x480 mode, rotated by 180 degrees + +Valid options are: + + - reflect_x (boolean): Perform an axial symmetry on the X axis + - reflect_y (boolean): Perform an axial symmetry on the Y axis + - rotate (integer): Rotate the initial framebuffer by x + degrees. Valid values are 0, 90, 180 and 270. + + ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** oOo ***** What is the VESA(TM) Coordinated Video Timings (CVT)? diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index 33d4988f22ae..e95fceac8f8b 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -824,6 +824,7 @@ bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation) { struct drm_connector *connector = modeset->connectors[0]; struct drm_plane *plane = modeset->crtc->primary; + struct drm_cmdline_mode *cmdline; u64 valid_mask = 0; unsigned int i; @@ -844,6 +845,35 @@ bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation) *rotation = DRM_MODE_ROTATE_0; } + /** + * The panel already defined the default rotation + * through its orientation. Whatever has been provided + * on the command line needs to be added to that. + * + * Unfortunately, the rotations are at different bit + * indices, so the math to add them up are not as + * trivial as they could. + * + * Reflections on the other hand are pretty trivial to deal with, a + * simple XOR between the two handle the addition nicely. + */ + cmdline = &connector->cmdline_mode; + if (cmdline->specified) { + unsigned int cmdline_rest, panel_rest; + unsigned int cmdline_rot, panel_rot; + unsigned int sum_rot, sum_rest; + + panel_rot = ilog2(*rotation & DRM_MODE_ROTATE_MASK); + cmdline_rot = ilog2(cmdline->rotation_reflection & DRM_MODE_ROTATE_MASK); + sum_rot = (panel_rot + cmdline_rot) % 4; + + panel_rest = *rotation & ~DRM_MODE_ROTATE_MASK; + cmdline_rest = cmdline->rotation_reflection & ~DRM_MODE_ROTATE_MASK; + sum_rest = panel_rest ^ cmdline_rest; + + *rotation = (1 << sum_rot) | sum_rest; + } + /* * TODO: support 90 / 270 degree hardware rotation, * depending on the hardware this may require the framebuffer diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 429d3be17800..dc6d11292685 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1554,6 +1554,71 @@ static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length, return 0; } +static int drm_mode_parse_cmdline_options(char *str, size_t len, + struct drm_connector *connector, + struct drm_cmdline_mode *mode) +{ + unsigned int rotation = 0; + char *sep = str; + + while ((sep = strchr(sep, ','))) { + char *delim, *option; + + option = sep + 1; + delim = strchr(option, '='); + if (!delim) { + delim = strchr(option, ','); + + if (!delim) + delim = str + len; + } + + if (!strncmp(option, "rotate", delim - option)) { + const char *value = delim + 1; + unsigned int deg; + + deg = simple_strtol(value, &sep, 10); + + /* Make sure we have parsed something */ + if (sep == value) + return -EINVAL; + + switch (deg) { + case 0: + rotation |= DRM_MODE_ROTATE_0; + break; + + case 90: + rotation |= DRM_MODE_ROTATE_90; + break; + + case 180: + rotation |= DRM_MODE_ROTATE_180; + break; + + case 270: + rotation |= DRM_MODE_ROTATE_270; + break; + + default: + return -EINVAL; + } + } else if (!strncmp(option, "reflect_x", delim - option)) { + rotation |= DRM_MODE_REFLECT_X; + sep = delim; + } else if (!strncmp(option, "reflect_y", delim - option)) { + rotation |= DRM_MODE_REFLECT_Y; + sep = delim; + } else { + return -EINVAL; + } + } + + mode->rotation_reflection = rotation; + + return 0; +} + /** * drm_mode_parse_command_line_for_connector - parse command line modeline for connector * @mode_option: optional per connector mode option @@ -1569,6 +1634,10 @@ static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length, * * x[M][R][-][@][i][m][eDd] * + * Additionals options can be provided following the mode, using a comma to + * separate each option. Valid options can be found in + * Documentation/fb/modedb.txt. + * * The intermediate drm_cmdline_mode structure is required to store additional * options from the command line modline like the force-enable/disable flag. * @@ -1581,9 +1650,10 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, { const char *name; bool named_mode = false, parse_extras = false; - unsigned int bpp_off = 0, refresh_off = 0; + unsigned int bpp_off = 0, refresh_off = 0, options_off = 0; unsigned int mode_end = 0; char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL; + char *options_ptr = NULL; char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL; int ret; @@ -1632,13 +1702,18 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, mode->refresh_specified = true; } + /* Locate the start of named options */ + options_ptr = strchr(name, ','); + if (options_ptr) + options_off = options_ptr - name; + /* Locate the end of the name / resolution, and parse it */ - if (bpp_ptr && refresh_ptr) { - mode_end = min(bpp_off, refresh_off); - } else if (bpp_ptr) { + if (bpp_ptr) { mode_end = bpp_off; } else if (refresh_ptr) { mode_end = refresh_off; + } else if (options_ptr) { + mode_end = options_off; } else { mode_end = strlen(name); parse_extras = true; @@ -1680,24 +1755,23 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, else if (refresh_ptr) extra_ptr = refresh_end_ptr; - if (extra_ptr) { - if (!named_mode) { - int len = strlen(name) - (extra_ptr - name); + if (extra_ptr && + extra_ptr != options_ptr) { + int len = strlen(name) - (extra_ptr - name); - ret = drm_mode_parse_cmdline_extra(extra_ptr, len, - connector, mode); - if (ret) - return false; - } else { - int remaining = strlen(name) - (extra_ptr - name); + ret = drm_mode_parse_cmdline_extra(extra_ptr, len, + connector, mode); + if (ret) + return false; + } - /* - * We still have characters to process, while - * we shouldn't have any - */ - if (remaining > 0) - return false; - } + if (options_ptr) { + int len = strlen(name) - (options_ptr - name); + + ret = drm_mode_parse_cmdline_options(options_ptr, len, + connector, mode); + if (ret) + return false; } return true; diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index cdf2fb910010..8eebe0432c73 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1025,6 +1025,16 @@ struct drm_cmdline_mode { * state to one of the DRM_FORCE_* values. */ enum drm_connector_force force; + + /** + * @rotation_reflection: + * + * Initial rotation and reflection of the mode setup from the + * command line. See DRM_MODE_ROTATE_* and + * DRM_MODE_REFLECT_*. The only rotations supported are + * DRM_MODE_ROTATE_0 and DRM_MODE_ROTATE_180. + */ + unsigned int rotation_reflection; }; /** -- cgit v1.2.3 From 22045e8e52bd802f743f0471242782fc3b479707 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 19 Jun 2019 12:17:51 +0200 Subject: drm/connector: Introduce a TV margins structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TV margins has been defined as a structure inside the drm_connector_state structure so far. However, we will need it in other structures as well, so let's move that structure definition so that it can be reused. Reviewed-by: Noralf Trønnes Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/38b773b03f15ec7a135cdf8f7db669e5ada20cf2.1560783090.git-series.maxime.ripard@bootlin.com --- include/drm/drm_connector.h | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 8eebe0432c73..b22e3150e33d 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -463,14 +463,38 @@ int drm_display_info_set_bus_formats(struct drm_display_info *info, const u32 *formats, unsigned int num_formats); +/** + * struct drm_connector_tv_margins - TV connector related margins + * + * Describes the margins in pixels to put around the image on TV + * connectors to deal with overscan. + */ +struct drm_connector_tv_margins { + /** + * @bottom: Bottom margin in pixels. + */ + unsigned int bottom; + + /** + * @left: Left margin in pixels. + */ + unsigned int left; + + /** + * @right: Right margin in pixels. + */ + unsigned int right; + + /** + * @top: Top margin in pixels. + */ + unsigned int top; +}; + /** * struct drm_tv_connector_state - TV connector related states * @subconnector: selected subconnector - * @margins: margins (all margins are expressed in pixels) - * @margins.left: left margin - * @margins.right: right margin - * @margins.top: top margin - * @margins.bottom: bottom margin + * @margins: TV margins * @mode: TV mode * @brightness: brightness in percent * @contrast: contrast in percent @@ -481,12 +505,7 @@ int drm_display_info_set_bus_formats(struct drm_display_info *info, */ struct drm_tv_connector_state { enum drm_mode_subconnector subconnector; - struct { - unsigned int left; - unsigned int right; - unsigned int top; - unsigned int bottom; - } margins; + struct drm_connector_tv_margins margins; unsigned int mode; unsigned int brightness; unsigned int contrast; -- cgit v1.2.3 From 3d46a3007cd8f73bae502bf5c171977b91d7aacc Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 19 Jun 2019 12:17:51 +0200 Subject: drm/modes: Parse overscan properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Properly configuring the overscan properties might be needed for the initial setup of the framebuffer for display that still have overscan. Let's allow for more properties on the kernel command line to setup each margin. Reviewed-by: Noralf Trønnes Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/e481f1628e3768ca49226ec2115cfa4dfcbd5e4c.1560783090.git-series.maxime.ripard@bootlin.com --- Documentation/fb/modedb.txt | 2 ++ drivers/gpu/drm/drm_modes.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 5 +++++ 3 files changed, 51 insertions(+) (limited to 'include/drm') diff --git a/Documentation/fb/modedb.txt b/Documentation/fb/modedb.txt index 52418c6dbfc4..1dd5a52f9390 100644 --- a/Documentation/fb/modedb.txt +++ b/Documentation/fb/modedb.txt @@ -57,6 +57,8 @@ Options can also be passed after the mode, using commas as separator. Valid options are: + - margin_top, margin_bottom, margin_left, margin_right (integer): + Number of pixels in the margins, typically to deal with overscan on TVs - reflect_x (boolean): Perform an axial symmetry on the X axis - reflect_y (boolean): Perform an axial symmetry on the Y axis - rotate (integer): Rotate the initial framebuffer by x diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index dc6d11292685..57e6408288c8 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1609,6 +1609,50 @@ static int drm_mode_parse_cmdline_options(char *str, size_t len, } else if (!strncmp(option, "reflect_y", delim - option)) { rotation |= DRM_MODE_REFLECT_Y; sep = delim; + } else if (!strncmp(option, "margin_right", delim - option)) { + const char *value = delim + 1; + unsigned int margin; + + margin = simple_strtol(value, &sep, 10); + + /* Make sure we have parsed something */ + if (sep == value) + return -EINVAL; + + mode->tv_margins.right = margin; + } else if (!strncmp(option, "margin_left", delim - option)) { + const char *value = delim + 1; + unsigned int margin; + + margin = simple_strtol(value, &sep, 10); + + /* Make sure we have parsed something */ + if (sep == value) + return -EINVAL; + + mode->tv_margins.left = margin; + } else if (!strncmp(option, "margin_top", delim - option)) { + const char *value = delim + 1; + unsigned int margin; + + margin = simple_strtol(value, &sep, 10); + + /* Make sure we have parsed something */ + if (sep == value) + return -EINVAL; + + mode->tv_margins.top = margin; + } else if (!strncmp(option, "margin_bottom", delim - option)) { + const char *value = delim + 1; + unsigned int margin; + + margin = simple_strtol(value, &sep, 10); + + /* Make sure we have parsed something */ + if (sep == value) + return -EINVAL; + + mode->tv_margins.bottom = margin; } else { return -EINVAL; } diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index b22e3150e33d..ca745d9feaf5 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1054,6 +1054,11 @@ struct drm_cmdline_mode { * DRM_MODE_ROTATE_0 and DRM_MODE_ROTATE_180. */ unsigned int rotation_reflection; + + /** + * @tv_margins: TV margins to apply to the mode. + */ + struct drm_connector_tv_margins tv_margins; }; /** -- cgit v1.2.3 From 731514b446fe6748d5a55a3dff202efb45c7d8df Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 19 Jun 2019 12:17:52 +0200 Subject: drm/atomic: Add a function to reset connector TV properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During the connector reset, if that connector has a TV property, it needs to be reset to the value provided on the command line. Provide a helper to do that. Reviewed-by: Noralf Trønnes Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/84a7b657f09303a2850e1cc79e68f623547f3fdd.1560783090.git-series.maxime.ripard@bootlin.com --- drivers/gpu/drm/drm_atomic_state_helper.c | 18 ++++++++++++++++++ include/drm/drm_atomic_state_helper.h | 1 + 2 files changed, 19 insertions(+) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 7d7347a6f194..46dc264a248b 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -379,6 +379,24 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector) } EXPORT_SYMBOL(drm_atomic_helper_connector_reset); +/** + * drm_atomic_helper_connector_tv_reset - Resets TV connector properties + * @connector: DRM connector + * + * Resets the TV-related properties attached to a connector. + */ +void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector) +{ + struct drm_cmdline_mode *cmdline = &connector->cmdline_mode; + struct drm_connector_state *state = connector->state; + + state->tv.margins.left = cmdline->tv_margins.left; + state->tv.margins.right = cmdline->tv_margins.right; + state->tv.margins.top = cmdline->tv_margins.top; + state->tv.margins.bottom = cmdline->tv_margins.bottom; +} +EXPORT_SYMBOL(drm_atomic_helper_connector_tv_reset); + /** * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state * @connector: connector object diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index 4e6d2e7a40b8..e4577cc11689 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -62,6 +62,7 @@ void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, void __drm_atomic_helper_connector_reset(struct drm_connector *connector, struct drm_connector_state *conn_state); void drm_atomic_helper_connector_reset(struct drm_connector *connector); +void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector); void __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, struct drm_connector_state *state); -- cgit v1.2.3 From 852a6626d5fdd5dd442e6c6ab51ce0cb022d75b4 Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Tue, 18 Jul 2017 19:27:55 +0800 Subject: drm/amdgpu: add navi10 asic type Signed-off-by: Huang Rui Reviewed-by: Alex Deucher Reviewed-by: Hawking Zhang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 1 + include/drm/amd_asic_type.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include/drm') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index e6ddd30f31a6..4c40de13dd29 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -95,6 +95,7 @@ static const char *amdgpu_asic_name[] = { "VEGA12", "VEGA20", "RAVEN", + "NAVI10", "LAST", }; diff --git a/include/drm/amd_asic_type.h b/include/drm/amd_asic_type.h index dd63d08cc54e..bcc2bcf32886 100644 --- a/include/drm/amd_asic_type.h +++ b/include/drm/amd_asic_type.h @@ -49,6 +49,7 @@ enum amd_asic_type { CHIP_VEGA12, CHIP_VEGA20, CHIP_RAVEN, + CHIP_NAVI10, CHIP_LAST, }; -- cgit v1.2.3 From d7cd0e053b17dfa0dd4669dfb388c100be272823 Mon Sep 17 00:00:00 2001 From: Nikola Cornij Date: Mon, 15 Apr 2019 17:31:44 -0400 Subject: drm/amd/display: Add 170Mpix/sec DSC throughput support [why] It was missing, although defined in DP spec [how] - Add handling of this value to DSC code - Also remove unused file dsc_helpers.c Signed-off-by: Nikola Cornij Reviewed-by: Joshua Aberback Acked-by: Bhawanpreet Lakha Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c | 10 +- drivers/gpu/drm/amd/display/dc/dsc/dsc_helpers.c | 243 ----------------------- include/drm/drm_dp_helper.h | 4 + 3 files changed, 12 insertions(+), 245 deletions(-) delete mode 100644 drivers/gpu/drm/amd/display/dc/dsc/dsc_helpers.c (limited to 'include/drm') diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c index d58d718171b5..f09f23707a94 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c @@ -74,6 +74,12 @@ static bool dsc_line_buff_depth_from_dpcd(int dpcd_line_buff_bit_depth, int *lin static bool dsc_throughput_from_dpcd(int dpcd_throughput, int *throughput) { switch (dpcd_throughput) { + case DP_DSC_THROUGHPUT_MODE_0_UPSUPPORTED: + *throughput = 0; + break; + case DP_DSC_THROUGHPUT_MODE_0_170: + *throughput = 170; + break; case DP_DSC_THROUGHPUT_MODE_0_340: *throughput = 340; break; @@ -170,7 +176,7 @@ static void get_dsc_enc_caps( /* Returns 'false' if no intersection was found for at least one capablity. * It also implicitly validates some sink caps against invalid value of zero. */ -static bool dc_intersect_dsc_caps( +static bool intersect_dsc_caps( const struct dsc_dec_dpcd_caps *dsc_sink_caps, const struct dsc_enc_caps *dsc_enc_caps, enum dc_pixel_encoding pixel_encoding, @@ -537,7 +543,7 @@ static bool setup_dsc_config( goto done; // Intersect decoder with encoder DSC caps and validate DSC settings - is_dsc_possible = dc_intersect_dsc_caps(dsc_sink_caps, dsc_enc_caps, timing->pixel_encoding, &dsc_common_caps); + is_dsc_possible = intersect_dsc_caps(dsc_sink_caps, dsc_enc_caps, timing->pixel_encoding, &dsc_common_caps); if (!is_dsc_possible) goto done; diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dsc_helpers.c b/drivers/gpu/drm/amd/display/dc/dsc/dsc_helpers.c deleted file mode 100644 index 0ecd5065d120..000000000000 --- a/drivers/gpu/drm/amd/display/dc/dsc/dsc_helpers.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright 2018 Advanced Micro Devices, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: AMD - * - */ - -#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT - -#include "dc.h" -#include "dsc.h" -#include "dc_hw_types.h" -#include - -#define DC_LOGGER \ - dsc->ctx->logger - -static bool dsc_buff_block_size_from_dpcd(int dpcd_buff_block_size, int *buff_block_size); -static bool dsc_line_buff_depth_from_dpcd(int dpcd_line_buff_bit_depth, int *line_buff_bit_depth); -static bool dsc_throughput_from_dpcd(int dpcd_throughput, int *throughput); -static bool dsc_bpp_increment_div_from_dpcd(int bpp_increment_dpcd, uint32_t *bpp_increment_div); - -void dsc_optc_config_log(struct display_stream_compressor *dsc, - struct dsc_optc_config *config) -{ - DC_LOG_DSC("Setting optc DSC config at DSC inst %d", dsc->inst); - DC_LOG_DSC("\n\tbytes_per_pixel %d\n\tis_pixel_format_444 %d\n\tslice_width %d", - config->bytes_per_pixel, - config->is_pixel_format_444, config->slice_width); -} - -void dsc_config_log(struct display_stream_compressor *dsc, - const struct dsc_config *config) -{ - DC_LOG_DSC("Setting DSC Config at DSC inst %d", dsc->inst); - DC_LOG_DSC("\n\tnum_slices_h %d\n\tnum_slices_v %d\n\tbits_per_pixel %d\n\tcolor_depth %d", - config->dc_dsc_cfg.num_slices_h, - config->dc_dsc_cfg.num_slices_v, - config->dc_dsc_cfg.bits_per_pixel, - config->color_depth); -} - - -bool dsc_parse_dsc_dpcd(const uint8_t *dpcd_dsc_data, struct dsc_dec_dpcd_caps *dsc_sink_caps) -{ - dsc_sink_caps->is_dsc_supported = (dpcd_dsc_data[DP_DSC_SUPPORT - DP_DSC_SUPPORT] & DP_DSC_DECOMPRESSION_IS_SUPPORTED) != 0; - if (!dsc_sink_caps->is_dsc_supported) - return true; - - dsc_sink_caps->dsc_version = dpcd_dsc_data[DP_DSC_REV - DP_DSC_SUPPORT]; - - { - int buff_block_size; - int buff_size; - - if (!dsc_buff_block_size_from_dpcd(dpcd_dsc_data[DP_DSC_RC_BUF_BLK_SIZE - DP_DSC_SUPPORT], &buff_block_size)) - return false; - - buff_size = dpcd_dsc_data[DP_DSC_RC_BUF_SIZE - DP_DSC_SUPPORT] + 1; - dsc_sink_caps->rc_buffer_size = buff_size * buff_block_size; - } - - dsc_sink_caps->slice_caps1.raw = dpcd_dsc_data[DP_DSC_SLICE_CAP_1 - DP_DSC_SUPPORT]; - if (!dsc_line_buff_depth_from_dpcd(dpcd_dsc_data[DP_DSC_LINE_BUF_BIT_DEPTH - DP_DSC_SUPPORT], &dsc_sink_caps->lb_bit_depth)) - return false; - - dsc_sink_caps->is_block_pred_supported = - (dpcd_dsc_data[DP_DSC_BLK_PREDICTION_SUPPORT - DP_DSC_SUPPORT] & DP_DSC_BLK_PREDICTION_IS_SUPPORTED) != 0; - - dsc_sink_caps->edp_max_bits_per_pixel = - dpcd_dsc_data[DP_DSC_MAX_BITS_PER_PIXEL_LOW - DP_DSC_SUPPORT] | - dpcd_dsc_data[DP_DSC_MAX_BITS_PER_PIXEL_HI - DP_DSC_SUPPORT] << 8; - - dsc_sink_caps->color_formats.raw = dpcd_dsc_data[DP_DSC_DEC_COLOR_FORMAT_CAP - DP_DSC_SUPPORT]; - dsc_sink_caps->color_depth.raw = dpcd_dsc_data[DP_DSC_DEC_COLOR_DEPTH_CAP - DP_DSC_SUPPORT]; - - { - int dpcd_throughput = dpcd_dsc_data[DP_DSC_PEAK_THROUGHPUT - DP_DSC_SUPPORT]; - - if (!dsc_throughput_from_dpcd(dpcd_throughput & DP_DSC_THROUGHPUT_MODE_0_MASK, &dsc_sink_caps->throughput_mode_0_mps)) - return false; - - dpcd_throughput = (dpcd_throughput & DP_DSC_THROUGHPUT_MODE_1_MASK) >> DP_DSC_THROUGHPUT_MODE_1_SHIFT; - if (!dsc_throughput_from_dpcd(dpcd_throughput, &dsc_sink_caps->throughput_mode_1_mps)) - return false; - } - - dsc_sink_caps->max_slice_width = dpcd_dsc_data[DP_DSC_MAX_SLICE_WIDTH - DP_DSC_SUPPORT] * 320; - dsc_sink_caps->slice_caps2.raw = dpcd_dsc_data[DP_DSC_SLICE_CAP_2 - DP_DSC_SUPPORT]; - - if (!dsc_bpp_increment_div_from_dpcd(dpcd_dsc_data[DP_DSC_BITS_PER_PIXEL_INC - DP_DSC_SUPPORT], &dsc_sink_caps->bpp_increment_div)) - return false; - - return true; -} - - -/* This module's internal functions */ - -static bool dsc_buff_block_size_from_dpcd(int dpcd_buff_block_size, int *buff_block_size) -{ - - switch (dpcd_buff_block_size) { - case DP_DSC_RC_BUF_BLK_SIZE_1: - *buff_block_size = 1024; - break; - case DP_DSC_RC_BUF_BLK_SIZE_4: - *buff_block_size = 4 * 1024; - break; - case DP_DSC_RC_BUF_BLK_SIZE_16: - *buff_block_size = 16 * 1024; - break; - case DP_DSC_RC_BUF_BLK_SIZE_64: - *buff_block_size = 64 * 1024; - break; - default: { - dm_error("%s: DPCD DSC buffer size not recoginzed.\n", __func__); - return false; - } - } - - return true; -} - - -static bool dsc_line_buff_depth_from_dpcd(int dpcd_line_buff_bit_depth, int *line_buff_bit_depth) -{ - if (0 <= dpcd_line_buff_bit_depth && dpcd_line_buff_bit_depth <= 7) - *line_buff_bit_depth = dpcd_line_buff_bit_depth + 9; - else if (dpcd_line_buff_bit_depth == 8) - *line_buff_bit_depth = 8; - else { - dm_error("%s: DPCD DSC buffer depth not recoginzed.\n", __func__); - return false; - } - - return true; -} - - -static bool dsc_throughput_from_dpcd(int dpcd_throughput, int *throughput) -{ - switch (dpcd_throughput) { - case DP_DSC_THROUGHPUT_MODE_0_340: - *throughput = 340; - break; - case DP_DSC_THROUGHPUT_MODE_0_400: - *throughput = 400; - break; - case DP_DSC_THROUGHPUT_MODE_0_450: - *throughput = 450; - break; - case DP_DSC_THROUGHPUT_MODE_0_500: - *throughput = 500; - break; - case DP_DSC_THROUGHPUT_MODE_0_550: - *throughput = 550; - break; - case DP_DSC_THROUGHPUT_MODE_0_600: - *throughput = 600; - break; - case DP_DSC_THROUGHPUT_MODE_0_650: - *throughput = 650; - break; - case DP_DSC_THROUGHPUT_MODE_0_700: - *throughput = 700; - break; - case DP_DSC_THROUGHPUT_MODE_0_750: - *throughput = 750; - break; - case DP_DSC_THROUGHPUT_MODE_0_800: - *throughput = 800; - break; - case DP_DSC_THROUGHPUT_MODE_0_850: - *throughput = 850; - break; - case DP_DSC_THROUGHPUT_MODE_0_900: - *throughput = 900; - break; - case DP_DSC_THROUGHPUT_MODE_0_950: - *throughput = 950; - break; - case DP_DSC_THROUGHPUT_MODE_0_1000: - *throughput = 1000; - break; - default: { - dm_error("%s: DPCD DSC througput mode not recoginzed.\n", __func__); - return false; - } - } - - return true; -} - - -static bool dsc_bpp_increment_div_from_dpcd(int bpp_increment_dpcd, uint32_t *bpp_increment_div) -{ - - switch (bpp_increment_dpcd) { - case 0: - *bpp_increment_div = 16; - break; - case 1: - *bpp_increment_div = 8; - break; - case 2: - *bpp_increment_div = 4; - break; - case 3: - *bpp_increment_div = 2; - break; - case 4: - *bpp_increment_div = 1; - break; - default: { - dm_error("%s: DPCD DSC bits-per-pixel increment not recoginzed.\n", __func__); - return false; - } - } - - return true; -} - - -#endif // CONFIG_DRM_AMD_DC_DSC_SUPPORT diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 3fc534ee8174..c0c947a42a59 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -249,6 +249,7 @@ #define DP_DSC_PEAK_THROUGHPUT 0x06B # define DP_DSC_THROUGHPUT_MODE_0_MASK (0xf << 0) # define DP_DSC_THROUGHPUT_MODE_0_SHIFT 0 +# define DP_DSC_THROUGHPUT_MODE_0_UPSUPPORTED 0 # define DP_DSC_THROUGHPUT_MODE_0_340 (1 << 0) # define DP_DSC_THROUGHPUT_MODE_0_400 (2 << 0) # define DP_DSC_THROUGHPUT_MODE_0_450 (3 << 0) @@ -263,8 +264,10 @@ # define DP_DSC_THROUGHPUT_MODE_0_900 (12 << 0) # define DP_DSC_THROUGHPUT_MODE_0_950 (13 << 0) # define DP_DSC_THROUGHPUT_MODE_0_1000 (14 << 0) +# define DP_DSC_THROUGHPUT_MODE_0_170 (15 << 4) # define DP_DSC_THROUGHPUT_MODE_1_MASK (0xf << 4) # define DP_DSC_THROUGHPUT_MODE_1_SHIFT 4 +# define DP_DSC_THROUGHPUT_MODE_1_UPSUPPORTED 0 # define DP_DSC_THROUGHPUT_MODE_1_340 (1 << 4) # define DP_DSC_THROUGHPUT_MODE_1_400 (2 << 4) # define DP_DSC_THROUGHPUT_MODE_1_450 (3 << 4) @@ -279,6 +282,7 @@ # define DP_DSC_THROUGHPUT_MODE_1_900 (12 << 4) # define DP_DSC_THROUGHPUT_MODE_1_950 (13 << 4) # define DP_DSC_THROUGHPUT_MODE_1_1000 (14 << 4) +# define DP_DSC_THROUGHPUT_MODE_1_170 (15 << 4) #define DP_DSC_MAX_SLICE_WIDTH 0x06C #define DP_DSC_MIN_SLICE_WIDTH_VALUE 2560 -- cgit v1.2.3 From f446489adcbc9c4833ae724a985166731c577bcd Mon Sep 17 00:00:00 2001 From: Nikola Cornij Date: Wed, 17 Apr 2019 19:07:08 -0400 Subject: drm/amd/display: Add support for extended DSC DPCD caps [why] A few of the new DSC DPCD caps were introduced by a DP 1.4a SCR in order to give DSC branch decoders a chance to expose their maximum throughput and maximum line width limitations. Signed-off-by: Nikola Cornij Reviewed-by: Wenjing Liu Acked-by: Bhawanpreet Lakha Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c | 50 +++--- drivers/gpu/drm/amd/display/dc/dc_dsc.h | 3 +- drivers/gpu/drm/amd/display/dc/dc_types.h | 5 + drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c | 118 ++++++++++----- drivers/gpu/drm/amd/display/include/dpcd_structs.h | 168 +++++++++++++++++++++ include/drm/drm_dp_helper.h | 5 + 6 files changed, 289 insertions(+), 60 deletions(-) create mode 100644 drivers/gpu/drm/amd/display/include/dpcd_structs.h (limited to 'include/drm') diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c index effc36745671..017f88c9f2e4 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c @@ -2383,8 +2383,8 @@ static bool retrieve_link_cap(struct dc_link *link) int i; struct dp_sink_hw_fw_revision dp_hw_fw_revision; #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT - uint8_t dsc_data[16]; - struct dsc_dec_dpcd_caps *dsc_caps; + uint8_t dsc_data[16]; /* DP_DSC_BITS_PER_PIXEL_INC - DP_DSC_SUPPORT + 1 == 16 */ + struct dsc_dec_dpcd_caps *dsc_dec_caps; #endif memset(dpcd_data, '\0', sizeof(dpcd_data)); @@ -2558,8 +2558,8 @@ static bool retrieve_link_cap(struct dc_link *link) sizeof(dp_hw_fw_revision.ieee_fw_rev)); #ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT - dsc_caps = &link->dpcd_caps.dsc_sink_caps; - memset(dsc_caps, '\0', sizeof(*dsc_caps)); + dsc_dec_caps = &link->dpcd_caps.dsc_sink_caps; + memset(dsc_dec_caps, '\0', sizeof(*dsc_dec_caps)); memset(&link->dpcd_caps.dsc_sink_caps, '\0', sizeof(link->dpcd_caps.dsc_sink_caps)); memset(&link->dpcd_caps.fec_cap, '\0', sizeof(link->dpcd_caps.fec_cap)); @@ -2571,7 +2571,7 @@ static bool retrieve_link_cap(struct dc_link *link) dsc_data, sizeof(dsc_data)); if (status == DC_OK) { - DC_LOG_DSC("DSC capability read at link %d:", + DC_LOG_DSC("DSC DPCD capability read at link %d:", link->link_index); DC_LOG_DSC("\t%02x %02x %02x %02x", dsc_data[0], dsc_data[1], @@ -2590,37 +2590,43 @@ static bool retrieve_link_cap(struct dc_link *link) return false; } - if (dc_dsc_parse_dsc_dpcd(dsc_data, - dsc_caps)) { - DC_LOG_DSC("DSC capability parsed at link %d:", + if (dc_dsc_parse_dsc_dpcd(dsc_data, NULL, + dsc_dec_caps)) { + DC_LOG_DSC("DSC DPCD capabilities parsed at link %d:", link->link_index); DC_LOG_DSC("\tis_dsc_supported:\t%d", - dsc_caps->is_dsc_supported); - DC_LOG_DSC("\tdsc_version:\t%d", dsc_caps->dsc_version); + dsc_dec_caps->is_dsc_supported); + DC_LOG_DSC("\tdsc_version:\t%d", dsc_dec_caps->dsc_version); DC_LOG_DSC("\trc_buffer_size:\t%d", - dsc_caps->rc_buffer_size); + dsc_dec_caps->rc_buffer_size); DC_LOG_DSC("\tslice_caps1:\t0x%x20", - dsc_caps->slice_caps1.raw); + dsc_dec_caps->slice_caps1.raw); DC_LOG_DSC("\tslice_caps2:\t0x%x20", - dsc_caps->slice_caps2.raw); + dsc_dec_caps->slice_caps2.raw); DC_LOG_DSC("\tlb_bit_depth:\t%d", - dsc_caps->lb_bit_depth); + dsc_dec_caps->lb_bit_depth); DC_LOG_DSC("\tis_block_pred_supported:\t%d", - dsc_caps->is_block_pred_supported); + dsc_dec_caps->is_block_pred_supported); DC_LOG_DSC("\tedp_max_bits_per_pixel:\t%d", - dsc_caps->edp_max_bits_per_pixel); + dsc_dec_caps->edp_max_bits_per_pixel); DC_LOG_DSC("\tcolor_formats:\t%d", - dsc_caps->color_formats.raw); + dsc_dec_caps->color_formats.raw); DC_LOG_DSC("\tcolor_depth:\t%d", - dsc_caps->color_depth.raw); + dsc_dec_caps->color_depth.raw); DC_LOG_DSC("\tthroughput_mode_0_mps:\t%d", - dsc_caps->throughput_mode_0_mps); + dsc_dec_caps->throughput_mode_0_mps); DC_LOG_DSC("\tthroughput_mode_1_mps:\t%d", - dsc_caps->throughput_mode_1_mps); + dsc_dec_caps->throughput_mode_1_mps); DC_LOG_DSC("\tmax_slice_width:\t%d", - dsc_caps->max_slice_width); + dsc_dec_caps->max_slice_width); DC_LOG_DSC("\tbpp_increment_div:\t%d", - dsc_caps->bpp_increment_div); + dsc_dec_caps->bpp_increment_div); + DC_LOG_DSC("\tbranch_overall_throughput_0_mps:\t%d", + dsc_dec_caps->branch_overall_throughput_0_mps); + DC_LOG_DSC("\tbranch_overall_throughput_1_mps:\t%d", + dsc_dec_caps->branch_overall_throughput_1_mps); + DC_LOG_DSC("\tbranch_max_line_width:\t%d", + dsc_dec_caps->branch_max_line_width); } else { /* Some sinks return bogus DSC DPCD data * when they don't support DSC. diff --git a/drivers/gpu/drm/amd/display/dc/dc_dsc.h b/drivers/gpu/drm/amd/display/dc/dc_dsc.h index be0f7b09086a..6de3bc9162ea 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dsc.h +++ b/drivers/gpu/drm/amd/display/dc/dc_dsc.h @@ -34,7 +34,8 @@ struct dc_dsc_bw_range { }; -bool dc_dsc_parse_dsc_dpcd(const uint8_t *dpcd_dsc_data, +bool dc_dsc_parse_dsc_dpcd(const uint8_t *dpcd_dsc_basic_data, + const uint8_t *dpcd_dsc_ext_data, struct dsc_dec_dpcd_caps *dsc_sink_caps); bool dc_dsc_compute_bandwidth_range( diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h index b7e2c6f767aa..5984be3cdf0c 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_types.h @@ -773,6 +773,11 @@ struct dsc_dec_dpcd_caps { int32_t throughput_mode_1_mps; /* In MPs */ int32_t max_slice_width; uint32_t bpp_increment_div; /* bpp increment divisor, e.g. if 16, it's 1/16th of a bit */ + + /* Extended DSC caps */ + uint32_t branch_overall_throughput_0_mps; /* In MPs */ + uint32_t branch_overall_throughput_1_mps; /* In MPs */ + uint32_t branch_max_line_width; }; #endif #endif /* DC_TYPES_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c index f09f23707a94..94a623dc37f4 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c @@ -252,7 +252,7 @@ struct dc_dsc_policy { int min_target_bpp; // Minimum target bits per pixel }; -static inline uint32_t dsc_round_up(uint32_t value) +static inline uint32_t dsc_div_by_10_round_up(uint32_t value) { return (value + 9) / 10; } @@ -304,7 +304,7 @@ static void get_dsc_bandwidth_range( range->stream_kbps = dc_bandwidth_in_kbps_from_timing(timing); /* max dsc target bpp */ - range->max_kbps = dsc_round_up(max_bpp * timing->pix_clk_100hz); + range->max_kbps = dsc_div_by_10_round_up(max_bpp * timing->pix_clk_100hz); range->max_target_bpp_x16 = max_bpp * 16; if (range->max_kbps > range->stream_kbps) { /* max dsc target bpp is capped to native bandwidth */ @@ -313,7 +313,7 @@ static void get_dsc_bandwidth_range( } /* min dsc target bpp */ - range->min_kbps = dsc_round_up(min_bpp * timing->pix_clk_100hz); + range->min_kbps = dsc_div_by_10_round_up(min_bpp * timing->pix_clk_100hz); range->min_target_bpp_x16 = min_bpp * 16; if (range->min_kbps > range->max_kbps) { /* min dsc target bpp is capped to max dsc bandwidth*/ @@ -532,16 +532,23 @@ static bool setup_dsc_config( int pic_width; int slice_width; int target_bpp; - int sink_per_slice_throughput; + int sink_per_slice_throughput_mps; + int branch_max_throughput_mps = 0; bool is_dsc_possible = false; int num_slices_v; int pic_height; memset(dsc_cfg, 0, sizeof(struct dc_dsc_config)); + pic_width = timing->h_addressable + timing->h_border_left + timing->h_border_right; + pic_height = timing->v_addressable + timing->v_border_top + timing->v_border_bottom; + if (!dsc_sink_caps->is_dsc_supported) goto done; + if (dsc_sink_caps->branch_max_line_width && dsc_sink_caps->branch_max_line_width < pic_width) + goto done; + // Intersect decoder with encoder DSC caps and validate DSC settings is_dsc_possible = intersect_dsc_caps(dsc_sink_caps, dsc_enc_caps, timing->pixel_encoding, &dsc_common_caps); if (!is_dsc_possible) @@ -554,39 +561,46 @@ static bool setup_dsc_config( if (!is_dsc_possible) goto done; - sink_per_slice_throughput = 0; + sink_per_slice_throughput_mps = 0; // Validate available DSC settings against the mode timing - // Color format + // Validate color format (and pick up the throughput values) dsc_cfg->ycbcr422_simple = false; switch (timing->pixel_encoding) { case PIXEL_ENCODING_RGB: is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.RGB; - sink_per_slice_throughput = dsc_sink_caps->throughput_mode_0_mps; + sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_0_mps; + branch_max_throughput_mps = dsc_sink_caps->branch_overall_throughput_0_mps; break; case PIXEL_ENCODING_YCBCR444: is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_444; - sink_per_slice_throughput = dsc_sink_caps->throughput_mode_0_mps; - break; - case PIXEL_ENCODING_YCBCR422: { - is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_NATIVE_422; - sink_per_slice_throughput = dsc_sink_caps->throughput_mode_1_mps; - if (!is_dsc_possible) { - is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_SIMPLE_422; - dsc_cfg->ycbcr422_simple = is_dsc_possible; - sink_per_slice_throughput = dsc_sink_caps->throughput_mode_0_mps; - } + sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_0_mps; + branch_max_throughput_mps = dsc_sink_caps->branch_overall_throughput_0_mps; + break; + case PIXEL_ENCODING_YCBCR422: + is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_NATIVE_422; + sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_1_mps; + branch_max_throughput_mps = dsc_sink_caps->branch_overall_throughput_1_mps; + if (!is_dsc_possible) { + is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_SIMPLE_422; + dsc_cfg->ycbcr422_simple = is_dsc_possible; + sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_0_mps; } break; case PIXEL_ENCODING_YCBCR420: is_dsc_possible = (bool)dsc_common_caps.color_formats.bits.YCBCR_NATIVE_420; - sink_per_slice_throughput = dsc_sink_caps->throughput_mode_1_mps; + sink_per_slice_throughput_mps = dsc_sink_caps->throughput_mode_1_mps; + branch_max_throughput_mps = dsc_sink_caps->branch_overall_throughput_1_mps; break; default: is_dsc_possible = false; } + // Validate branch's maximum throughput + if (branch_max_throughput_mps && dsc_div_by_10_round_up(timing->pix_clk_100hz) > branch_max_throughput_mps * 1000) + is_dsc_possible = false; + if (!is_dsc_possible) goto done; @@ -611,7 +625,6 @@ static bool setup_dsc_config( // DSC slicing max_slices_h = get_max_dsc_slices(dsc_common_caps.slice_caps); - pic_width = timing->h_addressable + timing->h_border_left + timing->h_border_right; while (max_slices_h > 0) { if (pic_width % max_slices_h == 0) break; @@ -630,7 +643,8 @@ static bool setup_dsc_config( min_slices_h = fit_num_slices_up(dsc_common_caps.slice_caps, min_slices_h); while (min_slices_h <= max_slices_h) { - if (dsc_round_up(timing->pix_clk_100hz) / (min_slices_h) <= sink_per_slice_throughput * 1000) + int pix_clk_per_slice_khz = dsc_div_by_10_round_up(timing->pix_clk_100hz) / min_slices_h; + if (pix_clk_per_slice_khz <= sink_per_slice_throughput_mps * 1000) break; min_slices_h = inc_num_slices(dsc_common_caps.slice_caps, min_slices_h); @@ -673,7 +687,6 @@ static bool setup_dsc_config( // Vertical number of slices: start from policy and pick the first one that height is divisible by. // For 4:2:0 make sure the slice height is divisible by 2 as well. - pic_height = timing->v_addressable + timing->v_border_top + timing->v_border_bottom; num_slices_v = dsc_policy.num_slices_v; if (num_slices_v < 1) num_slices_v = 1; @@ -710,41 +723,41 @@ done: return is_dsc_possible; } -bool dc_dsc_parse_dsc_dpcd(const uint8_t *dpcd_dsc_data, struct dsc_dec_dpcd_caps *dsc_sink_caps) +bool dc_dsc_parse_dsc_dpcd(const uint8_t *dpcd_dsc_basic_data, const uint8_t *dpcd_dsc_ext_data, struct dsc_dec_dpcd_caps *dsc_sink_caps) { - dsc_sink_caps->is_dsc_supported = (dpcd_dsc_data[DP_DSC_SUPPORT - DP_DSC_SUPPORT] & DP_DSC_DECOMPRESSION_IS_SUPPORTED) != 0; + dsc_sink_caps->is_dsc_supported = (dpcd_dsc_basic_data[DP_DSC_SUPPORT - DP_DSC_SUPPORT] & DP_DSC_DECOMPRESSION_IS_SUPPORTED) != 0; if (!dsc_sink_caps->is_dsc_supported) return true; - dsc_sink_caps->dsc_version = dpcd_dsc_data[DP_DSC_REV - DP_DSC_SUPPORT]; + dsc_sink_caps->dsc_version = dpcd_dsc_basic_data[DP_DSC_REV - DP_DSC_SUPPORT]; { int buff_block_size; int buff_size; - if (!dsc_buff_block_size_from_dpcd(dpcd_dsc_data[DP_DSC_RC_BUF_BLK_SIZE - DP_DSC_SUPPORT], &buff_block_size)) + if (!dsc_buff_block_size_from_dpcd(dpcd_dsc_basic_data[DP_DSC_RC_BUF_BLK_SIZE - DP_DSC_SUPPORT], &buff_block_size)) return false; - buff_size = dpcd_dsc_data[DP_DSC_RC_BUF_SIZE - DP_DSC_SUPPORT] + 1; + buff_size = dpcd_dsc_basic_data[DP_DSC_RC_BUF_SIZE - DP_DSC_SUPPORT] + 1; dsc_sink_caps->rc_buffer_size = buff_size * buff_block_size; } - dsc_sink_caps->slice_caps1.raw = dpcd_dsc_data[DP_DSC_SLICE_CAP_1 - DP_DSC_SUPPORT]; - if (!dsc_line_buff_depth_from_dpcd(dpcd_dsc_data[DP_DSC_LINE_BUF_BIT_DEPTH - DP_DSC_SUPPORT], &dsc_sink_caps->lb_bit_depth)) + dsc_sink_caps->slice_caps1.raw = dpcd_dsc_basic_data[DP_DSC_SLICE_CAP_1 - DP_DSC_SUPPORT]; + if (!dsc_line_buff_depth_from_dpcd(dpcd_dsc_basic_data[DP_DSC_LINE_BUF_BIT_DEPTH - DP_DSC_SUPPORT], &dsc_sink_caps->lb_bit_depth)) return false; dsc_sink_caps->is_block_pred_supported = - (dpcd_dsc_data[DP_DSC_BLK_PREDICTION_SUPPORT - DP_DSC_SUPPORT] & DP_DSC_BLK_PREDICTION_IS_SUPPORTED) != 0; + (dpcd_dsc_basic_data[DP_DSC_BLK_PREDICTION_SUPPORT - DP_DSC_SUPPORT] & DP_DSC_BLK_PREDICTION_IS_SUPPORTED) != 0; dsc_sink_caps->edp_max_bits_per_pixel = - dpcd_dsc_data[DP_DSC_MAX_BITS_PER_PIXEL_LOW - DP_DSC_SUPPORT] | - dpcd_dsc_data[DP_DSC_MAX_BITS_PER_PIXEL_HI - DP_DSC_SUPPORT] << 8; + dpcd_dsc_basic_data[DP_DSC_MAX_BITS_PER_PIXEL_LOW - DP_DSC_SUPPORT] | + dpcd_dsc_basic_data[DP_DSC_MAX_BITS_PER_PIXEL_HI - DP_DSC_SUPPORT] << 8; - dsc_sink_caps->color_formats.raw = dpcd_dsc_data[DP_DSC_DEC_COLOR_FORMAT_CAP - DP_DSC_SUPPORT]; - dsc_sink_caps->color_depth.raw = dpcd_dsc_data[DP_DSC_DEC_COLOR_DEPTH_CAP - DP_DSC_SUPPORT]; + dsc_sink_caps->color_formats.raw = dpcd_dsc_basic_data[DP_DSC_DEC_COLOR_FORMAT_CAP - DP_DSC_SUPPORT]; + dsc_sink_caps->color_depth.raw = dpcd_dsc_basic_data[DP_DSC_DEC_COLOR_DEPTH_CAP - DP_DSC_SUPPORT]; { - int dpcd_throughput = dpcd_dsc_data[DP_DSC_PEAK_THROUGHPUT - DP_DSC_SUPPORT]; + int dpcd_throughput = dpcd_dsc_basic_data[DP_DSC_PEAK_THROUGHPUT - DP_DSC_SUPPORT]; if (!dsc_throughput_from_dpcd(dpcd_throughput & DP_DSC_THROUGHPUT_MODE_0_MASK, &dsc_sink_caps->throughput_mode_0_mps)) return false; @@ -754,12 +767,43 @@ bool dc_dsc_parse_dsc_dpcd(const uint8_t *dpcd_dsc_data, struct dsc_dec_dpcd_cap return false; } - dsc_sink_caps->max_slice_width = dpcd_dsc_data[DP_DSC_MAX_SLICE_WIDTH - DP_DSC_SUPPORT] * 320; - dsc_sink_caps->slice_caps2.raw = dpcd_dsc_data[DP_DSC_SLICE_CAP_2 - DP_DSC_SUPPORT]; + dsc_sink_caps->max_slice_width = dpcd_dsc_basic_data[DP_DSC_MAX_SLICE_WIDTH - DP_DSC_SUPPORT] * 320; + dsc_sink_caps->slice_caps2.raw = dpcd_dsc_basic_data[DP_DSC_SLICE_CAP_2 - DP_DSC_SUPPORT]; - if (!dsc_bpp_increment_div_from_dpcd(dpcd_dsc_data[DP_DSC_BITS_PER_PIXEL_INC - DP_DSC_SUPPORT], &dsc_sink_caps->bpp_increment_div)) + if (!dsc_bpp_increment_div_from_dpcd(dpcd_dsc_basic_data[DP_DSC_BITS_PER_PIXEL_INC - DP_DSC_SUPPORT], &dsc_sink_caps->bpp_increment_div)) return false; + /* Extended caps */ + if (dpcd_dsc_ext_data == NULL) { // Extended DPCD DSC data can be null, e.g. because it doesn't apply to SST + dsc_sink_caps->branch_overall_throughput_0_mps = 0; + dsc_sink_caps->branch_overall_throughput_1_mps = 0; + dsc_sink_caps->branch_max_line_width = 0; + return true; + } + + dsc_sink_caps->branch_overall_throughput_0_mps = dpcd_dsc_ext_data[DP_DSC_BRANCH_OVERALL_THROUGHPUT_0 - DP_DSC_BRANCH_OVERALL_THROUGHPUT_0]; + if (dsc_sink_caps->branch_overall_throughput_0_mps == 0) + dsc_sink_caps->branch_overall_throughput_0_mps = 0; + else if (dsc_sink_caps->branch_overall_throughput_0_mps == 1) + dsc_sink_caps->branch_overall_throughput_0_mps = 680; + else { + dsc_sink_caps->branch_overall_throughput_0_mps *= 50; + dsc_sink_caps->branch_overall_throughput_0_mps += 600; + } + + dsc_sink_caps->branch_overall_throughput_1_mps = dpcd_dsc_ext_data[DP_DSC_BRANCH_OVERALL_THROUGHPUT_1 - DP_DSC_BRANCH_OVERALL_THROUGHPUT_0]; + if (dsc_sink_caps->branch_overall_throughput_1_mps == 0) + dsc_sink_caps->branch_overall_throughput_1_mps = 0; + else if (dsc_sink_caps->branch_overall_throughput_1_mps == 1) + dsc_sink_caps->branch_overall_throughput_1_mps = 680; + else { + dsc_sink_caps->branch_overall_throughput_1_mps *= 50; + dsc_sink_caps->branch_overall_throughput_1_mps += 600; + } + + dsc_sink_caps->branch_max_line_width = dpcd_dsc_ext_data[DP_DSC_BRANCH_MAX_LINE_WIDTH - DP_DSC_BRANCH_OVERALL_THROUGHPUT_0] * 320; + ASSERT(dsc_sink_caps->branch_max_line_width == 0 || dsc_sink_caps->branch_max_line_width >= 5120); + return true; } diff --git a/drivers/gpu/drm/amd/display/include/dpcd_structs.h b/drivers/gpu/drm/amd/display/include/dpcd_structs.h new file mode 100644 index 000000000000..6f417e0480e6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/include/dpcd_structs.h @@ -0,0 +1,168 @@ +/* + * dpcd_structs.h + * + * Created on: Oct 31, 2018 + * Author: jlei + */ + +#ifndef DAL_INCLUDE_DPCD_STRUCTS_H_ +#define DAL_INCLUDE_DPCD_STRUCTS_H_ + +struct dpcd_receive_port0_cap01 { + union { + struct { + // Byte 0 + unsigned char reserved0 :1; // Bit0 + unsigned char local_edid_present :1; + unsigned char associated_to_preceding_port :1; + unsigned char hblank_expansion_capable :1; + unsigned char buffer_size_unit :1; // Bit4 + unsigned char buffer_size_per_port :1; + unsigned char reserved1 :2; + + // Byte 1 + unsigned char buffer_size :8; + } fields; + unsigned char raw[2]; + }; +}; + +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT + +struct dpcd_dsc_basic_capabilities { + union { + struct { + // Byte 0 + struct { + + unsigned char dsc_support :1; // Bit0 + unsigned char reserved :7; + } dsc_support; + + // Byte 1 + struct { + unsigned char dsc_version_major :4; + unsigned char dsc_version_minor :4; + } dsc_algorithm_revision; + + // Byte 2 + struct { + unsigned char rc_block_buffer_size :2; + unsigned char reserved :6; + } dsc_rc_buffer_block_size; + + // Byte 3 + unsigned char dsc_rc_buffer_size; + + // Byte 4 + struct { + unsigned char one_slice_per_dp_dsc_sink_device :1; // Bit0 + unsigned char two_slices_per_dp_dsc_sink_device :1; + unsigned char reserved :1; + unsigned char four_slices_per_dp_dsc_sink_device :1; + unsigned char six_slices_per_dp_dsc_sink_device :1; // Bit 4 + unsigned char eight_slices_per_dp_dsc_sink_device :1; + unsigned char ten_slices_per_dp_dsc_sink_device :1; + unsigned char twelve_slices_per_dp_dsc_sink_device :1; + } dsc_slice_capabilities_1; + + // Byte 5 + struct { + unsigned char line_buffer_bit_depth :4; + unsigned char reserved :4; + } dsc_line_buffer_bit_depth; + + // Byte 6 + struct { + unsigned char block_prediction_support :1; + unsigned char reserved :7; + } dsc_block_prediction_support; + + // Byte 7,8 + struct { + unsigned char maximum_bits_per_pixel_supported_by_the_decompressor_low :7; + unsigned char maximum_bits_per_pixel_supported_by_the_decompressor_high :7; + } maximum_bits_per_pixel_supported_by_the_decompressor; + + // Byte 9 + struct { + unsigned char rgb_support :1; // Bit0 + unsigned char y_cb_cr_444_support :1; + unsigned char y_cb_cr_simple_422_support :1; + unsigned char y_cb_cr_native_422_support :1; + unsigned char y_cb_cr_native_420_support :1; // Bit 4 + unsigned char reserved :3; + } dsc_decoder_color_format_capabilities; + + // Byte 10 + struct { + unsigned char reserved0 :1; // Bit0 + unsigned char eight_bits_per_color_support :1; + unsigned char ten_bits_per_color_support :1; + unsigned char twelve_bits_per_color_support :1; + unsigned char reserved1 :4; // Bit 4 + } dsc_decoder_color_depth_capabilities; + + // Byte 11 + struct { + unsigned char throughput_mode_0 :4; + unsigned char throughput_mode_1 :4; + } peak_dsc_throughput_dsc_sink; + + // Byte 12 + unsigned char dsc_maximum_slice_width; + + // Byte 13 + struct { + unsigned char sixteen_slices_per_dsc_sink_device :1; + unsigned char twenty_slices_per_dsc_sink_device :1; + unsigned char twentyfour_slices_per_dsc_sink_device :1; + unsigned char reserved :5; + } dsc_slice_capabilities_2; + + // Byte 14 + unsigned char reserved; + + // Byte 15 + struct { + unsigned char increment_of_bits_per_pixel_supported :3; + unsigned char reserved :5; + } bits_per_pixel_increment; + } fields; + unsigned char raw[16]; + }; +}; + +struct dpcd_dsc_ext_capabilities { + union { + struct { + unsigned char branch_overall_throughput_0; // Byte 0 + unsigned char branch_overall_throughput_1; // Byte 1 + unsigned char branch_max_line_width; // Byte 2 + } fields; + unsigned char raw[3]; + }; +}; + +struct dpcd_dsc_capabilities { + struct dpcd_dsc_basic_capabilities dsc_basic_caps; + struct dpcd_dsc_ext_capabilities dsc_ext_caps; +}; + +struct dpcd_fec_capability { + union { + struct { + // Byte 0 + unsigned char fec_capable :1; // Bit0 + unsigned char uncorrected_block_error_count_capable :1; + unsigned char corrected_block_error_count_capable :1; + unsigned char bit_error_count_capable :1; + unsigned char reserved :4; // Bit4 + } fields; + unsigned char raw[1]; + }; +}; + +#endif + +#endif /* DAL_INCLUDE_DPCD_STRUCTS_H_ */ diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index c0c947a42a59..6d10b455b80a 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -356,6 +356,11 @@ # define DP_FEC_CORR_BLK_ERROR_COUNT_CAP (1 << 2) # define DP_FEC_BIT_ERROR_COUNT_CAP (1 << 3) +/* DP Extended DSC Capabilities */ +#define DP_DSC_BRANCH_OVERALL_THROUGHPUT_0 0x0a0 /* DP 1.4a SCR */ +#define DP_DSC_BRANCH_OVERALL_THROUGHPUT_1 0x0a1 +#define DP_DSC_BRANCH_MAX_LINE_WIDTH 0x0a2 + /* link configuration */ #define DP_LINK_BW_SET 0x100 # define DP_LINK_RATE_TABLE 0x00 /* eDP 1.4 */ -- cgit v1.2.3 From e28ad544f462231d3fd081a7316339359efbb481 Mon Sep 17 00:00:00 2001 From: Andres Rodriguez Date: Wed, 19 Jun 2019 14:09:01 -0400 Subject: drm/edid: parse CEA blocks embedded in DisplayID DisplayID blocks allow embedding of CEA blocks. The payloads are identical to traditional top level CEA extension blocks, but the header is slightly different. This change allows the CEA parser to find a CEA block inside a DisplayID block. Additionally, it adds support for parsing the embedded CTA header. No further changes are necessary due to payload parity. This change fixes audio support for the Valve Index HMD. Signed-off-by: Andres Rodriguez Reviewed-by: Dave Airlie Cc: Jani Nikula Cc: # v4.15 Signed-off-by: Dave Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20190619180901.17901-1-andresx7@gmail.com --- drivers/gpu/drm/drm_edid.c | 81 +++++++++++++++++++++++++++++++++++++++------ include/drm/drm_displayid.h | 10 ++++++ 2 files changed, 80 insertions(+), 11 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 9d8f2b952004..df635afe41b7 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1342,6 +1342,7 @@ MODULE_PARM_DESC(edid_fixup, static void drm_get_displayid(struct drm_connector *connector, struct edid *edid); +static int validate_displayid(u8 *displayid, int length, int idx); static int drm_edid_block_checksum(const u8 *raw_edid) { @@ -2926,16 +2927,46 @@ static u8 *drm_find_edid_extension(const struct edid *edid, int ext_id) return edid_ext; } -static u8 *drm_find_cea_extension(const struct edid *edid) -{ - return drm_find_edid_extension(edid, CEA_EXT); -} static u8 *drm_find_displayid_extension(const struct edid *edid) { return drm_find_edid_extension(edid, DISPLAYID_EXT); } +static u8 *drm_find_cea_extension(const struct edid *edid) +{ + int ret; + int idx = 1; + int length = EDID_LENGTH; + struct displayid_block *block; + u8 *cea; + u8 *displayid; + + /* Look for a top level CEA extension block */ + cea = drm_find_edid_extension(edid, CEA_EXT); + if (cea) + return cea; + + /* CEA blocks can also be found embedded in a DisplayID block */ + displayid = drm_find_displayid_extension(edid); + if (!displayid) + return NULL; + + ret = validate_displayid(displayid, length, idx); + if (ret) + return NULL; + + idx += sizeof(struct displayid_hdr); + for_each_displayid_db(displayid, block, idx, length) { + if (block->tag == DATA_BLOCK_CTA) { + cea = (u8 *)block; + break; + } + } + + return cea; +} + /* * Calculate the alternate clock for the CEA mode * (60Hz vs. 59.94Hz etc.) @@ -3659,13 +3690,38 @@ cea_revision(const u8 *cea) static int cea_db_offsets(const u8 *cea, int *start, int *end) { - /* Data block offset in CEA extension block */ - *start = 4; - *end = cea[2]; - if (*end == 0) - *end = 127; - if (*end < 4 || *end > 127) - return -ERANGE; + /* DisplayID CTA extension blocks and top-level CEA EDID + * block header definitions differ in the following bytes: + * 1) Byte 2 of the header specifies length differently, + * 2) Byte 3 is only present in the CEA top level block. + * + * The different definitions for byte 2 follow. + * + * DisplayID CTA extension block defines byte 2 as: + * Number of payload bytes + * + * CEA EDID block defines byte 2 as: + * Byte number (decimal) within this block where the 18-byte + * DTDs begin. If no non-DTD data is present in this extension + * block, the value should be set to 04h (the byte after next). + * If set to 00h, there are no DTDs present in this block and + * no non-DTD data. + */ + if (cea[0] == DATA_BLOCK_CTA) { + *start = 3; + *end = *start + cea[2]; + } else if (cea[0] == CEA_EXT) { + /* Data block offset in CEA extension block */ + *start = 4; + *end = cea[2]; + if (*end == 0) + *end = 127; + if (*end < 4 || *end > 127) + return -ERANGE; + } else { + return -ENOTSUPP; + } + return 0; } @@ -5406,6 +5462,9 @@ static int drm_parse_display_id(struct drm_connector *connector, case DATA_BLOCK_TYPE_1_DETAILED_TIMING: /* handled in mode gathering code. */ break; + case DATA_BLOCK_CTA: + /* handled in the cea parser code. */ + break; default: DRM_DEBUG_KMS("found DisplayID tag 0x%x, unhandled\n", block->tag); break; diff --git a/include/drm/drm_displayid.h b/include/drm/drm_displayid.h index c0d4df6a606f..9d3b745c3107 100644 --- a/include/drm/drm_displayid.h +++ b/include/drm/drm_displayid.h @@ -40,6 +40,7 @@ #define DATA_BLOCK_DISPLAY_INTERFACE 0x0f #define DATA_BLOCK_STEREO_DISPLAY_INTERFACE 0x10 #define DATA_BLOCK_TILED_DISPLAY 0x12 +#define DATA_BLOCK_CTA 0x81 #define DATA_BLOCK_VENDOR_SPECIFIC 0x7f @@ -90,4 +91,13 @@ struct displayid_detailed_timing_block { struct displayid_block base; struct displayid_detailed_timings_1 timings[0]; }; + +#define for_each_displayid_db(displayid, block, idx, length) \ + for ((block) = (struct displayid_block *)&(displayid)[idx]; \ + (idx) + sizeof(struct displayid_block) <= (length) && \ + (idx) + sizeof(struct displayid_block) + (block)->num_bytes <= (length) && \ + (block)->num_bytes > 0; \ + (idx) += (block)->num_bytes + sizeof(struct displayid_block), \ + (block) = (struct displayid_block *)&(displayid)[idx]) + #endif -- cgit v1.2.3 From 3729fe2bc2a01f4cc1aa88be8f64af06084c87d6 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 16 Jul 2019 04:07:13 +1000 Subject: Revert "Merge branch 'vmwgfx-next' of git://people.freedesktop.org/~thomash/linux into drm-next" This reverts commit 031e610a6a21448a63dff7a0416e5e206724caac, reversing changes made to 52d2d44eee8091e740d0d275df1311fb8373c9a9. The mm changes in there we premature and not fully ack or reviewed by core mm folks, I dropped the ball by merging them via this tree, so lets take em all back out. Signed-off-by: Dave Airlie --- MAINTAINERS | 1 - drivers/gpu/drm/ttm/ttm_bo.c | 1 - drivers/gpu/drm/ttm/ttm_bo_vm.c | 169 +++----- drivers/gpu/drm/vmwgfx/Kconfig | 1 - drivers/gpu/drm/vmwgfx/Makefile | 2 +- .../drm/vmwgfx/device_include/svga3d_surfacedefs.h | 233 +--------- drivers/gpu/drm/vmwgfx/ttm_lock.c | 100 +++++ drivers/gpu/drm/vmwgfx/ttm_lock.h | 30 ++ drivers/gpu/drm/vmwgfx/vmwgfx_bo.c | 12 +- drivers/gpu/drm/vmwgfx/vmwgfx_context.c | 4 - drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c | 13 +- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 167 +++++++- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 139 ++---- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 1 + drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 23 +- drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c | 472 --------------------- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 245 ++--------- drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h | 15 - drivers/gpu/drm/vmwgfx/vmwgfx_shader.c | 8 +- drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 405 +----------------- drivers/gpu/drm/vmwgfx/vmwgfx_validation.c | 74 +--- drivers/gpu/drm/vmwgfx/vmwgfx_validation.h | 16 +- include/drm/ttm/ttm_bo_api.h | 10 - include/drm/ttm/ttm_bo_driver.h | 6 - include/linux/mm.h | 19 +- include/uapi/drm/vmwgfx_drm.h | 4 +- mm/Kconfig | 3 - mm/Makefile | 1 - mm/as_dirty_helpers.c | 300 ------------- mm/memory.c | 145 ++----- 30 files changed, 483 insertions(+), 2136 deletions(-) delete mode 100644 drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c delete mode 100644 mm/as_dirty_helpers.c (limited to 'include/drm') diff --git a/MAINTAINERS b/MAINTAINERS index d6600715a662..2abf6d28db64 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5191,7 +5191,6 @@ T: git git://people.freedesktop.org/~thomash/linux S: Supported F: drivers/gpu/drm/vmwgfx/ F: include/uapi/drm/vmwgfx_drm.h -F: mm/as_dirty_helpers.c DRM DRIVERS M: David Airlie diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index a7fd5a4955c9..58c403eda04e 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -1739,7 +1739,6 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev, mutex_lock(&ttm_global_mutex); list_add_tail(&bdev->device_list, &glob->device_list); mutex_unlock(&ttm_global_mutex); - bdev->vm_ops = &ttm_bo_vm_ops; return 0; out_no_sys: diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 0c4576cbafcf..6dacff49c1cc 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -42,6 +42,8 @@ #include #include +#define TTM_BO_VM_NUM_PREFAULT 16 + static vm_fault_t ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo, struct vm_fault *vmf) { @@ -104,30 +106,25 @@ static unsigned long ttm_bo_io_mem_pfn(struct ttm_buffer_object *bo, + page_offset; } -/** - * ttm_bo_vm_reserve - Reserve a buffer object in a retryable vm callback - * @bo: The buffer object - * @vmf: The fault structure handed to the callback - * - * vm callbacks like fault() and *_mkwrite() allow for the mm_sem to be dropped - * during long waits, and after the wait the callback will be restarted. This - * is to allow other threads using the same virtual memory space concurrent - * access to map(), unmap() completely unrelated buffer objects. TTM buffer - * object reservations sometimes wait for GPU and should therefore be - * considered long waits. This function reserves the buffer object interruptibly - * taking this into account. Starvation is avoided by the vm system not - * allowing too many repeated restarts. - * This function is intended to be used in customized fault() and _mkwrite() - * handlers. - * - * Return: - * 0 on success and the bo was reserved. - * VM_FAULT_RETRY if blocking wait. - * VM_FAULT_NOPAGE if blocking wait and retrying was not allowed. - */ -vm_fault_t ttm_bo_vm_reserve(struct ttm_buffer_object *bo, - struct vm_fault *vmf) +static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; + struct ttm_buffer_object *bo = (struct ttm_buffer_object *) + vma->vm_private_data; + struct ttm_bo_device *bdev = bo->bdev; + unsigned long page_offset; + unsigned long page_last; + unsigned long pfn; + struct ttm_tt *ttm = NULL; + struct page *page; + int err; + int i; + vm_fault_t ret = VM_FAULT_NOPAGE; + unsigned long address = vmf->address; + struct ttm_mem_type_manager *man = + &bdev->man[bo->mem.mem_type]; + struct vm_area_struct cvma; + /* * Work around locking order reversal in fault / nopfn * between mmap_sem and bo_reserve: Perform a trylock operation @@ -154,55 +151,14 @@ vm_fault_t ttm_bo_vm_reserve(struct ttm_buffer_object *bo, return VM_FAULT_NOPAGE; } - return 0; -} -EXPORT_SYMBOL(ttm_bo_vm_reserve); - -/** - * ttm_bo_vm_fault_reserved - TTM fault helper - * @vmf: The struct vm_fault given as argument to the fault callback - * @prot: The page protection to be used for this memory area. - * @num_prefault: Maximum number of prefault pages. The caller may want to - * specify this based on madvice settings and the size of the GPU object - * backed by the memory. - * - * This function inserts one or more page table entries pointing to the - * memory backing the buffer object, and then returns a return code - * instructing the caller to retry the page access. - * - * Return: - * VM_FAULT_NOPAGE on success or pending signal - * VM_FAULT_SIGBUS on unspecified error - * VM_FAULT_OOM on out-of-memory - * VM_FAULT_RETRY if retryable wait - */ -vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf, - pgprot_t prot, - pgoff_t num_prefault) -{ - struct vm_area_struct *vma = vmf->vma; - struct vm_area_struct cvma = *vma; - struct ttm_buffer_object *bo = (struct ttm_buffer_object *) - vma->vm_private_data; - struct ttm_bo_device *bdev = bo->bdev; - unsigned long page_offset; - unsigned long page_last; - unsigned long pfn; - struct ttm_tt *ttm = NULL; - struct page *page; - int err; - pgoff_t i; - vm_fault_t ret = VM_FAULT_NOPAGE; - unsigned long address = vmf->address; - struct ttm_mem_type_manager *man = - &bdev->man[bo->mem.mem_type]; - /* * Refuse to fault imported pages. This should be handled * (if at all) by redirecting mmap to the exporter. */ - if (bo->ttm && (bo->ttm->page_flags & TTM_PAGE_FLAG_SG)) - return VM_FAULT_SIGBUS; + if (bo->ttm && (bo->ttm->page_flags & TTM_PAGE_FLAG_SG)) { + ret = VM_FAULT_SIGBUS; + goto out_unlock; + } if (bdev->driver->fault_reserve_notify) { struct dma_fence *moving = dma_fence_get(bo->moving); @@ -213,9 +169,11 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf, break; case -EBUSY: case -ERESTARTSYS: - return VM_FAULT_NOPAGE; + ret = VM_FAULT_NOPAGE; + goto out_unlock; default: - return VM_FAULT_SIGBUS; + ret = VM_FAULT_SIGBUS; + goto out_unlock; } if (bo->moving != moving) { @@ -231,12 +189,21 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf, * move. */ ret = ttm_bo_vm_fault_idle(bo, vmf); - if (unlikely(ret != 0)) - return ret; + if (unlikely(ret != 0)) { + if (ret == VM_FAULT_RETRY && + !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) { + /* The BO has already been unreserved. */ + return ret; + } + + goto out_unlock; + } err = ttm_mem_io_lock(man, true); - if (unlikely(err != 0)) - return VM_FAULT_NOPAGE; + if (unlikely(err != 0)) { + ret = VM_FAULT_NOPAGE; + goto out_unlock; + } err = ttm_mem_io_reserve_vm(bo); if (unlikely(err != 0)) { ret = VM_FAULT_SIGBUS; @@ -253,8 +220,18 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf, goto out_io_unlock; } - cvma.vm_page_prot = ttm_io_prot(bo->mem.placement, prot); - if (!bo->mem.bus.is_iomem) { + /* + * Make a local vma copy to modify the page_prot member + * and vm_flags if necessary. The vma parameter is protected + * by mmap_sem in write mode. + */ + cvma = *vma; + cvma.vm_page_prot = vm_get_page_prot(cvma.vm_flags); + + if (bo->mem.bus.is_iomem) { + cvma.vm_page_prot = ttm_io_prot(bo->mem.placement, + cvma.vm_page_prot); + } else { struct ttm_operation_ctx ctx = { .interruptible = false, .no_wait_gpu = false, @@ -263,21 +240,24 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf, }; ttm = bo->ttm; - if (ttm_tt_populate(bo->ttm, &ctx)) { + cvma.vm_page_prot = ttm_io_prot(bo->mem.placement, + cvma.vm_page_prot); + + /* Allocate all page at once, most common usage */ + if (ttm_tt_populate(ttm, &ctx)) { ret = VM_FAULT_OOM; goto out_io_unlock; } - } else { - /* Iomem should not be marked encrypted */ - cvma.vm_page_prot = pgprot_decrypted(cvma.vm_page_prot); } /* * Speculatively prefault a number of pages. Only error on * first page. */ - for (i = 0; i < num_prefault; ++i) { + for (i = 0; i < TTM_BO_VM_NUM_PREFAULT; ++i) { if (bo->mem.bus.is_iomem) { + /* Iomem should not be marked encrypted */ + cvma.vm_page_prot = pgprot_decrypted(cvma.vm_page_prot); pfn = ttm_bo_io_mem_pfn(bo, page_offset); } else { page = ttm->pages[page_offset]; @@ -315,26 +295,7 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf, ret = VM_FAULT_NOPAGE; out_io_unlock: ttm_mem_io_unlock(man); - return ret; -} -EXPORT_SYMBOL(ttm_bo_vm_fault_reserved); - -static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) -{ - struct vm_area_struct *vma = vmf->vma; - pgprot_t prot; - struct ttm_buffer_object *bo = vma->vm_private_data; - vm_fault_t ret; - - ret = ttm_bo_vm_reserve(bo, vmf); - if (ret) - return ret; - - prot = vm_get_page_prot(vma->vm_flags); - ret = ttm_bo_vm_fault_reserved(vmf, prot, TTM_BO_VM_NUM_PREFAULT); - if (ret == VM_FAULT_RETRY && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) - return ret; - +out_unlock: reservation_object_unlock(bo->resv); return ret; } @@ -434,7 +395,7 @@ static int ttm_bo_vm_access(struct vm_area_struct *vma, unsigned long addr, return ret; } -const struct vm_operations_struct ttm_bo_vm_ops = { +static const struct vm_operations_struct ttm_bo_vm_ops = { .fault = ttm_bo_vm_fault, .open = ttm_bo_vm_open, .close = ttm_bo_vm_close, @@ -487,7 +448,7 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma, if (unlikely(ret != 0)) goto out_unref; - vma->vm_ops = bdev->vm_ops; + vma->vm_ops = &ttm_bo_vm_ops; /* * Note: We're transferring the bo reference to @@ -519,7 +480,7 @@ int ttm_fbdev_mmap(struct vm_area_struct *vma, struct ttm_buffer_object *bo) ttm_bo_get(bo); - vma->vm_ops = bo->bdev->vm_ops; + vma->vm_ops = &ttm_bo_vm_ops; vma->vm_private_data = bo; vma->vm_flags |= VM_MIXEDMAP; vma->vm_flags |= VM_IO | VM_DONTEXPAND; diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig index d5fd81a521f6..6b28a326f8bb 100644 --- a/drivers/gpu/drm/vmwgfx/Kconfig +++ b/drivers/gpu/drm/vmwgfx/Kconfig @@ -8,7 +8,6 @@ config DRM_VMWGFX select FB_CFB_IMAGEBLIT select DRM_TTM select FB - select AS_DIRTY_HELPERS # Only needed for the transitional use of drm_crtc_init - can be removed # again once vmwgfx sets up the primary plane itself. select DRM_KMS_HELPER diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile index c877a21a0739..8841bd30e1e5 100644 --- a/drivers/gpu/drm/vmwgfx/Makefile +++ b/drivers/gpu/drm/vmwgfx/Makefile @@ -8,7 +8,7 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ vmwgfx_cmdbuf_res.o vmwgfx_cmdbuf.o vmwgfx_stdu.o \ vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o vmwgfx_msg.o \ vmwgfx_simple_resource.o vmwgfx_va.o vmwgfx_blit.o \ - vmwgfx_validation.o vmwgfx_page_dirty.o \ + vmwgfx_validation.o \ ttm_object.o ttm_lock.o obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h b/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h index 61414f105c67..f2bfd3d80598 100644 --- a/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h +++ b/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h @@ -1280,6 +1280,7 @@ svga3dsurface_get_pixel_offset(SVGA3dSurfaceFormat format, return offset; } + static inline u32 svga3dsurface_get_image_offset(SVGA3dSurfaceFormat format, surf_size_struct baseLevelSize, @@ -1374,236 +1375,4 @@ svga3dsurface_is_screen_target_format(SVGA3dSurfaceFormat format) return svga3dsurface_is_dx_screen_target_format(format); } -/** - * struct svga3dsurface_mip - Mimpmap level information - * @bytes: Bytes required in the backing store of this mipmap level. - * @img_stride: Byte stride per image. - * @row_stride: Byte stride per block row. - * @size: The size of the mipmap. - */ -struct svga3dsurface_mip { - size_t bytes; - size_t img_stride; - size_t row_stride; - struct drm_vmw_size size; - -}; - -/** - * struct svga3dsurface_cache - Cached surface information - * @desc: Pointer to the surface descriptor - * @mip: Array of mipmap level information. Valid size is @num_mip_levels. - * @mip_chain_bytes: Bytes required in the backing store for the whole chain - * of mip levels. - * @sheet_bytes: Bytes required in the backing store for a sheet - * representing a single sample. - * @num_mip_levels: Valid size of the @mip array. Number of mipmap levels in - * a chain. - * @num_layers: Number of slices in an array texture or number of faces in - * a cubemap texture. - */ -struct svga3dsurface_cache { - const struct svga3d_surface_desc *desc; - struct svga3dsurface_mip mip[DRM_VMW_MAX_MIP_LEVELS]; - size_t mip_chain_bytes; - size_t sheet_bytes; - u32 num_mip_levels; - u32 num_layers; -}; - -/** - * struct svga3dsurface_loc - Surface location - * @sub_resource: Surface subresource. Defined as layer * num_mip_levels + - * mip_level. - * @x: X coordinate. - * @y: Y coordinate. - * @z: Z coordinate. - */ -struct svga3dsurface_loc { - u32 sub_resource; - u32 x, y, z; -}; - -/** - * svga3dsurface_subres - Compute the subresource from layer and mipmap. - * @cache: Surface layout data. - * @mip_level: The mipmap level. - * @layer: The surface layer (face or array slice). - * - * Return: The subresource. - */ -static inline u32 svga3dsurface_subres(const struct svga3dsurface_cache *cache, - u32 mip_level, u32 layer) -{ - return cache->num_mip_levels * layer + mip_level; -} - -/** - * svga3dsurface_setup_cache - Build a surface cache entry - * @size: The surface base level dimensions. - * @format: The surface format. - * @num_mip_levels: Number of mipmap levels. - * @num_layers: Number of layers. - * @cache: Pointer to a struct svga3dsurface_cach object to be filled in. - * - * Return: Zero on success, -EINVAL on invalid surface layout. - */ -static inline int svga3dsurface_setup_cache(const struct drm_vmw_size *size, - SVGA3dSurfaceFormat format, - u32 num_mip_levels, - u32 num_layers, - u32 num_samples, - struct svga3dsurface_cache *cache) -{ - const struct svga3d_surface_desc *desc; - u32 i; - - memset(cache, 0, sizeof(*cache)); - cache->desc = desc = svga3dsurface_get_desc(format); - cache->num_mip_levels = num_mip_levels; - cache->num_layers = num_layers; - for (i = 0; i < cache->num_mip_levels; i++) { - struct svga3dsurface_mip *mip = &cache->mip[i]; - - mip->size = svga3dsurface_get_mip_size(*size, i); - mip->bytes = svga3dsurface_get_image_buffer_size - (desc, &mip->size, 0); - mip->row_stride = - __KERNEL_DIV_ROUND_UP(mip->size.width, - desc->block_size.width) * - desc->bytes_per_block * num_samples; - if (!mip->row_stride) - goto invalid_dim; - - mip->img_stride = - __KERNEL_DIV_ROUND_UP(mip->size.height, - desc->block_size.height) * - mip->row_stride; - if (!mip->img_stride) - goto invalid_dim; - - cache->mip_chain_bytes += mip->bytes; - } - cache->sheet_bytes = cache->mip_chain_bytes * num_layers; - if (!cache->sheet_bytes) - goto invalid_dim; - - return 0; - -invalid_dim: - VMW_DEBUG_USER("Invalid surface layout for dirty tracking.\n"); - return -EINVAL; -} - -/** - * svga3dsurface_get_loc - Get a surface location from an offset into the - * backing store - * @cache: Surface layout data. - * @loc: Pointer to a struct svga3dsurface_loc to be filled in. - * @offset: Offset into the surface backing store. - */ -static inline void -svga3dsurface_get_loc(const struct svga3dsurface_cache *cache, - struct svga3dsurface_loc *loc, - size_t offset) -{ - const struct svga3dsurface_mip *mip = &cache->mip[0]; - const struct svga3d_surface_desc *desc = cache->desc; - u32 layer; - int i; - - if (offset >= cache->sheet_bytes) - offset %= cache->sheet_bytes; - - layer = offset / cache->mip_chain_bytes; - offset -= layer * cache->mip_chain_bytes; - for (i = 0; i < cache->num_mip_levels; ++i, ++mip) { - if (mip->bytes > offset) - break; - offset -= mip->bytes; - } - - loc->sub_resource = svga3dsurface_subres(cache, i, layer); - loc->z = offset / mip->img_stride; - offset -= loc->z * mip->img_stride; - loc->z *= desc->block_size.depth; - loc->y = offset / mip->row_stride; - offset -= loc->y * mip->row_stride; - loc->y *= desc->block_size.height; - loc->x = offset / desc->bytes_per_block; - loc->x *= desc->block_size.width; -} - -/** - * svga3dsurface_inc_loc - Clamp increment a surface location with one block - * size - * in each dimension. - * @loc: Pointer to a struct svga3dsurface_loc to be incremented. - * - * When computing the size of a range as size = end - start, the range does not - * include the end element. However a location representing the last byte - * of a touched region in the backing store *is* included in the range. - * This function modifies such a location to match the end definition - * given as start + size which is the one used in a SVGA3dBox. - */ -static inline void -svga3dsurface_inc_loc(const struct svga3dsurface_cache *cache, - struct svga3dsurface_loc *loc) -{ - const struct svga3d_surface_desc *desc = cache->desc; - u32 mip = loc->sub_resource % cache->num_mip_levels; - const struct drm_vmw_size *size = &cache->mip[mip].size; - - loc->sub_resource++; - loc->x += desc->block_size.width; - if (loc->x > size->width) - loc->x = size->width; - loc->y += desc->block_size.height; - if (loc->y > size->height) - loc->y = size->height; - loc->z += desc->block_size.depth; - if (loc->z > size->depth) - loc->z = size->depth; -} - -/** - * svga3dsurface_min_loc - The start location in a subresource - * @cache: Surface layout data. - * @sub_resource: The subresource. - * @loc: Pointer to a struct svga3dsurface_loc to be filled in. - */ -static inline void -svga3dsurface_min_loc(const struct svga3dsurface_cache *cache, - u32 sub_resource, - struct svga3dsurface_loc *loc) -{ - loc->sub_resource = sub_resource; - loc->x = loc->y = loc->z = 0; -} - -/** - * svga3dsurface_min_loc - The end location in a subresource - * @cache: Surface layout data. - * @sub_resource: The subresource. - * @loc: Pointer to a struct svga3dsurface_loc to be filled in. - * - * Following the end definition given in svga3dsurface_inc_loc(), - * Compute the end location of a surface subresource. - */ -static inline void -svga3dsurface_max_loc(const struct svga3dsurface_cache *cache, - u32 sub_resource, - struct svga3dsurface_loc *loc) -{ - const struct drm_vmw_size *size; - u32 mip; - - loc->sub_resource = sub_resource + 1; - mip = sub_resource % cache->num_mip_levels; - size = &cache->mip[mip].size; - loc->x = size->width; - loc->y = size->height; - loc->z = size->depth; -} - #endif /* _SVGA3D_SURFACEDEFS_H_ */ diff --git a/drivers/gpu/drm/vmwgfx/ttm_lock.c b/drivers/gpu/drm/vmwgfx/ttm_lock.c index 5971c72e6d10..16b2083cb9d4 100644 --- a/drivers/gpu/drm/vmwgfx/ttm_lock.c +++ b/drivers/gpu/drm/vmwgfx/ttm_lock.c @@ -29,6 +29,7 @@ * Authors: Thomas Hellstrom */ +#include #include #include #include @@ -48,6 +49,8 @@ void ttm_lock_init(struct ttm_lock *lock) init_waitqueue_head(&lock->queue); lock->rw = 0; lock->flags = 0; + lock->kill_takers = false; + lock->signal = SIGKILL; } void ttm_read_unlock(struct ttm_lock *lock) @@ -63,6 +66,11 @@ static bool __ttm_read_lock(struct ttm_lock *lock) bool locked = false; spin_lock(&lock->lock); + if (unlikely(lock->kill_takers)) { + send_sig(lock->signal, current, 0); + spin_unlock(&lock->lock); + return false; + } if (lock->rw >= 0 && lock->flags == 0) { ++lock->rw; locked = true; @@ -90,6 +98,11 @@ static bool __ttm_read_trylock(struct ttm_lock *lock, bool *locked) *locked = false; spin_lock(&lock->lock); + if (unlikely(lock->kill_takers)) { + send_sig(lock->signal, current, 0); + spin_unlock(&lock->lock); + return false; + } if (lock->rw >= 0 && lock->flags == 0) { ++lock->rw; block = false; @@ -134,6 +147,11 @@ static bool __ttm_write_lock(struct ttm_lock *lock) bool locked = false; spin_lock(&lock->lock); + if (unlikely(lock->kill_takers)) { + send_sig(lock->signal, current, 0); + spin_unlock(&lock->lock); + return false; + } if (lock->rw == 0 && ((lock->flags & ~TTM_WRITE_LOCK_PENDING) == 0)) { lock->rw = -1; lock->flags &= ~TTM_WRITE_LOCK_PENDING; @@ -164,6 +182,88 @@ int ttm_write_lock(struct ttm_lock *lock, bool interruptible) return ret; } +static int __ttm_vt_unlock(struct ttm_lock *lock) +{ + int ret = 0; + + spin_lock(&lock->lock); + if (unlikely(!(lock->flags & TTM_VT_LOCK))) + ret = -EINVAL; + lock->flags &= ~TTM_VT_LOCK; + wake_up_all(&lock->queue); + spin_unlock(&lock->lock); + + return ret; +} + +static void ttm_vt_lock_remove(struct ttm_base_object **p_base) +{ + struct ttm_base_object *base = *p_base; + struct ttm_lock *lock = container_of(base, struct ttm_lock, base); + int ret; + + *p_base = NULL; + ret = __ttm_vt_unlock(lock); + BUG_ON(ret != 0); +} + +static bool __ttm_vt_lock(struct ttm_lock *lock) +{ + bool locked = false; + + spin_lock(&lock->lock); + if (lock->rw == 0) { + lock->flags &= ~TTM_VT_LOCK_PENDING; + lock->flags |= TTM_VT_LOCK; + locked = true; + } else { + lock->flags |= TTM_VT_LOCK_PENDING; + } + spin_unlock(&lock->lock); + return locked; +} + +int ttm_vt_lock(struct ttm_lock *lock, + bool interruptible, + struct ttm_object_file *tfile) +{ + int ret = 0; + + if (interruptible) { + ret = wait_event_interruptible(lock->queue, + __ttm_vt_lock(lock)); + if (unlikely(ret != 0)) { + spin_lock(&lock->lock); + lock->flags &= ~TTM_VT_LOCK_PENDING; + wake_up_all(&lock->queue); + spin_unlock(&lock->lock); + return ret; + } + } else + wait_event(lock->queue, __ttm_vt_lock(lock)); + + /* + * Add a base-object, the destructor of which will + * make sure the lock is released if the client dies + * while holding it. + */ + + ret = ttm_base_object_init(tfile, &lock->base, false, + ttm_lock_type, &ttm_vt_lock_remove, NULL); + if (ret) + (void)__ttm_vt_unlock(lock); + else + lock->vt_holder = tfile; + + return ret; +} + +int ttm_vt_unlock(struct ttm_lock *lock) +{ + return ttm_ref_object_base_unref(lock->vt_holder, + lock->base.handle, TTM_REF_USAGE); +} + void ttm_suspend_unlock(struct ttm_lock *lock) { spin_lock(&lock->lock); diff --git a/drivers/gpu/drm/vmwgfx/ttm_lock.h b/drivers/gpu/drm/vmwgfx/ttm_lock.h index 3d454e8b491f..0c3af9836863 100644 --- a/drivers/gpu/drm/vmwgfx/ttm_lock.h +++ b/drivers/gpu/drm/vmwgfx/ttm_lock.h @@ -63,6 +63,8 @@ * @lock: Spinlock protecting some lock members. * @rw: Read-write lock counter. Protected by @lock. * @flags: Lock state. Protected by @lock. + * @kill_takers: Boolean whether to kill takers of the lock. + * @signal: Signal to send when kill_takers is true. */ struct ttm_lock { @@ -71,6 +73,9 @@ struct ttm_lock { spinlock_t lock; int32_t rw; uint32_t flags; + bool kill_takers; + int signal; + struct ttm_object_file *vt_holder; }; @@ -215,4 +220,29 @@ extern void ttm_write_unlock(struct ttm_lock *lock); */ extern int ttm_write_lock(struct ttm_lock *lock, bool interruptible); +/** + * ttm_lock_set_kill + * + * @lock: Pointer to a struct ttm_lock + * @val: Boolean whether to kill processes taking the lock. + * @signal: Signal to send to the process taking the lock. + * + * The kill-when-taking-lock functionality is used to kill processes that keep + * on using the TTM functionality when its resources has been taken down, for + * example when the X server exits. A typical sequence would look like this: + * - X server takes lock in write mode. + * - ttm_lock_set_kill() is called with @val set to true. + * - As part of X server exit, TTM resources are taken down. + * - X server releases the lock on file release. + * - Another dri client wants to render, takes the lock and is killed. + * + */ +static inline void ttm_lock_set_kill(struct ttm_lock *lock, bool val, + int signal) +{ + lock->kill_takers = val; + if (val) + lock->signal = signal; +} + #endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c index e8bc7a7ac031..5d5c2bce01f3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c @@ -463,8 +463,6 @@ void vmw_bo_bo_free(struct ttm_buffer_object *bo) { struct vmw_buffer_object *vmw_bo = vmw_buffer_object(bo); - WARN_ON(vmw_bo->dirty); - WARN_ON(!RB_EMPTY_ROOT(&vmw_bo->res_tree)); vmw_bo_unmap(vmw_bo); kfree(vmw_bo); } @@ -478,11 +476,8 @@ void vmw_bo_bo_free(struct ttm_buffer_object *bo) static void vmw_user_bo_destroy(struct ttm_buffer_object *bo) { struct vmw_user_buffer_object *vmw_user_bo = vmw_user_buffer_object(bo); - struct vmw_buffer_object *vbo = &vmw_user_bo->vbo; - WARN_ON(vbo->dirty); - WARN_ON(!RB_EMPTY_ROOT(&vbo->res_tree)); - vmw_bo_unmap(vbo); + vmw_bo_unmap(&vmw_user_bo->vbo); ttm_prime_object_kfree(vmw_user_bo, prime); } @@ -515,9 +510,8 @@ int vmw_bo_init(struct vmw_private *dev_priv, acc_size = vmw_bo_acc_size(dev_priv, size, user); memset(vmw_bo, 0, sizeof(*vmw_bo)); - BUILD_BUG_ON(TTM_MAX_BO_PRIORITY <= 3); - vmw_bo->base.priority = 3; - vmw_bo->res_tree = RB_ROOT; + + INIT_LIST_HEAD(&vmw_bo->res_list); ret = ttm_bo_init(bdev, &vmw_bo->base, size, ttm_bo_type_device, placement, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c index a56c9d802382..63f111068a44 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c @@ -88,8 +88,6 @@ static const struct vmw_res_func vmw_gb_context_func = { .res_type = vmw_res_context, .needs_backup = true, .may_evict = true, - .prio = 3, - .dirty_prio = 3, .type_name = "guest backed contexts", .backup_placement = &vmw_mob_placement, .create = vmw_gb_context_create, @@ -102,8 +100,6 @@ static const struct vmw_res_func vmw_dx_context_func = { .res_type = vmw_res_dx_context, .needs_backup = true, .may_evict = true, - .prio = 3, - .dirty_prio = 3, .type_name = "dx contexts", .backup_placement = &vmw_mob_placement, .create = vmw_dx_context_create, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c index 8c699cb2565b..b4f6e1217c9d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c @@ -116,8 +116,6 @@ static const struct vmw_res_func vmw_cotable_func = { .res_type = vmw_res_cotable, .needs_backup = true, .may_evict = true, - .prio = 3, - .dirty_prio = 3, .type_name = "context guest backed object tables", .backup_placement = &vmw_mob_placement, .create = vmw_cotable_create, @@ -309,7 +307,7 @@ static int vmw_cotable_unbind(struct vmw_resource *res, struct ttm_buffer_object *bo = val_buf->bo; struct vmw_fence_obj *fence; - if (!vmw_resource_mob_attached(res)) + if (list_empty(&res->mob_head)) return 0; WARN_ON_ONCE(bo->mem.mem_type != VMW_PL_MOB); @@ -455,7 +453,6 @@ static int vmw_cotable_resize(struct vmw_resource *res, size_t new_size) goto out_wait; } - vmw_resource_mob_detach(res); res->backup = buf; res->backup_size = new_size; vcotbl->size_read_back = cur_size_read_back; @@ -470,12 +467,12 @@ static int vmw_cotable_resize(struct vmw_resource *res, size_t new_size) res->backup = old_buf; res->backup_size = old_size; vcotbl->size_read_back = old_size_read_back; - vmw_resource_mob_attach(res); goto out_wait; } - vmw_resource_mob_attach(res); /* Let go of the old mob. */ + list_del(&res->mob_head); + list_add_tail(&res->mob_head, &buf->res_list); vmw_bo_unreference(&old_buf); res->id = vcotbl->type; @@ -499,7 +496,7 @@ out_wait: * is called before bind() in the validation sequence is instead used for two * things. * 1) Unscrub the cotable if it is scrubbed and still attached to a backup - * buffer. + * buffer, that is, if @res->mob_head is non-empty. * 2) Resize the cotable if needed. */ static int vmw_cotable_create(struct vmw_resource *res) @@ -515,7 +512,7 @@ static int vmw_cotable_create(struct vmw_resource *res) new_size *= 2; if (likely(new_size <= res->backup_size)) { - if (vcotbl->scrubbed && vmw_resource_mob_attached(res)) { + if (vcotbl->scrubbed && !list_empty(&res->mob_head)) { ret = vmw_cotable_unscrub(res); if (ret) return ret; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 8349a6cc126f..4ff11a0077e1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -254,6 +254,7 @@ static int vmw_restrict_dma_mask; static int vmw_assume_16bpp; static int vmw_probe(struct pci_dev *, const struct pci_device_id *); +static void vmw_master_init(struct vmw_master *); static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, void *ptr); @@ -761,6 +762,10 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) DRM_INFO("MMIO at 0x%08x size is %u kiB\n", dev_priv->mmio_start, dev_priv->mmio_size / 1024); + vmw_master_init(&dev_priv->fbdev_master); + ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM); + dev_priv->active_master = &dev_priv->fbdev_master; + dev_priv->mmio_virt = memremap(dev_priv->mmio_start, dev_priv->mmio_size, MEMREMAP_WB); @@ -828,11 +833,6 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) DRM_ERROR("Failed initializing TTM buffer object driver.\n"); goto out_no_bdev; } - dev_priv->vm_ops = *dev_priv->bdev.vm_ops; - dev_priv->vm_ops.fault = vmw_bo_vm_fault; - dev_priv->vm_ops.pfn_mkwrite = vmw_bo_vm_mkwrite; - dev_priv->vm_ops.page_mkwrite = vmw_bo_vm_mkwrite; - dev_priv->bdev.vm_ops = &dev_priv->vm_ops; /* * Enable VRAM, but initially don't use it until SVGA is enabled and @@ -1007,7 +1007,18 @@ static void vmw_driver_unload(struct drm_device *dev) static void vmw_postclose(struct drm_device *dev, struct drm_file *file_priv) { - struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); + struct vmw_fpriv *vmw_fp; + + vmw_fp = vmw_fpriv(file_priv); + + if (vmw_fp->locked_master) { + struct vmw_master *vmaster = + vmw_master(vmw_fp->locked_master); + + ttm_lock_set_kill(&vmaster->lock, true, SIGTERM); + ttm_vt_unlock(&vmaster->lock); + drm_master_put(&vmw_fp->locked_master); + } ttm_object_file_release(&vmw_fp->tfile); kfree(vmw_fp); @@ -1036,6 +1047,55 @@ out_no_tfile: return ret; } +static struct vmw_master *vmw_master_check(struct drm_device *dev, + struct drm_file *file_priv, + unsigned int flags) +{ + int ret; + struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); + struct vmw_master *vmaster; + + if (!drm_is_primary_client(file_priv) || !(flags & DRM_AUTH)) + return NULL; + + ret = mutex_lock_interruptible(&dev->master_mutex); + if (unlikely(ret != 0)) + return ERR_PTR(-ERESTARTSYS); + + if (drm_is_current_master(file_priv)) { + mutex_unlock(&dev->master_mutex); + return NULL; + } + + /* + * Check if we were previously master, but now dropped. In that + * case, allow at least render node functionality. + */ + if (vmw_fp->locked_master) { + mutex_unlock(&dev->master_mutex); + + if (flags & DRM_RENDER_ALLOW) + return NULL; + + DRM_ERROR("Dropped master trying to access ioctl that " + "requires authentication.\n"); + return ERR_PTR(-EACCES); + } + mutex_unlock(&dev->master_mutex); + + /* + * Take the TTM lock. Possibly sleep waiting for the authenticating + * master to become master again, or for a SIGTERM if the + * authenticating master exits. + */ + vmaster = vmw_master(file_priv->master); + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + vmaster = ERR_PTR(ret); + + return vmaster; +} + static long vmw_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg, long (*ioctl_func)(struct file *, unsigned int, @@ -1044,6 +1104,7 @@ static long vmw_generic_ioctl(struct file *filp, unsigned int cmd, struct drm_file *file_priv = filp->private_data; struct drm_device *dev = file_priv->minor->dev; unsigned int nr = DRM_IOCTL_NR(cmd); + struct vmw_master *vmaster; unsigned int flags; long ret; @@ -1079,7 +1140,21 @@ static long vmw_generic_ioctl(struct file *filp, unsigned int cmd, } else if (!drm_ioctl_flags(nr, &flags)) return -EINVAL; - return ioctl_func(filp, cmd, arg); + vmaster = vmw_master_check(dev, file_priv, flags); + if (IS_ERR(vmaster)) { + ret = PTR_ERR(vmaster); + + if (ret != -ERESTARTSYS) + DRM_INFO("IOCTL ERROR Command %d, Error %ld.\n", + nr, ret); + return ret; + } + + ret = ioctl_func(filp, cmd, arg); + if (vmaster) + ttm_read_unlock(&vmaster->lock); + + return ret; out_io_encoding: DRM_ERROR("Invalid command format, ioctl %d\n", @@ -1106,10 +1181,65 @@ static void vmw_lastclose(struct drm_device *dev) { } +static void vmw_master_init(struct vmw_master *vmaster) +{ + ttm_lock_init(&vmaster->lock); +} + +static int vmw_master_create(struct drm_device *dev, + struct drm_master *master) +{ + struct vmw_master *vmaster; + + vmaster = kzalloc(sizeof(*vmaster), GFP_KERNEL); + if (unlikely(!vmaster)) + return -ENOMEM; + + vmw_master_init(vmaster); + ttm_lock_set_kill(&vmaster->lock, true, SIGTERM); + master->driver_priv = vmaster; + + return 0; +} + +static void vmw_master_destroy(struct drm_device *dev, + struct drm_master *master) +{ + struct vmw_master *vmaster = vmw_master(master); + + master->driver_priv = NULL; + kfree(vmaster); +} + static int vmw_master_set(struct drm_device *dev, struct drm_file *file_priv, bool from_open) { + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); + struct vmw_master *active = dev_priv->active_master; + struct vmw_master *vmaster = vmw_master(file_priv->master); + int ret = 0; + + if (active) { + BUG_ON(active != &dev_priv->fbdev_master); + ret = ttm_vt_lock(&active->lock, false, vmw_fp->tfile); + if (unlikely(ret != 0)) + return ret; + + ttm_lock_set_kill(&active->lock, true, SIGTERM); + dev_priv->active_master = NULL; + } + + ttm_lock_set_kill(&vmaster->lock, false, SIGTERM); + if (!from_open) { + ttm_vt_unlock(&vmaster->lock); + BUG_ON(vmw_fp->locked_master != file_priv->master); + drm_master_put(&vmw_fp->locked_master); + } + + dev_priv->active_master = vmaster; + /* * Inform a new master that the layout may have changed while * it was gone. @@ -1124,10 +1254,31 @@ static void vmw_master_drop(struct drm_device *dev, struct drm_file *file_priv) { struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); + struct vmw_master *vmaster = vmw_master(file_priv->master); + int ret; + + /** + * Make sure the master doesn't disappear while we have + * it locked. + */ + vmw_fp->locked_master = drm_master_get(file_priv->master); + ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile); vmw_kms_legacy_hotspot_clear(dev_priv); + if (unlikely((ret != 0))) { + DRM_ERROR("Unable to lock TTM at VT switch.\n"); + drm_master_put(&vmw_fp->locked_master); + } + + ttm_lock_set_kill(&vmaster->lock, false, SIGTERM); + if (!dev_priv->enable_fb) vmw_svga_disable(dev_priv); + + dev_priv->active_master = &dev_priv->fbdev_master; + ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM); + ttm_vt_unlock(&dev_priv->fbdev_master.lock); } /** @@ -1406,6 +1557,8 @@ static struct drm_driver driver = { .disable_vblank = vmw_disable_vblank, .ioctls = vmw_ioctls, .num_ioctls = ARRAY_SIZE(vmw_ioctls), + .master_create = vmw_master_create, + .master_destroy = vmw_master_destroy, .master_set = vmw_master_set, .master_drop = vmw_master_drop, .open = vmw_driver_open, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 3a358a5495e4..366dcfc1f9bb 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -44,9 +44,9 @@ #include #define VMWGFX_DRIVER_NAME "vmwgfx" -#define VMWGFX_DRIVER_DATE "20190328" +#define VMWGFX_DRIVER_DATE "20180704" #define VMWGFX_DRIVER_MAJOR 2 -#define VMWGFX_DRIVER_MINOR 16 +#define VMWGFX_DRIVER_MINOR 15 #define VMWGFX_DRIVER_PATCHLEVEL 0 #define VMWGFX_FIFO_STATIC_SIZE (1024*1024) #define VMWGFX_MAX_RELOCATIONS 2048 @@ -81,30 +81,19 @@ #define VMW_RES_SHADER ttm_driver_type4 struct vmw_fpriv { + struct drm_master *locked_master; struct ttm_object_file *tfile; bool gb_aware; /* user-space is guest-backed aware */ }; -/** - * struct vmw_buffer_object - TTM buffer object with vmwgfx additions - * @base: The TTM buffer object - * @res_tree: RB tree of resources using this buffer object as a backing MOB - * @pin_count: pin depth - * @dx_query_ctx: DX context if this buffer object is used as a DX query MOB - * @map: Kmap object for semi-persistent mappings - * @res_prios: Eviction priority counts for attached resources - * @dirty: structure for user-space dirty-tracking - */ struct vmw_buffer_object { struct ttm_buffer_object base; - struct rb_root res_tree; + struct list_head res_list; s32 pin_count; /* Not ref-counted. Protected by binding_mutex */ struct vmw_resource *dx_query_ctx; /* Protected by reservation */ struct ttm_bo_kmap_obj map; - u32 res_prios[TTM_MAX_BO_PRIORITY]; - struct vmw_bo_dirty *dirty; }; /** @@ -135,8 +124,7 @@ struct vmw_res_func; * @res_dirty: Resource contains data not yet in the backup buffer. Protected * by resource reserved. * @backup_dirty: Backup buffer contains data not yet in the HW resource. - * Protected by resource reserved. - * @coherent: Emulate coherency by tracking vm accesses. + * Protecte by resource reserved. * @backup: The backup buffer if any. Protected by resource reserved. * @backup_offset: Offset into the backup buffer if any. Protected by resource * reserved. Note that only a few resource types can have a @backup_offset @@ -145,32 +133,28 @@ struct vmw_res_func; * pin-count greater than zero. It is not on the resource LRU lists and its * backup buffer is pinned. Hence it can't be evicted. * @func: Method vtable for this resource. Immutable. - * @mob_node; Node for the MOB backup rbtree. Protected by @backup reserved. * @lru_head: List head for the LRU list. Protected by @dev_priv::resource_lock. + * @mob_head: List head for the MOB backup list. Protected by @backup reserved. * @binding_head: List head for the context binding list. Protected by * the @dev_priv::binding_mutex * @res_free: The resource destructor. * @hw_destroy: Callback to destroy the resource on the device, as part of * resource destruction. */ -struct vmw_resource_dirty; struct vmw_resource { struct kref kref; struct vmw_private *dev_priv; int id; - u32 used_prio; unsigned long backup_size; - u32 res_dirty : 1; - u32 backup_dirty : 1; - u32 coherent : 1; + bool res_dirty; + bool backup_dirty; struct vmw_buffer_object *backup; unsigned long backup_offset; unsigned long pin_count; const struct vmw_res_func *func; - struct rb_node mob_node; struct list_head lru_head; + struct list_head mob_head; struct list_head binding_head; - struct vmw_resource_dirty *dirty; void (*res_free) (struct vmw_resource *res); void (*hw_destroy) (struct vmw_resource *res); }; @@ -392,6 +376,10 @@ struct vmw_sw_context{ struct vmw_legacy_display; struct vmw_overlay; +struct vmw_master { + struct ttm_lock lock; +}; + struct vmw_vga_topology_state { uint32_t width; uint32_t height; @@ -554,8 +542,11 @@ struct vmw_private { spinlock_t svga_lock; /** - * PM management. + * Master management. */ + + struct vmw_master *active_master; + struct vmw_master fbdev_master; struct notifier_block pm_nb; bool refuse_hibernation; bool suspend_locked; @@ -604,9 +595,6 @@ struct vmw_private { /* Validation memory reservation */ struct vmw_validation_mem vvm; - - /* VM operations */ - struct vm_operations_struct vm_ops; }; static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res) @@ -624,6 +612,11 @@ static inline struct vmw_fpriv *vmw_fpriv(struct drm_file *file_priv) return (struct vmw_fpriv *)file_priv->driver_priv; } +static inline struct vmw_master *vmw_master(struct drm_master *master) +{ + return (struct vmw_master *) master->driver_priv; +} + /* * The locking here is fine-grained, so that it is performed once * for every read- and write operation. This is of course costly, but we @@ -676,8 +669,7 @@ extern void vmw_resource_unreference(struct vmw_resource **p_res); extern struct vmw_resource *vmw_resource_reference(struct vmw_resource *res); extern struct vmw_resource * vmw_resource_reference_unless_doomed(struct vmw_resource *res); -extern int vmw_resource_validate(struct vmw_resource *res, bool intr, - bool dirtying); +extern int vmw_resource_validate(struct vmw_resource *res, bool intr); extern int vmw_resource_reserve(struct vmw_resource *res, bool interruptible, bool no_backup); extern bool vmw_resource_needs_backup(const struct vmw_resource *res); @@ -717,23 +709,6 @@ extern void vmw_query_move_notify(struct ttm_buffer_object *bo, extern int vmw_query_readback_all(struct vmw_buffer_object *dx_query_mob); extern void vmw_resource_evict_all(struct vmw_private *dev_priv); extern void vmw_resource_unbind_list(struct vmw_buffer_object *vbo); -void vmw_resource_mob_attach(struct vmw_resource *res); -void vmw_resource_mob_detach(struct vmw_resource *res); -void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start, - pgoff_t end); -int vmw_resources_clean(struct vmw_buffer_object *vbo, pgoff_t start, - pgoff_t end, pgoff_t *num_prefault); - -/** - * vmw_resource_mob_attached - Whether a resource currently has a mob attached - * @res: The resource - * - * Return: true if the resource has a mob attached, false otherwise. - */ -static inline bool vmw_resource_mob_attached(const struct vmw_resource *res) -{ - return !RB_EMPTY_NODE(&res->mob_node); -} /** * vmw_user_resource_noref_release - release a user resource pointer looked up @@ -812,54 +787,6 @@ static inline void vmw_user_bo_noref_release(void) ttm_base_object_noref_release(); } -/** - * vmw_bo_adjust_prio - Adjust the buffer object eviction priority - * according to attached resources - * @vbo: The struct vmw_buffer_object - */ -static inline void vmw_bo_prio_adjust(struct vmw_buffer_object *vbo) -{ - int i = ARRAY_SIZE(vbo->res_prios); - - while (i--) { - if (vbo->res_prios[i]) { - vbo->base.priority = i; - return; - } - } - - vbo->base.priority = 3; -} - -/** - * vmw_bo_prio_add - Notify a buffer object of a newly attached resource - * eviction priority - * @vbo: The struct vmw_buffer_object - * @prio: The resource priority - * - * After being notified, the code assigns the highest resource eviction priority - * to the backing buffer object (mob). - */ -static inline void vmw_bo_prio_add(struct vmw_buffer_object *vbo, int prio) -{ - if (vbo->res_prios[prio]++ == 0) - vmw_bo_prio_adjust(vbo); -} - -/** - * vmw_bo_prio_del - Notify a buffer object of a resource with a certain - * priority being removed - * @vbo: The struct vmw_buffer_object - * @prio: The resource priority - * - * After being notified, the code assigns the highest resource eviction priority - * to the backing buffer object (mob). - */ -static inline void vmw_bo_prio_del(struct vmw_buffer_object *vbo, int prio) -{ - if (--vbo->res_prios[prio] == 0) - vmw_bo_prio_adjust(vbo); -} /** * Misc Ioctl functionality - vmwgfx_ioctl.c @@ -1089,6 +1016,7 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf, int vmw_kms_write_svga(struct vmw_private *vmw_priv, unsigned width, unsigned height, unsigned pitch, unsigned bpp, unsigned depth); +void vmw_kms_idle_workqueues(struct vmw_master *vmaster); bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, uint32_t pitch, uint32_t height); @@ -1410,25 +1338,6 @@ int vmw_host_log(const char *log); #define VMW_DEBUG_USER(fmt, ...) \ DRM_DEBUG_DRIVER(fmt, ##__VA_ARGS__) -/** - * VMW_DEBUG_KMS - Debug output for kernel mode-setting - * - * This macro is for debugging vmwgfx mode-setting code. - */ -#define VMW_DEBUG_KMS(fmt, ...) \ - DRM_DEBUG_DRIVER(fmt, ##__VA_ARGS__) - -/* Resource dirtying - vmwgfx_page_dirty.c */ -void vmw_bo_dirty_scan(struct vmw_buffer_object *vbo); -int vmw_bo_dirty_add(struct vmw_buffer_object *vbo); -void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res); -void vmw_bo_dirty_clear_res(struct vmw_resource *res); -void vmw_bo_dirty_release(struct vmw_buffer_object *vbo); -void vmw_bo_dirty_unmap(struct vmw_buffer_object *vbo, - pgoff_t start, pgoff_t end); -vm_fault_t vmw_bo_vm_fault(struct vm_fault *vmf); -vm_fault_t vmw_bo_vm_mkwrite(struct vm_fault *vmf); - /** * Inline helper functions */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 319c1ca35663..33533d126277 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -2560,6 +2560,7 @@ static int vmw_cmd_dx_check_subresource(struct vmw_private *dev_priv, offsetof(typeof(*cmd), sid)); cmd = container_of(header, typeof(*cmd), header); + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, VMW_RES_DIRTY_NONE, user_surface_converter, &cmd->sid, NULL); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index e7222fa2cfdf..b97bc8e5944b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1462,7 +1462,7 @@ static int vmw_kms_check_display_memory(struct drm_device *dev, if (dev_priv->active_display_unit == vmw_du_screen_target && (drm_rect_width(&rects[i]) > dev_priv->stdu_max_width || drm_rect_height(&rects[i]) > dev_priv->stdu_max_height)) { - VMW_DEBUG_KMS("Screen size not supported.\n"); + DRM_ERROR("Screen size not supported.\n"); return -EINVAL; } @@ -1486,7 +1486,7 @@ static int vmw_kms_check_display_memory(struct drm_device *dev, * limit on primary bounding box */ if (pixel_mem > dev_priv->prim_bb_mem) { - VMW_DEBUG_KMS("Combined output size too large.\n"); + DRM_ERROR("Combined output size too large.\n"); return -EINVAL; } @@ -1496,7 +1496,7 @@ static int vmw_kms_check_display_memory(struct drm_device *dev, bb_mem = (u64) bounding_box.x2 * bounding_box.y2 * 4; if (bb_mem > dev_priv->prim_bb_mem) { - VMW_DEBUG_KMS("Topology is beyond supported limits.\n"); + DRM_ERROR("Topology is beyond supported limits.\n"); return -EINVAL; } } @@ -1645,7 +1645,6 @@ static int vmw_kms_check_topology(struct drm_device *dev, struct vmw_connector_state *vmw_conn_state; if (!du->pref_active && new_crtc_state->enable) { - VMW_DEBUG_KMS("Enabling a disabled display unit\n"); ret = -EINVAL; goto clean; } @@ -1702,10 +1701,8 @@ vmw_kms_atomic_check_modeset(struct drm_device *dev, return ret; ret = vmw_kms_check_implicit(dev, state); - if (ret) { - VMW_DEBUG_KMS("Invalid implicit state\n"); + if (ret) return ret; - } if (!state->allow_modeset) return ret; @@ -2350,9 +2347,6 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, if (!arg->num_outputs) { struct drm_rect def_rect = {0, 0, 800, 600}; - VMW_DEBUG_KMS("Default layout x1 = %d y1 = %d x2 = %d y2 = %d\n", - def_rect.x1, def_rect.y1, - def_rect.x2, def_rect.y2); vmw_du_update_layout(dev_priv, 1, &def_rect); return 0; } @@ -2373,7 +2367,6 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, drm_rects = (struct drm_rect *)rects; - VMW_DEBUG_KMS("Layout count = %u\n", arg->num_outputs); for (i = 0; i < arg->num_outputs; i++) { struct drm_vmw_rect curr_rect; @@ -2390,10 +2383,6 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, drm_rects[i].x2 = curr_rect.x + curr_rect.w; drm_rects[i].y2 = curr_rect.y + curr_rect.h; - VMW_DEBUG_KMS(" x1 = %d y1 = %d x2 = %d y2 = %d\n", - drm_rects[i].x1, drm_rects[i].y1, - drm_rects[i].x2, drm_rects[i].y2); - /* * Currently this check is limiting the topology within * mode_config->max (which actually is max texture size @@ -2404,9 +2393,7 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, if (drm_rects[i].x1 < 0 || drm_rects[i].y1 < 0 || drm_rects[i].x2 > mode_config->max_width || drm_rects[i].y2 > mode_config->max_height) { - VMW_DEBUG_KMS("Invalid layout %d %d %d %d\n", - drm_rects[i].x1, drm_rects[i].y1, - drm_rects[i].x2, drm_rects[i].y2); + DRM_ERROR("Invalid GUI layout.\n"); ret = -EINVAL; goto out_free; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c deleted file mode 100644 index 730c51e397dd..000000000000 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c +++ /dev/null @@ -1,472 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 OR MIT -/************************************************************************** - * - * Copyright 2019 VMware, Inc., Palo Alto, CA., USA - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - **************************************************************************/ -#include "vmwgfx_drv.h" - -/* - * Different methods for tracking dirty: - * VMW_BO_DIRTY_PAGETABLE - Scan the pagetable for hardware dirty bits - * VMW_BO_DIRTY_MKWRITE - Write-protect page table entries and record write- - * accesses in the VM mkwrite() callback - */ -enum vmw_bo_dirty_method { - VMW_BO_DIRTY_PAGETABLE, - VMW_BO_DIRTY_MKWRITE, -}; - -/* - * No dirtied pages at scan trigger a transition to the _MKWRITE method, - * similarly a certain percentage of dirty pages trigger a transition to - * the _PAGETABLE method. How many triggers should we wait for before - * changing method? - */ -#define VMW_DIRTY_NUM_CHANGE_TRIGGERS 2 - -/* Percentage to trigger a transition to the _PAGETABLE method */ -#define VMW_DIRTY_PERCENTAGE 10 - -/** - * struct vmw_bo_dirty - Dirty information for buffer objects - * @start: First currently dirty bit - * @end: Last currently dirty bit + 1 - * @method: The currently used dirty method - * @change_count: Number of consecutive method change triggers - * @ref_count: Reference count for this structure - * @bitmap_size: The size of the bitmap in bits. Typically equal to the - * nuber of pages in the bo. - * @size: The accounting size for this struct. - * @bitmap: A bitmap where each bit represents a page. A set bit means a - * dirty page. - */ -struct vmw_bo_dirty { - unsigned long start; - unsigned long end; - enum vmw_bo_dirty_method method; - unsigned int change_count; - unsigned int ref_count; - unsigned long bitmap_size; - size_t size; - unsigned long bitmap[0]; -}; - -/** - * vmw_bo_dirty_scan_pagetable - Perform a pagetable scan for dirty bits - * @vbo: The buffer object to scan - * - * Scans the pagetable for dirty bits. Clear those bits and modify the - * dirty structure with the results. This function may change the - * dirty-tracking method. - */ -static void vmw_bo_dirty_scan_pagetable(struct vmw_buffer_object *vbo) -{ - struct vmw_bo_dirty *dirty = vbo->dirty; - pgoff_t offset = drm_vma_node_start(&vbo->base.vma_node); - struct address_space *mapping = vbo->base.bdev->dev_mapping; - pgoff_t num_marked; - - num_marked = apply_as_clean(mapping, - offset, dirty->bitmap_size, - offset, &dirty->bitmap[0], - &dirty->start, &dirty->end); - if (num_marked == 0) - dirty->change_count++; - else - dirty->change_count = 0; - - if (dirty->change_count > VMW_DIRTY_NUM_CHANGE_TRIGGERS) { - dirty->change_count = 0; - dirty->method = VMW_BO_DIRTY_MKWRITE; - apply_as_wrprotect(mapping, - offset, dirty->bitmap_size); - apply_as_clean(mapping, - offset, dirty->bitmap_size, - offset, &dirty->bitmap[0], - &dirty->start, &dirty->end); - } -} - -/** - * vmw_bo_dirty_scan_mkwrite - Reset the mkwrite dirty-tracking method - * @vbo: The buffer object to scan - * - * Write-protect pages written to so that consecutive write accesses will - * trigger a call to mkwrite. - * - * This function may change the dirty-tracking method. - */ -static void vmw_bo_dirty_scan_mkwrite(struct vmw_buffer_object *vbo) -{ - struct vmw_bo_dirty *dirty = vbo->dirty; - unsigned long offset = drm_vma_node_start(&vbo->base.vma_node); - struct address_space *mapping = vbo->base.bdev->dev_mapping; - pgoff_t num_marked; - - if (dirty->end <= dirty->start) - return; - - num_marked = apply_as_wrprotect(vbo->base.bdev->dev_mapping, - dirty->start + offset, - dirty->end - dirty->start); - - if (100UL * num_marked / dirty->bitmap_size > - VMW_DIRTY_PERCENTAGE) { - dirty->change_count++; - } else { - dirty->change_count = 0; - } - - if (dirty->change_count > VMW_DIRTY_NUM_CHANGE_TRIGGERS) { - pgoff_t start = 0; - pgoff_t end = dirty->bitmap_size; - - dirty->method = VMW_BO_DIRTY_PAGETABLE; - apply_as_clean(mapping, offset, end, offset, &dirty->bitmap[0], - &start, &end); - bitmap_clear(&dirty->bitmap[0], 0, dirty->bitmap_size); - if (dirty->start < dirty->end) - bitmap_set(&dirty->bitmap[0], dirty->start, - dirty->end - dirty->start); - dirty->change_count = 0; - } -} - -/** - * vmw_bo_dirty_scan - Scan for dirty pages and add them to the dirty - * tracking structure - * @vbo: The buffer object to scan - * - * This function may change the dirty tracking method. - */ -void vmw_bo_dirty_scan(struct vmw_buffer_object *vbo) -{ - struct vmw_bo_dirty *dirty = vbo->dirty; - - if (dirty->method == VMW_BO_DIRTY_PAGETABLE) - vmw_bo_dirty_scan_pagetable(vbo); - else - vmw_bo_dirty_scan_mkwrite(vbo); -} - -/** - * vmw_bo_dirty_pre_unmap - write-protect and pick up dirty pages before - * an unmap_mapping_range operation. - * @vbo: The buffer object, - * @start: First page of the range within the buffer object. - * @end: Last page of the range within the buffer object + 1. - * - * If we're using the _PAGETABLE scan method, we may leak dirty pages - * when calling unmap_mapping_range(). This function makes sure we pick - * up all dirty pages. - */ -static void vmw_bo_dirty_pre_unmap(struct vmw_buffer_object *vbo, - pgoff_t start, pgoff_t end) -{ - struct vmw_bo_dirty *dirty = vbo->dirty; - unsigned long offset = drm_vma_node_start(&vbo->base.vma_node); - struct address_space *mapping = vbo->base.bdev->dev_mapping; - - if (dirty->method != VMW_BO_DIRTY_PAGETABLE || start >= end) - return; - - apply_as_wrprotect(mapping, start + offset, end - start); - apply_as_clean(mapping, start + offset, end - start, offset, - &dirty->bitmap[0], &dirty->start, &dirty->end); -} - -/** - * vmw_bo_dirty_unmap - Clear all ptes pointing to a range within a bo - * @vbo: The buffer object, - * @start: First page of the range within the buffer object. - * @end: Last page of the range within the buffer object + 1. - * - * This is similar to ttm_bo_unmap_virtual_locked() except it takes a subrange. - */ -void vmw_bo_dirty_unmap(struct vmw_buffer_object *vbo, - pgoff_t start, pgoff_t end) -{ - unsigned long offset = drm_vma_node_start(&vbo->base.vma_node); - struct address_space *mapping = vbo->base.bdev->dev_mapping; - - vmw_bo_dirty_pre_unmap(vbo, start, end); - unmap_shared_mapping_range(mapping, (offset + start) << PAGE_SHIFT, - (loff_t) (end - start) << PAGE_SHIFT); -} - -/** - * vmw_bo_dirty_add - Add a dirty-tracking user to a buffer object - * @vbo: The buffer object - * - * This function registers a dirty-tracking user to a buffer object. - * A user can be for example a resource or a vma in a special user-space - * mapping. - * - * Return: Zero on success, -ENOMEM on memory allocation failure. - */ -int vmw_bo_dirty_add(struct vmw_buffer_object *vbo) -{ - struct vmw_bo_dirty *dirty = vbo->dirty; - pgoff_t num_pages = vbo->base.num_pages; - size_t size, acc_size; - int ret; - static struct ttm_operation_ctx ctx = { - .interruptible = false, - .no_wait_gpu = false - }; - - if (dirty) { - dirty->ref_count++; - return 0; - } - - size = sizeof(*dirty) + BITS_TO_LONGS(num_pages) * sizeof(long); - acc_size = ttm_round_pot(size); - ret = ttm_mem_global_alloc(&ttm_mem_glob, acc_size, &ctx); - if (ret) { - VMW_DEBUG_USER("Out of graphics memory for buffer object " - "dirty tracker.\n"); - return ret; - } - dirty = kvzalloc(size, GFP_KERNEL); - if (!dirty) { - ret = -ENOMEM; - goto out_no_dirty; - } - - dirty->size = acc_size; - dirty->bitmap_size = num_pages; - dirty->start = dirty->bitmap_size; - dirty->end = 0; - dirty->ref_count = 1; - if (num_pages < PAGE_SIZE / sizeof(pte_t)) { - dirty->method = VMW_BO_DIRTY_PAGETABLE; - } else { - struct address_space *mapping = vbo->base.bdev->dev_mapping; - pgoff_t offset = drm_vma_node_start(&vbo->base.vma_node); - - dirty->method = VMW_BO_DIRTY_MKWRITE; - - /* Write-protect and then pick up already dirty bits */ - apply_as_wrprotect(mapping, offset, num_pages); - apply_as_clean(mapping, offset, num_pages, offset, - &dirty->bitmap[0], &dirty->start, &dirty->end); - } - - vbo->dirty = dirty; - - return 0; - -out_no_dirty: - ttm_mem_global_free(&ttm_mem_glob, acc_size); - return ret; -} - -/** - * vmw_bo_dirty_release - Release a dirty-tracking user from a buffer object - * @vbo: The buffer object - * - * This function releases a dirty-tracking user from a buffer object. - * If the reference count reaches zero, then the dirty-tracking object is - * freed and the pointer to it cleared. - * - * Return: Zero on success, -ENOMEM on memory allocation failure. - */ -void vmw_bo_dirty_release(struct vmw_buffer_object *vbo) -{ - struct vmw_bo_dirty *dirty = vbo->dirty; - - if (dirty && --dirty->ref_count == 0) { - size_t acc_size = dirty->size; - - kvfree(dirty); - ttm_mem_global_free(&ttm_mem_glob, acc_size); - vbo->dirty = NULL; - } -} - -/** - * vmw_bo_dirty_transfer_to_res - Pick up a resource's dirty region from - * its backing mob. - * @res: The resource - * - * This function will pick up all dirty ranges affecting the resource from - * it's backup mob, and call vmw_resource_dirty_update() once for each - * range. The transferred ranges will be cleared from the backing mob's - * dirty tracking. - */ -void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res) -{ - struct vmw_buffer_object *vbo = res->backup; - struct vmw_bo_dirty *dirty = vbo->dirty; - pgoff_t start, cur, end; - unsigned long res_start = res->backup_offset; - unsigned long res_end = res->backup_offset + res->backup_size; - - WARN_ON_ONCE(res_start & ~PAGE_MASK); - res_start >>= PAGE_SHIFT; - res_end = DIV_ROUND_UP(res_end, PAGE_SIZE); - - if (res_start >= dirty->end || res_end <= dirty->start) - return; - - cur = max(res_start, dirty->start); - res_end = max(res_end, dirty->end); - while (cur < res_end) { - unsigned long num; - - start = find_next_bit(&dirty->bitmap[0], res_end, cur); - if (start >= res_end) - break; - - end = find_next_zero_bit(&dirty->bitmap[0], res_end, start + 1); - cur = end + 1; - num = end - start; - bitmap_clear(&dirty->bitmap[0], start, num); - vmw_resource_dirty_update(res, start, end); - } - - if (res_start <= dirty->start && res_end > dirty->start) - dirty->start = res_end; - if (res_start < dirty->end && res_end >= dirty->end) - dirty->end = res_start; -} - -/** - * vmw_bo_dirty_clear_res - Clear a resource's dirty region from - * its backing mob. - * @res: The resource - * - * This function will clear all dirty ranges affecting the resource from - * it's backup mob's dirty tracking. - */ -void vmw_bo_dirty_clear_res(struct vmw_resource *res) -{ - unsigned long res_start = res->backup_offset; - unsigned long res_end = res->backup_offset + res->backup_size; - struct vmw_buffer_object *vbo = res->backup; - struct vmw_bo_dirty *dirty = vbo->dirty; - - res_start >>= PAGE_SHIFT; - res_end = DIV_ROUND_UP(res_end, PAGE_SIZE); - - if (res_start >= dirty->end || res_end <= dirty->start) - return; - - res_start = max(res_start, dirty->start); - res_end = min(res_end, dirty->end); - bitmap_clear(&dirty->bitmap[0], res_start, res_end - res_start); - - if (res_start <= dirty->start && res_end > dirty->start) - dirty->start = res_end; - if (res_start < dirty->end && res_end >= dirty->end) - dirty->end = res_start; -} - -vm_fault_t vmw_bo_vm_mkwrite(struct vm_fault *vmf) -{ - struct vm_area_struct *vma = vmf->vma; - struct ttm_buffer_object *bo = (struct ttm_buffer_object *) - vma->vm_private_data; - vm_fault_t ret; - unsigned long page_offset; - struct vmw_buffer_object *vbo = - container_of(bo, typeof(*vbo), base); - - ret = ttm_bo_vm_reserve(bo, vmf); - if (ret) - return ret; - - page_offset = vmf->pgoff - drm_vma_node_start(&bo->vma_node); - if (unlikely(page_offset >= bo->num_pages)) { - ret = VM_FAULT_SIGBUS; - goto out_unlock; - } - - if (vbo->dirty && vbo->dirty->method == VMW_BO_DIRTY_MKWRITE && - !test_bit(page_offset, &vbo->dirty->bitmap[0])) { - struct vmw_bo_dirty *dirty = vbo->dirty; - - __set_bit(page_offset, &dirty->bitmap[0]); - dirty->start = min(dirty->start, page_offset); - dirty->end = max(dirty->end, page_offset + 1); - } - -out_unlock: - reservation_object_unlock(bo->resv); - return ret; -} - -vm_fault_t vmw_bo_vm_fault(struct vm_fault *vmf) -{ - struct vm_area_struct *vma = vmf->vma; - struct ttm_buffer_object *bo = (struct ttm_buffer_object *) - vma->vm_private_data; - struct vmw_buffer_object *vbo = - container_of(bo, struct vmw_buffer_object, base); - pgoff_t num_prefault; - pgprot_t prot; - vm_fault_t ret; - - ret = ttm_bo_vm_reserve(bo, vmf); - if (ret) - return ret; - - num_prefault = (vma->vm_flags & VM_RAND_READ) ? 1 : - TTM_BO_VM_NUM_PREFAULT; - - if (vbo->dirty) { - pgoff_t allowed_prefault; - unsigned long page_offset; - - page_offset = vmf->pgoff - drm_vma_node_start(&bo->vma_node); - if (page_offset >= bo->num_pages || - vmw_resources_clean(vbo, page_offset, - page_offset + PAGE_SIZE, - &allowed_prefault)) { - ret = VM_FAULT_SIGBUS; - goto out_unlock; - } - - num_prefault = min(num_prefault, allowed_prefault); - } - - /* - * If we don't track dirty using the MKWRITE method, make sure - * sure the page protection is write-enabled so we don't get - * a lot of unnecessary write faults. - */ - if (vbo->dirty && vbo->dirty->method == VMW_BO_DIRTY_MKWRITE) - prot = vma->vm_page_prot; - else - prot = vm_get_page_prot(vma->vm_flags); - - ret = ttm_bo_vm_fault_reserved(vmf, prot, num_prefault); - if (ret == VM_FAULT_RETRY && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) - return ret; - -out_unlock: - reservation_object_unlock(bo->resv); - return ret; -} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index d70ee0df5c13..1d38a8b2f2ec 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -34,51 +34,6 @@ #define VMW_RES_EVICT_ERR_COUNT 10 -/** - * vmw_resource_mob_attach - Mark a resource as attached to its backing mob - * @res: The resource - */ -void vmw_resource_mob_attach(struct vmw_resource *res) -{ - struct vmw_buffer_object *backup = res->backup; - struct rb_node **new = &backup->res_tree.rb_node, *parent = NULL; - - lockdep_assert_held(&backup->base.resv->lock.base); - res->used_prio = (res->res_dirty) ? res->func->dirty_prio : - res->func->prio; - - while (*new) { - struct vmw_resource *this = - container_of(*new, struct vmw_resource, mob_node); - - parent = *new; - new = (res->backup_offset < this->backup_offset) ? - &((*new)->rb_left) : &((*new)->rb_right); - } - - rb_link_node(&res->mob_node, parent, new); - rb_insert_color(&res->mob_node, &backup->res_tree); - - vmw_bo_prio_add(backup, res->used_prio); -} - -/** - * vmw_resource_mob_detach - Mark a resource as detached from its backing mob - * @res: The resource - */ -void vmw_resource_mob_detach(struct vmw_resource *res) -{ - struct vmw_buffer_object *backup = res->backup; - - lockdep_assert_held(&backup->base.resv->lock.base); - if (vmw_resource_mob_attached(res)) { - rb_erase(&res->mob_node, &backup->res_tree); - RB_CLEAR_NODE(&res->mob_node); - vmw_bo_prio_del(backup, res->used_prio); - } -} - - struct vmw_resource *vmw_resource_reference(struct vmw_resource *res) { kref_get(&res->kref); @@ -125,7 +80,7 @@ static void vmw_resource_release(struct kref *kref) struct ttm_buffer_object *bo = &res->backup->base; ttm_bo_reserve(bo, false, false, NULL); - if (vmw_resource_mob_attached(res) && + if (!list_empty(&res->mob_head) && res->func->unbind != NULL) { struct ttm_validate_buffer val_buf; @@ -134,11 +89,7 @@ static void vmw_resource_release(struct kref *kref) res->func->unbind(res, false, &val_buf); } res->backup_dirty = false; - vmw_resource_mob_detach(res); - if (res->dirty) - res->func->dirty_free(res); - if (res->coherent) - vmw_bo_dirty_release(res->backup); + list_del_init(&res->mob_head); ttm_bo_unreserve(bo); vmw_bo_unreference(&res->backup); } @@ -220,17 +171,14 @@ int vmw_resource_init(struct vmw_private *dev_priv, struct vmw_resource *res, res->res_free = res_free; res->dev_priv = dev_priv; res->func = func; - RB_CLEAR_NODE(&res->mob_node); INIT_LIST_HEAD(&res->lru_head); + INIT_LIST_HEAD(&res->mob_head); INIT_LIST_HEAD(&res->binding_head); res->id = -1; res->backup = NULL; res->backup_offset = 0; res->backup_dirty = false; res->res_dirty = false; - res->coherent = false; - res->used_prio = 3; - res->dirty = NULL; if (delay_id) return 0; else @@ -395,8 +343,7 @@ out_no_bo: * should be retried once resources have been freed up. */ static int vmw_resource_do_validate(struct vmw_resource *res, - struct ttm_validate_buffer *val_buf, - bool dirtying) + struct ttm_validate_buffer *val_buf) { int ret = 0; const struct vmw_res_func *func = res->func; @@ -408,47 +355,14 @@ static int vmw_resource_do_validate(struct vmw_resource *res, } if (func->bind && - ((func->needs_backup && !vmw_resource_mob_attached(res) && + ((func->needs_backup && list_empty(&res->mob_head) && val_buf->bo != NULL) || (!func->needs_backup && val_buf->bo != NULL))) { ret = func->bind(res, val_buf); if (unlikely(ret != 0)) goto out_bind_failed; if (func->needs_backup) - vmw_resource_mob_attach(res); - } - - /* - * Handle the case where the backup mob is marked coherent but - * the resource isn't. - */ - if (func->dirty_alloc && vmw_resource_mob_attached(res) && - !res->coherent) { - if (res->backup->dirty && !res->dirty) { - ret = func->dirty_alloc(res); - if (ret) - return ret; - } else if (!res->backup->dirty && res->dirty) { - func->dirty_free(res); - } - } - - /* - * Transfer the dirty regions to the resource and update - * the resource. - */ - if (res->dirty) { - if (dirtying && !res->res_dirty) { - pgoff_t start = res->backup_offset >> PAGE_SHIFT; - pgoff_t end = __KERNEL_DIV_ROUND_UP - (res->backup_offset + res->backup_size, - PAGE_SIZE); - - vmw_bo_dirty_unmap(res->backup, start, end); - } - - vmw_bo_dirty_transfer_to_res(res); - return func->dirty_sync(res); + list_add_tail(&res->mob_head, &res->backup->res_list); } return 0; @@ -488,29 +402,19 @@ void vmw_resource_unreserve(struct vmw_resource *res, if (switch_backup && new_backup != res->backup) { if (res->backup) { - vmw_resource_mob_detach(res); - if (res->coherent) - vmw_bo_dirty_release(res->backup); + lockdep_assert_held(&res->backup->base.resv->lock.base); + list_del_init(&res->mob_head); vmw_bo_unreference(&res->backup); } if (new_backup) { res->backup = vmw_bo_reference(new_backup); - - /* - * The validation code should already have added a - * dirty tracker here. - */ - WARN_ON(res->coherent && !new_backup->dirty); - - vmw_resource_mob_attach(res); + lockdep_assert_held(&new_backup->base.resv->lock.base); + list_add_tail(&res->mob_head, &new_backup->res_list); } else { res->backup = NULL; } - } else if (switch_backup && res->coherent) { - vmw_bo_dirty_release(res->backup); } - if (switch_backup) res->backup_offset = new_backup_offset; @@ -565,7 +469,7 @@ vmw_resource_check_buffer(struct ww_acquire_ctx *ticket, if (unlikely(ret != 0)) goto out_no_reserve; - if (res->func->needs_backup && !vmw_resource_mob_attached(res)) + if (res->func->needs_backup && list_empty(&res->mob_head)) return 0; backup_dirty = res->backup_dirty; @@ -670,11 +574,11 @@ static int vmw_resource_do_evict(struct ww_acquire_ctx *ticket, return ret; if (unlikely(func->unbind != NULL && - (!func->needs_backup || vmw_resource_mob_attached(res)))) { + (!func->needs_backup || !list_empty(&res->mob_head)))) { ret = func->unbind(res, res->res_dirty, &val_buf); if (unlikely(ret != 0)) goto out_no_unbind; - vmw_resource_mob_detach(res); + list_del_init(&res->mob_head); } ret = func->destroy(res); res->backup_dirty = true; @@ -691,7 +595,6 @@ out_no_unbind: * to the device. * @res: The resource to make visible to the device. * @intr: Perform waits interruptible if possible. - * @dirtying: Pending GPU operation will dirty the resource * * On succesful return, any backup DMA buffer pointed to by @res->backup will * be reserved and validated. @@ -701,8 +604,7 @@ out_no_unbind: * Return: Zero on success, -ERESTARTSYS if interrupted, negative error code * on failure. */ -int vmw_resource_validate(struct vmw_resource *res, bool intr, - bool dirtying) +int vmw_resource_validate(struct vmw_resource *res, bool intr) { int ret; struct vmw_resource *evict_res; @@ -719,7 +621,7 @@ int vmw_resource_validate(struct vmw_resource *res, bool intr, if (res->backup) val_buf.bo = &res->backup->base; do { - ret = vmw_resource_do_validate(res, &val_buf, dirtying); + ret = vmw_resource_do_validate(res, &val_buf); if (likely(ret != -EBUSY)) break; @@ -758,7 +660,7 @@ int vmw_resource_validate(struct vmw_resource *res, bool intr, if (unlikely(ret != 0)) goto out_no_validate; else if (!res->func->needs_backup && res->backup) { - WARN_ON_ONCE(vmw_resource_mob_attached(res)); + list_del_init(&res->mob_head); vmw_bo_unreference(&res->backup); } @@ -782,23 +684,22 @@ out_no_validate: */ void vmw_resource_unbind_list(struct vmw_buffer_object *vbo) { + + struct vmw_resource *res, *next; struct ttm_validate_buffer val_buf = { .bo = &vbo->base, .num_shared = 0 }; lockdep_assert_held(&vbo->base.resv->lock.base); - while (!RB_EMPTY_ROOT(&vbo->res_tree)) { - struct rb_node *node = vbo->res_tree.rb_node; - struct vmw_resource *res = - container_of(node, struct vmw_resource, mob_node); - - if (!WARN_ON_ONCE(!res->func->unbind)) - (void) res->func->unbind(res, res->res_dirty, &val_buf); + list_for_each_entry_safe(res, next, &vbo->res_list, mob_head) { + if (!res->func->unbind) + continue; + (void) res->func->unbind(res, res->res_dirty, &val_buf); res->backup_dirty = true; res->res_dirty = false; - vmw_resource_mob_detach(res); + list_del_init(&res->mob_head); } (void) ttm_bo_wait(&vbo->base, false, false); @@ -1019,7 +920,7 @@ int vmw_resource_pin(struct vmw_resource *res, bool interruptible) /* Do we really need to pin the MOB as well? */ vmw_bo_pin_reserved(vbo, true); } - ret = vmw_resource_validate(res, interruptible, true); + ret = vmw_resource_validate(res, interruptible); if (vbo) ttm_bo_unreserve(&vbo->base); if (ret) @@ -1079,101 +980,3 @@ enum vmw_res_type vmw_res_type(const struct vmw_resource *res) { return res->func->res_type; } - -/** - * vmw_resource_update_dirty - Update a resource's dirty tracker with a - * sequential range of touched backing store memory. - * @res: The resource. - * @start: The first page touched. - * @end: The last page touched + 1. - */ -void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start, - pgoff_t end) -{ - if (res->dirty) - res->func->dirty_range_add(res, start << PAGE_SHIFT, - end << PAGE_SHIFT); -} - -/** - * vmw_resources_clean - Clean resources intersecting a mob range - * @vbo: The mob buffer object - * @start: The mob page offset starting the range - * @end: The mob page offset ending the range - * @num_prefault: Returns how many pages including the first have been - * cleaned and are ok to prefault - */ -int vmw_resources_clean(struct vmw_buffer_object *vbo, pgoff_t start, - pgoff_t end, pgoff_t *num_prefault) -{ - struct rb_node *cur = vbo->res_tree.rb_node; - struct vmw_resource *found = NULL; - unsigned long res_start = start << PAGE_SHIFT; - unsigned long res_end = end << PAGE_SHIFT; - unsigned long last_cleaned = 0; - - /* - * Find the resource with lowest backup_offset that intersects the - * range. - */ - while (cur) { - struct vmw_resource *cur_res = - container_of(cur, struct vmw_resource, mob_node); - - if (cur_res->backup_offset >= res_end) { - cur = cur->rb_left; - } else if (cur_res->backup_offset + cur_res->backup_size <= - res_start) { - cur = cur->rb_right; - } else { - found = cur_res; - cur = cur->rb_left; - /* Continue to look for resources with lower offsets */ - } - } - - /* - * In order of increasing backup_offset, clean dirty resorces - * intersecting the range. - */ - while (found) { - if (found->res_dirty) { - int ret; - - if (!found->func->clean) - return -EINVAL; - - ret = found->func->clean(found); - if (ret) - return ret; - - found->res_dirty = false; - } - last_cleaned = found->backup_offset + found->backup_size; - cur = rb_next(&found->mob_node); - if (!cur) - break; - - found = container_of(cur, struct vmw_resource, mob_node); - if (found->backup_offset >= res_end) - break; - } - - /* - * Set number of pages allowed prefaulting and fence the buffer object - */ - *num_prefault = 1; - if (last_cleaned > res_start) { - struct ttm_buffer_object *bo = &vbo->base; - - *num_prefault = __KERNEL_DIV_ROUND_UP(last_cleaned - res_start, - PAGE_SIZE); - vmw_bo_fence_single(bo, NULL); - if (bo->moving) - dma_fence_put(bo->moving); - bo->moving = dma_fence_get - (reservation_object_get_excl(bo->resv)); - } - - return 0; -} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h index 3b7438b2d289..7e19eba0b0b8 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h @@ -71,13 +71,6 @@ struct vmw_user_resource_conv { * @commit_notify: If the resource is a command buffer managed resource, * callback to notify that a define or remove command * has been committed to the device. - * @dirty_alloc: Allocate a dirty tracker. NULL if dirty-tracking is not - * supported. - * @dirty_free: Free the dirty tracker. - * @dirty_sync: Upload the dirty mob contents to the resource. - * @dirty_add_range: Add a sequential dirty range to the resource - * dirty tracker. - * @clean: Clean the resource. */ struct vmw_res_func { enum vmw_res_type res_type; @@ -85,8 +78,6 @@ struct vmw_res_func { const char *type_name; struct ttm_placement *backup_placement; bool may_evict; - u32 prio; - u32 dirty_prio; int (*create) (struct vmw_resource *res); int (*destroy) (struct vmw_resource *res); @@ -97,12 +88,6 @@ struct vmw_res_func { struct ttm_validate_buffer *val_buf); void (*commit_notify)(struct vmw_resource *res, enum vmw_cmdbuf_res_state state); - int (*dirty_alloc)(struct vmw_resource *res); - void (*dirty_free)(struct vmw_resource *res); - int (*dirty_sync)(struct vmw_resource *res); - void (*dirty_range_add)(struct vmw_resource *res, size_t start, - size_t end); - int (*clean)(struct vmw_resource *res); }; /** diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c index e139fdfd1635..d310d21f0d54 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c @@ -95,8 +95,6 @@ static const struct vmw_res_func vmw_gb_shader_func = { .res_type = vmw_res_shader, .needs_backup = true, .may_evict = true, - .prio = 3, - .dirty_prio = 3, .type_name = "guest backed shaders", .backup_placement = &vmw_mob_placement, .create = vmw_gb_shader_create, @@ -108,9 +106,7 @@ static const struct vmw_res_func vmw_gb_shader_func = { static const struct vmw_res_func vmw_dx_shader_func = { .res_type = vmw_res_shader, .needs_backup = true, - .may_evict = true, - .prio = 3, - .dirty_prio = 3, + .may_evict = false, .type_name = "dx shaders", .backup_placement = &vmw_mob_placement, .create = vmw_dx_shader_create, @@ -427,7 +423,7 @@ static int vmw_dx_shader_create(struct vmw_resource *res) WARN_ON_ONCE(!shader->committed); - if (vmw_resource_mob_attached(res)) { + if (!list_empty(&res->mob_head)) { mutex_lock(&dev_priv->binding_mutex); ret = vmw_dx_shader_unscrub(res); mutex_unlock(&dev_priv->binding_mutex); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index 862ca44680ca..219471903bc1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -68,20 +68,6 @@ struct vmw_surface_offset { uint32_t bo_offset; }; -/** - * vmw_surface_dirty - Surface dirty-tracker - * @cache: Cached layout information of the surface. - * @size: Accounting size for the struct vmw_surface_dirty. - * @num_subres: Number of subresources. - * @boxes: Array of SVGA3dBoxes indicating dirty regions. One per subresource. - */ -struct vmw_surface_dirty { - struct svga3dsurface_cache cache; - size_t size; - u32 num_subres; - SVGA3dBox boxes[0]; -}; - static void vmw_user_surface_free(struct vmw_resource *res); static struct vmw_resource * vmw_user_surface_base_to_res(struct ttm_base_object *base); @@ -110,13 +96,6 @@ vmw_gb_surface_reference_internal(struct drm_device *dev, struct drm_vmw_gb_surface_ref_ext_rep *rep, struct drm_file *file_priv); -static void vmw_surface_dirty_free(struct vmw_resource *res); -static int vmw_surface_dirty_alloc(struct vmw_resource *res); -static int vmw_surface_dirty_sync(struct vmw_resource *res); -static void vmw_surface_dirty_range_add(struct vmw_resource *res, size_t start, - size_t end); -static int vmw_surface_clean(struct vmw_resource *res); - static const struct vmw_user_resource_conv user_surface_conv = { .object_type = VMW_RES_SURFACE, .base_obj_to_res = vmw_user_surface_base_to_res, @@ -133,8 +112,6 @@ static const struct vmw_res_func vmw_legacy_surface_func = { .res_type = vmw_res_surface, .needs_backup = false, .may_evict = true, - .prio = 1, - .dirty_prio = 1, .type_name = "legacy surfaces", .backup_placement = &vmw_srf_placement, .create = &vmw_legacy_srf_create, @@ -147,19 +124,12 @@ static const struct vmw_res_func vmw_gb_surface_func = { .res_type = vmw_res_surface, .needs_backup = true, .may_evict = true, - .prio = 1, - .dirty_prio = 2, .type_name = "guest backed surfaces", .backup_placement = &vmw_mob_placement, .create = vmw_gb_surface_create, .destroy = vmw_gb_surface_destroy, .bind = vmw_gb_surface_bind, - .unbind = vmw_gb_surface_unbind, - .dirty_alloc = vmw_surface_dirty_alloc, - .dirty_free = vmw_surface_dirty_free, - .dirty_sync = vmw_surface_dirty_sync, - .dirty_range_add = vmw_surface_dirty_range_add, - .clean = vmw_surface_clean, + .unbind = vmw_gb_surface_unbind }; /** @@ -667,7 +637,6 @@ static void vmw_user_surface_free(struct vmw_resource *res) struct vmw_private *dev_priv = srf->res.dev_priv; uint32_t size = user_srf->size; - WARN_ON_ONCE(res->dirty); if (user_srf->master) drm_master_put(&user_srf->master); kfree(srf->offsets); @@ -946,6 +915,12 @@ vmw_surface_handle_reference(struct vmw_private *dev_priv, if (unlikely(drm_is_render_client(file_priv))) require_exist = true; + if (READ_ONCE(vmw_fpriv(file_priv)->locked_master)) { + DRM_ERROR("Locked master refused legacy " + "surface reference.\n"); + return -EACCES; + } + handle = u_handle; } @@ -1195,16 +1170,10 @@ static int vmw_gb_surface_bind(struct vmw_resource *res, cmd2->header.id = SVGA_3D_CMD_UPDATE_GB_SURFACE; cmd2->header.size = sizeof(cmd2->body); cmd2->body.sid = res->id; + res->backup_dirty = false; } vmw_fifo_commit(dev_priv, submit_size); - if (res->backup->dirty && res->backup_dirty) { - /* We've just made a full upload. Cear dirty regions. */ - vmw_bo_dirty_clear_res(res); - } - - res->backup_dirty = false; - return 0; } @@ -1669,8 +1638,7 @@ vmw_gb_surface_define_internal(struct drm_device *dev, } } } else if (req->base.drm_surface_flags & - (drm_vmw_surface_flag_create_buffer | - drm_vmw_surface_flag_coherent)) + drm_vmw_surface_flag_create_buffer) ret = vmw_user_bo_alloc(dev_priv, tfile, res->backup_size, req->base.drm_surface_flags & @@ -1684,26 +1652,6 @@ vmw_gb_surface_define_internal(struct drm_device *dev, goto out_unlock; } - if (req->base.drm_surface_flags & drm_vmw_surface_flag_coherent) { - struct vmw_buffer_object *backup = res->backup; - - ttm_bo_reserve(&backup->base, false, false, NULL); - if (!res->func->dirty_alloc) - ret = -EINVAL; - if (!ret) - ret = vmw_bo_dirty_add(backup); - if (!ret) { - res->coherent = true; - ret = res->func->dirty_alloc(res); - } - ttm_bo_unreserve(&backup->base); - if (ret) { - vmw_resource_unreference(&res); - goto out_unlock; - } - - } - tmp = vmw_resource_reference(res); ret = ttm_prime_object_init(tfile, res->backup_size, &user_srf->prime, req->base.drm_surface_flags & @@ -1812,338 +1760,3 @@ out_bad_resource: return ret; } - -/** - * vmw_subres_dirty_add - Add a dirty region to a subresource - * @dirty: The surfaces's dirty tracker. - * @loc_start: The location corresponding to the start of the region. - * @loc_end: The location corresponding to the end of the region. - * - * As we are assuming that @loc_start and @loc_end represent a sequential - * range of backing store memory, if the region spans multiple lines then - * regardless of the x coordinate, the full lines are dirtied. - * Correspondingly if the region spans multiple z slices, then full rather - * than partial z slices are dirtied. - */ -static void vmw_subres_dirty_add(struct vmw_surface_dirty *dirty, - const struct svga3dsurface_loc *loc_start, - const struct svga3dsurface_loc *loc_end) -{ - const struct svga3dsurface_cache *cache = &dirty->cache; - SVGA3dBox *box = &dirty->boxes[loc_start->sub_resource]; - u32 mip = loc_start->sub_resource % cache->num_mip_levels; - const struct drm_vmw_size *size = &cache->mip[mip].size; - u32 box_c2 = box->z + box->d; - - if (WARN_ON(loc_start->sub_resource >= dirty->num_subres)) - return; - - if (box->d == 0 || box->z > loc_start->z) - box->z = loc_start->z; - if (box_c2 < loc_end->z) - box->d = loc_end->z - box->z; - - if (loc_start->z + 1 == loc_end->z) { - box_c2 = box->y + box->h; - if (box->h == 0 || box->y > loc_start->y) - box->y = loc_start->y; - if (box_c2 < loc_end->y) - box->h = loc_end->y - box->y; - - if (loc_start->y + 1 == loc_end->y) { - box_c2 = box->x + box->w; - if (box->w == 0 || box->x > loc_start->x) - box->x = loc_start->x; - if (box_c2 < loc_end->x) - box->w = loc_end->x - box->x; - } else { - box->x = 0; - box->w = size->width; - } - } else { - box->y = 0; - box->h = size->height; - box->x = 0; - box->w = size->width; - } -} - -/** - * vmw_subres_dirty_full - Mark a full subresource as dirty - * @dirty: The surface's dirty tracker. - * @subres: The subresource - */ -static void vmw_subres_dirty_full(struct vmw_surface_dirty *dirty, u32 subres) -{ - const struct svga3dsurface_cache *cache = &dirty->cache; - u32 mip = subres % cache->num_mip_levels; - const struct drm_vmw_size *size = &cache->mip[mip].size; - SVGA3dBox *box = &dirty->boxes[subres]; - - box->x = 0; - box->y = 0; - box->z = 0; - box->w = size->width; - box->h = size->height; - box->d = size->depth; -} - -/* - * vmw_surface_tex_dirty_add_range - The dirty_add_range callback for texture - * surfaces. - */ -static void vmw_surface_tex_dirty_range_add(struct vmw_resource *res, - size_t start, size_t end) -{ - struct vmw_surface_dirty *dirty = - (struct vmw_surface_dirty *) res->dirty; - size_t backup_end = res->backup_offset + res->backup_size; - struct svga3dsurface_loc loc1, loc2; - const struct svga3dsurface_cache *cache; - - start = max_t(size_t, start, res->backup_offset) - res->backup_offset; - end = min(end, backup_end) - res->backup_offset; - cache = &dirty->cache; - svga3dsurface_get_loc(cache, &loc1, start); - svga3dsurface_get_loc(cache, &loc2, end - 1); - svga3dsurface_inc_loc(cache, &loc2); - - if (loc1.sub_resource + 1 == loc2.sub_resource) { - /* Dirty range covers a single sub-resource */ - vmw_subres_dirty_add(dirty, &loc1, &loc2); - } else { - /* Dirty range covers multiple sub-resources */ - struct svga3dsurface_loc loc_min, loc_max; - u32 sub_res = loc1.sub_resource; - - svga3dsurface_max_loc(cache, loc1.sub_resource, &loc_max); - vmw_subres_dirty_add(dirty, &loc1, &loc_max); - svga3dsurface_min_loc(cache, loc2.sub_resource - 1, &loc_min); - vmw_subres_dirty_add(dirty, &loc_min, &loc2); - for (sub_res = loc1.sub_resource + 1; - sub_res < loc2.sub_resource - 1; ++sub_res) - vmw_subres_dirty_full(dirty, sub_res); - } -} - -/* - * vmw_surface_tex_dirty_add_range - The dirty_add_range callback for buffer - * surfaces. - */ -static void vmw_surface_buf_dirty_range_add(struct vmw_resource *res, - size_t start, size_t end) -{ - struct vmw_surface_dirty *dirty = - (struct vmw_surface_dirty *) res->dirty; - const struct svga3dsurface_cache *cache = &dirty->cache; - size_t backup_end = res->backup_offset + cache->mip_chain_bytes; - SVGA3dBox *box = &dirty->boxes[0]; - u32 box_c2; - - box->h = box->d = 1; - start = max_t(size_t, start, res->backup_offset) - res->backup_offset; - end = min(end, backup_end) - res->backup_offset; - box_c2 = box->x + box->w; - if (box->w == 0 || box->x > start) - box->x = start; - if (box_c2 < end) - box->w = end - box->x; -} - -/* - * vmw_surface_tex_dirty_add_range - The dirty_add_range callback for surfaces - */ -static void vmw_surface_dirty_range_add(struct vmw_resource *res, size_t start, - size_t end) -{ - struct vmw_surface *srf = vmw_res_to_srf(res); - - if (WARN_ON(end <= res->backup_offset || - start >= res->backup_offset + res->backup_size)) - return; - - if (srf->format == SVGA3D_BUFFER) - vmw_surface_buf_dirty_range_add(res, start, end); - else - vmw_surface_tex_dirty_range_add(res, start, end); -} - -/* - * vmw_surface_dirty_sync - The surface's dirty_sync callback. - */ -static int vmw_surface_dirty_sync(struct vmw_resource *res) -{ - struct vmw_private *dev_priv = res->dev_priv; - bool has_dx = 0; - u32 i, num_dirty; - struct vmw_surface_dirty *dirty = - (struct vmw_surface_dirty *) res->dirty; - size_t alloc_size; - const struct svga3dsurface_cache *cache = &dirty->cache; - struct { - SVGA3dCmdHeader header; - SVGA3dCmdDXUpdateSubResource body; - } *cmd1; - struct { - SVGA3dCmdHeader header; - SVGA3dCmdUpdateGBImage body; - } *cmd2; - void *cmd; - - num_dirty = 0; - for (i = 0; i < dirty->num_subres; ++i) { - const SVGA3dBox *box = &dirty->boxes[i]; - - if (box->d) - num_dirty++; - } - - if (!num_dirty) - goto out; - - alloc_size = num_dirty * ((has_dx) ? sizeof(*cmd1) : sizeof(*cmd2)); - cmd = VMW_FIFO_RESERVE(dev_priv, alloc_size); - if (!cmd) - return -ENOMEM; - - cmd1 = cmd; - cmd2 = cmd; - - for (i = 0; i < dirty->num_subres; ++i) { - const SVGA3dBox *box = &dirty->boxes[i]; - - if (!box->d) - continue; - - /* - * DX_UPDATE_SUBRESOURCE is aware of array surfaces. - * UPDATE_GB_IMAGE is not. - */ - if (has_dx) { - cmd1->header.id = SVGA_3D_CMD_DX_UPDATE_SUBRESOURCE; - cmd1->header.size = sizeof(cmd1->body); - cmd1->body.sid = res->id; - cmd1->body.subResource = i; - cmd1->body.box = *box; - cmd1++; - } else { - cmd2->header.id = SVGA_3D_CMD_UPDATE_GB_IMAGE; - cmd2->header.size = sizeof(cmd2->body); - cmd2->body.image.sid = res->id; - cmd2->body.image.face = i / cache->num_mip_levels; - cmd2->body.image.mipmap = i - - (cache->num_mip_levels * cmd2->body.image.face); - cmd2->body.box = *box; - cmd2++; - } - - } - vmw_fifo_commit(dev_priv, alloc_size); - out: - memset(&dirty->boxes[0], 0, sizeof(dirty->boxes[0]) * - dirty->num_subres); - - return 0; -} - -/* - * vmw_surface_dirty_alloc - The surface's dirty_alloc callback. - */ -static int vmw_surface_dirty_alloc(struct vmw_resource *res) -{ - struct vmw_surface *srf = vmw_res_to_srf(res); - struct vmw_surface_dirty *dirty; - u32 num_layers = 1; - u32 num_mip; - u32 num_subres; - u32 num_samples; - size_t dirty_size, acc_size; - static struct ttm_operation_ctx ctx = { - .interruptible = false, - .no_wait_gpu = false - }; - int ret; - - if (srf->array_size) - num_layers = srf->array_size; - else if (srf->flags & SVGA3D_SURFACE_CUBEMAP) - num_layers *= SVGA3D_MAX_SURFACE_FACES; - - num_mip = srf->mip_levels[0]; - if (!num_mip) - num_mip = 1; - - num_subres = num_layers * num_mip; - dirty_size = sizeof(*dirty) + num_subres * sizeof(dirty->boxes[0]); - acc_size = ttm_round_pot(dirty_size); - ret = ttm_mem_global_alloc(vmw_mem_glob(res->dev_priv), - acc_size, &ctx); - if (ret) { - VMW_DEBUG_USER("Out of graphics memory for surface " - "dirty tracker.\n"); - return ret; - } - - dirty = kvzalloc(dirty_size, GFP_KERNEL); - if (!dirty) { - ret = -ENOMEM; - goto out_no_dirty; - } - - num_samples = max_t(u32, 1, srf->multisample_count); - ret = svga3dsurface_setup_cache(&srf->base_size, srf->format, num_mip, - num_layers, num_samples, &dirty->cache); - if (ret) - goto out_no_cache; - - dirty->num_subres = num_subres; - dirty->size = acc_size; - res->dirty = (struct vmw_resource_dirty *) dirty; - - return 0; - -out_no_cache: - kvfree(dirty); -out_no_dirty: - ttm_mem_global_free(vmw_mem_glob(res->dev_priv), acc_size); - return ret; -} - -/* - * vmw_surface_dirty_free - The surface's dirty_free callback - */ -static void vmw_surface_dirty_free(struct vmw_resource *res) -{ - struct vmw_surface_dirty *dirty = - (struct vmw_surface_dirty *) res->dirty; - size_t acc_size = dirty->size; - - kvfree(dirty); - ttm_mem_global_free(vmw_mem_glob(res->dev_priv), acc_size); - res->dirty = NULL; -} - -/* - * vmw_surface_clean - The surface's clean callback - */ -static int vmw_surface_clean(struct vmw_resource *res) -{ - struct vmw_private *dev_priv = res->dev_priv; - size_t alloc_size; - struct { - SVGA3dCmdHeader header; - SVGA3dCmdReadbackGBSurface body; - } *cmd; - - alloc_size = sizeof(*cmd); - cmd = VMW_FIFO_RESERVE(dev_priv, alloc_size); - if (!cmd) - return -ENOMEM; - - cmd->header.id = SVGA_3D_CMD_READBACK_GB_SURFACE; - cmd->header.size = sizeof(cmd->body); - cmd->body.sid = res->id; - vmw_fifo_commit(dev_priv, alloc_size); - - return 0; -} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c index 9aaf807ed73c..f611b2290a1b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c @@ -33,8 +33,6 @@ * struct vmw_validation_bo_node - Buffer object validation metadata. * @base: Metadata used for TTM reservation- and validation. * @hash: A hash entry used for the duplicate detection hash table. - * @coherent_count: If switching backup buffers, number of new coherent - * resources that will have this buffer as a backup buffer. * @as_mob: Validate as mob. * @cpu_blit: Validate for cpu blit access. * @@ -44,7 +42,6 @@ struct vmw_validation_bo_node { struct ttm_validate_buffer base; struct drm_hash_item hash; - unsigned int coherent_count; u32 as_mob : 1; u32 cpu_blit : 1; }; @@ -462,19 +459,6 @@ int vmw_validation_res_reserve(struct vmw_validation_context *ctx, if (ret) goto out_unreserve; } - - if (val->switching_backup && val->new_backup && - res->coherent) { - struct vmw_validation_bo_node *bo_node = - vmw_validation_find_bo_dup(ctx, - val->new_backup); - - if (WARN_ON(!bo_node)) { - ret = -EINVAL; - goto out_unreserve; - } - bo_node->coherent_count++; - } } return 0; @@ -578,9 +562,6 @@ int vmw_validation_bo_validate(struct vmw_validation_context *ctx, bool intr) int ret; list_for_each_entry(entry, &ctx->bo_list, base.head) { - struct vmw_buffer_object *vbo = - container_of(entry->base.bo, typeof(*vbo), base); - if (entry->cpu_blit) { struct ttm_operation_ctx ctx = { .interruptible = intr, @@ -595,27 +576,6 @@ int vmw_validation_bo_validate(struct vmw_validation_context *ctx, bool intr) } if (ret) return ret; - - /* - * Rather than having the resource code allocating the bo - * dirty tracker in resource_unreserve() where we can't fail, - * Do it here when validating the buffer object. - */ - if (entry->coherent_count) { - unsigned int coherent_count = entry->coherent_count; - - while (coherent_count) { - ret = vmw_bo_dirty_add(vbo); - if (ret) - return ret; - - coherent_count--; - } - entry->coherent_count -= coherent_count; - } - - if (vbo->dirty) - vmw_bo_dirty_scan(vbo); } return 0; } @@ -641,8 +601,7 @@ int vmw_validation_res_validate(struct vmw_validation_context *ctx, bool intr) struct vmw_resource *res = val->res; struct vmw_buffer_object *backup = res->backup; - ret = vmw_resource_validate(res, intr, val->dirty_set && - val->dirty); + ret = vmw_resource_validate(res, intr); if (ret) { if (ret != -ERESTARTSYS) DRM_ERROR("Failed to validate resource.\n"); @@ -869,34 +828,3 @@ int vmw_validation_preload_res(struct vmw_validation_context *ctx, ctx->mem_size_left += size; return 0; } - -/** - * vmw_validation_bo_backoff - Unreserve buffer objects registered with a - * validation context - * @ctx: The validation context - * - * This function unreserves the buffer objects previously reserved using - * vmw_validation_bo_reserve. It's typically used as part of an error path - */ -void vmw_validation_bo_backoff(struct vmw_validation_context *ctx) -{ - struct vmw_validation_bo_node *entry; - - /* - * Switching coherent resource backup buffers failed. - * Release corresponding buffer object dirty trackers. - */ - list_for_each_entry(entry, &ctx->bo_list, base.head) { - if (entry->coherent_count) { - unsigned int coherent_count = entry->coherent_count; - struct vmw_buffer_object *vbo = - container_of(entry->base.bo, typeof(*vbo), - base); - - while (coherent_count--) - vmw_bo_dirty_release(vbo); - } - } - - ttm_eu_backoff_reservation(&ctx->ticket, &ctx->bo_list); -} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h index fd83e017c2a5..1d2322ad6fd5 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h @@ -172,6 +172,20 @@ vmw_validation_bo_reserve(struct vmw_validation_context *ctx, NULL, true); } +/** + * vmw_validation_bo_backoff - Unreserve buffer objects registered with a + * validation context + * @ctx: The validation context + * + * This function unreserves the buffer objects previously reserved using + * vmw_validation_bo_reserve. It's typically used as part of an error path + */ +static inline void +vmw_validation_bo_backoff(struct vmw_validation_context *ctx) +{ + ttm_eu_backoff_reservation(&ctx->ticket, &ctx->bo_list); +} + /** * vmw_validation_bo_fence - Unreserve and fence buffer objects registered * with a validation context @@ -254,6 +268,4 @@ int vmw_validation_preload_res(struct vmw_validation_context *ctx, unsigned int size); void vmw_validation_res_set_dirty(struct vmw_validation_context *ctx, void *val_private, u32 dirty); -void vmw_validation_bo_backoff(struct vmw_validation_context *ctx); - #endif diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h index 435d02f719a8..49d9cdfc58f2 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo_api.h @@ -768,14 +768,4 @@ int ttm_bo_swapout(struct ttm_bo_global *glob, struct ttm_operation_ctx *ctx); void ttm_bo_swapout_all(struct ttm_bo_device *bdev); int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo); - -/* Default number of pre-faulted pages in the TTM fault handler */ -#define TTM_BO_VM_NUM_PREFAULT 16 - -vm_fault_t ttm_bo_vm_reserve(struct ttm_buffer_object *bo, - struct vm_fault *vmf); - -vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf, - pgprot_t prot, - pgoff_t num_prefault); #endif diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index a2d810a2504d..c9b8ba492f24 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -442,9 +442,6 @@ extern struct ttm_bo_global { * @driver: Pointer to a struct ttm_bo_driver struct setup by the driver. * @man: An array of mem_type_managers. * @vma_manager: Address space manager - * @vm_ops: Pointer to the struct vm_operations_struct used for this - * device's VM operations. The driver may override this before the first - * mmap() call. * lru_lock: Spinlock that protects the buffer+device lru lists and * ddestroy lists. * @dev_mapping: A pointer to the struct address_space representing the @@ -463,7 +460,6 @@ struct ttm_bo_device { struct ttm_bo_global *glob; struct ttm_bo_driver *driver; struct ttm_mem_type_manager man[TTM_NUM_MEM_TYPES]; - const struct vm_operations_struct *vm_ops; /* * Protected by internal locks. @@ -492,8 +488,6 @@ struct ttm_bo_device { bool no_retry; }; -extern const struct vm_operations_struct ttm_bo_vm_ops; - /** * struct ttm_lru_bulk_move_pos * diff --git a/include/linux/mm.h b/include/linux/mm.h index 798cdda9560e..dd0b5f4e1e45 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2686,24 +2686,7 @@ typedef int (*pte_fn_t)(pte_t *pte, pgtable_t token, unsigned long addr, extern int apply_to_page_range(struct mm_struct *mm, unsigned long address, unsigned long size, pte_fn_t fn, void *data); -struct pfn_range_apply; -typedef int (*pter_fn_t)(pte_t *pte, pgtable_t token, unsigned long addr, - struct pfn_range_apply *closure); -struct pfn_range_apply { - struct mm_struct *mm; - pter_fn_t ptefn; - unsigned int alloc; -}; -extern int apply_to_pfn_range(struct pfn_range_apply *closure, - unsigned long address, unsigned long size); -unsigned long apply_as_wrprotect(struct address_space *mapping, - pgoff_t first_index, pgoff_t nr); -unsigned long apply_as_clean(struct address_space *mapping, - pgoff_t first_index, pgoff_t nr, - pgoff_t bitmap_pgoff, - unsigned long *bitmap, - pgoff_t *start, - pgoff_t *end); + #ifdef CONFIG_PAGE_POISONING extern bool page_poisoning_enabled(void); extern void kernel_poison_pages(struct page *page, int numpages, int enable); diff --git a/include/uapi/drm/vmwgfx_drm.h b/include/uapi/drm/vmwgfx_drm.h index 02cab33f2f25..399f58317cff 100644 --- a/include/uapi/drm/vmwgfx_drm.h +++ b/include/uapi/drm/vmwgfx_drm.h @@ -891,13 +891,11 @@ struct drm_vmw_shader_arg { * surface. * @drm_vmw_surface_flag_create_buffer: Create a backup buffer if none is * given. - * @drm_vmw_surface_flag_coherent: Back surface with coherent memory. */ enum drm_vmw_surface_flags { drm_vmw_surface_flag_shareable = (1 << 0), drm_vmw_surface_flag_scanout = (1 << 1), - drm_vmw_surface_flag_create_buffer = (1 << 2), - drm_vmw_surface_flag_coherent = (1 << 3), + drm_vmw_surface_flag_create_buffer = (1 << 2) }; /** diff --git a/mm/Kconfig b/mm/Kconfig index 5006d0e6a5c7..f0c76ba47695 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -765,7 +765,4 @@ config GUP_BENCHMARK config ARCH_HAS_PTE_SPECIAL bool -config AS_DIRTY_HELPERS - bool - endmenu diff --git a/mm/Makefile b/mm/Makefile index f5d412bbc2f7..ac5e5ba78874 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -104,4 +104,3 @@ obj-$(CONFIG_HARDENED_USERCOPY) += usercopy.o obj-$(CONFIG_PERCPU_STATS) += percpu-stats.o obj-$(CONFIG_HMM) += hmm.o obj-$(CONFIG_MEMFD_CREATE) += memfd.o -obj-$(CONFIG_AS_DIRTY_HELPERS) += as_dirty_helpers.o diff --git a/mm/as_dirty_helpers.c b/mm/as_dirty_helpers.c deleted file mode 100644 index f600e31534fb..000000000000 --- a/mm/as_dirty_helpers.c +++ /dev/null @@ -1,300 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include -#include -#include - -/** - * struct apply_as - Closure structure for apply_as_range - * @base: struct pfn_range_apply we derive from - * @start: Address of first modified pte - * @end: Address of last modified pte + 1 - * @total: Total number of modified ptes - * @vma: Pointer to the struct vm_area_struct we're currently operating on - */ -struct apply_as { - struct pfn_range_apply base; - unsigned long start; - unsigned long end; - unsigned long total; - struct vm_area_struct *vma; -}; - -/** - * apply_pt_wrprotect - Leaf pte callback to write-protect a pte - * @pte: Pointer to the pte - * @token: Page table token, see apply_to_pfn_range() - * @addr: The virtual page address - * @closure: Pointer to a struct pfn_range_apply embedded in a - * struct apply_as - * - * The function write-protects a pte and records the range in - * virtual address space of touched ptes for efficient range TLB flushes. - * - * Return: Always zero. - */ -static int apply_pt_wrprotect(pte_t *pte, pgtable_t token, - unsigned long addr, - struct pfn_range_apply *closure) -{ - struct apply_as *aas = container_of(closure, typeof(*aas), base); - pte_t ptent = *pte; - - if (pte_write(ptent)) { - pte_t old_pte = ptep_modify_prot_start(aas->vma, addr, pte); - - ptent = pte_wrprotect(old_pte); - ptep_modify_prot_commit(aas->vma, addr, pte, old_pte, ptent); - aas->total++; - aas->start = min(aas->start, addr); - aas->end = max(aas->end, addr + PAGE_SIZE); - } - - return 0; -} - -/** - * struct apply_as_clean - Closure structure for apply_as_clean - * @base: struct apply_as we derive from - * @bitmap_pgoff: Address_space Page offset of the first bit in @bitmap - * @bitmap: Bitmap with one bit for each page offset in the address_space range - * covered. - * @start: Address_space page offset of first modified pte relative - * to @bitmap_pgoff - * @end: Address_space page offset of last modified pte relative - * to @bitmap_pgoff - */ -struct apply_as_clean { - struct apply_as base; - pgoff_t bitmap_pgoff; - unsigned long *bitmap; - pgoff_t start; - pgoff_t end; -}; - -/** - * apply_pt_clean - Leaf pte callback to clean a pte - * @pte: Pointer to the pte - * @token: Page table token, see apply_to_pfn_range() - * @addr: The virtual page address - * @closure: Pointer to a struct pfn_range_apply embedded in a - * struct apply_as_clean - * - * The function cleans a pte and records the range in - * virtual address space of touched ptes for efficient TLB flushes. - * It also records dirty ptes in a bitmap representing page offsets - * in the address_space, as well as the first and last of the bits - * touched. - * - * Return: Always zero. - */ -static int apply_pt_clean(pte_t *pte, pgtable_t token, - unsigned long addr, - struct pfn_range_apply *closure) -{ - struct apply_as *aas = container_of(closure, typeof(*aas), base); - struct apply_as_clean *clean = container_of(aas, typeof(*clean), base); - pte_t ptent = *pte; - - if (pte_dirty(ptent)) { - pgoff_t pgoff = ((addr - aas->vma->vm_start) >> PAGE_SHIFT) + - aas->vma->vm_pgoff - clean->bitmap_pgoff; - pte_t old_pte = ptep_modify_prot_start(aas->vma, addr, pte); - - ptent = pte_mkclean(old_pte); - ptep_modify_prot_commit(aas->vma, addr, pte, old_pte, ptent); - - aas->total++; - aas->start = min(aas->start, addr); - aas->end = max(aas->end, addr + PAGE_SIZE); - - __set_bit(pgoff, clean->bitmap); - clean->start = min(clean->start, pgoff); - clean->end = max(clean->end, pgoff + 1); - } - - return 0; -} - -/** - * apply_as_range - Apply a pte callback to all PTEs pointing into a range - * of an address_space. - * @mapping: Pointer to the struct address_space - * @aas: Closure structure - * @first_index: First page offset in the address_space - * @nr: Number of incremental page offsets to cover - * - * Return: Number of ptes touched. Note that this number might be larger - * than @nr if there are overlapping vmas - */ -static unsigned long apply_as_range(struct address_space *mapping, - struct apply_as *aas, - pgoff_t first_index, pgoff_t nr) -{ - struct vm_area_struct *vma; - pgoff_t vba, vea, cba, cea; - unsigned long start_addr, end_addr; - struct mmu_notifier_range range; - - i_mmap_lock_read(mapping); - vma_interval_tree_foreach(vma, &mapping->i_mmap, first_index, - first_index + nr - 1) { - unsigned long vm_flags = READ_ONCE(vma->vm_flags); - - /* - * We can only do advisory flag tests below, since we can't - * require the vm's mmap_sem to be held to protect the flags. - * Therefore, callers that strictly depend on specific mmap - * flags to remain constant throughout the operation must - * either ensure those flags are immutable for all relevant - * vmas or can't use this function. Fixing this properly would - * require the vma::vm_flags to be protected by a separate - * lock taken after the i_mmap_lock - */ - - /* Skip non-applicable VMAs */ - if ((vm_flags & (VM_SHARED | VM_WRITE)) != - (VM_SHARED | VM_WRITE)) - continue; - - /* Warn on and skip VMAs whose flags indicate illegal usage */ - if (WARN_ON((vm_flags & (VM_HUGETLB | VM_IO)) != VM_IO)) - continue; - - /* Clip to the vma */ - vba = vma->vm_pgoff; - vea = vba + vma_pages(vma); - cba = first_index; - cba = max(cba, vba); - cea = first_index + nr; - cea = min(cea, vea); - - /* Translate to virtual address */ - start_addr = ((cba - vba) << PAGE_SHIFT) + vma->vm_start; - end_addr = ((cea - vba) << PAGE_SHIFT) + vma->vm_start; - if (start_addr >= end_addr) - continue; - - aas->base.mm = vma->vm_mm; - aas->vma = vma; - aas->start = end_addr; - aas->end = start_addr; - - mmu_notifier_range_init(&range, MMU_NOTIFY_PROTECTION_PAGE, 0, - vma, vma->vm_mm, start_addr, end_addr); - mmu_notifier_invalidate_range_start(&range); - - /* Needed when we only change protection? */ - flush_cache_range(vma, start_addr, end_addr); - - /* - * We're not using tlb_gather_mmu() since typically - * only a small subrange of PTEs are affected. - */ - inc_tlb_flush_pending(vma->vm_mm); - - /* Should not error since aas->base.alloc == 0 */ - WARN_ON(apply_to_pfn_range(&aas->base, start_addr, - end_addr - start_addr)); - if (aas->end > aas->start) - flush_tlb_range(vma, aas->start, aas->end); - - mmu_notifier_invalidate_range_end(&range); - dec_tlb_flush_pending(vma->vm_mm); - } - i_mmap_unlock_read(mapping); - - return aas->total; -} - -/** - * apply_as_wrprotect - Write-protect all ptes in an address_space range - * @mapping: The address_space we want to write protect - * @first_index: The first page offset in the range - * @nr: Number of incremental page offsets to cover - * - * WARNING: This function should only be used for address spaces whose - * vmas are marked VM_IO and that do not contain huge pages. - * To avoid interference with COW'd pages, vmas not marked VM_SHARED are - * simply skipped. - * - * Return: The number of ptes actually write-protected. Note that - * already write-protected ptes are not counted. - */ -unsigned long apply_as_wrprotect(struct address_space *mapping, - pgoff_t first_index, pgoff_t nr) -{ - struct apply_as aas = { - .base = { - .alloc = 0, - .ptefn = apply_pt_wrprotect, - }, - .total = 0, - }; - - return apply_as_range(mapping, &aas, first_index, nr); -} -EXPORT_SYMBOL_GPL(apply_as_wrprotect); - -/** - * apply_as_clean - Clean all ptes in an address_space range - * @mapping: The address_space we want to clean - * @first_index: The first page offset in the range - * @nr: Number of incremental page offsets to cover - * @bitmap_pgoff: The page offset of the first bit in @bitmap - * @bitmap: Pointer to a bitmap of at least @nr bits. The bitmap needs to - * cover the whole range @first_index..@first_index + @nr. - * @start: Pointer to number of the first set bit in @bitmap. - * is modified as new bits are set by the function. - * @end: Pointer to the number of the last set bit in @bitmap. - * none set. The value is modified as new bits are set by the function. - * - * Note: When this function returns there is no guarantee that a CPU has - * not already dirtied new ptes. However it will not clean any ptes not - * reported in the bitmap. - * - * If a caller needs to make sure all dirty ptes are picked up and none - * additional are added, it first needs to write-protect the address-space - * range and make sure new writers are blocked in page_mkwrite() or - * pfn_mkwrite(). And then after a TLB flush following the write-protection - * pick up all dirty bits. - * - * WARNING: This function should only be used for address spaces whose - * vmas are marked VM_IO and that do not contain huge pages. - * To avoid interference with COW'd pages, vmas not marked VM_SHARED are - * simply skipped. - * - * Return: The number of dirty ptes actually cleaned. - */ -unsigned long apply_as_clean(struct address_space *mapping, - pgoff_t first_index, pgoff_t nr, - pgoff_t bitmap_pgoff, - unsigned long *bitmap, - pgoff_t *start, - pgoff_t *end) -{ - bool none_set = (*start >= *end); - struct apply_as_clean clean = { - .base = { - .base = { - .alloc = 0, - .ptefn = apply_pt_clean, - }, - .total = 0, - }, - .bitmap_pgoff = bitmap_pgoff, - .bitmap = bitmap, - .start = none_set ? nr : *start, - .end = none_set ? 0 : *end, - }; - unsigned long ret = apply_as_range(mapping, &clean.base, first_index, - nr); - - *start = clean.start; - *end = clean.end; - return ret; -} -EXPORT_SYMBOL_GPL(apply_as_clean); diff --git a/mm/memory.c b/mm/memory.c index 462aa47f8878..ddf20bd0c317 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2032,17 +2032,18 @@ int vm_iomap_memory(struct vm_area_struct *vma, phys_addr_t start, unsigned long } EXPORT_SYMBOL(vm_iomap_memory); -static int apply_to_pte_range(struct pfn_range_apply *closure, pmd_t *pmd, - unsigned long addr, unsigned long end) +static int apply_to_pte_range(struct mm_struct *mm, pmd_t *pmd, + unsigned long addr, unsigned long end, + pte_fn_t fn, void *data) { pte_t *pte; int err; pgtable_t token; spinlock_t *uninitialized_var(ptl); - pte = (closure->mm == &init_mm) ? + pte = (mm == &init_mm) ? pte_alloc_kernel(pmd, addr) : - pte_alloc_map_lock(closure->mm, pmd, addr, &ptl); + pte_alloc_map_lock(mm, pmd, addr, &ptl); if (!pte) return -ENOMEM; @@ -2053,109 +2054,86 @@ static int apply_to_pte_range(struct pfn_range_apply *closure, pmd_t *pmd, token = pmd_pgtable(*pmd); do { - err = closure->ptefn(pte++, token, addr, closure); + err = fn(pte++, token, addr, data); if (err) break; } while (addr += PAGE_SIZE, addr != end); arch_leave_lazy_mmu_mode(); - if (closure->mm != &init_mm) + if (mm != &init_mm) pte_unmap_unlock(pte-1, ptl); return err; } -static int apply_to_pmd_range(struct pfn_range_apply *closure, pud_t *pud, - unsigned long addr, unsigned long end) +static int apply_to_pmd_range(struct mm_struct *mm, pud_t *pud, + unsigned long addr, unsigned long end, + pte_fn_t fn, void *data) { pmd_t *pmd; unsigned long next; - int err = 0; + int err; BUG_ON(pud_huge(*pud)); - pmd = pmd_alloc(closure->mm, pud, addr); + pmd = pmd_alloc(mm, pud, addr); if (!pmd) return -ENOMEM; - do { next = pmd_addr_end(addr, end); - if (!closure->alloc && pmd_none_or_clear_bad(pmd)) - continue; - err = apply_to_pte_range(closure, pmd, addr, next); + err = apply_to_pte_range(mm, pmd, addr, next, fn, data); if (err) break; } while (pmd++, addr = next, addr != end); return err; } -static int apply_to_pud_range(struct pfn_range_apply *closure, p4d_t *p4d, - unsigned long addr, unsigned long end) +static int apply_to_pud_range(struct mm_struct *mm, p4d_t *p4d, + unsigned long addr, unsigned long end, + pte_fn_t fn, void *data) { pud_t *pud; unsigned long next; - int err = 0; + int err; - pud = pud_alloc(closure->mm, p4d, addr); + pud = pud_alloc(mm, p4d, addr); if (!pud) return -ENOMEM; - do { next = pud_addr_end(addr, end); - if (!closure->alloc && pud_none_or_clear_bad(pud)) - continue; - err = apply_to_pmd_range(closure, pud, addr, next); + err = apply_to_pmd_range(mm, pud, addr, next, fn, data); if (err) break; } while (pud++, addr = next, addr != end); return err; } -static int apply_to_p4d_range(struct pfn_range_apply *closure, pgd_t *pgd, - unsigned long addr, unsigned long end) +static int apply_to_p4d_range(struct mm_struct *mm, pgd_t *pgd, + unsigned long addr, unsigned long end, + pte_fn_t fn, void *data) { p4d_t *p4d; unsigned long next; - int err = 0; + int err; - p4d = p4d_alloc(closure->mm, pgd, addr); + p4d = p4d_alloc(mm, pgd, addr); if (!p4d) return -ENOMEM; - do { next = p4d_addr_end(addr, end); - if (!closure->alloc && p4d_none_or_clear_bad(p4d)) - continue; - err = apply_to_pud_range(closure, p4d, addr, next); + err = apply_to_pud_range(mm, p4d, addr, next, fn, data); if (err) break; } while (p4d++, addr = next, addr != end); return err; } -/** - * apply_to_pfn_range - Scan a region of virtual memory, calling a provided - * function on each leaf page table entry - * @closure: Details about how to scan and what function to apply - * @addr: Start virtual address - * @size: Size of the region - * - * If @closure->alloc is set to 1, the function will fill in the page table - * as necessary. Otherwise it will skip non-present parts. - * Note: The caller must ensure that the range does not contain huge pages. - * The caller must also assure that the proper mmu_notifier functions are - * called before and after the call to apply_to_pfn_range. - * - * WARNING: Do not use this function unless you know exactly what you are - * doing. It is lacking support for huge pages and transparent huge pages. - * - * Return: Zero on success. If the provided function returns a non-zero status, - * the page table walk will terminate and that status will be returned. - * If @closure->alloc is set to 1, then this function may also return memory - * allocation errors arising from allocating page table memory. +/* + * Scan a region of virtual memory, filling in page tables as necessary + * and calling a provided function on each leaf page table. */ -int apply_to_pfn_range(struct pfn_range_apply *closure, - unsigned long addr, unsigned long size) +int apply_to_page_range(struct mm_struct *mm, unsigned long addr, + unsigned long size, pte_fn_t fn, void *data) { pgd_t *pgd; unsigned long next; @@ -2165,65 +2143,16 @@ int apply_to_pfn_range(struct pfn_range_apply *closure, if (WARN_ON(addr >= end)) return -EINVAL; - pgd = pgd_offset(closure->mm, addr); + pgd = pgd_offset(mm, addr); do { next = pgd_addr_end(addr, end); - if (!closure->alloc && pgd_none_or_clear_bad(pgd)) - continue; - err = apply_to_p4d_range(closure, pgd, addr, next); + err = apply_to_p4d_range(mm, pgd, addr, next, fn, data); if (err) break; } while (pgd++, addr = next, addr != end); return err; } - -/** - * struct page_range_apply - Closure structure for apply_to_page_range() - * @pter: The base closure structure we derive from - * @fn: The leaf pte function to call - * @data: The leaf pte function closure - */ -struct page_range_apply { - struct pfn_range_apply pter; - pte_fn_t fn; - void *data; -}; - -/* - * Callback wrapper to enable use of apply_to_pfn_range for - * the apply_to_page_range interface - */ -static int apply_to_page_range_wrapper(pte_t *pte, pgtable_t token, - unsigned long addr, - struct pfn_range_apply *pter) -{ - struct page_range_apply *pra = - container_of(pter, typeof(*pra), pter); - - return pra->fn(pte, token, addr, pra->data); -} - -/* - * Scan a region of virtual memory, filling in page tables as necessary - * and calling a provided function on each leaf page table. - * - * WARNING: Do not use this function unless you know exactly what you are - * doing. It is lacking support for huge pages and transparent huge pages. - */ -int apply_to_page_range(struct mm_struct *mm, unsigned long addr, - unsigned long size, pte_fn_t fn, void *data) -{ - struct page_range_apply pra = { - .pter = {.mm = mm, - .alloc = 1, - .ptefn = apply_to_page_range_wrapper }, - .fn = fn, - .data = data - }; - - return apply_to_pfn_range(&pra.pter, addr, size); -} EXPORT_SYMBOL_GPL(apply_to_page_range); /* @@ -2309,7 +2238,7 @@ static vm_fault_t do_page_mkwrite(struct vm_fault *vmf) ret = vmf->vma->vm_ops->page_mkwrite(vmf); /* Restore original flags so that caller is not surprised */ vmf->flags = old_flags; - if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY))) + if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) return ret; if (unlikely(!(ret & VM_FAULT_LOCKED))) { lock_page(page); @@ -2586,7 +2515,7 @@ static vm_fault_t wp_pfn_shared(struct vm_fault *vmf) pte_unmap_unlock(vmf->pte, vmf->ptl); vmf->flags |= FAULT_FLAG_MKWRITE; ret = vma->vm_ops->pfn_mkwrite(vmf); - if (ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY)) + if (ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE)) return ret; return finish_mkwrite_fault(vmf); } @@ -2607,8 +2536,7 @@ static vm_fault_t wp_page_shared(struct vm_fault *vmf) pte_unmap_unlock(vmf->pte, vmf->ptl); tmp = do_page_mkwrite(vmf); if (unlikely(!tmp || (tmp & - (VM_FAULT_ERROR | VM_FAULT_NOPAGE | - VM_FAULT_RETRY)))) { + (VM_FAULT_ERROR | VM_FAULT_NOPAGE)))) { put_page(vmf->page); return tmp; } @@ -3673,8 +3601,7 @@ static vm_fault_t do_shared_fault(struct vm_fault *vmf) unlock_page(vmf->page); tmp = do_page_mkwrite(vmf); if (unlikely(!tmp || - (tmp & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | - VM_FAULT_RETRY)))) { + (tmp & (VM_FAULT_ERROR | VM_FAULT_NOPAGE)))) { put_page(vmf->page); return tmp; } -- cgit v1.2.3