summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2022-04-22 06:43:52 +1000
committerDave Airlie <airlied@redhat.com>2022-04-22 11:15:30 +1000
commit19df0cfa258cd42f7f106f6085f1e625f26283db (patch)
treebe212959c494f95279fd160ba04e9b498d60be8d /drivers/gpu
parentc54b39a565227538c52ead2349eb17d54aadd6f7 (diff)
parent40d8d4bd06720aed6c1125bab7296c57de4f1157 (diff)
Merge tag 'drm-misc-next-2022-04-21' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for 5.19-rc1 UAPI Changes: Cross-subsystem Changes: - of: Create a platform_device for offb Core Changes: - edid: block read refactoring - ttm: Add common debugfs code for resource managers Driver Changes: - bridges: - adv7611: Enable DRM_BRIDGE_OP_HPD if there's an interrupt - anx7625: Fill ELD if no monitor is connected - dw_hdmi: Add General Parallel Audio support - icn6211: Add data-lanes DT property - new driver: Lontium LT9211 - nouveau: make some structures static - tidss: Reset DISPC on startup - solomon: SPI Support and DT bindings improvements Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maxime Ripard <maxime@cerno.tech> Link: https://patchwork.freedesktop.org/patch/msgid/20220421065948.2pyp3j7acxtl6pz5@houat
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/Kconfig1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c15
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c55
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_display.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_object.c21
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_res_cursor.h97
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c89
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c7
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c11
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c11
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c359
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h89
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c3
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_plane.c10
-rw-r--r--drivers/gpu/drm/arm/malidp_crtc.c5
-rw-r--r--drivers/gpu/drm/arm/malidp_planes.c14
-rw-r--r--drivers/gpu/drm/bridge/Kconfig14
-rw-r--r--drivers/gpu/drm/bridge/Makefile1
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511_drv.c6
-rw-r--r--drivers/gpu/drm/bridge/analogix/anx7625.c24
-rw-r--r--drivers/gpu/drm/bridge/analogix/anx7625.h4
-rw-r--r--drivers/gpu/drm/bridge/chipone-icn6211.c119
-rw-r--r--drivers/gpu/drm/bridge/display-connector.c15
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9211.c802
-rw-r--r--drivers/gpu/drm/bridge/panel.c7
-rw-r--r--drivers/gpu/drm/bridge/synopsys/Kconfig10
-rw-r--r--drivers/gpu/drm/bridge/synopsys/Makefile1
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c199
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c186
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.h16
-rw-r--r--drivers/gpu/drm/dp/drm_dp.c33
-rw-r--r--drivers/gpu/drm/drm_buddy.c3
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c12
-rw-r--r--drivers/gpu/drm/drm_edid.c358
-rw-r--r--drivers/gpu/drm/drm_gem.c3
-rw-r--r--drivers/gpu/drm/drm_gem_atomic_helper.c2
-rw-r--r--drivers/gpu/drm/drm_modes.c4
-rw-r--r--drivers/gpu/drm/drm_vblank.c2
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gem.c6
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c10
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.c5
-rw-r--r--drivers/gpu/drm/i915/display/intel_atomic_plane.c3
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_busy.c10
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_clflush.c3
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_lmem.c2
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_object.c42
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_object.h8
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c20
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_userptr.c2
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_wait.c6
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c3
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_migrate.c5
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c4
-rw-r--r--drivers/gpu/drm/i915/i915_deps.c2
-rw-r--r--drivers/gpu/drm/i915/i915_request.c3
-rw-r--r--drivers/gpu/drm/i915/i915_sw_fence.c2
-rw-r--r--drivers/gpu/drm/i915/i915_vma.c17
-rw-r--r--drivers/gpu/drm/i915/selftests/intel_memory_region.c3
-rw-r--r--drivers/gpu/drm/ingenic/Kconfig9
-rw-r--r--drivers/gpu/drm/ingenic/Makefile1
-rw-r--r--drivers/gpu/drm/ingenic/ingenic-drm-drv.c28
-rw-r--r--drivers/gpu/drm/ingenic/ingenic-dw-hdmi.c103
-rw-r--r--drivers/gpu/drm/lima/lima_gem.c7
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c3
-rw-r--r--drivers/gpu/drm/msm/msm_gem_submit.c6
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/base917c.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndw.c3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.c17
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fence.c12
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.c3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_prime.c17
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c2
-rw-r--r--drivers/gpu/drm/panel/panel-lvds.c31
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_drv.c3
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_job.c2
-rw-r--r--drivers/gpu/drm/qxl/qxl_debugfs.c3
-rw-r--r--drivers/gpu/drm/qxl/qxl_release.c3
-rw-r--r--drivers/gpu/drm/qxl/qxl_ttm.c39
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_gem.c9
-rw-r--r--drivers/gpu/drm/radeon/radeon_mn.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.c13
-rw-r--r--drivers/gpu/drm/radeon/radeon_prime.c16
-rw-r--r--drivers/gpu/drm/radeon/radeon_sync.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_ttm.c36
-rw-r--r--drivers/gpu/drm/radeon/radeon_uvd.c12
-rw-r--r--drivers/gpu/drm/scheduler/sched_main.c3
-rw-r--r--drivers/gpu/drm/solomon/Kconfig18
-rw-r--r--drivers/gpu/drm/solomon/Makefile1
-rw-r--r--drivers/gpu/drm/solomon/ssd130x-i2c.c56
-rw-r--r--drivers/gpu/drm/solomon/ssd130x-spi.c178
-rw-r--r--drivers/gpu/drm/solomon/ssd130x.c108
-rw-r--r--drivers/gpu/drm/solomon/ssd130x.h16
-rw-r--r--drivers/gpu/drm/sti/sti_gdp.c2
-rw-r--r--drivers/gpu/drm/sti/sti_hdmi.c22
-rw-r--r--drivers/gpu/drm/stm/ltdc.c16
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c2
-rw-r--r--drivers/gpu/drm/tidss/tidss_dispc.c18
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c37
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_util.c27
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_vm.c34
-rw-r--r--drivers/gpu/drm/ttm/ttm_execbuf_util.c8
-rw-r--r--drivers/gpu/drm/ttm/ttm_resource.c34
-rw-r--r--drivers/gpu/drm/v3d/v3d_gem.c4
-rw-r--r--drivers/gpu/drm/vc4/vc4_gem.c6
-rw-r--r--drivers/gpu/drm/vc4/vc4_hvs.c26
-rw-r--r--drivers/gpu/drm/vc4/vc4_kms.c39
-rw-r--r--drivers/gpu/drm/vc4/vc4_txp.c8
-rw-r--r--drivers/gpu/drm/vgem/vgem_fence.c13
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_gem.c3
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_ioctl.c5
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_bo.c7
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c18
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_resource.c5
122 files changed, 2953 insertions, 960 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index f1422bee3dcc..5133c3f028ab 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -280,6 +280,7 @@ config DRM_AMDGPU
select HWMON
select BACKLIGHT_CLASS_DEVICE
select INTERVAL_TREE
+ select DRM_BUDDY
help
Choose this option if you have a recent AMD Radeon graphics card.
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
index 98b1736bb221..3dc5ab2764ff 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
@@ -263,7 +263,7 @@ static int amdgpu_amdkfd_remove_eviction_fence(struct amdgpu_bo *bo,
*/
replacement = dma_fence_get_stub();
dma_resv_replace_fences(bo->tbo.base.resv, ef->base.context,
- replacement);
+ replacement, DMA_RESV_USAGE_READ);
dma_fence_put(replacement);
return 0;
}
@@ -2447,6 +2447,8 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef)
struct amdgpu_bo *bo = mem->bo;
uint32_t domain = mem->domain;
struct kfd_mem_attachment *attachment;
+ struct dma_resv_iter cursor;
+ struct dma_fence *fence;
total_size += amdgpu_bo_size(bo);
@@ -2461,10 +2463,13 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence **ef)
goto validate_map_fail;
}
}
- ret = amdgpu_sync_fence(&sync_obj, bo->tbo.moving);
- if (ret) {
- pr_debug("Memory eviction: Sync BO fence failed. Try again\n");
- goto validate_map_fail;
+ dma_resv_for_each_fence(&cursor, bo->tbo.base.resv,
+ DMA_RESV_USAGE_KERNEL, fence) {
+ ret = amdgpu_sync_fence(&sync_obj, fence);
+ if (ret) {
+ pr_debug("Memory eviction: Sync BO fence failed. Try again\n");
+ goto validate_map_fail;
+ }
}
list_for_each_entry(attachment, &mem->attachments, list) {
if (!attachment->is_mapped)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h
index 044b41f0bfd9..529d52a204cf 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h
@@ -34,7 +34,6 @@ struct amdgpu_fpriv;
struct amdgpu_bo_list_entry {
struct ttm_validate_buffer tv;
struct amdgpu_bo_va *bo_va;
- struct dma_fence_chain *chain;
uint32_t priority;
struct page **user_pages;
bool user_invalidated;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index e85e347eb670..8de283997769 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -55,8 +55,8 @@ static int amdgpu_cs_user_fence_chunk(struct amdgpu_cs_parser *p,
bo = amdgpu_bo_ref(gem_to_amdgpu_bo(gobj));
p->uf_entry.priority = 0;
p->uf_entry.tv.bo = &bo->tbo;
- /* One for TTM and one for the CS job */
- p->uf_entry.tv.num_shared = 2;
+ /* One for TTM and two for the CS job */
+ p->uf_entry.tv.num_shared = 3;
drm_gem_object_put(gobj);
@@ -574,14 +574,6 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
struct amdgpu_bo *bo = ttm_to_amdgpu_bo(e->tv.bo);
e->bo_va = amdgpu_vm_bo_find(vm, bo);
-
- if (bo->tbo.base.dma_buf && !amdgpu_bo_explicit_sync(bo)) {
- e->chain = dma_fence_chain_alloc();
- if (!e->chain) {
- r = -ENOMEM;
- goto error_validate;
- }
- }
}
/* Move fence waiting after getting reservation lock of
@@ -642,13 +634,8 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
}
error_validate:
- if (r) {
- amdgpu_bo_list_for_each_entry(e, p->bo_list) {
- dma_fence_chain_free(e->chain);
- e->chain = NULL;
- }
+ if (r)
ttm_eu_backoff_reservation(&p->ticket, &p->validated);
- }
out:
return r;
}
@@ -688,17 +675,9 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser, int error,
{
unsigned i;
- if (error && backoff) {
- struct amdgpu_bo_list_entry *e;
-
- amdgpu_bo_list_for_each_entry(e, parser->bo_list) {
- dma_fence_chain_free(e->chain);
- e->chain = NULL;
- }
-
+ if (error && backoff)
ttm_eu_backoff_reservation(&parser->ticket,
&parser->validated);
- }
for (i = 0; i < parser->num_post_deps; i++) {
drm_syncobj_put(parser->post_deps[i].syncobj);
@@ -1272,29 +1251,9 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
amdgpu_vm_move_to_lru_tail(p->adev, &fpriv->vm);
- amdgpu_bo_list_for_each_entry(e, p->bo_list) {
- struct dma_resv *resv = e->tv.bo->base.resv;
- struct dma_fence_chain *chain = e->chain;
- struct dma_resv_iter cursor;
- struct dma_fence *fence;
-
- if (!chain)
- continue;
-
- /*
- * Temporary workaround dma_resv shortcommings by wrapping up
- * the submission in a dma_fence_chain and add it as exclusive
- * fence.
- *
- * TODO: Remove together with dma_resv rework.
- */
- dma_resv_for_each_fence(&cursor, resv, false, fence) {
- break;
- }
- dma_fence_chain_init(chain, fence, dma_fence_get(p->fence), 1);
- rcu_assign_pointer(resv->fence_excl, &chain->base);
- e->chain = NULL;
- }
+ /* Make sure all BOs are remembered as writers */
+ amdgpu_bo_list_for_each_entry(e, p->bo_list)
+ e->tv.num_shared = 0;
ttm_eu_fence_buffer_objects(&p->ticket, &p->validated, p->fence);
mutex_unlock(&p->adev->notifier_lock);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
index fae5c1debfad..7a6908d71820 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
@@ -200,8 +200,7 @@ int amdgpu_display_crtc_page_flip_target(struct drm_crtc *crtc,
goto unpin;
}
- /* TODO: Unify this with other drivers */
- r = dma_resv_get_fences(new_abo->tbo.base.resv, true,
+ r = dma_resv_get_fences(new_abo->tbo.base.resv, DMA_RESV_USAGE_WRITE,
&work->shared_count,
&work->shared);
if (unlikely(r != 0)) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c
index 579adfafe4d0..782cbca37538 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c
@@ -102,21 +102,9 @@ static int amdgpu_dma_buf_pin(struct dma_buf_attachment *attach)
{
struct drm_gem_object *obj = attach->dmabuf->priv;
struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj);
- int r;
/* pin buffer into GTT */
- r = amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_GTT);
- if (r)
- return r;
-
- if (bo->tbo.moving) {
- r = dma_fence_wait(bo->tbo.moving, true);
- if (r) {
- amdgpu_bo_unpin(bo);
- return r;
- }
- }
- return 0;
+ return amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_GTT);
}
/**
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
index 57b74d35052f..84a53758e18e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
@@ -526,7 +526,8 @@ int amdgpu_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
return -ENOENT;
}
robj = gem_to_amdgpu_bo(gobj);
- ret = dma_resv_wait_timeout(robj->tbo.base.resv, true, true, timeout);
+ ret = dma_resv_wait_timeout(robj->tbo.base.resv, DMA_RESV_USAGE_READ,
+ true, timeout);
/* ret == 0 means not signaled,
* ret > 0 means signaled
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
index 81207737c716..4ba4b54092f1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
@@ -111,7 +111,7 @@ void amdgpu_pasid_free_delayed(struct dma_resv *resv,
struct dma_fence *fence;
int r;
- r = dma_resv_get_singleton(resv, true, &fence);
+ r = dma_resv_get_singleton(resv, DMA_RESV_USAGE_BOOKKEEP, &fence);
if (r)
goto fallback;
@@ -139,7 +139,8 @@ fallback:
/* Not enough memory for the delayed delete, as last resort
* block for all the fences to complete.
*/
- dma_resv_wait_timeout(resv, true, false, MAX_SCHEDULE_TIMEOUT);
+ dma_resv_wait_timeout(resv, DMA_RESV_USAGE_BOOKKEEP,
+ false, MAX_SCHEDULE_TIMEOUT);
amdgpu_pasid_free(pasid);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c
index 4b153daf283d..b86c0b8252a5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c
@@ -75,8 +75,8 @@ static bool amdgpu_mn_invalidate_gfx(struct mmu_interval_notifier *mni,
mmu_interval_set_seq(mni, cur_seq);
- r = dma_resv_wait_timeout(bo->tbo.base.resv, true, false,
- MAX_SCHEDULE_TIMEOUT);
+ r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP,
+ false, MAX_SCHEDULE_TIMEOUT);
mutex_unlock(&adev->notifier_lock);
if (r <= 0)
DRM_ERROR("(%ld) failed to wait for user bo\n", r);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
index dd6693fff280..e92ecabfa7bd 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
@@ -612,9 +612,8 @@ int amdgpu_bo_create(struct amdgpu_device *adev,
if (unlikely(r))
goto fail_unreserve;
- amdgpu_bo_fence(bo, fence, false);
- dma_fence_put(bo->tbo.moving);
- bo->tbo.moving = dma_fence_get(fence);
+ dma_resv_add_fence(bo->tbo.base.resv, fence,
+ DMA_RESV_USAGE_KERNEL);
dma_fence_put(fence);
}
if (!bp->resv)
@@ -761,6 +760,11 @@ int amdgpu_bo_kmap(struct amdgpu_bo *bo, void **ptr)
if (bo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS)
return -EPERM;
+ r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_KERNEL,
+ false, MAX_SCHEDULE_TIMEOUT);
+ if (r < 0)
+ return r;
+
kptr = amdgpu_bo_kptr(bo);
if (kptr) {
if (ptr)
@@ -768,11 +772,6 @@ int amdgpu_bo_kmap(struct amdgpu_bo *bo, void **ptr)
return 0;
}
- r = dma_resv_wait_timeout(bo->tbo.base.resv, false, false,
- MAX_SCHEDULE_TIMEOUT);
- if (r < 0)
- return r;
-
r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.resource->num_pages, &bo->kmap);
if (r)
return r;
@@ -1399,10 +1398,8 @@ void amdgpu_bo_fence(struct amdgpu_bo *bo, struct dma_fence *fence,
return;
}
- if (shared)
- dma_resv_add_shared_fence(resv, fence);
- else
- dma_resv_add_excl_fence(resv, fence);
+ dma_resv_add_fence(resv, fence, shared ? DMA_RESV_USAGE_READ :
+ DMA_RESV_USAGE_WRITE);
}
/**
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_res_cursor.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_res_cursor.h
index acfa207cf970..6546552e596c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_res_cursor.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_res_cursor.h
@@ -30,12 +30,15 @@
#include <drm/ttm/ttm_resource.h>
#include <drm/ttm/ttm_range_manager.h>
+#include "amdgpu_vram_mgr.h"
+
/* state back for walking over vram_mgr and gtt_mgr allocations */
struct amdgpu_res_cursor {
uint64_t start;
uint64_t size;
uint64_t remaining;
- struct drm_mm_node *node;
+ void *node;
+ uint32_t mem_type;
};
/**
@@ -52,27 +55,63 @@ static inline void amdgpu_res_first(struct ttm_resource *res,
uint64_t start, uint64_t size,
struct amdgpu_res_cursor *cur)
{
+ struct drm_buddy_block *block;
+ struct list_head *head, *next;
struct drm_mm_node *node;
- if (!res || res->mem_type == TTM_PL_SYSTEM) {
- cur->start = start;
- cur->size = size;
- cur->remaining = size;
- cur->node = NULL;
- WARN_ON(res && start + size > res->num_pages << PAGE_SHIFT);
- return;
- }
+ if (!res)
+ goto fallback;
BUG_ON(start + size > res->num_pages << PAGE_SHIFT);
- node = to_ttm_range_mgr_node(res)->mm_nodes;
- while (start >= node->size << PAGE_SHIFT)
- start -= node++->size << PAGE_SHIFT;
+ cur->mem_type = res->mem_type;
+
+ switch (cur->mem_type) {
+ case TTM_PL_VRAM:
+ head = &to_amdgpu_vram_mgr_resource(res)->blocks;
+
+ block = list_first_entry_or_null(head,
+ struct drm_buddy_block,
+ link);
+ if (!block)
+ goto fallback;
+
+ while (start >= amdgpu_vram_mgr_block_size(block)) {
+ start -= amdgpu_vram_mgr_block_size(block);
+
+ next = block->link.next;
+ if (next != head)
+ block = list_entry(next, struct drm_buddy_block, link);
+ }
+
+ cur->start = amdgpu_vram_mgr_block_start(block) + start;
+ cur->size = min(amdgpu_vram_mgr_block_size(block) - start, size);
+ cur->remaining = size;
+ cur->node = block;
+ break;
+ case TTM_PL_TT:
+ node = to_ttm_range_mgr_node(res)->mm_nodes;
+ while (start >= node->size << PAGE_SHIFT)
+ start -= node++->size << PAGE_SHIFT;
+
+ cur->start = (node->start << PAGE_SHIFT) + start;
+ cur->size = min((node->size << PAGE_SHIFT) - start, size);
+ cur->remaining = size;
+ cur->node = node;
+ break;
+ default:
+ goto fallback;
+ }
- cur->start = (node->start << PAGE_SHIFT) + start;
- cur->size = min((node->size << PAGE_SHIFT) - start, size);
+ return;
+
+fallback:
+ cur->start = start;
+ cur->size = size;
cur->remaining = size;
- cur->node = node;
+ cur->node = NULL;
+ WARN_ON(res && start + size > res->num_pages << PAGE_SHIFT);
+ return;
}
/**
@@ -85,7 +124,9 @@ static inline void amdgpu_res_first(struct ttm_resource *res,
*/
static inline void amdgpu_res_next(struct amdgpu_res_cursor *cur, uint64_t size)
{
- struct drm_mm_node *node = cur->node;
+ struct drm_buddy_block *block;
+ struct drm_mm_node *node;
+ struct list_head *next;
BUG_ON(size > cur->remaining);
@@ -99,9 +140,27 @@ static inline void amdgpu_res_next(struct amdgpu_res_cursor *cur, uint64_t size)
return;
}
- cur->node = ++node;
- cur->start = node->start << PAGE_SHIFT;
- cur->size = min(node->size << PAGE_SHIFT, cur->remaining);
+ switch (cur->mem_type) {
+ case TTM_PL_VRAM:
+ block = cur->node;
+
+ next = block->link.next;
+ block = list_entry(next, struct drm_buddy_block, link);
+
+ cur->node = block;
+ cur->start = amdgpu_vram_mgr_block_start(block);
+ cur->size = min(amdgpu_vram_mgr_block_size(block), cur->remaining);
+ break;
+ case TTM_PL_TT:
+ node = cur->node;
+
+ cur->node = ++node;
+ cur->start = node->start << PAGE_SHIFT;
+ cur->size = min(node->size << PAGE_SHIFT, cur->remaining);
+ break;
+ default:
+ return;
+ }
}
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c
index 40e06745fae9..11c46b3e4c60 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c
@@ -259,7 +259,8 @@ int amdgpu_sync_resv(struct amdgpu_device *adev, struct amdgpu_sync *sync,
if (resv == NULL)
return -EINVAL;
- dma_resv_for_each_fence(&cursor, resv, true, f) {
+ /* TODO: Use DMA_RESV_USAGE_READ here */
+ dma_resv_for_each_fence(&cursor, resv, DMA_RESV_USAGE_BOOKKEEP, f) {
dma_fence_chain_for_each(f, f) {
struct dma_fence *tmp = dma_fence_chain_contained(f);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index f7f149588432..ec26edd4f4d8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -1344,7 +1344,8 @@ static bool amdgpu_ttm_bo_eviction_valuable(struct ttm_buffer_object *bo,
* If true, then return false as any KFD process needs all its BOs to
* be resident to run successfully
*/
- dma_resv_for_each_fence(&resv_cursor, bo->base.resv, true, f) {
+ dma_resv_for_each_fence(&resv_cursor, bo->base.resv,
+ DMA_RESV_USAGE_BOOKKEEP, f) {
if (amdkfd_fence_check_mm(f, current->mm))
return false;
}
@@ -2160,17 +2161,6 @@ int amdgpu_ttm_evict_resources(struct amdgpu_device *adev, int mem_type)
#if defined(CONFIG_DEBUG_FS)
-static int amdgpu_mm_vram_table_show(struct seq_file *m, void *unused)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
- struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev,
- TTM_PL_VRAM);
- struct drm_printer p = drm_seq_file_printer(m);
-
- ttm_resource_manager_debug(man, &p);
- return 0;
-}
-
static int amdgpu_ttm_page_pool_show(struct seq_file *m, void *unused)
{
struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
@@ -2178,55 +2168,6 @@ static int amdgpu_ttm_page_pool_show(struct seq_file *m, void *unused)
return ttm_pool_debugfs(&adev->mman.bdev.pool, m);
}
-static int amdgpu_mm_tt_table_show(struct seq_file *m, void *unused)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
- struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev,
- TTM_PL_TT);
- struct drm_printer p = drm_seq_file_printer(m);
-
- ttm_resource_manager_debug(man, &p);
- return 0;
-}
-
-static int amdgpu_mm_gds_table_show(struct seq_file *m, void *unused)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
- struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev,
- AMDGPU_PL_GDS);
- struct drm_printer p = drm_seq_file_printer(m);
-
- ttm_resource_manager_debug(man, &p);
- return 0;
-}
-
-static int amdgpu_mm_gws_table_show(struct seq_file *m, void *unused)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
- struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev,
- AMDGPU_PL_GWS);
- struct drm_printer p = drm_seq_file_printer(m);
-
- ttm_resource_manager_debug(man, &p);
- return 0;
-}
-
-static int amdgpu_mm_oa_table_show(struct seq_file *m, void *unused)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
- struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev,
- AMDGPU_PL_OA);
- struct drm_printer p = drm_seq_file_printer(m);
-
- ttm_resource_manager_debug(man, &p);
- return 0;
-}
-
-DEFINE_SHOW_ATTRIBUTE(amdgpu_mm_vram_table);
-DEFINE_SHOW_ATTRIBUTE(amdgpu_mm_tt_table);
-DEFINE_SHOW_ATTRIBUTE(amdgpu_mm_gds_table);
-DEFINE_SHOW_ATTRIBUTE(amdgpu_mm_gws_table);
-DEFINE_SHOW_ATTRIBUTE(amdgpu_mm_oa_table);
DEFINE_SHOW_ATTRIBUTE(amdgpu_ttm_page_pool);
/*
@@ -2436,17 +2377,23 @@ void amdgpu_ttm_debugfs_init(struct amdgpu_device *adev)
&amdgpu_ttm_vram_fops, adev->gmc.mc_vram_size);
debugfs_create_file("amdgpu_iomem", 0444, root, adev,
&amdgpu_ttm_iomem_fops);
- debugfs_create_file("amdgpu_vram_mm", 0444, root, adev,
- &amdgpu_mm_vram_table_fops);
- debugfs_create_file("amdgpu_gtt_mm", 0444, root, adev,
- &amdgpu_mm_tt_table_fops);
- debugfs_create_file("amdgpu_gds_mm", 0444, root, adev,
- &amdgpu_mm_gds_table_fops);
- debugfs_create_file("amdgpu_gws_mm", 0444, root, adev,
- &amdgpu_mm_gws_table_fops);
- debugfs_create_file("amdgpu_oa_mm", 0444, root, adev,
- &amdgpu_mm_oa_table_fops);
debugfs_create_file("ttm_page_pool", 0444, root, adev,
&amdgpu_ttm_page_pool_fops);
+ ttm_resource_manager_create_debugfs(ttm_manager_type(&adev->mman.bdev,
+ TTM_PL_VRAM),
+ root, "amdgpu_vram_mm");
+ ttm_resource_manager_create_debugfs(ttm_manager_type(&adev->mman.bdev,
+ TTM_PL_TT),
+ root, "amdgpu_gtt_mm");
+ ttm_resource_manager_create_debugfs(ttm_manager_type(&adev->mman.bdev,
+ AMDGPU_PL_GDS),
+ root, "amdgpu_gds_mm");
+ ttm_resource_manager_create_debugfs(ttm_manager_type(&adev->mman.bdev,
+ AMDGPU_PL_GWS),
+ root, "amdgpu_gws_mm");
+ ttm_resource_manager_create_debugfs(ttm_manager_type(&adev->mman.bdev,
+ AMDGPU_PL_OA),
+ root, "amdgpu_oa_mm");
+
#endif
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
index 9120ae80ef52..6a70818039dd 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
@@ -26,6 +26,7 @@
#include <linux/dma-direction.h>
#include <drm/gpu_scheduler.h>
+#include "amdgpu_vram_mgr.h"
#include "amdgpu.h"
#define AMDGPU_PL_GDS (TTM_PL_PRIV + 0)
@@ -38,15 +39,6 @@
#define AMDGPU_POISON 0xd0bed0be
-struct amdgpu_vram_mgr {
- struct ttm_resource_manager manager;
- struct drm_mm mm;
- spinlock_t lock;
- struct list_head reservations_pending;
- struct list_head reserved_pages;
- atomic64_t vis_usage;
-};
-
struct amdgpu_gtt_mgr {
struct ttm_resource_manager manager;
struct drm_mm mm;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
index 39c74d9fa7cc..6eac649499d3 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
@@ -1163,7 +1163,8 @@ static int amdgpu_uvd_send_msg(struct amdgpu_ring *ring, struct amdgpu_bo *bo,
ib->length_dw = 16;
if (direct) {
- r = dma_resv_wait_timeout(bo->tbo.base.resv, true, false,
+ r = dma_resv_wait_timeout(bo->tbo.base.resv,
+ DMA_RESV_USAGE_KERNEL, false,
msecs_to_jiffies(10));
if (r == 0)
r = -ETIMEDOUT;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index b13451255e8b..5277c10d901d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -2059,7 +2059,7 @@ static void amdgpu_vm_prt_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)
struct dma_resv_iter cursor;
struct dma_fence *fence;
- dma_resv_for_each_fence(&cursor, resv, true, fence) {
+ dma_resv_for_each_fence(&cursor, resv, DMA_RESV_USAGE_BOOKKEEP, fence) {
/* Add a callback for each fence in the reservation object */
amdgpu_vm_prt_get(adev);
amdgpu_vm_add_prt_cb(adev, fence);
@@ -2665,7 +2665,7 @@ bool amdgpu_vm_evictable(struct amdgpu_bo *bo)
return true;
/* Don't evict VM page tables while they are busy */
- if (!dma_resv_test_signaled(bo->tbo.base.resv, true))
+ if (!dma_resv_test_signaled(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP))
return false;
/* Try to block ongoing updates */
@@ -2845,7 +2845,8 @@ void amdgpu_vm_adjust_size(struct amdgpu_device *adev, uint32_t min_vm_size,
*/
long amdgpu_vm_wait_idle(struct amdgpu_vm *vm, long timeout)
{
- timeout = dma_resv_wait_timeout(vm->root.bo->tbo.base.resv, true,
+ timeout = dma_resv_wait_timeout(vm->root.bo->tbo.base.resv,
+ DMA_RESV_USAGE_BOOKKEEP,
true, timeout);
if (timeout <= 0)
return timeout;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c
index e3fbf0f10add..31913ae86de6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_cpu.c
@@ -74,13 +74,12 @@ static int amdgpu_vm_cpu_update(struct amdgpu_vm_update_params *p,
{
unsigned int i;
uint64_t value;
- int r;
+ long r;
- if (vmbo->bo.tbo.moving) {
- r = dma_fence_wait(vmbo->bo.tbo.moving, true);
- if (r)
- return r;
- }
+ r = dma_resv_wait_timeout(vmbo->bo.tbo.base.resv, DMA_RESV_USAGE_KERNEL,
+ true, MAX_SCHEDULE_TIMEOUT);
+ if (r < 0)
+ return r;
pe += (unsigned long)amdgpu_bo_kptr(&vmbo->bo);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c
index dbb551762805..bdb44cee19d3 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c
@@ -204,14 +204,19 @@ static int amdgpu_vm_sdma_update(struct amdgpu_vm_update_params *p,
struct amdgpu_bo *bo = &vmbo->bo;
enum amdgpu_ib_pool_type pool = p->immediate ? AMDGPU_IB_POOL_IMMEDIATE
: AMDGPU_IB_POOL_DELAYED;
+ struct dma_resv_iter cursor;
unsigned int i, ndw, nptes;
+ struct dma_fence *fence;
uint64_t *pte;
int r;
/* Wait for PD/PT moves to be completed */
- r = amdgpu_sync_fence(&p->job->sync, bo->tbo.moving);
- if (r)
- return r;
+ dma_resv_for_each_fence(&cursor, bo->tbo.base.resv,
+ DMA_RESV_USAGE_KERNEL, fence) {
+ r = amdgpu_sync_fence(&p->job->sync, fence);
+ if (r)
+ return r;
+ }
do {
ndw = p->num_dw_left;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
index 0a7611648573..49e4092f447f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
@@ -32,8 +32,10 @@
#include "atom.h"
struct amdgpu_vram_reservation {
- struct list_head node;
- struct drm_mm_node mm_node;
+ u64 start;
+ u64 size;
+ struct list_head allocated;
+ struct list_head blocks;
};
static inline struct amdgpu_vram_mgr *
@@ -186,18 +188,18 @@ const struct attribute_group amdgpu_vram_mgr_attr_group = {
};
/**
- * amdgpu_vram_mgr_vis_size - Calculate visible node size
+ * amdgpu_vram_mgr_vis_size - Calculate visible block size
*
* @adev: amdgpu_device pointer
- * @node: MM node structure
+ * @block: DRM BUDDY block structure
*
- * Calculate how many bytes of the MM node are inside visible VRAM
+ * Calculate how many bytes of the DRM BUDDY block are inside visible VRAM
*/
static u64 amdgpu_vram_mgr_vis_size(struct amdgpu_device *adev,
- struct drm_mm_node *node)
+ struct drm_buddy_block *block)
{
- uint64_t start = node->start << PAGE_SHIFT;
- uint64_t end = (node->size + node->start) << PAGE_SHIFT;
+ u64 start = amdgpu_vram_mgr_block_start(block);
+ u64 end = start + amdgpu_vram_mgr_block_size(block);
if (start >= adev->gmc.visible_vram_size)
return 0;
@@ -218,9 +220,9 @@ u64 amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo *bo)
{
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
struct ttm_resource *res = bo->tbo.resource;
- unsigned pages = res->num_pages;
- struct drm_mm_node *mm;
- u64 usage;
+ struct amdgpu_vram_mgr_resource *vres = to_amdgpu_vram_mgr_resource(res);
+ struct drm_buddy_block *block;
+ u64 usage = 0;
if (amdgpu_gmc_vram_full_visible(&adev->gmc))
return amdgpu_bo_size(bo);
@@ -228,9 +230,8 @@ u64 amdgpu_vram_mgr_bo_visible_size(struct amdgpu_bo *bo)
if (res->start >= adev->gmc.visible_vram_size >> PAGE_SHIFT)
return 0;
- mm = &container_of(res, struct ttm_range_mgr_node, base)->mm_nodes[0];
- for (usage = 0; pages; pages -= mm->size, mm++)
- usage += amdgpu_vram_mgr_vis_size(adev, mm);
+ list_for_each_entry(block, &vres->blocks, link)
+ usage += amdgpu_vram_mgr_vis_size(adev, block);
return usage;
}
@@ -240,23 +241,30 @@ static void amdgpu_vram_mgr_do_reserve(struct ttm_resource_manager *man)
{
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
struct amdgpu_device *adev = to_amdgpu_device(mgr);
- struct drm_mm *mm = &mgr->mm;
+ struct drm_buddy *mm = &mgr->mm;
struct amdgpu_vram_reservation *rsv, *temp;
+ struct drm_buddy_block *block;
uint64_t vis_usage;
- list_for_each_entry_safe(rsv, temp, &mgr->reservations_pending, node) {
- if (drm_mm_reserve_node(mm, &rsv->mm_node))
+ list_for_each_entry_safe(rsv, temp, &mgr->reservations_pending, blocks) {
+ if (drm_buddy_alloc_blocks(mm, rsv->start, rsv->start + rsv->size,
+ rsv->size, mm->chunk_size, &rsv->allocated,
+ DRM_BUDDY_RANGE_ALLOCATION))
+ continue;
+
+ block = amdgpu_vram_mgr_first_block(&rsv->allocated);
+ if (!block)
continue;
dev_dbg(adev->dev, "Reservation 0x%llx - %lld, Succeeded\n",
- rsv->mm_node.start, rsv->mm_node.size);
+ rsv->start, rsv->size);
- vis_usage = amdgpu_vram_mgr_vis_size(adev, &rsv->mm_node);
+ vis_usage = amdgpu_vram_mgr_vis_size(adev, block);
atomic64_add(vis_usage, &mgr->vis_usage);
spin_lock(&man->bdev->lru_lock);
- man->usage += rsv->mm_node.size << PAGE_SHIFT;
+ man->usage += rsv->size;
spin_unlock(&man->bdev->lru_lock);
- list_move(&rsv->node, &mgr->reserved_pages);
+ list_move(&rsv->blocks, &mgr->reserved_pages);
}
}
@@ -278,14 +286,16 @@ int amdgpu_vram_mgr_reserve_range(struct amdgpu_vram_mgr *mgr,
if (!rsv)
return -ENOMEM;
- INIT_LIST_HEAD(&rsv->node);
- rsv->mm_node.start = start >> PAGE_SHIFT;
- rsv->mm_node.size = size >> PAGE_SHIFT;
+ INIT_LIST_HEAD(&rsv->allocated);
+ INIT_LIST_HEAD(&rsv->blocks);
- spin_lock(&mgr->lock);
- list_add_tail(&rsv->node, &mgr->reservations_pending);
+ rsv->start = start;
+ rsv->size = size;
+
+ mutex_lock(&mgr->lock);
+ list_add_tail(&rsv->blocks, &mgr->reservations_pending);
amdgpu_vram_mgr_do_reserve(&mgr->manager);
- spin_unlock(&mgr->lock);
+ mutex_unlock(&mgr->lock);
return 0;
}
@@ -307,19 +317,19 @@ int amdgpu_vram_mgr_query_page_status(struct amdgpu_vram_mgr *mgr,
struct amdgpu_vram_reservation *rsv;
int ret;
- spin_lock(&mgr->lock);
+ mutex_lock(&mgr->lock);
- list_for_each_entry(rsv, &mgr->reservations_pending, node) {
- if ((rsv->mm_node.start <= start) &&
- (start < (rsv->mm_node.start + rsv->mm_node.size))) {
+ list_for_each_entry(rsv, &mgr->reservations_pending, blocks) {
+ if (rsv->start <= start &&
+ (start < (rsv->start + rsv->size))) {
ret = -EBUSY;
goto out;
}
}
- list_for_each_entry(rsv, &mgr->reserved_pages, node) {
- if ((rsv->mm_node.start <= start) &&
- (start < (rsv->mm_node.start + rsv->mm_node.size))) {
+ list_for_each_entry(rsv, &mgr->reserved_pages, blocks) {
+ if (rsv->start <= start &&
+ (start < (rsv->start + rsv->size))) {
ret = 0;
goto out;
}
@@ -327,33 +337,11 @@ int amdgpu_vram_mgr_query_page_status(struct amdgpu_vram_mgr *mgr,
ret = -ENOENT;
out:
- spin_unlock(&mgr->lock);
+ mutex_unlock(&mgr->lock);
return ret;
}
/**
- * amdgpu_vram_mgr_virt_start - update virtual start address
- *
- * @mem: ttm_resource to update
- * @node: just allocated node
- *
- * Calculate a virtual BO start address to easily check if everything is CPU
- * accessible.
- */
-static void amdgpu_vram_mgr_virt_start(struct ttm_resource *mem,
- struct drm_mm_node *node)
-{
- unsigned long start;
-
- start = node->start + node->size;
- if (start > mem->num_pages)
- start -= mem->num_pages;
- else
- start = 0;
- mem->start = max(mem->start, start);
-}
-
-/**
* amdgpu_vram_mgr_new - allocate new ranges
*
* @man: TTM memory type manager
@@ -368,46 +356,44 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man,
const struct ttm_place *place,
struct ttm_resource **res)
{
- unsigned long lpfn, num_nodes, pages_per_node, pages_left, pages;
+ u64 vis_usage = 0, max_bytes, cur_size, min_block_size;
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
struct amdgpu_device *adev = to_amdgpu_device(mgr);
- uint64_t vis_usage = 0, mem_bytes, max_bytes;
- struct ttm_range_mgr_node *node;
- struct drm_mm *mm = &mgr->mm;
- enum drm_mm_insert_mode mode;
- unsigned i;
+ struct amdgpu_vram_mgr_resource *vres;
+ u64 size, remaining_size, lpfn, fpfn;
+ struct drm_buddy *mm = &mgr->mm;
+ struct drm_buddy_block *block;
+ unsigned long pages_per_block;
int r;
- lpfn = place->lpfn;
+ lpfn = place->lpfn << PAGE_SHIFT;
if (!lpfn)
- lpfn = man->size >> PAGE_SHIFT;
+ lpfn = man->size;
+
+ fpfn = place->fpfn << PAGE_SHIFT;
max_bytes = adev->gmc.mc_vram_size;
if (tbo->type != ttm_bo_type_kernel)
max_bytes -= AMDGPU_VM_RESERVED_VRAM;
- mem_bytes = tbo->base.size;
if (place->flags & TTM_PL_FLAG_CONTIGUOUS) {
- pages_per_node = ~0ul;
- num_nodes = 1;
+ pages_per_block = ~0ul;
} else {
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
- pages_per_node = HPAGE_PMD_NR;
+ pages_per_block = HPAGE_PMD_NR;
#else
/* default to 2MB */
- pages_per_node = 2UL << (20UL - PAGE_SHIFT);
+ pages_per_block = 2UL << (20UL - PAGE_SHIFT);
#endif
- pages_per_node = max_t(uint32_t, pages_per_node,
- tbo->page_alignment);
- num_nodes = DIV_ROUND_UP_ULL(PFN_UP(mem_bytes), pages_per_node);
+ pages_per_block = max_t(uint32_t, pages_per_block,
+ tbo->page_alignment);
}
- node = kvmalloc(struct_size(node, mm_nodes, num_nodes),
- GFP_KERNEL | __GFP_ZERO);
- if (!node)
+ vres = kzalloc(sizeof(*vres), GFP_KERNEL);
+ if (!vres)
return -ENOMEM;
- ttm_resource_init(tbo, place, &node->base);
+ ttm_resource_init(tbo, place, &vres->base);
/* bail out quickly if there's likely not enough VRAM for this BO */
if (ttm_resource_manager_usage(man) > max_bytes) {
@@ -415,66 +401,130 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man,
goto error_fini;
}
- mode = DRM_MM_INSERT_BEST;
+ INIT_LIST_HEAD(&vres->blocks);
+
if (place->flags & TTM_PL_FLAG_TOPDOWN)
- mode = DRM_MM_INSERT_HIGH;
-
- pages_left = node->base.num_pages;
-
- /* Limit maximum size to 2GB due to SG table limitations */
- pages = min(pages_left, 2UL << (30 - PAGE_SHIFT));
-
- i = 0;
- spin_lock(&mgr->lock);
- while (pages_left) {
- uint32_t alignment = tbo->page_alignment;
-
- if (pages >= pages_per_node)
- alignment = pages_per_node;
-
- r = drm_mm_insert_node_in_range(mm, &node->mm_nodes[i], pages,
- alignment, 0, place->fpfn,
- lpfn, mode);
- if (unlikely(r)) {
- if (pages > pages_per_node) {
- if (is_power_of_2(pages))
- pages = pages / 2;
- else
- pages = rounddown_pow_of_two(pages);
- continue;
+ vres->flags |= DRM_BUDDY_TOPDOWN_ALLOCATION;
+
+ if (fpfn || lpfn != man->size)
+ /* Allocate blocks in desired range */
+ vres->flags |= DRM_BUDDY_RANGE_ALLOCATION;
+
+ remaining_size = vres->base.num_pages << PAGE_SHIFT;
+
+ mutex_lock(&mgr->lock);
+ while (remaining_size) {
+ if (tbo->page_alignment)
+ min_block_size = tbo->page_alignment << PAGE_SHIFT;
+ else
+ min_block_size = mgr->default_page_size;
+
+ BUG_ON(min_block_size < mm->chunk_size);
+
+ /* Limit maximum size to 2GiB due to SG table limitations */
+ size = min(remaining_size, 2ULL << 30);
+
+ if (size >= pages_per_block << PAGE_SHIFT)
+ min_block_size = pages_per_block << PAGE_SHIFT;
+
+ cur_size = size;
+
+ if (fpfn + size != place->lpfn << PAGE_SHIFT) {
+ /*
+ * Except for actual range allocation, modify the size and
+ * min_block_size conforming to continuous flag enablement
+ */
+ if (place->flags & TTM_PL_FLAG_CONTIGUOUS) {
+ size = roundup_pow_of_two(size);
+ min_block_size = size;
+ /*
+ * Modify the size value if size is not
+ * aligned with min_block_size
+ */
+ } else if (!IS_ALIGNED(size, min_block_size)) {
+ size = round_up(size, min_block_size);
}
- goto error_free;
}
- vis_usage += amdgpu_vram_mgr_vis_size(adev, &node->mm_nodes[i]);
- amdgpu_vram_mgr_virt_start(&node->base, &node->mm_nodes[i]);
- pages_left -= pages;
- ++i;
+ r = drm_buddy_alloc_blocks(mm, fpfn,
+ lpfn,
+ size,
+ min_block_size,
+ &vres->blocks,
+ vres->flags);
+ if (unlikely(r))
+ goto error_free_blocks;
+
+ if (size > remaining_size)
+ remaining_size = 0;
+ else
+ remaining_size -= size;
+ }
+ mutex_unlock(&mgr->lock);
+
+ if (cur_size != size) {
+ struct drm_buddy_block *block;
+ struct list_head *trim_list;
+ u64 original_size;
+ LIST_HEAD(temp);
+
+ trim_list = &vres->blocks;
+ original_size = vres->base.num_pages << PAGE_SHIFT;
+
+ /*
+ * If size value is rounded up to min_block_size, trim the last
+ * block to the required size
+ */
+ if (!list_is_singular(&vres->blocks)) {
+ block = list_last_entry(&vres->blocks, typeof(*block), link);
+ list_move_tail(&block->link, &temp);
+ trim_list = &temp;
+ /*
+ * Compute the original_size value by subtracting the
+ * last block size with (aligned size - original size)
+ */
+ original_size = amdgpu_vram_mgr_block_size(block) - (size - cur_size);
+ }
- if (pages > pages_left)
- pages = pages_left;
+ mutex_lock(&mgr->lock);
+ drm_buddy_block_trim(mm,
+ original_size,
+ trim_list);
+ mutex_unlock(&mgr->lock);
+
+ if (!list_empty(&temp))
+ list_splice_tail(trim_list, &vres->blocks);
+ }
+
+ list_for_each_entry(block, &vres->blocks, link)
+ vis_usage += amdgpu_vram_mgr_vis_size(adev, block);
+
+ block = amdgpu_vram_mgr_first_block(&vres->blocks);
+ if (!block) {
+ r = -EINVAL;
+ goto error_fini;
}
- spin_unlock(&mgr->lock);
- if (i == 1)
- node->base.placement |= TTM_PL_FLAG_CONTIGUOUS;
+ vres->base.start = amdgpu_vram_mgr_block_start(block) >> PAGE_SHIFT;
+
+ if (amdgpu_is_vram_mgr_blocks_contiguous(&vres->blocks))
+ vres->base.placement |= TTM_PL_FLAG_CONTIGUOUS;
if (adev->gmc.xgmi.connected_to_cpu)
- node->base.bus.caching = ttm_cached;
+ vres->base.bus.caching = ttm_cached;
else
- node->base.bus.caching = ttm_write_combined;
+ vres->base.bus.caching = ttm_write_combined;
atomic64_add(vis_usage, &mgr->vis_usage);
- *res = &node->base;
+ *res = &vres->base;
return 0;
-error_free:
- while (i--)
- drm_mm_remove_node(&node->mm_nodes[i]);
- spin_unlock(&mgr->lock);
+error_free_blocks:
+ drm_buddy_free_list(mm, &vres->blocks);
+ mutex_unlock(&mgr->lock);
error_fini:
- ttm_resource_fini(man, &node->base);
- kvfree(node);
+ ttm_resource_fini(man, &vres->base);
+ kfree(vres);
return r;
}
@@ -490,27 +540,26 @@ error_fini:
static void amdgpu_vram_mgr_del(struct ttm_resource_manager *man,
struct ttm_resource *res)
{
- struct ttm_range_mgr_node *node = to_ttm_range_mgr_node(res);
+ struct amdgpu_vram_mgr_resource *vres = to_amdgpu_vram_mgr_resource(res);
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
struct amdgpu_device *adev = to_amdgpu_device(mgr);
+ struct drm_buddy *mm = &mgr->mm;
+ struct drm_buddy_block *block;
uint64_t vis_usage = 0;
- unsigned i, pages;
- spin_lock(&mgr->lock);
- for (i = 0, pages = res->num_pages; pages;
- pages -= node->mm_nodes[i].size, ++i) {
- struct drm_mm_node *mm = &node->mm_nodes[i];
+ mutex_lock(&mgr->lock);
+ list_for_each_entry(block, &vres->blocks, link)
+ vis_usage += amdgpu_vram_mgr_vis_size(adev, block);
- drm_mm_remove_node(mm);
- vis_usage += amdgpu_vram_mgr_vis_size(adev, mm);
- }
amdgpu_vram_mgr_do_reserve(man);
- spin_unlock(&mgr->lock);
+
+ drm_buddy_free_list(mm, &vres->blocks);
+ mutex_unlock(&mgr->lock);
atomic64_sub(vis_usage, &mgr->vis_usage);
ttm_resource_fini(man, res);
- kvfree(node);
+ kfree(vres);
}
/**
@@ -542,7 +591,7 @@ int amdgpu_vram_mgr_alloc_sgt(struct amdgpu_device *adev,
if (!*sgt)
return -ENOMEM;
- /* Determine the number of DRM_MM nodes to export */
+ /* Determine the number of DRM_BUDDY blocks to export */
amdgpu_res_first(res, offset, length, &cursor);
while (cursor.remaining) {
num_entries++;
@@ -558,10 +607,10 @@ int amdgpu_vram_mgr_alloc_sgt(struct amdgpu_device *adev,
sg->length = 0;
/*
- * Walk down DRM_MM nodes to populate scatterlist nodes
- * @note: Use iterator api to get first the DRM_MM node
+ * Walk down DRM_BUDDY blocks to populate scatterlist nodes
+ * @note: Use iterator api to get first the DRM_BUDDY block
* and the number of bytes from it. Access the following
- * DRM_MM node(s) if more buffer needs to exported
+ * DRM_BUDDY block(s) if more buffer needs to exported
*/
amdgpu_res_first(res, offset, length, &cursor);
for_each_sgtable_sg((*sgt), sg, i) {
@@ -648,13 +697,22 @@ static void amdgpu_vram_mgr_debug(struct ttm_resource_manager *man,
struct drm_printer *printer)
{
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
+ struct drm_buddy *mm = &mgr->mm;
+ struct drm_buddy_block *block;
drm_printf(printer, " vis usage:%llu\n",
amdgpu_vram_mgr_vis_usage(mgr));
- spin_lock(&mgr->lock);
- drm_mm_print(&mgr->mm, printer);
- spin_unlock(&mgr->lock);
+ mutex_lock(&mgr->lock);
+ drm_printf(printer, "default_page_size: %lluKiB\n",
+ mgr->default_page_size >> 10);
+
+ drm_buddy_print(mm, printer);
+
+ drm_printf(printer, "reserved:\n");
+ list_for_each_entry(block, &mgr->reserved_pages, link)
+ drm_buddy_block_print(mm, block, printer);
+ mutex_unlock(&mgr->lock);
}
static const struct ttm_resource_manager_func amdgpu_vram_mgr_func = {
@@ -674,16 +732,21 @@ int amdgpu_vram_mgr_init(struct amdgpu_device *adev)
{
struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr;
struct ttm_resource_manager *man = &mgr->manager;
+ int err;
ttm_resource_manager_init(man, &adev->mman.bdev,
adev->gmc.real_vram_size);
man->func = &amdgpu_vram_mgr_func;
- drm_mm_init(&mgr->mm, 0, man->size >> PAGE_SHIFT);
- spin_lock_init(&mgr->lock);
+ err = drm_buddy_init(&mgr->mm, man->size, PAGE_SIZE);
+ if (err)
+ return err;
+
+ mutex_init(&mgr->lock);
INIT_LIST_HEAD(&mgr->reservations_pending);
INIT_LIST_HEAD(&mgr->reserved_pages);
+ mgr->default_page_size = PAGE_SIZE;
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, &mgr->manager);
ttm_resource_manager_set_used(man, true);
@@ -711,16 +774,16 @@ void amdgpu_vram_mgr_fini(struct amdgpu_device *adev)
if (ret)
return;
- spin_lock(&mgr->lock);
- list_for_each_entry_safe(rsv, temp, &mgr->reservations_pending, node)
+ mutex_lock(&mgr->lock);
+ list_for_each_entry_safe(rsv, temp, &mgr->reservations_pending, blocks)
kfree(rsv);
- list_for_each_entry_safe(rsv, temp, &mgr->reserved_pages, node) {
- drm_mm_remove_node(&rsv->mm_node);
+ list_for_each_entry_safe(rsv, temp, &mgr->reserved_pages, blocks) {
+ drm_buddy_free_list(&mgr->mm, &rsv->blocks);
kfree(rsv);
}
- drm_mm_takedown(&mgr->mm);
- spin_unlock(&mgr->lock);
+ drm_buddy_fini(&mgr->mm);
+ mutex_unlock(&mgr->lock);
ttm_resource_manager_cleanup(man);
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, NULL);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h
new file mode 100644
index 000000000000..9a2db87186c7
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: MIT
+ * Copyright 2021 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.
+ *
+ */
+
+#ifndef __AMDGPU_VRAM_MGR_H__
+#define __AMDGPU_VRAM_MGR_H__
+
+#include <drm/drm_buddy.h>
+
+struct amdgpu_vram_mgr {
+ struct ttm_resource_manager manager;
+ struct drm_buddy mm;
+ /* protects access to buffer objects */
+ struct mutex lock;
+ struct list_head reservations_pending;
+ struct list_head reserved_pages;
+ atomic64_t vis_usage;
+ u64 default_page_size;
+};
+
+struct amdgpu_vram_mgr_resource {
+ struct ttm_resource base;
+ struct list_head blocks;
+ unsigned long flags;
+};
+
+static inline u64 amdgpu_vram_mgr_block_start(struct drm_buddy_block *block)
+{
+ return drm_buddy_block_offset(block);
+}
+
+static inline u64 amdgpu_vram_mgr_block_size(struct drm_buddy_block *block)
+{
+ return PAGE_SIZE << drm_buddy_block_order(block);
+}
+
+static inline struct drm_buddy_block *
+amdgpu_vram_mgr_first_block(struct list_head *list)
+{
+ return list_first_entry_or_null(list, struct drm_buddy_block, link);
+}
+
+static inline bool amdgpu_is_vram_mgr_blocks_contiguous(struct list_head *head)
+{
+ struct drm_buddy_block *block;
+ u64 start, size;
+
+ block = amdgpu_vram_mgr_first_block(head);
+ if (!block)
+ return false;
+
+ while (head != block->link.next) {
+ start = amdgpu_vram_mgr_block_start(block);
+ size = amdgpu_vram_mgr_block_size(block);
+
+ block = list_entry(block->link.next, struct drm_buddy_block, link);
+ if (start + size != amdgpu_vram_mgr_block_start(block))
+ return false;
+ }
+
+ return true;
+}
+
+static inline struct amdgpu_vram_mgr_resource *
+to_amdgpu_vram_mgr_resource(struct ttm_resource *res)
+{
+ return container_of(res, struct amdgpu_vram_mgr_resource, base);
+}
+
+#endif
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 62139ff35476..73423b805b54 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -9238,7 +9238,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
* deadlock during GPU reset when this fence will not signal
* but we hold reservation lock for the BO.
*/
- r = dma_resv_wait_timeout(abo->tbo.base.resv, true, false,
+ r = dma_resv_wait_timeout(abo->tbo.base.resv,
+ DMA_RESV_USAGE_WRITE, false,
msecs_to_jiffies(5000));
if (unlikely(r <= 0))
DRM_ERROR("Waiting for fences timed out!");
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
index 541949f2d44a..e0b9f7063b20 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_plane.c
@@ -256,6 +256,10 @@ static int komeda_plane_add(struct komeda_kms_dev *kms,
formats = komeda_get_layer_fourcc_list(&mdev->fmt_tbl,
layer->layer_type, &n_formats);
+ if (!formats) {
+ kfree(kplane);
+ return -ENOMEM;
+ }
err = drm_universal_plane_init(&kms->base, plane,
get_possible_crtcs(kms, c->pipeline),
@@ -266,8 +270,10 @@ static int komeda_plane_add(struct komeda_kms_dev *kms,
komeda_put_fourcc_list(formats);
- if (err)
- goto cleanup;
+ if (err) {
+ kfree(kplane);
+ return err;
+ }
drm_plane_helper_add(plane, &komeda_plane_helper_funcs);
diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c
index 494075ddbef6..b5928b52e279 100644
--- a/drivers/gpu/drm/arm/malidp_crtc.c
+++ b/drivers/gpu/drm/arm/malidp_crtc.c
@@ -487,7 +487,10 @@ static void malidp_crtc_reset(struct drm_crtc *crtc)
if (crtc->state)
malidp_crtc_destroy_state(crtc, crtc->state);
- __drm_atomic_helper_crtc_reset(crtc, &state->base);
+ if (state)
+ __drm_atomic_helper_crtc_reset(crtc, &state->base);
+ else
+ __drm_atomic_helper_crtc_reset(crtc, NULL);
}
static int malidp_crtc_enable_vblank(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c
index 0562bdaac00c..81d9f5004025 100644
--- a/drivers/gpu/drm/arm/malidp_planes.c
+++ b/drivers/gpu/drm/arm/malidp_planes.c
@@ -310,17 +310,13 @@ static int malidp_se_check_scaling(struct malidp_plane *mp,
static u32 malidp_get_pgsize_bitmap(struct malidp_plane *mp)
{
- u32 pgsize_bitmap = 0;
+ struct iommu_domain *mmu_dom;
- if (iommu_present(&platform_bus_type)) {
- struct iommu_domain *mmu_dom =
- iommu_get_domain_for_dev(mp->base.dev->dev);
+ mmu_dom = iommu_get_domain_for_dev(mp->base.dev->dev);
+ if (mmu_dom)
+ return mmu_dom->pgsize_bitmap;
- if (mmu_dom)
- pgsize_bitmap = mmu_dom->pgsize_bitmap;
- }
-
- return pgsize_bitmap;
+ return 0;
}
/*
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 658837caaf39..c08ccb4b332b 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -32,6 +32,7 @@ config DRM_CHIPONE_ICN6211
select DRM_KMS_HELPER
select DRM_MIPI_DSI
select DRM_PANEL_BRIDGE
+ select REGMAP_I2C
help
ICN6211 is MIPI-DSI/RGB Converter bridge from chipone.
@@ -99,6 +100,19 @@ config DRM_LONTIUM_LT8912B
Say M here if you want to support this hardware as a module.
The module will be named "lontium-lt8912b".
+config DRM_LONTIUM_LT9211
+ tristate "Lontium LT9211 DSI/LVDS/DPI bridge"
+ depends on OF
+ select DRM_PANEL_BRIDGE
+ select DRM_KMS_HELPER
+ select DRM_MIPI_DSI
+ select REGMAP_I2C
+ help
+ Driver for Lontium LT9211 Single/Dual-Link DSI/LVDS or Single DPI
+ input to Single-link/Dual-Link DSI/LVDS or Single DPI output bridge
+ chip.
+ Please say Y if you have such hardware.
+
config DRM_LONTIUM_LT9611
tristate "Lontium LT9611 DSI/HDMI bridge"
select SND_SOC_HDMI_CODEC if SND_SOC
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 425844c30495..b0edf2022fa0 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o
obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o
obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o
+obj-$(CONFIG_DRM_LONTIUM_LT9211) += lontium-lt9211.o
obj-$(CONFIG_DRM_LONTIUM_LT9611) += lontium-lt9611.o
obj-$(CONFIG_DRM_LONTIUM_LT9611UXC) += lontium-lt9611uxc.o
obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
index 668dcefbae17..b3f10c54e064 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
@@ -1292,8 +1292,10 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
goto err_unregister_cec;
adv7511->bridge.funcs = &adv7511_bridge_funcs;
- adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
- | DRM_BRIDGE_OP_HPD;
+ adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID;
+ if (adv7511->i2c_main->irq)
+ adv7511->bridge.ops |= DRM_BRIDGE_OP_HPD;
+
adv7511->bridge.of_node = dev->of_node;
adv7511->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
index 6516f9570b86..376da01243a3 100644
--- a/drivers/gpu/drm/bridge/analogix/anx7625.c
+++ b/drivers/gpu/drm/bridge/analogix/anx7625.c
@@ -1486,12 +1486,12 @@ static void anx7625_dp_adjust_swing(struct anx7625_data *ctx)
for (i = 0; i < ctx->pdata.dp_lane0_swing_reg_cnt; i++)
anx7625_reg_write(ctx, ctx->i2c.tx_p1_client,
DP_TX_LANE0_SWING_REG0 + i,
- ctx->pdata.lane0_reg_data[i] & 0xFF);
+ ctx->pdata.lane0_reg_data[i]);
for (i = 0; i < ctx->pdata.dp_lane1_swing_reg_cnt; i++)
anx7625_reg_write(ctx, ctx->i2c.tx_p1_client,
DP_TX_LANE1_SWING_REG0 + i,
- ctx->pdata.lane1_reg_data[i] & 0xFF);
+ ctx->pdata.lane1_reg_data[i]);
}
static void dp_hpd_change_handler(struct anx7625_data *ctx, bool on)
@@ -1598,8 +1598,8 @@ static int anx7625_get_swing_setting(struct device *dev,
num_regs = DP_TX_SWING_REG_CNT;
pdata->dp_lane0_swing_reg_cnt = num_regs;
- of_property_read_u32_array(dev->of_node, "analogix,lane0-swing",
- pdata->lane0_reg_data, num_regs);
+ of_property_read_u8_array(dev->of_node, "analogix,lane0-swing",
+ pdata->lane0_reg_data, num_regs);
}
if (of_get_property(dev->of_node,
@@ -1608,8 +1608,8 @@ static int anx7625_get_swing_setting(struct device *dev,
num_regs = DP_TX_SWING_REG_CNT;
pdata->dp_lane1_swing_reg_cnt = num_regs;
- of_property_read_u32_array(dev->of_node, "analogix,lane1-swing",
- pdata->lane1_reg_data, num_regs);
+ of_property_read_u8_array(dev->of_node, "analogix,lane1-swing",
+ pdata->lane1_reg_data, num_regs);
}
return 0;
@@ -1932,14 +1932,14 @@ static int anx7625_audio_get_eld(struct device *dev, void *data,
struct anx7625_data *ctx = dev_get_drvdata(dev);
if (!ctx->connector) {
- dev_err(dev, "connector not initial\n");
- return -EINVAL;
+ /* Pass en empty ELD if connector not available */
+ memset(buf, 0, len);
+ } else {
+ dev_dbg(dev, "audio copy eld\n");
+ memcpy(buf, ctx->connector->eld,
+ min(sizeof(ctx->connector->eld), len));
}
- dev_dbg(dev, "audio copy eld\n");
- memcpy(buf, ctx->connector->eld,
- min(sizeof(ctx->connector->eld), len));
-
return 0;
}
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.h b/drivers/gpu/drm/bridge/analogix/anx7625.h
index edbbfe410a56..e257a84db962 100644
--- a/drivers/gpu/drm/bridge/analogix/anx7625.h
+++ b/drivers/gpu/drm/bridge/analogix/anx7625.h
@@ -426,9 +426,9 @@ struct anx7625_platform_data {
int mipi_lanes;
int audio_en;
int dp_lane0_swing_reg_cnt;
- int lane0_reg_data[DP_TX_SWING_REG_CNT];
+ u8 lane0_reg_data[DP_TX_SWING_REG_CNT];
int dp_lane1_swing_reg_cnt;
- int lane1_reg_data[DP_TX_SWING_REG_CNT];
+ u8 lane1_reg_data[DP_TX_SWING_REG_CNT];
u32 low_power_mode;
struct device_node *mipi_host_node;
};
diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c
index b72aa3f3b5c2..47dea657a752 100644
--- a/drivers/gpu/drm/bridge/chipone-icn6211.c
+++ b/drivers/gpu/drm/bridge/chipone-icn6211.c
@@ -14,6 +14,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#define VENDOR_ID 0x00
@@ -134,6 +135,7 @@
struct chipone {
struct device *dev;
+ struct regmap *regmap;
struct i2c_client *client;
struct drm_bridge bridge;
struct drm_display_mode mode;
@@ -146,6 +148,77 @@ struct chipone {
bool interface_i2c;
};
+static const struct regmap_range chipone_dsi_readable_ranges[] = {
+ regmap_reg_range(VENDOR_ID, VERSION_ID),
+ regmap_reg_range(FIRMWARE_VERSION, PLL_SSC_OFFSET(3)),
+ regmap_reg_range(GPIO_OEN, MIPI_ULPS_CTRL),
+ regmap_reg_range(MIPI_CLK_CHK_VAR, MIPI_T_TA_SURE_PRE),
+ regmap_reg_range(MIPI_T_LPX_SET, MIPI_INIT_TIME_H),
+ regmap_reg_range(MIPI_T_CLK_TERM_EN, MIPI_T_CLK_SETTLE),
+ regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY_(5)),
+ regmap_reg_range(MIPI_PD_RX, MIPI_RST_NUM),
+ regmap_reg_range(MIPI_DBG_SET_(0), MIPI_DBG_SET_(9)),
+ regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS_(1)),
+};
+
+static const struct regmap_access_table chipone_dsi_readable_table = {
+ .yes_ranges = chipone_dsi_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(chipone_dsi_readable_ranges),
+};
+
+static const struct regmap_range chipone_dsi_writeable_ranges[] = {
+ regmap_reg_range(CONFIG_FINISH, PLL_SSC_OFFSET(3)),
+ regmap_reg_range(GPIO_OEN, MIPI_ULPS_CTRL),
+ regmap_reg_range(MIPI_CLK_CHK_VAR, MIPI_T_TA_SURE_PRE),
+ regmap_reg_range(MIPI_T_LPX_SET, MIPI_INIT_TIME_H),
+ regmap_reg_range(MIPI_T_CLK_TERM_EN, MIPI_T_CLK_SETTLE),
+ regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY_(5)),
+ regmap_reg_range(MIPI_PD_RX, MIPI_RST_NUM),
+ regmap_reg_range(MIPI_DBG_SET_(0), MIPI_DBG_SET_(9)),
+ regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS_(1)),
+};
+
+static const struct regmap_access_table chipone_dsi_writeable_table = {
+ .yes_ranges = chipone_dsi_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(chipone_dsi_writeable_ranges),
+};
+
+static const struct regmap_config chipone_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .rd_table = &chipone_dsi_readable_table,
+ .wr_table = &chipone_dsi_writeable_table,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = MIPI_ATE_STATUS_(1),
+};
+
+static int chipone_dsi_read(void *context,
+ const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ struct mipi_dsi_device *dsi = context;
+ const u16 reg16 = (val_size << 8) | *(u8 *)reg;
+ int ret;
+
+ ret = mipi_dsi_generic_read(dsi, &reg16, 2, val, val_size);
+
+ return ret == val_size ? 0 : -EINVAL;
+}
+
+static int chipone_dsi_write(void *context, const void *data, size_t count)
+{
+ struct mipi_dsi_device *dsi = context;
+
+ return mipi_dsi_generic_write(dsi, data, 2);
+}
+
+static const struct regmap_bus chipone_dsi_regmap_bus = {
+ .read = chipone_dsi_read,
+ .write = chipone_dsi_write,
+ .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
+ .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
+};
+
static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge)
{
return container_of(bridge, struct chipone, bridge);
@@ -153,18 +226,16 @@ static inline struct chipone *bridge_to_chipone(struct drm_bridge *bridge)
static void chipone_readb(struct chipone *icn, u8 reg, u8 *val)
{
- if (icn->interface_i2c)
- *val = i2c_smbus_read_byte_data(icn->client, reg);
- else
- mipi_dsi_generic_read(icn->dsi, (u8[]){reg, 1}, 2, val, 1);
+ int ret, pval;
+
+ ret = regmap_read(icn->regmap, reg, &pval);
+
+ *val = ret ? 0 : pval & 0xff;
}
static int chipone_writeb(struct chipone *icn, u8 reg, u8 val)
{
- if (icn->interface_i2c)
- return i2c_smbus_write_byte_data(icn->client, reg, val);
- else
- return mipi_dsi_generic_write(icn->dsi, (u8[]){reg, val}, 2);
+ return regmap_write(icn->regmap, reg, val);
}
static void chipone_configure_pll(struct chipone *icn,
@@ -324,6 +395,11 @@ static void chipone_atomic_enable(struct drm_bridge *bridge,
/* dsi specific sequence */
chipone_writeb(icn, SYNC_EVENT_DLY, 0x80);
chipone_writeb(icn, HFP_MIN, hfp & 0xff);
+
+ /* DSI data lane count */
+ chipone_writeb(icn, DSI_CTRL,
+ DSI_CTRL_UNKNOWN | DSI_CTRL_DSI_LANES(icn->dsi->lanes - 1));
+
chipone_writeb(icn, MIPI_PD_CK_LANE, 0xa0);
chipone_writeb(icn, PLL_CTRL(12), 0xff);
chipone_writeb(icn, MIPI_PN_SWAP, 0x00);
@@ -409,9 +485,23 @@ static void chipone_mode_set(struct drm_bridge *bridge,
static int chipone_dsi_attach(struct chipone *icn)
{
struct mipi_dsi_device *dsi = icn->dsi;
- int ret;
+ struct device *dev = icn->dev;
+ struct device_node *endpoint;
+ int dsi_lanes, ret;
+
+ endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0);
+ dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
+ of_node_put(endpoint);
+
+ /*
+ * If the 'data-lanes' property does not exist in DT or is invalid,
+ * default to previously hard-coded behavior, which was 4 data lanes.
+ */
+ if (dsi_lanes >= 1 && dsi_lanes <= 4)
+ icn->dsi->lanes = dsi_lanes;
+ else
+ icn->dsi->lanes = 4;
- dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
@@ -591,6 +681,11 @@ static int chipone_dsi_probe(struct mipi_dsi_device *dsi)
if (ret)
return ret;
+ icn->regmap = devm_regmap_init(dev, &chipone_dsi_regmap_bus,
+ dsi, &chipone_regmap_config);
+ if (IS_ERR(icn->regmap))
+ return PTR_ERR(icn->regmap);
+
icn->interface_i2c = false;
icn->dsi = dsi;
@@ -616,6 +711,10 @@ static int chipone_i2c_probe(struct i2c_client *client,
if (ret)
return ret;
+ icn->regmap = devm_regmap_init_i2c(client, &chipone_regmap_config);
+ if (IS_ERR(icn->regmap))
+ return PTR_ERR(icn->regmap);
+
icn->interface_i2c = true;
icn->client = client;
dev_set_drvdata(dev, icn);
diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c
index d24f5b90feab..e4d52a7e31b7 100644
--- a/drivers/gpu/drm/bridge/display-connector.c
+++ b/drivers/gpu/drm/bridge/display-connector.c
@@ -24,6 +24,7 @@ struct display_connector {
int hpd_irq;
struct regulator *dp_pwr;
+ struct gpio_desc *ddc_en;
};
static inline struct display_connector *
@@ -345,6 +346,17 @@ static int display_connector_probe(struct platform_device *pdev)
}
}
+ /* enable DDC */
+ if (type == DRM_MODE_CONNECTOR_HDMIA) {
+ conn->ddc_en = devm_gpiod_get_optional(&pdev->dev, "ddc-en",
+ GPIOD_OUT_HIGH);
+
+ if (IS_ERR(conn->ddc_en)) {
+ dev_err(&pdev->dev, "Couldn't get ddc-en gpio\n");
+ return PTR_ERR(conn->ddc_en);
+ }
+ }
+
conn->bridge.funcs = &display_connector_bridge_funcs;
conn->bridge.of_node = pdev->dev.of_node;
@@ -373,6 +385,9 @@ static int display_connector_remove(struct platform_device *pdev)
{
struct display_connector *conn = platform_get_drvdata(pdev);
+ if (conn->ddc_en)
+ gpiod_set_value(conn->ddc_en, 0);
+
if (conn->dp_pwr)
regulator_disable(conn->dp_pwr);
diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c
new file mode 100644
index 000000000000..e92821fbc639
--- /dev/null
+++ b/drivers/gpu/drm/bridge/lontium-lt9211.c
@@ -0,0 +1,802 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Lontium LT9211 bridge driver
+ *
+ * LT9211 is capable of converting:
+ * 2xDSI/2xLVDS/1xDPI -> 2xDSI/2xLVDS/1xDPI
+ * Currently supported is:
+ * 1xDSI -> 1xLVDS
+ *
+ * Copyright (C) 2022 Marek Vasut <marex@denx.de>
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#define REG_PAGE_CONTROL 0xff
+#define REG_CHIPID0 0x8100
+#define REG_CHIPID0_VALUE 0x18
+#define REG_CHIPID1 0x8101
+#define REG_CHIPID1_VALUE 0x01
+#define REG_CHIPID2 0x8102
+#define REG_CHIPID2_VALUE 0xe3
+
+#define REG_DSI_LANE 0xd000
+/* DSI lane count - 0 means 4 lanes ; 1, 2, 3 means 1, 2, 3 lanes. */
+#define REG_DSI_LANE_COUNT(n) ((n) & 3)
+
+struct lt9211 {
+ struct drm_bridge bridge;
+ struct device *dev;
+ struct regmap *regmap;
+ struct mipi_dsi_device *dsi;
+ struct drm_bridge *panel_bridge;
+ struct gpio_desc *reset_gpio;
+ struct regulator *vccio;
+ bool lvds_dual_link;
+ bool lvds_dual_link_even_odd_swap;
+};
+
+static const struct regmap_range lt9211_rw_ranges[] = {
+ regmap_reg_range(0xff, 0xff),
+ regmap_reg_range(0x8100, 0x816b),
+ regmap_reg_range(0x8200, 0x82aa),
+ regmap_reg_range(0x8500, 0x85ff),
+ regmap_reg_range(0x8600, 0x86a0),
+ regmap_reg_range(0x8700, 0x8746),
+ regmap_reg_range(0xd000, 0xd0a7),
+ regmap_reg_range(0xd400, 0xd42c),
+ regmap_reg_range(0xd800, 0xd838),
+ regmap_reg_range(0xd9c0, 0xd9d5),
+};
+
+static const struct regmap_access_table lt9211_rw_table = {
+ .yes_ranges = lt9211_rw_ranges,
+ .n_yes_ranges = ARRAY_SIZE(lt9211_rw_ranges),
+};
+
+static const struct regmap_range_cfg lt9211_range = {
+ .name = "lt9211",
+ .range_min = 0x0000,
+ .range_max = 0xda00,
+ .selector_reg = REG_PAGE_CONTROL,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 0x100,
+};
+
+static const struct regmap_config lt9211_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .rd_table = &lt9211_rw_table,
+ .wr_table = &lt9211_rw_table,
+ .volatile_table = &lt9211_rw_table,
+ .ranges = &lt9211_range,
+ .num_ranges = 1,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = 0xda00,
+};
+
+static struct lt9211 *bridge_to_lt9211(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct lt9211, bridge);
+}
+
+static int lt9211_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ struct lt9211 *ctx = bridge_to_lt9211(bridge);
+
+ return drm_bridge_attach(bridge->encoder, ctx->panel_bridge,
+ &ctx->bridge, flags);
+}
+
+static int lt9211_read_chipid(struct lt9211 *ctx)
+{
+ u8 chipid[3];
+ int ret;
+
+ /* Read Chip ID registers and verify the chip can communicate. */
+ ret = regmap_bulk_read(ctx->regmap, REG_CHIPID0, chipid, 3);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to read Chip ID: %d\n", ret);
+ return ret;
+ }
+
+ /* Test for known Chip ID. */
+ if (chipid[0] != REG_CHIPID0_VALUE || chipid[1] != REG_CHIPID1_VALUE ||
+ chipid[2] != REG_CHIPID2_VALUE) {
+ dev_err(ctx->dev, "Unknown Chip ID: 0x%02x 0x%02x 0x%02x\n",
+ chipid[0], chipid[1], chipid[2]);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int lt9211_system_init(struct lt9211 *ctx)
+{
+ const struct reg_sequence lt9211_system_init_seq[] = {
+ { 0x8201, 0x18 },
+ { 0x8606, 0x61 },
+ { 0x8607, 0xa8 },
+ { 0x8714, 0x08 },
+ { 0x8715, 0x00 },
+ { 0x8718, 0x0f },
+ { 0x8722, 0x08 },
+ { 0x8723, 0x00 },
+ { 0x8726, 0x0f },
+ { 0x810b, 0xfe },
+ };
+
+ return regmap_multi_reg_write(ctx->regmap, lt9211_system_init_seq,
+ ARRAY_SIZE(lt9211_system_init_seq));
+}
+
+static int lt9211_configure_rx(struct lt9211 *ctx)
+{
+ const struct reg_sequence lt9211_rx_phy_seq[] = {
+ { 0x8202, 0x44 },
+ { 0x8204, 0xa0 },
+ { 0x8205, 0x22 },
+ { 0x8207, 0x9f },
+ { 0x8208, 0xfc },
+ /* ORR with 0xf8 here to enable DSI DN/DP swap. */
+ { 0x8209, 0x01 },
+ { 0x8217, 0x0c },
+ { 0x8633, 0x1b },
+ };
+
+ const struct reg_sequence lt9211_rx_cal_reset_seq[] = {
+ { 0x8120, 0x7f },
+ { 0x8120, 0xff },
+ };
+
+ const struct reg_sequence lt9211_rx_dig_seq[] = {
+ { 0x8630, 0x85 },
+ /* 0x8588: BIT 6 set = MIPI-RX, BIT 4 unset = LVDS-TX */
+ { 0x8588, 0x40 },
+ { 0x85ff, 0xd0 },
+ { REG_DSI_LANE, REG_DSI_LANE_COUNT(ctx->dsi->lanes) },
+ { 0xd002, 0x05 },
+ };
+
+ const struct reg_sequence lt9211_rx_div_reset_seq[] = {
+ { 0x810a, 0xc0 },
+ { 0x8120, 0xbf },
+ };
+
+ const struct reg_sequence lt9211_rx_div_clear_seq[] = {
+ { 0x810a, 0xc1 },
+ { 0x8120, 0xff },
+ };
+
+ int ret;
+
+ ret = regmap_multi_reg_write(ctx->regmap, lt9211_rx_phy_seq,
+ ARRAY_SIZE(lt9211_rx_phy_seq));
+ if (ret)
+ return ret;
+
+ ret = regmap_multi_reg_write(ctx->regmap, lt9211_rx_cal_reset_seq,
+ ARRAY_SIZE(lt9211_rx_cal_reset_seq));
+ if (ret)
+ return ret;
+
+ ret = regmap_multi_reg_write(ctx->regmap, lt9211_rx_dig_seq,
+ ARRAY_SIZE(lt9211_rx_dig_seq));
+ if (ret)
+ return ret;
+
+ ret = regmap_multi_reg_write(ctx->regmap, lt9211_rx_div_reset_seq,
+ ARRAY_SIZE(lt9211_rx_div_reset_seq));
+ if (ret)
+ return ret;
+
+ usleep_range(10000, 15000);
+
+ return regmap_multi_reg_write(ctx->regmap, lt9211_rx_div_clear_seq,
+ ARRAY_SIZE(lt9211_rx_div_clear_seq));
+}
+
+static int lt9211_autodetect_rx(struct lt9211 *ctx,
+ const struct drm_display_mode *mode)
+{
+ u16 width, height;
+ u32 byteclk;
+ u8 buf[5];
+ u8 format;
+ u8 bc[3];
+ int ret;
+
+ /* Measure ByteClock frequency. */
+ ret = regmap_write(ctx->regmap, 0x8600, 0x01);
+ if (ret)
+ return ret;
+
+ /* Give the chip time to lock onto RX stream. */
+ msleep(100);
+
+ /* Read the ByteClock frequency from the chip. */
+ ret = regmap_bulk_read(ctx->regmap, 0x8608, bc, sizeof(bc));
+ if (ret)
+ return ret;
+
+ /* RX ByteClock in kHz */
+ byteclk = ((bc[0] & 0xf) << 16) | (bc[1] << 8) | bc[2];
+
+ /* Width/Height/Format Auto-detection */
+ ret = regmap_bulk_read(ctx->regmap, 0xd082, buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ width = (buf[0] << 8) | buf[1];
+ height = (buf[3] << 8) | buf[4];
+ format = buf[2] & 0xf;
+
+ if (format == 0x3) { /* YUV422 16bit */
+ width /= 2;
+ } else if (format == 0xa) { /* RGB888 24bit */
+ width /= 3;
+ } else {
+ dev_err(ctx->dev, "Unsupported DSI pixel format 0x%01x\n",
+ format);
+ return -EINVAL;
+ }
+
+ if (width != mode->hdisplay) {
+ dev_err(ctx->dev,
+ "RX: Detected DSI width (%d) does not match mode hdisplay (%d)\n",
+ width, mode->hdisplay);
+ return -EINVAL;
+ }
+
+ if (height != mode->vdisplay) {
+ dev_err(ctx->dev,
+ "RX: Detected DSI height (%d) does not match mode vdisplay (%d)\n",
+ height, mode->vdisplay);
+ return -EINVAL;
+ }
+
+ dev_dbg(ctx->dev, "RX: %dx%d format=0x%01x byteclock=%d kHz\n",
+ width, height, format, byteclk);
+
+ return 0;
+}
+
+static int lt9211_configure_timing(struct lt9211 *ctx,
+ const struct drm_display_mode *mode)
+{
+ const struct reg_sequence lt9211_timing[] = {
+ { 0xd00d, (mode->vtotal >> 8) & 0xff },
+ { 0xd00e, mode->vtotal & 0xff },
+ { 0xd00f, (mode->vdisplay >> 8) & 0xff },
+ { 0xd010, mode->vdisplay & 0xff },
+ { 0xd011, (mode->htotal >> 8) & 0xff },
+ { 0xd012, mode->htotal & 0xff },
+ { 0xd013, (mode->hdisplay >> 8) & 0xff },
+ { 0xd014, mode->hdisplay & 0xff },
+ { 0xd015, (mode->vsync_end - mode->vsync_start) & 0xff },
+ { 0xd016, (mode->hsync_end - mode->hsync_start) & 0xff },
+ { 0xd017, ((mode->vsync_start - mode->vdisplay) >> 8) & 0xff },
+ { 0xd018, (mode->vsync_start - mode->vdisplay) & 0xff },
+ { 0xd019, ((mode->hsync_start - mode->hdisplay) >> 8) & 0xff },
+ { 0xd01a, (mode->hsync_start - mode->hdisplay) & 0xff },
+ };
+
+ return regmap_multi_reg_write(ctx->regmap, lt9211_timing,
+ ARRAY_SIZE(lt9211_timing));
+}
+
+static int lt9211_configure_plls(struct lt9211 *ctx,
+ const struct drm_display_mode *mode)
+{
+ const struct reg_sequence lt9211_pcr_seq[] = {
+ { 0xd026, 0x17 },
+ { 0xd027, 0xc3 },
+ { 0xd02d, 0x30 },
+ { 0xd031, 0x10 },
+ { 0xd023, 0x20 },
+ { 0xd038, 0x02 },
+ { 0xd039, 0x10 },
+ { 0xd03a, 0x20 },
+ { 0xd03b, 0x60 },
+ { 0xd03f, 0x04 },
+ { 0xd040, 0x08 },
+ { 0xd041, 0x10 },
+ { 0x810b, 0xee },
+ { 0x810b, 0xfe },
+ };
+
+ unsigned int pval;
+ int ret;
+
+ /* DeSSC PLL reference clock is 25 MHz XTal. */
+ ret = regmap_write(ctx->regmap, 0x822d, 0x48);
+ if (ret)
+ return ret;
+
+ if (mode->clock < 44000) {
+ ret = regmap_write(ctx->regmap, 0x8235, 0x83);
+ } else if (mode->clock < 88000) {
+ ret = regmap_write(ctx->regmap, 0x8235, 0x82);
+ } else if (mode->clock < 176000) {
+ ret = regmap_write(ctx->regmap, 0x8235, 0x81);
+ } else {
+ dev_err(ctx->dev,
+ "Unsupported mode clock (%d kHz) above 176 MHz.\n",
+ mode->clock);
+ return -EINVAL;
+ }
+
+ if (ret)
+ return ret;
+
+ /* Wait for the DeSSC PLL to stabilize. */
+ msleep(100);
+
+ ret = regmap_multi_reg_write(ctx->regmap, lt9211_pcr_seq,
+ ARRAY_SIZE(lt9211_pcr_seq));
+ if (ret)
+ return ret;
+
+ /* PCR stability test takes seconds. */
+ ret = regmap_read_poll_timeout(ctx->regmap, 0xd087, pval, pval & 0x8,
+ 20000, 10000000);
+ if (ret)
+ dev_err(ctx->dev, "PCR unstable, ret=%i\n", ret);
+
+ return ret;
+}
+
+static int lt9211_configure_tx(struct lt9211 *ctx, bool jeida,
+ bool bpp24, bool de)
+{
+ const struct reg_sequence system_lt9211_tx_phy_seq[] = {
+ /* DPI output disable */
+ { 0x8262, 0x00 },
+ /* BIT(7) is LVDS dual-port */
+ { 0x823b, 0x38 | (ctx->lvds_dual_link ? BIT(7) : 0) },
+ { 0x823e, 0x92 },
+ { 0x823f, 0x48 },
+ { 0x8240, 0x31 },
+ { 0x8243, 0x80 },
+ { 0x8244, 0x00 },
+ { 0x8245, 0x00 },
+ { 0x8249, 0x00 },
+ { 0x824a, 0x01 },
+ { 0x824e, 0x00 },
+ { 0x824f, 0x00 },
+ { 0x8250, 0x00 },
+ { 0x8253, 0x00 },
+ { 0x8254, 0x01 },
+ /* LVDS channel order, Odd:Even 0x10..A:B, 0x40..B:A */
+ { 0x8646, ctx->lvds_dual_link_even_odd_swap ? 0x40 : 0x10 },
+ { 0x8120, 0x7b },
+ { 0x816b, 0xff },
+ };
+
+ const struct reg_sequence system_lt9211_tx_dig_seq[] = {
+ { 0x8559, 0x40 | (jeida ? BIT(7) : 0) |
+ (de ? BIT(5) : 0) | (bpp24 ? BIT(4) : 0) },
+ { 0x855a, 0xaa },
+ { 0x855b, 0xaa },
+ { 0x855c, ctx->lvds_dual_link ? BIT(0) : 0 },
+ { 0x85a1, 0x77 },
+ { 0x8640, 0x40 },
+ { 0x8641, 0x34 },
+ { 0x8642, 0x10 },
+ { 0x8643, 0x23 },
+ { 0x8644, 0x41 },
+ { 0x8645, 0x02 },
+ };
+
+ const struct reg_sequence system_lt9211_tx_pll_seq[] = {
+ /* TX PLL power down */
+ { 0x8236, 0x01 },
+ { 0x8237, ctx->lvds_dual_link ? 0x2a : 0x29 },
+ { 0x8238, 0x06 },
+ { 0x8239, 0x30 },
+ { 0x823a, 0x8e },
+ { 0x8737, 0x14 },
+ { 0x8713, 0x00 },
+ { 0x8713, 0x80 },
+ };
+
+ unsigned int pval;
+ int ret;
+
+ ret = regmap_multi_reg_write(ctx->regmap, system_lt9211_tx_phy_seq,
+ ARRAY_SIZE(system_lt9211_tx_phy_seq));
+ if (ret)
+ return ret;
+
+ ret = regmap_multi_reg_write(ctx->regmap, system_lt9211_tx_dig_seq,
+ ARRAY_SIZE(system_lt9211_tx_dig_seq));
+ if (ret)
+ return ret;
+
+ ret = regmap_multi_reg_write(ctx->regmap, system_lt9211_tx_pll_seq,
+ ARRAY_SIZE(system_lt9211_tx_pll_seq));
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(ctx->regmap, 0x871f, pval, pval & 0x80,
+ 10000, 1000000);
+ if (ret) {
+ dev_err(ctx->dev, "TX PLL unstable, ret=%i\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read_poll_timeout(ctx->regmap, 0x8720, pval, pval & 0x80,
+ 10000, 1000000);
+ if (ret) {
+ dev_err(ctx->dev, "TX PLL unstable, ret=%i\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void lt9211_atomic_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_bridge_state)
+{
+ struct lt9211 *ctx = bridge_to_lt9211(bridge);
+ struct drm_atomic_state *state = old_bridge_state->base.state;
+ const struct drm_bridge_state *bridge_state;
+ const struct drm_crtc_state *crtc_state;
+ const struct drm_display_mode *mode;
+ struct drm_connector *connector;
+ struct drm_crtc *crtc;
+ bool lvds_format_24bpp;
+ bool lvds_format_jeida;
+ u32 bus_flags;
+ int ret;
+
+ ret = regulator_enable(ctx->vccio);
+ if (ret) {
+ dev_err(ctx->dev, "Failed to enable vccio: %d\n", ret);
+ return;
+ }
+
+ /* Deassert reset */
+ gpiod_set_value(ctx->reset_gpio, 1);
+ usleep_range(20000, 21000); /* Very long post-reset delay. */
+
+ /* Get the LVDS format from the bridge state. */
+ bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
+ bus_flags = bridge_state->output_bus_cfg.flags;
+
+ switch (bridge_state->output_bus_cfg.format) {
+ case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+ lvds_format_24bpp = false;
+ lvds_format_jeida = true;
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
+ lvds_format_24bpp = true;
+ lvds_format_jeida = true;
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
+ lvds_format_24bpp = true;
+ lvds_format_jeida = false;
+ break;
+ default:
+ /*
+ * Some bridges still don't set the correct
+ * LVDS bus pixel format, use SPWG24 default
+ * format until those are fixed.
+ */
+ lvds_format_24bpp = true;
+ lvds_format_jeida = false;
+ dev_warn(ctx->dev,
+ "Unsupported LVDS bus format 0x%04x, please check output bridge driver. Falling back to SPWG24.\n",
+ bridge_state->output_bus_cfg.format);
+ break;
+ }
+
+ /*
+ * Retrieve the CRTC adjusted mode. This requires a little dance to go
+ * from the bridge to the encoder, to the connector and to the CRTC.
+ */
+ connector = drm_atomic_get_new_connector_for_encoder(state,
+ bridge->encoder);
+ crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ mode = &crtc_state->adjusted_mode;
+
+ ret = lt9211_read_chipid(ctx);
+ if (ret)
+ return;
+
+ ret = lt9211_system_init(ctx);
+ if (ret)
+ return;
+
+ ret = lt9211_configure_rx(ctx);
+ if (ret)
+ return;
+
+ ret = lt9211_autodetect_rx(ctx, mode);
+ if (ret)
+ return;
+
+ ret = lt9211_configure_timing(ctx, mode);
+ if (ret)
+ return;
+
+ ret = lt9211_configure_plls(ctx, mode);
+ if (ret)
+ return;
+
+ ret = lt9211_configure_tx(ctx, lvds_format_jeida, lvds_format_24bpp,
+ bus_flags & DRM_BUS_FLAG_DE_HIGH);
+ if (ret)
+ return;
+
+ dev_dbg(ctx->dev, "LT9211 enabled.\n");
+}
+
+static void lt9211_atomic_disable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_bridge_state)
+{
+ struct lt9211 *ctx = bridge_to_lt9211(bridge);
+ int ret;
+
+ /*
+ * Put the chip in reset, pull nRST line low,
+ * and assure lengthy 10ms reset low timing.
+ */
+ gpiod_set_value(ctx->reset_gpio, 0);
+ usleep_range(10000, 11000); /* Very long reset duration. */
+
+ ret = regulator_disable(ctx->vccio);
+ if (ret)
+ dev_err(ctx->dev, "Failed to disable vccio: %d\n", ret);
+
+ regcache_mark_dirty(ctx->regmap);
+}
+
+static enum drm_mode_status
+lt9211_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ /* LVDS output clock range 25..176 MHz */
+ if (mode->clock < 25000)
+ return MODE_CLOCK_LOW;
+ if (mode->clock > 176000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+#define MAX_INPUT_SEL_FORMATS 1
+
+static u32 *
+lt9211_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ u32 output_fmt,
+ unsigned int *num_input_fmts)
+{
+ u32 *input_fmts;
+
+ *num_input_fmts = 0;
+
+ input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
+ GFP_KERNEL);
+ if (!input_fmts)
+ return NULL;
+
+ /* This is the DSI-end bus format */
+ input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
+ *num_input_fmts = 1;
+
+ return input_fmts;
+}
+
+static const struct drm_bridge_funcs lt9211_funcs = {
+ .attach = lt9211_attach,
+ .mode_valid = lt9211_mode_valid,
+ .atomic_enable = lt9211_atomic_enable,
+ .atomic_disable = lt9211_atomic_disable,
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_get_input_bus_fmts = lt9211_atomic_get_input_bus_fmts,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+};
+
+static int lt9211_parse_dt(struct lt9211 *ctx)
+{
+ struct device_node *port2, *port3;
+ struct drm_bridge *panel_bridge;
+ struct device *dev = ctx->dev;
+ struct drm_panel *panel;
+ int dual_link;
+ int ret;
+
+ ctx->vccio = devm_regulator_get(dev, "vccio");
+ if (IS_ERR(ctx->vccio))
+ return dev_err_probe(dev, PTR_ERR(ctx->vccio),
+ "Failed to get supply 'vccio'\n");
+
+ ctx->lvds_dual_link = false;
+ ctx->lvds_dual_link_even_odd_swap = false;
+
+ port2 = of_graph_get_port_by_id(dev->of_node, 2);
+ port3 = of_graph_get_port_by_id(dev->of_node, 3);
+ dual_link = drm_of_lvds_get_dual_link_pixel_order(port2, port3);
+ of_node_put(port2);
+ of_node_put(port3);
+
+ if (dual_link == DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS) {
+ ctx->lvds_dual_link = true;
+ /* Odd pixels to LVDS Channel A, even pixels to B */
+ ctx->lvds_dual_link_even_odd_swap = false;
+ } else if (dual_link == DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS) {
+ ctx->lvds_dual_link = true;
+ /* Even pixels to LVDS Channel A, odd pixels to B */
+ ctx->lvds_dual_link_even_odd_swap = true;
+ }
+
+ ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, &panel_bridge);
+ if (ret < 0)
+ return ret;
+ if (panel) {
+ panel_bridge = devm_drm_panel_bridge_add(dev, panel);
+ if (IS_ERR(panel_bridge))
+ return PTR_ERR(panel_bridge);
+ }
+
+ ctx->panel_bridge = panel_bridge;
+
+ return 0;
+}
+
+static int lt9211_host_attach(struct lt9211 *ctx)
+{
+ const struct mipi_dsi_device_info info = {
+ .type = "lt9211",
+ .channel = 0,
+ .node = NULL,
+ };
+ struct device *dev = ctx->dev;
+ struct device_node *host_node;
+ struct device_node *endpoint;
+ struct mipi_dsi_device *dsi;
+ struct mipi_dsi_host *host;
+ int dsi_lanes;
+ int ret;
+
+ endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
+ dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
+ host_node = of_graph_get_remote_port_parent(endpoint);
+ host = of_find_mipi_dsi_host_by_node(host_node);
+ of_node_put(host_node);
+ of_node_put(endpoint);
+
+ if (!host)
+ return -EPROBE_DEFER;
+
+ if (dsi_lanes < 0 || dsi_lanes > 4)
+ return -EINVAL;
+
+ dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
+ if (IS_ERR(dsi))
+ return dev_err_probe(dev, PTR_ERR(dsi),
+ "failed to create dsi device\n");
+
+ ctx->dsi = dsi;
+
+ dsi->lanes = dsi_lanes;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+ MIPI_DSI_MODE_VIDEO_HSE;
+
+ ret = devm_mipi_dsi_attach(dev, dsi);
+ if (ret < 0) {
+ dev_err(dev, "failed to attach dsi to host: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lt9211_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct lt9211 *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->dev = dev;
+
+ /*
+ * Put the chip in reset, pull nRST line low,
+ * and assure lengthy 10ms reset low timing.
+ */
+ ctx->reset_gpio = devm_gpiod_get_optional(ctx->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->reset_gpio))
+ return PTR_ERR(ctx->reset_gpio);
+
+ usleep_range(10000, 11000); /* Very long reset duration. */
+
+ ret = lt9211_parse_dt(ctx);
+ if (ret)
+ return ret;
+
+ ctx->regmap = devm_regmap_init_i2c(client, &lt9211_regmap_config);
+ if (IS_ERR(ctx->regmap))
+ return PTR_ERR(ctx->regmap);
+
+ dev_set_drvdata(dev, ctx);
+ i2c_set_clientdata(client, ctx);
+
+ ctx->bridge.funcs = &lt9211_funcs;
+ ctx->bridge.of_node = dev->of_node;
+ drm_bridge_add(&ctx->bridge);
+
+ ret = lt9211_host_attach(ctx);
+ if (ret)
+ drm_bridge_remove(&ctx->bridge);
+
+ return ret;
+}
+
+static int lt9211_remove(struct i2c_client *client)
+{
+ struct lt9211 *ctx = i2c_get_clientdata(client);
+
+ drm_bridge_remove(&ctx->bridge);
+
+ return 0;
+}
+
+static struct i2c_device_id lt9211_id[] = {
+ { "lontium,lt9211" },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, lt9211_id);
+
+static const struct of_device_id lt9211_match_table[] = {
+ { .compatible = "lontium,lt9211" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, lt9211_match_table);
+
+static struct i2c_driver lt9211_driver = {
+ .probe = lt9211_probe,
+ .remove = lt9211_remove,
+ .id_table = lt9211_id,
+ .driver = {
+ .name = "lt9211",
+ .of_match_table = lt9211_match_table,
+ },
+};
+module_i2c_driver(lt9211_driver);
+
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_DESCRIPTION("Lontium LT9211 DSI/LVDS/DPI bridge driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index ff1c37b2e6e5..0ee563eb2b6f 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -83,8 +83,11 @@ static int panel_bridge_attach(struct drm_bridge *bridge,
drm_connector_attach_encoder(&panel_bridge->connector,
bridge->encoder);
- if (connector->funcs->reset)
- connector->funcs->reset(connector);
+ if (bridge->dev->registered) {
+ if (connector->funcs->reset)
+ connector->funcs->reset(connector);
+ drm_connector_register(connector);
+ }
return 0;
}
diff --git a/drivers/gpu/drm/bridge/synopsys/Kconfig b/drivers/gpu/drm/bridge/synopsys/Kconfig
index 21a1be3ced0f..a4a31b669b65 100644
--- a/drivers/gpu/drm/bridge/synopsys/Kconfig
+++ b/drivers/gpu/drm/bridge/synopsys/Kconfig
@@ -25,6 +25,16 @@ config DRM_DW_HDMI_I2S_AUDIO
Support the I2S Audio interface which is part of the Synopsys
Designware HDMI block.
+config DRM_DW_HDMI_GP_AUDIO
+ tristate "Synopsys Designware GP Audio interface"
+ depends on DRM_DW_HDMI && SND
+ select SND_PCM
+ select SND_PCM_ELD
+ select SND_PCM_IEC958
+ help
+ Support the GP Audio interface which is part of the Synopsys
+ Designware HDMI block.
+
config DRM_DW_HDMI_CEC
tristate "Synopsis Designware CEC interface"
depends on DRM_DW_HDMI
diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile
index 91d746ad5de1..ce715562e9e5 100644
--- a/drivers/gpu/drm/bridge/synopsys/Makefile
+++ b/drivers/gpu/drm/bridge/synopsys/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
+obj-$(CONFIG_DRM_DW_HDMI_GP_AUDIO) += dw-hdmi-gp-audio.o
obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o
obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c
new file mode 100644
index 000000000000..11ea1c84eb35
--- /dev/null
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * dw-hdmi-gp-audio.c
+ *
+ * Copyright 2020-2022 NXP
+ */
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_connector.h>
+
+#include <sound/hdmi-codec.h>
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_drm_eld.h>
+#include <sound/pcm_iec958.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "dw-hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-gp-audio"
+#define DRV_NAME "hdmi-gp-audio"
+
+struct snd_dw_hdmi {
+ struct dw_hdmi_audio_data data;
+ struct platform_device *audio_pdev;
+ unsigned int pos;
+};
+
+struct dw_hdmi_channel_conf {
+ u8 conf1;
+ u8 ca;
+};
+
+/*
+ * The default mapping of ALSA channels to HDMI channels and speaker
+ * allocation bits. Note that we can't do channel remapping here -
+ * channels must be in the same order.
+ *
+ * Mappings for alsa-lib pcm/surround*.conf files:
+ *
+ * Front Sur4.0 Sur4.1 Sur5.0 Sur5.1 Sur7.1
+ * Channels 2 4 6 6 6 8
+ *
+ * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
+ *
+ * Number of ALSA channels
+ * ALSA Channel 2 3 4 5 6 7 8
+ * 0 FL:0 = = = = = =
+ * 1 FR:1 = = = = = =
+ * 2 FC:3 RL:4 LFE:2 = = =
+ * 3 RR:5 RL:4 FC:3 = =
+ * 4 RR:5 RL:4 = =
+ * 5 RR:5 = =
+ * 6 RC:6 =
+ * 7 RLC/FRC RLC/FRC
+ */
+static struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = {
+ { 0x03, 0x00 }, /* FL,FR */
+ { 0x0b, 0x02 }, /* FL,FR,FC */
+ { 0x33, 0x08 }, /* FL,FR,RL,RR */
+ { 0x37, 0x09 }, /* FL,FR,LFE,RL,RR */
+ { 0x3f, 0x0b }, /* FL,FR,LFE,FC,RL,RR */
+ { 0x7f, 0x0f }, /* FL,FR,LFE,FC,RL,RR,RC */
+ { 0xff, 0x13 }, /* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */
+};
+
+static int audio_hw_params(struct device *dev, void *data,
+ struct hdmi_codec_daifmt *daifmt,
+ struct hdmi_codec_params *params)
+{
+ struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+ int ret = 0;
+ u8 ca;
+
+ dw_hdmi_set_sample_rate(dw->data.hdmi, params->sample_rate);
+
+ ca = default_hdmi_channel_config[params->channels - 2].ca;
+
+ dw_hdmi_set_channel_count(dw->data.hdmi, params->channels);
+ dw_hdmi_set_channel_allocation(dw->data.hdmi, ca);
+
+ dw_hdmi_set_sample_non_pcm(dw->data.hdmi,
+ params->iec.status[0] & IEC958_AES0_NONAUDIO);
+ dw_hdmi_set_sample_width(dw->data.hdmi, params->sample_width);
+
+ return ret;
+}
+
+static void audio_shutdown(struct device *dev, void *data)
+{
+}
+
+static int audio_mute_stream(struct device *dev, void *data,
+ bool enable, int direction)
+{
+ struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (!enable)
+ dw_hdmi_audio_enable(dw->data.hdmi);
+ else
+ dw_hdmi_audio_disable(dw->data.hdmi);
+
+ return ret;
+}
+
+static int audio_get_eld(struct device *dev, void *data,
+ u8 *buf, size_t len)
+{
+ struct dw_hdmi_audio_data *audio = data;
+ u8 *eld;
+
+ eld = audio->get_eld(audio->hdmi);
+ if (eld)
+ memcpy(buf, eld, min_t(size_t, MAX_ELD_BYTES, len));
+ else
+ /* Pass en empty ELD if connector not available */
+ memset(buf, 0, len);
+
+ return 0;
+}
+
+static int audio_hook_plugged_cb(struct device *dev, void *data,
+ hdmi_codec_plugged_cb fn,
+ struct device *codec_dev)
+{
+ struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+ return dw_hdmi_set_plugged_cb(dw->data.hdmi, fn, codec_dev);
+}
+
+static const struct hdmi_codec_ops audio_codec_ops = {
+ .hw_params = audio_hw_params,
+ .audio_shutdown = audio_shutdown,
+ .mute_stream = audio_mute_stream,
+ .get_eld = audio_get_eld,
+ .hook_plugged_cb = audio_hook_plugged_cb,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+ struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+ struct snd_dw_hdmi *dw;
+
+ const struct hdmi_codec_pdata codec_data = {
+ .i2s = 1,
+ .spdif = 0,
+ .ops = &audio_codec_ops,
+ .max_i2s_channels = 8,
+ .data = data,
+ };
+
+ dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
+ if (!dw)
+ return -ENOMEM;
+
+ dw->data = *data;
+
+ platform_set_drvdata(pdev, dw);
+
+ dw->audio_pdev = platform_device_register_data(&pdev->dev,
+ HDMI_CODEC_DRV_NAME, 1,
+ &codec_data,
+ sizeof(codec_data));
+
+ return PTR_ERR_OR_ZERO(dw->audio_pdev);
+}
+
+static int snd_dw_hdmi_remove(struct platform_device *pdev)
+{
+ struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+ platform_device_unregister(dw->audio_pdev);
+
+ return 0;
+}
+
+static struct platform_driver snd_dw_hdmi_driver = {
+ .probe = snd_dw_hdmi_probe,
+ .remove = snd_dw_hdmi_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
+MODULE_DESCRIPTION("Synopsys Designware HDMI GPA ALSA interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index a563460f8d20..f3f82a04c252 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -191,7 +191,10 @@ struct dw_hdmi {
spinlock_t audio_lock;
struct mutex audio_mutex;
+ unsigned int sample_non_pcm;
+ unsigned int sample_width;
unsigned int sample_rate;
+ unsigned int channels;
unsigned int audio_cts;
unsigned int audio_n;
bool audio_enable;
@@ -589,6 +592,8 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
n = 4096;
else if (pixel_clk == 74176000 || pixel_clk == 148352000)
n = 11648;
+ else if (pixel_clk == 297000000)
+ n = 3072;
else
n = 4096;
n *= mult;
@@ -601,6 +606,8 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
n = 17836;
else if (pixel_clk == 148352000)
n = 8918;
+ else if (pixel_clk == 297000000)
+ n = 4704;
else
n = 6272;
n *= mult;
@@ -615,6 +622,8 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
n = 11648;
else if (pixel_clk == 148352000)
n = 5824;
+ else if (pixel_clk == 297000000)
+ n = 5120;
else
n = 6144;
n *= mult;
@@ -659,8 +668,8 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID);
- /* Only compute CTS when using internal AHB audio */
- if (config3 & HDMI_CONFIG3_AHBAUDDMA) {
+ /* Compute CTS when using internal AHB audio or General Parallel audio*/
+ if ((config3 & HDMI_CONFIG3_AHBAUDDMA) || (config3 & HDMI_CONFIG3_GPAUD)) {
/*
* Compute the CTS value from the N value. Note that CTS and N
* can be up to 20 bits in total, so we need 64-bit math. Also
@@ -702,6 +711,22 @@ static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
mutex_unlock(&hdmi->audio_mutex);
}
+void dw_hdmi_set_sample_width(struct dw_hdmi *hdmi, unsigned int width)
+{
+ mutex_lock(&hdmi->audio_mutex);
+ hdmi->sample_width = width;
+ mutex_unlock(&hdmi->audio_mutex);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_width);
+
+void dw_hdmi_set_sample_non_pcm(struct dw_hdmi *hdmi, unsigned int non_pcm)
+{
+ mutex_lock(&hdmi->audio_mutex);
+ hdmi->sample_non_pcm = non_pcm;
+ mutex_unlock(&hdmi->audio_mutex);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_non_pcm);
+
void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
{
mutex_lock(&hdmi->audio_mutex);
@@ -717,6 +742,7 @@ void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt)
u8 layout;
mutex_lock(&hdmi->audio_mutex);
+ hdmi->channels = cnt;
/*
* For >2 channel PCM audio, we need to select layout 1
@@ -765,6 +791,89 @@ static u8 *hdmi_audio_get_eld(struct dw_hdmi *hdmi)
return hdmi->curr_conn->eld;
}
+static void dw_hdmi_gp_audio_enable(struct dw_hdmi *hdmi)
+{
+ const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
+ int sample_freq = 0x2, org_sample_freq = 0xD;
+ int ch_mask = BIT(hdmi->channels) - 1;
+
+ switch (hdmi->sample_rate) {
+ case 32000:
+ sample_freq = 0x03;
+ org_sample_freq = 0x0C;
+ break;
+ case 44100:
+ sample_freq = 0x00;
+ org_sample_freq = 0x0F;
+ break;
+ case 48000:
+ sample_freq = 0x02;
+ org_sample_freq = 0x0D;
+ break;
+ case 88200:
+ sample_freq = 0x08;
+ org_sample_freq = 0x07;
+ break;
+ case 96000:
+ sample_freq = 0x0A;
+ org_sample_freq = 0x05;
+ break;
+ case 176400:
+ sample_freq = 0x0C;
+ org_sample_freq = 0x03;
+ break;
+ case 192000:
+ sample_freq = 0x0E;
+ org_sample_freq = 0x01;
+ break;
+ default:
+ break;
+ }
+
+ hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+ hdmi_enable_audio_clk(hdmi, true);
+
+ hdmi_writeb(hdmi, 0x1, HDMI_FC_AUDSCHNLS0);
+ hdmi_writeb(hdmi, hdmi->channels, HDMI_FC_AUDSCHNLS2);
+ hdmi_writeb(hdmi, 0x22, HDMI_FC_AUDSCHNLS3);
+ hdmi_writeb(hdmi, 0x22, HDMI_FC_AUDSCHNLS4);
+ hdmi_writeb(hdmi, 0x11, HDMI_FC_AUDSCHNLS5);
+ hdmi_writeb(hdmi, 0x11, HDMI_FC_AUDSCHNLS6);
+ hdmi_writeb(hdmi, (0x3 << 4) | sample_freq, HDMI_FC_AUDSCHNLS7);
+ hdmi_writeb(hdmi, (org_sample_freq << 4) | 0xb, HDMI_FC_AUDSCHNLS8);
+
+ hdmi_writeb(hdmi, ch_mask, HDMI_GP_CONF1);
+ hdmi_writeb(hdmi, 0x02, HDMI_GP_CONF2);
+ hdmi_writeb(hdmi, 0x01, HDMI_GP_CONF0);
+
+ hdmi_modb(hdmi, 0x3, 0x3, HDMI_FC_DATAUTO3);
+
+ /* hbr */
+ if (hdmi->sample_rate == 192000 && hdmi->channels == 8 &&
+ hdmi->sample_width == 32 && hdmi->sample_non_pcm)
+ hdmi_modb(hdmi, 0x01, 0x01, HDMI_GP_CONF2);
+
+ if (pdata->enable_audio)
+ pdata->enable_audio(hdmi,
+ hdmi->channels,
+ hdmi->sample_width,
+ hdmi->sample_rate,
+ hdmi->sample_non_pcm);
+}
+
+static void dw_hdmi_gp_audio_disable(struct dw_hdmi *hdmi)
+{
+ const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
+
+ hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
+
+ hdmi_modb(hdmi, 0, 0x3, HDMI_FC_DATAUTO3);
+ if (pdata->disable_audio)
+ pdata->disable_audio(hdmi);
+
+ hdmi_enable_audio_clk(hdmi, false);
+}
+
static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi)
{
hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
@@ -1108,6 +1217,8 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP;
struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
u8 val, vp_conf;
+ u8 clear_gcp_auto = 0;
+
if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) ||
@@ -1117,6 +1228,7 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
case 8:
color_depth = 4;
output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
+ clear_gcp_auto = 1;
break;
case 10:
color_depth = 5;
@@ -1136,6 +1248,7 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
case 0:
case 8:
remap_size = HDMI_VP_REMAP_YCC422_16bit;
+ clear_gcp_auto = 1;
break;
case 10:
remap_size = HDMI_VP_REMAP_YCC422_20bit;
@@ -1160,6 +1273,19 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);
hdmi_writeb(hdmi, val, HDMI_VP_PR_CD);
+ /* HDMI1.4b specification section 6.5.3:
+ * Source shall only send GCPs with non-zero CD to sinks
+ * that indicate support for Deep Color.
+ * GCP only transmit CD and do not handle AVMUTE, PP norDefault_Phase (yet).
+ * Disable Auto GCP when 24-bit color for sinks that not support Deep Color.
+ */
+ val = hdmi_readb(hdmi, HDMI_FC_DATAUTO3);
+ if (clear_gcp_auto == 1)
+ val &= ~HDMI_FC_DATAUTO3_GCP_AUTO;
+ else
+ val |= HDMI_FC_DATAUTO3_GCP_AUTO;
+ hdmi_writeb(hdmi, val, HDMI_FC_DATAUTO3);
+
hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF);
@@ -1357,13 +1483,21 @@ static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable)
HDMI_PHY_CONF0_SELDIPIF_MASK);
}
-void dw_hdmi_phy_reset(struct dw_hdmi *hdmi)
+void dw_hdmi_phy_gen1_reset(struct dw_hdmi *hdmi)
+{
+ /* PHY reset. The reset signal is active low on Gen1 PHYs. */
+ hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ);
+ hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen1_reset);
+
+void dw_hdmi_phy_gen2_reset(struct dw_hdmi *hdmi)
{
/* PHY reset. The reset signal is active high on Gen2 PHYs. */
hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ);
hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ);
}
-EXPORT_SYMBOL_GPL(dw_hdmi_phy_reset);
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen2_reset);
void dw_hdmi_phy_i2c_set_addr(struct dw_hdmi *hdmi, u8 address)
{
@@ -1517,7 +1651,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi,
if (phy->has_svsret)
dw_hdmi_phy_enable_svsret(hdmi, 1);
- dw_hdmi_phy_reset(hdmi);
+ dw_hdmi_phy_gen2_reset(hdmi);
hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
@@ -2086,30 +2220,21 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
* then write one of the FC registers several times.
*
* The number of iterations matters and depends on the HDMI TX revision
- * (and possibly on the platform). So far i.MX6Q (v1.30a), i.MX6DL
- * (v1.31a) and multiple Allwinner SoCs (v1.32a) have been identified
- * as needing the workaround, with 4 iterations for v1.30a and 1
- * iteration for others.
- * The Amlogic Meson GX SoCs (v2.01a) have been identified as needing
- * the workaround with a single iteration.
- * The Rockchip RK3288 SoC (v2.00a) and RK3328/RK3399 SoCs (v2.11a) have
- * been identified as needing the workaround with a single iteration.
+ * (and possibly on the platform).
+ * 4 iterations for i.MX6Q(v1.30a) and 1 iteration for others.
+ * i.MX6DL (v1.31a), Allwinner SoCs (v1.32a), Rockchip RK3288 SoC (v2.00a),
+ * Amlogic Meson GX SoCs (v2.01a), RK3328/RK3399 SoCs (v2.11a)
+ * and i.MX8MPlus (v2.13a) have been identified as needing the workaround
+ * with a single iteration.
*/
switch (hdmi->version) {
case 0x130a:
count = 4;
break;
- case 0x131a:
- case 0x132a:
- case 0x200a:
- case 0x201a:
- case 0x211a:
- case 0x212a:
+ default:
count = 1;
break;
- default:
- return;
}
/* TMDS software reset */
@@ -3242,6 +3367,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
hdmi->plat_data = plat_data;
hdmi->dev = dev;
hdmi->sample_rate = 48000;
+ hdmi->channels = 2;
hdmi->disabled = true;
hdmi->rxsense = true;
hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
@@ -3465,6 +3591,24 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
pdevinfo.size_data = sizeof(audio);
pdevinfo.dma_mask = DMA_BIT_MASK(32);
hdmi->audio = platform_device_register_full(&pdevinfo);
+ } else if (iores && config3 & HDMI_CONFIG3_GPAUD) {
+ struct dw_hdmi_audio_data audio;
+
+ audio.phys = iores->start;
+ audio.base = hdmi->regs;
+ audio.irq = irq;
+ audio.hdmi = hdmi;
+ audio.get_eld = hdmi_audio_get_eld;
+
+ hdmi->enable_audio = dw_hdmi_gp_audio_enable;
+ hdmi->disable_audio = dw_hdmi_gp_audio_disable;
+
+ pdevinfo.name = "dw-hdmi-gp-audio";
+ pdevinfo.id = PLATFORM_DEVID_NONE;
+ pdevinfo.data = &audio;
+ pdevinfo.size_data = sizeof(audio);
+ pdevinfo.dma_mask = DMA_BIT_MASK(32);
+ hdmi->audio = platform_device_register_full(&pdevinfo);
}
if (!plat_data->disable_cec && (config0 & HDMI_CONFIG0_CEC)) {
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
index 1999db05bc3b..af43a0414b78 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
@@ -158,8 +158,17 @@
#define HDMI_FC_SPDDEVICEINF 0x1062
#define HDMI_FC_AUDSCONF 0x1063
#define HDMI_FC_AUDSSTAT 0x1064
-#define HDMI_FC_AUDSCHNLS7 0x106e
-#define HDMI_FC_AUDSCHNLS8 0x106f
+#define HDMI_FC_AUDSV 0x1065
+#define HDMI_FC_AUDSU 0x1066
+#define HDMI_FC_AUDSCHNLS0 0x1067
+#define HDMI_FC_AUDSCHNLS1 0x1068
+#define HDMI_FC_AUDSCHNLS2 0x1069
+#define HDMI_FC_AUDSCHNLS3 0x106A
+#define HDMI_FC_AUDSCHNLS4 0x106B
+#define HDMI_FC_AUDSCHNLS5 0x106C
+#define HDMI_FC_AUDSCHNLS6 0x106D
+#define HDMI_FC_AUDSCHNLS7 0x106E
+#define HDMI_FC_AUDSCHNLS8 0x106F
#define HDMI_FC_DATACH0FILL 0x1070
#define HDMI_FC_DATACH1FILL 0x1071
#define HDMI_FC_DATACH2FILL 0x1072
@@ -850,6 +859,9 @@ enum {
HDMI_FC_DATAUTO0_VSD_MASK = 0x08,
HDMI_FC_DATAUTO0_VSD_OFFSET = 3,
+/* FC_DATAUTO3 field values */
+ HDMI_FC_DATAUTO3_GCP_AUTO = 0x04,
+
/* PHY_CONF0 field values */
HDMI_PHY_CONF0_PDZ_MASK = 0x80,
HDMI_PHY_CONF0_PDZ_OFFSET = 7,
diff --git a/drivers/gpu/drm/dp/drm_dp.c b/drivers/gpu/drm/dp/drm_dp.c
index 580016a1b9eb..2a1f5ff6633c 100644
--- a/drivers/gpu/drm/dp/drm_dp.c
+++ b/drivers/gpu/drm/dp/drm_dp.c
@@ -528,6 +528,31 @@ unlock:
}
/**
+ * drm_dp_dpcd_probe() - probe a given DPCD address with a 1-byte read access
+ * @aux: DisplayPort AUX channel (SST)
+ * @offset: address of the register to probe
+ *
+ * Probe the provided DPCD address by reading 1 byte from it. The function can
+ * be used to trigger some side-effect the read access has, like waking up the
+ * sink, without the need for the read-out value.
+ *
+ * Returns 0 if the read access suceeded, or a negative error code on failure.
+ */
+int drm_dp_dpcd_probe(struct drm_dp_aux *aux, unsigned int offset)
+{
+ u8 buffer;
+ int ret;
+
+ ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, &buffer, 1);
+ WARN_ON(ret == 0);
+
+ drm_dp_dump_access(aux, DP_AUX_NATIVE_READ, offset, &buffer, ret);
+
+ return ret < 0 ? ret : 0;
+}
+EXPORT_SYMBOL(drm_dp_dpcd_probe);
+
+/**
* drm_dp_dpcd_read() - read a series of bytes from the DPCD
* @aux: DisplayPort AUX channel (SST or MST)
* @offset: address of the (first) register to read
@@ -559,10 +584,9 @@ ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
* monitor doesn't power down exactly after the throw away read.
*/
if (!aux->is_remote) {
- ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, DP_DPCD_REV,
- buffer, 1);
- if (ret != 1)
- goto out;
+ ret = drm_dp_dpcd_probe(aux, DP_DPCD_REV);
+ if (ret < 0)
+ return ret;
}
if (aux->is_remote)
@@ -571,7 +595,6 @@ ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset,
buffer, size);
-out:
drm_dp_dump_access(aux, DP_AUX_NATIVE_READ, offset, buffer, ret);
return ret;
}
diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c
index 72f52f293249..11bb59399471 100644
--- a/drivers/gpu/drm/drm_buddy.c
+++ b/drivers/gpu/drm/drm_buddy.c
@@ -665,6 +665,9 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm,
if (start + size == end)
return __drm_buddy_alloc_range(mm, start, size, blocks);
+ if (!IS_ALIGNED(size, min_page_size))
+ return -EINVAL;
+
pages = size >> ilog2(mm->chunk_size);
order = fls(pages) - 1;
min_order = ilog2(min_page_size) - ilog2(mm->chunk_size);
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index bff917531f33..b632825654a9 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -297,15 +297,15 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
return false;
}
- saved_mode = crtc->mode;
- saved_hwmode = crtc->hwmode;
+ drm_mode_init(&saved_mode, &crtc->mode);
+ drm_mode_init(&saved_hwmode, &crtc->hwmode);
saved_x = crtc->x;
saved_y = crtc->y;
/* Update crtc values up front so the driver can rely on them for mode
* setting.
*/
- crtc->mode = *mode;
+ drm_mode_copy(&crtc->mode, mode);
crtc->x = x;
crtc->y = y;
@@ -341,7 +341,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
}
DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
- crtc->hwmode = *adjusted_mode;
+ drm_mode_copy(&crtc->hwmode, adjusted_mode);
/* Prepare the encoders and CRTCs before setting the mode. */
drm_for_each_encoder(encoder, dev) {
@@ -411,8 +411,8 @@ done:
drm_mode_destroy(dev, adjusted_mode);
if (!ret) {
crtc->enabled = saved_enabled;
- crtc->mode = saved_mode;
- crtc->hwmode = saved_hwmode;
+ drm_mode_copy(&crtc->mode, &saved_mode);
+ drm_mode_copy(&crtc->hwmode, &saved_hwmode);
crtc->x = saved_x;
crtc->y = saved_y;
}
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 04e818ecd662..324ce8467915 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -1568,6 +1568,38 @@ static const struct drm_display_mode edid_4k_modes[] = {
/*** DDC fetch and block validation ***/
+static int edid_extension_block_count(const struct edid *edid)
+{
+ return edid->extensions;
+}
+
+static int edid_block_count(const struct edid *edid)
+{
+ return edid_extension_block_count(edid) + 1;
+}
+
+static int edid_size_by_blocks(int num_blocks)
+{
+ return num_blocks * EDID_LENGTH;
+}
+
+static int edid_size(const struct edid *edid)
+{
+ return edid_size_by_blocks(edid_block_count(edid));
+}
+
+static const void *edid_block_data(const struct edid *edid, int index)
+{
+ BUILD_BUG_ON(sizeof(*edid) != EDID_LENGTH);
+
+ return edid + index;
+}
+
+static const void *edid_extension_block_data(const struct edid *edid, int index)
+{
+ return edid_block_data(edid, index + 1);
+}
+
static const u8 edid_header[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
};
@@ -1632,9 +1664,9 @@ static int edid_block_tag(const void *_block)
return block[0];
}
-static bool edid_is_zero(const void *edid, int length)
+static bool edid_block_is_zero(const void *edid)
{
- return !memchr_inv(edid, 0, length);
+ return !memchr_inv(edid, 0, EDID_LENGTH);
}
/**
@@ -1654,8 +1686,8 @@ bool drm_edid_are_equal(const struct edid *edid1, const struct edid *edid2)
return false;
if (edid1) {
- edid1_len = EDID_LENGTH * (1 + edid1->extensions);
- edid2_len = EDID_LENGTH * (1 + edid2->extensions);
+ edid1_len = edid_size(edid1);
+ edid2_len = edid_size(edid2);
if (edid1_len != edid2_len)
return false;
@@ -1670,7 +1702,9 @@ EXPORT_SYMBOL(drm_edid_are_equal);
enum edid_block_status {
EDID_BLOCK_OK = 0,
+ EDID_BLOCK_READ_FAIL,
EDID_BLOCK_NULL,
+ EDID_BLOCK_ZERO,
EDID_BLOCK_HEADER_CORRUPT,
EDID_BLOCK_HEADER_REPAIR,
EDID_BLOCK_HEADER_FIXED,
@@ -1689,15 +1723,23 @@ static enum edid_block_status edid_block_check(const void *_block,
if (is_base_block) {
int score = drm_edid_header_is_valid(block);
- if (score < clamp(edid_fixup, 0, 8))
- return EDID_BLOCK_HEADER_CORRUPT;
+ if (score < clamp(edid_fixup, 0, 8)) {
+ if (edid_block_is_zero(block))
+ return EDID_BLOCK_ZERO;
+ else
+ return EDID_BLOCK_HEADER_CORRUPT;
+ }
if (score < 8)
return EDID_BLOCK_HEADER_REPAIR;
}
- if (edid_block_compute_checksum(block) != edid_block_get_checksum(block))
- return EDID_BLOCK_CHECKSUM;
+ if (edid_block_compute_checksum(block) != edid_block_get_checksum(block)) {
+ if (edid_block_is_zero(block))
+ return EDID_BLOCK_ZERO;
+ else
+ return EDID_BLOCK_CHECKSUM;
+ }
if (is_base_block) {
if (block->version != 1)
@@ -1720,6 +1762,70 @@ static bool edid_block_valid(const void *block, bool base)
edid_block_tag(block));
}
+static void edid_block_status_print(enum edid_block_status status,
+ const struct edid *block,
+ int block_num)
+{
+ switch (status) {
+ case EDID_BLOCK_OK:
+ break;
+ case EDID_BLOCK_READ_FAIL:
+ pr_debug("EDID block %d read failed\n", block_num);
+ break;
+ case EDID_BLOCK_NULL:
+ pr_debug("EDID block %d pointer is NULL\n", block_num);
+ break;
+ case EDID_BLOCK_ZERO:
+ pr_notice("EDID block %d is all zeroes\n", block_num);
+ break;
+ case EDID_BLOCK_HEADER_CORRUPT:
+ pr_notice("EDID has corrupt header\n");
+ break;
+ case EDID_BLOCK_HEADER_REPAIR:
+ pr_debug("EDID corrupt header needs repair\n");
+ break;
+ case EDID_BLOCK_HEADER_FIXED:
+ pr_debug("EDID corrupt header fixed\n");
+ break;
+ case EDID_BLOCK_CHECKSUM:
+ if (edid_block_status_valid(status, edid_block_tag(block))) {
+ pr_debug("EDID block %d (tag 0x%02x) checksum is invalid, remainder is %d, ignoring\n",
+ block_num, edid_block_tag(block),
+ edid_block_compute_checksum(block));
+ } else {
+ pr_notice("EDID block %d (tag 0x%02x) checksum is invalid, remainder is %d\n",
+ block_num, edid_block_tag(block),
+ edid_block_compute_checksum(block));
+ }
+ break;
+ case EDID_BLOCK_VERSION:
+ pr_notice("EDID has major version %d, instead of 1\n",
+ block->version);
+ break;
+ default:
+ WARN(1, "EDID block %d unknown edid block status code %d\n",
+ block_num, status);
+ break;
+ }
+}
+
+static void edid_block_dump(const char *level, const void *block, int block_num)
+{
+ enum edid_block_status status;
+ char prefix[20];
+
+ status = edid_block_check(block, block_num == 0);
+ if (status == EDID_BLOCK_ZERO)
+ sprintf(prefix, "\t[%02x] ZERO ", block_num);
+ else if (!edid_block_status_valid(status, edid_block_tag(block)))
+ sprintf(prefix, "\t[%02x] BAD ", block_num);
+ else
+ sprintf(prefix, "\t[%02x] GOOD ", block_num);
+
+ print_hex_dump(level, prefix, DUMP_PREFIX_NONE, 16, 1,
+ block, EDID_LENGTH, false);
+}
+
/**
* drm_edid_block_valid - Sanity check the EDID block (base or extension)
* @raw_edid: pointer to raw EDID block
@@ -1766,33 +1872,14 @@ bool drm_edid_block_valid(u8 *_block, int block_num, bool print_bad_edid,
*edid_corrupt = true;
}
+ edid_block_status_print(status, block, block_num);
+
/* Determine whether we can use this block with this status. */
valid = edid_block_status_valid(status, edid_block_tag(block));
- /* Some fairly random status printouts. */
- if (status == EDID_BLOCK_CHECKSUM) {
- if (valid) {
- DRM_DEBUG("EDID block checksum is invalid, remainder is %d\n",
- edid_block_compute_checksum(block));
- DRM_DEBUG("Assuming a KVM switch modified the block but left the original checksum\n");
- } else if (print_bad_edid) {
- DRM_NOTE("EDID block checksum is invalid, remainder is %d\n",
- edid_block_compute_checksum(block));
- }
- } else if (status == EDID_BLOCK_VERSION) {
- DRM_NOTE("EDID has major version %d, instead of 1\n",
- block->version);
- }
-
- if (!valid && print_bad_edid) {
- if (edid_is_zero(block, EDID_LENGTH)) {
- pr_notice("EDID block is all zeroes\n");
- } else {
- pr_notice("Raw EDID:\n");
- print_hex_dump(KERN_NOTICE,
- " \t", DUMP_PREFIX_NONE, 16, 1,
- block, EDID_LENGTH, false);
- }
+ if (!valid && print_bad_edid && status != EDID_BLOCK_ZERO) {
+ pr_notice("Raw EDID:\n");
+ edid_block_dump(KERN_NOTICE, block, block_num);
}
return valid;
@@ -1810,14 +1897,16 @@ EXPORT_SYMBOL(drm_edid_block_valid);
bool drm_edid_is_valid(struct edid *edid)
{
int i;
- u8 *raw = (u8 *)edid;
if (!edid)
return false;
- for (i = 0; i <= edid->extensions; i++)
- if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true, NULL))
+ for (i = 0; i < edid_block_count(edid); i++) {
+ void *block = (void *)edid_block_data(edid, i);
+
+ if (!drm_edid_block_valid(block, i, true, NULL))
return false;
+ }
return true;
}
@@ -1830,13 +1919,13 @@ static struct edid *edid_filter_invalid_blocks(const struct edid *edid,
int valid_extensions = edid->extensions - invalid_blocks;
int i;
- new = kmalloc_array(valid_extensions + 1, EDID_LENGTH, GFP_KERNEL);
+ new = kmalloc(edid_size_by_blocks(valid_extensions + 1), GFP_KERNEL);
if (!new)
goto out;
dest_block = new;
- for (i = 0; i <= edid->extensions; i++) {
- const void *block = edid + i;
+ for (i = 0; i < edid_block_count(edid); i++) {
+ const void *block = edid_block_data(edid, i);
if (edid_block_valid(block, i == 0))
memcpy(dest_block++, block, EDID_LENGTH);
@@ -1916,7 +2005,7 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len)
}
static void connector_bad_edid(struct drm_connector *connector,
- u8 *edid, int num_blocks)
+ const struct edid *edid, int num_blocks)
{
int i;
u8 last_block;
@@ -1927,32 +2016,19 @@ static void connector_bad_edid(struct drm_connector *connector,
* of 0x7e in the EDID of the _index_ of the last block in the
* combined chunk of memory.
*/
- last_block = edid[0x7e];
+ last_block = edid->extensions;
/* Calculate real checksum for the last edid extension block data */
if (last_block < num_blocks)
connector->real_edid_checksum =
- edid_block_compute_checksum(edid + last_block * EDID_LENGTH);
+ edid_block_compute_checksum(edid + last_block);
if (connector->bad_edid_counter++ && !drm_debug_enabled(DRM_UT_KMS))
return;
drm_dbg_kms(connector->dev, "%s: EDID is invalid:\n", connector->name);
- for (i = 0; i < num_blocks; i++) {
- u8 *block = edid + i * EDID_LENGTH;
- char prefix[20];
-
- if (edid_is_zero(block, EDID_LENGTH))
- sprintf(prefix, "\t[%02x] ZERO ", i);
- else if (!drm_edid_block_valid(block, i, false, NULL))
- sprintf(prefix, "\t[%02x] BAD ", i);
- else
- sprintf(prefix, "\t[%02x] GOOD ", i);
-
- print_hex_dump(KERN_DEBUG,
- prefix, DUMP_PREFIX_NONE, 16, 1,
- block, EDID_LENGTH, false);
- }
+ for (i = 0; i < num_blocks; i++)
+ edid_block_dump(KERN_DEBUG, edid + i, i);
}
/* Get override or firmware EDID */
@@ -1999,43 +2075,39 @@ int drm_add_override_edid_modes(struct drm_connector *connector)
}
EXPORT_SYMBOL(drm_add_override_edid_modes);
-static struct edid *drm_do_get_edid_base_block(struct drm_connector *connector,
- int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
- size_t len),
- void *data)
+typedef int read_block_fn(void *context, u8 *buf, unsigned int block, size_t len);
+
+static enum edid_block_status edid_block_read(void *block, unsigned int block_num,
+ read_block_fn read_block,
+ void *context)
{
- int *null_edid_counter = connector ? &connector->null_edid_counter : NULL;
- bool *edid_corrupt = connector ? &connector->edid_corrupt : NULL;
- void *edid;
+ enum edid_block_status status;
+ bool is_base_block = block_num == 0;
int try;
- edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
- if (edid == NULL)
- return NULL;
-
- /* base block fetch */
for (try = 0; try < 4; try++) {
- if (get_edid_block(data, edid, 0, EDID_LENGTH))
- goto out;
- if (drm_edid_block_valid(edid, 0, false, edid_corrupt))
- break;
- if (try == 0 && edid_is_zero(edid, EDID_LENGTH)) {
- if (null_edid_counter)
- (*null_edid_counter)++;
- goto carp;
+ if (read_block(context, block, block_num, EDID_LENGTH))
+ return EDID_BLOCK_READ_FAIL;
+
+ status = edid_block_check(block, is_base_block);
+ if (status == EDID_BLOCK_HEADER_REPAIR) {
+ edid_header_fix(block);
+
+ /* Retry with fixed header, update status if that worked. */
+ status = edid_block_check(block, is_base_block);
+ if (status == EDID_BLOCK_OK)
+ status = EDID_BLOCK_HEADER_FIXED;
}
- }
- if (try == 4)
- goto carp;
- return edid;
+ if (edid_block_status_valid(status, edid_block_tag(block)))
+ break;
-carp:
- if (connector)
- connector_bad_edid(connector, edid, 1);
-out:
- kfree(edid);
- return NULL;
+ /* Fail early for unrepairable base block all zeros. */
+ if (try == 0 && is_base_block && status == EDID_BLOCK_ZERO)
+ break;
+ }
+
+ return status;
}
/**
@@ -2059,53 +2131,74 @@ out:
* Return: Pointer to valid EDID or NULL if we couldn't find any.
*/
struct edid *drm_do_get_edid(struct drm_connector *connector,
- int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
- size_t len),
- void *data)
+ read_block_fn read_block,
+ void *context)
{
- int j, invalid_blocks = 0;
- struct edid *edid, *new, *override;
+ enum edid_block_status status;
+ int i, invalid_blocks = 0;
+ struct edid *edid, *new;
- override = drm_get_override_edid(connector);
- if (override)
- return override;
+ edid = drm_get_override_edid(connector);
+ if (edid)
+ goto ok;
- edid = drm_do_get_edid_base_block(connector, get_edid_block, data);
+ edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
if (!edid)
return NULL;
- if (edid->extensions == 0)
- return edid;
+ status = edid_block_read(edid, 0, read_block, context);
+
+ edid_block_status_print(status, edid, 0);
+
+ if (status == EDID_BLOCK_READ_FAIL)
+ goto fail;
+
+ /* FIXME: Clarify what a corrupt EDID actually means. */
+ if (status == EDID_BLOCK_OK || status == EDID_BLOCK_VERSION)
+ connector->edid_corrupt = false;
+ else
+ connector->edid_corrupt = true;
+
+ if (!edid_block_status_valid(status, edid_block_tag(edid))) {
+ if (status == EDID_BLOCK_ZERO)
+ connector->null_edid_counter++;
+
+ connector_bad_edid(connector, edid, 1);
+ goto fail;
+ }
- new = krealloc(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL);
+ if (!edid_extension_block_count(edid))
+ goto ok;
+
+ new = krealloc(edid, edid_size(edid), GFP_KERNEL);
if (!new)
- goto out;
+ goto fail;
edid = new;
- for (j = 1; j <= edid->extensions; j++) {
- void *block = edid + j;
- int try;
+ for (i = 1; i < edid_block_count(edid); i++) {
+ void *block = (void *)edid_block_data(edid, i);
- for (try = 0; try < 4; try++) {
- if (get_edid_block(data, block, j, EDID_LENGTH))
- goto out;
- if (drm_edid_block_valid(block, j, false, NULL))
- break;
- }
+ status = edid_block_read(block, i, read_block, context);
+
+ edid_block_status_print(status, block, i);
- if (try == 4)
+ if (!edid_block_status_valid(status, edid_block_tag(block))) {
+ if (status == EDID_BLOCK_READ_FAIL)
+ goto fail;
invalid_blocks++;
+ }
}
if (invalid_blocks) {
- connector_bad_edid(connector, (u8 *)edid, edid->extensions + 1);
+ connector_bad_edid(connector, edid, edid_block_count(edid));
edid = edid_filter_invalid_blocks(edid, invalid_blocks);
}
+ok:
return edid;
-out:
+fail:
kfree(edid);
return NULL;
}
@@ -2199,20 +2292,27 @@ static u32 edid_extract_panel_id(const struct edid *edid)
u32 drm_edid_get_panel_id(struct i2c_adapter *adapter)
{
- const struct edid *edid;
- u32 panel_id;
-
- edid = drm_do_get_edid_base_block(NULL, drm_do_probe_ddc_edid, adapter);
+ enum edid_block_status status;
+ void *base_block;
+ u32 panel_id = 0;
/*
* There are no manufacturer IDs of 0, so if there is a problem reading
* the EDID then we'll just return 0.
*/
- if (!edid)
+
+ base_block = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ if (!base_block)
return 0;
- panel_id = edid_extract_panel_id(edid);
- kfree(edid);
+ status = edid_block_read(base_block, 0, drm_do_probe_ddc_edid, adapter);
+
+ edid_block_status_print(status, base_block, 0);
+
+ if (edid_block_status_valid(status, edid_block_tag(base_block)))
+ panel_id = edid_extract_panel_id(base_block);
+
+ kfree(base_block);
return panel_id;
}
@@ -2255,7 +2355,7 @@ EXPORT_SYMBOL(drm_get_edid_switcheroo);
*/
struct edid *drm_edid_duplicate(const struct edid *edid)
{
- return kmemdup(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL);
+ return kmemdup(edid, edid_size(edid), GFP_KERNEL);
}
EXPORT_SYMBOL(drm_edid_duplicate);
@@ -2439,8 +2539,8 @@ drm_for_each_detailed_block(const struct edid *edid, detailed_cb *cb, void *clos
for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
cb(&(edid->detailed_timings[i]), closure);
- for (i = 1; i <= edid->extensions; i++) {
- const u8 *ext = (const u8 *)edid + (i * EDID_LENGTH);
+ for (i = 0; i < edid_extension_block_count(edid); i++) {
+ const u8 *ext = edid_extension_block_data(edid, i);
switch (*ext) {
case CEA_EXT:
@@ -3410,17 +3510,17 @@ const u8 *drm_find_edid_extension(const struct edid *edid,
int i;
/* No EDID or EDID extensions */
- if (edid == NULL || edid->extensions == 0)
+ if (!edid || !edid_extension_block_count(edid))
return NULL;
/* Find CEA extension */
- for (i = *ext_index; i < edid->extensions; i++) {
- edid_ext = (const u8 *)edid + EDID_LENGTH * (i + 1);
+ for (i = *ext_index; i < edid_extension_block_count(edid); i++) {
+ edid_ext = edid_extension_block_data(edid, i);
if (edid_block_tag(edid_ext) == ext_id)
break;
}
- if (i >= edid->extensions)
+ if (i >= edid_extension_block_count(edid))
return NULL;
*ext_index = i + 1;
@@ -3551,9 +3651,11 @@ static u8 drm_match_cea_mode_clock_tolerance(const struct drm_display_mode *to_m
match_flags |= DRM_MODE_MATCH_ASPECT_RATIO;
for (vic = 1; vic < cea_num_vics(); vic = cea_next_vic(vic)) {
- struct drm_display_mode cea_mode = *cea_mode_for_vic(vic);
+ struct drm_display_mode cea_mode;
unsigned int clock1, clock2;
+ drm_mode_init(&cea_mode, cea_mode_for_vic(vic));
+
/* Check both 60Hz and 59.94Hz */
clock1 = cea_mode.clock;
clock2 = cea_mode_alternate_clock(&cea_mode);
@@ -3590,9 +3692,11 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
match_flags |= DRM_MODE_MATCH_ASPECT_RATIO;
for (vic = 1; vic < cea_num_vics(); vic = cea_next_vic(vic)) {
- struct drm_display_mode cea_mode = *cea_mode_for_vic(vic);
+ struct drm_display_mode cea_mode;
unsigned int clock1, clock2;
+ drm_mode_init(&cea_mode, cea_mode_for_vic(vic));
+
/* Check both 60Hz and 59.94Hz */
clock1 = cea_mode.clock;
clock2 = cea_mode_alternate_clock(&cea_mode);
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 133dfae06fab..eb0c2d041f13 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -771,7 +771,8 @@ long drm_gem_dma_resv_wait(struct drm_file *filep, u32 handle,
return -EINVAL;
}
- ret = dma_resv_wait_timeout(obj->resv, wait_all, true, timeout);
+ ret = dma_resv_wait_timeout(obj->resv, dma_resv_usage_rw(wait_all),
+ true, timeout);
if (ret == 0)
ret = -ETIME;
else if (ret > 0)
diff --git a/drivers/gpu/drm/drm_gem_atomic_helper.c b/drivers/gpu/drm/drm_gem_atomic_helper.c
index 9338ddb7edff..a6d89aed0bda 100644
--- a/drivers/gpu/drm/drm_gem_atomic_helper.c
+++ b/drivers/gpu/drm/drm_gem_atomic_helper.c
@@ -151,7 +151,7 @@ int drm_gem_plane_helper_prepare_fb(struct drm_plane *plane, struct drm_plane_st
return 0;
obj = drm_gem_fb_get_obj(state->fb, 0);
- ret = dma_resv_get_singleton(obj->resv, false, &fence);
+ ret = dma_resv_get_singleton(obj->resv, DMA_RESV_USAGE_WRITE, &fence);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 6e7e10f16ec0..e699950cc34d 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -837,7 +837,9 @@ EXPORT_SYMBOL(drm_mode_vrefresh);
void drm_mode_get_hv_timing(const struct drm_display_mode *mode,
int *hdisplay, int *vdisplay)
{
- struct drm_display_mode adjusted = *mode;
+ struct drm_display_mode adjusted;
+
+ drm_mode_init(&adjusted, mode);
drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY);
*hdisplay = adjusted.crtc_hdisplay;
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index b701cda86d0c..2ff31717a3de 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -644,7 +644,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc,
vblank->linedur_ns = linedur_ns;
vblank->framedur_ns = framedur_ns;
- vblank->hwmode = *mode;
+ drm_mode_copy(&vblank->hwmode, mode);
drm_dbg_core(dev,
"crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
index d5314aa28ff7..507172e2780b 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
@@ -380,12 +380,14 @@ int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op,
}
if (op & ETNA_PREP_NOSYNC) {
- if (!dma_resv_test_signaled(obj->resv, write))
+ if (!dma_resv_test_signaled(obj->resv,
+ dma_resv_usage_rw(write)))
return -EBUSY;
} else {
unsigned long remain = etnaviv_timeout_to_jiffies(timeout);
- ret = dma_resv_wait_timeout(obj->resv, write, true, remain);
+ ret = dma_resv_wait_timeout(obj->resv, dma_resv_usage_rw(write),
+ true, remain);
if (ret <= 0)
return ret == 0 ? -ETIMEDOUT : ret;
}
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
index 53f7c78628a4..98bb5c9239de 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
@@ -202,14 +202,10 @@ static void submit_attach_object_fences(struct etnaviv_gem_submit *submit)
for (i = 0; i < submit->nr_bos; i++) {
struct drm_gem_object *obj = &submit->bos[i].obj->base;
+ bool write = submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE;
- if (submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE)
- dma_resv_add_excl_fence(obj->resv,
- submit->out_fence);
- else
- dma_resv_add_shared_fence(obj->resv,
- submit->out_fence);
-
+ dma_resv_add_fence(obj->resv, submit->out_fence, write ?
+ DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ);
submit_unlock_object(submit, i);
}
}
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index 2aff54d505e2..1d8744f3e702 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -396,9 +396,8 @@ static int psb_driver_load(struct drm_device *dev, unsigned long flags)
drm_for_each_connector_iter(connector, &conn_iter) {
gma_encoder = gma_attached_encoder(connector);
- switch (gma_encoder->type) {
- case INTEL_OUTPUT_LVDS:
- case INTEL_OUTPUT_MIPI:
+ if (gma_encoder->type == INTEL_OUTPUT_LVDS ||
+ gma_encoder->type == INTEL_OUTPUT_MIPI) {
ret = gma_backlight_init(dev);
break;
}
diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.c b/drivers/gpu/drm/i915/display/intel_atomic_plane.c
index 3d87da283cfa..efe8591619e3 100644
--- a/drivers/gpu/drm/i915/display/intel_atomic_plane.c
+++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.c
@@ -1047,7 +1047,8 @@ intel_prepare_plane_fb(struct drm_plane *_plane,
if (ret < 0)
goto unpin_fb;
- dma_resv_iter_begin(&cursor, obj->base.resv, false);
+ dma_resv_iter_begin(&cursor, obj->base.resv,
+ DMA_RESV_USAGE_WRITE);
dma_resv_for_each_fence_unlocked(&cursor, fence) {
add_rps_boost_after_vblank(new_plane_state->hw.crtc,
fence);
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_busy.c b/drivers/gpu/drm/i915/gem/i915_gem_busy.c
index 470fdfd61a0f..ddda468241ef 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_busy.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_busy.c
@@ -138,21 +138,21 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
* Alternatively, we can trade that extra information on read/write
* activity with
* args->busy =
- * !dma_resv_test_signaled(obj->resv, true);
+ * !dma_resv_test_signaled(obj->resv, DMA_RESV_USAGE_READ);
* to report the overall busyness. This is what the wait-ioctl does.
*
*/
args->busy = 0;
- dma_resv_iter_begin(&cursor, obj->base.resv, true);
+ dma_resv_iter_begin(&cursor, obj->base.resv, DMA_RESV_USAGE_READ);
dma_resv_for_each_fence_unlocked(&cursor, fence) {
if (dma_resv_iter_is_restarted(&cursor))
args->busy = 0;
- if (dma_resv_iter_is_exclusive(&cursor))
- /* Translate the exclusive fence to the READ *and* WRITE engine */
+ if (dma_resv_iter_usage(&cursor) <= DMA_RESV_USAGE_WRITE)
+ /* Translate the write fences to the READ *and* WRITE engine */
args->busy |= busy_check_writer(fence);
else
- /* Translate shared fences to READ set of engines */
+ /* Translate read fences to READ set of engines */
args->busy |= busy_check_reader(fence);
}
dma_resv_iter_end(&cursor);
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_clflush.c b/drivers/gpu/drm/i915/gem/i915_gem_clflush.c
index 1fd0cc9ca213..0512afdd20d8 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_clflush.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_clflush.c
@@ -116,7 +116,8 @@ bool i915_gem_clflush_object(struct drm_i915_gem_object *obj,
obj->base.resv, NULL, true,
i915_fence_timeout(i915),
I915_FENCE_GFP);
- dma_resv_add_excl_fence(obj->base.resv, &clflush->base.dma);
+ dma_resv_add_fence(obj->base.resv, &clflush->base.dma,
+ DMA_RESV_USAGE_KERNEL);
dma_fence_work_commit(&clflush->base);
/*
* We must have successfully populated the pages(since we are
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_lmem.c b/drivers/gpu/drm/i915/gem/i915_gem_lmem.c
index ede084f36ca9..02ed3b269326 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_lmem.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_lmem.c
@@ -68,7 +68,7 @@ bool __i915_gem_object_is_lmem(struct drm_i915_gem_object *obj)
struct intel_memory_region *mr = READ_ONCE(obj->mm.region);
#ifdef CONFIG_LOCKDEP
- GEM_WARN_ON(dma_resv_test_signaled(obj->base.resv, true) &&
+ GEM_WARN_ON(dma_resv_test_signaled(obj->base.resv, DMA_RESV_USAGE_BOOKKEEP) &&
i915_gem_object_evictable(obj));
#endif
return mr && (mr->type == INTEL_MEMORY_LOCAL ||
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c
index c1c3b510b9e2..747ac65e060f 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c
@@ -742,30 +742,19 @@ static const struct drm_gem_object_funcs i915_gem_object_funcs = {
/**
* i915_gem_object_get_moving_fence - Get the object's moving fence if any
* @obj: The object whose moving fence to get.
+ * @fence: The resulting fence
*
* A non-signaled moving fence means that there is an async operation
* pending on the object that needs to be waited on before setting up
* any GPU- or CPU PTEs to the object's pages.
*
- * Return: A refcounted pointer to the object's moving fence if any,
- * NULL otherwise.
+ * Return: Negative error code or 0 for success.
*/
-struct dma_fence *
-i915_gem_object_get_moving_fence(struct drm_i915_gem_object *obj)
+int i915_gem_object_get_moving_fence(struct drm_i915_gem_object *obj,
+ struct dma_fence **fence)
{
- return dma_fence_get(i915_gem_to_ttm(obj)->moving);
-}
-
-void i915_gem_object_set_moving_fence(struct drm_i915_gem_object *obj,
- struct dma_fence *fence)
-{
- struct dma_fence **moving = &i915_gem_to_ttm(obj)->moving;
-
- if (*moving == fence)
- return;
-
- dma_fence_put(*moving);
- *moving = dma_fence_get(fence);
+ return dma_resv_get_singleton(obj->base.resv, DMA_RESV_USAGE_KERNEL,
+ fence);
}
/**
@@ -783,23 +772,16 @@ void i915_gem_object_set_moving_fence(struct drm_i915_gem_object *obj,
int i915_gem_object_wait_moving_fence(struct drm_i915_gem_object *obj,
bool intr)
{
- struct dma_fence *fence = i915_gem_to_ttm(obj)->moving;
- int ret;
+ long ret;
assert_object_held(obj);
- if (!fence)
- return 0;
- ret = dma_fence_wait(fence, intr);
- if (ret)
- return ret;
+ ret = dma_resv_wait_timeout(obj->base. resv, DMA_RESV_USAGE_KERNEL,
+ intr, MAX_SCHEDULE_TIMEOUT);
+ if (!ret)
+ ret = -ETIME;
- if (fence->error)
- return fence->error;
-
- i915_gem_to_ttm(obj)->moving = NULL;
- dma_fence_put(fence);
- return 0;
+ return ret < 0 ? ret : 0;
}
#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
index 02c37fe4a535..e11d82a9f7c3 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
@@ -520,12 +520,8 @@ i915_gem_object_finish_access(struct drm_i915_gem_object *obj)
i915_gem_object_unpin_pages(obj);
}
-struct dma_fence *
-i915_gem_object_get_moving_fence(struct drm_i915_gem_object *obj);
-
-void i915_gem_object_set_moving_fence(struct drm_i915_gem_object *obj,
- struct dma_fence *fence);
-
+int i915_gem_object_get_moving_fence(struct drm_i915_gem_object *obj,
+ struct dma_fence **fence);
int i915_gem_object_wait_moving_fence(struct drm_i915_gem_object *obj,
bool intr);
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c
index 432ac74ff225..a10716f4e717 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c
@@ -467,19 +467,6 @@ out:
return fence;
}
-static int
-prev_deps(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx,
- struct i915_deps *deps)
-{
- int ret;
-
- ret = i915_deps_add_dependency(deps, bo->moving, ctx);
- if (!ret)
- ret = i915_deps_add_resv(deps, bo->base.resv, ctx);
-
- return ret;
-}
-
/**
* i915_ttm_move - The TTM move callback used by i915.
* @bo: The buffer object.
@@ -534,7 +521,7 @@ int i915_ttm_move(struct ttm_buffer_object *bo, bool evict,
struct i915_deps deps;
i915_deps_init(&deps, GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN);
- ret = prev_deps(bo, ctx, &deps);
+ ret = i915_deps_add_resv(&deps, bo->base.resv, ctx);
if (ret) {
i915_refct_sgt_put(dst_rsgt);
return ret;
@@ -637,9 +624,8 @@ int i915_gem_obj_copy_ttm(struct drm_i915_gem_object *dst,
if (IS_ERR_OR_NULL(copy_fence))
return PTR_ERR_OR_ZERO(copy_fence);
- dma_resv_add_excl_fence(dst_bo->base.resv, copy_fence);
- dma_resv_add_shared_fence(src_bo->base.resv, copy_fence);
-
+ dma_resv_add_fence(dst_bo->base.resv, copy_fence, DMA_RESV_USAGE_WRITE);
+ dma_resv_add_fence(src_bo->base.resv, copy_fence, DMA_RESV_USAGE_READ);
dma_fence_put(copy_fence);
return 0;
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
index 6d1a71d6404c..094f06b4ce33 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
@@ -86,7 +86,7 @@ static bool i915_gem_userptr_invalidate(struct mmu_interval_notifier *mni,
return true;
/* we will unbind on next submission, still have userptr pins */
- r = dma_resv_wait_timeout(obj->base.resv, true, false,
+ r = dma_resv_wait_timeout(obj->base.resv, DMA_RESV_USAGE_BOOKKEEP, false,
MAX_SCHEDULE_TIMEOUT);
if (r <= 0)
drm_err(&i915->drm, "(%ld) failed to wait for idle\n", r);
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_wait.c b/drivers/gpu/drm/i915/gem/i915_gem_wait.c
index dab3d30c09a0..319936f91ac5 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_wait.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_wait.c
@@ -40,7 +40,8 @@ i915_gem_object_wait_reservation(struct dma_resv *resv,
struct dma_fence *fence;
long ret = timeout ?: 1;
- dma_resv_iter_begin(&cursor, resv, flags & I915_WAIT_ALL);
+ dma_resv_iter_begin(&cursor, resv,
+ dma_resv_usage_rw(flags & I915_WAIT_ALL));
dma_resv_for_each_fence_unlocked(&cursor, fence) {
ret = i915_gem_object_wait_fence(fence, flags, timeout);
if (ret <= 0)
@@ -117,7 +118,8 @@ i915_gem_object_wait_priority(struct drm_i915_gem_object *obj,
struct dma_resv_iter cursor;
struct dma_fence *fence;
- dma_resv_iter_begin(&cursor, obj->base.resv, flags & I915_WAIT_ALL);
+ dma_resv_iter_begin(&cursor, obj->base.resv,
+ dma_resv_usage_rw(flags & I915_WAIT_ALL));
dma_resv_for_each_fence_unlocked(&cursor, fence)
i915_gem_fence_wait_priority(fence, attr);
dma_resv_iter_end(&cursor);
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c
index b071a58dd6da..b4275b55e5b8 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c
@@ -219,7 +219,8 @@ static int igt_dmabuf_import_same_driver(struct drm_i915_private *i915,
goto out_detach;
}
- timeout = dma_resv_wait_timeout(dmabuf->resv, false, true, 5 * HZ);
+ timeout = dma_resv_wait_timeout(dmabuf->resv, DMA_RESV_USAGE_WRITE,
+ true, 5 * HZ);
if (!timeout) {
pr_err("dmabuf wait for exclusive fence timed out.\n");
timeout = -ETIME;
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_migrate.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_migrate.c
index 0e52eb87cd55..0ad443a90c8b 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_migrate.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_migrate.c
@@ -218,9 +218,8 @@ static int __igt_lmem_pages_migrate(struct intel_gt *gt,
if (rq) {
err = dma_resv_reserve_fences(obj->base.resv, 1);
if (!err)
- dma_resv_add_excl_fence(obj->base.resv,
- &rq->fence);
- i915_gem_object_set_moving_fence(obj, &rq->fence);
+ dma_resv_add_fence(obj->base.resv, &rq->fence,
+ DMA_RESV_USAGE_KERNEL);
i915_request_put(rq);
}
if (err)
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
index c4c2c91a2ee7..5bc93a1ce3e3 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
@@ -1221,8 +1221,8 @@ static int __igt_mmap_migrate(struct intel_memory_region **placements,
expand32(POISON_INUSE), &rq);
i915_gem_object_unpin_pages(obj);
if (rq) {
- dma_resv_add_excl_fence(obj->base.resv, &rq->fence);
- i915_gem_object_set_moving_fence(obj, &rq->fence);
+ dma_resv_add_fence(obj->base.resv, &rq->fence,
+ DMA_RESV_USAGE_KERNEL);
i915_request_put(rq);
}
i915_gem_object_unlock(obj);
diff --git a/drivers/gpu/drm/i915/i915_deps.c b/drivers/gpu/drm/i915/i915_deps.c
index 999210b37325..297b8e4e42ee 100644
--- a/drivers/gpu/drm/i915/i915_deps.c
+++ b/drivers/gpu/drm/i915/i915_deps.c
@@ -226,7 +226,7 @@ int i915_deps_add_resv(struct i915_deps *deps, struct dma_resv *resv,
struct dma_fence *fence;
dma_resv_assert_held(resv);
- dma_resv_for_each_fence(&iter, resv, true, fence) {
+ dma_resv_for_each_fence(&iter, resv, dma_resv_usage_rw(true), fence) {
int ret = i915_deps_add_dependency(deps, fence, ctx);
if (ret)
diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c
index 582770360ad1..73d5195146b0 100644
--- a/drivers/gpu/drm/i915/i915_request.c
+++ b/drivers/gpu/drm/i915/i915_request.c
@@ -1598,7 +1598,8 @@ i915_request_await_object(struct i915_request *to,
struct dma_fence *fence;
int ret = 0;
- dma_resv_for_each_fence(&cursor, obj->base.resv, write, fence) {
+ dma_resv_for_each_fence(&cursor, obj->base.resv,
+ dma_resv_usage_rw(write), fence) {
ret = i915_request_await_dma_fence(to, fence);
if (ret)
break;
diff --git a/drivers/gpu/drm/i915/i915_sw_fence.c b/drivers/gpu/drm/i915/i915_sw_fence.c
index 2a74a9a1cafe..ae984c66c48a 100644
--- a/drivers/gpu/drm/i915/i915_sw_fence.c
+++ b/drivers/gpu/drm/i915/i915_sw_fence.c
@@ -585,7 +585,7 @@ int i915_sw_fence_await_reservation(struct i915_sw_fence *fence,
debug_fence_assert(fence);
might_sleep_if(gfpflags_allow_blocking(gfp));
- dma_resv_iter_begin(&cursor, resv, write);
+ dma_resv_iter_begin(&cursor, resv, dma_resv_usage_rw(write));
dma_resv_for_each_fence_unlocked(&cursor, f) {
pending = i915_sw_fence_await_dma_fence(fence, f, timeout,
gfp);
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index bae3423f58e8..d077f7b9eaad 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -1357,10 +1357,17 @@ int i915_vma_pin_ww(struct i915_vma *vma, struct i915_gem_ww_ctx *ww,
if (err)
return err;
+ if (vma->obj) {
+ err = i915_gem_object_get_moving_fence(vma->obj, &moving);
+ if (err)
+ return err;
+ } else {
+ moving = NULL;
+ }
+
if (flags & PIN_GLOBAL)
wakeref = intel_runtime_pm_get(&vma->vm->i915->runtime_pm);
- moving = vma->obj ? i915_gem_object_get_moving_fence(vma->obj) : NULL;
if (flags & vma->vm->bind_async_flags || moving) {
/* lock VM */
err = i915_vm_lock_objects(vma->vm, ww);
@@ -1826,7 +1833,8 @@ int _i915_vma_move_to_active(struct i915_vma *vma,
}
if (fence) {
- dma_resv_add_excl_fence(vma->obj->base.resv, fence);
+ dma_resv_add_fence(vma->obj->base.resv, fence,
+ DMA_RESV_USAGE_WRITE);
obj->write_domain = I915_GEM_DOMAIN_RENDER;
obj->read_domains = 0;
}
@@ -1838,7 +1846,8 @@ int _i915_vma_move_to_active(struct i915_vma *vma,
}
if (fence) {
- dma_resv_add_shared_fence(vma->obj->base.resv, fence);
+ dma_resv_add_fence(vma->obj->base.resv, fence,
+ DMA_RESV_USAGE_READ);
obj->write_domain = 0;
}
}
@@ -2078,7 +2087,7 @@ int i915_vma_unbind_async(struct i915_vma *vma, bool trylock_vm)
goto out_rpm;
}
- dma_resv_add_shared_fence(obj->base.resv, fence);
+ dma_resv_add_fence(obj->base.resv, fence, DMA_RESV_USAGE_READ);
dma_fence_put(fence);
out_rpm:
diff --git a/drivers/gpu/drm/i915/selftests/intel_memory_region.c b/drivers/gpu/drm/i915/selftests/intel_memory_region.c
index 6114e013092b..73eb53edb8de 100644
--- a/drivers/gpu/drm/i915/selftests/intel_memory_region.c
+++ b/drivers/gpu/drm/i915/selftests/intel_memory_region.c
@@ -1056,7 +1056,8 @@ static int igt_lmem_write_cpu(void *arg)
obj->mm.pages->sgl, I915_CACHE_NONE,
true, 0xdeadbeaf, &rq);
if (rq) {
- dma_resv_add_excl_fence(obj->base.resv, &rq->fence);
+ dma_resv_add_fence(obj->base.resv, &rq->fence,
+ DMA_RESV_USAGE_WRITE);
i915_request_put(rq);
}
diff --git a/drivers/gpu/drm/ingenic/Kconfig b/drivers/gpu/drm/ingenic/Kconfig
index 001f59fb06d5..090830bcbde7 100644
--- a/drivers/gpu/drm/ingenic/Kconfig
+++ b/drivers/gpu/drm/ingenic/Kconfig
@@ -24,4 +24,13 @@ config DRM_INGENIC_IPU
The Image Processing Unit (IPU) will appear as a second primary plane.
+config DRM_INGENIC_DW_HDMI
+ tristate "Ingenic specific support for Synopsys DW HDMI"
+ depends on MACH_JZ4780
+ select DRM_DW_HDMI
+ help
+ Choose this option to enable Synopsys DesignWare HDMI based driver.
+ If you want to enable HDMI on Ingenic JZ4780 based SoC, you should
+ select this option.
+
endif
diff --git a/drivers/gpu/drm/ingenic/Makefile b/drivers/gpu/drm/ingenic/Makefile
index d313326bdddb..f10cc1c5a5f2 100644
--- a/drivers/gpu/drm/ingenic/Makefile
+++ b/drivers/gpu/drm/ingenic/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_DRM_INGENIC) += ingenic-drm.o
ingenic-drm-y = ingenic-drm-drv.o
ingenic-drm-$(CONFIG_DRM_INGENIC_IPU) += ingenic-ipu.o
+obj-$(CONFIG_DRM_INGENIC_DW_HDMI) += ingenic-dw-hdmi.o
diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index a4f5a323f490..8eb0ad501a7b 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -833,6 +833,32 @@ static int ingenic_drm_bridge_atomic_check(struct drm_bridge *bridge,
}
}
+static u32 *
+ingenic_drm_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ u32 output_fmt,
+ unsigned int *num_input_fmts)
+{
+ switch (output_fmt) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_RGB666_1X18:
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ case MEDIA_BUS_FMT_RGB888_3X8:
+ case MEDIA_BUS_FMT_RGB888_3X8_DELTA:
+ break;
+ default:
+ *num_input_fmts = 0;
+ return NULL;
+ }
+
+ return drm_atomic_helper_bridge_propagate_bus_fmt(bridge, bridge_state,
+ crtc_state, conn_state,
+ output_fmt,
+ num_input_fmts);
+}
+
static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg)
{
struct ingenic_drm *priv = drm_device_get_priv(arg);
@@ -984,7 +1010,7 @@ static const struct drm_bridge_funcs ingenic_drm_bridge_funcs = {
.atomic_reset = drm_atomic_helper_bridge_reset,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
+ .atomic_get_input_bus_fmts = ingenic_drm_bridge_atomic_get_input_bus_fmts,
};
static const struct drm_mode_config_funcs ingenic_drm_mode_config_funcs = {
diff --git a/drivers/gpu/drm/ingenic/ingenic-dw-hdmi.c b/drivers/gpu/drm/ingenic/ingenic-dw-hdmi.c
new file mode 100644
index 000000000000..72f8b44998a5
--- /dev/null
+++ b/drivers/gpu/drm/ingenic/ingenic-dw-hdmi.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
+ * Copyright (C) 2019, 2020 Paul Boddie <paul@boddie.org.uk>
+ *
+ * Derived from dw_hdmi-imx.c with i.MX portions removed.
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_of.h>
+#include <drm/drm_print.h>
+
+static const struct dw_hdmi_mpll_config ingenic_mpll_cfg[] = {
+ { 45250000, { { 0x01e0, 0x0000 }, { 0x21e1, 0x0000 }, { 0x41e2, 0x0000 } } },
+ { 92500000, { { 0x0140, 0x0005 }, { 0x2141, 0x0005 }, { 0x4142, 0x0005 } } },
+ { 148500000, { { 0x00a0, 0x000a }, { 0x20a1, 0x000a }, { 0x40a2, 0x000a } } },
+ { 216000000, { { 0x00a0, 0x000a }, { 0x2001, 0x000f }, { 0x4002, 0x000f } } },
+ { ~0UL, { { 0x0000, 0x0000 }, { 0x0000, 0x0000 }, { 0x0000, 0x0000 } } }
+};
+
+static const struct dw_hdmi_curr_ctrl ingenic_cur_ctr[] = {
+ /*pixelclk bpp8 bpp10 bpp12 */
+ { 54000000, { 0x091c, 0x091c, 0x06dc } },
+ { 58400000, { 0x091c, 0x06dc, 0x06dc } },
+ { 72000000, { 0x06dc, 0x06dc, 0x091c } },
+ { 74250000, { 0x06dc, 0x0b5c, 0x091c } },
+ { 118800000, { 0x091c, 0x091c, 0x06dc } },
+ { 216000000, { 0x06dc, 0x0b5c, 0x091c } },
+ { ~0UL, { 0x0000, 0x0000, 0x0000 } },
+};
+
+/*
+ * Resistance term 133Ohm Cfg
+ * PREEMP config 0.00
+ * TX/CK level 10
+ */
+static const struct dw_hdmi_phy_config ingenic_phy_config[] = {
+ /*pixelclk symbol term vlev */
+ { 216000000, 0x800d, 0x0005, 0x01ad},
+ { ~0UL, 0x0000, 0x0000, 0x0000}
+};
+
+static enum drm_mode_status
+ingenic_dw_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ if (mode->clock < 13500)
+ return MODE_CLOCK_LOW;
+ /* FIXME: Hardware is capable of 270MHz, but setup data is missing. */
+ if (mode->clock > 216000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static struct dw_hdmi_plat_data ingenic_dw_hdmi_plat_data = {
+ .mpll_cfg = ingenic_mpll_cfg,
+ .cur_ctr = ingenic_cur_ctr,
+ .phy_config = ingenic_phy_config,
+ .mode_valid = ingenic_dw_hdmi_mode_valid,
+ .output_port = 1,
+};
+
+static const struct of_device_id ingenic_dw_hdmi_dt_ids[] = {
+ { .compatible = "ingenic,jz4780-dw-hdmi" },
+ { /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ingenic_dw_hdmi_dt_ids);
+
+static void ingenic_dw_hdmi_cleanup(void *data)
+{
+ struct dw_hdmi *hdmi = (struct dw_hdmi *)data;
+
+ dw_hdmi_remove(hdmi);
+}
+
+static int ingenic_dw_hdmi_probe(struct platform_device *pdev)
+{
+ struct dw_hdmi *hdmi;
+
+ hdmi = dw_hdmi_probe(pdev, &ingenic_dw_hdmi_plat_data);
+ if (IS_ERR(hdmi))
+ return PTR_ERR(hdmi);
+
+ return devm_add_action_or_reset(&pdev->dev, ingenic_dw_hdmi_cleanup, hdmi);
+}
+
+static struct platform_driver ingenic_dw_hdmi_driver = {
+ .probe = ingenic_dw_hdmi_probe,
+ .driver = {
+ .name = "dw-hdmi-ingenic",
+ .of_match_table = ingenic_dw_hdmi_dt_ids,
+ },
+};
+module_platform_driver(ingenic_dw_hdmi_driver);
+
+MODULE_DESCRIPTION("JZ4780 Specific DW-HDMI Driver Extension");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dw-hdmi-ingenic");
diff --git a/drivers/gpu/drm/lima/lima_gem.c b/drivers/gpu/drm/lima/lima_gem.c
index e0a11ee0e86d..0f1ca0b0db49 100644
--- a/drivers/gpu/drm/lima/lima_gem.c
+++ b/drivers/gpu/drm/lima/lima_gem.c
@@ -364,10 +364,9 @@ int lima_gem_submit(struct drm_file *file, struct lima_submit *submit)
fence = lima_sched_context_queue_task(submit->task);
for (i = 0; i < submit->nr_bos; i++) {
- if (submit->bos[i].flags & LIMA_SUBMIT_BO_WRITE)
- dma_resv_add_excl_fence(lima_bo_resv(bos[i]), fence);
- else
- dma_resv_add_shared_fence(lima_bo_resv(bos[i]), fence);
+ dma_resv_add_fence(lima_bo_resv(bos[i]), fence,
+ submit->bos[i].flags & LIMA_SUBMIT_BO_WRITE ?
+ DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ);
}
drm_gem_unlock_reservations((struct drm_gem_object **)bos,
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index 02b9ae65a96a..01bbb5f2d462 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -848,7 +848,8 @@ int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, ktime_t *timeout)
op & MSM_PREP_NOSYNC ? 0 : timeout_to_jiffies(timeout);
long ret;
- ret = dma_resv_wait_timeout(obj->resv, write, true, remain);
+ ret = dma_resv_wait_timeout(obj->resv, dma_resv_usage_rw(write),
+ true, remain);
if (ret == 0)
return remain == 0 ? -EBUSY : -ETIMEDOUT;
else if (ret < 0)
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
index 3164db8be893..8d1eef914ba8 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -395,9 +395,11 @@ static void submit_attach_object_fences(struct msm_gem_submit *submit)
struct drm_gem_object *obj = &submit->bos[i].obj->base;
if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE)
- dma_resv_add_excl_fence(obj->resv, submit->user_fence);
+ dma_resv_add_fence(obj->resv, submit->user_fence,
+ DMA_RESV_USAGE_WRITE);
else if (submit->bos[i].flags & MSM_SUBMIT_BO_READ)
- dma_resv_add_shared_fence(obj->resv, submit->user_fence);
+ dma_resv_add_fence(obj->resv, submit->user_fence,
+ DMA_RESV_USAGE_READ);
}
}
diff --git a/drivers/gpu/drm/nouveau/dispnv50/base917c.c b/drivers/gpu/drm/nouveau/dispnv50/base917c.c
index a1baed4fe0e9..ca260509a4f1 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/base917c.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/base917c.c
@@ -22,7 +22,7 @@
#include "base.h"
#include "atom.h"
-const u32
+static const u32
base917c_format[] = {
DRM_FORMAT_C8,
DRM_FORMAT_XRGB8888,
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.c b/drivers/gpu/drm/nouveau/dispnv50/wndw.c
index e2faf92e4831..8642b84ea20c 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/wndw.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.c
@@ -558,7 +558,8 @@ nv50_wndw_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state)
asyw->image.handle[0] = ctxdma->object.handle;
}
- ret = dma_resv_get_singleton(nvbo->bo.base.resv, false,
+ ret = dma_resv_get_singleton(nvbo->bo.base.resv,
+ DMA_RESV_USAGE_WRITE,
&asyw->state.fence);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index 74f8652d2bd3..05076e530e7d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -962,11 +962,11 @@ nouveau_bo_vm_cleanup(struct ttm_buffer_object *bo,
struct dma_fence *fence;
int ret;
- /* TODO: This is actually a memory management dependency */
- ret = dma_resv_get_singleton(bo->base.resv, false, &fence);
+ ret = dma_resv_get_singleton(bo->base.resv, DMA_RESV_USAGE_WRITE,
+ &fence);
if (ret)
- dma_resv_wait_timeout(bo->base.resv, false, false,
- MAX_SCHEDULE_TIMEOUT);
+ dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_WRITE,
+ false, MAX_SCHEDULE_TIMEOUT);
nv10_bo_put_tile_region(dev, *old_tile, fence);
*old_tile = new_tile;
@@ -1308,10 +1308,11 @@ nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence, bool excl
{
struct dma_resv *resv = nvbo->bo.base.resv;
- if (exclusive)
- dma_resv_add_excl_fence(resv, &fence->base);
- else if (fence)
- dma_resv_add_shared_fence(resv, &fence->base);
+ if (!fence)
+ return;
+
+ dma_resv_add_fence(resv, &fence->base, exclusive ?
+ DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ);
}
static void
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
index 0268259e97eb..7f01dcf81fab 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -350,17 +350,21 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan,
if (ret)
return ret;
- /* Waiting for the exclusive fence first causes performance regressions
- * under some circumstances. So manually wait for the shared ones first.
+ /* Waiting for the writes first causes performance regressions
+ * under some circumstances. So manually wait for the reads first.
*/
for (i = 0; i < 2; ++i) {
struct dma_resv_iter cursor;
struct dma_fence *fence;
- dma_resv_for_each_fence(&cursor, resv, exclusive, fence) {
+ dma_resv_for_each_fence(&cursor, resv,
+ dma_resv_usage_rw(exclusive),
+ fence) {
+ enum dma_resv_usage usage;
struct nouveau_fence *f;
- if (i == 0 && dma_resv_iter_is_exclusive(&cursor))
+ usage = dma_resv_iter_usage(&cursor);
+ if (i == 0 && usage == DMA_RESV_USAGE_WRITE)
continue;
f = nouveau_local_fence(fence, chan->drm);
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index 9416bee92141..fab542a758ff 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -962,7 +962,8 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,
return -ENOENT;
nvbo = nouveau_gem_object(gem);
- lret = dma_resv_wait_timeout(nvbo->bo.base.resv, write, true,
+ lret = dma_resv_wait_timeout(nvbo->bo.base.resv,
+ dma_resv_usage_rw(write), true,
no_wait ? 0 : 30 * HZ);
if (!lret)
ret = -EBUSY;
diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c
index 60019d0532fc..347488685f74 100644
--- a/drivers/gpu/drm/nouveau/nouveau_prime.c
+++ b/drivers/gpu/drm/nouveau/nouveau_prime.c
@@ -93,22 +93,7 @@ int nouveau_gem_prime_pin(struct drm_gem_object *obj)
if (ret)
return -EINVAL;
- ret = ttm_bo_reserve(&nvbo->bo, false, false, NULL);
- if (ret)
- goto error;
-
- if (nvbo->bo.moving)
- ret = dma_fence_wait(nvbo->bo.moving, true);
-
- ttm_bo_unreserve(&nvbo->bo);
- if (ret)
- goto error;
-
- return ret;
-
-error:
- nouveau_bo_unpin(nvbo);
- return ret;
+ return 0;
}
void nouveau_gem_prime_unpin(struct drm_gem_object *obj)
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c
index 030640bb3dca..ab3760e804b8 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf108.c
@@ -143,7 +143,7 @@ gf108_gr = {
}
};
-const struct gf100_gr_fwif
+static const struct gf100_gr_fwif
gf108_gr_fwif[] = {
{ -1, gf100_gr_load, &gf108_gr },
{ -1, gf100_gr_nofw, &gf108_gr },
diff --git a/drivers/gpu/drm/panel/panel-lvds.c b/drivers/gpu/drm/panel/panel-lvds.c
index 27a1c9923b09..eca067e78579 100644
--- a/drivers/gpu/drm/panel/panel-lvds.c
+++ b/drivers/gpu/drm/panel/panel-lvds.c
@@ -30,9 +30,9 @@ struct panel_lvds {
const char *label;
unsigned int width;
unsigned int height;
- struct videomode video_mode;
+ struct drm_display_mode dmode;
+ u32 bus_flags;
unsigned int bus_format;
- bool data_mirror;
struct regulator *supply;
@@ -87,21 +87,18 @@ static int panel_lvds_get_modes(struct drm_panel *panel,
struct panel_lvds *lvds = to_panel_lvds(panel);
struct drm_display_mode *mode;
- mode = drm_mode_create(connector->dev);
+ mode = drm_mode_duplicate(connector->dev, &lvds->dmode);
if (!mode)
return 0;
- drm_display_mode_from_videomode(&lvds->video_mode, mode);
mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
- connector->display_info.width_mm = lvds->width;
- connector->display_info.height_mm = lvds->height;
+ connector->display_info.width_mm = lvds->dmode.width_mm;
+ connector->display_info.height_mm = lvds->dmode.height_mm;
drm_display_info_set_bus_formats(&connector->display_info,
&lvds->bus_format, 1);
- connector->display_info.bus_flags = lvds->data_mirror
- ? DRM_BUS_FLAG_DATA_LSB_TO_MSB
- : DRM_BUS_FLAG_DATA_MSB_TO_LSB;
+ connector->display_info.bus_flags = lvds->bus_flags;
drm_connector_set_panel_orientation(connector, lvds->orientation);
return 1;
@@ -116,7 +113,6 @@ static const struct drm_panel_funcs panel_lvds_funcs = {
static int panel_lvds_parse_dt(struct panel_lvds *lvds)
{
struct device_node *np = lvds->dev->of_node;
- struct display_timing timing;
int ret;
ret = of_drm_get_panel_orientation(np, &lvds->orientation);
@@ -125,23 +121,20 @@ static int panel_lvds_parse_dt(struct panel_lvds *lvds)
return ret;
}
- ret = of_get_display_timing(np, "panel-timing", &timing);
+ ret = of_get_drm_panel_display_mode(np, &lvds->dmode, &lvds->bus_flags);
if (ret < 0) {
dev_err(lvds->dev, "%pOF: problems parsing panel-timing (%d)\n",
np, ret);
return ret;
}
- videomode_from_timing(&timing, &lvds->video_mode);
-
- ret = of_property_read_u32(np, "width-mm", &lvds->width);
- if (ret < 0) {
+ if (lvds->dmode.width_mm == 0) {
dev_err(lvds->dev, "%pOF: invalid or missing %s DT property\n",
np, "width-mm");
return -ENODEV;
}
- ret = of_property_read_u32(np, "height-mm", &lvds->height);
- if (ret < 0) {
+
+ if (lvds->dmode.height_mm == 0) {
dev_err(lvds->dev, "%pOF: invalid or missing %s DT property\n",
np, "height-mm");
return -ENODEV;
@@ -158,7 +151,9 @@ static int panel_lvds_parse_dt(struct panel_lvds *lvds)
lvds->bus_format = ret;
- lvds->data_mirror = of_property_read_bool(np, "data-mirror");
+ lvds->bus_flags |= of_property_read_bool(np, "data-mirror") ?
+ DRM_BUS_FLAG_DATA_LSB_TO_MSB :
+ DRM_BUS_FLAG_DATA_MSB_TO_LSB;
return 0;
}
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 94b6f0a19c83..7fcbc2a5b6cd 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -316,7 +316,8 @@ panfrost_ioctl_wait_bo(struct drm_device *dev, void *data,
if (!gem_obj)
return -ENOENT;
- ret = dma_resv_wait_timeout(gem_obj->resv, true, true, timeout);
+ ret = dma_resv_wait_timeout(gem_obj->resv, DMA_RESV_USAGE_READ,
+ true, timeout);
if (!ret)
ret = timeout ? -ETIMEDOUT : -EBUSY;
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index c34114560e49..fda5871aebe3 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -268,7 +268,7 @@ static void panfrost_attach_object_fences(struct drm_gem_object **bos,
int i;
for (i = 0; i < bo_count; i++)
- dma_resv_add_excl_fence(bos[i]->resv, fence);
+ dma_resv_add_fence(bos[i]->resv, fence, DMA_RESV_USAGE_WRITE);
}
int panfrost_job_push(struct panfrost_job *job)
diff --git a/drivers/gpu/drm/qxl/qxl_debugfs.c b/drivers/gpu/drm/qxl/qxl_debugfs.c
index 6a36b0fd845c..2d9ed3b94574 100644
--- a/drivers/gpu/drm/qxl/qxl_debugfs.c
+++ b/drivers/gpu/drm/qxl/qxl_debugfs.c
@@ -61,7 +61,8 @@ qxl_debugfs_buffers_info(struct seq_file *m, void *data)
struct dma_fence *fence;
int rel = 0;
- dma_resv_iter_begin(&cursor, bo->tbo.base.resv, true);
+ dma_resv_iter_begin(&cursor, bo->tbo.base.resv,
+ DMA_RESV_USAGE_BOOKKEEP);
dma_resv_for_each_fence_unlocked(&cursor, fence) {
if (dma_resv_iter_is_restarted(&cursor))
rel = 0;
diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c
index cde1e8ddaeaa..368d26da0d6a 100644
--- a/drivers/gpu/drm/qxl/qxl_release.c
+++ b/drivers/gpu/drm/qxl/qxl_release.c
@@ -429,7 +429,8 @@ void qxl_release_fence_buffer_objects(struct qxl_release *release)
list_for_each_entry(entry, &release->bos, head) {
bo = entry->bo;
- dma_resv_add_shared_fence(bo->base.resv, &release->base);
+ dma_resv_add_fence(bo->base.resv, &release->base,
+ DMA_RESV_USAGE_READ);
ttm_bo_move_to_lru_tail_unlocked(bo);
dma_resv_unlock(bo->base.resv);
}
diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c
index 9ba871bd19b1..ee95001e6b5e 100644
--- a/drivers/gpu/drm/qxl/qxl_ttm.c
+++ b/drivers/gpu/drm/qxl/qxl_ttm.c
@@ -222,41 +222,14 @@ void qxl_ttm_fini(struct qxl_device *qdev)
DRM_INFO("qxl: ttm finalized\n");
}
-#define QXL_DEBUGFS_MEM_TYPES 2
-
-#if defined(CONFIG_DEBUG_FS)
-static int qxl_mm_dump_table(struct seq_file *m, void *data)
-{
- struct drm_info_node *node = (struct drm_info_node *)m->private;
- struct ttm_resource_manager *man = (struct ttm_resource_manager *)node->info_ent->data;
- struct drm_printer p = drm_seq_file_printer(m);
-
- ttm_resource_manager_debug(man, &p);
- return 0;
-}
-#endif
-
void qxl_ttm_debugfs_init(struct qxl_device *qdev)
{
#if defined(CONFIG_DEBUG_FS)
- static struct drm_info_list qxl_mem_types_list[QXL_DEBUGFS_MEM_TYPES];
- static char qxl_mem_types_names[QXL_DEBUGFS_MEM_TYPES][32];
- unsigned int i;
-
- for (i = 0; i < QXL_DEBUGFS_MEM_TYPES; i++) {
- if (i == 0)
- sprintf(qxl_mem_types_names[i], "qxl_mem_mm");
- else
- sprintf(qxl_mem_types_names[i], "qxl_surf_mm");
- qxl_mem_types_list[i].name = qxl_mem_types_names[i];
- qxl_mem_types_list[i].show = &qxl_mm_dump_table;
- qxl_mem_types_list[i].driver_features = 0;
- if (i == 0)
- qxl_mem_types_list[i].data = ttm_manager_type(&qdev->mman.bdev, TTM_PL_VRAM);
- else
- qxl_mem_types_list[i].data = ttm_manager_type(&qdev->mman.bdev, TTM_PL_PRIV);
-
- }
- qxl_debugfs_add_files(qdev, qxl_mem_types_list, i);
+ ttm_resource_manager_create_debugfs(ttm_manager_type(&qdev->mman.bdev,
+ TTM_PL_VRAM),
+ qdev->ddev.primary->debugfs_root, "qxl_mem_mm");
+ ttm_resource_manager_create_debugfs(ttm_manager_type(&qdev->mman.bdev,
+ TTM_PL_PRIV),
+ qdev->ddev.primary->debugfs_root, "qxl_surf_mm");
#endif
}
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index f60e826cd292..57ff2b723c87 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -533,7 +533,8 @@ static int radeon_crtc_page_flip_target(struct drm_crtc *crtc,
DRM_ERROR("failed to pin new rbo buffer before flip\n");
goto cleanup;
}
- r = dma_resv_get_singleton(new_rbo->tbo.base.resv, false, &work->fence);
+ r = dma_resv_get_singleton(new_rbo->tbo.base.resv, DMA_RESV_USAGE_WRITE,
+ &work->fence);
if (r) {
radeon_bo_unreserve(new_rbo);
DRM_ERROR("failed to get new rbo buffer fences\n");
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index f563284a7fac..8c01a7f0e027 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -162,7 +162,9 @@ static int radeon_gem_set_domain(struct drm_gem_object *gobj,
}
if (domain == RADEON_GEM_DOMAIN_CPU) {
/* Asking for cpu access wait for object idle */
- r = dma_resv_wait_timeout(robj->tbo.base.resv, true, true, 30 * HZ);
+ r = dma_resv_wait_timeout(robj->tbo.base.resv,
+ DMA_RESV_USAGE_BOOKKEEP,
+ true, 30 * HZ);
if (!r)
r = -EBUSY;
@@ -524,7 +526,7 @@ int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,
}
robj = gem_to_radeon_bo(gobj);
- r = dma_resv_test_signaled(robj->tbo.base.resv, true);
+ r = dma_resv_test_signaled(robj->tbo.base.resv, DMA_RESV_USAGE_READ);
if (r == 0)
r = -EBUSY;
else
@@ -553,7 +555,8 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
}
robj = gem_to_radeon_bo(gobj);
- ret = dma_resv_wait_timeout(robj->tbo.base.resv, true, true, 30 * HZ);
+ ret = dma_resv_wait_timeout(robj->tbo.base.resv, DMA_RESV_USAGE_READ,
+ true, 30 * HZ);
if (ret == 0)
r = -EBUSY;
else if (ret < 0)
diff --git a/drivers/gpu/drm/radeon/radeon_mn.c b/drivers/gpu/drm/radeon/radeon_mn.c
index 9fa88549c89e..29fe8423bd90 100644
--- a/drivers/gpu/drm/radeon/radeon_mn.c
+++ b/drivers/gpu/drm/radeon/radeon_mn.c
@@ -66,8 +66,8 @@ static bool radeon_mn_invalidate(struct mmu_interval_notifier *mn,
return true;
}
- r = dma_resv_wait_timeout(bo->tbo.base.resv, true, false,
- MAX_SCHEDULE_TIMEOUT);
+ r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP,
+ false, MAX_SCHEDULE_TIMEOUT);
if (r <= 0)
DRM_ERROR("(%ld) failed to wait for user bo\n", r);
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index 7ffd2e90f325..6c4a6802ca96 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -219,7 +219,12 @@ int radeon_bo_create(struct radeon_device *rdev,
int radeon_bo_kmap(struct radeon_bo *bo, void **ptr)
{
bool is_iomem;
- int r;
+ long r;
+
+ r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_KERNEL,
+ false, MAX_SCHEDULE_TIMEOUT);
+ if (r < 0)
+ return r;
if (bo->kptr) {
if (ptr) {
@@ -791,8 +796,6 @@ void radeon_bo_fence(struct radeon_bo *bo, struct radeon_fence *fence,
return;
}
- if (shared)
- dma_resv_add_shared_fence(resv, &fence->base);
- else
- dma_resv_add_excl_fence(resv, &fence->base);
+ dma_resv_add_fence(resv, &fence->base, shared ?
+ DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE);
}
diff --git a/drivers/gpu/drm/radeon/radeon_prime.c b/drivers/gpu/drm/radeon/radeon_prime.c
index 4a90807351e7..42a87948e28c 100644
--- a/drivers/gpu/drm/radeon/radeon_prime.c
+++ b/drivers/gpu/drm/radeon/radeon_prime.c
@@ -77,19 +77,9 @@ int radeon_gem_prime_pin(struct drm_gem_object *obj)
/* pin buffer into GTT */
ret = radeon_bo_pin(bo, RADEON_GEM_DOMAIN_GTT, NULL);
- if (unlikely(ret))
- goto error;
-
- if (bo->tbo.moving) {
- ret = dma_fence_wait(bo->tbo.moving, false);
- if (unlikely(ret)) {
- radeon_bo_unpin(bo);
- goto error;
- }
- }
-
- bo->prime_shared_count++;
-error:
+ if (likely(ret == 0))
+ bo->prime_shared_count++;
+
radeon_bo_unreserve(bo);
return ret;
}
diff --git a/drivers/gpu/drm/radeon/radeon_sync.c b/drivers/gpu/drm/radeon/radeon_sync.c
index b991ba1bcd51..49bbb2266c0f 100644
--- a/drivers/gpu/drm/radeon/radeon_sync.c
+++ b/drivers/gpu/drm/radeon/radeon_sync.c
@@ -96,7 +96,7 @@ int radeon_sync_resv(struct radeon_device *rdev,
struct dma_fence *f;
int r = 0;
- dma_resv_for_each_fence(&cursor, resv, shared, f) {
+ dma_resv_for_each_fence(&cursor, resv, dma_resv_usage_rw(shared), f) {
fence = to_radeon_fence(f);
if (fence && fence->rdev == rdev)
radeon_sync_fence(sync, fence);
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index 44594d16611f..d33fec488713 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -781,17 +781,6 @@ void radeon_ttm_set_active_vram_size(struct radeon_device *rdev, u64 size)
#if defined(CONFIG_DEBUG_FS)
-static int radeon_mm_vram_dump_table_show(struct seq_file *m, void *unused)
-{
- struct radeon_device *rdev = (struct radeon_device *)m->private;
- struct ttm_resource_manager *man = ttm_manager_type(&rdev->mman.bdev,
- TTM_PL_VRAM);
- struct drm_printer p = drm_seq_file_printer(m);
-
- ttm_resource_manager_debug(man, &p);
- return 0;
-}
-
static int radeon_ttm_page_pool_show(struct seq_file *m, void *data)
{
struct radeon_device *rdev = (struct radeon_device *)m->private;
@@ -799,19 +788,6 @@ static int radeon_ttm_page_pool_show(struct seq_file *m, void *data)
return ttm_pool_debugfs(&rdev->mman.bdev.pool, m);
}
-static int radeon_mm_gtt_dump_table_show(struct seq_file *m, void *unused)
-{
- struct radeon_device *rdev = (struct radeon_device *)m->private;
- struct ttm_resource_manager *man = ttm_manager_type(&rdev->mman.bdev,
- TTM_PL_TT);
- struct drm_printer p = drm_seq_file_printer(m);
-
- ttm_resource_manager_debug(man, &p);
- return 0;
-}
-
-DEFINE_SHOW_ATTRIBUTE(radeon_mm_vram_dump_table);
-DEFINE_SHOW_ATTRIBUTE(radeon_mm_gtt_dump_table);
DEFINE_SHOW_ATTRIBUTE(radeon_ttm_page_pool);
static int radeon_ttm_vram_open(struct inode *inode, struct file *filep)
@@ -930,15 +906,15 @@ static void radeon_ttm_debugfs_init(struct radeon_device *rdev)
debugfs_create_file("radeon_vram", 0444, root, rdev,
&radeon_ttm_vram_fops);
-
debugfs_create_file("radeon_gtt", 0444, root, rdev,
&radeon_ttm_gtt_fops);
-
- debugfs_create_file("radeon_vram_mm", 0444, root, rdev,
- &radeon_mm_vram_dump_table_fops);
- debugfs_create_file("radeon_gtt_mm", 0444, root, rdev,
- &radeon_mm_gtt_dump_table_fops);
debugfs_create_file("ttm_page_pool", 0444, root, rdev,
&radeon_ttm_page_pool_fops);
+ ttm_resource_manager_create_debugfs(ttm_manager_type(&rdev->mman.bdev,
+ TTM_PL_VRAM),
+ root, "radeon_vram_mm");
+ ttm_resource_manager_create_debugfs(ttm_manager_type(&rdev->mman.bdev,
+ TTM_PL_TT),
+ root, "radeon_gtt_mm");
#endif
}
diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c
index bc0f44299bb9..a2cda184b2b2 100644
--- a/drivers/gpu/drm/radeon/radeon_uvd.c
+++ b/drivers/gpu/drm/radeon/radeon_uvd.c
@@ -470,24 +470,16 @@ static int radeon_uvd_cs_msg(struct radeon_cs_parser *p, struct radeon_bo *bo,
int32_t *msg, msg_type, handle;
unsigned img_size = 0;
void *ptr;
- long r;
- int i;
+ int i, r;
if (offset & 0x3F) {
DRM_ERROR("UVD messages must be 64 byte aligned!\n");
return -EINVAL;
}
- r = dma_resv_wait_timeout(bo->tbo.base.resv, false, false,
- MAX_SCHEDULE_TIMEOUT);
- if (r <= 0) {
- DRM_ERROR("Failed waiting for UVD message (%ld)!\n", r);
- return r ? r : -ETIME;
- }
-
r = radeon_bo_kmap(bo, &ptr);
if (r) {
- DRM_ERROR("Failed mapping the UVD message (%ld)!\n", r);
+ DRM_ERROR("Failed mapping the UVD message (%d)!\n", r);
return r;
}
diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c
index c5660b066554..76fd2904c7c6 100644
--- a/drivers/gpu/drm/scheduler/sched_main.c
+++ b/drivers/gpu/drm/scheduler/sched_main.c
@@ -705,7 +705,8 @@ int drm_sched_job_add_implicit_dependencies(struct drm_sched_job *job,
dma_resv_assert_held(obj->resv);
- dma_resv_for_each_fence(&cursor, obj->resv, write, fence) {
+ dma_resv_for_each_fence(&cursor, obj->resv, dma_resv_usage_rw(write),
+ fence) {
/* Make sure to grab an additional ref on the added fence */
dma_fence_get(fence);
ret = drm_sched_job_add_dependency(job, fence);
diff --git a/drivers/gpu/drm/solomon/Kconfig b/drivers/gpu/drm/solomon/Kconfig
index 6230369505c9..e170716d976b 100644
--- a/drivers/gpu/drm/solomon/Kconfig
+++ b/drivers/gpu/drm/solomon/Kconfig
@@ -5,9 +5,9 @@ config DRM_SSD130X
select DRM_GEM_SHMEM_HELPER
select DRM_KMS_HELPER
help
- DRM driver for the SSD1305, SSD1306, SSD1307 and SSD1309 Solomon
- OLED controllers. This is only for the core driver, a driver for
- the appropriate bus transport in your chip also must be selected.
+ DRM driver for the SSD130x Solomon and SINO WEALTH SH110x OLED
+ controllers. This is only for the core driver, a driver for the
+ appropriate bus transport in your chip also must be selected.
If M is selected the module will be called ssd130x.
@@ -16,6 +16,16 @@ config DRM_SSD130X_I2C
depends on DRM_SSD130X && I2C
select REGMAP_I2C
help
- Say Y here if the SSD130x OLED display is connected via I2C bus.
+ Say Y here if the SSD130x or SH110x OLED display is connected via
+ I2C bus.
If M is selected the module will be called ssd130x-i2c.
+
+config DRM_SSD130X_SPI
+ tristate "DRM support for Solomon SSD130X OLED displays (SPI bus)"
+ depends on DRM_SSD130X && SPI
+ select REGMAP
+ help
+ Say Y here if the SSD130x OLED display is connected via SPI bus.
+
+ If M is selected the module will be called ssd130x-spi.
diff --git a/drivers/gpu/drm/solomon/Makefile b/drivers/gpu/drm/solomon/Makefile
index 4bfc5acb0447..b5fc792257d7 100644
--- a/drivers/gpu/drm/solomon/Makefile
+++ b/drivers/gpu/drm/solomon/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_DRM_SSD130X) += ssd130x.o
obj-$(CONFIG_DRM_SSD130X_I2C) += ssd130x-i2c.o
+obj-$(CONFIG_DRM_SSD130X_SPI) += ssd130x-spi.o
diff --git a/drivers/gpu/drm/solomon/ssd130x-i2c.c b/drivers/gpu/drm/solomon/ssd130x-i2c.c
index 3126aeda4ced..d6835ec71c39 100644
--- a/drivers/gpu/drm/solomon/ssd130x-i2c.c
+++ b/drivers/gpu/drm/solomon/ssd130x-i2c.c
@@ -53,48 +53,43 @@ static void ssd130x_i2c_shutdown(struct i2c_client *client)
ssd130x_shutdown(ssd130x);
}
-static struct ssd130x_deviceinfo ssd130x_ssd1305_deviceinfo = {
- .default_vcomh = 0x34,
- .default_dclk_div = 1,
- .default_dclk_frq = 7,
-};
-
-static struct ssd130x_deviceinfo ssd130x_ssd1306_deviceinfo = {
- .default_vcomh = 0x20,
- .default_dclk_div = 1,
- .default_dclk_frq = 8,
- .need_chargepump = 1,
-};
-
-static struct ssd130x_deviceinfo ssd130x_ssd1307_deviceinfo = {
- .default_vcomh = 0x20,
- .default_dclk_div = 2,
- .default_dclk_frq = 12,
- .need_pwm = 1,
-};
-
-static struct ssd130x_deviceinfo ssd130x_ssd1309_deviceinfo = {
- .default_vcomh = 0x34,
- .default_dclk_div = 1,
- .default_dclk_frq = 10,
-};
-
static const struct of_device_id ssd130x_of_match[] = {
{
+ .compatible = "sinowealth,sh1106",
+ .data = &ssd130x_variants[SH1106_ID],
+ },
+ {
+ .compatible = "solomon,ssd1305",
+ .data = &ssd130x_variants[SSD1305_ID],
+ },
+ {
+ .compatible = "solomon,ssd1306",
+ .data = &ssd130x_variants[SSD1306_ID],
+ },
+ {
+ .compatible = "solomon,ssd1307",
+ .data = &ssd130x_variants[SSD1307_ID],
+ },
+ {
+ .compatible = "solomon,ssd1309",
+ .data = &ssd130x_variants[SSD1309_ID],
+ },
+ /* Deprecated but kept for backward compatibility */
+ {
.compatible = "solomon,ssd1305fb-i2c",
- .data = &ssd130x_ssd1305_deviceinfo,
+ .data = &ssd130x_variants[SSD1305_ID],
},
{
.compatible = "solomon,ssd1306fb-i2c",
- .data = &ssd130x_ssd1306_deviceinfo,
+ .data = &ssd130x_variants[SSD1306_ID],
},
{
.compatible = "solomon,ssd1307fb-i2c",
- .data = &ssd130x_ssd1307_deviceinfo,
+ .data = &ssd130x_variants[SSD1307_ID],
},
{
.compatible = "solomon,ssd1309fb-i2c",
- .data = &ssd130x_ssd1309_deviceinfo,
+ .data = &ssd130x_variants[SSD1309_ID],
},
{ /* sentinel */ }
};
@@ -114,3 +109,4 @@ module_i2c_driver(ssd130x_i2c_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Javier Martinez Canillas <javierm@redhat.com>");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(DRM_SSD130X);
diff --git a/drivers/gpu/drm/solomon/ssd130x-spi.c b/drivers/gpu/drm/solomon/ssd130x-spi.c
new file mode 100644
index 000000000000..43722adab1f8
--- /dev/null
+++ b/drivers/gpu/drm/solomon/ssd130x-spi.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * DRM driver for Solomon SSD130X OLED displays (SPI bus)
+ *
+ * Copyright 2022 Red Hat Inc.
+ * Authors: Javier Martinez Canillas <javierm@redhat.com>
+ */
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+
+#include "ssd130x.h"
+
+#define DRIVER_NAME "ssd130x-spi"
+#define DRIVER_DESC "DRM driver for Solomon SSD130X OLED displays (SPI)"
+
+struct ssd130x_spi_transport {
+ struct spi_device *spi;
+ struct gpio_desc *dc;
+};
+
+static const struct regmap_config ssd130x_spi_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+/*
+ * The regmap bus .write handler, it is just a wrapper around spi_write()
+ * but toggling the Data/Command control pin (D/C#). Since for 4-wire SPI
+ * a D/C# pin is used, in contrast with I2C where a control byte is sent,
+ * prior to every data byte, that contains a bit with the D/C# value.
+ *
+ * These control bytes are considered registers by the ssd130x core driver
+ * and can be used by the ssd130x SPI driver to determine if the data sent
+ * is for a command register or for the Graphic Display Data RAM (GDDRAM).
+ */
+static int ssd130x_spi_write(void *context, const void *data, size_t count)
+{
+ struct ssd130x_spi_transport *t = context;
+ struct spi_device *spi = t->spi;
+ const u8 *reg = data;
+
+ if (*reg == SSD130X_COMMAND)
+ gpiod_set_value_cansleep(t->dc, 0);
+
+ if (*reg == SSD130X_DATA)
+ gpiod_set_value_cansleep(t->dc, 1);
+
+ /* Remove control byte since is not used in a 4-wire SPI interface */
+ return spi_write(spi, reg + 1, count - 1);
+}
+
+/* The ssd130x driver does not read registers but regmap expects a .read */
+static int ssd130x_spi_read(void *context, const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ return -EOPNOTSUPP;
+}
+
+/*
+ * A custom bus is needed due the special write that toggles a D/C# pin,
+ * another option could be to just have a .reg_write() callback but that
+ * will prevent to do data writes in bulk.
+ *
+ * Once the regmap API is extended to support defining a bulk write handler
+ * in the struct regmap_config, this can be simplified and the bus dropped.
+ */
+static struct regmap_bus regmap_ssd130x_spi_bus = {
+ .write = ssd130x_spi_write,
+ .read = ssd130x_spi_read,
+};
+
+static int ssd130x_spi_probe(struct spi_device *spi)
+{
+ struct ssd130x_spi_transport *t;
+ struct ssd130x_device *ssd130x;
+ struct regmap *regmap;
+ struct gpio_desc *dc;
+ struct device *dev = &spi->dev;
+
+ dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW);
+ if (IS_ERR(dc))
+ return dev_err_probe(dev, PTR_ERR(dc),
+ "Failed to get dc gpio\n");
+
+ t = devm_kzalloc(dev, sizeof(*t), GFP_KERNEL);
+ if (!t)
+ return dev_err_probe(dev, -ENOMEM,
+ "Failed to allocate SPI transport data\n");
+
+ t->spi = spi;
+ t->dc = dc;
+
+ regmap = devm_regmap_init(dev, &regmap_ssd130x_spi_bus, t,
+ &ssd130x_spi_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ssd130x = ssd130x_probe(dev, regmap);
+ if (IS_ERR(ssd130x))
+ return PTR_ERR(ssd130x);
+
+ spi_set_drvdata(spi, ssd130x);
+
+ return 0;
+}
+
+static void ssd130x_spi_remove(struct spi_device *spi)
+{
+ struct ssd130x_device *ssd130x = spi_get_drvdata(spi);
+
+ ssd130x_remove(ssd130x);
+}
+
+static void ssd130x_spi_shutdown(struct spi_device *spi)
+{
+ struct ssd130x_device *ssd130x = spi_get_drvdata(spi);
+
+ ssd130x_shutdown(ssd130x);
+}
+
+static const struct of_device_id ssd130x_of_match[] = {
+ {
+ .compatible = "sinowealth,sh1106",
+ .data = &ssd130x_variants[SH1106_ID],
+ },
+ {
+ .compatible = "solomon,ssd1305",
+ .data = &ssd130x_variants[SSD1305_ID],
+ },
+ {
+ .compatible = "solomon,ssd1306",
+ .data = &ssd130x_variants[SSD1306_ID],
+ },
+ {
+ .compatible = "solomon,ssd1307",
+ .data = &ssd130x_variants[SSD1307_ID],
+ },
+ {
+ .compatible = "solomon,ssd1309",
+ .data = &ssd130x_variants[SSD1309_ID],
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ssd130x_of_match);
+
+/*
+ * The SPI core always reports a MODALIAS uevent of the form "spi:<dev>", even
+ * if the device was registered via OF. This means that the module will not be
+ * auto loaded, unless it contains an alias that matches the MODALIAS reported.
+ *
+ * To workaround this issue, add a SPI device ID table. Even when this should
+ * not be needed for this driver to match the registered SPI devices.
+ */
+static const struct spi_device_id ssd130x_spi_table[] = {
+ { "sh1106", SH1106_ID },
+ { "ssd1305", SSD1305_ID },
+ { "ssd1306", SSD1306_ID },
+ { "ssd1307", SSD1307_ID },
+ { "ssd1309", SSD1309_ID },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, ssd130x_spi_table);
+
+static struct spi_driver ssd130x_spi_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = ssd130x_of_match,
+ },
+ .probe = ssd130x_spi_probe,
+ .remove = ssd130x_spi_remove,
+ .shutdown = ssd130x_spi_shutdown,
+};
+module_spi_driver(ssd130x_spi_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Javier Martinez Canillas <javierm@redhat.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(DRM_SSD130X);
diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c
index 38b6c2c14f53..ba2de93d00f0 100644
--- a/drivers/gpu/drm/solomon/ssd130x.c
+++ b/drivers/gpu/drm/solomon/ssd130x.c
@@ -39,9 +39,8 @@
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
-#define SSD130X_DATA 0x40
-#define SSD130X_COMMAND 0x80
-
+#define SSD130X_PAGE_COL_START_LOW 0x00
+#define SSD130X_PAGE_COL_START_HIGH 0x10
#define SSD130X_SET_ADDRESS_MODE 0x20
#define SSD130X_SET_COL_RANGE 0x21
#define SSD130X_SET_PAGE_RANGE 0x22
@@ -61,6 +60,11 @@
#define SSD130X_SET_COM_PINS_CONFIG 0xda
#define SSD130X_SET_VCOMH 0xdb
+#define SSD130X_PAGE_COL_START_MASK GENMASK(3, 0)
+#define SSD130X_PAGE_COL_START_HIGH_SET(val) FIELD_PREP(SSD130X_PAGE_COL_START_MASK, (val) >> 4)
+#define SSD130X_PAGE_COL_START_LOW_SET(val) FIELD_PREP(SSD130X_PAGE_COL_START_MASK, (val))
+#define SSD130X_START_PAGE_ADDRESS_MASK GENMASK(2, 0)
+#define SSD130X_START_PAGE_ADDRESS_SET(val) FIELD_PREP(SSD130X_START_PAGE_ADDRESS_MASK, (val))
#define SSD130X_SET_SEG_REMAP_MASK GENMASK(0, 0)
#define SSD130X_SET_SEG_REMAP_SET(val) FIELD_PREP(SSD130X_SET_SEG_REMAP_MASK, (val))
#define SSD130X_SET_COM_SCAN_DIR_MASK GENMASK(3, 3)
@@ -87,6 +91,38 @@
#define MAX_CONTRAST 255
+const struct ssd130x_deviceinfo ssd130x_variants[] = {
+ [SH1106_ID] = {
+ .default_vcomh = 0x40,
+ .default_dclk_div = 1,
+ .default_dclk_frq = 5,
+ .page_mode_only = 1,
+ },
+ [SSD1305_ID] = {
+ .default_vcomh = 0x34,
+ .default_dclk_div = 1,
+ .default_dclk_frq = 7,
+ },
+ [SSD1306_ID] = {
+ .default_vcomh = 0x20,
+ .default_dclk_div = 1,
+ .default_dclk_frq = 8,
+ .need_chargepump = 1,
+ },
+ [SSD1307_ID] = {
+ .default_vcomh = 0x20,
+ .default_dclk_div = 2,
+ .default_dclk_frq = 12,
+ .need_pwm = 1,
+ },
+ [SSD1309_ID] = {
+ .default_vcomh = 0x34,
+ .default_dclk_div = 1,
+ .default_dclk_frq = 10,
+ }
+};
+EXPORT_SYMBOL_NS_GPL(ssd130x_variants, DRM_SSD130X);
+
static inline struct ssd130x_device *drm_to_ssd130x(struct drm_device *drm)
{
return container_of(drm, struct ssd130x_device, drm);
@@ -130,6 +166,7 @@ out_end:
return ret;
}
+/* Set address range for horizontal/vertical addressing modes */
static int ssd130x_set_col_range(struct ssd130x_device *ssd130x,
u8 col_start, u8 cols)
{
@@ -166,6 +203,26 @@ static int ssd130x_set_page_range(struct ssd130x_device *ssd130x,
return 0;
}
+/* Set page and column start address for page addressing mode */
+static int ssd130x_set_page_pos(struct ssd130x_device *ssd130x,
+ u8 page_start, u8 col_start)
+{
+ int ret;
+ u32 page, col_low, col_high;
+
+ page = SSD130X_START_PAGE_ADDRESS |
+ SSD130X_START_PAGE_ADDRESS_SET(page_start);
+ col_low = SSD130X_PAGE_COL_START_LOW |
+ SSD130X_PAGE_COL_START_LOW_SET(col_start);
+ col_high = SSD130X_PAGE_COL_START_HIGH |
+ SSD130X_PAGE_COL_START_HIGH_SET(col_start);
+ ret = ssd130x_write_cmd(ssd130x, 3, page, col_low, col_high);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int ssd130x_pwm_enable(struct ssd130x_device *ssd130x)
{
struct device *dev = ssd130x->dev;
@@ -342,6 +399,11 @@ static int ssd130x_init(struct ssd130x_device *ssd130x)
}
}
+ /* Switch to page addressing mode */
+ if (ssd130x->page_address_mode)
+ return ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_ADDRESS_MODE,
+ SSD130X_SET_ADDRESS_MODE_PAGE);
+
/* Switch to horizontal addressing mode */
return ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_ADDRESS_MODE,
SSD130X_SET_ADDRESS_MODE_HORIZONTAL);
@@ -396,13 +458,16 @@ static int ssd130x_update_rect(struct ssd130x_device *ssd130x, u8 *buf,
* (5) A4 B4 C4 D4 E4 F4 G4 H4
*/
- ret = ssd130x_set_col_range(ssd130x, ssd130x->col_offset + x, width);
- if (ret < 0)
- goto out_free;
+ if (!ssd130x->page_address_mode) {
+ /* Set address range for horizontal addressing mode */
+ ret = ssd130x_set_col_range(ssd130x, ssd130x->col_offset + x, width);
+ if (ret < 0)
+ goto out_free;
- ret = ssd130x_set_page_range(ssd130x, ssd130x->page_offset + y / 8, pages);
- if (ret < 0)
- goto out_free;
+ ret = ssd130x_set_page_range(ssd130x, ssd130x->page_offset + y / 8, pages);
+ if (ret < 0)
+ goto out_free;
+ }
for (i = 0; i < pages; i++) {
int m = 8;
@@ -421,9 +486,29 @@ static int ssd130x_update_rect(struct ssd130x_device *ssd130x, u8 *buf,
}
data_array[array_idx++] = data;
}
+
+ /*
+ * In page addressing mode, the start address needs to be reset,
+ * and each page then needs to be written out separately.
+ */
+ if (ssd130x->page_address_mode) {
+ ret = ssd130x_set_page_pos(ssd130x,
+ ssd130x->page_offset + i,
+ ssd130x->col_offset + x);
+ if (ret < 0)
+ goto out_free;
+
+ ret = ssd130x_write_data(ssd130x, data_array, width);
+ if (ret < 0)
+ goto out_free;
+
+ array_idx = 0;
+ }
}
- ret = ssd130x_write_data(ssd130x, data_array, width * pages);
+ /* Write out update in one go if we aren't using page addressing mode */
+ if (!ssd130x->page_address_mode)
+ ret = ssd130x_write_data(ssd130x, data_array, width * pages);
out_free:
kfree(data_array);
@@ -806,6 +891,9 @@ struct ssd130x_device *ssd130x_probe(struct device *dev, struct regmap *regmap)
ssd130x->regmap = regmap;
ssd130x->device_info = device_get_match_data(dev);
+ if (ssd130x->device_info->page_mode_only)
+ ssd130x->page_address_mode = 1;
+
ssd130x_parse_properties(ssd130x);
ret = ssd130x_get_resources(ssd130x);
diff --git a/drivers/gpu/drm/solomon/ssd130x.h b/drivers/gpu/drm/solomon/ssd130x.h
index cd21cdccb566..d14f78c2eb07 100644
--- a/drivers/gpu/drm/solomon/ssd130x.h
+++ b/drivers/gpu/drm/solomon/ssd130x.h
@@ -18,12 +18,25 @@
#include <linux/regmap.h>
+#define SSD130X_DATA 0x40
+#define SSD130X_COMMAND 0x80
+
+enum ssd130x_variants {
+ SH1106_ID,
+ SSD1305_ID,
+ SSD1306_ID,
+ SSD1307_ID,
+ SSD1309_ID,
+ NR_SSD130X_VARIANTS
+};
+
struct ssd130x_deviceinfo {
u32 default_vcomh;
u32 default_dclk_div;
u32 default_dclk_frq;
int need_pwm;
int need_chargepump;
+ bool page_mode_only;
};
struct ssd130x_device {
@@ -38,6 +51,7 @@ struct ssd130x_device {
const struct ssd130x_deviceinfo *device_info;
+ unsigned page_address_mode : 1;
unsigned area_color_enable : 1;
unsigned com_invdir : 1;
unsigned com_lrremap : 1;
@@ -69,6 +83,8 @@ struct ssd130x_device {
u8 page_end;
};
+extern const struct ssd130x_deviceinfo ssd130x_variants[];
+
struct ssd130x_device *ssd130x_probe(struct device *dev, struct regmap *regmap);
int ssd130x_remove(struct ssd130x_device *ssd130x);
void ssd130x_shutdown(struct ssd130x_device *ssd130x);
diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c
index 3db3768a3241..b58415f2e4d8 100644
--- a/drivers/gpu/drm/sti/sti_gdp.c
+++ b/drivers/gpu/drm/sti/sti_gdp.c
@@ -406,7 +406,7 @@ static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_gdp *gdp)
(hw_nvn != gdp->node_list[i].top_field_paddr))
return &gdp->node_list[i];
- /* in hazardious cases restart with the first node */
+ /* in hazardous cases restart with the first node */
DRM_ERROR("inconsistent NVN for %s: 0x%08X\n",
sti_plane_to_str(&gdp->plane), hw_nvn);
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
index f3ace11209dd..b3fbee7eac11 100644
--- a/drivers/gpu/drm/sti/sti_hdmi.c
+++ b/drivers/gpu/drm/sti/sti_hdmi.c
@@ -183,7 +183,7 @@ void hdmi_write(struct sti_hdmi *hdmi, u32 val, int offset)
writel(val, hdmi->regs + offset);
}
-/**
+/*
* HDMI interrupt handler threaded
*
* @irq: irq number
@@ -215,7 +215,7 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg)
return IRQ_HANDLED;
}
-/**
+/*
* HDMI interrupt handler
*
* @irq: irq number
@@ -237,7 +237,7 @@ static irqreturn_t hdmi_irq(int irq, void *arg)
return IRQ_WAKE_THREAD;
}
-/**
+/*
* Set hdmi active area depending on the drm display mode selected
*
* @hdmi: pointer on the hdmi internal structure
@@ -258,7 +258,7 @@ static void hdmi_active_area(struct sti_hdmi *hdmi)
hdmi_write(hdmi, ymax, HDMI_ACTIVE_VID_YMAX);
}
-/**
+/*
* Overall hdmi configuration
*
* @hdmi: pointer on the hdmi internal structure
@@ -336,7 +336,7 @@ static void hdmi_infoframe_reset(struct sti_hdmi *hdmi,
hdmi_write(hdmi, 0x0, pack_offset + i);
}
-/**
+/*
* Helper to concatenate infoframe in 32 bits word
*
* @ptr: pointer on the hdmi internal structure
@@ -353,7 +353,7 @@ static inline unsigned int hdmi_infoframe_subpack(const u8 *ptr, size_t size)
return value;
}
-/**
+/*
* Helper to write info frame
*
* @hdmi: pointer on the hdmi internal structure
@@ -423,7 +423,7 @@ static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi,
hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
}
-/**
+/*
* Prepare and configure the AVI infoframe
*
* AVI infoframe are transmitted at least once per two video field and
@@ -466,7 +466,7 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi)
return 0;
}
-/**
+/*
* Prepare and configure the AUDIO infoframe
*
* AUDIO infoframe are transmitted once per frame and
@@ -551,7 +551,7 @@ static int hdmi_vendor_infoframe_config(struct sti_hdmi *hdmi)
#define HDMI_TIMEOUT_SWRESET 100 /*milliseconds */
-/**
+/*
* Software reset of the hdmi subsystem
*
* @hdmi: pointer on the hdmi internal structure
@@ -785,7 +785,7 @@ static void sti_hdmi_disable(struct drm_bridge *bridge)
cec_notifier_set_phys_addr(hdmi->notifier, CEC_PHYS_ADDR_INVALID);
}
-/**
+/*
* sti_hdmi_audio_get_non_coherent_n() - get N parameter for non-coherent
* clocks. None-coherent clocks means that audio and TMDS clocks have not the
* same source (drifts between clocks). In this case assumption is that CTS is
@@ -892,7 +892,7 @@ static void sti_hdmi_pre_enable(struct drm_bridge *bridge)
if (clk_prepare_enable(hdmi->clk_tmds))
DRM_ERROR("Failed to prepare/enable hdmi_tmds clk\n");
if (clk_prepare_enable(hdmi->clk_phy))
- DRM_ERROR("Failed to prepare/enable hdmi_rejec_pll clk\n");
+ DRM_ERROR("Failed to prepare/enable hdmi_rejection_pll clk\n");
hdmi->enabled = true;
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
index 17cc050207f4..6bd45df8f5a7 100644
--- a/drivers/gpu/drm/stm/ltdc.c
+++ b/drivers/gpu/drm/stm/ltdc.c
@@ -869,8 +869,8 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
struct drm_device *ddev = crtc->dev;
struct drm_connector_list_iter iter;
struct drm_connector *connector = NULL;
- struct drm_encoder *encoder = NULL;
- struct drm_bridge *bridge = NULL;
+ struct drm_encoder *encoder = NULL, *en_iter;
+ struct drm_bridge *bridge = NULL, *br_iter;
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h;
u32 total_width, total_height;
@@ -880,15 +880,19 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
int ret;
/* get encoder from crtc */
- drm_for_each_encoder(encoder, ddev)
- if (encoder->crtc == crtc)
+ drm_for_each_encoder(en_iter, ddev)
+ if (en_iter->crtc == crtc) {
+ encoder = en_iter;
break;
+ }
if (encoder) {
/* get bridge from encoder */
- list_for_each_entry(bridge, &encoder->bridge_chain, chain_node)
- if (bridge->encoder == encoder)
+ list_for_each_entry(br_iter, &encoder->bridge_chain, chain_node)
+ if (br_iter->encoder == encoder) {
+ bridge = br_iter;
break;
+ }
/* Get the connector from encoder */
drm_connector_list_iter_begin(ddev, &iter);
diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
index 5e2b0175df36..2860e6bff8b7 100644
--- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
+++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
@@ -135,7 +135,7 @@ static int sun8i_hdmi_phy_config_a83t(struct dw_hdmi *hdmi,
dw_hdmi_phy_gen2_txpwron(hdmi, 0);
dw_hdmi_phy_gen2_pddq(hdmi, 1);
- dw_hdmi_phy_reset(hdmi);
+ dw_hdmi_phy_gen2_reset(hdmi);
dw_hdmi_phy_gen2_pddq(hdmi, 0);
diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
index 60b92df615aa..dae47853b728 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.c
+++ b/drivers/gpu/drm/tidss/tidss_dispc.c
@@ -2650,6 +2650,20 @@ static void dispc_init_errata(struct dispc_device *dispc)
}
}
+static void dispc_softreset(struct dispc_device *dispc)
+{
+ u32 val;
+ int ret = 0;
+
+ /* Soft reset */
+ REG_FLD_MOD(dispc, DSS_SYSCONFIG, 1, 1, 1);
+ /* Wait for reset to complete */
+ ret = readl_poll_timeout(dispc->base_common + DSS_SYSSTATUS,
+ val, val & 1, 100, 5000);
+ if (ret)
+ dev_warn(dispc->dev, "failed to reset dispc\n");
+}
+
int dispc_init(struct tidss_device *tidss)
{
struct device *dev = tidss->dev;
@@ -2709,6 +2723,10 @@ int dispc_init(struct tidss_device *tidss)
return r;
}
+ /* K2G display controller does not support soft reset */
+ if (feat->subrev != DISPC_K2G)
+ dispc_softreset(dispc);
+
for (i = 0; i < dispc->feat->num_vps; i++) {
u32 gamma_size = dispc->feat->vp_feat.color.gamma_size;
u32 *gamma_table;
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index c49996cf25d0..75d308ec173d 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -223,7 +223,7 @@ static void ttm_bo_flush_all_fences(struct ttm_buffer_object *bo)
struct dma_resv_iter cursor;
struct dma_fence *fence;
- dma_resv_iter_begin(&cursor, resv, true);
+ dma_resv_iter_begin(&cursor, resv, DMA_RESV_USAGE_BOOKKEEP);
dma_resv_for_each_fence_unlocked(&cursor, fence) {
if (!fence->ops->signaled)
dma_fence_enable_sw_signaling(fence);
@@ -252,7 +252,7 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo,
struct dma_resv *resv = &bo->base._resv;
int ret;
- if (dma_resv_test_signaled(resv, true))
+ if (dma_resv_test_signaled(resv, DMA_RESV_USAGE_BOOKKEEP))
ret = 0;
else
ret = -EBUSY;
@@ -264,7 +264,8 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo,
dma_resv_unlock(bo->base.resv);
spin_unlock(&bo->bdev->lru_lock);
- lret = dma_resv_wait_timeout(resv, true, interruptible,
+ lret = dma_resv_wait_timeout(resv, DMA_RESV_USAGE_BOOKKEEP,
+ interruptible,
30 * HZ);
if (lret < 0)
@@ -367,7 +368,8 @@ static void ttm_bo_release(struct kref *kref)
/* Last resort, if we fail to allocate memory for the
* fences block for the BO to become idle
*/
- dma_resv_wait_timeout(bo->base.resv, true, false,
+ dma_resv_wait_timeout(bo->base.resv,
+ DMA_RESV_USAGE_BOOKKEEP, false,
30 * HZ);
}
@@ -378,7 +380,7 @@ static void ttm_bo_release(struct kref *kref)
ttm_mem_io_free(bdev, bo->resource);
}
- if (!dma_resv_test_signaled(bo->base.resv, true) ||
+ if (!dma_resv_test_signaled(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP) ||
!dma_resv_trylock(bo->base.resv)) {
/* The BO is not idle, resurrect it for delayed destroy */
ttm_bo_flush_all_fences(bo);
@@ -416,7 +418,6 @@ static void ttm_bo_release(struct kref *kref)
dma_resv_unlock(bo->base.resv);
atomic_dec(&ttm_glob.bo_count);
- dma_fence_put(bo->moving);
bo->destroy(bo);
}
@@ -712,9 +713,8 @@ void ttm_bo_unpin(struct ttm_buffer_object *bo)
EXPORT_SYMBOL(ttm_bo_unpin);
/*
- * Add the last move fence to the BO and reserve a new shared slot. We only use
- * a shared slot to avoid unecessary sync and rely on the subsequent bo move to
- * either stall or use an exclusive fence respectively set bo->moving.
+ * Add the last move fence to the BO as kernel dependency and reserve a new
+ * fence slot.
*/
static int ttm_bo_add_move_fence(struct ttm_buffer_object *bo,
struct ttm_resource_manager *man,
@@ -737,17 +737,11 @@ static int ttm_bo_add_move_fence(struct ttm_buffer_object *bo,
return ret;
}
- dma_resv_add_shared_fence(bo->base.resv, fence);
+ dma_resv_add_fence(bo->base.resv, fence, DMA_RESV_USAGE_KERNEL);
ret = dma_resv_reserve_fences(bo->base.resv, 1);
- if (unlikely(ret)) {
- dma_fence_put(fence);
- return ret;
- }
-
- dma_fence_put(bo->moving);
- bo->moving = fence;
- return 0;
+ dma_fence_put(fence);
+ return ret;
}
/*
@@ -949,7 +943,6 @@ int ttm_bo_init_reserved(struct ttm_device *bdev,
bo->bdev = bdev;
bo->type = type;
bo->page_alignment = page_alignment;
- bo->moving = NULL;
bo->pin_count = 0;
bo->sg = sg;
bo->bulk_move = NULL;
@@ -1044,14 +1037,14 @@ int ttm_bo_wait(struct ttm_buffer_object *bo,
long timeout = 15 * HZ;
if (no_wait) {
- if (dma_resv_test_signaled(bo->base.resv, true))
+ if (dma_resv_test_signaled(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP))
return 0;
else
return -EBUSY;
}
- timeout = dma_resv_wait_timeout(bo->base.resv, true, interruptible,
- timeout);
+ timeout = dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP,
+ interruptible, timeout);
if (timeout < 0)
return timeout;
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index 1b96b91bf81b..1cbfb00c1d65 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -228,7 +228,6 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
atomic_inc(&ttm_glob.bo_count);
INIT_LIST_HEAD(&fbo->base.ddestroy);
- fbo->base.moving = NULL;
drm_vma_node_reset(&fbo->base.base.vma_node);
kref_init(&fbo->base.kref);
@@ -500,14 +499,12 @@ static int ttm_bo_move_to_ghost(struct ttm_buffer_object *bo,
* operation has completed.
*/
- dma_fence_put(bo->moving);
- bo->moving = dma_fence_get(fence);
-
ret = ttm_buffer_object_transfer(bo, &ghost_obj);
if (ret)
return ret;
- dma_resv_add_excl_fence(&ghost_obj->base._resv, fence);
+ dma_resv_add_fence(&ghost_obj->base._resv, fence,
+ DMA_RESV_USAGE_KERNEL);
/**
* If we're not moving to fixed memory, the TTM object
@@ -545,9 +542,6 @@ static void ttm_bo_move_pipeline_evict(struct ttm_buffer_object *bo,
spin_unlock(&from->move_lock);
ttm_resource_free(bo, &bo->resource);
-
- dma_fence_put(bo->moving);
- bo->moving = dma_fence_get(fence);
}
int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,
@@ -561,7 +555,7 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,
struct ttm_resource_manager *man = ttm_manager_type(bdev, new_mem->mem_type);
int ret = 0;
- dma_resv_add_excl_fence(bo->base.resv, fence);
+ dma_resv_add_fence(bo->base.resv, fence, DMA_RESV_USAGE_KERNEL);
if (!evict)
ret = ttm_bo_move_to_ghost(bo, fence, man->use_tt);
else if (!from->use_tt && pipeline)
@@ -578,6 +572,21 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,
}
EXPORT_SYMBOL(ttm_bo_move_accel_cleanup);
+void ttm_bo_move_sync_cleanup(struct ttm_buffer_object *bo,
+ struct ttm_resource *new_mem)
+{
+ struct ttm_device *bdev = bo->bdev;
+ struct ttm_resource_manager *man = ttm_manager_type(bdev, new_mem->mem_type);
+ int ret;
+
+ ret = ttm_bo_wait_free_node(bo, man->use_tt);
+ if (WARN_ON(ret))
+ return;
+
+ ttm_bo_assign_mem(bo, new_mem);
+}
+EXPORT_SYMBOL(ttm_bo_move_sync_cleanup);
+
/**
* ttm_bo_pipeline_gutting - purge the contents of a bo
* @bo: The buffer object
diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
index 08ba083a80d2..5b324f245265 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
@@ -46,17 +46,13 @@
static vm_fault_t ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo,
struct vm_fault *vmf)
{
- vm_fault_t ret = 0;
- int err = 0;
-
- if (likely(!bo->moving))
- goto out_unlock;
+ long err = 0;
/*
* Quick non-stalling check for idle.
*/
- if (dma_fence_is_signaled(bo->moving))
- goto out_clear;
+ if (dma_resv_test_signaled(bo->base.resv, DMA_RESV_USAGE_KERNEL))
+ return 0;
/*
* If possible, avoid waiting for GPU with mmap_lock
@@ -64,34 +60,30 @@ static vm_fault_t ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo,
* is the first attempt.
*/
if (fault_flag_allow_retry_first(vmf->flags)) {
- ret = VM_FAULT_RETRY;
if (vmf->flags & FAULT_FLAG_RETRY_NOWAIT)
- goto out_unlock;
+ return VM_FAULT_RETRY;
ttm_bo_get(bo);
mmap_read_unlock(vmf->vma->vm_mm);
- (void) dma_fence_wait(bo->moving, true);
+ (void)dma_resv_wait_timeout(bo->base.resv,
+ DMA_RESV_USAGE_KERNEL, true,
+ MAX_SCHEDULE_TIMEOUT);
dma_resv_unlock(bo->base.resv);
ttm_bo_put(bo);
- goto out_unlock;
+ return VM_FAULT_RETRY;
}
/*
* Ordinary wait.
*/
- err = dma_fence_wait(bo->moving, true);
- if (unlikely(err != 0)) {
- ret = (err != -ERESTARTSYS) ? VM_FAULT_SIGBUS :
+ err = dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_KERNEL, true,
+ MAX_SCHEDULE_TIMEOUT);
+ if (unlikely(err < 0)) {
+ return (err != -ERESTARTSYS) ? VM_FAULT_SIGBUS :
VM_FAULT_NOPAGE;
- goto out_unlock;
}
-out_clear:
- dma_fence_put(bo->moving);
- bo->moving = NULL;
-
-out_unlock:
- return ret;
+ return 0;
}
static unsigned long ttm_bo_io_mem_pfn(struct ttm_buffer_object *bo,
diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c
index 789c645f004e..dbee34a058df 100644
--- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c
+++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c
@@ -101,7 +101,7 @@ int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket,
continue;
}
- num_fences = min(entry->num_shared, 1u);
+ num_fences = max(entry->num_shared, 1u);
if (!ret) {
ret = dma_resv_reserve_fences(bo->base.resv,
num_fences);
@@ -154,10 +154,8 @@ void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket,
list_for_each_entry(entry, list, head) {
struct ttm_buffer_object *bo = entry->bo;
- if (entry->num_shared)
- dma_resv_add_shared_fence(bo->base.resv, fence);
- else
- dma_resv_add_excl_fence(bo->base.resv, fence);
+ dma_resv_add_fence(bo->base.resv, fence, entry->num_shared ?
+ DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE);
ttm_bo_move_to_lru_tail_unlocked(bo);
dma_resv_unlock(bo->base.resv);
}
diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c
index 7d5a438180e9..65889b3caf50 100644
--- a/drivers/gpu/drm/ttm/ttm_resource.c
+++ b/drivers/gpu/drm/ttm/ttm_resource.c
@@ -644,3 +644,37 @@ ttm_kmap_iter_linear_io_fini(struct ttm_kmap_iter_linear_io *iter_io,
ttm_mem_io_free(bdev, mem);
}
+
+#if defined(CONFIG_DEBUG_FS)
+
+static int ttm_resource_manager_show(struct seq_file *m, void *unused)
+{
+ struct ttm_resource_manager *man =
+ (struct ttm_resource_manager *)m->private;
+ struct drm_printer p = drm_seq_file_printer(m);
+ ttm_resource_manager_debug(man, &p);
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ttm_resource_manager);
+
+#endif
+
+/**
+ * ttm_resource_manager_create_debugfs - Create debugfs entry for specified
+ * resource manager.
+ * @man: The TTM resource manager for which the debugfs stats file be creates
+ * @parent: debugfs directory in which the file will reside
+ * @name: The filename to create.
+ *
+ * This function setups up a debugfs file that can be used to look
+ * at debug statistics of the specified ttm_resource_manager.
+ */
+void ttm_resource_manager_create_debugfs(struct ttm_resource_manager *man,
+ struct dentry * parent,
+ const char *name)
+{
+#if defined(CONFIG_DEBUG_FS)
+ debugfs_create_file(name, 0444, parent, man, &ttm_resource_manager_fops);
+#endif
+}
+EXPORT_SYMBOL(ttm_resource_manager_create_debugfs);
diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c
index 961812d33827..2352e9640922 100644
--- a/drivers/gpu/drm/v3d/v3d_gem.c
+++ b/drivers/gpu/drm/v3d/v3d_gem.c
@@ -550,8 +550,8 @@ v3d_attach_fences_and_unlock_reservation(struct drm_file *file_priv,
for (i = 0; i < job->bo_count; i++) {
/* XXX: Use shared fences for read-only objects. */
- dma_resv_add_excl_fence(job->bo[i]->resv,
- job->done_fence);
+ dma_resv_add_fence(job->bo[i]->resv, job->done_fence,
+ DMA_RESV_USAGE_WRITE);
}
drm_gem_unlock_reservations(job->bo, job->bo_count, acquire_ctx);
diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c
index 594bd6bb00d2..9eaf304fc20d 100644
--- a/drivers/gpu/drm/vc4/vc4_gem.c
+++ b/drivers/gpu/drm/vc4/vc4_gem.c
@@ -546,7 +546,8 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
bo = to_vc4_bo(&exec->bo[i]->base);
bo->seqno = seqno;
- dma_resv_add_shared_fence(bo->base.base.resv, exec->fence);
+ dma_resv_add_fence(bo->base.base.resv, exec->fence,
+ DMA_RESV_USAGE_READ);
}
list_for_each_entry(bo, &exec->unref_list, unref_head) {
@@ -557,7 +558,8 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
bo = to_vc4_bo(&exec->rcl_write_bo[i]->base);
bo->write_seqno = seqno;
- dma_resv_add_excl_fence(bo->base.base.resv, exec->fence);
+ dma_resv_add_fence(bo->base.base.resv, exec->fence,
+ DMA_RESV_USAGE_WRITE);
}
}
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index 9194cb52e706..2a58fc421cf6 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -611,6 +611,7 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
struct vc4_hvs *hvs = NULL;
int ret;
u32 dispctrl;
+ u32 reg;
hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL);
if (!hvs)
@@ -682,6 +683,26 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
vc4->hvs = hvs;
+ reg = HVS_READ(SCALER_DISPECTRL);
+ reg &= ~SCALER_DISPECTRL_DSP2_MUX_MASK;
+ HVS_WRITE(SCALER_DISPECTRL,
+ reg | VC4_SET_FIELD(0, SCALER_DISPECTRL_DSP2_MUX));
+
+ reg = HVS_READ(SCALER_DISPCTRL);
+ reg &= ~SCALER_DISPCTRL_DSP3_MUX_MASK;
+ HVS_WRITE(SCALER_DISPCTRL,
+ reg | VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX));
+
+ reg = HVS_READ(SCALER_DISPEOLN);
+ reg &= ~SCALER_DISPEOLN_DSP4_MUX_MASK;
+ HVS_WRITE(SCALER_DISPEOLN,
+ reg | VC4_SET_FIELD(3, SCALER_DISPEOLN_DSP4_MUX));
+
+ reg = HVS_READ(SCALER_DISPDITHER);
+ reg &= ~SCALER_DISPDITHER_DSP5_MUX_MASK;
+ HVS_WRITE(SCALER_DISPDITHER,
+ reg | VC4_SET_FIELD(3, SCALER_DISPDITHER_DSP5_MUX));
+
dispctrl = HVS_READ(SCALER_DISPCTRL);
dispctrl |= SCALER_DISPCTRL_ENABLE;
@@ -689,10 +710,6 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
SCALER_DISPCTRL_DISPEIRQ(1) |
SCALER_DISPCTRL_DISPEIRQ(2);
- /* Set DSP3 (PV1) to use HVS channel 2, which would otherwise
- * be unused.
- */
- dispctrl &= ~SCALER_DISPCTRL_DSP3_MUX_MASK;
dispctrl &= ~(SCALER_DISPCTRL_DMAEIRQ |
SCALER_DISPCTRL_SLVWREIRQ |
SCALER_DISPCTRL_SLVRDEIRQ |
@@ -706,7 +723,6 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
SCALER_DISPCTRL_DSPEISLUR(1) |
SCALER_DISPCTRL_DSPEISLUR(2) |
SCALER_DISPCTRL_SCLEIRQ);
- dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX);
HVS_WRITE(SCALER_DISPCTRL, dispctrl);
diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c
index 747a2d199eca..c169bd72e53b 100644
--- a/drivers/gpu/drm/vc4/vc4_kms.c
+++ b/drivers/gpu/drm/vc4/vc4_kms.c
@@ -283,13 +283,18 @@ static void vc5_hvs_pv_muxing_commit(struct vc4_dev *vc4,
for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ unsigned int channel = vc4_state->assigned_channel;
if (!vc4_state->update_muxing)
continue;
switch (vc4_crtc->data->hvs_output) {
case 2:
- mux = (vc4_state->assigned_channel == 2) ? 0 : 1;
+ drm_WARN_ON(&vc4->base,
+ VC4_GET_FIELD(HVS_READ(SCALER_DISPCTRL),
+ SCALER_DISPCTRL_DSP3_MUX) == channel);
+
+ mux = (channel == 2) ? 0 : 1;
reg = HVS_READ(SCALER_DISPECTRL);
HVS_WRITE(SCALER_DISPECTRL,
(reg & ~SCALER_DISPECTRL_DSP2_MUX_MASK) |
@@ -297,10 +302,10 @@ static void vc5_hvs_pv_muxing_commit(struct vc4_dev *vc4,
break;
case 3:
- if (vc4_state->assigned_channel == VC4_HVS_CHANNEL_DISABLED)
+ if (channel == VC4_HVS_CHANNEL_DISABLED)
mux = 3;
else
- mux = vc4_state->assigned_channel;
+ mux = channel;
reg = HVS_READ(SCALER_DISPCTRL);
HVS_WRITE(SCALER_DISPCTRL,
@@ -309,10 +314,10 @@ static void vc5_hvs_pv_muxing_commit(struct vc4_dev *vc4,
break;
case 4:
- if (vc4_state->assigned_channel == VC4_HVS_CHANNEL_DISABLED)
+ if (channel == VC4_HVS_CHANNEL_DISABLED)
mux = 3;
else
- mux = vc4_state->assigned_channel;
+ mux = channel;
reg = HVS_READ(SCALER_DISPEOLN);
HVS_WRITE(SCALER_DISPEOLN,
@@ -322,10 +327,10 @@ static void vc5_hvs_pv_muxing_commit(struct vc4_dev *vc4,
break;
case 5:
- if (vc4_state->assigned_channel == VC4_HVS_CHANNEL_DISABLED)
+ if (channel == VC4_HVS_CHANNEL_DISABLED)
mux = 3;
else
- mux = vc4_state->assigned_channel;
+ mux = channel;
reg = HVS_READ(SCALER_DISPDITHER);
HVS_WRITE(SCALER_DISPDITHER,
@@ -434,6 +439,9 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
* requirements.
*/
clk_set_min_rate(hvs->core_clk, new_hvs_state->core_clock_rate);
+
+ drm_dbg(dev, "Core clock actual rate: %lu Hz\n",
+ clk_get_rate(hvs->core_clk));
}
}
@@ -817,9 +825,18 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
unsigned int matching_channels;
unsigned int channel;
+ drm_dbg(dev, "%s: Trying to find a channel.\n", crtc->name);
+
/* Nothing to do here, let's skip it */
- if (old_crtc_state->enable == new_crtc_state->enable)
+ if (old_crtc_state->enable == new_crtc_state->enable) {
+ if (new_crtc_state->enable)
+ drm_dbg(dev, "%s: Already enabled, reusing channel %d.\n",
+ crtc->name, new_vc4_crtc_state->assigned_channel);
+ else
+ drm_dbg(dev, "%s: Disabled, ignoring.\n", crtc->name);
+
continue;
+ }
/* Muxing will need to be modified, mark it as such */
new_vc4_crtc_state->update_muxing = true;
@@ -827,6 +844,10 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
/* If we're disabling our CRTC, we put back our channel */
if (!new_crtc_state->enable) {
channel = old_vc4_crtc_state->assigned_channel;
+
+ drm_dbg(dev, "%s: Disabling, Freeing channel %d\n",
+ crtc->name, channel);
+
hvs_new_state->fifo_state[channel].in_use = false;
new_vc4_crtc_state->assigned_channel = VC4_HVS_CHANNEL_DISABLED;
continue;
@@ -861,6 +882,8 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
return -EINVAL;
channel = ffs(matching_channels) - 1;
+
+ drm_dbg(dev, "Assigned HVS channel %d to CRTC %s\n", channel, crtc->name);
new_vc4_crtc_state->assigned_channel = channel;
unassigned_channels &= ~BIT(channel);
hvs_new_state->fifo_state[channel].in_use = true;
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index 9809ca3e2945..82beb8c159f2 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -298,12 +298,18 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn,
if (WARN_ON(i == ARRAY_SIZE(drm_fmts)))
return;
- ctrl = TXP_GO | TXP_VSTART_AT_EOF | TXP_EI |
+ ctrl = TXP_GO | TXP_EI |
VC4_SET_FIELD(0xf, TXP_BYTE_ENABLE) |
VC4_SET_FIELD(txp_fmts[i], TXP_FORMAT);
if (fb->format->has_alpha)
ctrl |= TXP_ALPHA_ENABLE;
+ else
+ /*
+ * If TXP_ALPHA_ENABLE isn't set and TXP_ALPHA_INVERT is, the
+ * hardware will force the output padding to be 0xff.
+ */
+ ctrl |= TXP_ALPHA_INVERT;
gem = drm_fb_cma_get_gem_obj(fb, 0);
TXP_WRITE(TXP_DST_PTR, gem->paddr + fb->offsets[0]);
diff --git a/drivers/gpu/drm/vgem/vgem_fence.c b/drivers/gpu/drm/vgem/vgem_fence.c
index 2ddbebca87d9..c2a879734d40 100644
--- a/drivers/gpu/drm/vgem/vgem_fence.c
+++ b/drivers/gpu/drm/vgem/vgem_fence.c
@@ -130,6 +130,7 @@ int vgem_fence_attach_ioctl(struct drm_device *dev,
struct vgem_file *vfile = file->driver_priv;
struct dma_resv *resv;
struct drm_gem_object *obj;
+ enum dma_resv_usage usage;
struct dma_fence *fence;
int ret;
@@ -151,7 +152,8 @@ int vgem_fence_attach_ioctl(struct drm_device *dev,
/* Check for a conflicting fence */
resv = obj->resv;
- if (!dma_resv_test_signaled(resv, arg->flags & VGEM_FENCE_WRITE)) {
+ usage = dma_resv_usage_rw(arg->flags & VGEM_FENCE_WRITE);
+ if (!dma_resv_test_signaled(resv, usage)) {
ret = -EBUSY;
goto err_fence;
}
@@ -159,12 +161,9 @@ int vgem_fence_attach_ioctl(struct drm_device *dev,
/* Expose the fence via the dma-buf */
dma_resv_lock(resv, NULL);
ret = dma_resv_reserve_fences(resv, 1);
- if (!ret) {
- if (arg->flags & VGEM_FENCE_WRITE)
- dma_resv_add_excl_fence(resv, fence);
- else
- dma_resv_add_shared_fence(resv, fence);
- }
+ if (!ret)
+ dma_resv_add_fence(resv, fence, arg->flags & VGEM_FENCE_WRITE ?
+ DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ);
dma_resv_unlock(resv);
/* Record the fence in our idr for later signaling */
diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c
index 1820ca6cf673..580a78809836 100644
--- a/drivers/gpu/drm/virtio/virtgpu_gem.c
+++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
@@ -250,7 +250,8 @@ void virtio_gpu_array_add_fence(struct virtio_gpu_object_array *objs,
int i;
for (i = 0; i < objs->nents; i++)
- dma_resv_add_excl_fence(objs->objs[i]->resv, fence);
+ dma_resv_add_fence(objs->objs[i]->resv, fence,
+ DMA_RESV_USAGE_WRITE);
}
void virtio_gpu_array_put_free(struct virtio_gpu_object_array *objs)
diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
index 77743fd2c61a..f8d83358d2a0 100644
--- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c
+++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
@@ -518,9 +518,10 @@ static int virtio_gpu_wait_ioctl(struct drm_device *dev, void *data,
return -ENOENT;
if (args->flags & VIRTGPU_WAIT_NOWAIT) {
- ret = dma_resv_test_signaled(obj->resv, true);
+ ret = dma_resv_test_signaled(obj->resv, DMA_RESV_USAGE_READ);
} else {
- ret = dma_resv_wait_timeout(obj->resv, true, true, timeout);
+ ret = dma_resv_wait_timeout(obj->resv, DMA_RESV_USAGE_READ,
+ true, timeout);
}
if (ret == 0)
ret = -EBUSY;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
index fe13aa8b4a64..408ede1f967f 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
@@ -528,8 +528,8 @@ static int vmw_user_bo_synccpu_grab(struct vmw_buffer_object *vmw_bo,
if (flags & drm_vmw_synccpu_allow_cs) {
long lret;
- lret = dma_resv_wait_timeout(bo->base.resv, true, true,
- nonblock ? 0 :
+ lret = dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_READ,
+ true, nonblock ? 0 :
MAX_SCHEDULE_TIMEOUT);
if (!lret)
return -EBUSY;
@@ -758,7 +758,8 @@ void vmw_bo_fence_single(struct ttm_buffer_object *bo,
ret = dma_resv_reserve_fences(bo->base.resv, 1);
if (!ret)
- dma_resv_add_excl_fence(bo->base.resv, &fence->base);
+ dma_resv_add_fence(bo->base.resv, &fence->base,
+ DMA_RESV_USAGE_KERNEL);
else
/* Last resort fallback when we are OOM */
dma_fence_wait(&fence->base, false);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 791f9a5f3868..ffd975c14136 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -1385,6 +1385,23 @@ static void vmw_remove(struct pci_dev *pdev)
vmw_driver_unload(dev);
}
+static void vmw_debugfs_resource_managers_init(struct vmw_private *vmw)
+{
+ struct drm_minor *minor = vmw->drm.primary;
+ struct dentry *root = minor->debugfs_root;
+
+ ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, TTM_PL_SYSTEM),
+ root, "system_ttm");
+ ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, TTM_PL_VRAM),
+ root, "vram_ttm");
+ ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, VMW_PL_GMR),
+ root, "gmr_ttm");
+ ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, VMW_PL_MOB),
+ root, "mob_ttm");
+ ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, VMW_PL_SYSTEM),
+ root, "system_mob_ttm");
+}
+
static unsigned long
vmw_get_unmapped_area(struct file *file, unsigned long uaddr,
unsigned long len, unsigned long pgoff,
@@ -1632,6 +1649,7 @@ static int vmw_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto out_unload;
vmw_debugfs_gem_init(vmw);
+ vmw_debugfs_resource_managers_init(vmw);
return 0;
out_unload:
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index 626067104751..a7d62a4eb47b 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -1161,11 +1161,6 @@ int vmw_resources_clean(struct vmw_buffer_object *vbo, pgoff_t start,
*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);
-
- return dma_resv_get_singleton(bo->base.resv, false,
- &bo->moving);
}
return 0;