summaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2020-09-23 09:49:48 +1000
committerDave Airlie <airlied@redhat.com>2020-09-23 09:52:24 +1000
commit6ea6be77086f23d4b346c9946dae24593befda2e (patch)
treedc3926a543ed2b8270aa111d97ee603970560eda /drivers/gpu
parentfc88fef916e8971eefeacc62241b7408b7e7939d (diff)
parent089d83418914abd4d908db117d9a3eca7f51a68c (diff)
downloadlinux-6ea6be77086f23d4b346c9946dae24593befda2e.tar.gz
linux-6ea6be77086f23d4b346c9946dae24593befda2e.tar.bz2
linux-6ea6be77086f23d4b346c9946dae24593befda2e.zip
Merge tag 'drm-misc-next-2020-09-21' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for 5.10: UAPI Changes: Cross-subsystem Changes: - virtio: Merged a PR for patches that will affect drm/virtio Core Changes: - dev: More devm_drm convertions and removal of drm_dev_init - atomic: Split out drm_atomic_helper_calc_timestamping_constants of drm_atomic_helper_update_legacy_modeset_state - ttm: More rework Driver Changes: - i915: selftests improvements - panfrost: support for Amlogic SoC - vc4: one fix - tree-wide: conversions to devm_drm_dev_alloc, - ast: simplifications of the atomic modesetting code - panfrost: multiple fixes - vc4: multiple fixes Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maxime Ripard <maxime@cerno.tech> Link: https://patchwork.freedesktop.org/patch/msgid/20200921152956.2gxnsdgxmwhvjyut@gilmour.lan
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/Makefile2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c16
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_object.c24
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c125
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c3
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c1
-rw-r--r--drivers/gpu/drm/armada/armada_crtc.c4
-rw-r--r--drivers/gpu/drm/armada/armada_debugfs.c2
-rw-r--r--drivers/gpu/drm/armada/armada_drm.h2
-rw-r--r--drivers/gpu/drm/armada/armada_drv.c30
-rw-r--r--drivers/gpu/drm/armada/armada_fbdev.c4
-rw-r--r--drivers/gpu/drm/armada/armada_gem.c4
-rw-r--r--drivers/gpu/drm/armada/armada_overlay.c8
-rw-r--r--drivers/gpu/drm/aspeed/aspeed_gfx_drv.c15
-rw-r--r--drivers/gpu/drm/ast/ast_drv.h2
-rw-r--r--drivers/gpu/drm/ast/ast_mode.c103
-rw-r--r--drivers/gpu/drm/bridge/parade-ps8640.c68
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c155
-rw-r--r--drivers/gpu/drm/bridge/tc358775.c2
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c24
-rw-r--r--drivers/gpu/drm/drm_connector.c45
-rw-r--r--drivers/gpu/drm/drm_debugfs_crc.c4
-rw-r--r--drivers/gpu/drm/drm_dp_mst_topology.c106
-rw-r--r--drivers/gpu/drm/drm_drv.c119
-rw-r--r--drivers/gpu/drm/drm_framebuffer.c9
-rw-r--r--drivers/gpu/drm/drm_gem_shmem_helper.c2
-rw-r--r--drivers/gpu/drm/drm_gem_ttm_helper.c5
-rw-r--r--drivers/gpu/drm/drm_gem_vram_helper.c126
-rw-r--r--drivers/gpu/drm/drm_internal.h1
-rw-r--r--drivers/gpu/drm/drm_managed.c15
-rw-r--r--drivers/gpu/drm/drm_prime.c13
-rw-r--r--drivers/gpu/drm/drm_vblank.c4
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gem.c3
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c2
-rw-r--r--drivers/gpu/drm/gma500/framebuffer.c6
-rw-r--r--drivers/gpu/drm/i810/i810_dma.c6
-rw-r--r--drivers/gpu/drm/i915/display/intel_display.c6
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/huge_pages.c2
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c2
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c2
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_object.c2
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_phys.c2
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_timeline.c2
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_gem_evict.c2
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_gem_gtt.c2
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_request.c2
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_vma.c2
-rw-r--r--drivers/gpu/drm/i915/selftests/intel_memory_region.c2
-rw-r--r--drivers/gpu/drm/i915/selftests/mock_gem_device.c44
-rw-r--r--drivers/gpu/drm/i915/selftests/mock_gem_device.h2
-rw-r--r--drivers/gpu/drm/imx/Kconfig2
-rw-r--r--drivers/gpu/drm/imx/Makefile1
-rw-r--r--drivers/gpu/drm/imx/dcss/Kconfig9
-rw-r--r--drivers/gpu/drm/imx/dcss/Makefile6
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-blkctl.c70
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-crtc.c219
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-ctxld.c424
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-dev.c325
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-dev.h177
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-dpr.c562
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-drv.c138
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-dtg.c409
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-kms.c198
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-kms.h44
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-plane.c405
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-scaler.c826
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-ss.c180
-rw-r--r--drivers/gpu/drm/ingenic/ingenic-drm-drv.c114
-rw-r--r--drivers/gpu/drm/ingenic/ingenic-drm.h4
-rw-r--r--drivers/gpu/drm/ingenic/ingenic-ipu.c12
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c2
-rw-r--r--drivers/gpu/drm/msm/msm_gem_prime.c2
-rw-r--r--drivers/gpu/drm/mxsfb/mxsfb_drv.c21
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/crtc.c9
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/disp.c5
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/overlay.c4
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/disp.c6
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndw.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_abi16.c3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.c321
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.h15
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_chan.c4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dmem.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.c5
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.c34
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_prime.c13
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sgdma.c66
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_ttm.c75
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_ttm.h3
-rw-r--r--drivers/gpu/drm/nouveau/nv17_fence.c5
-rw-r--r--drivers/gpu/drm/nouveau/nv50_fence.c5
-rw-r--r--drivers/gpu/drm/nouveau/nv84_fence.c13
-rw-r--r--drivers/gpu/drm/panel/Kconfig23
-rw-r--r--drivers/gpu/drm/panel/Makefile2
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c139
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c101
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0.c154
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0.h13
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_device.h3
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_drv.c11
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gpu.c19
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gpu.h2
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_regs.h4
-rw-r--r--drivers/gpu/drm/qxl/qxl_object.c30
-rw-r--r--drivers/gpu/drm/qxl/qxl_ttm.c45
-rw-r--r--drivers/gpu/drm/radeon/radeon.h8
-rw-r--r--drivers/gpu/drm/radeon/radeon_cs.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_gem.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_mn.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.c44
-rw-r--r--drivers/gpu/drm/radeon/radeon_prime.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_ttm.c226
-rw-r--r--drivers/gpu/drm/radeon/radeon_vm.c2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_gem.c5
-rw-r--r--drivers/gpu/drm/tegra/gem.c2
-rw-r--r--drivers/gpu/drm/ttm/ttm_agp_backend.c45
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c180
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_util.c364
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_vm.c42
-rw-r--r--drivers/gpu/drm/ttm/ttm_memory.c9
-rw-r--r--drivers/gpu/drm/ttm/ttm_page_alloc.c6
-rw-r--r--drivers/gpu/drm/ttm/ttm_page_alloc_dma.c6
-rw-r--r--drivers/gpu/drm/ttm/ttm_range_manager.c7
-rw-r--r--drivers/gpu/drm/ttm/ttm_resource.c5
-rw-r--r--drivers/gpu/drm/ttm/ttm_tt.c89
-rw-r--r--drivers/gpu/drm/vboxvideo/vbox_mode.c10
-rw-r--r--drivers/gpu/drm/vc4/Makefile1
-rw-r--r--drivers/gpu/drm/vc4/vc4_crtc.c354
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.c5
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.h43
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.c1650
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.h184
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi_phy.c521
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi_regs.h442
-rw-r--r--drivers/gpu/drm/vc4/vc4_hvs.c269
-rw-r--r--drivers/gpu/drm/vc4/vc4_kms.c246
-rw-r--r--drivers/gpu/drm/vc4/vc4_plane.c224
-rw-r--r--drivers/gpu/drm/vc4/vc4_regs.h177
-rw-r--r--drivers/gpu/drm/vc4/vc4_txp.c4
-rw-r--r--drivers/gpu/drm/vgem/vgem_drv.c57
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_debugfs.c1
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_display.c11
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_drv.h2
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_kms.c10
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_vq.c53
-rw-r--r--drivers/gpu/drm/vkms/Makefile9
-rw-r--r--drivers/gpu/drm/vkms/vkms_composer.c98
-rw-r--r--drivers/gpu/drm/vkms/vkms_drv.c56
-rw-r--r--drivers/gpu/drm/vkms/vkms_drv.h10
-rw-r--r--drivers/gpu/drm/vkms/vkms_output.c4
-rw-r--r--drivers/gpu/drm/vkms/vkms_writeback.c142
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_blit.c8
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_bo.c6
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c7
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.h2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_thp.c11
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c119
-rw-r--r--drivers/gpu/drm/xen/xen_drm_front_gem.c3
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_disp.c6
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_dpsub.c27
165 files changed, 9579 insertions, 2684 deletions
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 2f31579f91d4..81569009f884 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -100,7 +100,7 @@ obj-$(CONFIG_DRM_MSM) += msm/
obj-$(CONFIG_DRM_TEGRA) += tegra/
obj-$(CONFIG_DRM_STM) += stm/
obj-$(CONFIG_DRM_STI) += sti/
-obj-$(CONFIG_DRM_IMX) += imx/
+obj-y += imx/
obj-$(CONFIG_DRM_INGENIC) += ingenic/
obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
obj-$(CONFIG_DRM_MESON) += meson/
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c
index 427a7c5ba93d..957934926b24 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c
@@ -303,7 +303,8 @@ static struct sg_table *amdgpu_dma_buf_map(struct dma_buf_attachment *attach,
switch (bo->tbo.mem.mem_type) {
case TTM_PL_TT:
- sgt = drm_prime_pages_to_sg(bo->tbo.ttm->pages,
+ sgt = drm_prime_pages_to_sg(obj->dev,
+ bo->tbo.ttm->pages,
bo->tbo.num_pages);
if (IS_ERR(sgt))
return sgt;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index a4b518211b1f..81e4cf869f50 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -1159,25 +1159,20 @@ static int amdgpu_pci_probe(struct pci_dev *pdev,
if (ret)
return ret;
- adev = kzalloc(sizeof(*adev), GFP_KERNEL);
- if (!adev)
- return -ENOMEM;
+ adev = devm_drm_dev_alloc(&pdev->dev, &kms_driver, typeof(*adev), ddev);
+ if (IS_ERR(adev))
+ return PTR_ERR(adev);
adev->dev = &pdev->dev;
adev->pdev = pdev;
ddev = adev_to_drm(adev);
- ret = drm_dev_init(ddev, &kms_driver, &pdev->dev);
- if (ret)
- goto err_free;
-
- drmm_add_final_kfree(ddev, adev);
if (!supports_atomic)
ddev->driver_features &= ~DRIVER_ATOMIC;
ret = pci_enable_device(pdev);
if (ret)
- goto err_free;
+ return ret;
ddev->pdev = pdev;
pci_set_drvdata(pdev, ddev);
@@ -1205,8 +1200,6 @@ retry_init:
err_pci:
pci_disable_device(pdev);
-err_free:
- drm_dev_put(ddev);
return ret;
}
@@ -1223,7 +1216,6 @@ amdgpu_pci_remove(struct pci_dev *pdev)
amdgpu_driver_unload_kms(dev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
- drm_dev_put(dev);
}
static void
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c
index 2b1b7c136343..f203e4a6a3f2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c
@@ -95,8 +95,6 @@ int amdgpu_gtt_mgr_init(struct amdgpu_device *adev, uint64_t gtt_size)
man->use_tt = true;
man->func = &amdgpu_gtt_mgr_func;
- man->available_caching = TTM_PL_MASK_CACHING;
- man->default_caching = TTM_PL_FLAG_CACHED;
ttm_resource_manager_init(man, gtt_size >> PAGE_SHIFT);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
index 887a4da5eefc..ac043baac05d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
@@ -136,8 +136,8 @@ void amdgpu_bo_placement_from_domain(struct amdgpu_bo *abo, u32 domain)
places[c].fpfn = 0;
places[c].lpfn = 0;
- places[c].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED |
- TTM_PL_FLAG_VRAM;
+ places[c].mem_type = TTM_PL_VRAM;
+ places[c].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED;
if (flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED)
places[c].lpfn = visible_pfn;
@@ -152,7 +152,8 @@ void amdgpu_bo_placement_from_domain(struct amdgpu_bo *abo, u32 domain)
if (domain & AMDGPU_GEM_DOMAIN_GTT) {
places[c].fpfn = 0;
places[c].lpfn = 0;
- places[c].flags = TTM_PL_FLAG_TT;
+ places[c].mem_type = TTM_PL_TT;
+ places[c].flags = 0;
if (flags & AMDGPU_GEM_CREATE_CPU_GTT_USWC)
places[c].flags |= TTM_PL_FLAG_WC |
TTM_PL_FLAG_UNCACHED;
@@ -164,7 +165,8 @@ void amdgpu_bo_placement_from_domain(struct amdgpu_bo *abo, u32 domain)
if (domain & AMDGPU_GEM_DOMAIN_CPU) {
places[c].fpfn = 0;
places[c].lpfn = 0;
- places[c].flags = TTM_PL_FLAG_SYSTEM;
+ places[c].mem_type = TTM_PL_SYSTEM;
+ places[c].flags = 0;
if (flags & AMDGPU_GEM_CREATE_CPU_GTT_USWC)
places[c].flags |= TTM_PL_FLAG_WC |
TTM_PL_FLAG_UNCACHED;
@@ -176,28 +178,32 @@ void amdgpu_bo_placement_from_domain(struct amdgpu_bo *abo, u32 domain)
if (domain & AMDGPU_GEM_DOMAIN_GDS) {
places[c].fpfn = 0;
places[c].lpfn = 0;
- places[c].flags = TTM_PL_FLAG_UNCACHED | AMDGPU_PL_FLAG_GDS;
+ places[c].mem_type = AMDGPU_PL_GDS;
+ places[c].flags = TTM_PL_FLAG_UNCACHED;
c++;
}
if (domain & AMDGPU_GEM_DOMAIN_GWS) {
places[c].fpfn = 0;
places[c].lpfn = 0;
- places[c].flags = TTM_PL_FLAG_UNCACHED | AMDGPU_PL_FLAG_GWS;
+ places[c].mem_type = AMDGPU_PL_GWS;
+ places[c].flags = TTM_PL_FLAG_UNCACHED;
c++;
}
if (domain & AMDGPU_GEM_DOMAIN_OA) {
places[c].fpfn = 0;
places[c].lpfn = 0;
- places[c].flags = TTM_PL_FLAG_UNCACHED | AMDGPU_PL_FLAG_OA;
+ places[c].mem_type = AMDGPU_PL_OA;
+ places[c].flags = TTM_PL_FLAG_UNCACHED;
c++;
}
if (!c) {
places[c].fpfn = 0;
places[c].lpfn = 0;
- places[c].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+ places[c].mem_type = TTM_PL_SYSTEM;
+ places[c].flags = TTM_PL_MASK_CACHING;
c++;
}
@@ -594,7 +600,7 @@ static int amdgpu_bo_do_create(struct amdgpu_device *adev,
amdgpu_cs_report_moved_bytes(adev, ctx.bytes_moved, 0);
if (bp->flags & AMDGPU_GEM_CREATE_VRAM_CLEARED &&
- bo->tbo.mem.placement & TTM_PL_FLAG_VRAM) {
+ bo->tbo.mem.mem_type == TTM_PL_VRAM) {
struct dma_fence *fence;
r = amdgpu_fill_buffer(bo, 0, bo->tbo.base.resv, &fence);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 4a85f8cedd77..8039d2399584 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -63,12 +63,15 @@
#define AMDGPU_TTM_VRAM_MAX_DW_READ (size_t)128
+static int amdgpu_ttm_backend_bind(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm,
+ struct ttm_resource *bo_mem);
+
static int amdgpu_ttm_init_on_chip(struct amdgpu_device *adev,
unsigned int type,
uint64_t size)
{
return ttm_range_man_init(&adev->mman.bdev, type,
- TTM_PL_FLAG_UNCACHED, TTM_PL_FLAG_UNCACHED,
false, size >> PAGE_SHIFT);
}
@@ -88,7 +91,8 @@ static void amdgpu_evict_flags(struct ttm_buffer_object *bo,
static const struct ttm_place placements = {
.fpfn = 0,
.lpfn = 0,
- .flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM
+ .mem_type = TTM_PL_SYSTEM,
+ .flags = TTM_PL_MASK_CACHING
};
/* Don't handle scatter gather BOs */
@@ -175,24 +179,6 @@ static int amdgpu_verify_access(struct ttm_buffer_object *bo, struct file *filp)
}
/**
- * amdgpu_move_null - Register memory for a buffer object
- *
- * @bo: The bo to assign the memory to
- * @new_mem: The memory to be assigned.
- *
- * Assign the memory from new_mem to the memory of the buffer object bo.
- */
-static void amdgpu_move_null(struct ttm_buffer_object *bo,
- struct ttm_resource *new_mem)
-{
- struct ttm_resource *old_mem = &bo->mem;
-
- BUG_ON(old_mem->mm_node != NULL);
- *old_mem = *new_mem;
- new_mem->mm_node = NULL;
-}
-
-/**
* amdgpu_mm_node_addr - Compute the GPU relative offset of a GTT buffer.
*
* @bo: The bo to assign the memory to.
@@ -514,9 +500,9 @@ static int amdgpu_move_blit(struct ttm_buffer_object *bo,
/* Always block for VM page tables before committing the new location */
if (bo->type == ttm_bo_type_kernel)
- r = ttm_bo_move_accel_cleanup(bo, fence, true, new_mem);
+ r = ttm_bo_move_accel_cleanup(bo, fence, true, false, new_mem);
else
- r = ttm_bo_pipeline_move(bo, fence, evict, new_mem);
+ r = ttm_bo_move_accel_cleanup(bo, fence, evict, true, new_mem);
dma_fence_put(fence);
return r;
@@ -551,7 +537,8 @@ static int amdgpu_move_vram_ram(struct ttm_buffer_object *bo, bool evict,
placement.busy_placement = &placements;
placements.fpfn = 0;
placements.lpfn = 0;
- placements.flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
+ placements.mem_type = TTM_PL_TT;
+ placements.flags = TTM_PL_MASK_CACHING;
r = ttm_bo_mem_space(bo, &placement, &tmp_mem, ctx);
if (unlikely(r)) {
pr_err("Failed to find GTT space for blit from VRAM\n");
@@ -564,8 +551,12 @@ static int amdgpu_move_vram_ram(struct ttm_buffer_object *bo, bool evict,
goto out_cleanup;
}
+ r = ttm_tt_populate(bo->bdev, bo->ttm, ctx);
+ if (unlikely(r))
+ goto out_cleanup;
+
/* Bind the memory to the GTT space */
- r = ttm_tt_bind(bo->ttm, &tmp_mem, ctx);
+ r = amdgpu_ttm_backend_bind(bo->bdev, bo->ttm, &tmp_mem);
if (unlikely(r)) {
goto out_cleanup;
}
@@ -607,7 +598,8 @@ static int amdgpu_move_ram_vram(struct ttm_buffer_object *bo, bool evict,
placement.busy_placement = &placements;
placements.fpfn = 0;
placements.lpfn = 0;
- placements.flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
+ placements.mem_type = TTM_PL_TT;
+ placements.flags = TTM_PL_MASK_CACHING;
r = ttm_bo_mem_space(bo, &placement, &tmp_mem, ctx);
if (unlikely(r)) {
pr_err("Failed to find GTT space for blit to VRAM\n");
@@ -676,7 +668,7 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict,
adev = amdgpu_ttm_adev(bo->bdev);
if (old_mem->mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) {
- amdgpu_move_null(bo, new_mem);
+ ttm_bo_move_null(bo, new_mem);
return 0;
}
if ((old_mem->mem_type == TTM_PL_TT &&
@@ -684,7 +676,7 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict,
(old_mem->mem_type == TTM_PL_SYSTEM &&
new_mem->mem_type == TTM_PL_TT)) {
/* bind is enough */
- amdgpu_move_null(bo, new_mem);
+ ttm_bo_move_null(bo, new_mem);
return 0;
}
if (old_mem->mem_type == AMDGPU_PL_GDS ||
@@ -694,7 +686,7 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict,
new_mem->mem_type == AMDGPU_PL_GWS ||
new_mem->mem_type == AMDGPU_PL_OA) {
/* Nothing to save here */
- amdgpu_move_null(bo, new_mem);
+ ttm_bo_move_null(bo, new_mem);
return 0;
}
@@ -773,7 +765,7 @@ static int amdgpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_reso
mem->bus.addr = (u8 *)adev->mman.aper_base_kaddr +
mem->bus.offset;
- mem->bus.base = adev->gmc.aper_base;
+ mem->bus.offset += adev->gmc.aper_base;
mem->bus.is_iomem = true;
break;
default:
@@ -785,12 +777,13 @@ static int amdgpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_reso
static unsigned long amdgpu_ttm_io_mem_pfn(struct ttm_buffer_object *bo,
unsigned long page_offset)
{
+ struct amdgpu_device *adev = amdgpu_ttm_adev(bo->bdev);
uint64_t offset = (page_offset << PAGE_SHIFT);
struct drm_mm_node *mm;
mm = amdgpu_find_mm_node(&bo->mem, &offset);
- return (bo->mem.bus.base >> PAGE_SHIFT) + mm->start +
- (offset >> PAGE_SHIFT);
+ offset += adev->gmc.aper_base;
+ return mm->start + (offset >> PAGE_SHIFT);
}
/**
@@ -824,6 +817,7 @@ struct amdgpu_ttm_tt {
uint64_t userptr;
struct task_struct *usertask;
uint32_t userflags;
+ bool bound;
#if IS_ENABLED(CONFIG_DRM_AMDGPU_USERPTR)
struct hmm_range *range;
#endif
@@ -991,9 +985,10 @@ void amdgpu_ttm_tt_set_user_pages(struct ttm_tt *ttm, struct page **pages)
*
* Called by amdgpu_ttm_backend_bind()
**/
-static int amdgpu_ttm_tt_pin_userptr(struct ttm_tt *ttm)
+static int amdgpu_ttm_tt_pin_userptr(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm)
{
- struct amdgpu_device *adev = amdgpu_ttm_adev(ttm->bdev);
+ struct amdgpu_device *adev = amdgpu_ttm_adev(bdev);
struct amdgpu_ttm_tt *gtt = (void *)ttm;
int r;
@@ -1028,9 +1023,10 @@ release_sg:
/**
* amdgpu_ttm_tt_unpin_userptr - Unpin and unmap userptr pages
*/
-static void amdgpu_ttm_tt_unpin_userptr(struct ttm_tt *ttm)
+static void amdgpu_ttm_tt_unpin_userptr(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm)
{
- struct amdgpu_device *adev = amdgpu_ttm_adev(ttm->bdev);
+ struct amdgpu_device *adev = amdgpu_ttm_adev(bdev);
struct amdgpu_ttm_tt *gtt = (void *)ttm;
int write = !(gtt->userflags & AMDGPU_GEM_USERPTR_READONLY);
@@ -1111,16 +1107,23 @@ gart_bind_fail:
* Called by ttm_tt_bind() on behalf of ttm_bo_handle_move_mem().
* This handles binding GTT memory to the device address space.
*/
-static int amdgpu_ttm_backend_bind(struct ttm_tt *ttm,
+static int amdgpu_ttm_backend_bind(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm,
struct ttm_resource *bo_mem)
{
- struct amdgpu_device *adev = amdgpu_ttm_adev(ttm->bdev);
+ struct amdgpu_device *adev = amdgpu_ttm_adev(bdev);
struct amdgpu_ttm_tt *gtt = (void*)ttm;
uint64_t flags;
int r = 0;
+ if (!bo_mem)
+ return -EINVAL;
+
+ if (gtt->bound)
+ return 0;
+
if (gtt->userptr) {
- r = amdgpu_ttm_tt_pin_userptr(ttm);
+ r = amdgpu_ttm_tt_pin_userptr(bdev, ttm);
if (r) {
DRM_ERROR("failed to pin userptr\n");
return r;
@@ -1152,6 +1155,7 @@ static int amdgpu_ttm_backend_bind(struct ttm_tt *ttm,
if (r)
DRM_ERROR("failed to bind %lu pages at 0x%08llX\n",
ttm->num_pages, gtt->offset);
+ gtt->bound = true;
return r;
}
@@ -1191,8 +1195,8 @@ int amdgpu_ttm_alloc_gart(struct ttm_buffer_object *bo)
placement.busy_placement = &placements;
placements.fpfn = 0;
placements.lpfn = adev->gmc.gart_size >> PAGE_SHIFT;
- placements.flags = (bo->mem.placement & ~TTM_PL_MASK_MEM) |
- TTM_PL_FLAG_TT;
+ placements.mem_type = TTM_PL_TT;
+ placements.flags = bo->mem.placement;
r = ttm_bo_mem_space(bo, &placement, &tmp, &ctx);
if (unlikely(r))
@@ -1243,15 +1247,19 @@ int amdgpu_ttm_recover_gart(struct ttm_buffer_object *tbo)
* Called by ttm_tt_unbind() on behalf of ttm_bo_move_ttm() and
* ttm_tt_destroy().
*/
-static void amdgpu_ttm_backend_unbind(struct ttm_tt *ttm)
+static void amdgpu_ttm_backend_unbind(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm)
{
- struct amdgpu_device *adev = amdgpu_ttm_adev(ttm->bdev);
+ struct amdgpu_device *adev = amdgpu_ttm_adev(bdev);
struct amdgpu_ttm_tt *gtt = (void *)ttm;
int r;
+ if (!gtt->bound)
+ return;
+
/* if the pages have userptr pinning then clear that first */
if (gtt->userptr)
- amdgpu_ttm_tt_unpin_userptr(ttm);
+ amdgpu_ttm_tt_unpin_userptr(bdev, ttm);
if (gtt->offset == AMDGPU_BO_INVALID_OFFSET)
return;
@@ -1261,12 +1269,16 @@ static void amdgpu_ttm_backend_unbind(struct ttm_tt *ttm)
if (r)
DRM_ERROR("failed to unbind %lu pages at 0x%08llX\n",
gtt->ttm.ttm.num_pages, gtt->offset);
+ gtt->bound = false;
}
-static void amdgpu_ttm_backend_destroy(struct ttm_tt *ttm)
+static void amdgpu_ttm_backend_destroy(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm)
{
struct amdgpu_ttm_tt *gtt = (void *)ttm;
+ amdgpu_ttm_backend_unbind(bdev, ttm);
+ ttm_tt_destroy_common(bdev, ttm);
if (gtt->usertask)
put_task_struct(gtt->usertask);
@@ -1274,12 +1286,6 @@ static void amdgpu_ttm_backend_destroy(struct ttm_tt *ttm)
kfree(gtt);
}
-static struct ttm_backend_func amdgpu_backend_func = {
- .bind = &amdgpu_ttm_backend_bind,
- .unbind = &amdgpu_ttm_backend_unbind,
- .destroy = &amdgpu_ttm_backend_destroy,
-};
-
/**
* amdgpu_ttm_tt_create - Create a ttm_tt object for a given BO
*
@@ -1296,7 +1302,6 @@ static struct ttm_tt *amdgpu_ttm_tt_create(struct ttm_buffer_object *bo,
if (gtt == NULL) {
return NULL;
}
- gtt->ttm.ttm.func = &amdgpu_backend_func;
gtt->gobj = &bo->base;
/* allocate space for the uninitialized page entries */
@@ -1313,10 +1318,11 @@ static struct ttm_tt *amdgpu_ttm_tt_create(struct ttm_buffer_object *bo,
* Map the pages of a ttm_tt object to an address space visible
* to the underlying device.
*/
-static int amdgpu_ttm_tt_populate(struct ttm_tt *ttm,
- struct ttm_operation_ctx *ctx)
+static int amdgpu_ttm_tt_populate(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm,
+ struct ttm_operation_ctx *ctx)
{
- struct amdgpu_device *adev = amdgpu_ttm_adev(ttm->bdev);
+ struct amdgpu_device *adev = amdgpu_ttm_adev(bdev);
struct amdgpu_ttm_tt *gtt = (void *)ttm;
/* user pages are bound by amdgpu_ttm_tt_pin_userptr() */
@@ -1326,7 +1332,7 @@ static int amdgpu_ttm_tt_populate(struct ttm_tt *ttm,
return -ENOMEM;
ttm->page_flags |= TTM_PAGE_FLAG_SG;
- ttm->state = tt_unbound;
+ ttm_tt_set_populated(ttm);
return 0;
}
@@ -1346,7 +1352,7 @@ static int amdgpu_ttm_tt_populate(struct ttm_tt *ttm,
drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages,
gtt->ttm.dma_address,
ttm->num_pages);
- ttm->state = tt_unbound;
+ ttm_tt_set_populated(ttm);
return 0;
}
@@ -1367,7 +1373,7 @@ static int amdgpu_ttm_tt_populate(struct ttm_tt *ttm,
* Unmaps pages of a ttm_tt object from the device address space and
* unpopulates the page array backing it.
*/
-static void amdgpu_ttm_tt_unpopulate(struct ttm_tt *ttm)
+static void amdgpu_ttm_tt_unpopulate(struct ttm_bo_device *bdev, struct ttm_tt *ttm)
{
struct amdgpu_ttm_tt *gtt = (void *)ttm;
struct amdgpu_device *adev;
@@ -1391,7 +1397,7 @@ static void amdgpu_ttm_tt_unpopulate(struct ttm_tt *ttm)
if (ttm->page_flags & TTM_PAGE_FLAG_SG)
return;
- adev = amdgpu_ttm_adev(ttm->bdev);
+ adev = amdgpu_ttm_adev(bdev);
#ifdef CONFIG_SWIOTLB
if (adev->need_swiotlb && swiotlb_nr_tbl()) {
@@ -1697,6 +1703,9 @@ static struct ttm_bo_driver amdgpu_bo_driver = {
.ttm_tt_create = &amdgpu_ttm_tt_create,
.ttm_tt_populate = &amdgpu_ttm_tt_populate,
.ttm_tt_unpopulate = &amdgpu_ttm_tt_unpopulate,
+ .ttm_tt_bind = &amdgpu_ttm_backend_bind,
+ .ttm_tt_unbind = &amdgpu_ttm_backend_unbind,
+ .ttm_tt_destroy = &amdgpu_ttm_backend_destroy,
.eviction_valuable = amdgpu_ttm_bo_eviction_valuable,
.evict_flags = &amdgpu_evict_flags,
.move = &amdgpu_bo_move,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
index 68c5e3662b87..a87951b2f06d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
@@ -32,10 +32,6 @@
#define AMDGPU_PL_GWS (TTM_PL_PRIV + 1)
#define AMDGPU_PL_OA (TTM_PL_PRIV + 2)
-#define AMDGPU_PL_FLAG_GDS (TTM_PL_FLAG_PRIV << 0)
-#define AMDGPU_PL_FLAG_GWS (TTM_PL_FLAG_PRIV << 1)
-#define AMDGPU_PL_FLAG_OA (TTM_PL_FLAG_PRIV << 2)
-
#define AMDGPU_GTT_MAX_TRANSFER_SIZE 512
#define AMDGPU_GTT_NUM_TRANSFER_WINDOWS 2
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
index b2adc2abc581..01c1171afbe0 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
@@ -179,9 +179,6 @@ int amdgpu_vram_mgr_init(struct amdgpu_device *adev)
struct ttm_resource_manager *man = &mgr->manager;
int ret;
- man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC;
- man->default_caching = TTM_PL_FLAG_WC;
-
ttm_resource_manager_init(man, adev->gmc.real_vram_size >> PAGE_SHIFT);
man->func = &amdgpu_vram_mgr_func;
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 57738164625b..bb1bc7f5d149 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -7494,6 +7494,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
bool mode_set_reset_required = false;
drm_atomic_helper_update_legacy_modeset_state(dev, state);
+ drm_atomic_helper_calc_timestamping_constants(state);
dm_state = dm_atomic_get_new_state(state);
if (dm_state && dm_state->context) {
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 38dfaa46d306..a887b6a5f8bd 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -757,7 +757,7 @@ static int armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
static void armada_drm_crtc_destroy(struct drm_crtc *crtc)
{
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
- struct armada_private *priv = crtc->dev->dev_private;
+ struct armada_private *priv = drm_to_armada_dev(crtc->dev);
if (dcrtc->cursor_obj)
drm_gem_object_put(&dcrtc->cursor_obj->obj);
@@ -901,7 +901,7 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
struct resource *res, int irq, const struct armada_variant *variant,
struct device_node *port)
{
- struct armada_private *priv = drm->dev_private;
+ struct armada_private *priv = drm_to_armada_dev(drm);
struct armada_crtc *dcrtc;
struct drm_plane *primary;
void __iomem *base;
diff --git a/drivers/gpu/drm/armada/armada_debugfs.c b/drivers/gpu/drm/armada/armada_debugfs.c
index c6fc2f1d58e9..29f4b52e3c8d 100644
--- a/drivers/gpu/drm/armada/armada_debugfs.c
+++ b/drivers/gpu/drm/armada/armada_debugfs.c
@@ -19,7 +19,7 @@ static int armada_debugfs_gem_linear_show(struct seq_file *m, void *data)
{
struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
- struct armada_private *priv = dev->dev_private;
+ struct armada_private *priv = drm_to_armada_dev(dev);
struct drm_printer p = drm_seq_file_printer(m);
mutex_lock(&priv->linear_lock);
diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h
index a11bdaccbb33..6a5a87932576 100644
--- a/drivers/gpu/drm/armada/armada_drm.h
+++ b/drivers/gpu/drm/armada/armada_drm.h
@@ -73,6 +73,8 @@ struct armada_private {
#endif
};
+#define drm_to_armada_dev(dev) container_of(dev, struct armada_private, drm)
+
int armada_fbdev_init(struct drm_device *);
void armada_fbdev_fini(struct drm_device *);
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
index 5fc25c3f445c..980d3f1f8f16 100644
--- a/drivers/gpu/drm/armada/armada_drv.c
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -87,24 +87,13 @@ static int armada_drm_bind(struct device *dev)
"armada-drm"))
return -EBUSY;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- /*
- * The drm_device structure must be at the start of
- * armada_private for drm_dev_put() to work correctly.
- */
- BUILD_BUG_ON(offsetof(struct armada_private, drm) != 0);
-
- ret = drm_dev_init(&priv->drm, &armada_drm_driver, dev);
- if (ret) {
- dev_err(dev, "[" DRM_NAME ":%s] drm_dev_init failed: %d\n",
- __func__, ret);
- kfree(priv);
- return ret;
+ priv = devm_drm_dev_alloc(dev, &armada_drm_driver,
+ struct armada_private, drm);
+ if (IS_ERR(priv)) {
+ dev_err(dev, "[" DRM_NAME ":%s] devm_drm_dev_alloc failed: %li\n",
+ __func__, PTR_ERR(priv));
+ return PTR_ERR(priv);
}
- drmm_add_final_kfree(&priv->drm, priv);
/* Remove early framebuffers */
ret = drm_fb_helper_remove_conflicting_framebuffers(NULL,
@@ -117,8 +106,6 @@ static int armada_drm_bind(struct device *dev)
return ret;
}
- priv->drm.dev_private = priv;
-
dev_set_drvdata(dev, &priv->drm);
/* Mode setting support */
@@ -174,14 +161,13 @@ static int armada_drm_bind(struct device *dev)
err_kms:
drm_mode_config_cleanup(&priv->drm);
drm_mm_takedown(&priv->linear);
- drm_dev_put(&priv->drm);
return ret;
}
static void armada_drm_unbind(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
- struct armada_private *priv = drm->dev_private;
+ struct armada_private *priv = drm_to_armada_dev(drm);
drm_kms_helper_poll_fini(&priv->drm);
armada_fbdev_fini(&priv->drm);
@@ -194,8 +180,6 @@ static void armada_drm_unbind(struct device *dev)
drm_mode_config_cleanup(&priv->drm);
drm_mm_takedown(&priv->linear);
-
- drm_dev_put(&priv->drm);
}
static int compare_of(struct device *dev, void *data)
diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
index 0c4601275507..38f5170c0fea 100644
--- a/drivers/gpu/drm/armada/armada_fbdev.c
+++ b/drivers/gpu/drm/armada/armada_fbdev.c
@@ -117,7 +117,7 @@ static const struct drm_fb_helper_funcs armada_fb_helper_funcs = {
int armada_fbdev_init(struct drm_device *dev)
{
- struct armada_private *priv = dev->dev_private;
+ struct armada_private *priv = drm_to_armada_dev(dev);
struct drm_fb_helper *fbh;
int ret;
@@ -151,7 +151,7 @@ int armada_fbdev_init(struct drm_device *dev)
void armada_fbdev_fini(struct drm_device *dev)
{
- struct armada_private *priv = dev->dev_private;
+ struct armada_private *priv = drm_to_armada_dev(dev);
struct drm_fb_helper *fbh = priv->fbdev;
if (fbh) {
diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c
index a63008ce284d..6654bccd9466 100644
--- a/drivers/gpu/drm/armada/armada_gem.c
+++ b/drivers/gpu/drm/armada/armada_gem.c
@@ -39,7 +39,7 @@ static size_t roundup_gem_size(size_t size)
void armada_gem_free_object(struct drm_gem_object *obj)
{
struct armada_gem_object *dobj = drm_to_armada_gem(obj);
- struct armada_private *priv = obj->dev->dev_private;
+ struct armada_private *priv = drm_to_armada_dev(obj->dev);
DRM_DEBUG_DRIVER("release obj %p\n", dobj);
@@ -77,7 +77,7 @@ void armada_gem_free_object(struct drm_gem_object *obj)
int
armada_gem_linear_back(struct drm_device *dev, struct armada_gem_object *obj)
{
- struct armada_private *priv = dev->dev_private;
+ struct armada_private *priv = drm_to_armada_dev(dev);
size_t size = obj->obj.size;
if (obj->page || obj->linear)
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index 07f0da4d9ba1..30e01101f59e 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -344,7 +344,7 @@ static int armada_overlay_set_property(struct drm_plane *plane,
struct drm_plane_state *state, struct drm_property *property,
uint64_t val)
{
- struct armada_private *priv = plane->dev->dev_private;
+ struct armada_private *priv = drm_to_armada_dev(plane->dev);
#define K2R(val) (((val) >> 0) & 0xff)
#define K2G(val) (((val) >> 8) & 0xff)
@@ -412,7 +412,7 @@ static int armada_overlay_get_property(struct drm_plane *plane,
const struct drm_plane_state *state, struct drm_property *property,
uint64_t *val)
{
- struct armada_private *priv = plane->dev->dev_private;
+ struct armada_private *priv = drm_to_armada_dev(plane->dev);
#define C2K(c,s) (((c) >> (s)) & 0xff)
#define R2BGR(r,g,b,s) (C2K(r,s) << 0 | C2K(g,s) << 8 | C2K(b,s) << 16)
@@ -505,7 +505,7 @@ static const struct drm_prop_enum_list armada_drm_colorkey_enum_list[] = {
static int armada_overlay_create_properties(struct drm_device *dev)
{
- struct armada_private *priv = dev->dev_private;
+ struct armada_private *priv = drm_to_armada_dev(dev);
if (priv->colorkey_prop)
return 0;
@@ -539,7 +539,7 @@ static int armada_overlay_create_properties(struct drm_device *dev)
int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
{
- struct armada_private *priv = dev->dev_private;
+ struct armada_private *priv = drm_to_armada_dev(dev);
struct drm_mode_object *mobj;
struct drm_plane *overlay;
int ret;
diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
index 903f4f304647..2b424b2b85cc 100644
--- a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
+++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
@@ -63,15 +63,21 @@ static const struct drm_mode_config_funcs aspeed_gfx_mode_config_funcs = {
.atomic_commit = drm_atomic_helper_commit,
};
-static void aspeed_gfx_setup_mode_config(struct drm_device *drm)
+static int aspeed_gfx_setup_mode_config(struct drm_device *drm)
{
- drm_mode_config_init(drm);
+ int ret;
+
+ ret = drmm_mode_config_init(drm);
+ if (ret)
+ return ret;
drm->mode_config.min_width = 0;
drm->mode_config.min_height = 0;
drm->mode_config.max_width = 800;
drm->mode_config.max_height = 600;
drm->mode_config.funcs = &aspeed_gfx_mode_config_funcs;
+
+ return ret;
}
static irqreturn_t aspeed_gfx_irq_handler(int irq, void *data)
@@ -144,7 +150,9 @@ static int aspeed_gfx_load(struct drm_device *drm)
writel(0, priv->base + CRT_CTRL1);
writel(0, priv->base + CRT_CTRL2);
- aspeed_gfx_setup_mode_config(drm);
+ ret = aspeed_gfx_setup_mode_config(drm);
+ if (ret < 0)
+ return ret;
ret = drm_vblank_init(drm, 1);
if (ret < 0) {
@@ -179,7 +187,6 @@ static int aspeed_gfx_load(struct drm_device *drm)
static void aspeed_gfx_unload(struct drm_device *drm)
{
drm_kms_helper_poll_fini(drm);
- drm_mode_config_cleanup(drm);
}
DEFINE_DRM_GEM_CMA_FOPS(fops);
diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h
index c1af6b725933..467049ca8430 100644
--- a/drivers/gpu/drm/ast/ast_drv.h
+++ b/drivers/gpu/drm/ast/ast_drv.h
@@ -177,6 +177,8 @@ struct ast_private *ast_device_create(struct drm_driver *drv,
#define AST_IO_MM_OFFSET (0x380)
+#define AST_IO_VGAIR1_VREFRESH BIT(3)
+
#define __ast_read(x) \
static inline u##x ast_read##x(struct ast_private *ast, u32 reg) { \
u##x val = 0;\
diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
index 62fe682a7de6..834a156e3a75 100644
--- a/drivers/gpu/drm/ast/ast_mode.c
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -514,6 +514,16 @@ static void ast_set_start_address_crt1(struct ast_private *ast,
}
+static void ast_wait_for_vretrace(struct ast_private *ast)
+{
+ unsigned long timeout = jiffies + HZ;
+ u8 vgair1;
+
+ do {
+ vgair1 = ast_io_read8(ast, AST_IO_INPUT_STATUS1_READ);
+ } while (!(vgair1 & AST_IO_VGAIR1_VREFRESH) && time_before(jiffies, timeout));
+}
+
/*
* Primary plane
*/
@@ -562,13 +572,24 @@ ast_primary_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_plane_state *state = plane->state;
struct drm_gem_vram_object *gbo;
s64 gpu_addr;
+ struct drm_framebuffer *fb = state->fb;
+ struct drm_framebuffer *old_fb = old_state->fb;
- gbo = drm_gem_vram_of_gem(state->fb->obj[0]);
+ if (!old_fb || (fb->format != old_fb->format)) {
+ struct drm_crtc_state *crtc_state = state->crtc->state;
+ struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state);
+ struct ast_vbios_mode_info *vbios_mode_info = &ast_crtc_state->vbios_mode_info;
+
+ ast_set_color_reg(ast, fb->format);
+ ast_set_vbios_color_reg(ast, fb->format, vbios_mode_info);
+ }
+
+ gbo = drm_gem_vram_of_gem(fb->obj[0]);
gpu_addr = drm_gem_vram_offset(gbo);
if (drm_WARN_ON_ONCE(dev, gpu_addr < 0))
return; /* Bug: we didn't pin the BO to VRAM in prepare_fb. */
- ast_set_offset_reg(ast, state->fb);
+ ast_set_offset_reg(ast, fb);
ast_set_start_address_crt1(ast, (u32)gpu_addr);
ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0x00);
@@ -733,6 +754,7 @@ static void ast_crtc_dpms(struct drm_crtc *crtc, int mode)
static int ast_crtc_helper_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
+ struct drm_device *dev = crtc->dev;
struct ast_crtc_state *ast_state;
const struct drm_format_info *format;
bool succ;
@@ -743,8 +765,8 @@ static int ast_crtc_helper_atomic_check(struct drm_crtc *crtc,
ast_state = to_ast_crtc_state(state);
format = ast_state->format;
- if (!format)
- return 0;
+ if (drm_WARN_ON_ONCE(dev, !format))
+ return -EINVAL; /* BUG: We didn't set format in primary check(). */
succ = ast_get_vbios_mode_info(format, &state->mode,
&state->adjusted_mode,
@@ -755,39 +777,17 @@ static int ast_crtc_helper_atomic_check(struct drm_crtc *crtc,
return 0;
}
-static void ast_crtc_helper_atomic_begin(struct drm_crtc *crtc,
- struct drm_crtc_state *old_crtc_state)
-{
- struct ast_private *ast = to_ast_private(crtc->dev);
-
- ast_open_key(ast);
-}
-
-static void ast_crtc_helper_atomic_flush(struct drm_crtc *crtc,
- struct drm_crtc_state *old_crtc_state)
+static void
+ast_crtc_helper_atomic_enable(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_crtc_state)
{
struct drm_device *dev = crtc->dev;
struct ast_private *ast = to_ast_private(dev);
- struct ast_crtc_state *ast_state;
- const struct drm_format_info *format;
- struct ast_vbios_mode_info *vbios_mode_info;
- struct drm_display_mode *adjusted_mode;
-
- ast_state = to_ast_crtc_state(crtc->state);
-
- format = ast_state->format;
- if (!format)
- return;
-
- vbios_mode_info = &ast_state->vbios_mode_info;
-
- ast_set_color_reg(ast, format);
- ast_set_vbios_color_reg(ast, format, vbios_mode_info);
-
- if (!crtc->state->mode_changed)
- return;
-
- adjusted_mode = &crtc->state->adjusted_mode;
+ struct drm_crtc_state *crtc_state = crtc->state;
+ struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state);
+ struct ast_vbios_mode_info *vbios_mode_info =
+ &ast_crtc_state->vbios_mode_info;
+ struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
ast_set_vbios_mode_reg(ast, adjusted_mode, vbios_mode_info);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x06);
@@ -796,12 +796,7 @@ static void ast_crtc_helper_atomic_flush(struct drm_crtc *crtc,
ast_set_dclk_reg(ast, adjusted_mode, vbios_mode_info);
ast_set_crtthd_reg(ast);
ast_set_sync_reg(ast, adjusted_mode, vbios_mode_info);
-}
-static void
-ast_crtc_helper_atomic_enable(struct drm_crtc *crtc,
- struct drm_crtc_state *old_crtc_state)
-{
ast_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
}
@@ -809,13 +804,32 @@ static void
ast_crtc_helper_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
{
+ struct drm_device *dev = crtc->dev;
+ struct ast_private *ast = to_ast_private(dev);
+
ast_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+
+ /*
+ * HW cursors require the underlying primary plane and CRTC to
+ * display a valid mode and image. This is not the case during
+ * full modeset operations. So we temporarily disable any active
+ * plane, including the HW cursor. Each plane's atomic_update()
+ * helper will re-enable it if necessary.
+ *
+ * We only do this during *full* modesets. It does not affect
+ * simple pageflips on the planes.
+ */
+ drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
+
+ /*
+ * Ensure that no scanout takes place before reprogramming mode
+ * and format registers.
+ */
+ ast_wait_for_vretrace(ast);
}
static const struct drm_crtc_helper_funcs ast_crtc_helper_funcs = {
.atomic_check = ast_crtc_helper_atomic_check,
- .atomic_begin = ast_crtc_helper_atomic_begin,
- .atomic_flush = ast_crtc_helper_atomic_flush,
.atomic_enable = ast_crtc_helper_atomic_enable,
.atomic_disable = ast_crtc_helper_atomic_disable,
};
@@ -1054,6 +1068,11 @@ static int ast_connector_init(struct drm_device *dev)
* Mode config
*/
+static const struct drm_mode_config_helper_funcs
+ast_mode_config_helper_funcs = {
+ .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+
static const struct drm_mode_config_funcs ast_mode_config_funcs = {
.fb_create = drm_gem_fb_create,
.mode_valid = drm_vram_helper_mode_valid,
@@ -1093,6 +1112,8 @@ int ast_mode_config_init(struct ast_private *ast)
dev->mode_config.max_height = 1200;
}
+ dev->mode_config.helper_private = &ast_mode_config_helper_funcs;
+
memset(&ast->primary_plane, 0, sizeof(ast->primary_plane));
ret = drm_universal_plane_init(dev, &ast->primary_plane, 0x01,
&ast_primary_plane_funcs,
diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c
index 9f7b7a9c53c5..7bd0affa057a 100644
--- a/drivers/gpu/drm/bridge/parade-ps8640.c
+++ b/drivers/gpu/drm/bridge/parade-ps8640.c
@@ -65,6 +65,7 @@ struct ps8640 {
struct regulator_bulk_data supplies[2];
struct gpio_desc *gpio_reset;
struct gpio_desc *gpio_powerdown;
+ bool powered;
};
static inline struct ps8640 *bridge_to_ps8640(struct drm_bridge *e)
@@ -91,13 +92,15 @@ static int ps8640_bridge_vdo_control(struct ps8640 *ps_bridge,
return 0;
}
-static void ps8640_pre_enable(struct drm_bridge *bridge)
+static void ps8640_bridge_poweron(struct ps8640 *ps_bridge)
{
- struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
struct i2c_client *client = ps_bridge->page[PAGE2_TOP_CNTL];
unsigned long timeout;
int ret, status;
+ if (ps_bridge->powered)
+ return;
+
ret = regulator_bulk_enable(ARRAY_SIZE(ps_bridge->supplies),
ps_bridge->supplies);
if (ret < 0) {
@@ -152,10 +155,6 @@ static void ps8640_pre_enable(struct drm_bridge *bridge)
goto err_regulators_disable;
}
- ret = ps8640_bridge_vdo_control(ps_bridge, ENABLE);
- if (ret)
- goto err_regulators_disable;
-
/* Switch access edp panel's edid through i2c */
ret = i2c_smbus_write_byte_data(client, PAGE2_I2C_BYPASS,
I2C_BYPASS_EN);
@@ -164,6 +163,8 @@ static void ps8640_pre_enable(struct drm_bridge *bridge)
goto err_regulators_disable;
}
+ ps_bridge->powered = true;
+
return;
err_regulators_disable:
@@ -171,12 +172,12 @@ err_regulators_disable:
ps_bridge->supplies);
}
-static void ps8640_post_disable(struct drm_bridge *bridge)
+static void ps8640_bridge_poweroff(struct ps8640 *ps_bridge)
{
- struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
int ret;
- ps8640_bridge_vdo_control(ps_bridge, DISABLE);
+ if (!ps_bridge->powered)
+ return;
gpiod_set_value(ps_bridge->gpio_reset, 1);
gpiod_set_value(ps_bridge->gpio_powerdown, 1);
@@ -184,6 +185,28 @@ static void ps8640_post_disable(struct drm_bridge *bridge)
ps_bridge->supplies);
if (ret < 0)
DRM_ERROR("cannot disable regulators %d\n", ret);
+
+ ps_bridge->powered = false;
+}
+
+static void ps8640_pre_enable(struct drm_bridge *bridge)
+{
+ struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
+ int ret;
+
+ ps8640_bridge_poweron(ps_bridge);
+
+ ret = ps8640_bridge_vdo_control(ps_bridge, ENABLE);
+ if (ret < 0)
+ ps8640_bridge_poweroff(ps_bridge);
+}
+
+static void ps8640_post_disable(struct drm_bridge *bridge)
+{
+ struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
+
+ ps8640_bridge_vdo_control(ps_bridge, DISABLE);
+ ps8640_bridge_poweroff(ps_bridge);
}
static int ps8640_bridge_attach(struct drm_bridge *bridge,
@@ -249,9 +272,34 @@ static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge,
struct drm_connector *connector)
{
struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
+ bool poweroff = !ps_bridge->powered;
+ struct edid *edid;
+
+ /*
+ * When we end calling get_edid() triggered by an ioctl, i.e
+ *
+ * drm_mode_getconnector (ioctl)
+ * -> drm_helper_probe_single_connector_modes
+ * -> drm_bridge_connector_get_modes
+ * -> ps8640_bridge_get_edid
+ *
+ * We need to make sure that what we need is enabled before reading
+ * EDID, for this chip, we need to do a full poweron, otherwise it will
+ * fail.
+ */
+ drm_bridge_chain_pre_enable(bridge);
- return drm_get_edid(connector,
+ edid = drm_get_edid(connector,
ps_bridge->page[PAGE0_DP_CNTL]->adapter);
+
+ /*
+ * If we call the get_edid() function without having enabled the chip
+ * before, return the chip to its original power state.
+ */
+ if (poweroff)
+ drm_bridge_chain_post_disable(bridge);
+
+ return edid;
}
static const struct drm_bridge_funcs ps8640_bridge_funcs = {
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
index d580b2aa4ce9..6b268f9445b3 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -89,7 +89,9 @@
#define VID_MODE_TYPE_NON_BURST_SYNC_EVENTS 0x1
#define VID_MODE_TYPE_BURST 0x2
#define VID_MODE_TYPE_MASK 0x3
+#define ENABLE_LOW_POWER_CMD BIT(15)
#define VID_MODE_VPG_ENABLE BIT(16)
+#define VID_MODE_VPG_MODE BIT(20)
#define VID_MODE_VPG_HORIZONTAL BIT(24)
#define DSI_VID_PKT_SIZE 0x3c
@@ -220,6 +222,21 @@
#define PHY_STATUS_TIMEOUT_US 10000
#define CMD_PKT_STATUS_TIMEOUT_US 20000
+#ifdef CONFIG_DEBUG_FS
+#define VPG_DEFS(name, dsi) \
+ ((void __force *)&((*dsi).vpg_defs.name))
+
+#define REGISTER(name, mask, dsi) \
+ { #name, VPG_DEFS(name, dsi), mask, dsi }
+
+struct debugfs_entries {
+ const char *name;
+ bool *reg;
+ u32 mask;
+ struct dw_mipi_dsi *dsi;
+};
+#endif /* CONFIG_DEBUG_FS */
+
struct dw_mipi_dsi {
struct drm_bridge bridge;
struct mipi_dsi_host dsi_host;
@@ -237,9 +254,12 @@ struct dw_mipi_dsi {
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
-
- bool vpg;
- bool vpg_horizontal;
+ struct debugfs_entries *debugfs_vpg;
+ struct {
+ bool vpg;
+ bool vpg_horizontal;
+ bool vpg_ber_pattern;
+ } vpg_defs;
#endif /* CONFIG_DEBUG_FS */
struct dw_mipi_dsi *master; /* dual-dsi master ptr */
@@ -360,13 +380,28 @@ static void dw_mipi_message_config(struct dw_mipi_dsi *dsi,
bool lpm = msg->flags & MIPI_DSI_MSG_USE_LPM;
u32 val = 0;
+ /*
+ * TODO dw drv improvements
+ * largest packet sizes during hfp or during vsa/vpb/vfp
+ * should be computed according to byte lane, lane number and only
+ * if sending lp cmds in high speed is enable (PHY_TXREQUESTCLKHS)
+ */
+ dsi_write(dsi, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(16)
+ | INVACT_LPCMD_TIME(4));
+
if (msg->flags & MIPI_DSI_MSG_REQ_ACK)
val |= ACK_RQST_EN;
if (lpm)
val |= CMD_MODE_ALL_LP;
- dsi_write(dsi, DSI_LPCLK_CTRL, lpm ? 0 : PHY_TXREQUESTCLKHS);
dsi_write(dsi, DSI_CMD_MODE_CFG, val);
+
+ val = dsi_read(dsi, DSI_VID_MODE_CFG);
+ if (lpm)
+ val |= ENABLE_LOW_POWER_CMD;
+ else
+ val &= ~ENABLE_LOW_POWER_CMD;
+ dsi_write(dsi, DSI_VID_MODE_CFG, val);
}
static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val)
@@ -529,9 +564,11 @@ static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi)
val |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS;
#ifdef CONFIG_DEBUG_FS
- if (dsi->vpg) {
+ if (dsi->vpg_defs.vpg) {
val |= VID_MODE_VPG_ENABLE;
- val |= dsi->vpg_horizontal ? VID_MODE_VPG_HORIZONTAL : 0;
+ val |= dsi->vpg_defs.vpg_horizontal ?
+ VID_MODE_VPG_HORIZONTAL : 0;
+ val |= dsi->vpg_defs.vpg_ber_pattern ? VID_MODE_VPG_MODE : 0;
}
#endif /* CONFIG_DEBUG_FS */
@@ -541,16 +578,22 @@ static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi)
static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi,
unsigned long mode_flags)
{
+ u32 val;
+
dsi_write(dsi, DSI_PWR_UP, RESET);
if (mode_flags & MIPI_DSI_MODE_VIDEO) {
dsi_write(dsi, DSI_MODE_CFG, ENABLE_VIDEO_MODE);
dw_mipi_dsi_video_mode_config(dsi);
- dsi_write(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS);
} else {
dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
}
+ val = PHY_TXREQUESTCLKHS;
+ if (dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)
+ val |= AUTO_CLKLANE_CTRL;
+ dsi_write(dsi, DSI_LPCLK_CTRL, val);
+
dsi_write(dsi, DSI_PWR_UP, POWERUP);
}
@@ -562,15 +605,30 @@ static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi)
static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi)
{
+ const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
+ unsigned int esc_rate; /* in MHz */
+ u32 esc_clk_division;
+ int ret;
+
/*
* The maximum permitted escape clock is 20MHz and it is derived from
- * lanebyteclk, which is running at "lane_mbps / 8". Thus we want:
- *
- * (lane_mbps >> 3) / esc_clk_division < 20
+ * lanebyteclk, which is running at "lane_mbps / 8".
+ */
+ if (phy_ops->get_esc_clk_rate) {
+ ret = phy_ops->get_esc_clk_rate(dsi->plat_data->priv_data,
+ &esc_rate);
+ if (ret)
+ DRM_DEBUG_DRIVER("Phy get_esc_clk_rate() failed\n");
+ } else
+ esc_rate = 20; /* Default to 20MHz */
+
+ /*
+ * We want :
+ * (lane_mbps >> 3) / esc_clk_division < X
* which is:
- * (lane_mbps >> 3) / 20 > esc_clk_division
+ * (lane_mbps >> 3) / X > esc_clk_division
*/
- u32 esc_clk_division = (dsi->lane_mbps >> 3) / 20 + 1;
+ esc_clk_division = (dsi->lane_mbps >> 3) / esc_rate + 1;
dsi_write(dsi, DSI_PWR_UP, RESET);
@@ -611,14 +669,6 @@ static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
dsi_write(dsi, DSI_DPI_VCID, DPI_VCID(dsi->channel));
dsi_write(dsi, DSI_DPI_COLOR_CODING, color);
dsi_write(dsi, DSI_DPI_CFG_POL, val);
- /*
- * TODO dw drv improvements
- * largest packet sizes during hfp or during vsa/vpb/vfp
- * should be computed according to byte lane, lane number and only
- * if sending lp cmds in high speed is enable (PHY_TXREQUESTCLKHS)
- */
- dsi_write(dsi, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(4)
- | INVACT_LPCMD_TIME(4));
}
static void dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
@@ -964,6 +1014,66 @@ static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = {
#ifdef CONFIG_DEBUG_FS
+static int dw_mipi_dsi_debugfs_write(void *data, u64 val)
+{
+ struct debugfs_entries *vpg = data;
+ struct dw_mipi_dsi *dsi;
+ u32 mode_cfg;
+
+ if (!vpg)
+ return -ENODEV;
+
+ dsi = vpg->dsi;
+
+ *vpg->reg = (bool)val;
+
+ mode_cfg = dsi_read(dsi, DSI_VID_MODE_CFG);
+
+ if (*vpg->reg)
+ mode_cfg |= vpg->mask;
+ else
+ mode_cfg &= ~vpg->mask;
+
+ dsi_write(dsi, DSI_VID_MODE_CFG, mode_cfg);
+
+ return 0;
+}
+
+static int dw_mipi_dsi_debugfs_show(void *data, u64 *val)
+{
+ struct debugfs_entries *vpg = data;
+
+ if (!vpg)
+ return -ENODEV;
+
+ *val = *vpg->reg;
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x32, dw_mipi_dsi_debugfs_show,
+ dw_mipi_dsi_debugfs_write, "%llu\n");
+
+static void debugfs_create_files(void *data)
+{
+ struct dw_mipi_dsi *dsi = data;
+ struct debugfs_entries debugfs[] = {
+ REGISTER(vpg, VID_MODE_VPG_ENABLE, dsi),
+ REGISTER(vpg_horizontal, VID_MODE_VPG_HORIZONTAL, dsi),
+ REGISTER(vpg_ber_pattern, VID_MODE_VPG_MODE, dsi),
+ };
+ int i;
+
+ dsi->debugfs_vpg = kmemdup(debugfs, sizeof(debugfs), GFP_KERNEL);
+ if (!dsi->debugfs_vpg)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(debugfs); i++)
+ debugfs_create_file(dsi->debugfs_vpg[i].name, 0644,
+ dsi->debugfs, &dsi->debugfs_vpg[i],
+ &fops_x32);
+}
+
static void dw_mipi_dsi_debugfs_init(struct dw_mipi_dsi *dsi)
{
dsi->debugfs = debugfs_create_dir(dev_name(dsi->dev), NULL);
@@ -972,14 +1082,13 @@ static void dw_mipi_dsi_debugfs_init(struct dw_mipi_dsi *dsi)
return;
}
- debugfs_create_bool("vpg", 0660, dsi->debugfs, &dsi->vpg);
- debugfs_create_bool("vpg_horizontal", 0660, dsi->debugfs,
- &dsi->vpg_horizontal);
+ debugfs_create_files(dsi);
}
static void dw_mipi_dsi_debugfs_remove(struct dw_mipi_dsi *dsi)
{
debugfs_remove_recursive(dsi->debugfs);
+ kfree(dsi->debugfs_vpg);
}
#else
diff --git a/drivers/gpu/drm/bridge/tc358775.c b/drivers/gpu/drm/bridge/tc358775.c
index d951cdc58297..2272adcc5b4a 100644
--- a/drivers/gpu/drm/bridge/tc358775.c
+++ b/drivers/gpu/drm/bridge/tc358775.c
@@ -485,7 +485,7 @@ static void tc_bridge_enable(struct drm_bridge *bridge)
val |= TC358775_LVCFG_PCLKDIV(DIVIDE_BY_6);
} else {
val |= TC358775_LVCFG_PCLKDIV(DIVIDE_BY_3);
- };
+ }
d2l_write(tc->i2c, LVCFG, val);
}
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 9e1ad493e689..f9170b4b22e7 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1115,9 +1115,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
* @old_state: atomic state object with old state structures
*
* This function updates all the various legacy modeset state pointers in
- * connectors, encoders and CRTCs. It also updates the timestamping constants
- * used for precise vblank timestamps by calling
- * drm_calc_timestamping_constants().
+ * connectors, encoders and CRTCs.
*
* Drivers can use this for building their own atomic commit if they don't have
* a pure helper-based modeset implementation.
@@ -1186,13 +1184,30 @@ drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev,
crtc->x = new_plane_state->src_x >> 16;
crtc->y = new_plane_state->src_y >> 16;
}
+ }
+}
+EXPORT_SYMBOL(drm_atomic_helper_update_legacy_modeset_state);
+/**
+ * drm_atomic_helper_calc_timestamping_constants - update vblank timestamping constants
+ * @state: atomic state object
+ *
+ * Updates the timestamping constants used for precise vblank timestamps
+ * by calling drm_calc_timestamping_constants() for all enabled crtcs in @state.
+ */
+void drm_atomic_helper_calc_timestamping_constants(struct drm_atomic_state *state)
+{
+ struct drm_crtc_state *new_crtc_state;
+ struct drm_crtc *crtc;
+ int i;
+
+ for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
if (new_crtc_state->enable)
drm_calc_timestamping_constants(crtc,
&new_crtc_state->adjusted_mode);
}
}
-EXPORT_SYMBOL(drm_atomic_helper_update_legacy_modeset_state);
+EXPORT_SYMBOL(drm_atomic_helper_calc_timestamping_constants);
static void
crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
@@ -1276,6 +1291,7 @@ void drm_atomic_helper_commit_modeset_disables(struct drm_device *dev,
disable_outputs(dev, old_state);
drm_atomic_helper_update_legacy_modeset_state(dev, old_state);
+ drm_atomic_helper_calc_timestamping_constants(old_state);
crtc_set_mode(dev, old_state);
}
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 3d48ad1c3682..717c4e7271b0 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -2289,7 +2289,7 @@ static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *conne
static bool
drm_mode_expose_to_userspace(const struct drm_display_mode *mode,
- const struct list_head *export_list,
+ const struct list_head *modes,
const struct drm_file *file_priv)
{
/*
@@ -2305,15 +2305,17 @@ drm_mode_expose_to_userspace(const struct drm_display_mode *mode,
* while preparing the list of user-modes.
*/
if (!file_priv->aspect_ratio_allowed) {
- struct drm_display_mode *mode_itr;
+ const struct drm_display_mode *mode_itr;
- list_for_each_entry(mode_itr, export_list, export_head)
- if (drm_mode_match(mode_itr, mode,
+ list_for_each_entry(mode_itr, modes, head) {
+ if (mode_itr->expose_to_userspace &&
+ drm_mode_match(mode_itr, mode,
DRM_MODE_MATCH_TIMINGS |
DRM_MODE_MATCH_CLOCK |
DRM_MODE_MATCH_FLAGS |
DRM_MODE_MATCH_3D_FLAGS))
return false;
+ }
}
return true;
@@ -2333,7 +2335,6 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
struct drm_mode_modeinfo u_mode;
struct drm_mode_modeinfo __user *mode_ptr;
uint32_t __user *encoder_ptr;
- LIST_HEAD(export_list);
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EOPNOTSUPP;
@@ -2377,25 +2378,30 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
out_resp->connection = connector->status;
/* delayed so we get modes regardless of pre-fill_modes state */
- list_for_each_entry(mode, &connector->modes, head)
- if (drm_mode_expose_to_userspace(mode, &export_list,
+ list_for_each_entry(mode, &connector->modes, head) {
+ WARN_ON(mode->expose_to_userspace);
+
+ if (drm_mode_expose_to_userspace(mode, &connector->modes,
file_priv)) {
- list_add_tail(&mode->export_head, &export_list);
+ mode->expose_to_userspace = true;
mode_count++;
}
+ }
/*
* This ioctl is called twice, once to determine how much space is
* needed, and the 2nd time to fill it.
- * The modes that need to be exposed to the user are maintained in the
- * 'export_list'. When the ioctl is called first time to determine the,
- * space, the export_list gets filled, to find the no.of modes. In the
- * 2nd time, the user modes are filled, one by one from the export_list.
*/
if ((out_resp->count_modes >= mode_count) && mode_count) {
copied = 0;
mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr;
- list_for_each_entry(mode, &export_list, export_head) {
+ list_for_each_entry(mode, &connector->modes, head) {
+ if (!mode->expose_to_userspace)
+ continue;
+
+ /* Clear the tag for the next time around */
+ mode->expose_to_userspace = false;
+
drm_mode_convert_to_umode(&u_mode, mode);
/*
* Reset aspect ratio flags of user-mode, if modes with
@@ -2406,13 +2412,26 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
if (copy_to_user(mode_ptr + copied,
&u_mode, sizeof(u_mode))) {
ret = -EFAULT;
+
+ /*
+ * Clear the tag for the rest of
+ * the modes for the next time around.
+ */
+ list_for_each_entry_continue(mode, &connector->modes, head)
+ mode->expose_to_userspace = false;
+
mutex_unlock(&dev->mode_config.mutex);
goto out;
}
copied++;
}
+ } else {
+ /* Clear the tag for the next time around */
+ list_for_each_entry(mode, &connector->modes, head)
+ mode->expose_to_userspace = false;
}
+
out_resp->count_modes = mode_count;
mutex_unlock(&dev->mode_config.mutex);
diff --git a/drivers/gpu/drm/drm_debugfs_crc.c b/drivers/gpu/drm/drm_debugfs_crc.c
index 5d67a41f7c3a..3dd70d813f69 100644
--- a/drivers/gpu/drm/drm_debugfs_crc.c
+++ b/drivers/gpu/drm/drm_debugfs_crc.c
@@ -144,8 +144,10 @@ static ssize_t crc_control_write(struct file *file, const char __user *ubuf,
source[len - 1] = '\0';
ret = crtc->funcs->verify_crc_source(crtc, source, &values_cnt);
- if (ret)
+ if (ret) {
+ kfree(source);
return ret;
+ }
spin_lock_irq(&crc->lock);
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 54864400015d..e87542533640 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -1039,6 +1039,8 @@ static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw,
return drm_dp_sideband_parse_remote_dpcd_write(raw, msg);
case DP_REMOTE_I2C_READ:
return drm_dp_sideband_parse_remote_i2c_read_ack(raw, msg);
+ case DP_REMOTE_I2C_WRITE:
+ return true; /* since there's nothing to parse */
case DP_ENUM_PATH_RESOURCES:
return drm_dp_sideband_parse_enum_path_resources_ack(raw, msg);
case DP_ALLOCATE_PAYLOAD:
@@ -5499,29 +5501,29 @@ static bool remote_i2c_read_ok(const struct i2c_msg msgs[], int num)
msgs[num - 1].len <= 0xff;
}
-/* I2C device */
-static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
- int num)
+static bool remote_i2c_write_ok(const struct i2c_msg msgs[], int num)
+{
+ int i;
+
+ for (i = 0; i < num - 1; i++) {
+ if (msgs[i].flags & I2C_M_RD || !(msgs[i].flags & I2C_M_STOP) ||
+ msgs[i].len > 0xff)
+ return false;
+ }
+
+ return !(msgs[num - 1].flags & I2C_M_RD) && msgs[num - 1].len <= 0xff;
+}
+
+static int drm_dp_mst_i2c_read(struct drm_dp_mst_branch *mstb,
+ struct drm_dp_mst_port *port,
+ struct i2c_msg *msgs, int num)
{
- struct drm_dp_aux *aux = adapter->algo_data;
- struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port, aux);
- struct drm_dp_mst_branch *mstb;
struct drm_dp_mst_topology_mgr *mgr = port->mgr;
unsigned int i;
struct drm_dp_sideband_msg_req_body msg;
struct drm_dp_sideband_msg_tx *txmsg = NULL;
int ret;
- mstb = drm_dp_mst_topology_get_mstb_validated(mgr, port->parent);
- if (!mstb)
- return -EREMOTEIO;
-
- if (!remote_i2c_read_ok(msgs, num)) {
- DRM_DEBUG_KMS("Unsupported I2C transaction for MST device\n");
- ret = -EIO;
- goto out;
- }
-
memset(&msg, 0, sizeof(msg));
msg.req_type = DP_REMOTE_I2C_READ;
msg.u.i2c_read.num_transactions = num - 1;
@@ -5562,6 +5564,78 @@ static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs
}
out:
kfree(txmsg);
+ return ret;
+}
+
+static int drm_dp_mst_i2c_write(struct drm_dp_mst_branch *mstb,
+ struct drm_dp_mst_port *port,
+ struct i2c_msg *msgs, int num)
+{
+ struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+ unsigned int i;
+ struct drm_dp_sideband_msg_req_body msg;
+ struct drm_dp_sideband_msg_tx *txmsg = NULL;
+ int ret;
+
+ txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+ if (!txmsg) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < num; i++) {
+ memset(&msg, 0, sizeof(msg));
+ msg.req_type = DP_REMOTE_I2C_WRITE;
+ msg.u.i2c_write.port_number = port->port_num;
+ msg.u.i2c_write.write_i2c_device_id = msgs[i].addr;
+ msg.u.i2c_write.num_bytes = msgs[i].len;
+ msg.u.i2c_write.bytes = msgs[i].buf;
+
+ memset(txmsg, 0, sizeof(*txmsg));
+ txmsg->dst = mstb;
+
+ drm_dp_encode_sideband_req(&msg, txmsg);
+ drm_dp_queue_down_tx(mgr, txmsg);
+
+ ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+ if (ret > 0) {
+ if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) {
+ ret = -EREMOTEIO;
+ goto out;
+ }
+ } else {
+ goto out;
+ }
+ }
+ ret = num;
+out:
+ kfree(txmsg);
+ return ret;
+}
+
+/* I2C device */
+static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ struct drm_dp_aux *aux = adapter->algo_data;
+ struct drm_dp_mst_port *port =
+ container_of(aux, struct drm_dp_mst_port, aux);
+ struct drm_dp_mst_branch *mstb;
+ struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+ int ret;
+
+ mstb = drm_dp_mst_topology_get_mstb_validated(mgr, port->parent);
+ if (!mstb)
+ return -EREMOTEIO;
+
+ if (remote_i2c_read_ok(msgs, num)) {
+ ret = drm_dp_mst_i2c_read(mstb, port, msgs, num);
+ } else if (remote_i2c_write_ok(msgs, num)) {
+ ret = drm_dp_mst_i2c_write(mstb, port, msgs, num);
+ } else {
+ DRM_DEBUG_KMS("Unsupported I2C transaction for MST device\n");
+ ret = -EIO;
+ }
+
drm_dp_mst_topology_put_mstb(mstb);
return ret;
}
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 13068fdf4331..cd162d406078 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -240,13 +240,13 @@ void drm_minor_release(struct drm_minor *minor)
* DOC: driver instance overview
*
* A device instance for a drm driver is represented by &struct drm_device. This
- * is initialized with drm_dev_init(), usually from bus-specific ->probe()
- * callbacks implemented by the driver. The driver then needs to initialize all
- * the various subsystems for the drm device like memory management, vblank
- * handling, modesetting support and intial output configuration plus obviously
- * initialize all the corresponding hardware bits. Finally when everything is up
- * and running and ready for userspace the device instance can be published
- * using drm_dev_register().
+ * is allocated and initialized with devm_drm_dev_alloc(), usually from
+ * bus-specific ->probe() callbacks implemented by the driver. The driver then
+ * needs to initialize all the various subsystems for the drm device like memory
+ * management, vblank handling, modesetting support and initial output
+ * configuration plus obviously initialize all the corresponding hardware bits.
+ * Finally when everything is up and running and ready for userspace the device
+ * instance can be published using drm_dev_register().
*
* There is also deprecated support for initalizing device instances using
* bus-specific helpers and the &drm_driver.load callback. But due to
@@ -274,7 +274,7 @@ void drm_minor_release(struct drm_minor *minor)
*
* The following example shows a typical structure of a DRM display driver.
* The example focus on the probe() function and the other functions that is
- * almost always present and serves as a demonstration of devm_drm_dev_init().
+ * almost always present and serves as a demonstration of devm_drm_dev_alloc().
*
* .. code-block:: c
*
@@ -294,22 +294,12 @@ void drm_minor_release(struct drm_minor *minor)
* struct drm_device *drm;
* int ret;
*
- * // devm_kzalloc() can't be used here because the drm_device '
- * // lifetime can exceed the device lifetime if driver unbind
- * // happens when userspace still has open file descriptors.
- * priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- * if (!priv)
- * return -ENOMEM;
- *
+ * priv = devm_drm_dev_alloc(&pdev->dev, &driver_drm_driver,
+ * struct driver_device, drm);
+ * if (IS_ERR(priv))
+ * return PTR_ERR(priv);
* drm = &priv->drm;
*
- * ret = devm_drm_dev_init(&pdev->dev, drm, &driver_drm_driver);
- * if (ret) {
- * kfree(priv);
- * return ret;
- * }
- * drmm_add_final_kfree(drm, priv);
- *
* ret = drmm_mode_config_init(drm);
* if (ret)
* return ret;
@@ -550,9 +540,9 @@ static void drm_fs_inode_free(struct inode *inode)
* following guidelines apply:
*
* - The entire device initialization procedure should be run from the
- * &component_master_ops.master_bind callback, starting with drm_dev_init(),
- * then binding all components with component_bind_all() and finishing with
- * drm_dev_register().
+ * &component_master_ops.master_bind callback, starting with
+ * devm_drm_dev_alloc(), then binding all components with
+ * component_bind_all() and finishing with drm_dev_register().
*
* - The opaque pointer passed to all components through component_bind_all()
* should point at &struct drm_device of the device instance, not some driver
@@ -583,43 +573,9 @@ static void drm_dev_init_release(struct drm_device *dev, void *res)
drm_legacy_destroy_members(dev);
}
-/**
- * drm_dev_init - Initialise new DRM device
- * @dev: DRM device
- * @driver: DRM driver
- * @parent: Parent device object
- *
- * Initialize a new DRM device. No device registration is done.
- * Call drm_dev_register() to advertice the device to user space and register it
- * with other core subsystems. This should be done last in the device
- * initialization sequence to make sure userspace can't access an inconsistent
- * state.
- *
- * The initial ref-count of the object is 1. Use drm_dev_get() and
- * drm_dev_put() to take and drop further ref-counts.
- *
- * It is recommended that drivers embed &struct drm_device into their own device
- * structure.
- *
- * Drivers that do not want to allocate their own device struct
- * embedding &struct drm_device can call drm_dev_alloc() instead. For drivers
- * that do embed &struct drm_device it must be placed first in the overall
- * structure, and the overall structure must be allocated using kmalloc(): The
- * drm core's release function unconditionally calls kfree() on the @dev pointer
- * when the final reference is released. To override this behaviour, and so
- * allow embedding of the drm_device inside the driver's device struct at an
- * arbitrary offset, you must supply a &drm_driver.release callback and control
- * the finalization explicitly.
- *
- * Note that drivers must call drmm_add_final_kfree() after this function has
- * completed successfully.
- *
- * RETURNS:
- * 0 on success, or error code on failure.
- */
-int drm_dev_init(struct drm_device *dev,
- struct drm_driver *driver,
- struct device *parent)
+static int drm_dev_init(struct drm_device *dev,
+ struct drm_driver *driver,
+ struct device *parent)
{
int ret;
@@ -699,31 +655,15 @@ err:
return ret;
}
-EXPORT_SYMBOL(drm_dev_init);
static void devm_drm_dev_init_release(void *data)
{
drm_dev_put(data);
}
-/**
- * devm_drm_dev_init - Resource managed drm_dev_init()
- * @parent: Parent device object
- * @dev: DRM device
- * @driver: DRM driver
- *
- * Managed drm_dev_init(). The DRM device initialized with this function is
- * automatically put on driver detach using drm_dev_put().
- *
- * Note that drivers must call drmm_add_final_kfree() after this function has
- * completed successfully.
- *
- * RETURNS:
- * 0 on success, or error code on failure.
- */
-int devm_drm_dev_init(struct device *parent,
- struct drm_device *dev,
- struct drm_driver *driver)
+static int devm_drm_dev_init(struct device *parent,
+ struct drm_device *dev,
+ struct drm_driver *driver)
{
int ret;
@@ -737,7 +677,6 @@ int devm_drm_dev_init(struct device *parent,
return ret;
}
-EXPORT_SYMBOL(devm_drm_dev_init);
void *__devm_drm_dev_alloc(struct device *parent, struct drm_driver *driver,
size_t size, size_t offset)
@@ -767,19 +706,9 @@ EXPORT_SYMBOL(__devm_drm_dev_alloc);
* @driver: DRM driver to allocate device for
* @parent: Parent device object
*
- * Allocate and initialize a new DRM device. No device registration is done.
- * Call drm_dev_register() to advertice the device to user space and register it
- * with other core subsystems. This should be done last in the device
- * initialization sequence to make sure userspace can't access an inconsistent
- * state.
- *
- * The initial ref-count of the object is 1. Use drm_dev_get() and
- * drm_dev_put() to take and drop further ref-counts.
- *
- * Note that for purely virtual devices @parent can be NULL.
- *
- * Drivers that wish to subclass or embed &struct drm_device into their
- * own struct should look at using drm_dev_init() instead.
+ * This is the deprecated version of devm_drm_dev_alloc(), which does not support
+ * subclassing through embedding the struct &drm_device in a driver private
+ * structure, and which does not support automatic cleanup through devres.
*
* RETURNS:
* Pointer to new DRM device, or ERR_PTR on failure.
diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c
index df656366a530..2f5b0c2bb0fe 100644
--- a/drivers/gpu/drm/drm_framebuffer.c
+++ b/drivers/gpu/drm/drm_framebuffer.c
@@ -176,8 +176,7 @@ static int framebuffer_check(struct drm_device *dev,
int i;
/* check if the format is supported at all */
- info = __drm_format_info(r->pixel_format);
- if (!info) {
+ if (!__drm_format_info(r->pixel_format)) {
struct drm_format_name_buf format_name;
DRM_DEBUG_KMS("bad framebuffer format %s\n",
@@ -186,9 +185,6 @@ static int framebuffer_check(struct drm_device *dev,
return -EINVAL;
}
- /* now let the driver pick its own format info */
- info = drm_get_format_info(dev, r);
-
if (r->width == 0) {
DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width);
return -EINVAL;
@@ -199,6 +195,9 @@ static int framebuffer_check(struct drm_device *dev,
return -EINVAL;
}
+ /* now let the driver pick its own format info */
+ info = drm_get_format_info(dev, r);
+
for (i = 0; i < info->num_planes; i++) {
unsigned int width = fb_plane_width(r->width, info, i);
unsigned int height = fb_plane_height(r->height, info, i);
diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 47d8211221f2..d77c9f8ff26c 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -655,7 +655,7 @@ struct sg_table *drm_gem_shmem_get_sg_table(struct drm_gem_object *obj)
WARN_ON(shmem->base.import_attach);
- return drm_prime_pages_to_sg(shmem->pages, obj->size >> PAGE_SHIFT);
+ return drm_prime_pages_to_sg(obj->dev, shmem->pages, obj->size >> PAGE_SHIFT);
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_get_sg_table);
diff --git a/drivers/gpu/drm/drm_gem_ttm_helper.c b/drivers/gpu/drm/drm_gem_ttm_helper.c
index 892b2288a104..0e4fb9ba43ad 100644
--- a/drivers/gpu/drm/drm_gem_ttm_helper.c
+++ b/drivers/gpu/drm/drm_gem_ttm_helper.c
@@ -43,12 +43,9 @@ void drm_gem_ttm_print_info(struct drm_printer *p, unsigned int indent,
drm_print_bits(p, bo->mem.placement, plname, ARRAY_SIZE(plname));
drm_printf(p, "\n");
- if (bo->mem.bus.is_iomem) {
- drm_printf_indent(p, indent, "bus.base=%lx\n",
- (unsigned long)bo->mem.bus.base);
+ if (bo->mem.bus.is_iomem)
drm_printf_indent(p, indent, "bus.offset=%lx\n",
(unsigned long)bo->mem.bus.offset);
- }
}
EXPORT_SYMBOL(drm_gem_ttm_print_info);
diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c
index 545a877406f4..50cad0e4a92e 100644
--- a/drivers/gpu/drm/drm_gem_vram_helper.c
+++ b/drivers/gpu/drm/drm_gem_vram_helper.c
@@ -97,8 +97,8 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs;
* hardware's draing engine.
*
* To access a buffer object's memory from the DRM driver, call
- * drm_gem_vram_kmap(). It (optionally) maps the buffer into kernel address
- * space and returns the memory address. Use drm_gem_vram_kunmap() to
+ * drm_gem_vram_vmap(). It maps the buffer into kernel address
+ * space and returns the memory address. Use drm_gem_vram_vunmap() to
* release the mapping.
*/
@@ -135,28 +135,28 @@ static void ttm_buffer_object_destroy(struct ttm_buffer_object *bo)
static void drm_gem_vram_placement(struct drm_gem_vram_object *gbo,
unsigned long pl_flag)
{
+ u32 invariant_flags = 0;
unsigned int i;
unsigned int c = 0;
- u32 invariant_flags = pl_flag & TTM_PL_FLAG_TOPDOWN;
+
+ if (pl_flag & DRM_GEM_VRAM_PL_FLAG_TOPDOWN)
+ pl_flag = TTM_PL_FLAG_TOPDOWN;
gbo->placement.placement = gbo->placements;
gbo->placement.busy_placement = gbo->placements;
- if (pl_flag & TTM_PL_FLAG_VRAM)
+ if (pl_flag & DRM_GEM_VRAM_PL_FLAG_VRAM) {
+ gbo->placements[c].mem_type = TTM_PL_VRAM;
gbo->placements[c++].flags = TTM_PL_FLAG_WC |
TTM_PL_FLAG_UNCACHED |
- TTM_PL_FLAG_VRAM |
- invariant_flags;
-
- if (pl_flag & TTM_PL_FLAG_SYSTEM)
- gbo->placements[c++].flags = TTM_PL_MASK_CACHING |
- TTM_PL_FLAG_SYSTEM |
invariant_flags;
+ }
- if (!c)
+ if (pl_flag & DRM_GEM_VRAM_PL_FLAG_SYSTEM || !c) {
+ gbo->placements[c].mem_type = TTM_PL_SYSTEM;
gbo->placements[c++].flags = TTM_PL_MASK_CACHING |
- TTM_PL_FLAG_SYSTEM |
invariant_flags;
+ }
gbo->placement.num_placement = c;
gbo->placement.num_busy_placement = c;
@@ -167,6 +167,10 @@ static void drm_gem_vram_placement(struct drm_gem_vram_object *gbo,
}
}
+/*
+ * Note that on error, drm_gem_vram_init will free the buffer object.
+ */
+
static int drm_gem_vram_init(struct drm_device *dev,
struct drm_gem_vram_object *gbo,
size_t size, unsigned long pg_align)
@@ -176,32 +180,37 @@ static int drm_gem_vram_init(struct drm_device *dev,
int ret;
size_t acc_size;
- if (WARN_ONCE(!vmm, "VRAM MM not initialized"))
+ if (WARN_ONCE(!vmm, "VRAM MM not initialized")) {
+ kfree(gbo);
return -EINVAL;
+ }
bdev = &vmm->bdev;
gbo->bo.base.funcs = &drm_gem_vram_object_funcs;
ret = drm_gem_object_init(dev, &gbo->bo.base, size);
- if (ret)
+ if (ret) {
+ kfree(gbo);
return ret;
+ }
acc_size = ttm_bo_dma_acc_size(bdev, size, sizeof(*gbo));
gbo->bo.bdev = bdev;
- drm_gem_vram_placement(gbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
+ drm_gem_vram_placement(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM |
+ DRM_GEM_VRAM_PL_FLAG_SYSTEM);
ret = ttm_bo_init(bdev, &gbo->bo, size, ttm_bo_type_device,
&gbo->placement, pg_align, false, acc_size,
NULL, NULL, ttm_buffer_object_destroy);
if (ret)
- goto err_drm_gem_object_release;
+ /*
+ * A failing ttm_bo_init will call ttm_buffer_object_destroy
+ * to release gbo->bo.base and kfree gbo.
+ */
+ return ret;
return 0;
-
-err_drm_gem_object_release:
- drm_gem_object_release(&gbo->bo.base);
- return ret;
}
/**
@@ -235,13 +244,9 @@ struct drm_gem_vram_object *drm_gem_vram_create(struct drm_device *dev,
ret = drm_gem_vram_init(dev, gbo, size, pg_align);
if (ret < 0)
- goto err_kfree;
+ return ERR_PTR(ret);
return gbo;
-
-err_kfree:
- kfree(gbo);
- return ERR_PTR(ret);
}
EXPORT_SYMBOL(drm_gem_vram_create);
@@ -436,39 +441,6 @@ out:
return kmap->virtual;
}
-/**
- * drm_gem_vram_kmap() - Maps a GEM VRAM object into kernel address space
- * @gbo: the GEM VRAM object
- * @map: establish a mapping if necessary
- * @is_iomem: returns true if the mapped memory is I/O memory, or false \
- otherwise; can be NULL
- *
- * This function maps the buffer object into the kernel's address space
- * or returns the current mapping. If the parameter map is false, the
- * function only queries the current mapping, but does not establish a
- * new one.
- *
- * Returns:
- * The buffers virtual address if mapped, or
- * NULL if not mapped, or
- * an ERR_PTR()-encoded error code otherwise.
- */
-void *drm_gem_vram_kmap(struct drm_gem_vram_object *gbo, bool map,
- bool *is_iomem)
-{
- int ret;
- void *virtual;
-
- ret = ttm_bo_reserve(&gbo->bo, true, false, NULL);
- if (ret)
- return ERR_PTR(ret);
- virtual = drm_gem_vram_kmap_locked(gbo, map, is_iomem);
- ttm_bo_unreserve(&gbo->bo);
-
- return virtual;
-}
-EXPORT_SYMBOL(drm_gem_vram_kmap);
-
static void drm_gem_vram_kunmap_locked(struct drm_gem_vram_object *gbo)
{
if (WARN_ON_ONCE(!gbo->kmap_use_count))
@@ -485,22 +457,6 @@ static void drm_gem_vram_kunmap_locked(struct drm_gem_vram_object *gbo)
}
/**
- * drm_gem_vram_kunmap() - Unmaps a GEM VRAM object
- * @gbo: the GEM VRAM object
- */
-void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo)
-{
- int ret;
-
- ret = ttm_bo_reserve(&gbo->bo, false, false, NULL);
- if (WARN_ONCE(ret, "ttm_bo_reserve_failed(): ret=%d\n", ret))
- return;
- drm_gem_vram_kunmap_locked(gbo);
- ttm_bo_unreserve(&gbo->bo);
-}
-EXPORT_SYMBOL(drm_gem_vram_kunmap);
-
-/**
* drm_gem_vram_vmap() - Pins and maps a GEM VRAM object into kernel address
* space
* @gbo: The GEM VRAM object to map
@@ -511,9 +467,6 @@ EXPORT_SYMBOL(drm_gem_vram_kunmap);
* permanently. Call drm_gem_vram_vunmap() with the returned address to
* unmap and unpin the GEM VRAM object.
*
- * If you have special requirements for the pinning or mapping operations,
- * call drm_gem_vram_pin() and drm_gem_vram_kmap() directly.
- *
* Returns:
* The buffer's virtual address on success, or
* an ERR_PTR()-encoded error code otherwise.
@@ -647,7 +600,7 @@ static bool drm_is_gem_vram(struct ttm_buffer_object *bo)
static void drm_gem_vram_bo_driver_evict_flags(struct drm_gem_vram_object *gbo,
struct ttm_placement *pl)
{
- drm_gem_vram_placement(gbo, TTM_PL_FLAG_SYSTEM);
+ drm_gem_vram_placement(gbo, DRM_GEM_VRAM_PL_FLAG_SYSTEM);
*pl = gbo->placement;
}
@@ -967,16 +920,13 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs = {
* TTM TT
*/
-static void backend_func_destroy(struct ttm_tt *tt)
+static void bo_driver_ttm_tt_destroy(struct ttm_bo_device *bdev, struct ttm_tt *tt)
{
+ ttm_tt_destroy_common(bdev, tt);
ttm_tt_fini(tt);
kfree(tt);
}
-static struct ttm_backend_func backend_func = {
- .destroy = backend_func_destroy
-};
-
/*
* TTM BO device
*/
@@ -991,8 +941,6 @@ static struct ttm_tt *bo_driver_ttm_tt_create(struct ttm_buffer_object *bo,
if (!tt)
return NULL;
- tt->func = &backend_func;
-
ret = ttm_tt_init(tt, bo, page_flags);
if (ret < 0)
goto err_ttm_tt_init;
@@ -1042,8 +990,7 @@ static int bo_driver_io_mem_reserve(struct ttm_bo_device *bdev,
case TTM_PL_SYSTEM: /* nothing to do */
break;
case TTM_PL_VRAM:
- mem->bus.offset = mem->start << PAGE_SHIFT;
- mem->bus.base = vmm->vram_base;
+ mem->bus.offset = (mem->start << PAGE_SHIFT) + vmm->vram_base;
mem->bus.is_iomem = true;
break;
default:
@@ -1055,6 +1002,7 @@ static int bo_driver_io_mem_reserve(struct ttm_bo_device *bdev,
static struct ttm_bo_driver bo_driver = {
.ttm_tt_create = bo_driver_ttm_tt_create,
+ .ttm_tt_destroy = bo_driver_ttm_tt_destroy,
.eviction_valuable = ttm_bo_eviction_valuable,
.evict_flags = bo_driver_evict_flags,
.move_notify = bo_driver_move_notify,
@@ -1110,9 +1058,7 @@ static int drm_vram_mm_init(struct drm_vram_mm *vmm, struct drm_device *dev,
return ret;
ret = ttm_range_man_init(&vmm->bdev, TTM_PL_VRAM,
- TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC,
- TTM_PL_FLAG_WC, false,
- vram_size >> PAGE_SHIFT);
+ false, vram_size >> PAGE_SHIFT);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index 8e01caaf95cc..b65865c630b0 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -95,6 +95,7 @@ void drm_minor_release(struct drm_minor *minor);
/* drm_managed.c */
void drm_managed_release(struct drm_device *dev);
+void drmm_add_final_kfree(struct drm_device *dev, void *container);
/* drm_vblank.c */
static inline bool drm_vblank_passed(u64 seq, u64 ref)
diff --git a/drivers/gpu/drm/drm_managed.c b/drivers/gpu/drm/drm_managed.c
index 1e1356560c2e..37d7db6223be 100644
--- a/drivers/gpu/drm/drm_managed.c
+++ b/drivers/gpu/drm/drm_managed.c
@@ -27,7 +27,7 @@
* be done directly with drmm_kmalloc() and the related functions. Everything
* will be released on the final drm_dev_put() in reverse order of how the
* release actions have been added and memory has been allocated since driver
- * loading started with drm_dev_init().
+ * loading started with devm_drm_dev_alloc().
*
* Note that release actions and managed memory can also be added and removed
* during the lifetime of the driver, all the functions are fully concurrent
@@ -125,18 +125,6 @@ static void add_dr(struct drm_device *dev, struct drmres *dr)
dr, dr->node.name, (unsigned long) dr->node.size);
}
-/**
- * drmm_add_final_kfree - add release action for the final kfree()
- * @dev: DRM device
- * @container: pointer to the kmalloc allocation containing @dev
- *
- * Since the allocation containing the struct &drm_device must be allocated
- * before it can be initialized with drm_dev_init() there's no way to allocate
- * that memory with drmm_kmalloc(). To side-step this chicken-egg problem the
- * pointer for this final kfree() must be specified by calling this function. It
- * will be released in the final drm_dev_put() for @dev, after all other release
- * actions installed through drmm_add_action() have been processed.
- */
void drmm_add_final_kfree(struct drm_device *dev, void *container)
{
WARN_ON(dev->managed.final_kfree);
@@ -144,7 +132,6 @@ void drmm_add_final_kfree(struct drm_device *dev, void *container)
WARN_ON(dev + 1 > (struct drm_device *) (container + ksize(container)));
dev->managed.final_kfree = container;
}
-EXPORT_SYMBOL(drmm_add_final_kfree);
int __drmm_add_action(struct drm_device *dev,
drmres_release_t action,
diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c
index b8c7f068a5a4..11fe9ff76fd5 100644
--- a/drivers/gpu/drm/drm_prime.c
+++ b/drivers/gpu/drm/drm_prime.c
@@ -803,9 +803,11 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = {
*
* This is useful for implementing &drm_gem_object_funcs.get_sg_table.
*/
-struct sg_table *drm_prime_pages_to_sg(struct page **pages, unsigned int nr_pages)
+struct sg_table *drm_prime_pages_to_sg(struct drm_device *dev,
+ struct page **pages, unsigned int nr_pages)
{
struct sg_table *sg = NULL;
+ size_t max_segment = 0;
int ret;
sg = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
@@ -814,8 +816,13 @@ struct sg_table *drm_prime_pages_to_sg(struct page **pages, unsigned int nr_page
goto out;
}
- ret = sg_alloc_table_from_pages(sg, pages, nr_pages, 0,
- nr_pages << PAGE_SHIFT, GFP_KERNEL);
+ if (dev)
+ max_segment = dma_max_mapping_size(dev->dev);
+ if (max_segment == 0 || max_segment > SCATTERLIST_MAX_SEGMENT)
+ max_segment = SCATTERLIST_MAX_SEGMENT;
+ ret = __sg_alloc_table_from_pages(sg, pages, nr_pages, 0,
+ nr_pages << PAGE_SHIFT,
+ max_segment, GFP_KERNEL);
if (ret)
goto out;
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index b18e1efbbae1..f135b79593dd 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -674,7 +674,7 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
*
* Note that atomic drivers must call drm_calc_timestamping_constants() before
* enabling a CRTC. The atomic helpers already take care of that in
- * drm_atomic_helper_update_legacy_modeset_state().
+ * drm_atomic_helper_calc_timestamping_constants().
*
* Returns:
*
@@ -819,7 +819,7 @@ EXPORT_SYMBOL(drm_crtc_vblank_helper_get_vblank_timestamp_internal);
*
* Note that atomic drivers must call drm_calc_timestamping_constants() before
* enabling a CRTC. The atomic helpers already take care of that in
- * drm_atomic_helper_update_legacy_modeset_state().
+ * drm_atomic_helper_calc_timestamping_constants().
*
* Returns:
*
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
index eaf1949bc2e4..d1533bdc1335 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
@@ -103,7 +103,8 @@ struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *etnaviv_obj)
int npages = etnaviv_obj->base.size >> PAGE_SHIFT;
struct sg_table *sgt;
- sgt = drm_prime_pages_to_sg(etnaviv_obj->pages, npages);
+ sgt = drm_prime_pages_to_sg(etnaviv_obj->base.dev,
+ etnaviv_obj->pages, npages);
if (IS_ERR(sgt)) {
dev_err(dev->dev, "failed to allocate sgt: %ld\n",
PTR_ERR(sgt));
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c
index 6d9e5c3c4dd5..4aa3426a9ba4 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c
@@ -19,7 +19,7 @@ struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj)
if (WARN_ON(!etnaviv_obj->pages)) /* should have already pinned! */
return ERR_PTR(-EINVAL);
- return drm_prime_pages_to_sg(etnaviv_obj->pages, npages);
+ return drm_prime_pages_to_sg(obj->dev, etnaviv_obj->pages, npages);
}
void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj)
diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
index da02d7e8a8f5..54d9876b5305 100644
--- a/drivers/gpu/drm/gma500/framebuffer.c
+++ b/drivers/gpu/drm/gma500/framebuffer.c
@@ -164,7 +164,7 @@ static int psbfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
return 0;
}
-static struct fb_ops psbfb_ops = {
+static const struct fb_ops psbfb_ops = {
.owner = THIS_MODULE,
DRM_FB_HELPER_DEFAULT_OPS,
.fb_setcolreg = psbfb_setcolreg,
@@ -175,7 +175,7 @@ static struct fb_ops psbfb_ops = {
.fb_sync = psbfb_sync,
};
-static struct fb_ops psbfb_roll_ops = {
+static const struct fb_ops psbfb_roll_ops = {
.owner = THIS_MODULE,
DRM_FB_HELPER_DEFAULT_OPS,
.fb_setcolreg = psbfb_setcolreg,
@@ -186,7 +186,7 @@ static struct fb_ops psbfb_roll_ops = {
.fb_mmap = psbfb_mmap,
};
-static struct fb_ops psbfb_unaccel_ops = {
+static const struct fb_ops psbfb_unaccel_ops = {
.owner = THIS_MODULE,
DRM_FB_HELPER_DEFAULT_OPS,
.fb_setcolreg = psbfb_setcolreg,
diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c
index 303c2d483c6e..88250860f8e4 100644
--- a/drivers/gpu/drm/i810/i810_dma.c
+++ b/drivers/gpu/drm/i810/i810_dma.c
@@ -853,11 +853,11 @@ static void i810_dma_quiescent(struct drm_device *dev)
i810_wait_ring(dev, dev_priv->ring.Size - 8);
}
-static int i810_flush_queue(struct drm_device *dev)
+static void i810_flush_queue(struct drm_device *dev)
{
drm_i810_private_t *dev_priv = dev->dev_private;
struct drm_device_dma *dma = dev->dma;
- int i, ret = 0;
+ int i;
RING_LOCALS;
i810_kernel_lost_context(dev);
@@ -882,7 +882,7 @@ static int i810_flush_queue(struct drm_device *dev)
DRM_DEBUG("still on client\n");
}
- return ret;
+ return;
}
/* Must be called with the lock held */
diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index efc1d0f33fd7..5a9d933e425a 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -13484,12 +13484,6 @@ encoder_retry:
"hw max bpp: %i, pipe bpp: %i, dithering: %i\n",
base_bpp, pipe_config->pipe_bpp, pipe_config->dither);
- /*
- * Make drm_calc_timestamping_constants in
- * drm_atomic_helper_update_legacy_modeset_state() happy
- */
- pipe_config->uapi.adjusted_mode = pipe_config->hw.adjusted_mode;
-
return 0;
}
diff --git a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
index 5daf4a2be422..1f35e71429b4 100644
--- a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
+++ b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
@@ -1617,7 +1617,7 @@ int i915_gem_huge_page_mock_selftests(void)
out_put:
i915_vm_put(&ppgtt->vm);
out_unlock:
- drm_dev_put(&dev_priv->drm);
+ mock_destroy_device(dev_priv);
return err;
}
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
index 99becb86abd3..d3f87dc4eda3 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
@@ -1997,7 +1997,7 @@ int i915_gem_context_mock_selftests(void)
err = i915_subtests(tests, i915);
- drm_dev_put(&i915->drm);
+ mock_destroy_device(i915);
return err;
}
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 2a52b92586b9..0845ce1ae37c 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_dmabuf.c
@@ -272,7 +272,7 @@ int i915_gem_dmabuf_mock_selftests(void)
err = i915_subtests(tests, i915);
- drm_dev_put(&i915->drm);
+ mock_destroy_device(i915);
return err;
}
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_object.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_object.c
index faa5b6d91795..bf853c40ec65 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_object.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_object.c
@@ -85,7 +85,7 @@ int i915_gem_object_mock_selftests(void)
err = i915_subtests(tests, i915);
- drm_dev_put(&i915->drm);
+ mock_destroy_device(i915);
return err;
}
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_phys.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_phys.c
index a94243dc4c5c..8cee68c6a6dc 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_phys.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_phys.c
@@ -73,6 +73,6 @@ int i915_gem_phys_mock_selftests(void)
err = i915_subtests(tests, i915);
- drm_dev_put(&i915->drm);
+ mock_destroy_device(i915);
return err;
}
diff --git a/drivers/gpu/drm/i915/gt/selftest_timeline.c b/drivers/gpu/drm/i915/gt/selftest_timeline.c
index 96d164a3841d..19c2cb166e7c 100644
--- a/drivers/gpu/drm/i915/gt/selftest_timeline.c
+++ b/drivers/gpu/drm/i915/gt/selftest_timeline.c
@@ -158,7 +158,7 @@ out:
__mock_hwsp_record(&state, na, NULL);
kfree(state.history);
err_put:
- drm_dev_put(&i915->drm);
+ mock_destroy_device(i915);
return err;
}
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
index 028baae9631f..f88473d396f4 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
@@ -536,7 +536,7 @@ int i915_gem_evict_mock_selftests(void)
with_intel_runtime_pm(&i915->runtime_pm, wakeref)
err = i915_subtests(tests, &i915->gt);
- drm_dev_put(&i915->drm);
+ mock_destroy_device(i915);
return err;
}
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
index af8205a2bd8f..c53a222e3dec 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
@@ -1727,7 +1727,7 @@ int i915_gem_gtt_mock_selftests(void)
mock_fini_ggtt(ggtt);
kfree(ggtt);
out_put:
- drm_dev_put(&i915->drm);
+ mock_destroy_device(i915);
return err;
}
diff --git a/drivers/gpu/drm/i915/selftests/i915_request.c b/drivers/gpu/drm/i915/selftests/i915_request.c
index 3092ca763789..64bbb8288249 100644
--- a/drivers/gpu/drm/i915/selftests/i915_request.c
+++ b/drivers/gpu/drm/i915/selftests/i915_request.c
@@ -527,7 +527,7 @@ int i915_request_mock_selftests(void)
with_intel_runtime_pm(&i915->runtime_pm, wakeref)
err = i915_subtests(tests, i915);
- drm_dev_put(&i915->drm);
+ mock_destroy_device(i915);
return err;
}
diff --git a/drivers/gpu/drm/i915/selftests/i915_vma.c b/drivers/gpu/drm/i915/selftests/i915_vma.c
index 88c5e9acb84c..1b6125e4c1ac 100644
--- a/drivers/gpu/drm/i915/selftests/i915_vma.c
+++ b/drivers/gpu/drm/i915/selftests/i915_vma.c
@@ -841,7 +841,7 @@ int i915_vma_mock_selftests(void)
mock_fini_ggtt(ggtt);
kfree(ggtt);
out_put:
- drm_dev_put(&i915->drm);
+ mock_destroy_device(i915);
return err;
}
diff --git a/drivers/gpu/drm/i915/selftests/intel_memory_region.c b/drivers/gpu/drm/i915/selftests/intel_memory_region.c
index 93a38a323584..334b0648e253 100644
--- a/drivers/gpu/drm/i915/selftests/intel_memory_region.c
+++ b/drivers/gpu/drm/i915/selftests/intel_memory_region.c
@@ -791,7 +791,7 @@ int intel_memory_region_mock_selftests(void)
intel_memory_region_put(mem);
out_unref:
- drm_dev_put(&i915->drm);
+ mock_destroy_device(i915);
return err;
}
diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
index 397c313a8b69..b6c42fd872ad 100644
--- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c
+++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
@@ -79,8 +79,6 @@ static void mock_device_release(struct drm_device *dev)
out:
i915_params_free(&i915->params);
- put_device(&i915->drm.pdev->dev);
- i915->drm.pdev = NULL;
}
static struct drm_driver mock_driver = {
@@ -123,17 +121,10 @@ struct drm_i915_private *mock_gem_device(void)
#endif
struct drm_i915_private *i915;
struct pci_dev *pdev;
- int err;
pdev = kzalloc(sizeof(*pdev), GFP_KERNEL);
if (!pdev)
return NULL;
- i915 = kzalloc(sizeof(*i915), GFP_KERNEL);
- if (!i915) {
- kfree(pdev);
- return NULL;
- }
-
device_initialize(&pdev->dev);
pdev->class = PCI_BASE_CLASS_DISPLAY << 16;
pdev->dev.release = release_dev;
@@ -144,8 +135,23 @@ struct drm_i915_private *mock_gem_device(void)
/* HACK to disable iommu for the fake device; force identity mapping */
pdev->dev.iommu = &fake_iommu;
#endif
+ if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) {
+ put_device(&pdev->dev);
+ return NULL;
+ }
+
+ i915 = devm_drm_dev_alloc(&pdev->dev, &mock_driver,
+ struct drm_i915_private, drm);
+ if (IS_ERR(i915)) {
+ pr_err("Failed to allocate mock GEM device: err=%ld\n", PTR_ERR(i915));
+ devres_release_group(&pdev->dev, NULL);
+ put_device(&pdev->dev);
+
+ return NULL;
+ }
pci_set_drvdata(pdev, i915);
+ i915->drm.pdev = pdev;
dev_pm_domain_set(&pdev->dev, &pm_domain);
pm_runtime_enable(&pdev->dev);
@@ -153,16 +159,6 @@ struct drm_i915_private *mock_gem_device(void)
if (pm_runtime_enabled(&pdev->dev))
WARN_ON(pm_runtime_get_sync(&pdev->dev));
- err = drm_dev_init(&i915->drm, &mock_driver, &pdev->dev);
- if (err) {
- pr_err("Failed to initialise mock GEM device: err=%d\n", err);
- put_device(&pdev->dev);
- kfree(i915);
-
- return NULL;
- }
- i915->drm.pdev = pdev;
- drmm_add_final_kfree(&i915->drm, i915);
i915_params_copy(&i915->params, &i915_modparams);
@@ -222,7 +218,15 @@ err_drv:
intel_gt_driver_late_release(&i915->gt);
intel_memory_regions_driver_release(i915);
drm_mode_config_cleanup(&i915->drm);
- drm_dev_put(&i915->drm);
+ mock_destroy_device(i915);
return NULL;
}
+
+void mock_destroy_device(struct drm_i915_private *i915)
+{
+ struct device *dev = i915->drm.dev;
+
+ devres_release_group(dev, NULL);
+ put_device(dev);
+}
diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.h b/drivers/gpu/drm/i915/selftests/mock_gem_device.h
index b5dc4e394555..953cfe4fab34 100644
--- a/drivers/gpu/drm/i915/selftests/mock_gem_device.h
+++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.h
@@ -7,4 +7,6 @@ struct drm_i915_private;
struct drm_i915_private *mock_gem_device(void);
void mock_device_flush(struct drm_i915_private *i915);
+void mock_destroy_device(struct drm_i915_private *i915);
+
#endif /* !__MOCK_GEM_DEVICE_H__ */
diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig
index 207bf7409dfb..6231048aa5aa 100644
--- a/drivers/gpu/drm/imx/Kconfig
+++ b/drivers/gpu/drm/imx/Kconfig
@@ -39,3 +39,5 @@ config DRM_IMX_HDMI
depends on DRM_IMX
help
Choose this if you want to use HDMI on i.MX6.
+
+source "drivers/gpu/drm/imx/dcss/Kconfig"
diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile
index 21cdcc2faabc..b644deffe948 100644
--- a/drivers/gpu/drm/imx/Makefile
+++ b/drivers/gpu/drm/imx/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o
obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o
+obj-$(CONFIG_DRM_IMX_DCSS) += dcss/
diff --git a/drivers/gpu/drm/imx/dcss/Kconfig b/drivers/gpu/drm/imx/dcss/Kconfig
new file mode 100644
index 000000000000..2b17a964ff05
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcss/Kconfig
@@ -0,0 +1,9 @@
+config DRM_IMX_DCSS
+ tristate "i.MX8MQ DCSS"
+ select IMX_IRQSTEER
+ select DRM_KMS_CMA_HELPER
+ select VIDEOMODE_HELPERS
+ depends on DRM && ARCH_MXC && ARM64
+ help
+ Choose this if you have a NXP i.MX8MQ based system and want to use the
+ Display Controller Subsystem. This option enables DCSS support.
diff --git a/drivers/gpu/drm/imx/dcss/Makefile b/drivers/gpu/drm/imx/dcss/Makefile
new file mode 100644
index 000000000000..8c7c8da42792
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcss/Makefile
@@ -0,0 +1,6 @@
+imx-dcss-objs := dcss-drv.o dcss-dev.o dcss-blkctl.o dcss-ctxld.o dcss-dtg.o \
+ dcss-ss.o dcss-dpr.o dcss-scaler.o dcss-kms.o dcss-crtc.o \
+ dcss-plane.o
+
+obj-$(CONFIG_DRM_IMX_DCSS) += imx-dcss.o
+
diff --git a/drivers/gpu/drm/imx/dcss/dcss-blkctl.c b/drivers/gpu/drm/imx/dcss/dcss-blkctl.c
new file mode 100644
index 000000000000..c9b54bb2692d
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcss/dcss-blkctl.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#include "dcss-dev.h"
+
+#define DCSS_BLKCTL_RESET_CTRL 0x00
+#define B_CLK_RESETN BIT(0)
+#define APB_CLK_RESETN BIT(1)
+#define P_CLK_RESETN BIT(2)
+#define RTR_CLK_RESETN BIT(4)
+#define DCSS_BLKCTL_CONTROL0 0x10
+#define HDMI_MIPI_CLK_SEL BIT(0)
+#define DISPMIX_REFCLK_SEL_POS 4
+#define DISPMIX_REFCLK_SEL_MASK GENMASK(5, 4)
+#define DISPMIX_PIXCLK_SEL BIT(8)
+#define HDMI_SRC_SECURE_EN BIT(16)
+
+struct dcss_blkctl {
+ struct dcss_dev *dcss;
+ void __iomem *base_reg;
+};
+
+void dcss_blkctl_cfg(struct dcss_blkctl *blkctl)
+{
+ if (blkctl->dcss->hdmi_output)
+ dcss_writel(0, blkctl->base_reg + DCSS_BLKCTL_CONTROL0);
+ else
+ dcss_writel(DISPMIX_PIXCLK_SEL,
+ blkctl->base_reg + DCSS_BLKCTL_CONTROL0);
+
+ dcss_set(B_CLK_RESETN | APB_CLK_RESETN | P_CLK_RESETN | RTR_CLK_RESETN,
+ blkctl->base_reg + DCSS_BLKCTL_RESET_CTRL);
+}
+
+int dcss_blkctl_init(struct dcss_dev *dcss, unsigned long blkctl_base)
+{
+ struct dcss_blkctl *blkctl;
+
+ blkctl = kzalloc(sizeof(*blkctl), GFP_KERNEL);
+ if (!blkctl)
+ return -ENOMEM;
+
+ blkctl->base_reg = ioremap(blkctl_base, SZ_4K);
+ if (!blkctl->base_reg) {
+ dev_err(dcss->dev, "unable to remap BLK CTRL base\n");
+ kfree(blkctl);
+ return -ENOMEM;
+ }
+
+ dcss->blkctl = blkctl;
+ blkctl->dcss = dcss;
+
+ dcss_blkctl_cfg(blkctl);
+
+ return 0;
+}
+
+void dcss_blkctl_exit(struct dcss_blkctl *blkctl)
+{
+ if (blkctl->base_reg)
+ iounmap(blkctl->base_reg);
+
+ kfree(blkctl);
+}
diff --git a/drivers/gpu/drm/imx/dcss/dcss-crtc.c b/drivers/gpu/drm/imx/dcss/dcss-crtc.c
new file mode 100644
index 000000000000..36abff0890b2
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcss/dcss-crtc.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_vblank.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "dcss-dev.h"
+#include "dcss-kms.h"
+
+static int dcss_enable_vblank(struct drm_crtc *crtc)
+{
+ struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
+ base);
+ struct dcss_dev *dcss = crtc->dev->dev_private;
+
+ dcss_dtg_vblank_irq_enable(dcss->dtg, true);
+
+ dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true);
+
+ enable_irq(dcss_crtc->irq);
+
+ return 0;
+}
+
+static void dcss_disable_vblank(struct drm_crtc *crtc)
+{
+ struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
+ base);
+ struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
+
+ disable_irq_nosync(dcss_crtc->irq);
+
+ dcss_dtg_vblank_irq_enable(dcss->dtg, false);
+
+ if (dcss_crtc->disable_ctxld_kick_irq)
+ dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, false);
+}
+
+static const struct drm_crtc_funcs dcss_crtc_funcs = {
+ .set_config = drm_atomic_helper_set_config,
+ .destroy = drm_crtc_cleanup,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = drm_atomic_helper_crtc_reset,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .enable_vblank = dcss_enable_vblank,
+ .disable_vblank = dcss_disable_vblank,
+};
+
+static void dcss_crtc_atomic_begin(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_crtc_state)
+{
+ drm_crtc_vblank_on(crtc);
+}
+
+static void dcss_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_crtc_state)
+{
+ struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
+ base);
+ struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
+
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (crtc->state->event) {
+ WARN_ON(drm_crtc_vblank_get(crtc));
+ drm_crtc_arm_vblank_event(crtc, crtc->state->event);
+ crtc->state->event = NULL;
+ }
+ spin_unlock_irq(&crtc->dev->event_lock);
+
+ if (dcss_dtg_is_enabled(dcss->dtg))
+ dcss_ctxld_enable(dcss->ctxld);
+}
+
+static void dcss_crtc_atomic_enable(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_crtc_state)
+{
+ struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
+ base);
+ struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ struct drm_display_mode *old_mode = &old_crtc_state->adjusted_mode;
+ struct videomode vm;
+
+ drm_display_mode_to_videomode(mode, &vm);
+
+ pm_runtime_get_sync(dcss->dev);
+
+ vm.pixelclock = mode->crtc_clock * 1000;
+
+ dcss_ss_subsam_set(dcss->ss);
+ dcss_dtg_css_set(dcss->dtg);
+
+ if (!drm_mode_equal(mode, old_mode) || !old_crtc_state->active) {
+ dcss_dtg_sync_set(dcss->dtg, &vm);
+ dcss_ss_sync_set(dcss->ss, &vm,
+ mode->flags & DRM_MODE_FLAG_PHSYNC,
+ mode->flags & DRM_MODE_FLAG_PVSYNC);
+ }
+
+ dcss_enable_dtg_and_ss(dcss);
+
+ dcss_ctxld_enable(dcss->ctxld);
+
+ /* Allow CTXLD kick interrupt to be disabled when VBLANK is disabled. */
+ dcss_crtc->disable_ctxld_kick_irq = true;
+}
+
+static void dcss_crtc_atomic_disable(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_crtc_state)
+{
+ struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
+ base);
+ struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ struct drm_display_mode *old_mode = &old_crtc_state->adjusted_mode;
+
+ drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
+
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (crtc->state->event) {
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
+ crtc->state->event = NULL;
+ }
+ spin_unlock_irq(&crtc->dev->event_lock);
+
+ dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true);
+
+ reinit_completion(&dcss->disable_completion);
+
+ dcss_disable_dtg_and_ss(dcss);
+
+ dcss_ctxld_enable(dcss->ctxld);
+
+ if (!drm_mode_equal(mode, old_mode) || !crtc->state->active)
+ if (!wait_for_completion_timeout(&dcss->disable_completion,
+ msecs_to_jiffies(100)))
+ dev_err(dcss->dev, "Shutting off DTG timed out.\n");
+
+ /*
+ * Do not shut off CTXLD kick interrupt when shutting VBLANK off. It
+ * will be needed to commit the last changes, before going to suspend.
+ */
+ dcss_crtc->disable_ctxld_kick_irq = false;
+
+ drm_crtc_vblank_off(crtc);
+
+ pm_runtime_mark_last_busy(dcss->dev);
+ pm_runtime_put_autosuspend(dcss->dev);
+}
+
+static const struct drm_crtc_helper_funcs dcss_helper_funcs = {
+ .atomic_begin = dcss_crtc_atomic_begin,
+ .atomic_flush = dcss_crtc_atomic_flush,
+ .atomic_enable = dcss_crtc_atomic_enable,
+ .atomic_disable = dcss_crtc_atomic_disable,
+};
+
+static irqreturn_t dcss_crtc_irq_handler(int irq, void *dev_id)
+{
+ struct dcss_crtc *dcss_crtc = dev_id;
+ struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
+
+ if (!dcss_dtg_vblank_irq_valid(dcss->dtg))
+ return IRQ_NONE;
+
+ if (dcss_ctxld_is_flushed(dcss->ctxld))
+ drm_crtc_handle_vblank(&dcss_crtc->base);
+
+ dcss_dtg_vblank_irq_clear(dcss->dtg);
+
+ return IRQ_HANDLED;
+}
+
+int dcss_crtc_init(struct dcss_crtc *crtc, struct drm_device *drm)
+{
+ struct dcss_dev *dcss = drm->dev_private;
+ struct platform_device *pdev = to_platform_device(dcss->dev);
+ int ret;
+
+ crtc->plane[0] = dcss_plane_init(drm, drm_crtc_mask(&crtc->base),
+ DRM_PLANE_TYPE_PRIMARY, 0);
+ if (IS_ERR(crtc->plane[0]))
+ return PTR_ERR(crtc->plane[0]);
+
+ crtc->base.port = dcss->of_port;
+
+ drm_crtc_helper_add(&crtc->base, &dcss_helper_funcs);
+ ret = drm_crtc_init_with_planes(drm, &crtc->base, &crtc->plane[0]->base,
+ NULL, &dcss_crtc_funcs, NULL);
+ if (ret) {
+ dev_err(dcss->dev, "failed to init crtc\n");
+ return ret;
+ }
+
+ crtc->irq = platform_get_irq_byname(pdev, "vblank");
+ if (crtc->irq < 0)
+ return crtc->irq;
+
+ ret = request_irq(crtc->irq, dcss_crtc_irq_handler,
+ 0, "dcss_drm", crtc);
+ if (ret) {
+ dev_err(dcss->dev, "irq request failed with %d.\n", ret);
+ return ret;
+ }
+
+ disable_irq(crtc->irq);
+
+ return 0;
+}
+
+void dcss_crtc_deinit(struct dcss_crtc *crtc, struct drm_device *drm)
+{
+ free_irq(crtc->irq, crtc);
+}
diff --git a/drivers/gpu/drm/imx/dcss/dcss-ctxld.c b/drivers/gpu/drm/imx/dcss/dcss-ctxld.c
new file mode 100644
index 000000000000..3a84cb3209c4
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcss/dcss-ctxld.c
@@ -0,0 +1,424 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "dcss-dev.h"
+
+#define DCSS_CTXLD_CONTROL_STATUS 0x0
+#define CTXLD_ENABLE BIT(0)
+#define ARB_SEL BIT(1)
+#define RD_ERR_EN BIT(2)
+#define DB_COMP_EN BIT(3)
+#define SB_HP_COMP_EN BIT(4)
+#define SB_LP_COMP_EN BIT(5)
+#define DB_PEND_SB_REC_EN BIT(6)
+#define SB_PEND_DISP_ACTIVE_EN BIT(7)
+#define AHB_ERR_EN BIT(8)
+#define RD_ERR BIT(16)
+#define DB_COMP BIT(17)
+#define SB_HP_COMP BIT(18)
+#define SB_LP_COMP BIT(19)
+#define DB_PEND_SB_REC BIT(20)
+#define SB_PEND_DISP_ACTIVE BIT(21)
+#define AHB_ERR BIT(22)
+#define DCSS_CTXLD_DB_BASE_ADDR 0x10
+#define DCSS_CTXLD_DB_COUNT 0x14
+#define DCSS_CTXLD_SB_BASE_ADDR 0x18
+#define DCSS_CTXLD_SB_COUNT 0x1C
+#define SB_HP_COUNT_POS 0
+#define SB_HP_COUNT_MASK 0xffff
+#define SB_LP_COUNT_POS 16
+#define SB_LP_COUNT_MASK 0xffff0000
+#define DCSS_AHB_ERR_ADDR 0x20
+
+#define CTXLD_IRQ_COMPLETION (DB_COMP | SB_HP_COMP | SB_LP_COMP)
+#define CTXLD_IRQ_ERROR (RD_ERR | DB_PEND_SB_REC | AHB_ERR)
+
+/* The following sizes are in context loader entries, 8 bytes each. */
+#define CTXLD_DB_CTX_ENTRIES 1024 /* max 65536 */
+#define CTXLD_SB_LP_CTX_ENTRIES 10240 /* max 65536 */
+#define CTXLD_SB_HP_CTX_ENTRIES 20000 /* max 65536 */
+#define CTXLD_SB_CTX_ENTRIES (CTXLD_SB_LP_CTX_ENTRIES + \
+ CTXLD_SB_HP_CTX_ENTRIES)
+
+/* Sizes, in entries, of the DB, SB_HP and SB_LP context regions. */
+static u16 dcss_ctxld_ctx_size[3] = {
+ CTXLD_DB_CTX_ENTRIES,
+ CTXLD_SB_HP_CTX_ENTRIES,
+ CTXLD_SB_LP_CTX_ENTRIES
+};
+
+/* this represents an entry in the context loader map */
+struct dcss_ctxld_item {
+ u32 val;
+ u32 ofs;
+};
+
+#define CTX_ITEM_SIZE sizeof(struct dcss_ctxld_item)
+
+struct dcss_ctxld {
+ struct device *dev;
+ void __iomem *ctxld_reg;
+ int irq;
+ bool irq_en;
+
+ struct dcss_ctxld_item *db[2];
+ struct dcss_ctxld_item *sb_hp[2];
+ struct dcss_ctxld_item *sb_lp[2];
+
+ dma_addr_t db_paddr[2];
+ dma_addr_t sb_paddr[2];
+
+ u16 ctx_size[2][3]; /* holds the sizes of DB, SB_HP and SB_LP ctx */
+ u8 current_ctx;
+
+ bool in_use;
+ bool armed;
+
+ spinlock_t lock; /* protects concurent access to private data */
+};
+
+static irqreturn_t dcss_ctxld_irq_handler(int irq, void *data)
+{
+ struct dcss_ctxld *ctxld = data;
+ struct dcss_dev *dcss = dcss_drv_dev_to_dcss(ctxld->dev);
+ u32 irq_status;
+
+ irq_status = dcss_readl(ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
+
+ if (irq_status & CTXLD_IRQ_COMPLETION &&
+ !(irq_status & CTXLD_ENABLE) && ctxld->in_use) {
+ ctxld->in_use = false;
+
+ if (dcss && dcss->disable_callback)
+ dcss->disable_callback(dcss);
+ } else if (irq_status & CTXLD_IRQ_ERROR) {
+ /*
+ * Except for throwing an error message and clearing the status
+ * register, there's not much we can do here.
+ */
+ dev_err(ctxld->dev, "ctxld: error encountered: %08x\n",
+ irq_status);
+ dev_err(ctxld->dev, "ctxld: db=%d, sb_hp=%d, sb_lp=%d\n",
+ ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_DB],
+ ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_SB_HP],
+ ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_SB_LP]);
+ }
+
+ dcss_clr(irq_status & (CTXLD_IRQ_ERROR | CTXLD_IRQ_COMPLETION),
+ ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
+
+ return IRQ_HANDLED;
+}
+
+static int dcss_ctxld_irq_config(struct dcss_ctxld *ctxld,
+ struct platform_device *pdev)
+{
+ int ret;
+
+ ctxld->irq = platform_get_irq_byname(pdev, "ctxld");
+ if (ctxld->irq < 0)
+ return ctxld->irq;
+
+ ret = request_irq(ctxld->irq, dcss_ctxld_irq_handler,
+ 0, "dcss_ctxld", ctxld);
+ if (ret) {
+ dev_err(ctxld->dev, "ctxld: irq request failed.\n");
+ return ret;
+ }
+
+ ctxld->irq_en = true;
+
+ return 0;
+}
+
+static void dcss_ctxld_hw_cfg(struct dcss_ctxld *ctxld)
+{
+ dcss_writel(RD_ERR_EN | SB_HP_COMP_EN |
+ DB_PEND_SB_REC_EN | AHB_ERR_EN | RD_ERR | AHB_ERR,
+ ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
+}
+
+static void dcss_ctxld_free_ctx(struct dcss_ctxld *ctxld)
+{
+ struct dcss_ctxld_item *ctx;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (ctxld->db[i]) {
+ dma_free_coherent(ctxld->dev,
+ CTXLD_DB_CTX_ENTRIES * sizeof(*ctx),
+ ctxld->db[i], ctxld->db_paddr[i]);
+ ctxld->db[i] = NULL;
+ ctxld->db_paddr[i] = 0;
+ }
+
+ if (ctxld->sb_hp[i]) {
+ dma_free_coherent(ctxld->dev,
+ CTXLD_SB_CTX_ENTRIES * sizeof(*ctx),
+ ctxld->sb_hp[i], ctxld->sb_paddr[i]);
+ ctxld->sb_hp[i] = NULL;
+ ctxld->sb_paddr[i] = 0;
+ }
+ }
+}
+
+static int dcss_ctxld_alloc_ctx(struct dcss_ctxld *ctxld)
+{
+ struct dcss_ctxld_item *ctx;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ ctx = dma_alloc_coherent(ctxld->dev,
+ CTXLD_DB_CTX_ENTRIES * sizeof(*ctx),
+ &ctxld->db_paddr[i], GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctxld->db[i] = ctx;
+
+ ctx = dma_alloc_coherent(ctxld->dev,
+ CTXLD_SB_CTX_ENTRIES * sizeof(*ctx),
+ &ctxld->sb_paddr[i], GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctxld->sb_hp[i] = ctx;
+ ctxld->sb_lp[i] = ctx + CTXLD_SB_HP_CTX_ENTRIES;
+ }
+
+ return 0;
+}
+
+int dcss_ctxld_init(struct dcss_dev *dcss, unsigned long ctxld_base)
+{
+ struct dcss_ctxld *ctxld;
+ int ret;
+
+ ctxld = kzalloc(sizeof(*ctxld), GFP_KERNEL);
+ if (!ctxld)
+ return -ENOMEM;
+
+ dcss->ctxld = ctxld;
+ ctxld->dev = dcss->dev;
+
+ spin_lock_init(&ctxld->lock);
+
+ ret = dcss_ctxld_alloc_ctx(ctxld);
+ if (ret) {
+ dev_err(dcss->dev, "ctxld: cannot allocate context memory.\n");
+ goto err;
+ }
+
+ ctxld->ctxld_reg = ioremap(ctxld_base, SZ_4K);
+ if (!ctxld->ctxld_reg) {
+ dev_err(dcss->dev, "ctxld: unable to remap ctxld base\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = dcss_ctxld_irq_config(ctxld, to_platform_device(dcss->dev));
+ if (ret)
+ goto err_irq;
+
+ dcss_ctxld_hw_cfg(ctxld);
+
+ return 0;
+
+err_irq:
+ iounmap(ctxld->ctxld_reg);
+
+err:
+ dcss_ctxld_free_ctx(ctxld);
+ kfree(ctxld);
+
+ return ret;
+}
+
+void dcss_ctxld_exit(struct dcss_ctxld *ctxld)
+{
+ free_irq(ctxld->irq, ctxld);
+
+ if (ctxld->ctxld_reg)
+ iounmap(ctxld->ctxld_reg);
+
+ dcss_ctxld_free_ctx(ctxld);
+ kfree(ctxld);
+}
+
+static int dcss_ctxld_enable_locked(struct dcss_ctxld *ctxld)
+{
+ int curr_ctx = ctxld->current_ctx;
+ u32 db_base, sb_base, sb_count;
+ u32 sb_hp_cnt, sb_lp_cnt, db_cnt;
+ struct dcss_dev *dcss = dcss_drv_dev_to_dcss(ctxld->dev);
+
+ if (!dcss)
+ return 0;
+
+ dcss_dpr_write_sysctrl(dcss->dpr);
+
+ dcss_scaler_write_sclctrl(dcss->scaler);
+
+ sb_hp_cnt = ctxld->ctx_size[curr_ctx][CTX_SB_HP];
+ sb_lp_cnt = ctxld->ctx_size[curr_ctx][CTX_SB_LP];
+ db_cnt = ctxld->ctx_size[curr_ctx][CTX_DB];
+
+ /* make sure SB_LP context area comes after SB_HP */
+ if (sb_lp_cnt &&
+ ctxld->sb_lp[curr_ctx] != ctxld->sb_hp[curr_ctx] + sb_hp_cnt) {
+ struct dcss_ctxld_item *sb_lp_adjusted;
+
+ sb_lp_adjusted = ctxld->sb_hp[curr_ctx] + sb_hp_cnt;
+
+ memcpy(sb_lp_adjusted, ctxld->sb_lp[curr_ctx],
+ sb_lp_cnt * CTX_ITEM_SIZE);
+ }
+
+ db_base = db_cnt ? ctxld->db_paddr[curr_ctx] : 0;
+
+ dcss_writel(db_base, ctxld->ctxld_reg + DCSS_CTXLD_DB_BASE_ADDR);
+ dcss_writel(db_cnt, ctxld->ctxld_reg + DCSS_CTXLD_DB_COUNT);
+
+ if (sb_hp_cnt)
+ sb_count = ((sb_hp_cnt << SB_HP_COUNT_POS) & SB_HP_COUNT_MASK) |
+ ((sb_lp_cnt << SB_LP_COUNT_POS) & SB_LP_COUNT_MASK);
+ else
+ sb_count = (sb_lp_cnt << SB_HP_COUNT_POS) & SB_HP_COUNT_MASK;
+
+ sb_base = sb_count ? ctxld->sb_paddr[curr_ctx] : 0;
+
+ dcss_writel(sb_base, ctxld->ctxld_reg + DCSS_CTXLD_SB_BASE_ADDR);
+ dcss_writel(sb_count, ctxld->ctxld_reg + DCSS_CTXLD_SB_COUNT);
+
+ /* enable the context loader */
+ dcss_set(CTXLD_ENABLE, ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
+
+ ctxld->in_use = true;
+
+ /*
+ * Toggle the current context to the alternate one so that any updates
+ * in the modules' settings take place there.
+ */
+ ctxld->current_ctx ^= 1;
+
+ ctxld->ctx_size[ctxld->current_ctx][CTX_DB] = 0;
+ ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] = 0;
+ ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] = 0;
+
+ return 0;
+}
+
+int dcss_ctxld_enable(struct dcss_ctxld *ctxld)
+{
+ spin_lock_irq(&ctxld->lock);
+ ctxld->armed = true;
+ spin_unlock_irq(&ctxld->lock);
+
+ return 0;
+}
+
+void dcss_ctxld_kick(struct dcss_ctxld *ctxld)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctxld->lock, flags);
+ if (ctxld->armed && !ctxld->in_use) {
+ ctxld->armed = false;
+ dcss_ctxld_enable_locked(ctxld);
+ }
+ spin_unlock_irqrestore(&ctxld->lock, flags);
+}
+
+void dcss_ctxld_write_irqsafe(struct dcss_ctxld *ctxld, u32 ctx_id, u32 val,
+ u32 reg_ofs)
+{
+ int curr_ctx = ctxld->current_ctx;
+ struct dcss_ctxld_item *ctx[] = {
+ [CTX_DB] = ctxld->db[curr_ctx],
+ [CTX_SB_HP] = ctxld->sb_hp[curr_ctx],
+ [CTX_SB_LP] = ctxld->sb_lp[curr_ctx]
+ };
+ int item_idx = ctxld->ctx_size[curr_ctx][ctx_id];
+
+ if (item_idx + 1 > dcss_ctxld_ctx_size[ctx_id]) {
+ WARN_ON(1);
+ return;
+ }
+
+ ctx[ctx_id][item_idx].val = val;
+ ctx[ctx_id][item_idx].ofs = reg_ofs;
+ ctxld->ctx_size[curr_ctx][ctx_id] += 1;
+}
+
+void dcss_ctxld_write(struct dcss_ctxld *ctxld, u32 ctx_id,
+ u32 val, u32 reg_ofs)
+{
+ spin_lock_irq(&ctxld->lock);
+ dcss_ctxld_write_irqsafe(ctxld, ctx_id, val, reg_ofs);
+ spin_unlock_irq(&ctxld->lock);
+}
+
+bool dcss_ctxld_is_flushed(struct dcss_ctxld *ctxld)
+{
+ return ctxld->ctx_size[ctxld->current_ctx][CTX_DB] == 0 &&
+ ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] == 0 &&
+ ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] == 0;
+}
+
+int dcss_ctxld_resume(struct dcss_ctxld *ctxld)
+{
+ dcss_ctxld_hw_cfg(ctxld);
+
+ if (!ctxld->irq_en) {
+ enable_irq(ctxld->irq);
+ ctxld->irq_en = true;
+ }
+
+ return 0;
+}
+
+int dcss_ctxld_suspend(struct dcss_ctxld *ctxld)
+{
+ int ret = 0;
+ unsigned long timeout = jiffies + msecs_to_jiffies(500);
+
+ if (!dcss_ctxld_is_flushed(ctxld)) {
+ dcss_ctxld_kick(ctxld);
+
+ while (!time_after(jiffies, timeout) && ctxld->in_use)
+ msleep(20);
+
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ }
+
+ spin_lock_irq(&ctxld->lock);
+
+ if (ctxld->irq_en) {
+ disable_irq_nosync(ctxld->irq);
+ ctxld->irq_en = false;
+ }
+
+ /* reset context region and sizes */
+ ctxld->current_ctx = 0;
+ ctxld->ctx_size[0][CTX_DB] = 0;
+ ctxld->ctx_size[0][CTX_SB_HP] = 0;
+ ctxld->ctx_size[0][CTX_SB_LP] = 0;
+
+ spin_unlock_irq(&ctxld->lock);
+
+ return ret;
+}
+
+void dcss_ctxld_assert_locked(struct dcss_ctxld *ctxld)
+{
+ lockdep_assert_held(&ctxld->lock);
+}
diff --git a/drivers/gpu/drm/imx/dcss/dcss-dev.c b/drivers/gpu/drm/imx/dcss/dcss-dev.c
new file mode 100644
index 000000000000..c849533ca83e
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcss/dcss-dev.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <linux/clk.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_device.h>
+#include <drm/drm_modeset_helper.h>
+
+#include "dcss-dev.h"
+#include "dcss-kms.h"
+
+static void dcss_clocks_enable(struct dcss_dev *dcss)
+{
+ clk_prepare_enable(dcss->axi_clk);
+ clk_prepare_enable(dcss->apb_clk);
+ clk_prepare_enable(dcss->rtrm_clk);
+ clk_prepare_enable(dcss->dtrc_clk);
+ clk_prepare_enable(dcss->pix_clk);
+}
+
+static void dcss_clocks_disable(struct dcss_dev *dcss)
+{
+ clk_disable_unprepare(dcss->pix_clk);
+ clk_disable_unprepare(dcss->dtrc_clk);
+ clk_disable_unprepare(dcss->rtrm_clk);
+ clk_disable_unprepare(dcss->apb_clk);
+ clk_disable_unprepare(dcss->axi_clk);
+}
+
+static void dcss_disable_dtg_and_ss_cb(void *data)
+{
+ struct dcss_dev *dcss = data;
+
+ dcss->disable_callback = NULL;
+
+ dcss_ss_shutoff(dcss->ss);
+ dcss_dtg_shutoff(dcss->dtg);
+
+ complete(&dcss->disable_completion);
+}
+
+void dcss_disable_dtg_and_ss(struct dcss_dev *dcss)
+{
+ dcss->disable_callback = dcss_disable_dtg_and_ss_cb;
+}
+
+void dcss_enable_dtg_and_ss(struct dcss_dev *dcss)
+{
+ if (dcss->disable_callback)
+ dcss->disable_callback = NULL;
+
+ dcss_dtg_enable(dcss->dtg);
+ dcss_ss_enable(dcss->ss);
+}
+
+static int dcss_submodules_init(struct dcss_dev *dcss)
+{
+ int ret = 0;
+ u32 base_addr = dcss->start_addr;
+ const struct dcss_type_data *devtype = dcss->devtype;
+
+ dcss_clocks_enable(dcss);
+
+ ret = dcss_blkctl_init(dcss, base_addr + devtype->blkctl_ofs);
+ if (ret)
+ return ret;
+
+ ret = dcss_ctxld_init(dcss, base_addr + devtype->ctxld_ofs);
+ if (ret)
+ goto ctxld_err;
+
+ ret = dcss_dtg_init(dcss, base_addr + devtype->dtg_ofs);
+ if (ret)
+ goto dtg_err;
+
+ ret = dcss_ss_init(dcss, base_addr + devtype->ss_ofs);
+ if (ret)
+ goto ss_err;
+
+ ret = dcss_dpr_init(dcss, base_addr + devtype->dpr_ofs);
+ if (ret)
+ goto dpr_err;
+
+ ret = dcss_scaler_init(dcss, base_addr + devtype->scaler_ofs);
+ if (ret)
+ goto scaler_err;
+
+ dcss_clocks_disable(dcss);
+
+ return 0;
+
+scaler_err:
+ dcss_dpr_exit(dcss->dpr);
+
+dpr_err:
+ dcss_ss_exit(dcss->ss);
+
+ss_err:
+ dcss_dtg_exit(dcss->dtg);
+
+dtg_err:
+ dcss_ctxld_exit(dcss->ctxld);
+
+ctxld_err:
+ dcss_blkctl_exit(dcss->blkctl);
+
+ dcss_clocks_disable(dcss);
+
+ return ret;
+}
+
+static void dcss_submodules_stop(struct dcss_dev *dcss)
+{
+ dcss_clocks_enable(dcss);
+ dcss_scaler_exit(dcss->scaler);
+ dcss_dpr_exit(dcss->dpr);
+ dcss_ss_exit(dcss->ss);
+ dcss_dtg_exit(dcss->dtg);
+ dcss_ctxld_exit(dcss->ctxld);
+ dcss_blkctl_exit(dcss->blkctl);
+ dcss_clocks_disable(dcss);
+}
+
+static int dcss_clks_init(struct dcss_dev *dcss)
+{
+ int i;
+ struct {
+ const char *id;
+ struct clk **clk;
+ } clks[] = {
+ {"apb", &dcss->apb_clk},
+ {"axi", &dcss->axi_clk},
+ {"pix", &dcss->pix_clk},
+ {"rtrm", &dcss->rtrm_clk},
+ {"dtrc", &dcss->dtrc_clk},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(clks); i++) {
+ *clks[i].clk = devm_clk_get(dcss->dev, clks[i].id);
+ if (IS_ERR(*clks[i].clk)) {
+ dev_err(dcss->dev, "failed to get %s clock\n",
+ clks[i].id);
+ return PTR_ERR(*clks[i].clk);
+ }
+ }
+
+ return 0;
+}
+
+static void dcss_clks_release(struct dcss_dev *dcss)
+{
+ devm_clk_put(dcss->dev, dcss->dtrc_clk);
+ devm_clk_put(dcss->dev, dcss->rtrm_clk);
+ devm_clk_put(dcss->dev, dcss->pix_clk);
+ devm_clk_put(dcss->dev, dcss->axi_clk);
+ devm_clk_put(dcss->dev, dcss->apb_clk);
+}
+
+struct dcss_dev *dcss_dev_create(struct device *dev, bool hdmi_output)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int ret;
+ struct resource *res;
+ struct dcss_dev *dcss;
+ const struct dcss_type_data *devtype;
+
+ devtype = of_device_get_match_data(dev);
+ if (!devtype) {
+ dev_err(dev, "no device match found\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "cannot get memory resource\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ dcss = kzalloc(sizeof(*dcss), GFP_KERNEL);
+ if (!dcss)
+ return ERR_PTR(-ENOMEM);
+
+ dcss->dev = dev;
+ dcss->devtype = devtype;
+ dcss->hdmi_output = hdmi_output;
+
+ ret = dcss_clks_init(dcss);
+ if (ret) {
+ dev_err(dev, "clocks initialization failed\n");
+ goto err;
+ }
+
+ dcss->of_port = of_graph_get_port_by_id(dev->of_node, 0);
+ if (!dcss->of_port) {
+ dev_err(dev, "no port@0 node in %s\n", dev->of_node->full_name);
+ ret = -ENODEV;
+ goto clks_err;
+ }
+
+ dcss->start_addr = res->start;
+
+ ret = dcss_submodules_init(dcss);
+ if (ret) {
+ dev_err(dev, "submodules initialization failed\n");
+ goto clks_err;
+ }
+
+ init_completion(&dcss->disable_completion);
+
+ pm_runtime_set_autosuspend_delay(dev, 100);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_allow(dev);
+ pm_runtime_enable(dev);
+
+ return dcss;
+
+clks_err:
+ dcss_clks_release(dcss);
+
+err:
+ kfree(dcss);
+
+ return ERR_PTR(ret);
+}
+
+void dcss_dev_destroy(struct dcss_dev *dcss)
+{
+ if (!pm_runtime_suspended(dcss->dev)) {
+ dcss_ctxld_suspend(dcss->ctxld);
+ dcss_clocks_disable(dcss);
+ }
+
+ pm_runtime_disable(dcss->dev);
+
+ dcss_submodules_stop(dcss);
+
+ dcss_clks_release(dcss);
+
+ kfree(dcss);
+}
+
+#ifdef CONFIG_PM_SLEEP
+int dcss_dev_suspend(struct device *dev)
+{
+ struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
+ struct drm_device *ddev = dcss_drv_dev_to_drm(dev);
+ struct dcss_kms_dev *kms = container_of(ddev, struct dcss_kms_dev, base);
+ int ret;
+
+ drm_bridge_connector_disable_hpd(kms->connector);
+
+ drm_mode_config_helper_suspend(ddev);
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ ret = dcss_ctxld_suspend(dcss->ctxld);
+ if (ret)
+ return ret;
+
+ dcss_clocks_disable(dcss);
+
+ return 0;
+}
+
+int dcss_dev_resume(struct device *dev)
+{
+ struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
+ struct drm_device *ddev = dcss_drv_dev_to_drm(dev);
+ struct dcss_kms_dev *kms = container_of(ddev, struct dcss_kms_dev, base);
+
+ if (pm_runtime_suspended(dev)) {
+ drm_mode_config_helper_resume(ddev);
+ return 0;
+ }
+
+ dcss_clocks_enable(dcss);
+
+ dcss_blkctl_cfg(dcss->blkctl);
+
+ dcss_ctxld_resume(dcss->ctxld);
+
+ drm_mode_config_helper_resume(ddev);
+
+ drm_bridge_connector_enable_hpd(kms->connector);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM
+int dcss_dev_runtime_suspend(struct device *dev)
+{
+ struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
+ int ret;
+
+ ret = dcss_ctxld_suspend(dcss->ctxld);
+ if (ret)
+ return ret;
+
+ dcss_clocks_disable(dcss);
+
+ return 0;
+}
+
+int dcss_dev_runtime_resume(struct device *dev)
+{
+ struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
+
+ dcss_clocks_enable(dcss);
+
+ dcss_blkctl_cfg(dcss->blkctl);
+
+ dcss_ctxld_resume(dcss->ctxld);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
diff --git a/drivers/gpu/drm/imx/dcss/dcss-dev.h b/drivers/gpu/drm/imx/dcss/dcss-dev.h
new file mode 100644
index 000000000000..c642ae17837f
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcss/dcss-dev.h
@@ -0,0 +1,177 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2019 NXP.
+ */
+
+#ifndef __DCSS_PRV_H__
+#define __DCSS_PRV_H__
+
+#include <drm/drm_fourcc.h>
+#include <linux/io.h>
+#include <video/videomode.h>
+
+#define SET 0x04
+#define CLR 0x08
+#define TGL 0x0C
+
+#define dcss_writel(v, c) writel((v), (c))
+#define dcss_readl(c) readl(c)
+#define dcss_set(v, c) writel((v), (c) + SET)
+#define dcss_clr(v, c) writel((v), (c) + CLR)
+#define dcss_toggle(v, c) writel((v), (c) + TGL)
+
+static inline void dcss_update(u32 v, u32 m, void __iomem *c)
+{
+ writel((readl(c) & ~(m)) | (v), (c));
+}
+
+#define DCSS_DBG_REG(reg) {.name = #reg, .ofs = reg}
+
+enum {
+ DCSS_IMX8MQ = 0,
+};
+
+struct dcss_type_data {
+ const char *name;
+ u32 blkctl_ofs;
+ u32 ctxld_ofs;
+ u32 rdsrc_ofs;
+ u32 wrscl_ofs;
+ u32 dtg_ofs;
+ u32 scaler_ofs;
+ u32 ss_ofs;
+ u32 dpr_ofs;
+ u32 dtrc_ofs;
+ u32 dec400d_ofs;
+ u32 hdr10_ofs;
+};
+
+struct dcss_debug_reg {
+ char *name;
+ u32 ofs;
+};
+
+enum dcss_ctxld_ctx_type {
+ CTX_DB,
+ CTX_SB_HP, /* high-priority */
+ CTX_SB_LP, /* low-priority */
+};
+
+struct dcss_dev {
+ struct device *dev;
+ const struct dcss_type_data *devtype;
+ struct device_node *of_port;
+
+ u32 start_addr;
+
+ struct dcss_blkctl *blkctl;
+ struct dcss_ctxld *ctxld;
+ struct dcss_dpr *dpr;
+ struct dcss_dtg *dtg;
+ struct dcss_ss *ss;
+ struct dcss_hdr10 *hdr10;
+ struct dcss_scaler *scaler;
+ struct dcss_dtrc *dtrc;
+ struct dcss_dec400d *dec400d;
+ struct dcss_wrscl *wrscl;
+ struct dcss_rdsrc *rdsrc;
+
+ struct clk *apb_clk;
+ struct clk *axi_clk;
+ struct clk *pix_clk;
+ struct clk *rtrm_clk;
+ struct clk *dtrc_clk;
+ struct clk *pll_src_clk;
+ struct clk *pll_phy_ref_clk;
+
+ bool hdmi_output;
+
+ void (*disable_callback)(void *data);
+ struct completion disable_completion;
+};
+
+struct dcss_dev *dcss_drv_dev_to_dcss(struct device *dev);
+struct drm_device *dcss_drv_dev_to_drm(struct device *dev);
+struct dcss_dev *dcss_dev_create(struct device *dev, bool hdmi_output);
+void dcss_dev_destroy(struct dcss_dev *dcss);
+int dcss_dev_runtime_suspend(struct device *dev);
+int dcss_dev_runtime_resume(struct device *dev);
+int dcss_dev_suspend(struct device *dev);
+int dcss_dev_resume(struct device *dev);
+void dcss_enable_dtg_and_ss(struct dcss_dev *dcss);
+void dcss_disable_dtg_and_ss(struct dcss_dev *dcss);
+
+/* BLKCTL */
+int dcss_blkctl_init(struct dcss_dev *dcss, unsigned long blkctl_base);
+void dcss_blkctl_cfg(struct dcss_blkctl *blkctl);
+void dcss_blkctl_exit(struct dcss_blkctl *blkctl);
+
+/* CTXLD */
+int dcss_ctxld_init(struct dcss_dev *dcss, unsigned long ctxld_base);
+void dcss_ctxld_exit(struct dcss_ctxld *ctxld);
+void dcss_ctxld_write(struct dcss_ctxld *ctxld, u32 ctx_id,
+ u32 val, u32 reg_idx);
+int dcss_ctxld_resume(struct dcss_ctxld *dcss_ctxld);
+int dcss_ctxld_suspend(struct dcss_ctxld *dcss_ctxld);
+void dcss_ctxld_write_irqsafe(struct dcss_ctxld *ctlxd, u32 ctx_id, u32 val,
+ u32 reg_ofs);
+void dcss_ctxld_kick(struct dcss_ctxld *ctxld);
+bool dcss_ctxld_is_flushed(struct dcss_ctxld *ctxld);
+int dcss_ctxld_enable(struct dcss_ctxld *ctxld);
+void dcss_ctxld_register_completion(struct dcss_ctxld *ctxld,
+ struct completion *dis_completion);
+void dcss_ctxld_assert_locked(struct dcss_ctxld *ctxld);
+
+/* DPR */
+int dcss_dpr_init(struct dcss_dev *dcss, unsigned long dpr_base);
+void dcss_dpr_exit(struct dcss_dpr *dpr);
+void dcss_dpr_write_sysctrl(struct dcss_dpr *dpr);
+void dcss_dpr_set_res(struct dcss_dpr *dpr, int ch_num, u32 xres, u32 yres);
+void dcss_dpr_addr_set(struct dcss_dpr *dpr, int ch_num, u32 luma_base_addr,
+ u32 chroma_base_addr, u16 pitch);
+void dcss_dpr_enable(struct dcss_dpr *dpr, int ch_num, bool en);
+void dcss_dpr_format_set(struct dcss_dpr *dpr, int ch_num,
+ const struct drm_format_info *format, u64 modifier);
+void dcss_dpr_set_rotation(struct dcss_dpr *dpr, int ch_num, u32 rotation);
+
+/* DTG */
+int dcss_dtg_init(struct dcss_dev *dcss, unsigned long dtg_base);
+void dcss_dtg_exit(struct dcss_dtg *dtg);
+bool dcss_dtg_vblank_irq_valid(struct dcss_dtg *dtg);
+void dcss_dtg_vblank_irq_enable(struct dcss_dtg *dtg, bool en);
+void dcss_dtg_vblank_irq_clear(struct dcss_dtg *dtg);
+void dcss_dtg_sync_set(struct dcss_dtg *dtg, struct videomode *vm);
+void dcss_dtg_css_set(struct dcss_dtg *dtg);
+void dcss_dtg_enable(struct dcss_dtg *dtg);
+void dcss_dtg_shutoff(struct dcss_dtg *dtg);
+bool dcss_dtg_is_enabled(struct dcss_dtg *dtg);
+void dcss_dtg_ctxld_kick_irq_enable(struct dcss_dtg *dtg, bool en);
+bool dcss_dtg_global_alpha_changed(struct dcss_dtg *dtg, int ch_num, int alpha);
+void dcss_dtg_plane_alpha_set(struct dcss_dtg *dtg, int ch_num,
+ const struct drm_format_info *format, int alpha);
+void dcss_dtg_plane_pos_set(struct dcss_dtg *dtg, int ch_num,
+ int px, int py, int pw, int ph);
+void dcss_dtg_ch_enable(struct dcss_dtg *dtg, int ch_num, bool en);
+
+/* SUBSAM */
+int dcss_ss_init(struct dcss_dev *dcss, unsigned long subsam_base);
+void dcss_ss_exit(struct dcss_ss *ss);
+void dcss_ss_enable(struct dcss_ss *ss);
+void dcss_ss_shutoff(struct dcss_ss *ss);
+void dcss_ss_subsam_set(struct dcss_ss *ss);
+void dcss_ss_sync_set(struct dcss_ss *ss, struct videomode *vm,
+ bool phsync, bool pvsync);
+
+/* SCALER */
+int dcss_scaler_init(struct dcss_dev *dcss, unsigned long scaler_base);
+void dcss_scaler_exit(struct dcss_scaler *scl);
+void dcss_scaler_setup(struct dcss_scaler *scl, int ch_num,
+ const struct drm_format_info *format,
+ int src_xres, int src_yres, int dst_xres, int dst_yres,
+ u32 vrefresh_hz);
+void dcss_scaler_ch_enable(struct dcss_scaler *scl, int ch_num, bool en);
+int dcss_scaler_get_min_max_ratios(struct dcss_scaler *scl, int ch_num,
+ int *min, int *max);
+void dcss_scaler_write_sclctrl(struct dcss_scaler *scl);
+
+#endif /* __DCSS_PRV_H__ */
diff --git a/drivers/gpu/drm/imx/dcss/dcss-dpr.c b/drivers/gpu/drm/imx/dcss/dcss-dpr.c
new file mode 100644
index 000000000000..df9dab949bf2
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcss/dcss-dpr.c
@@ -0,0 +1,562 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#include "dcss-dev.h"
+
+#define DCSS_DPR_SYSTEM_CTRL0 0x000
+#define RUN_EN BIT(0)
+#define SOFT_RESET BIT(1)
+#define REPEAT_EN BIT(2)
+#define SHADOW_LOAD_EN BIT(3)
+#define SW_SHADOW_LOAD_SEL BIT(4)
+#define BCMD2AXI_MSTR_ID_CTRL BIT(16)
+#define DCSS_DPR_IRQ_MASK 0x020
+#define DCSS_DPR_IRQ_MASK_STATUS 0x030
+#define DCSS_DPR_IRQ_NONMASK_STATUS 0x040
+#define IRQ_DPR_CTRL_DONE BIT(0)
+#define IRQ_DPR_RUN BIT(1)
+#define IRQ_DPR_SHADOW_LOADED BIT(2)
+#define IRQ_AXI_READ_ERR BIT(3)
+#define DPR2RTR_YRGB_FIFO_OVFL BIT(4)
+#define DPR2RTR_UV_FIFO_OVFL BIT(5)
+#define DPR2RTR_FIFO_LD_BUF_RDY_YRGB_ERR BIT(6)
+#define DPR2RTR_FIFO_LD_BUF_RDY_UV_ERR BIT(7)
+#define DCSS_DPR_MODE_CTRL0 0x050
+#define RTR_3BUF_EN BIT(0)
+#define RTR_4LINE_BUF_EN BIT(1)
+#define TILE_TYPE_POS 2
+#define TILE_TYPE_MASK GENMASK(4, 2)
+#define YUV_EN BIT(6)
+#define COMP_2PLANE_EN BIT(7)
+#define PIX_SIZE_POS 8
+#define PIX_SIZE_MASK GENMASK(9, 8)
+#define PIX_LUMA_UV_SWAP BIT(10)
+#define PIX_UV_SWAP BIT(11)
+#define B_COMP_SEL_POS 12
+#define B_COMP_SEL_MASK GENMASK(13, 12)
+#define G_COMP_SEL_POS 14
+#define G_COMP_SEL_MASK GENMASK(15, 14)
+#define R_COMP_SEL_POS 16
+#define R_COMP_SEL_MASK GENMASK(17, 16)
+#define A_COMP_SEL_POS 18
+#define A_COMP_SEL_MASK GENMASK(19, 18)
+#define DCSS_DPR_FRAME_CTRL0 0x070
+#define HFLIP_EN BIT(0)
+#define VFLIP_EN BIT(1)
+#define ROT_ENC_POS 2
+#define ROT_ENC_MASK GENMASK(3, 2)
+#define ROT_FLIP_ORDER_EN BIT(4)
+#define PITCH_POS 16
+#define PITCH_MASK GENMASK(31, 16)
+#define DCSS_DPR_FRAME_1P_CTRL0 0x090
+#define DCSS_DPR_FRAME_1P_PIX_X_CTRL 0x0A0
+#define DCSS_DPR_FRAME_1P_PIX_Y_CTRL 0x0B0
+#define DCSS_DPR_FRAME_1P_BASE_ADDR 0x0C0
+#define DCSS_DPR_FRAME_2P_CTRL0 0x0E0
+#define DCSS_DPR_FRAME_2P_PIX_X_CTRL 0x0F0
+#define DCSS_DPR_FRAME_2P_PIX_Y_CTRL 0x100
+#define DCSS_DPR_FRAME_2P_BASE_ADDR 0x110
+#define DCSS_DPR_STATUS_CTRL0 0x130
+#define STATUS_MUX_SEL_MASK GENMASK(2, 0)
+#define STATUS_SRC_SEL_POS 16
+#define STATUS_SRC_SEL_MASK GENMASK(18, 16)
+#define DCSS_DPR_STATUS_CTRL1 0x140
+#define DCSS_DPR_RTRAM_CTRL0 0x200
+#define NUM_ROWS_ACTIVE BIT(0)
+#define THRES_HIGH_POS 1
+#define THRES_HIGH_MASK GENMASK(3, 1)
+#define THRES_LOW_POS 4
+#define THRES_LOW_MASK GENMASK(6, 4)
+#define ABORT_SEL BIT(7)
+
+enum dcss_tile_type {
+ TILE_LINEAR = 0,
+ TILE_GPU_STANDARD,
+ TILE_GPU_SUPER,
+ TILE_VPU_YUV420,
+ TILE_VPU_VP9,
+};
+
+enum dcss_pix_size {
+ PIX_SIZE_8,
+ PIX_SIZE_16,
+ PIX_SIZE_32,
+};
+
+struct dcss_dpr_ch {
+ struct dcss_dpr *dpr;
+ void __iomem *base_reg;
+ u32 base_ofs;
+
+ struct drm_format_info format;
+ enum dcss_pix_size pix_size;
+ enum dcss_tile_type tile;
+ bool rtram_4line_en;
+ bool rtram_3buf_en;
+
+ u32 frame_ctrl;
+ u32 mode_ctrl;
+ u32 sys_ctrl;
+ u32 rtram_ctrl;
+
+ bool sys_ctrl_chgd;
+
+ int ch_num;
+ int irq;
+};
+
+struct dcss_dpr {
+ struct device *dev;
+ struct dcss_ctxld *ctxld;
+ u32 ctx_id;
+
+ struct dcss_dpr_ch ch[3];
+};
+
+static void dcss_dpr_write(struct dcss_dpr_ch *ch, u32 val, u32 ofs)
+{
+ struct dcss_dpr *dpr = ch->dpr;
+
+ dcss_ctxld_write(dpr->ctxld, dpr->ctx_id, val, ch->base_ofs + ofs);
+}
+
+static int dcss_dpr_ch_init_all(struct dcss_dpr *dpr, unsigned long dpr_base)
+{
+ struct dcss_dpr_ch *ch;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ ch = &dpr->ch[i];
+
+ ch->base_ofs = dpr_base + i * 0x1000;
+
+ ch->base_reg = ioremap(ch->base_ofs, SZ_4K);
+ if (!ch->base_reg) {
+ dev_err(dpr->dev, "dpr: unable to remap ch %d base\n",
+ i);
+ return -ENOMEM;
+ }
+
+ ch->dpr = dpr;
+ ch->ch_num = i;
+
+ dcss_writel(0xff, ch->base_reg + DCSS_DPR_IRQ_MASK);
+ }
+
+ return 0;
+}
+
+int dcss_dpr_init(struct dcss_dev *dcss, unsigned long dpr_base)
+{
+ struct dcss_dpr *dpr;
+
+ dpr = kzalloc(sizeof(*dpr), GFP_KERNEL);
+ if (!dpr)
+ return -ENOMEM;
+
+ dcss->dpr = dpr;
+ dpr->dev = dcss->dev;
+ dpr->ctxld = dcss->ctxld;
+ dpr->ctx_id = CTX_SB_HP;
+
+ if (dcss_dpr_ch_init_all(dpr, dpr_base)) {
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (dpr->ch[i].base_reg)
+ iounmap(dpr->ch[i].base_reg);
+ }
+
+ kfree(dpr);
+
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void dcss_dpr_exit(struct dcss_dpr *dpr)
+{
+ int ch_no;
+
+ /* stop DPR on all channels */
+ for (ch_no = 0; ch_no < 3; ch_no++) {
+ struct dcss_dpr_ch *ch = &dpr->ch[ch_no];
+
+ dcss_writel(0, ch->base_reg + DCSS_DPR_SYSTEM_CTRL0);
+
+ if (ch->base_reg)
+ iounmap(ch->base_reg);
+ }
+
+ kfree(dpr);
+}
+
+static u32 dcss_dpr_x_pix_wide_adjust(struct dcss_dpr_ch *ch, u32 pix_wide,
+ u32 pix_format)
+{
+ u8 pix_in_64byte_map[3][5] = {
+ /* LIN, GPU_STD, GPU_SUP, VPU_YUV420, VPU_VP9 */
+ { 64, 8, 8, 8, 16}, /* PIX_SIZE_8 */
+ { 32, 8, 8, 8, 8}, /* PIX_SIZE_16 */
+ { 16, 4, 4, 8, 8}, /* PIX_SIZE_32 */
+ };
+ u32 offset;
+ u32 div_64byte_mod, pix_in_64byte;
+
+ pix_in_64byte = pix_in_64byte_map[ch->pix_size][ch->tile];
+
+ div_64byte_mod = pix_wide % pix_in_64byte;
+ offset = (div_64byte_mod == 0) ? 0 : (pix_in_64byte - div_64byte_mod);
+
+ return pix_wide + offset;
+}
+
+static u32 dcss_dpr_y_pix_high_adjust(struct dcss_dpr_ch *ch, u32 pix_high,
+ u32 pix_format)
+{
+ u8 num_rows_buf = ch->rtram_4line_en ? 4 : 8;
+ u32 offset, pix_y_mod;
+
+ pix_y_mod = pix_high % num_rows_buf;
+ offset = pix_y_mod ? (num_rows_buf - pix_y_mod) : 0;
+
+ return pix_high + offset;
+}
+
+void dcss_dpr_set_res(struct dcss_dpr *dpr, int ch_num, u32 xres, u32 yres)
+{
+ struct dcss_dpr_ch *ch = &dpr->ch[ch_num];
+ u32 pix_format = ch->format.format;
+ u32 gap = DCSS_DPR_FRAME_2P_BASE_ADDR - DCSS_DPR_FRAME_1P_BASE_ADDR;
+ int plane, max_planes = 1;
+ u32 pix_x_wide, pix_y_high;
+
+ if (pix_format == DRM_FORMAT_NV12 ||
+ pix_format == DRM_FORMAT_NV21)
+ max_planes = 2;
+
+ for (plane = 0; plane < max_planes; plane++) {
+ yres = plane == 1 ? yres >> 1 : yres;
+
+ pix_x_wide = dcss_dpr_x_pix_wide_adjust(ch, xres, pix_format);
+ pix_y_high = dcss_dpr_y_pix_high_adjust(ch, yres, pix_format);
+
+ dcss_dpr_write(ch, pix_x_wide,
+ DCSS_DPR_FRAME_1P_PIX_X_CTRL + plane * gap);
+ dcss_dpr_write(ch, pix_y_high,
+ DCSS_DPR_FRAME_1P_PIX_Y_CTRL + plane * gap);
+
+ dcss_dpr_write(ch, 2, DCSS_DPR_FRAME_1P_CTRL0 + plane * gap);
+ }
+}
+
+void dcss_dpr_addr_set(struct dcss_dpr *dpr, int ch_num, u32 luma_base_addr,
+ u32 chroma_base_addr, u16 pitch)
+{
+ struct dcss_dpr_ch *ch = &dpr->ch[ch_num];
+
+ dcss_dpr_write(ch, luma_base_addr, DCSS_DPR_FRAME_1P_BASE_ADDR);
+
+ dcss_dpr_write(ch, chroma_base_addr, DCSS_DPR_FRAME_2P_BASE_ADDR);
+
+ ch->frame_ctrl &= ~PITCH_MASK;
+ ch->frame_ctrl |= (((u32)pitch << PITCH_POS) & PITCH_MASK);
+}
+
+static void dcss_dpr_argb_comp_sel(struct dcss_dpr_ch *ch, int a_sel, int r_sel,
+ int g_sel, int b_sel)
+{
+ u32 sel;
+
+ sel = ((a_sel << A_COMP_SEL_POS) & A_COMP_SEL_MASK) |
+ ((r_sel << R_COMP_SEL_POS) & R_COMP_SEL_MASK) |
+ ((g_sel << G_COMP_SEL_POS) & G_COMP_SEL_MASK) |
+ ((b_sel << B_COMP_SEL_POS) & B_COMP_SEL_MASK);
+
+ ch->mode_ctrl &= ~(A_COMP_SEL_MASK | R_COMP_SEL_MASK |
+ G_COMP_SEL_MASK | B_COMP_SEL_MASK);
+ ch->mode_ctrl |= sel;
+}
+
+static void dcss_dpr_pix_size_set(struct dcss_dpr_ch *ch,
+ const struct drm_format_info *format)
+{
+ u32 val;
+
+ switch (format->format) {
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ val = PIX_SIZE_8;
+ break;
+
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_VYUY:
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVYU:
+ val = PIX_SIZE_16;
+ break;
+
+ default:
+ val = PIX_SIZE_32;
+ break;
+ }
+
+ ch->pix_size = val;
+
+ ch->mode_ctrl &= ~PIX_SIZE_MASK;
+ ch->mode_ctrl |= ((val << PIX_SIZE_POS) & PIX_SIZE_MASK);
+}
+
+static void dcss_dpr_uv_swap(struct dcss_dpr_ch *ch, bool swap)
+{
+ ch->mode_ctrl &= ~PIX_UV_SWAP;
+ ch->mode_ctrl |= (swap ? PIX_UV_SWAP : 0);
+}
+
+static void dcss_dpr_y_uv_swap(struct dcss_dpr_ch *ch, bool swap)
+{
+ ch->mode_ctrl &= ~PIX_LUMA_UV_SWAP;
+ ch->mode_ctrl |= (swap ? PIX_LUMA_UV_SWAP : 0);
+}
+
+static void dcss_dpr_2plane_en(struct dcss_dpr_ch *ch, bool en)
+{
+ ch->mode_ctrl &= ~COMP_2PLANE_EN;
+ ch->mode_ctrl |= (en ? COMP_2PLANE_EN : 0);
+}
+
+static void dcss_dpr_yuv_en(struct dcss_dpr_ch *ch, bool en)
+{
+ ch->mode_ctrl &= ~YUV_EN;
+ ch->mode_ctrl |= (en ? YUV_EN : 0);
+}
+
+void dcss_dpr_enable(struct dcss_dpr *dpr, int ch_num, bool en)
+{
+ struct dcss_dpr_ch *ch = &dpr->ch[ch_num];
+ u32 sys_ctrl;
+
+ sys_ctrl = (en ? REPEAT_EN | RUN_EN : 0);
+
+ if (en) {
+ dcss_dpr_write(ch, ch->mode_ctrl, DCSS_DPR_MODE_CTRL0);
+ dcss_dpr_write(ch, ch->frame_ctrl, DCSS_DPR_FRAME_CTRL0);
+ dcss_dpr_write(ch, ch->rtram_ctrl, DCSS_DPR_RTRAM_CTRL0);
+ }
+
+ if (ch->sys_ctrl != sys_ctrl)
+ ch->sys_ctrl_chgd = true;
+
+ ch->sys_ctrl = sys_ctrl;
+}
+
+struct rgb_comp_sel {
+ u32 drm_format;
+ int a_sel;
+ int r_sel;
+ int g_sel;
+ int b_sel;
+};
+
+static struct rgb_comp_sel comp_sel_map[] = {
+ {DRM_FORMAT_ARGB8888, 3, 2, 1, 0},
+ {DRM_FORMAT_XRGB8888, 3, 2, 1, 0},
+ {DRM_FORMAT_ABGR8888, 3, 0, 1, 2},
+ {DRM_FORMAT_XBGR8888, 3, 0, 1, 2},
+ {DRM_FORMAT_RGBA8888, 0, 3, 2, 1},
+ {DRM_FORMAT_RGBX8888, 0, 3, 2, 1},
+ {DRM_FORMAT_BGRA8888, 0, 1, 2, 3},
+ {DRM_FORMAT_BGRX8888, 0, 1, 2, 3},
+};
+
+static int to_comp_sel(u32 pix_fmt, int *a_sel, int *r_sel, int *g_sel,
+ int *b_sel)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(comp_sel_map); i++) {
+ if (comp_sel_map[i].drm_format == pix_fmt) {
+ *a_sel = comp_sel_map[i].a_sel;
+ *r_sel = comp_sel_map[i].r_sel;
+ *g_sel = comp_sel_map[i].g_sel;
+ *b_sel = comp_sel_map[i].b_sel;
+
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static void dcss_dpr_rtram_set(struct dcss_dpr_ch *ch, u32 pix_format)
+{
+ u32 val, mask;
+
+ switch (pix_format) {
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV12:
+ ch->rtram_3buf_en = true;
+ ch->rtram_4line_en = false;
+ break;
+
+ default:
+ ch->rtram_3buf_en = true;
+ ch->rtram_4line_en = true;
+ break;
+ }
+
+ val = (ch->rtram_4line_en ? RTR_4LINE_BUF_EN : 0);
+ val |= (ch->rtram_3buf_en ? RTR_3BUF_EN : 0);
+ mask = RTR_4LINE_BUF_EN | RTR_3BUF_EN;
+
+ ch->mode_ctrl &= ~mask;
+ ch->mode_ctrl |= (val & mask);
+
+ val = (ch->rtram_4line_en ? 0 : NUM_ROWS_ACTIVE);
+ val |= (3 << THRES_LOW_POS) & THRES_LOW_MASK;
+ val |= (4 << THRES_HIGH_POS) & THRES_HIGH_MASK;
+ mask = THRES_LOW_MASK | THRES_HIGH_MASK | NUM_ROWS_ACTIVE;
+
+ ch->rtram_ctrl &= ~mask;
+ ch->rtram_ctrl |= (val & mask);
+}
+
+static void dcss_dpr_setup_components(struct dcss_dpr_ch *ch,
+ const struct drm_format_info *format)
+{
+ int a_sel, r_sel, g_sel, b_sel;
+ bool uv_swap, y_uv_swap;
+
+ switch (format->format) {
+ case DRM_FORMAT_YVYU:
+ uv_swap = true;
+ y_uv_swap = true;
+ break;
+
+ case DRM_FORMAT_VYUY:
+ case DRM_FORMAT_NV21:
+ uv_swap = true;
+ y_uv_swap = false;
+ break;
+
+ case DRM_FORMAT_YUYV:
+ uv_swap = false;
+ y_uv_swap = true;
+ break;
+
+ default:
+ uv_swap = false;
+ y_uv_swap = false;
+ break;
+ }
+
+ dcss_dpr_uv_swap(ch, uv_swap);
+
+ dcss_dpr_y_uv_swap(ch, y_uv_swap);
+
+ if (!format->is_yuv) {
+ if (!to_comp_sel(format->format, &a_sel, &r_sel,
+ &g_sel, &b_sel)) {
+ dcss_dpr_argb_comp_sel(ch, a_sel, r_sel, g_sel, b_sel);
+ } else {
+ dcss_dpr_argb_comp_sel(ch, 3, 2, 1, 0);
+ }
+ } else {
+ dcss_dpr_argb_comp_sel(ch, 0, 0, 0, 0);
+ }
+}
+
+static void dcss_dpr_tile_set(struct dcss_dpr_ch *ch, uint64_t modifier)
+{
+ switch (ch->ch_num) {
+ case 0:
+ switch (modifier) {
+ case DRM_FORMAT_MOD_LINEAR:
+ ch->tile = TILE_LINEAR;
+ break;
+ case DRM_FORMAT_MOD_VIVANTE_TILED:
+ ch->tile = TILE_GPU_STANDARD;
+ break;
+ case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
+ ch->tile = TILE_GPU_SUPER;
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+ break;
+ case 1:
+ case 2:
+ ch->tile = TILE_LINEAR;
+ break;
+ default:
+ WARN_ON(1);
+ return;
+ }
+
+ ch->mode_ctrl &= ~TILE_TYPE_MASK;
+ ch->mode_ctrl |= ((ch->tile << TILE_TYPE_POS) & TILE_TYPE_MASK);
+}
+
+void dcss_dpr_format_set(struct dcss_dpr *dpr, int ch_num,
+ const struct drm_format_info *format, u64 modifier)
+{
+ struct dcss_dpr_ch *ch = &dpr->ch[ch_num];
+
+ ch->format = *format;
+
+ dcss_dpr_yuv_en(ch, format->is_yuv);
+
+ dcss_dpr_pix_size_set(ch, format);
+
+ dcss_dpr_setup_components(ch, format);
+
+ dcss_dpr_2plane_en(ch, format->num_planes == 2);
+
+ dcss_dpr_rtram_set(ch, format->format);
+
+ dcss_dpr_tile_set(ch, modifier);
+}
+
+/* This function will be called from interrupt context. */
+void dcss_dpr_write_sysctrl(struct dcss_dpr *dpr)
+{
+ int chnum;
+
+ dcss_ctxld_assert_locked(dpr->ctxld);
+
+ for (chnum = 0; chnum < 3; chnum++) {
+ struct dcss_dpr_ch *ch = &dpr->ch[chnum];
+
+ if (ch->sys_ctrl_chgd) {
+ dcss_ctxld_write_irqsafe(dpr->ctxld, dpr->ctx_id,
+ ch->sys_ctrl,
+ ch->base_ofs +
+ DCSS_DPR_SYSTEM_CTRL0);
+ ch->sys_ctrl_chgd = false;
+ }
+ }
+}
+
+void dcss_dpr_set_rotation(struct dcss_dpr *dpr, int ch_num, u32 rotation)
+{
+ struct dcss_dpr_ch *ch = &dpr->ch[ch_num];
+
+ ch->frame_ctrl &= ~(HFLIP_EN | VFLIP_EN | ROT_ENC_MASK);
+
+ ch->frame_ctrl |= rotation & DRM_MODE_REFLECT_X ? HFLIP_EN : 0;
+ ch->frame_ctrl |= rotation & DRM_MODE_REFLECT_Y ? VFLIP_EN : 0;
+
+ if (rotation & DRM_MODE_ROTATE_90)
+ ch->frame_ctrl |= 1 << ROT_ENC_POS;
+ else if (rotation & DRM_MODE_ROTATE_180)
+ ch->frame_ctrl |= 2 << ROT_ENC_POS;
+ else if (rotation & DRM_MODE_ROTATE_270)
+ ch->frame_ctrl |= 3 << ROT_ENC_POS;
+}
diff --git a/drivers/gpu/drm/imx/dcss/dcss-drv.c b/drivers/gpu/drm/imx/dcss/dcss-drv.c
new file mode 100644
index 000000000000..8dc2f85c514b
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcss/dcss-drv.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <drm/drm_of.h>
+
+#include "dcss-dev.h"
+#include "dcss-kms.h"
+
+struct dcss_drv {
+ struct dcss_dev *dcss;
+ struct dcss_kms_dev *kms;
+};
+
+struct dcss_dev *dcss_drv_dev_to_dcss(struct device *dev)
+{
+ struct dcss_drv *mdrv = dev_get_drvdata(dev);
+
+ return mdrv ? mdrv->dcss : NULL;
+}
+
+struct drm_device *dcss_drv_dev_to_drm(struct device *dev)
+{
+ struct dcss_drv *mdrv = dev_get_drvdata(dev);
+
+ return mdrv ? &mdrv->kms->base : NULL;
+}
+
+static int dcss_drv_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *remote;
+ struct dcss_drv *mdrv;
+ int err = 0;
+ bool hdmi_output = true;
+
+ if (!dev->of_node)
+ return -ENODEV;
+
+ remote = of_graph_get_remote_node(dev->of_node, 0, 0);
+ if (!remote)
+ return -ENODEV;
+
+ hdmi_output = !of_device_is_compatible(remote, "fsl,imx8mq-nwl-dsi");
+
+ of_node_put(remote);
+
+ mdrv = kzalloc(sizeof(*mdrv), GFP_KERNEL);
+ if (!mdrv)
+ return -ENOMEM;
+
+ mdrv->dcss = dcss_dev_create(dev, hdmi_output);
+ if (IS_ERR(mdrv->dcss)) {
+ err = PTR_ERR(mdrv->dcss);
+ goto err;
+ }
+
+ dev_set_drvdata(dev, mdrv);
+
+ mdrv->kms = dcss_kms_attach(mdrv->dcss);
+ if (IS_ERR(mdrv->kms)) {
+ err = PTR_ERR(mdrv->kms);
+ goto dcss_shutoff;
+ }
+
+ return 0;
+
+dcss_shutoff:
+ dcss_dev_destroy(mdrv->dcss);
+
+ dev_set_drvdata(dev, NULL);
+
+err:
+ kfree(mdrv);
+ return err;
+}
+
+static int dcss_drv_platform_remove(struct platform_device *pdev)
+{
+ struct dcss_drv *mdrv = dev_get_drvdata(&pdev->dev);
+
+ if (!mdrv)
+ return 0;
+
+ dcss_kms_detach(mdrv->kms);
+ dcss_dev_destroy(mdrv->dcss);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ kfree(mdrv);
+
+ return 0;
+}
+
+static struct dcss_type_data dcss_types[] = {
+ [DCSS_IMX8MQ] = {
+ .name = "DCSS_IMX8MQ",
+ .blkctl_ofs = 0x2F000,
+ .ctxld_ofs = 0x23000,
+ .dtg_ofs = 0x20000,
+ .scaler_ofs = 0x1C000,
+ .ss_ofs = 0x1B000,
+ .dpr_ofs = 0x18000,
+ },
+};
+
+static const struct of_device_id dcss_of_match[] = {
+ { .compatible = "nxp,imx8mq-dcss", .data = &dcss_types[DCSS_IMX8MQ], },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, dcss_of_match);
+
+static const struct dev_pm_ops dcss_dev_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(dcss_dev_suspend, dcss_dev_resume)
+ SET_RUNTIME_PM_OPS(dcss_dev_runtime_suspend,
+ dcss_dev_runtime_resume, NULL)
+};
+
+static struct platform_driver dcss_platform_driver = {
+ .probe = dcss_drv_platform_probe,
+ .remove = dcss_drv_platform_remove,
+ .driver = {
+ .name = "imx-dcss",
+ .of_match_table = dcss_of_match,
+ .pm = &dcss_dev_pm,
+ },
+};
+
+module_platform_driver(dcss_platform_driver);
+
+MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@nxp.com>");
+MODULE_DESCRIPTION("DCSS driver for i.MX8MQ");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/imx/dcss/dcss-dtg.c b/drivers/gpu/drm/imx/dcss/dcss-dtg.c
new file mode 100644
index 000000000000..30de00540f63
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcss/dcss-dtg.c
@@ -0,0 +1,409 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "dcss-dev.h"
+
+#define DCSS_DTG_TC_CONTROL_STATUS 0x00
+#define CH3_EN BIT(0)
+#define CH2_EN BIT(1)
+#define CH1_EN BIT(2)
+#define OVL_DATA_MODE BIT(3)
+#define BLENDER_VIDEO_ALPHA_SEL BIT(7)
+#define DTG_START BIT(8)
+#define DBY_MODE_EN BIT(9)
+#define CH1_ALPHA_SEL BIT(10)
+#define CSS_PIX_COMP_SWAP_POS 12
+#define CSS_PIX_COMP_SWAP_MASK GENMASK(14, 12)
+#define DEFAULT_FG_ALPHA_POS 24
+#define DEFAULT_FG_ALPHA_MASK GENMASK(31, 24)
+#define DCSS_DTG_TC_DTG 0x04
+#define DCSS_DTG_TC_DISP_TOP 0x08
+#define DCSS_DTG_TC_DISP_BOT 0x0C
+#define DCSS_DTG_TC_CH1_TOP 0x10
+#define DCSS_DTG_TC_CH1_BOT 0x14
+#define DCSS_DTG_TC_CH2_TOP 0x18
+#define DCSS_DTG_TC_CH2_BOT 0x1C
+#define DCSS_DTG_TC_CH3_TOP 0x20
+#define DCSS_DTG_TC_CH3_BOT 0x24
+#define TC_X_POS 0
+#define TC_X_MASK GENMASK(12, 0)
+#define TC_Y_POS 16
+#define TC_Y_MASK GENMASK(28, 16)
+#define DCSS_DTG_TC_CTXLD 0x28
+#define TC_CTXLD_DB_Y_POS 0
+#define TC_CTXLD_DB_Y_MASK GENMASK(12, 0)
+#define TC_CTXLD_SB_Y_POS 16
+#define TC_CTXLD_SB_Y_MASK GENMASK(28, 16)
+#define DCSS_DTG_TC_CH1_BKRND 0x2C
+#define DCSS_DTG_TC_CH2_BKRND 0x30
+#define BKRND_R_Y_COMP_POS 20
+#define BKRND_R_Y_COMP_MASK GENMASK(29, 20)
+#define BKRND_G_U_COMP_POS 10
+#define BKRND_G_U_COMP_MASK GENMASK(19, 10)
+#define BKRND_B_V_COMP_POS 0
+#define BKRND_B_V_COMP_MASK GENMASK(9, 0)
+#define DCSS_DTG_BLENDER_DBY_RANGEINV 0x38
+#define DCSS_DTG_BLENDER_DBY_RANGEMIN 0x3C
+#define DCSS_DTG_BLENDER_DBY_BDP 0x40
+#define DCSS_DTG_BLENDER_BKRND_I 0x44
+#define DCSS_DTG_BLENDER_BKRND_P 0x48
+#define DCSS_DTG_BLENDER_BKRND_T 0x4C
+#define DCSS_DTG_LINE0_INT 0x50
+#define DCSS_DTG_LINE1_INT 0x54
+#define DCSS_DTG_BG_ALPHA_DEFAULT 0x58
+#define DCSS_DTG_INT_STATUS 0x5C
+#define DCSS_DTG_INT_CONTROL 0x60
+#define DCSS_DTG_TC_CH3_BKRND 0x64
+#define DCSS_DTG_INT_MASK 0x68
+#define LINE0_IRQ BIT(0)
+#define LINE1_IRQ BIT(1)
+#define LINE2_IRQ BIT(2)
+#define LINE3_IRQ BIT(3)
+#define DCSS_DTG_LINE2_INT 0x6C
+#define DCSS_DTG_LINE3_INT 0x70
+#define DCSS_DTG_DBY_OL 0x74
+#define DCSS_DTG_DBY_BL 0x78
+#define DCSS_DTG_DBY_EL 0x7C
+
+struct dcss_dtg {
+ struct device *dev;
+ struct dcss_ctxld *ctxld;
+ void __iomem *base_reg;
+ u32 base_ofs;
+
+ u32 ctx_id;
+
+ bool in_use;
+
+ u32 dis_ulc_x;
+ u32 dis_ulc_y;
+
+ u32 control_status;
+ u32 alpha;
+ u32 alpha_cfg;
+
+ int ctxld_kick_irq;
+ bool ctxld_kick_irq_en;
+};
+
+static void dcss_dtg_write(struct dcss_dtg *dtg, u32 val, u32 ofs)
+{
+ if (!dtg->in_use)
+ dcss_writel(val, dtg->base_reg + ofs);
+
+ dcss_ctxld_write(dtg->ctxld, dtg->ctx_id,
+ val, dtg->base_ofs + ofs);
+}
+
+static irqreturn_t dcss_dtg_irq_handler(int irq, void *data)
+{
+ struct dcss_dtg *dtg = data;
+ u32 status;
+
+ status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
+
+ if (!(status & LINE0_IRQ))
+ return IRQ_NONE;
+
+ dcss_ctxld_kick(dtg->ctxld);
+
+ dcss_writel(status & LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
+
+ return IRQ_HANDLED;
+}
+
+static int dcss_dtg_irq_config(struct dcss_dtg *dtg,
+ struct platform_device *pdev)
+{
+ int ret;
+
+ dtg->ctxld_kick_irq = platform_get_irq_byname(pdev, "ctxld_kick");
+ if (dtg->ctxld_kick_irq < 0)
+ return dtg->ctxld_kick_irq;
+
+ dcss_update(0, LINE0_IRQ | LINE1_IRQ,
+ dtg->base_reg + DCSS_DTG_INT_MASK);
+
+ ret = request_irq(dtg->ctxld_kick_irq, dcss_dtg_irq_handler,
+ 0, "dcss_ctxld_kick", dtg);
+ if (ret) {
+ dev_err(dtg->dev, "dtg: irq request failed.\n");
+ return ret;
+ }
+
+ disable_irq(dtg->ctxld_kick_irq);
+
+ dtg->ctxld_kick_irq_en = false;
+
+ return 0;
+}
+
+int dcss_dtg_init(struct dcss_dev *dcss, unsigned long dtg_base)
+{
+ int ret = 0;
+ struct dcss_dtg *dtg;
+
+ dtg = kzalloc(sizeof(*dtg), GFP_KERNEL);
+ if (!dtg)
+ return -ENOMEM;
+
+ dcss->dtg = dtg;
+ dtg->dev = dcss->dev;
+ dtg->ctxld = dcss->ctxld;
+
+ dtg->base_reg = ioremap(dtg_base, SZ_4K);
+ if (!dtg->base_reg) {
+ dev_err(dcss->dev, "dtg: unable to remap dtg base\n");
+ ret = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ dtg->base_ofs = dtg_base;
+ dtg->ctx_id = CTX_DB;
+
+ dtg->alpha = 255;
+
+ dtg->control_status |= OVL_DATA_MODE | BLENDER_VIDEO_ALPHA_SEL |
+ ((dtg->alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK);
+
+ ret = dcss_dtg_irq_config(dtg, to_platform_device(dcss->dev));
+ if (ret)
+ goto err_irq;
+
+ return 0;
+
+err_irq:
+ iounmap(dtg->base_reg);
+
+err_ioremap:
+ kfree(dtg);
+
+ return ret;
+}
+
+void dcss_dtg_exit(struct dcss_dtg *dtg)
+{
+ free_irq(dtg->ctxld_kick_irq, dtg);
+
+ if (dtg->base_reg)
+ iounmap(dtg->base_reg);
+
+ kfree(dtg);
+}
+
+void dcss_dtg_sync_set(struct dcss_dtg *dtg, struct videomode *vm)
+{
+ struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dtg->dev);
+ u16 dtg_lrc_x, dtg_lrc_y;
+ u16 dis_ulc_x, dis_ulc_y;
+ u16 dis_lrc_x, dis_lrc_y;
+ u32 sb_ctxld_trig, db_ctxld_trig;
+ u32 pixclock = vm->pixelclock;
+ u32 actual_clk;
+
+ dtg_lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
+ vm->hactive - 1;
+ dtg_lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len +
+ vm->vactive - 1;
+ dis_ulc_x = vm->hsync_len + vm->hback_porch - 1;
+ dis_ulc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch - 1;
+ dis_lrc_x = vm->hsync_len + vm->hback_porch + vm->hactive - 1;
+ dis_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch +
+ vm->vactive - 1;
+
+ clk_disable_unprepare(dcss->pix_clk);
+ clk_set_rate(dcss->pix_clk, vm->pixelclock);
+ clk_prepare_enable(dcss->pix_clk);
+
+ actual_clk = clk_get_rate(dcss->pix_clk);
+ if (pixclock != actual_clk) {
+ dev_info(dtg->dev,
+ "Pixel clock set to %u kHz instead of %u kHz.\n",
+ (actual_clk / 1000), (pixclock / 1000));
+ }
+
+ dcss_dtg_write(dtg, ((dtg_lrc_y << TC_Y_POS) | dtg_lrc_x),
+ DCSS_DTG_TC_DTG);
+ dcss_dtg_write(dtg, ((dis_ulc_y << TC_Y_POS) | dis_ulc_x),
+ DCSS_DTG_TC_DISP_TOP);
+ dcss_dtg_write(dtg, ((dis_lrc_y << TC_Y_POS) | dis_lrc_x),
+ DCSS_DTG_TC_DISP_BOT);
+
+ dtg->dis_ulc_x = dis_ulc_x;
+ dtg->dis_ulc_y = dis_ulc_y;
+
+ sb_ctxld_trig = ((0 * dis_lrc_y / 100) << TC_CTXLD_SB_Y_POS) &
+ TC_CTXLD_SB_Y_MASK;
+ db_ctxld_trig = ((99 * dis_lrc_y / 100) << TC_CTXLD_DB_Y_POS) &
+ TC_CTXLD_DB_Y_MASK;
+
+ dcss_dtg_write(dtg, sb_ctxld_trig | db_ctxld_trig, DCSS_DTG_TC_CTXLD);
+
+ /* vblank trigger */
+ dcss_dtg_write(dtg, 0, DCSS_DTG_LINE1_INT);
+
+ /* CTXLD trigger */
+ dcss_dtg_write(dtg, ((90 * dis_lrc_y) / 100) << 16, DCSS_DTG_LINE0_INT);
+}
+
+void dcss_dtg_plane_pos_set(struct dcss_dtg *dtg, int ch_num,
+ int px, int py, int pw, int ph)
+{
+ u16 p_ulc_x, p_ulc_y;
+ u16 p_lrc_x, p_lrc_y;
+
+ p_ulc_x = dtg->dis_ulc_x + px;
+ p_ulc_y = dtg->dis_ulc_y + py;
+ p_lrc_x = p_ulc_x + pw;
+ p_lrc_y = p_ulc_y + ph;
+
+ if (!px && !py && !pw && !ph) {
+ dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
+ dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
+ } else {
+ dcss_dtg_write(dtg, ((p_ulc_y << TC_Y_POS) | p_ulc_x),
+ DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
+ dcss_dtg_write(dtg, ((p_lrc_y << TC_Y_POS) | p_lrc_x),
+ DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
+ }
+}
+
+bool dcss_dtg_global_alpha_changed(struct dcss_dtg *dtg, int ch_num, int alpha)
+{
+ if (ch_num)
+ return false;
+
+ return alpha != dtg->alpha;
+}
+
+void dcss_dtg_plane_alpha_set(struct dcss_dtg *dtg, int ch_num,
+ const struct drm_format_info *format, int alpha)
+{
+ /* we care about alpha only when channel 0 is concerned */
+ if (ch_num)
+ return;
+
+ /*
+ * Use global alpha if pixel format does not have alpha channel or the
+ * user explicitly chose to use global alpha (i.e. alpha is not OPAQUE).
+ */
+ if (!format->has_alpha || alpha != 255)
+ dtg->alpha_cfg = (alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK;
+ else /* use per-pixel alpha otherwise */
+ dtg->alpha_cfg = CH1_ALPHA_SEL;
+
+ dtg->alpha = alpha;
+}
+
+void dcss_dtg_css_set(struct dcss_dtg *dtg)
+{
+ dtg->control_status |=
+ (0x5 << CSS_PIX_COMP_SWAP_POS) & CSS_PIX_COMP_SWAP_MASK;
+}
+
+void dcss_dtg_enable(struct dcss_dtg *dtg)
+{
+ dtg->control_status |= DTG_START;
+
+ dtg->control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
+ dtg->control_status |= dtg->alpha_cfg;
+
+ dcss_dtg_write(dtg, dtg->control_status, DCSS_DTG_TC_CONTROL_STATUS);
+
+ dtg->in_use = true;
+}
+
+void dcss_dtg_shutoff(struct dcss_dtg *dtg)
+{
+ dtg->control_status &= ~DTG_START;
+
+ dcss_writel(dtg->control_status,
+ dtg->base_reg + DCSS_DTG_TC_CONTROL_STATUS);
+
+ dtg->in_use = false;
+}
+
+bool dcss_dtg_is_enabled(struct dcss_dtg *dtg)
+{
+ return dtg->in_use;
+}
+
+void dcss_dtg_ch_enable(struct dcss_dtg *dtg, int ch_num, bool en)
+{
+ u32 ch_en_map[] = {CH1_EN, CH2_EN, CH3_EN};
+ u32 control_status;
+
+ control_status = dtg->control_status & ~ch_en_map[ch_num];
+ control_status |= en ? ch_en_map[ch_num] : 0;
+
+ control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
+ control_status |= dtg->alpha_cfg;
+
+ if (dtg->control_status != control_status)
+ dcss_dtg_write(dtg, control_status, DCSS_DTG_TC_CONTROL_STATUS);
+
+ dtg->control_status = control_status;
+}
+
+void dcss_dtg_vblank_irq_enable(struct dcss_dtg *dtg, bool en)
+{
+ u32 status;
+ u32 mask = en ? LINE1_IRQ : 0;
+
+ if (en) {
+ status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
+ dcss_writel(status & LINE1_IRQ,
+ dtg->base_reg + DCSS_DTG_INT_CONTROL);
+ }
+
+ dcss_update(mask, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
+}
+
+void dcss_dtg_ctxld_kick_irq_enable(struct dcss_dtg *dtg, bool en)
+{
+ u32 status;
+ u32 mask = en ? LINE0_IRQ : 0;
+
+ if (en) {
+ status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
+
+ if (!dtg->ctxld_kick_irq_en) {
+ dcss_writel(status & LINE0_IRQ,
+ dtg->base_reg + DCSS_DTG_INT_CONTROL);
+ enable_irq(dtg->ctxld_kick_irq);
+ dtg->ctxld_kick_irq_en = true;
+ dcss_update(mask, LINE0_IRQ,
+ dtg->base_reg + DCSS_DTG_INT_MASK);
+ }
+
+ return;
+ }
+
+ if (!dtg->ctxld_kick_irq_en)
+ return;
+
+ disable_irq_nosync(dtg->ctxld_kick_irq);
+ dtg->ctxld_kick_irq_en = false;
+
+ dcss_update(mask, LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
+}
+
+void dcss_dtg_vblank_irq_clear(struct dcss_dtg *dtg)
+{
+ dcss_update(LINE1_IRQ, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
+}
+
+bool dcss_dtg_vblank_irq_valid(struct dcss_dtg *dtg)
+{
+ return !!(dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS) & LINE1_IRQ);
+}
+
diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.c b/drivers/gpu/drm/imx/dcss/dcss-kms.c
new file mode 100644
index 000000000000..135a62366ab8
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcss/dcss-kms.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+
+#include "dcss-dev.h"
+#include "dcss-kms.h"
+
+DEFINE_DRM_GEM_CMA_FOPS(dcss_cma_fops);
+
+static const struct drm_mode_config_funcs dcss_drm_mode_config_funcs = {
+ .fb_create = drm_gem_fb_create,
+ .output_poll_changed = drm_fb_helper_output_poll_changed,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+static struct drm_driver dcss_kms_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
+ .gem_free_object_unlocked = drm_gem_cma_free_object,
+ .gem_vm_ops = &drm_gem_cma_vm_ops,
+ .dumb_create = drm_gem_cma_dumb_create,
+
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+ .gem_prime_vmap = drm_gem_cma_prime_vmap,
+ .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+ .gem_prime_mmap = drm_gem_cma_prime_mmap,
+ .fops = &dcss_cma_fops,
+ .name = "imx-dcss",
+ .desc = "i.MX8MQ Display Subsystem",
+ .date = "20190917",
+ .major = 1,
+ .minor = 0,
+ .patchlevel = 0,
+};
+
+static const struct drm_mode_config_helper_funcs dcss_mode_config_helpers = {
+ .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+};
+
+static void dcss_kms_mode_config_init(struct dcss_kms_dev *kms)
+{
+ struct drm_mode_config *config = &kms->base.mode_config;
+
+ drm_mode_config_init(&kms->base);
+
+ config->min_width = 1;
+ config->min_height = 1;
+ config->max_width = 4096;
+ config->max_height = 4096;
+ config->allow_fb_modifiers = true;
+ config->normalize_zpos = true;
+
+ config->funcs = &dcss_drm_mode_config_funcs;
+ config->helper_private = &dcss_mode_config_helpers;
+}
+
+static const struct drm_encoder_funcs dcss_kms_simple_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+static int dcss_kms_bridge_connector_init(struct dcss_kms_dev *kms)
+{
+ struct drm_device *ddev = &kms->base;
+ struct drm_encoder *encoder = &kms->encoder;
+ struct drm_crtc *crtc = (struct drm_crtc *)&kms->crtc;
+ struct drm_panel *panel;
+ struct drm_bridge *bridge;
+ int ret;
+
+ ret = drm_of_find_panel_or_bridge(ddev->dev->of_node, 0, 0,
+ &panel, &bridge);
+ if (ret)
+ return ret;
+
+ if (!bridge) {
+ dev_err(ddev->dev, "No bridge found %d.\n", ret);
+ return -ENODEV;
+ }
+
+ encoder->possible_crtcs = drm_crtc_mask(crtc);
+
+ ret = drm_encoder_init(&kms->base, encoder,
+ &dcss_kms_simple_encoder_funcs,
+ DRM_MODE_ENCODER_NONE, NULL);
+ if (ret) {
+ dev_err(ddev->dev, "Failed initializing encoder %d.\n", ret);
+ return ret;
+ }
+
+ ret = drm_bridge_attach(encoder, bridge, NULL,
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (ret < 0) {
+ dev_err(ddev->dev, "Unable to attach bridge %pOF\n",
+ bridge->of_node);
+ return ret;
+ }
+
+ kms->connector = drm_bridge_connector_init(ddev, encoder);
+ if (IS_ERR(kms->connector)) {
+ dev_err(ddev->dev, "Unable to create bridge connector.\n");
+ return PTR_ERR(kms->connector);
+ }
+
+ drm_connector_attach_encoder(kms->connector, encoder);
+
+ return 0;
+}
+
+struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss)
+{
+ struct dcss_kms_dev *kms;
+ struct drm_device *drm;
+ struct dcss_crtc *crtc;
+ int ret;
+
+ kms = devm_drm_dev_alloc(dcss->dev, &dcss_kms_driver,
+ struct dcss_kms_dev, base);
+ if (IS_ERR(kms))
+ return kms;
+
+ drm = &kms->base;
+ crtc = &kms->crtc;
+
+ drm->dev_private = dcss;
+
+ dcss_kms_mode_config_init(kms);
+
+ ret = drm_vblank_init(drm, 1);
+ if (ret)
+ goto cleanup_mode_config;
+
+ drm->irq_enabled = true;
+
+ ret = dcss_kms_bridge_connector_init(kms);
+ if (ret)
+ goto cleanup_mode_config;
+
+ ret = dcss_crtc_init(crtc, drm);
+ if (ret)
+ goto cleanup_mode_config;
+
+ drm_mode_config_reset(drm);
+
+ drm_kms_helper_poll_init(drm);
+
+ drm_bridge_connector_enable_hpd(kms->connector);
+
+ ret = drm_dev_register(drm, 0);
+ if (ret)
+ goto cleanup_crtc;
+
+ drm_fbdev_generic_setup(drm, 32);
+
+ return kms;
+
+cleanup_crtc:
+ drm_bridge_connector_disable_hpd(kms->connector);
+ drm_kms_helper_poll_fini(drm);
+ dcss_crtc_deinit(crtc, drm);
+
+cleanup_mode_config:
+ drm_mode_config_cleanup(drm);
+ drm->dev_private = NULL;
+
+ return ERR_PTR(ret);
+}
+
+void dcss_kms_detach(struct dcss_kms_dev *kms)
+{
+ struct drm_device *drm = &kms->base;
+
+ drm_dev_unregister(drm);
+ drm_bridge_connector_disable_hpd(kms->connector);
+ drm_kms_helper_poll_fini(drm);
+ drm_atomic_helper_shutdown(drm);
+ drm_crtc_vblank_off(&kms->crtc.base);
+ drm->irq_enabled = false;
+ drm_mode_config_cleanup(drm);
+ dcss_crtc_deinit(&kms->crtc, drm);
+ drm->dev_private = NULL;
+}
diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.h b/drivers/gpu/drm/imx/dcss/dcss-kms.h
new file mode 100644
index 000000000000..dfe5dd99eea3
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcss/dcss-kms.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2019 NXP.
+ */
+
+#ifndef _DCSS_KMS_H_
+#define _DCSS_KMS_H_
+
+#include <drm/drm_encoder.h>
+
+struct dcss_plane {
+ struct drm_plane base;
+
+ int ch_num;
+};
+
+struct dcss_crtc {
+ struct drm_crtc base;
+ struct drm_crtc_state *state;
+
+ struct dcss_plane *plane[3];
+
+ int irq;
+
+ bool disable_ctxld_kick_irq;
+};
+
+struct dcss_kms_dev {
+ struct drm_device base;
+ struct dcss_crtc crtc;
+ struct drm_encoder encoder;
+ struct drm_connector *connector;
+};
+
+struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss);
+void dcss_kms_detach(struct dcss_kms_dev *kms);
+int dcss_crtc_init(struct dcss_crtc *crtc, struct drm_device *drm);
+void dcss_crtc_deinit(struct dcss_crtc *crtc, struct drm_device *drm);
+struct dcss_plane *dcss_plane_init(struct drm_device *drm,
+ unsigned int possible_crtcs,
+ enum drm_plane_type type,
+ unsigned int zpos);
+
+#endif
diff --git a/drivers/gpu/drm/imx/dcss/dcss-plane.c b/drivers/gpu/drm/imx/dcss/dcss-plane.c
new file mode 100644
index 000000000000..961d671f171b
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcss/dcss-plane.c
@@ -0,0 +1,405 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "dcss-dev.h"
+#include "dcss-kms.h"
+
+static const u32 dcss_common_formats[] = {
+ /* RGB */
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_RGBA8888,
+ DRM_FORMAT_RGBX8888,
+ DRM_FORMAT_BGRA8888,
+ DRM_FORMAT_BGRX8888,
+ DRM_FORMAT_XRGB2101010,
+ DRM_FORMAT_XBGR2101010,
+ DRM_FORMAT_RGBX1010102,
+ DRM_FORMAT_BGRX1010102,
+ DRM_FORMAT_ARGB2101010,
+ DRM_FORMAT_ABGR2101010,
+ DRM_FORMAT_RGBA1010102,
+ DRM_FORMAT_BGRA1010102,
+};
+
+static const u64 dcss_video_format_modifiers[] = {
+ DRM_FORMAT_MOD_LINEAR,
+ DRM_FORMAT_MOD_INVALID,
+};
+
+static const u64 dcss_graphics_format_modifiers[] = {
+ DRM_FORMAT_MOD_VIVANTE_TILED,
+ DRM_FORMAT_MOD_VIVANTE_SUPER_TILED,
+ DRM_FORMAT_MOD_LINEAR,
+ DRM_FORMAT_MOD_INVALID,
+};
+
+static inline struct dcss_plane *to_dcss_plane(struct drm_plane *p)
+{
+ return container_of(p, struct dcss_plane, base);
+}
+
+static inline bool dcss_plane_fb_is_linear(const struct drm_framebuffer *fb)
+{
+ return ((fb->flags & DRM_MODE_FB_MODIFIERS) == 0) ||
+ ((fb->flags & DRM_MODE_FB_MODIFIERS) != 0 &&
+ fb->modifier == DRM_FORMAT_MOD_LINEAR);
+}
+
+static void dcss_plane_destroy(struct drm_plane *plane)
+{
+ struct dcss_plane *dcss_plane = container_of(plane, struct dcss_plane,
+ base);
+
+ drm_plane_cleanup(plane);
+ kfree(dcss_plane);
+}
+
+static bool dcss_plane_format_mod_supported(struct drm_plane *plane,
+ u32 format,
+ u64 modifier)
+{
+ switch (plane->type) {
+ case DRM_PLANE_TYPE_PRIMARY:
+ switch (format) {
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_ARGB2101010:
+ return modifier == DRM_FORMAT_MOD_LINEAR ||
+ modifier == DRM_FORMAT_MOD_VIVANTE_TILED ||
+ modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED;
+ default:
+ return modifier == DRM_FORMAT_MOD_LINEAR;
+ }
+ break;
+ case DRM_PLANE_TYPE_OVERLAY:
+ return modifier == DRM_FORMAT_MOD_LINEAR;
+ default:
+ return false;
+ }
+}
+
+static const struct drm_plane_funcs dcss_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = dcss_plane_destroy,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .format_mod_supported = dcss_plane_format_mod_supported,
+};
+
+static bool dcss_plane_can_rotate(const struct drm_format_info *format,
+ bool mod_present, u64 modifier,
+ unsigned int rotation)
+{
+ bool linear_format = !mod_present ||
+ (mod_present && modifier == DRM_FORMAT_MOD_LINEAR);
+ u32 supported_rotation = DRM_MODE_ROTATE_0;
+
+ if (!format->is_yuv && linear_format)
+ supported_rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 |
+ DRM_MODE_REFLECT_MASK;
+ else if (!format->is_yuv &&
+ modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
+ supported_rotation = DRM_MODE_ROTATE_MASK |
+ DRM_MODE_REFLECT_MASK;
+ else if (format->is_yuv && linear_format &&
+ (format->format == DRM_FORMAT_NV12 ||
+ format->format == DRM_FORMAT_NV21))
+ supported_rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 |
+ DRM_MODE_REFLECT_MASK;
+
+ return !!(rotation & supported_rotation);
+}
+
+static bool dcss_plane_is_source_size_allowed(u16 src_w, u16 src_h, u32 pix_fmt)
+{
+ if (src_w < 64 &&
+ (pix_fmt == DRM_FORMAT_NV12 || pix_fmt == DRM_FORMAT_NV21))
+ return false;
+ else if (src_w < 32 &&
+ (pix_fmt == DRM_FORMAT_UYVY || pix_fmt == DRM_FORMAT_VYUY ||
+ pix_fmt == DRM_FORMAT_YUYV || pix_fmt == DRM_FORMAT_YVYU))
+ return false;
+
+ return src_w >= 16 && src_h >= 8;
+}
+
+static int dcss_plane_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct dcss_plane *dcss_plane = to_dcss_plane(plane);
+ struct dcss_dev *dcss = plane->dev->dev_private;
+ struct drm_framebuffer *fb = state->fb;
+ bool is_primary_plane = plane->type == DRM_PLANE_TYPE_PRIMARY;
+ struct drm_gem_cma_object *cma_obj;
+ struct drm_crtc_state *crtc_state;
+ int hdisplay, vdisplay;
+ int min, max;
+ int ret;
+
+ if (!fb || !state->crtc)
+ return 0;
+
+ cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
+ WARN_ON(!cma_obj);
+
+ crtc_state = drm_atomic_get_existing_crtc_state(state->state,
+ state->crtc);
+
+ hdisplay = crtc_state->adjusted_mode.hdisplay;
+ vdisplay = crtc_state->adjusted_mode.vdisplay;
+
+ if (!dcss_plane_is_source_size_allowed(state->src_w >> 16,
+ state->src_h >> 16,
+ fb->format->format)) {
+ DRM_DEBUG_KMS("Source plane size is not allowed!\n");
+ return -EINVAL;
+ }
+
+ dcss_scaler_get_min_max_ratios(dcss->scaler, dcss_plane->ch_num,
+ &min, &max);
+
+ ret = drm_atomic_helper_check_plane_state(state, crtc_state,
+ min, max, !is_primary_plane,
+ false);
+ if (ret)
+ return ret;
+
+ if (!state->visible)
+ return 0;
+
+ if (!dcss_plane_can_rotate(fb->format,
+ !!(fb->flags & DRM_MODE_FB_MODIFIERS),
+ fb->modifier,
+ state->rotation)) {
+ DRM_DEBUG_KMS("requested rotation is not allowed!\n");
+ return -EINVAL;
+ }
+
+ if ((state->crtc_x < 0 || state->crtc_y < 0 ||
+ state->crtc_x + state->crtc_w > hdisplay ||
+ state->crtc_y + state->crtc_h > vdisplay) &&
+ !dcss_plane_fb_is_linear(fb)) {
+ DRM_DEBUG_KMS("requested cropping operation is not allowed!\n");
+ return -EINVAL;
+ }
+
+ if ((fb->flags & DRM_MODE_FB_MODIFIERS) &&
+ !plane->funcs->format_mod_supported(plane,
+ fb->format->format,
+ fb->modifier)) {
+ DRM_DEBUG_KMS("Invalid modifier: %llx", fb->modifier);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void dcss_plane_atomic_set_base(struct dcss_plane *dcss_plane)
+{
+ struct drm_plane *plane = &dcss_plane->base;
+ struct drm_plane_state *state = plane->state;
+ struct dcss_dev *dcss = plane->dev->dev_private;
+ struct drm_framebuffer *fb = state->fb;
+ const struct drm_format_info *format = fb->format;
+ struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
+ unsigned long p1_ba = 0, p2_ba = 0;
+
+ if (!format->is_yuv ||
+ format->format == DRM_FORMAT_NV12 ||
+ format->format == DRM_FORMAT_NV21)
+ p1_ba = cma_obj->paddr + fb->offsets[0] +
+ fb->pitches[0] * (state->src.y1 >> 16) +
+ format->char_per_block[0] * (state->src.x1 >> 16);
+ else if (format->format == DRM_FORMAT_UYVY ||
+ format->format == DRM_FORMAT_VYUY ||
+ format->format == DRM_FORMAT_YUYV ||
+ format->format == DRM_FORMAT_YVYU)
+ p1_ba = cma_obj->paddr + fb->offsets[0] +
+ fb->pitches[0] * (state->src.y1 >> 16) +
+ 2 * format->char_per_block[0] * (state->src.x1 >> 17);
+
+ if (format->format == DRM_FORMAT_NV12 ||
+ format->format == DRM_FORMAT_NV21)
+ p2_ba = cma_obj->paddr + fb->offsets[1] +
+ (((fb->pitches[1] >> 1) * (state->src.y1 >> 17) +
+ (state->src.x1 >> 17)) << 1);
+
+ dcss_dpr_addr_set(dcss->dpr, dcss_plane->ch_num, p1_ba, p2_ba,
+ fb->pitches[0]);
+}
+
+static bool dcss_plane_needs_setup(struct drm_plane_state *state,
+ struct drm_plane_state *old_state)
+{
+ struct drm_framebuffer *fb = state->fb;
+ struct drm_framebuffer *old_fb = old_state->fb;
+
+ return state->crtc_x != old_state->crtc_x ||
+ state->crtc_y != old_state->crtc_y ||
+ state->crtc_w != old_state->crtc_w ||
+ state->crtc_h != old_state->crtc_h ||
+ state->src_x != old_state->src_x ||
+ state->src_y != old_state->src_y ||
+ state->src_w != old_state->src_w ||
+ state->src_h != old_state->src_h ||
+ fb->format->format != old_fb->format->format ||
+ fb->modifier != old_fb->modifier ||
+ state->rotation != old_state->rotation;
+}
+
+static void dcss_plane_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct drm_plane_state *state = plane->state;
+ struct dcss_plane *dcss_plane = to_dcss_plane(plane);
+ struct dcss_dev *dcss = plane->dev->dev_private;
+ struct drm_framebuffer *fb = state->fb;
+ u32 pixel_format;
+ struct drm_crtc_state *crtc_state;
+ bool modifiers_present;
+ u32 src_w, src_h, dst_w, dst_h;
+ struct drm_rect src, dst;
+ bool enable = true;
+
+ if (!fb || !state->crtc || !state->visible)
+ return;
+
+ pixel_format = state->fb->format->format;
+ crtc_state = state->crtc->state;
+ modifiers_present = !!(fb->flags & DRM_MODE_FB_MODIFIERS);
+
+ if (old_state->fb && !drm_atomic_crtc_needs_modeset(crtc_state) &&
+ !dcss_plane_needs_setup(state, old_state)) {
+ dcss_plane_atomic_set_base(dcss_plane);
+ return;
+ }
+
+ src = plane->state->src;
+ dst = plane->state->dst;
+
+ /*
+ * The width and height after clipping.
+ */
+ src_w = drm_rect_width(&src) >> 16;
+ src_h = drm_rect_height(&src) >> 16;
+ dst_w = drm_rect_width(&dst);
+ dst_h = drm_rect_height(&dst);
+
+ if (plane->type == DRM_PLANE_TYPE_OVERLAY &&
+ modifiers_present && fb->modifier == DRM_FORMAT_MOD_LINEAR)
+ modifiers_present = false;
+
+ dcss_dpr_format_set(dcss->dpr, dcss_plane->ch_num, state->fb->format,
+ modifiers_present ? fb->modifier :
+ DRM_FORMAT_MOD_LINEAR);
+ dcss_dpr_set_res(dcss->dpr, dcss_plane->ch_num, src_w, src_h);
+ dcss_dpr_set_rotation(dcss->dpr, dcss_plane->ch_num,
+ state->rotation);
+
+ dcss_plane_atomic_set_base(dcss_plane);
+
+ dcss_scaler_setup(dcss->scaler, dcss_plane->ch_num,
+ state->fb->format, src_w, src_h,
+ dst_w, dst_h,
+ drm_mode_vrefresh(&crtc_state->mode));
+
+ dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num,
+ dst.x1, dst.y1, dst_w, dst_h);
+ dcss_dtg_plane_alpha_set(dcss->dtg, dcss_plane->ch_num,
+ fb->format, state->alpha >> 8);
+
+ if (!dcss_plane->ch_num && (state->alpha >> 8) == 0)
+ enable = false;
+
+ dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, enable);
+ dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, enable);
+
+ if (!enable)
+ dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num,
+ 0, 0, 0, 0);
+
+ dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, enable);
+}
+
+static void dcss_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct dcss_plane *dcss_plane = to_dcss_plane(plane);
+ struct dcss_dev *dcss = plane->dev->dev_private;
+
+ dcss_dpr_enable(dcss->dpr, dcss_plane->ch_num, false);
+ dcss_scaler_ch_enable(dcss->scaler, dcss_plane->ch_num, false);
+ dcss_dtg_plane_pos_set(dcss->dtg, dcss_plane->ch_num, 0, 0, 0, 0);
+ dcss_dtg_ch_enable(dcss->dtg, dcss_plane->ch_num, false);
+}
+
+static const struct drm_plane_helper_funcs dcss_plane_helper_funcs = {
+ .prepare_fb = drm_gem_fb_prepare_fb,
+ .atomic_check = dcss_plane_atomic_check,
+ .atomic_update = dcss_plane_atomic_update,
+ .atomic_disable = dcss_plane_atomic_disable,
+};
+
+struct dcss_plane *dcss_plane_init(struct drm_device *drm,
+ unsigned int possible_crtcs,
+ enum drm_plane_type type,
+ unsigned int zpos)
+{
+ struct dcss_plane *dcss_plane;
+ const u64 *format_modifiers = dcss_video_format_modifiers;
+ int ret;
+
+ if (zpos > 2)
+ return ERR_PTR(-EINVAL);
+
+ dcss_plane = kzalloc(sizeof(*dcss_plane), GFP_KERNEL);
+ if (!dcss_plane) {
+ DRM_ERROR("failed to allocate plane\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ if (type == DRM_PLANE_TYPE_PRIMARY)
+ format_modifiers = dcss_graphics_format_modifiers;
+
+ ret = drm_universal_plane_init(drm, &dcss_plane->base, possible_crtcs,
+ &dcss_plane_funcs, dcss_common_formats,
+ ARRAY_SIZE(dcss_common_formats),
+ format_modifiers, type, NULL);
+ if (ret) {
+ DRM_ERROR("failed to initialize plane\n");
+ kfree(dcss_plane);
+ return ERR_PTR(ret);
+ }
+
+ drm_plane_helper_add(&dcss_plane->base, &dcss_plane_helper_funcs);
+
+ ret = drm_plane_create_zpos_immutable_property(&dcss_plane->base, zpos);
+ if (ret)
+ return ERR_PTR(ret);
+
+ drm_plane_create_rotation_property(&dcss_plane->base,
+ DRM_MODE_ROTATE_0,
+ DRM_MODE_ROTATE_0 |
+ DRM_MODE_ROTATE_90 |
+ DRM_MODE_ROTATE_180 |
+ DRM_MODE_ROTATE_270 |
+ DRM_MODE_REFLECT_X |
+ DRM_MODE_REFLECT_Y);
+
+ dcss_plane->ch_num = zpos;
+
+ return dcss_plane;
+}
diff --git a/drivers/gpu/drm/imx/dcss/dcss-scaler.c b/drivers/gpu/drm/imx/dcss/dcss-scaler.c
new file mode 100644
index 000000000000..cd21905de580
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcss/dcss-scaler.c
@@ -0,0 +1,826 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ *
+ * Scaling algorithms were contributed by Dzung Hoang <dzung.hoang@nxp.com>
+ */
+
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#include "dcss-dev.h"
+
+#define DCSS_SCALER_CTRL 0x00
+#define SCALER_EN BIT(0)
+#define REPEAT_EN BIT(4)
+#define SCALE2MEM_EN BIT(8)
+#define MEM2OFIFO_EN BIT(12)
+#define DCSS_SCALER_OFIFO_CTRL 0x04
+#define OFIFO_LOW_THRES_POS 0
+#define OFIFO_LOW_THRES_MASK GENMASK(9, 0)
+#define OFIFO_HIGH_THRES_POS 16
+#define OFIFO_HIGH_THRES_MASK GENMASK(25, 16)
+#define UNDERRUN_DETECT_CLR BIT(26)
+#define LOW_THRES_DETECT_CLR BIT(27)
+#define HIGH_THRES_DETECT_CLR BIT(28)
+#define UNDERRUN_DETECT_EN BIT(29)
+#define LOW_THRES_DETECT_EN BIT(30)
+#define HIGH_THRES_DETECT_EN BIT(31)
+#define DCSS_SCALER_SDATA_CTRL 0x08
+#define YUV_EN BIT(0)
+#define RTRAM_8LINES BIT(1)
+#define Y_UV_BYTE_SWAP BIT(4)
+#define A2R10G10B10_FORMAT_POS 8
+#define A2R10G10B10_FORMAT_MASK GENMASK(11, 8)
+#define DCSS_SCALER_BIT_DEPTH 0x0C
+#define LUM_BIT_DEPTH_POS 0
+#define LUM_BIT_DEPTH_MASK GENMASK(1, 0)
+#define CHR_BIT_DEPTH_POS 4
+#define CHR_BIT_DEPTH_MASK GENMASK(5, 4)
+#define DCSS_SCALER_SRC_FORMAT 0x10
+#define DCSS_SCALER_DST_FORMAT 0x14
+#define FORMAT_MASK GENMASK(1, 0)
+#define DCSS_SCALER_SRC_LUM_RES 0x18
+#define DCSS_SCALER_SRC_CHR_RES 0x1C
+#define DCSS_SCALER_DST_LUM_RES 0x20
+#define DCSS_SCALER_DST_CHR_RES 0x24
+#define WIDTH_POS 0
+#define WIDTH_MASK GENMASK(11, 0)
+#define HEIGHT_POS 16
+#define HEIGHT_MASK GENMASK(27, 16)
+#define DCSS_SCALER_V_LUM_START 0x48
+#define V_START_MASK GENMASK(15, 0)
+#define DCSS_SCALER_V_LUM_INC 0x4C
+#define V_INC_MASK GENMASK(15, 0)
+#define DCSS_SCALER_H_LUM_START 0x50
+#define H_START_MASK GENMASK(18, 0)
+#define DCSS_SCALER_H_LUM_INC 0x54
+#define H_INC_MASK GENMASK(15, 0)
+#define DCSS_SCALER_V_CHR_START 0x58
+#define DCSS_SCALER_V_CHR_INC 0x5C
+#define DCSS_SCALER_H_CHR_START 0x60
+#define DCSS_SCALER_H_CHR_INC 0x64
+#define DCSS_SCALER_COEF_VLUM 0x80
+#define DCSS_SCALER_COEF_HLUM 0x140
+#define DCSS_SCALER_COEF_VCHR 0x200
+#define DCSS_SCALER_COEF_HCHR 0x300
+
+struct dcss_scaler_ch {
+ void __iomem *base_reg;
+ u32 base_ofs;
+ struct dcss_scaler *scl;
+
+ u32 sdata_ctrl;
+ u32 scaler_ctrl;
+
+ bool scaler_ctrl_chgd;
+
+ u32 c_vstart;
+ u32 c_hstart;
+};
+
+struct dcss_scaler {
+ struct device *dev;
+
+ struct dcss_ctxld *ctxld;
+ u32 ctx_id;
+
+ struct dcss_scaler_ch ch[3];
+};
+
+/* scaler coefficients generator */
+#define PSC_FRAC_BITS 30
+#define PSC_FRAC_SCALE BIT(PSC_FRAC_BITS)
+#define PSC_BITS_FOR_PHASE 4
+#define PSC_NUM_PHASES 16
+#define PSC_STORED_PHASES (PSC_NUM_PHASES / 2 + 1)
+#define PSC_NUM_TAPS 7
+#define PSC_NUM_TAPS_RGBA 5
+#define PSC_COEFF_PRECISION 10
+#define PSC_PHASE_FRACTION_BITS 13
+#define PSC_PHASE_MASK (PSC_NUM_PHASES - 1)
+#define PSC_Q_FRACTION 19
+#define PSC_Q_ROUND_OFFSET (1 << (PSC_Q_FRACTION - 1))
+
+/**
+ * mult_q() - Performs fixed-point multiplication.
+ * @A: multiplier
+ * @B: multiplicand
+ */
+static int mult_q(int A, int B)
+{
+ int result;
+ s64 temp;
+
+ temp = (int64_t)A * (int64_t)B;
+ temp += PSC_Q_ROUND_OFFSET;
+ result = (int)(temp >> PSC_Q_FRACTION);
+ return result;
+}
+
+/**
+ * div_q() - Performs fixed-point division.
+ * @A: dividend
+ * @B: divisor
+ */
+static int div_q(int A, int B)
+{
+ int result;
+ s64 temp;
+
+ temp = (int64_t)A << PSC_Q_FRACTION;
+ if ((temp >= 0 && B >= 0) || (temp < 0 && B < 0))
+ temp += B / 2;
+ else
+ temp -= B / 2;
+
+ result = (int)(temp / B);
+ return result;
+}
+
+/**
+ * exp_approx_q() - Compute approximation to exp(x) function using Taylor
+ * series.
+ * @x: fixed-point argument of exp function
+ */
+static int exp_approx_q(int x)
+{
+ int sum = 1 << PSC_Q_FRACTION;
+ int term = 1 << PSC_Q_FRACTION;
+
+ term = mult_q(term, div_q(x, 1 << PSC_Q_FRACTION));
+ sum += term;
+ term = mult_q(term, div_q(x, 2 << PSC_Q_FRACTION));
+ sum += term;
+ term = mult_q(term, div_q(x, 3 << PSC_Q_FRACTION));
+ sum += term;
+ term = mult_q(term, div_q(x, 4 << PSC_Q_FRACTION));
+ sum += term;
+
+ return sum;
+}
+
+/**
+ * dcss_scaler_gaussian_filter() - Generate gaussian prototype filter.
+ * @fc_q: fixed-point cutoff frequency normalized to range [0, 1]
+ * @use_5_taps: indicates whether to use 5 taps or 7 taps
+ * @coef: output filter coefficients
+ */
+static void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps,
+ bool phase0_identity,
+ int coef[][PSC_NUM_TAPS])
+{
+ int sigma_q, g0_q, g1_q, g2_q;
+ int tap_cnt1, tap_cnt2, tap_idx, phase_cnt;
+ int mid;
+ int phase;
+ int i;
+ int taps;
+
+ if (use_5_taps)
+ for (phase = 0; phase < PSC_STORED_PHASES; phase++) {
+ coef[phase][0] = 0;
+ coef[phase][PSC_NUM_TAPS - 1] = 0;
+ }
+
+ /* seed coefficient scanner */
+ taps = use_5_taps ? PSC_NUM_TAPS_RGBA : PSC_NUM_TAPS;
+ mid = (PSC_NUM_PHASES * taps) / 2 - 1;
+ phase_cnt = (PSC_NUM_PHASES * (PSC_NUM_TAPS + 1)) / 2;
+ tap_cnt1 = (PSC_NUM_PHASES * PSC_NUM_TAPS) / 2;
+ tap_cnt2 = (PSC_NUM_PHASES * PSC_NUM_TAPS) / 2;
+
+ /* seed gaussian filter generator */
+ sigma_q = div_q(PSC_Q_ROUND_OFFSET, fc_q);
+ g0_q = 1 << PSC_Q_FRACTION;
+ g1_q = exp_approx_q(div_q(-PSC_Q_ROUND_OFFSET,
+ mult_q(sigma_q, sigma_q)));
+ g2_q = mult_q(g1_q, g1_q);
+ coef[phase_cnt & PSC_PHASE_MASK][tap_cnt1 >> PSC_BITS_FOR_PHASE] = g0_q;
+
+ for (i = 0; i < mid; i++) {
+ phase_cnt++;
+ tap_cnt1--;
+ tap_cnt2++;
+
+ g0_q = mult_q(g0_q, g1_q);
+ g1_q = mult_q(g1_q, g2_q);
+
+ if ((phase_cnt & PSC_PHASE_MASK) <= 8) {
+ tap_idx = tap_cnt1 >> PSC_BITS_FOR_PHASE;
+ coef[phase_cnt & PSC_PHASE_MASK][tap_idx] = g0_q;
+ }
+ if (((-phase_cnt) & PSC_PHASE_MASK) <= 8) {
+ tap_idx = tap_cnt2 >> PSC_BITS_FOR_PHASE;
+ coef[(-phase_cnt) & PSC_PHASE_MASK][tap_idx] = g0_q;
+ }
+ }
+
+ phase_cnt++;
+ tap_cnt1--;
+ coef[phase_cnt & PSC_PHASE_MASK][tap_cnt1 >> PSC_BITS_FOR_PHASE] = 0;
+
+ /* override phase 0 with identity filter if specified */
+ if (phase0_identity)
+ for (i = 0; i < PSC_NUM_TAPS; i++)
+ coef[0][i] = i == (PSC_NUM_TAPS >> 1) ?
+ (1 << PSC_COEFF_PRECISION) : 0;
+
+ /* normalize coef */
+ for (phase = 0; phase < PSC_STORED_PHASES; phase++) {
+ int sum = 0;
+ s64 ll_temp;
+
+ for (i = 0; i < PSC_NUM_TAPS; i++)
+ sum += coef[phase][i];
+ for (i = 0; i < PSC_NUM_TAPS; i++) {
+ ll_temp = coef[phase][i];
+ ll_temp <<= PSC_COEFF_PRECISION;
+ ll_temp += sum >> 1;
+ ll_temp /= sum;
+ coef[phase][i] = (int)ll_temp;
+ }
+ }
+}
+
+/**
+ * dcss_scaler_filter_design() - Compute filter coefficients using
+ * Gaussian filter.
+ * @src_length: length of input
+ * @dst_length: length of output
+ * @use_5_taps: 0 for 7 taps per phase, 1 for 5 taps
+ * @coef: output coefficients
+ */
+static void dcss_scaler_filter_design(int src_length, int dst_length,
+ bool use_5_taps, bool phase0_identity,
+ int coef[][PSC_NUM_TAPS])
+{
+ int fc_q;
+
+ /* compute cutoff frequency */
+ if (dst_length >= src_length)
+ fc_q = div_q(1, PSC_NUM_PHASES);
+ else
+ fc_q = div_q(dst_length, src_length * PSC_NUM_PHASES);
+
+ /* compute gaussian filter coefficients */
+ dcss_scaler_gaussian_filter(fc_q, use_5_taps, phase0_identity, coef);
+}
+
+static void dcss_scaler_write(struct dcss_scaler_ch *ch, u32 val, u32 ofs)
+{
+ struct dcss_scaler *scl = ch->scl;
+
+ dcss_ctxld_write(scl->ctxld, scl->ctx_id, val, ch->base_ofs + ofs);
+}
+
+static int dcss_scaler_ch_init_all(struct dcss_scaler *scl,
+ unsigned long scaler_base)
+{
+ struct dcss_scaler_ch *ch;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ ch = &scl->ch[i];
+
+ ch->base_ofs = scaler_base + i * 0x400;
+
+ ch->base_reg = ioremap(ch->base_ofs, SZ_4K);
+ if (!ch->base_reg) {
+ dev_err(scl->dev, "scaler: unable to remap ch base\n");
+ return -ENOMEM;
+ }
+
+ ch->scl = scl;
+ }
+
+ return 0;
+}
+
+int dcss_scaler_init(struct dcss_dev *dcss, unsigned long scaler_base)
+{
+ struct dcss_scaler *scaler;
+
+ scaler = kzalloc(sizeof(*scaler), GFP_KERNEL);
+ if (!scaler)
+ return -ENOMEM;
+
+ dcss->scaler = scaler;
+ scaler->dev = dcss->dev;
+ scaler->ctxld = dcss->ctxld;
+ scaler->ctx_id = CTX_SB_HP;
+
+ if (dcss_scaler_ch_init_all(scaler, scaler_base)) {
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (scaler->ch[i].base_reg)
+ iounmap(scaler->ch[i].base_reg);
+ }
+
+ kfree(scaler);
+
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void dcss_scaler_exit(struct dcss_scaler *scl)
+{
+ int ch_no;
+
+ for (ch_no = 0; ch_no < 3; ch_no++) {
+ struct dcss_scaler_ch *ch = &scl->ch[ch_no];
+
+ dcss_writel(0, ch->base_reg + DCSS_SCALER_CTRL);
+
+ if (ch->base_reg)
+ iounmap(ch->base_reg);
+ }
+
+ kfree(scl);
+}
+
+void dcss_scaler_ch_enable(struct dcss_scaler *scl, int ch_num, bool en)
+{
+ struct dcss_scaler_ch *ch = &scl->ch[ch_num];
+ u32 scaler_ctrl;
+
+ scaler_ctrl = en ? SCALER_EN | REPEAT_EN : 0;
+
+ if (en)
+ dcss_scaler_write(ch, ch->sdata_ctrl, DCSS_SCALER_SDATA_CTRL);
+
+ if (ch->scaler_ctrl != scaler_ctrl)
+ ch->scaler_ctrl_chgd = true;
+
+ ch->scaler_ctrl = scaler_ctrl;
+}
+
+static void dcss_scaler_yuv_enable(struct dcss_scaler_ch *ch, bool en)
+{
+ ch->sdata_ctrl &= ~YUV_EN;
+ ch->sdata_ctrl |= en ? YUV_EN : 0;
+}
+
+static void dcss_scaler_rtr_8lines_enable(struct dcss_scaler_ch *ch, bool en)
+{
+ ch->sdata_ctrl &= ~RTRAM_8LINES;
+ ch->sdata_ctrl |= en ? RTRAM_8LINES : 0;
+}
+
+static void dcss_scaler_bit_depth_set(struct dcss_scaler_ch *ch, int depth)
+{
+ u32 val;
+
+ val = depth == 30 ? 2 : 0;
+
+ dcss_scaler_write(ch,
+ ((val << CHR_BIT_DEPTH_POS) & CHR_BIT_DEPTH_MASK) |
+ ((val << LUM_BIT_DEPTH_POS) & LUM_BIT_DEPTH_MASK),
+ DCSS_SCALER_BIT_DEPTH);
+}
+
+enum buffer_format {
+ BUF_FMT_YUV420,
+ BUF_FMT_YUV422,
+ BUF_FMT_ARGB8888_YUV444,
+};
+
+enum chroma_location {
+ PSC_LOC_HORZ_0_VERT_1_OVER_4 = 0,
+ PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4 = 1,
+ PSC_LOC_HORZ_0_VERT_0 = 2,
+ PSC_LOC_HORZ_1_OVER_4_VERT_0 = 3,
+ PSC_LOC_HORZ_0_VERT_1_OVER_2 = 4,
+ PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2 = 5
+};
+
+static void dcss_scaler_format_set(struct dcss_scaler_ch *ch,
+ enum buffer_format src_fmt,
+ enum buffer_format dst_fmt)
+{
+ dcss_scaler_write(ch, src_fmt, DCSS_SCALER_SRC_FORMAT);
+ dcss_scaler_write(ch, dst_fmt, DCSS_SCALER_DST_FORMAT);
+}
+
+static void dcss_scaler_res_set(struct dcss_scaler_ch *ch,
+ int src_xres, int src_yres,
+ int dst_xres, int dst_yres,
+ u32 pix_format, enum buffer_format dst_format)
+{
+ u32 lsrc_xres, lsrc_yres, csrc_xres, csrc_yres;
+ u32 ldst_xres, ldst_yres, cdst_xres, cdst_yres;
+ bool src_is_444 = true;
+
+ lsrc_xres = src_xres;
+ csrc_xres = src_xres;
+ lsrc_yres = src_yres;
+ csrc_yres = src_yres;
+ ldst_xres = dst_xres;
+ cdst_xres = dst_xres;
+ ldst_yres = dst_yres;
+ cdst_yres = dst_yres;
+
+ if (pix_format == DRM_FORMAT_UYVY || pix_format == DRM_FORMAT_VYUY ||
+ pix_format == DRM_FORMAT_YUYV || pix_format == DRM_FORMAT_YVYU) {
+ csrc_xres >>= 1;
+ src_is_444 = false;
+ } else if (pix_format == DRM_FORMAT_NV12 ||
+ pix_format == DRM_FORMAT_NV21) {
+ csrc_xres >>= 1;
+ csrc_yres >>= 1;
+ src_is_444 = false;
+ }
+
+ if (dst_format == BUF_FMT_YUV422)
+ cdst_xres >>= 1;
+
+ /* for 4:4:4 to 4:2:2 conversion, source height should be 1 less */
+ if (src_is_444 && dst_format == BUF_FMT_YUV422) {
+ lsrc_yres--;
+ csrc_yres--;
+ }
+
+ dcss_scaler_write(ch, (((lsrc_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) |
+ (((lsrc_xres - 1) << WIDTH_POS) & WIDTH_MASK),
+ DCSS_SCALER_SRC_LUM_RES);
+ dcss_scaler_write(ch, (((csrc_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) |
+ (((csrc_xres - 1) << WIDTH_POS) & WIDTH_MASK),
+ DCSS_SCALER_SRC_CHR_RES);
+ dcss_scaler_write(ch, (((ldst_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) |
+ (((ldst_xres - 1) << WIDTH_POS) & WIDTH_MASK),
+ DCSS_SCALER_DST_LUM_RES);
+ dcss_scaler_write(ch, (((cdst_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) |
+ (((cdst_xres - 1) << WIDTH_POS) & WIDTH_MASK),
+ DCSS_SCALER_DST_CHR_RES);
+}
+
+#define downscale_fp(factor, fp_pos) ((factor) << (fp_pos))
+#define upscale_fp(factor, fp_pos) ((1 << (fp_pos)) / (factor))
+
+struct dcss_scaler_factors {
+ int downscale;
+ int upscale;
+};
+
+static const struct dcss_scaler_factors dcss_scaler_factors[] = {
+ {3, 8}, {5, 8}, {5, 8},
+};
+
+static void dcss_scaler_fractions_set(struct dcss_scaler_ch *ch,
+ int src_xres, int src_yres,
+ int dst_xres, int dst_yres,
+ u32 src_format, u32 dst_format,
+ enum chroma_location src_chroma_loc)
+{
+ int src_c_xres, src_c_yres, dst_c_xres, dst_c_yres;
+ u32 l_vinc, l_hinc, c_vinc, c_hinc;
+ u32 c_vstart, c_hstart;
+
+ src_c_xres = src_xres;
+ src_c_yres = src_yres;
+ dst_c_xres = dst_xres;
+ dst_c_yres = dst_yres;
+
+ c_vstart = 0;
+ c_hstart = 0;
+
+ /* adjustments for source chroma location */
+ if (src_format == BUF_FMT_YUV420) {
+ /* vertical input chroma position adjustment */
+ switch (src_chroma_loc) {
+ case PSC_LOC_HORZ_0_VERT_1_OVER_4:
+ case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4:
+ /*
+ * move chroma up to first luma line
+ * (1/4 chroma input line spacing)
+ */
+ c_vstart -= (1 << (PSC_PHASE_FRACTION_BITS - 2));
+ break;
+ case PSC_LOC_HORZ_0_VERT_1_OVER_2:
+ case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2:
+ /*
+ * move chroma up to first luma line
+ * (1/2 chroma input line spacing)
+ */
+ c_vstart -= (1 << (PSC_PHASE_FRACTION_BITS - 1));
+ break;
+ default:
+ break;
+ }
+ /* horizontal input chroma position adjustment */
+ switch (src_chroma_loc) {
+ case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4:
+ case PSC_LOC_HORZ_1_OVER_4_VERT_0:
+ case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2:
+ /* move chroma left 1/4 chroma input sample spacing */
+ c_hstart -= (1 << (PSC_PHASE_FRACTION_BITS - 2));
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* adjustments to chroma resolution */
+ if (src_format == BUF_FMT_YUV420) {
+ src_c_xres >>= 1;
+ src_c_yres >>= 1;
+ } else if (src_format == BUF_FMT_YUV422) {
+ src_c_xres >>= 1;
+ }
+
+ if (dst_format == BUF_FMT_YUV422)
+ dst_c_xres >>= 1;
+
+ l_vinc = ((src_yres << 13) + (dst_yres >> 1)) / dst_yres;
+ c_vinc = ((src_c_yres << 13) + (dst_c_yres >> 1)) / dst_c_yres;
+ l_hinc = ((src_xres << 13) + (dst_xres >> 1)) / dst_xres;
+ c_hinc = ((src_c_xres << 13) + (dst_c_xres >> 1)) / dst_c_xres;
+
+ /* save chroma start phase */
+ ch->c_vstart = c_vstart;
+ ch->c_hstart = c_hstart;
+
+ dcss_scaler_write(ch, 0, DCSS_SCALER_V_LUM_START);
+ dcss_scaler_write(ch, l_vinc, DCSS_SCALER_V_LUM_INC);
+
+ dcss_scaler_write(ch, 0, DCSS_SCALER_H_LUM_START);
+ dcss_scaler_write(ch, l_hinc, DCSS_SCALER_H_LUM_INC);
+
+ dcss_scaler_write(ch, c_vstart, DCSS_SCALER_V_CHR_START);
+ dcss_scaler_write(ch, c_vinc, DCSS_SCALER_V_CHR_INC);
+
+ dcss_scaler_write(ch, c_hstart, DCSS_SCALER_H_CHR_START);
+ dcss_scaler_write(ch, c_hinc, DCSS_SCALER_H_CHR_INC);
+}
+
+int dcss_scaler_get_min_max_ratios(struct dcss_scaler *scl, int ch_num,
+ int *min, int *max)
+{
+ *min = upscale_fp(dcss_scaler_factors[ch_num].upscale, 16);
+ *max = downscale_fp(dcss_scaler_factors[ch_num].downscale, 16);
+
+ return 0;
+}
+
+static void dcss_scaler_program_5_coef_set(struct dcss_scaler_ch *ch,
+ int base_addr,
+ int coef[][PSC_NUM_TAPS])
+{
+ int i, phase;
+
+ for (i = 0; i < PSC_STORED_PHASES; i++) {
+ dcss_scaler_write(ch, ((coef[i][1] & 0xfff) << 16 |
+ (coef[i][2] & 0xfff) << 4 |
+ (coef[i][3] & 0xf00) >> 8),
+ base_addr + i * sizeof(u32));
+ dcss_scaler_write(ch, ((coef[i][3] & 0x0ff) << 20 |
+ (coef[i][4] & 0xfff) << 8 |
+ (coef[i][5] & 0xff0) >> 4),
+ base_addr + 0x40 + i * sizeof(u32));
+ dcss_scaler_write(ch, ((coef[i][5] & 0x00f) << 24),
+ base_addr + 0x80 + i * sizeof(u32));
+ }
+
+ /* reverse both phase and tap orderings */
+ for (phase = (PSC_NUM_PHASES >> 1) - 1;
+ i < PSC_NUM_PHASES; i++, phase--) {
+ dcss_scaler_write(ch, ((coef[phase][5] & 0xfff) << 16 |
+ (coef[phase][4] & 0xfff) << 4 |
+ (coef[phase][3] & 0xf00) >> 8),
+ base_addr + i * sizeof(u32));
+ dcss_scaler_write(ch, ((coef[phase][3] & 0x0ff) << 20 |
+ (coef[phase][2] & 0xfff) << 8 |
+ (coef[phase][1] & 0xff0) >> 4),
+ base_addr + 0x40 + i * sizeof(u32));
+ dcss_scaler_write(ch, ((coef[phase][1] & 0x00f) << 24),
+ base_addr + 0x80 + i * sizeof(u32));
+ }
+}
+
+static void dcss_scaler_program_7_coef_set(struct dcss_scaler_ch *ch,
+ int base_addr,
+ int coef[][PSC_NUM_TAPS])
+{
+ int i, phase;
+
+ for (i = 0; i < PSC_STORED_PHASES; i++) {
+ dcss_scaler_write(ch, ((coef[i][0] & 0xfff) << 16 |
+ (coef[i][1] & 0xfff) << 4 |
+ (coef[i][2] & 0xf00) >> 8),
+ base_addr + i * sizeof(u32));
+ dcss_scaler_write(ch, ((coef[i][2] & 0x0ff) << 20 |
+ (coef[i][3] & 0xfff) << 8 |
+ (coef[i][4] & 0xff0) >> 4),
+ base_addr + 0x40 + i * sizeof(u32));
+ dcss_scaler_write(ch, ((coef[i][4] & 0x00f) << 24 |
+ (coef[i][5] & 0xfff) << 12 |
+ (coef[i][6] & 0xfff)),
+ base_addr + 0x80 + i * sizeof(u32));
+ }
+
+ /* reverse both phase and tap orderings */
+ for (phase = (PSC_NUM_PHASES >> 1) - 1;
+ i < PSC_NUM_PHASES; i++, phase--) {
+ dcss_scaler_write(ch, ((coef[phase][6] & 0xfff) << 16 |
+ (coef[phase][5] & 0xfff) << 4 |
+ (coef[phase][4] & 0xf00) >> 8),
+ base_addr + i * sizeof(u32));
+ dcss_scaler_write(ch, ((coef[phase][4] & 0x0ff) << 20 |
+ (coef[phase][3] & 0xfff) << 8 |
+ (coef[phase][2] & 0xff0) >> 4),
+ base_addr + 0x40 + i * sizeof(u32));
+ dcss_scaler_write(ch, ((coef[phase][2] & 0x00f) << 24 |
+ (coef[phase][1] & 0xfff) << 12 |
+ (coef[phase][0] & 0xfff)),
+ base_addr + 0x80 + i * sizeof(u32));
+ }
+}
+
+static void dcss_scaler_yuv_coef_set(struct dcss_scaler_ch *ch,
+ enum buffer_format src_format,
+ enum buffer_format dst_format,
+ bool use_5_taps,
+ int src_xres, int src_yres, int dst_xres,
+ int dst_yres)
+{
+ int coef[PSC_STORED_PHASES][PSC_NUM_TAPS];
+ bool program_5_taps = use_5_taps ||
+ (dst_format == BUF_FMT_YUV422 &&
+ src_format == BUF_FMT_ARGB8888_YUV444);
+
+ /* horizontal luma */
+ dcss_scaler_filter_design(src_xres, dst_xres, false,
+ src_xres == dst_xres, coef);
+ dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HLUM, coef);
+
+ /* vertical luma */
+ dcss_scaler_filter_design(src_yres, dst_yres, program_5_taps,
+ src_yres == dst_yres, coef);
+
+ if (program_5_taps)
+ dcss_scaler_program_5_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef);
+ else
+ dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef);
+
+ /* adjust chroma resolution */
+ if (src_format != BUF_FMT_ARGB8888_YUV444)
+ src_xres >>= 1;
+ if (src_format == BUF_FMT_YUV420)
+ src_yres >>= 1;
+ if (dst_format != BUF_FMT_ARGB8888_YUV444)
+ dst_xres >>= 1;
+ if (dst_format == BUF_FMT_YUV420) /* should not happen */
+ dst_yres >>= 1;
+
+ /* horizontal chroma */
+ dcss_scaler_filter_design(src_xres, dst_xres, false,
+ (src_xres == dst_xres) && (ch->c_hstart == 0),
+ coef);
+
+ dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HCHR, coef);
+
+ /* vertical chroma */
+ dcss_scaler_filter_design(src_yres, dst_yres, program_5_taps,
+ (src_yres == dst_yres) && (ch->c_vstart == 0),
+ coef);
+ if (program_5_taps)
+ dcss_scaler_program_5_coef_set(ch, DCSS_SCALER_COEF_VCHR, coef);
+ else
+ dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VCHR, coef);
+}
+
+static void dcss_scaler_rgb_coef_set(struct dcss_scaler_ch *ch,
+ int src_xres, int src_yres, int dst_xres,
+ int dst_yres)
+{
+ int coef[PSC_STORED_PHASES][PSC_NUM_TAPS];
+
+ /* horizontal RGB */
+ dcss_scaler_filter_design(src_xres, dst_xres, false,
+ src_xres == dst_xres, coef);
+ dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HLUM, coef);
+
+ /* vertical RGB */
+ dcss_scaler_filter_design(src_yres, dst_yres, false,
+ src_yres == dst_yres, coef);
+ dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef);
+}
+
+static void dcss_scaler_set_rgb10_order(struct dcss_scaler_ch *ch,
+ const struct drm_format_info *format)
+{
+ u32 a2r10g10b10_format;
+
+ if (format->is_yuv)
+ return;
+
+ ch->sdata_ctrl &= ~A2R10G10B10_FORMAT_MASK;
+
+ if (format->depth != 30)
+ return;
+
+ switch (format->format) {
+ case DRM_FORMAT_ARGB2101010:
+ case DRM_FORMAT_XRGB2101010:
+ a2r10g10b10_format = 0;
+ break;
+
+ case DRM_FORMAT_ABGR2101010:
+ case DRM_FORMAT_XBGR2101010:
+ a2r10g10b10_format = 5;
+ break;
+
+ case DRM_FORMAT_RGBA1010102:
+ case DRM_FORMAT_RGBX1010102:
+ a2r10g10b10_format = 6;
+ break;
+
+ case DRM_FORMAT_BGRA1010102:
+ case DRM_FORMAT_BGRX1010102:
+ a2r10g10b10_format = 11;
+ break;
+
+ default:
+ a2r10g10b10_format = 0;
+ break;
+ }
+
+ ch->sdata_ctrl |= a2r10g10b10_format << A2R10G10B10_FORMAT_POS;
+}
+
+void dcss_scaler_setup(struct dcss_scaler *scl, int ch_num,
+ const struct drm_format_info *format,
+ int src_xres, int src_yres, int dst_xres, int dst_yres,
+ u32 vrefresh_hz)
+{
+ struct dcss_scaler_ch *ch = &scl->ch[ch_num];
+ unsigned int pixel_depth = 0;
+ bool rtr_8line_en = false;
+ bool use_5_taps = false;
+ enum buffer_format src_format = BUF_FMT_ARGB8888_YUV444;
+ enum buffer_format dst_format = BUF_FMT_ARGB8888_YUV444;
+ u32 pix_format = format->format;
+
+ if (format->is_yuv) {
+ dcss_scaler_yuv_enable(ch, true);
+
+ if (pix_format == DRM_FORMAT_NV12 ||
+ pix_format == DRM_FORMAT_NV21) {
+ rtr_8line_en = true;
+ src_format = BUF_FMT_YUV420;
+ } else if (pix_format == DRM_FORMAT_UYVY ||
+ pix_format == DRM_FORMAT_VYUY ||
+ pix_format == DRM_FORMAT_YUYV ||
+ pix_format == DRM_FORMAT_YVYU) {
+ src_format = BUF_FMT_YUV422;
+ }
+
+ use_5_taps = !rtr_8line_en;
+ } else {
+ dcss_scaler_yuv_enable(ch, false);
+
+ pixel_depth = format->depth;
+ }
+
+ dcss_scaler_fractions_set(ch, src_xres, src_yres, dst_xres,
+ dst_yres, src_format, dst_format,
+ PSC_LOC_HORZ_0_VERT_1_OVER_4);
+
+ if (format->is_yuv)
+ dcss_scaler_yuv_coef_set(ch, src_format, dst_format,
+ use_5_taps, src_xres, src_yres,
+ dst_xres, dst_yres);
+ else
+ dcss_scaler_rgb_coef_set(ch, src_xres, src_yres,
+ dst_xres, dst_yres);
+
+ dcss_scaler_rtr_8lines_enable(ch, rtr_8line_en);
+ dcss_scaler_bit_depth_set(ch, pixel_depth);
+ dcss_scaler_set_rgb10_order(ch, format);
+ dcss_scaler_format_set(ch, src_format, dst_format);
+ dcss_scaler_res_set(ch, src_xres, src_yres, dst_xres, dst_yres,
+ pix_format, dst_format);
+}
+
+/* This function will be called from interrupt context. */
+void dcss_scaler_write_sclctrl(struct dcss_scaler *scl)
+{
+ int chnum;
+
+ dcss_ctxld_assert_locked(scl->ctxld);
+
+ for (chnum = 0; chnum < 3; chnum++) {
+ struct dcss_scaler_ch *ch = &scl->ch[chnum];
+
+ if (ch->scaler_ctrl_chgd) {
+ dcss_ctxld_write_irqsafe(scl->ctxld, scl->ctx_id,
+ ch->scaler_ctrl,
+ ch->base_ofs +
+ DCSS_SCALER_CTRL);
+ ch->scaler_ctrl_chgd = false;
+ }
+ }
+}
diff --git a/drivers/gpu/drm/imx/dcss/dcss-ss.c b/drivers/gpu/drm/imx/dcss/dcss-ss.c
new file mode 100644
index 000000000000..8ddf08da911b
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcss/dcss-ss.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#include "dcss-dev.h"
+
+#define DCSS_SS_SYS_CTRL 0x00
+#define RUN_EN BIT(0)
+#define DCSS_SS_DISPLAY 0x10
+#define LRC_X_POS 0
+#define LRC_X_MASK GENMASK(12, 0)
+#define LRC_Y_POS 16
+#define LRC_Y_MASK GENMASK(28, 16)
+#define DCSS_SS_HSYNC 0x20
+#define DCSS_SS_VSYNC 0x30
+#define SYNC_START_POS 0
+#define SYNC_START_MASK GENMASK(12, 0)
+#define SYNC_END_POS 16
+#define SYNC_END_MASK GENMASK(28, 16)
+#define SYNC_POL BIT(31)
+#define DCSS_SS_DE_ULC 0x40
+#define ULC_X_POS 0
+#define ULC_X_MASK GENMASK(12, 0)
+#define ULC_Y_POS 16
+#define ULC_Y_MASK GENMASK(28, 16)
+#define ULC_POL BIT(31)
+#define DCSS_SS_DE_LRC 0x50
+#define DCSS_SS_MODE 0x60
+#define PIPE_MODE_POS 0
+#define PIPE_MODE_MASK GENMASK(1, 0)
+#define DCSS_SS_COEFF 0x70
+#define HORIZ_A_POS 0
+#define HORIZ_A_MASK GENMASK(3, 0)
+#define HORIZ_B_POS 4
+#define HORIZ_B_MASK GENMASK(7, 4)
+#define HORIZ_C_POS 8
+#define HORIZ_C_MASK GENMASK(11, 8)
+#define HORIZ_H_NORM_POS 12
+#define HORIZ_H_NORM_MASK GENMASK(14, 12)
+#define VERT_A_POS 16
+#define VERT_A_MASK GENMASK(19, 16)
+#define VERT_B_POS 20
+#define VERT_B_MASK GENMASK(23, 20)
+#define VERT_C_POS 24
+#define VERT_C_MASK GENMASK(27, 24)
+#define VERT_H_NORM_POS 28
+#define VERT_H_NORM_MASK GENMASK(30, 28)
+#define DCSS_SS_CLIP_CB 0x80
+#define DCSS_SS_CLIP_CR 0x90
+#define CLIP_MIN_POS 0
+#define CLIP_MIN_MASK GENMASK(9, 0)
+#define CLIP_MAX_POS 0
+#define CLIP_MAX_MASK GENMASK(23, 16)
+#define DCSS_SS_INTER_MODE 0xA0
+#define INT_EN BIT(0)
+#define VSYNC_SHIFT BIT(1)
+
+struct dcss_ss {
+ struct device *dev;
+ void __iomem *base_reg;
+ u32 base_ofs;
+
+ struct dcss_ctxld *ctxld;
+ u32 ctx_id;
+
+ bool in_use;
+};
+
+static void dcss_ss_write(struct dcss_ss *ss, u32 val, u32 ofs)
+{
+ if (!ss->in_use)
+ dcss_writel(val, ss->base_reg + ofs);
+
+ dcss_ctxld_write(ss->ctxld, ss->ctx_id, val,
+ ss->base_ofs + ofs);
+}
+
+int dcss_ss_init(struct dcss_dev *dcss, unsigned long ss_base)
+{
+ struct dcss_ss *ss;
+
+ ss = kzalloc(sizeof(*ss), GFP_KERNEL);
+ if (!ss)
+ return -ENOMEM;
+
+ dcss->ss = ss;
+ ss->dev = dcss->dev;
+ ss->ctxld = dcss->ctxld;
+
+ ss->base_reg = ioremap(ss_base, SZ_4K);
+ if (!ss->base_reg) {
+ dev_err(dcss->dev, "ss: unable to remap ss base\n");
+ kfree(ss);
+ return -ENOMEM;
+ }
+
+ ss->base_ofs = ss_base;
+ ss->ctx_id = CTX_SB_HP;
+
+ return 0;
+}
+
+void dcss_ss_exit(struct dcss_ss *ss)
+{
+ /* stop SS */
+ dcss_writel(0, ss->base_reg + DCSS_SS_SYS_CTRL);
+
+ if (ss->base_reg)
+ iounmap(ss->base_reg);
+
+ kfree(ss);
+}
+
+void dcss_ss_subsam_set(struct dcss_ss *ss)
+{
+ dcss_ss_write(ss, 0x41614161, DCSS_SS_COEFF);
+ dcss_ss_write(ss, 0, DCSS_SS_MODE);
+ dcss_ss_write(ss, 0x03ff0000, DCSS_SS_CLIP_CB);
+ dcss_ss_write(ss, 0x03ff0000, DCSS_SS_CLIP_CR);
+}
+
+void dcss_ss_sync_set(struct dcss_ss *ss, struct videomode *vm,
+ bool phsync, bool pvsync)
+{
+ u16 lrc_x, lrc_y;
+ u16 hsync_start, hsync_end;
+ u16 vsync_start, vsync_end;
+ u16 de_ulc_x, de_ulc_y;
+ u16 de_lrc_x, de_lrc_y;
+
+ lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
+ vm->hactive - 1;
+ lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len +
+ vm->vactive - 1;
+
+ dcss_ss_write(ss, (lrc_y << LRC_Y_POS) | lrc_x, DCSS_SS_DISPLAY);
+
+ hsync_start = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
+ vm->hactive - 1;
+ hsync_end = vm->hsync_len - 1;
+
+ dcss_ss_write(ss, (phsync ? SYNC_POL : 0) |
+ ((u32)hsync_end << SYNC_END_POS) | hsync_start,
+ DCSS_SS_HSYNC);
+
+ vsync_start = vm->vfront_porch - 1;
+ vsync_end = vm->vfront_porch + vm->vsync_len - 1;
+
+ dcss_ss_write(ss, (pvsync ? SYNC_POL : 0) |
+ ((u32)vsync_end << SYNC_END_POS) | vsync_start,
+ DCSS_SS_VSYNC);
+
+ de_ulc_x = vm->hsync_len + vm->hback_porch - 1;
+ de_ulc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch;
+
+ dcss_ss_write(ss, SYNC_POL | ((u32)de_ulc_y << ULC_Y_POS) | de_ulc_x,
+ DCSS_SS_DE_ULC);
+
+ de_lrc_x = vm->hsync_len + vm->hback_porch + vm->hactive - 1;
+ de_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch +
+ vm->vactive - 1;
+
+ dcss_ss_write(ss, (de_lrc_y << LRC_Y_POS) | de_lrc_x, DCSS_SS_DE_LRC);
+}
+
+void dcss_ss_enable(struct dcss_ss *ss)
+{
+ dcss_ss_write(ss, RUN_EN, DCSS_SS_SYS_CTRL);
+ ss->in_use = true;
+}
+
+void dcss_ss_shutoff(struct dcss_ss *ss)
+{
+ dcss_writel(0, ss->base_reg + DCSS_SS_SYS_CTRL);
+ ss->in_use = false;
+}
diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index a3d1617d7c67..937d080f5d06 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -9,6 +9,8 @@
#include <linux/component.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
+#include <linux/dma-noncoherent.h>
+#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
@@ -19,6 +21,7 @@
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_damage_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_fb_cma_helper.h>
@@ -76,6 +79,11 @@ static const u32 ingenic_drm_primary_formats[] = {
DRM_FORMAT_XRGB8888,
};
+static bool ingenic_drm_cached_gem_buf;
+module_param_named(cached_gem_buffers, ingenic_drm_cached_gem_buf, bool, 0400);
+MODULE_PARM_DESC(cached_gem_buffers,
+ "Enable fully cached GEM buffers [default=false]");
+
static bool ingenic_drm_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -338,6 +346,8 @@ static int ingenic_drm_plane_atomic_check(struct drm_plane *plane,
plane->state->fb->format->format != state->fb->format->format))
crtc_state->mode_changed = true;
+ drm_atomic_helper_check_plane_damage(state->state, state);
+
return 0;
}
@@ -440,6 +450,38 @@ void ingenic_drm_plane_config(struct device *dev,
}
}
+void ingenic_drm_sync_data(struct device *dev,
+ struct drm_plane_state *old_state,
+ struct drm_plane_state *state)
+{
+ const struct drm_format_info *finfo = state->fb->format;
+ struct ingenic_drm *priv = dev_get_drvdata(dev);
+ struct drm_atomic_helper_damage_iter iter;
+ unsigned int offset, i;
+ struct drm_rect clip;
+ dma_addr_t paddr;
+ void *addr;
+
+ if (!ingenic_drm_cached_gem_buf)
+ return;
+
+ drm_atomic_helper_damage_iter_init(&iter, old_state, state);
+
+ drm_atomic_for_each_plane_damage(&iter, &clip) {
+ for (i = 0; i < finfo->num_planes; i++) {
+ paddr = drm_fb_cma_get_gem_addr(state->fb, state, i);
+ addr = phys_to_virt(paddr);
+
+ /* Ignore x1/x2 values, invalidate complete lines */
+ offset = clip.y1 * state->fb->pitches[i];
+
+ dma_cache_sync(priv->dev, addr + offset,
+ (clip.y2 - clip.y1) * state->fb->pitches[i],
+ DMA_TO_DEVICE);
+ }
+ }
+}
+
static void ingenic_drm_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *oldstate)
{
@@ -450,6 +492,8 @@ static void ingenic_drm_plane_atomic_update(struct drm_plane *plane,
dma_addr_t addr;
if (state && state->fb) {
+ ingenic_drm_sync_data(priv->dev, oldstate, state);
+
addr = drm_fb_cma_get_gem_addr(state->fb, state, 0);
width = state->src_w >> 16;
height = state->src_h >> 16;
@@ -605,7 +649,69 @@ static void ingenic_drm_disable_vblank(struct drm_crtc *crtc)
regmap_update_bits(priv->map, JZ_REG_LCD_CTRL, JZ_LCD_CTRL_EOF_IRQ, 0);
}
-DEFINE_DRM_GEM_CMA_FOPS(ingenic_drm_fops);
+static struct drm_framebuffer *
+ingenic_drm_gem_fb_create(struct drm_device *dev, struct drm_file *file,
+ const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ if (ingenic_drm_cached_gem_buf)
+ return drm_gem_fb_create_with_dirty(dev, file, mode_cmd);
+
+ return drm_gem_fb_create(dev, file, mode_cmd);
+}
+
+static int ingenic_drm_gem_mmap(struct drm_gem_object *obj,
+ struct vm_area_struct *vma)
+{
+ struct drm_gem_cma_object *cma_obj = to_drm_gem_cma_obj(obj);
+ struct device *dev = cma_obj->base.dev->dev;
+ unsigned long attrs;
+ int ret;
+
+ if (ingenic_drm_cached_gem_buf)
+ attrs = DMA_ATTR_NON_CONSISTENT;
+ else
+ attrs = DMA_ATTR_WRITE_COMBINE;
+
+ /*
+ * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the
+ * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map
+ * the whole buffer.
+ */
+ vma->vm_flags &= ~VM_PFNMAP;
+ vma->vm_pgoff = 0;
+ vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+
+ ret = dma_mmap_attrs(dev, vma, cma_obj->vaddr, cma_obj->paddr,
+ vma->vm_end - vma->vm_start, attrs);
+ if (ret)
+ drm_gem_vm_close(vma);
+
+ return ret;
+}
+
+static int ingenic_drm_gem_cma_mmap(struct file *filp,
+ struct vm_area_struct *vma)
+{
+ int ret;
+
+ ret = drm_gem_mmap(filp, vma);
+ if (ret)
+ return ret;
+
+ return ingenic_drm_gem_mmap(vma->vm_private_data, vma);
+}
+
+static const struct file_operations ingenic_drm_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+ .compat_ioctl = drm_compat_ioctl,
+ .poll = drm_poll,
+ .read = drm_read,
+ .llseek = noop_llseek,
+ .mmap = ingenic_drm_gem_cma_mmap,
+};
static struct drm_driver ingenic_drm_driver_data = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
@@ -669,7 +775,7 @@ static const struct drm_encoder_helper_funcs ingenic_drm_encoder_helper_funcs =
};
static const struct drm_mode_config_funcs ingenic_drm_mode_config_funcs = {
- .fb_create = drm_gem_fb_create,
+ .fb_create = ingenic_drm_gem_fb_create,
.output_poll_changed = drm_fb_helper_output_poll_changed,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
@@ -796,6 +902,8 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
return ret;
}
+ drm_plane_enable_fb_damage_clips(&priv->f1);
+
drm_crtc_helper_add(&priv->crtc, &ingenic_drm_crtc_helper_funcs);
ret = drm_crtc_init_with_planes(drm, &priv->crtc, &priv->f1,
@@ -821,6 +929,8 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
return ret;
}
+ drm_plane_enable_fb_damage_clips(&priv->f0);
+
if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU) && has_components) {
ret = component_bind_all(dev, drm);
if (ret) {
diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.h b/drivers/gpu/drm/ingenic/ingenic-drm.h
index 43f7d959cff7..df99f0f75d39 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm.h
+++ b/drivers/gpu/drm/ingenic/ingenic-drm.h
@@ -168,6 +168,10 @@ void ingenic_drm_plane_config(struct device *dev,
struct drm_plane *plane, u32 fourcc);
void ingenic_drm_plane_disable(struct device *dev, struct drm_plane *plane);
+void ingenic_drm_sync_data(struct device *dev,
+ struct drm_plane_state *old_state,
+ struct drm_plane_state *state);
+
extern struct platform_driver *ingenic_ipu_driver_ptr;
#endif /* DRIVERS_GPU_DRM_INGENIC_INGENIC_DRM_H */
diff --git a/drivers/gpu/drm/ingenic/ingenic-ipu.c b/drivers/gpu/drm/ingenic/ingenic-ipu.c
index fc8c6e970ee3..38c83e8cc6a5 100644
--- a/drivers/gpu/drm/ingenic/ingenic-ipu.c
+++ b/drivers/gpu/drm/ingenic/ingenic-ipu.c
@@ -20,6 +20,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_damage_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fourcc.h>
@@ -316,6 +317,8 @@ static void ingenic_ipu_plane_atomic_update(struct drm_plane *plane,
JZ_IPU_CTRL_CHIP_EN | JZ_IPU_CTRL_LCDC_SEL);
}
+ ingenic_drm_sync_data(ipu->master, oldstate, state);
+
/* New addresses will be committed in vblank handler... */
ipu->addr_y = drm_fb_cma_get_gem_addr(state->fb, state, 0);
if (finfo->num_planes > 1)
@@ -534,7 +537,7 @@ static int ingenic_ipu_plane_atomic_check(struct drm_plane *plane,
if (!state->crtc ||
!crtc_state->mode.hdisplay || !crtc_state->mode.vdisplay)
- return 0;
+ goto out_check_damage;
/* Plane must be fully visible */
if (state->crtc_x < 0 || state->crtc_y < 0 ||
@@ -551,7 +554,7 @@ static int ingenic_ipu_plane_atomic_check(struct drm_plane *plane,
return -EINVAL;
if (!osd_changed(state, plane->state))
- return 0;
+ goto out_check_damage;
crtc_state->mode_changed = true;
@@ -578,6 +581,9 @@ static int ingenic_ipu_plane_atomic_check(struct drm_plane *plane,
ipu->denom_w = denom_w;
ipu->denom_h = denom_h;
+out_check_damage:
+ drm_atomic_helper_check_plane_damage(state->state, state);
+
return 0;
}
@@ -759,6 +765,8 @@ static int ingenic_ipu_bind(struct device *dev, struct device *master, void *d)
return err;
}
+ drm_plane_enable_fb_damage_clips(plane);
+
/*
* Sharpness settings range is [0,32]
* 0 : nearest-neighbor
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index 8c7ae812b813..e47958c3704a 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -123,7 +123,7 @@ static struct page **get_pages(struct drm_gem_object *obj)
msm_obj->pages = p;
- msm_obj->sgt = drm_prime_pages_to_sg(p, npages);
+ msm_obj->sgt = drm_prime_pages_to_sg(obj->dev, p, npages);
if (IS_ERR(msm_obj->sgt)) {
void *ptr = ERR_CAST(msm_obj->sgt);
diff --git a/drivers/gpu/drm/msm/msm_gem_prime.c b/drivers/gpu/drm/msm/msm_gem_prime.c
index d7c8948427fe..515ef80816a0 100644
--- a/drivers/gpu/drm/msm/msm_gem_prime.c
+++ b/drivers/gpu/drm/msm/msm_gem_prime.c
@@ -19,7 +19,7 @@ struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj)
if (WARN_ON(!msm_obj->pages)) /* should have already pinned! */
return NULL;
- return drm_prime_pages_to_sg(msm_obj->pages, npages);
+ return drm_prime_pages_to_sg(obj->dev, msm_obj->pages, npages);
}
void *msm_gem_prime_vmap(struct drm_gem_object *obj)
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
index 8c549c3931af..35122aef037b 100644
--- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c
+++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
@@ -21,6 +21,7 @@
#include <drm/drm_connector.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
+#include <drm/drm_fourcc.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_irq.h>
@@ -81,8 +82,26 @@ void mxsfb_disable_axi_clk(struct mxsfb_drm_private *mxsfb)
clk_disable_unprepare(mxsfb->clk_axi);
}
+static struct drm_framebuffer *
+mxsfb_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+ const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ const struct drm_format_info *info;
+
+ info = drm_get_format_info(dev, mode_cmd);
+ if (!info)
+ return ERR_PTR(-EINVAL);
+
+ if (mode_cmd->width * info->cpp[0] != mode_cmd->pitches[0]) {
+ dev_dbg(dev->dev, "Invalid pitch: fb width must match pitch\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ return drm_gem_fb_create(dev, file_priv, mode_cmd);
+}
+
static const struct drm_mode_config_funcs mxsfb_mode_config_funcs = {
- .fb_create = drm_gem_fb_create,
+ .fb_create = mxsfb_fb_create,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
index 6416b6907aeb..f9e962fd94d0 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
@@ -615,7 +615,7 @@ nv_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb)
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
int ret;
- ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM, false);
+ ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, false);
if (ret == 0) {
if (disp->image[nv_crtc->index])
nouveau_bo_unpin(disp->image[nv_crtc->index]);
@@ -1172,7 +1172,7 @@ nv04_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
return -ENOMEM;
if (new_bo != old_bo) {
- ret = nouveau_bo_pin(new_bo, TTM_PL_FLAG_VRAM, true);
+ ret = nouveau_bo_pin(new_bo, NOUVEAU_GEM_DOMAIN_VRAM, true);
if (ret)
goto fail_free;
}
@@ -1336,10 +1336,11 @@ nv04_crtc_create(struct drm_device *dev, int crtc_num)
drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256);
ret = nouveau_bo_new(&nouveau_drm(dev)->client, 64*64*4, 0x100,
- TTM_PL_FLAG_VRAM, 0, 0x0000, NULL, NULL,
+ NOUVEAU_GEM_DOMAIN_VRAM, 0, 0x0000, NULL, NULL,
&nv_crtc->cursor.nvbo);
if (!ret) {
- ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM, false);
+ ret = nouveau_bo_pin(nv_crtc->cursor.nvbo,
+ NOUVEAU_GEM_DOMAIN_VRAM, false);
if (!ret) {
ret = nouveau_bo_map(nv_crtc->cursor.nvbo);
if (ret)
diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.c b/drivers/gpu/drm/nouveau/dispnv04/disp.c
index 3ee836dc5058..7739f46470d3 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c
@@ -134,7 +134,7 @@ nv04_display_init(struct drm_device *dev, bool resume, bool runtime)
if (!fb || !fb->obj[0])
continue;
nvbo = nouveau_gem_object(fb->obj[0]);
- ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM, true);
+ ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, true);
if (ret)
NV_ERROR(drm, "Could not pin framebuffer\n");
}
@@ -144,7 +144,8 @@ nv04_display_init(struct drm_device *dev, bool resume, bool runtime)
if (!nv_crtc->cursor.nvbo)
continue;
- ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM, true);
+ ret = nouveau_bo_pin(nv_crtc->cursor.nvbo,
+ NOUVEAU_GEM_DOMAIN_VRAM, true);
if (!ret && nv_crtc->cursor.set_offset)
ret = nouveau_bo_map(nv_crtc->cursor.nvbo);
if (ret)
diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
index 193ba9498f3d..37e63e98cd08 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
@@ -142,7 +142,7 @@ nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
return ret;
nvbo = nouveau_gem_object(fb->obj[0]);
- ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM, false);
+ ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, false);
if (ret)
return ret;
@@ -387,7 +387,7 @@ nv04_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
return ret;
nvbo = nouveau_gem_object(fb->obj[0]);
- ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM, false);
+ ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, false);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c
index 7799530e07c1..b111fe24a06b 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
@@ -2069,6 +2069,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state)
drm_atomic_helper_wait_for_fences(dev, state, false);
drm_atomic_helper_wait_for_dependencies(state);
drm_atomic_helper_update_legacy_modeset_state(dev, state);
+ drm_atomic_helper_calc_timestamping_constants(state);
if (atom->lock_core)
mutex_lock(&disp->mutex);
@@ -2622,10 +2623,11 @@ nv50_display_create(struct drm_device *dev)
dev->mode_config.normalize_zpos = true;
/* small shared memory area we use for notifiers and semaphores */
- ret = nouveau_bo_new(&drm->client, 4096, 0x1000, TTM_PL_FLAG_VRAM,
+ ret = nouveau_bo_new(&drm->client, 4096, 0x1000,
+ NOUVEAU_GEM_DOMAIN_VRAM,
0, 0x0000, NULL, NULL, &disp->sync);
if (!ret) {
- ret = nouveau_bo_pin(disp->sync, TTM_PL_FLAG_VRAM, true);
+ ret = nouveau_bo_pin(disp->sync, NOUVEAU_GEM_DOMAIN_VRAM, true);
if (!ret) {
ret = nouveau_bo_map(disp->sync);
if (ret)
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.c b/drivers/gpu/drm/nouveau/dispnv50/wndw.c
index 447ecc9fec42..0356474ad6f6 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/wndw.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.c
@@ -542,7 +542,7 @@ nv50_wndw_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state)
return 0;
nvbo = nouveau_gem_object(fb->obj[0]);
- ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM, true);
+ ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, true);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c
index 21537ca1dd39..9a5be6f32424 100644
--- a/drivers/gpu/drm/nouveau/nouveau_abi16.c
+++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c
@@ -328,7 +328,8 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
ret = nouveau_gem_new(cli, PAGE_SIZE, 0, NOUVEAU_GEM_DOMAIN_GART,
0, 0, &chan->ntfy);
if (ret == 0)
- ret = nouveau_bo_pin(chan->ntfy, TTM_PL_FLAG_TT, false);
+ ret = nouveau_bo_pin(chan->ntfy, NOUVEAU_GEM_DOMAIN_GART,
+ false);
if (ret)
goto done;
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index 9140387f30dc..2ee75646ad6f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -44,6 +44,9 @@
#include <nvif/if500b.h>
#include <nvif/if900b.h>
+static int nouveau_ttm_tt_bind(struct ttm_bo_device *bdev, struct ttm_tt *ttm,
+ struct ttm_resource *reg);
+
/*
* NV10-NV40 tiling helpers
*/
@@ -137,6 +140,7 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
struct nouveau_bo *nvbo = nouveau_bo(bo);
WARN_ON(nvbo->pin_refcnt > 0);
+ nouveau_bo_del_io_reserve_lru(bo);
nv10_bo_put_tile_region(dev, nvbo->tile, NULL);
/*
@@ -158,8 +162,7 @@ roundup_64(u64 x, u32 y)
}
static void
-nouveau_bo_fixup_align(struct nouveau_bo *nvbo, u32 flags,
- int *align, u64 *size)
+nouveau_bo_fixup_align(struct nouveau_bo *nvbo, int *align, u64 *size)
{
struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev);
struct nvif_device *device = &drm->client.device;
@@ -192,7 +195,7 @@ nouveau_bo_fixup_align(struct nouveau_bo *nvbo, u32 flags,
}
struct nouveau_bo *
-nouveau_bo_alloc(struct nouveau_cli *cli, u64 *size, int *align, u32 flags,
+nouveau_bo_alloc(struct nouveau_cli *cli, u64 *size, int *align, u32 domain,
u32 tile_mode, u32 tile_flags)
{
struct nouveau_drm *drm = cli->drm;
@@ -218,7 +221,7 @@ nouveau_bo_alloc(struct nouveau_cli *cli, u64 *size, int *align, u32 flags,
* mapping, but is what NOUVEAU_GEM_DOMAIN_COHERENT gets translated
* into in nouveau_gem_new().
*/
- if (flags & TTM_PL_FLAG_UNCACHED) {
+ if (domain & NOUVEAU_GEM_DOMAIN_COHERENT) {
/* Determine if we can get a cache-coherent map, forcing
* uncached mapping if we can't.
*/
@@ -258,9 +261,9 @@ nouveau_bo_alloc(struct nouveau_cli *cli, u64 *size, int *align, u32 flags,
* Skip page sizes that can't support needed domains.
*/
if (cli->device.info.family > NV_DEVICE_INFO_V0_CURIE &&
- (flags & TTM_PL_FLAG_VRAM) && !vmm->page[i].vram)
+ (domain & NOUVEAU_GEM_DOMAIN_VRAM) && !vmm->page[i].vram)
continue;
- if ((flags & TTM_PL_FLAG_TT) &&
+ if ((domain & NOUVEAU_GEM_DOMAIN_GART) &&
(!vmm->page[i].host || vmm->page[i].shift > PAGE_SHIFT))
continue;
@@ -287,13 +290,13 @@ nouveau_bo_alloc(struct nouveau_cli *cli, u64 *size, int *align, u32 flags,
}
nvbo->page = vmm->page[pi].shift;
- nouveau_bo_fixup_align(nvbo, flags, align, size);
+ nouveau_bo_fixup_align(nvbo, align, size);
return nvbo;
}
int
-nouveau_bo_init(struct nouveau_bo *nvbo, u64 size, int align, u32 flags,
+nouveau_bo_init(struct nouveau_bo *nvbo, u64 size, int align, u32 domain,
struct sg_table *sg, struct dma_resv *robj)
{
int type = sg ? ttm_bo_type_sg : ttm_bo_type_device;
@@ -303,7 +306,8 @@ nouveau_bo_init(struct nouveau_bo *nvbo, u64 size, int align, u32 flags,
acc_size = ttm_bo_dma_acc_size(nvbo->bo.bdev, size, sizeof(*nvbo));
nvbo->bo.mem.num_pages = size >> PAGE_SHIFT;
- nouveau_bo_placement_set(nvbo, flags, 0);
+ nouveau_bo_placement_set(nvbo, domain, 0);
+ INIT_LIST_HEAD(&nvbo->io_reserve_lru);
ret = ttm_bo_init(nvbo->bo.bdev, &nvbo->bo, size, type,
&nvbo->placement, align >> PAGE_SHIFT, false,
@@ -318,19 +322,19 @@ nouveau_bo_init(struct nouveau_bo *nvbo, u64 size, int align, u32 flags,
int
nouveau_bo_new(struct nouveau_cli *cli, u64 size, int align,
- uint32_t flags, uint32_t tile_mode, uint32_t tile_flags,
+ uint32_t domain, uint32_t tile_mode, uint32_t tile_flags,
struct sg_table *sg, struct dma_resv *robj,
struct nouveau_bo **pnvbo)
{
struct nouveau_bo *nvbo;
int ret;
- nvbo = nouveau_bo_alloc(cli, &size, &align, flags, tile_mode,
+ nvbo = nouveau_bo_alloc(cli, &size, &align, domain, tile_mode,
tile_flags);
if (IS_ERR(nvbo))
return PTR_ERR(nvbo);
- ret = nouveau_bo_init(nvbo, size, align, flags, sg, robj);
+ ret = nouveau_bo_init(nvbo, size, align, domain, sg, robj);
if (ret)
return ret;
@@ -339,27 +343,49 @@ nouveau_bo_new(struct nouveau_cli *cli, u64 size, int align,
}
static void
-set_placement_list(struct ttm_place *pl, unsigned *n, uint32_t type, uint32_t flags)
+set_placement_list(struct nouveau_drm *drm, struct ttm_place *pl, unsigned *n,
+ uint32_t domain, uint32_t flags)
{
*n = 0;
- if (type & TTM_PL_FLAG_VRAM)
- pl[(*n)++].flags = TTM_PL_FLAG_VRAM | flags;
- if (type & TTM_PL_FLAG_TT)
- pl[(*n)++].flags = TTM_PL_FLAG_TT | flags;
- if (type & TTM_PL_FLAG_SYSTEM)
- pl[(*n)++].flags = TTM_PL_FLAG_SYSTEM | flags;
+ if (domain & NOUVEAU_GEM_DOMAIN_VRAM) {
+ struct nvif_mmu *mmu = &drm->client.mmu;
+ const u8 type = mmu->type[drm->ttm.type_vram].type;
+
+ pl[*n].mem_type = TTM_PL_VRAM;
+ pl[*n].flags = flags & ~TTM_PL_FLAG_CACHED;
+
+ /* Some BARs do not support being ioremapped WC */
+ if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA &&
+ type & NVIF_MEM_UNCACHED)
+ pl[*n].flags &= ~TTM_PL_FLAG_WC;
+
+ (*n)++;
+ }
+ if (domain & NOUVEAU_GEM_DOMAIN_GART) {
+ pl[*n].mem_type = TTM_PL_TT;
+ pl[*n].flags = flags;
+
+ if (drm->agp.bridge)
+ pl[*n].flags &= ~TTM_PL_FLAG_CACHED;
+
+ (*n)++;
+ }
+ if (domain & NOUVEAU_GEM_DOMAIN_CPU) {
+ pl[*n].mem_type = TTM_PL_SYSTEM;
+ pl[(*n)++].flags = flags;
+ }
}
static void
-set_placement_range(struct nouveau_bo *nvbo, uint32_t type)
+set_placement_range(struct nouveau_bo *nvbo, uint32_t domain)
{
struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev);
u32 vram_pages = drm->client.device.info.ram_size >> PAGE_SHIFT;
unsigned i, fpfn, lpfn;
if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CELSIUS &&
- nvbo->mode && (type & TTM_PL_FLAG_VRAM) &&
+ nvbo->mode && (domain & NOUVEAU_GEM_DOMAIN_VRAM) &&
nvbo->bo.mem.num_pages < vram_pages / 4) {
/*
* Make sure that the color and depth buffers are handled
@@ -386,26 +412,28 @@ set_placement_range(struct nouveau_bo *nvbo, uint32_t type)
}
void
-nouveau_bo_placement_set(struct nouveau_bo *nvbo, uint32_t type, uint32_t busy)
+nouveau_bo_placement_set(struct nouveau_bo *nvbo, uint32_t domain,
+ uint32_t busy)
{
+ struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev);
struct ttm_placement *pl = &nvbo->placement;
uint32_t flags = (nvbo->force_coherent ? TTM_PL_FLAG_UNCACHED :
TTM_PL_MASK_CACHING) |
(nvbo->pin_refcnt ? TTM_PL_FLAG_NO_EVICT : 0);
pl->placement = nvbo->placements;
- set_placement_list(nvbo->placements, &pl->num_placement,
- type, flags);
+ set_placement_list(drm, nvbo->placements, &pl->num_placement,
+ domain, flags);
pl->busy_placement = nvbo->busy_placements;
- set_placement_list(nvbo->busy_placements, &pl->num_busy_placement,
- type | busy, flags);
+ set_placement_list(drm, nvbo->busy_placements, &pl->num_busy_placement,
+ domain | busy, flags);
- set_placement_range(nvbo, type);
+ set_placement_range(nvbo, domain);
}
int
-nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype, bool contig)
+nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t domain, bool contig)
{
struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev);
struct ttm_buffer_object *bo = &nvbo->bo;
@@ -417,7 +445,7 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype, bool contig)
return ret;
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA &&
- memtype == TTM_PL_FLAG_VRAM && contig) {
+ domain == NOUVEAU_GEM_DOMAIN_VRAM && contig) {
if (!nvbo->contig) {
nvbo->contig = true;
force = true;
@@ -426,10 +454,22 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype, bool contig)
}
if (nvbo->pin_refcnt) {
- if (!(memtype & (1 << bo->mem.mem_type)) || evict) {
+ bool error = evict;
+
+ switch (bo->mem.mem_type) {
+ case TTM_PL_VRAM:
+ error |= !(domain & NOUVEAU_GEM_DOMAIN_VRAM);
+ break;
+ case TTM_PL_TT:
+ error |= !(domain & NOUVEAU_GEM_DOMAIN_GART);
+ default:
+ break;
+ }
+
+ if (error) {
NV_ERROR(drm, "bo %p pinned elsewhere: "
"0x%08x vs 0x%08x\n", bo,
- 1 << bo->mem.mem_type, memtype);
+ bo->mem.mem_type, domain);
ret = -EBUSY;
}
nvbo->pin_refcnt++;
@@ -437,14 +477,14 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype, bool contig)
}
if (evict) {
- nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_TT, 0);
+ nouveau_bo_placement_set(nvbo, NOUVEAU_GEM_DOMAIN_GART, 0);
ret = nouveau_bo_validate(nvbo, false, false);
if (ret)
goto out;
}
nvbo->pin_refcnt++;
- nouveau_bo_placement_set(nvbo, memtype, 0);
+ nouveau_bo_placement_set(nvbo, domain, 0);
/* drop pin_refcnt temporarily, so we don't trip the assertion
* in nouveau_bo_move() that makes sure we're not trying to
@@ -490,7 +530,16 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo)
if (ref)
goto out;
- nouveau_bo_placement_set(nvbo, bo->mem.placement, 0);
+ switch (bo->mem.mem_type) {
+ case TTM_PL_VRAM:
+ nouveau_bo_placement_set(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, 0);
+ break;
+ case TTM_PL_TT:
+ nouveau_bo_placement_set(nvbo, NOUVEAU_GEM_DOMAIN_GART, 0);
+ break;
+ default:
+ break;
+ }
ret = nouveau_bo_validate(nvbo, false, false);
if (ret == 0) {
@@ -574,6 +623,26 @@ nouveau_bo_sync_for_cpu(struct nouveau_bo *nvbo)
PAGE_SIZE, DMA_FROM_DEVICE);
}
+void nouveau_bo_add_io_reserve_lru(struct ttm_buffer_object *bo)
+{
+ struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
+ struct nouveau_bo *nvbo = nouveau_bo(bo);
+
+ mutex_lock(&drm->ttm.io_reserve_mutex);
+ list_move_tail(&nvbo->io_reserve_lru, &drm->ttm.io_reserve_lru);
+ mutex_unlock(&drm->ttm.io_reserve_mutex);
+}
+
+void nouveau_bo_del_io_reserve_lru(struct ttm_buffer_object *bo)
+{
+ struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
+ struct nouveau_bo *nvbo = nouveau_bo(bo);
+
+ mutex_lock(&drm->ttm.io_reserve_mutex);
+ list_del_init(&nvbo->io_reserve_lru);
+ mutex_unlock(&drm->ttm.io_reserve_mutex);
+}
+
int
nouveau_bo_validate(struct nouveau_bo *nvbo, bool interruptible,
bool no_wait_gpu)
@@ -646,6 +715,36 @@ nouveau_ttm_tt_create(struct ttm_buffer_object *bo, uint32_t page_flags)
return nouveau_sgdma_create_ttm(bo, page_flags);
}
+static int
+nouveau_ttm_tt_bind(struct ttm_bo_device *bdev, struct ttm_tt *ttm,
+ struct ttm_resource *reg)
+{
+#if IS_ENABLED(CONFIG_AGP)
+ struct nouveau_drm *drm = nouveau_bdev(bdev);
+#endif
+ if (!reg)
+ return -EINVAL;
+#if IS_ENABLED(CONFIG_AGP)
+ if (drm->agp.bridge)
+ return ttm_agp_bind(ttm, reg);
+#endif
+ return nouveau_sgdma_bind(bdev, ttm, reg);
+}
+
+static void
+nouveau_ttm_tt_unbind(struct ttm_bo_device *bdev, struct ttm_tt *ttm)
+{
+#if IS_ENABLED(CONFIG_AGP)
+ struct nouveau_drm *drm = nouveau_bdev(bdev);
+
+ if (drm->agp.bridge) {
+ ttm_agp_unbind(ttm);
+ return;
+ }
+#endif
+ nouveau_sgdma_unbind(bdev, ttm);
+}
+
static void
nouveau_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
{
@@ -653,11 +752,11 @@ nouveau_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
switch (bo->mem.mem_type) {
case TTM_PL_VRAM:
- nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_TT,
- TTM_PL_FLAG_SYSTEM);
+ nouveau_bo_placement_set(nvbo, NOUVEAU_GEM_DOMAIN_GART,
+ NOUVEAU_GEM_DOMAIN_CPU);
break;
default:
- nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_SYSTEM, 0);
+ nouveau_bo_placement_set(nvbo, NOUVEAU_GEM_DOMAIN_CPU, 0);
break;
}
@@ -725,7 +824,7 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
if (ret == 0) {
ret = ttm_bo_move_accel_cleanup(bo,
&fence->base,
- evict,
+ evict, false,
new_reg);
nouveau_fence_unref(&fence);
}
@@ -811,7 +910,8 @@ nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr,
struct ttm_place placement_memtype = {
.fpfn = 0,
.lpfn = 0,
- .flags = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING
+ .mem_type = TTM_PL_TT,
+ .flags = TTM_PL_MASK_CACHING
};
struct ttm_placement placement;
struct ttm_resource tmp_reg;
@@ -826,7 +926,11 @@ nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr,
if (ret)
return ret;
- ret = ttm_tt_bind(bo->ttm, &tmp_reg, &ctx);
+ ret = ttm_tt_populate(bo->bdev, bo->ttm, &ctx);
+ if (ret)
+ goto out;
+
+ ret = nouveau_ttm_tt_bind(bo->bdev, bo->ttm, &tmp_reg);
if (ret)
goto out;
@@ -848,7 +952,8 @@ nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr,
struct ttm_place placement_memtype = {
.fpfn = 0,
.lpfn = 0,
- .flags = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING
+ .mem_type = TTM_PL_TT,
+ .flags = TTM_PL_MASK_CACHING
};
struct ttm_placement placement;
struct ttm_resource tmp_reg;
@@ -888,6 +993,8 @@ nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, bool evict,
if (bo->destroy != nouveau_bo_del_ttm)
return;
+ nouveau_bo_del_io_reserve_lru(bo);
+
if (mem && new_reg->mem_type != TTM_PL_SYSTEM &&
mem->mem.page == nvbo->page) {
list_for_each_entry(vma, &nvbo->vma_list, head) {
@@ -969,9 +1076,7 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict,
/* Fake bo copy. */
if (old_reg->mem_type == TTM_PL_SYSTEM && !bo->ttm) {
- BUG_ON(bo->mem.mm_node != NULL);
- bo->mem = *new_reg;
- new_reg->mm_node = NULL;
+ ttm_bo_move_null(bo, new_reg);
goto out;
}
@@ -1018,32 +1123,60 @@ nouveau_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp)
filp->private_data);
}
+static void
+nouveau_ttm_io_mem_free_locked(struct nouveau_drm *drm,
+ struct ttm_resource *reg)
+{
+ struct nouveau_mem *mem = nouveau_mem(reg);
+
+ if (drm->client.mem->oclass >= NVIF_CLASS_MEM_NV50) {
+ switch (reg->mem_type) {
+ case TTM_PL_TT:
+ if (mem->kind)
+ nvif_object_unmap_handle(&mem->mem.object);
+ break;
+ case TTM_PL_VRAM:
+ nvif_object_unmap_handle(&mem->mem.object);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
static int
nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_resource *reg)
{
struct nouveau_drm *drm = nouveau_bdev(bdev);
struct nvkm_device *device = nvxx_device(&drm->client.device);
struct nouveau_mem *mem = nouveau_mem(reg);
+ int ret;
+ mutex_lock(&drm->ttm.io_reserve_mutex);
+retry:
switch (reg->mem_type) {
case TTM_PL_SYSTEM:
/* System memory */
- return 0;
+ ret = 0;
+ goto out;
case TTM_PL_TT:
#if IS_ENABLED(CONFIG_AGP)
if (drm->agp.bridge) {
- reg->bus.offset = reg->start << PAGE_SHIFT;
- reg->bus.base = drm->agp.base;
+ reg->bus.offset = (reg->start << PAGE_SHIFT) +
+ drm->agp.base;
reg->bus.is_iomem = !drm->agp.cma;
}
#endif
- if (drm->client.mem->oclass < NVIF_CLASS_MEM_NV50 || !mem->kind)
+ if (drm->client.mem->oclass < NVIF_CLASS_MEM_NV50 ||
+ !mem->kind) {
/* untiled */
+ ret = 0;
break;
+ }
fallthrough; /* tiled memory */
case TTM_PL_VRAM:
- reg->bus.offset = reg->start << PAGE_SHIFT;
- reg->bus.base = device->func->resource_addr(device, 1);
+ reg->bus.offset = (reg->start << PAGE_SHIFT) +
+ device->func->resource_addr(device, 1);
reg->bus.is_iomem = true;
if (drm->client.mem->oclass >= NVIF_CLASS_MEM_NV50) {
union {
@@ -1052,7 +1185,6 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_resource *reg)
} args;
u64 handle, length;
u32 argc = 0;
- int ret;
switch (mem->mem.object.oclass) {
case NVIF_CLASS_MEM_NV50:
@@ -1078,39 +1210,46 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_resource *reg)
&handle, &length);
if (ret != 1) {
if (WARN_ON(ret == 0))
- return -EINVAL;
- return ret;
+ ret = -EINVAL;
+ goto out;
}
- reg->bus.base = 0;
reg->bus.offset = handle;
+ ret = 0;
}
break;
default:
- return -EINVAL;
+ ret = -EINVAL;
}
- return 0;
+
+out:
+ if (ret == -ENOSPC) {
+ struct nouveau_bo *nvbo;
+
+ nvbo = list_first_entry_or_null(&drm->ttm.io_reserve_lru,
+ typeof(*nvbo),
+ io_reserve_lru);
+ if (nvbo) {
+ list_del_init(&nvbo->io_reserve_lru);
+ drm_vma_node_unmap(&nvbo->bo.base.vma_node,
+ bdev->dev_mapping);
+ nouveau_ttm_io_mem_free_locked(drm, &nvbo->bo.mem);
+ goto retry;
+ }
+
+ }
+ mutex_unlock(&drm->ttm.io_reserve_mutex);
+ return ret;
}
static void
nouveau_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_resource *reg)
{
struct nouveau_drm *drm = nouveau_bdev(bdev);
- struct nouveau_mem *mem = nouveau_mem(reg);
- if (drm->client.mem->oclass >= NVIF_CLASS_MEM_NV50) {
- switch (reg->mem_type) {
- case TTM_PL_TT:
- if (mem->kind)
- nvif_object_unmap_handle(&mem->mem.object);
- break;
- case TTM_PL_VRAM:
- nvif_object_unmap_handle(&mem->mem.object);
- break;
- default:
- break;
- }
- }
+ mutex_lock(&drm->ttm.io_reserve_mutex);
+ nouveau_ttm_io_mem_free_locked(drm, reg);
+ mutex_unlock(&drm->ttm.io_reserve_mutex);
}
static int
@@ -1131,7 +1270,8 @@ nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo)
return 0;
if (bo->mem.mem_type == TTM_PL_SYSTEM) {
- nouveau_bo_placement_set(nvbo, TTM_PL_TT, 0);
+ nouveau_bo_placement_set(nvbo, NOUVEAU_GEM_DOMAIN_GART,
+ 0);
ret = nouveau_bo_validate(nvbo, false, false);
if (ret)
@@ -1155,35 +1295,36 @@ nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo)
nvbo->busy_placements[i].lpfn = mappable;
}
- nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_VRAM, 0);
+ nouveau_bo_placement_set(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, 0);
return nouveau_bo_validate(nvbo, false, false);
}
static int
-nouveau_ttm_tt_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)
+nouveau_ttm_tt_populate(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)
{
struct ttm_dma_tt *ttm_dma = (void *)ttm;
struct nouveau_drm *drm;
struct device *dev;
bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG);
- if (ttm->state != tt_unpopulated)
+ if (ttm_tt_is_populated(ttm))
return 0;
if (slave && ttm->sg) {
/* make userspace faulting work */
drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages,
ttm_dma->dma_address, ttm->num_pages);
- ttm->state = tt_unbound;
+ ttm_tt_set_populated(ttm);
return 0;
}
- drm = nouveau_bdev(ttm->bdev);
+ drm = nouveau_bdev(bdev);
dev = drm->dev->dev;
#if IS_ENABLED(CONFIG_AGP)
if (drm->agp.bridge) {
- return ttm_agp_tt_populate(ttm, ctx);
+ return ttm_pool_populate(ttm, ctx);
}
#endif
@@ -1196,7 +1337,8 @@ nouveau_ttm_tt_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)
}
static void
-nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm)
+nouveau_ttm_tt_unpopulate(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm)
{
struct ttm_dma_tt *ttm_dma = (void *)ttm;
struct nouveau_drm *drm;
@@ -1206,12 +1348,12 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm)
if (slave)
return;
- drm = nouveau_bdev(ttm->bdev);
+ drm = nouveau_bdev(bdev);
dev = drm->dev->dev;
#if IS_ENABLED(CONFIG_AGP)
if (drm->agp.bridge) {
- ttm_agp_tt_unpopulate(ttm);
+ ttm_pool_unpopulate(ttm);
return;
}
#endif
@@ -1226,6 +1368,22 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm)
ttm_unmap_and_unpopulate_pages(dev, ttm_dma);
}
+static void
+nouveau_ttm_tt_destroy(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm)
+{
+#if IS_ENABLED(CONFIG_AGP)
+ struct nouveau_drm *drm = nouveau_bdev(bdev);
+ if (drm->agp.bridge) {
+ ttm_agp_unbind(ttm);
+ ttm_tt_destroy_common(bdev, ttm);
+ ttm_agp_destroy(ttm);
+ return;
+ }
+#endif
+ nouveau_sgdma_destroy(bdev, ttm);
+}
+
void
nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence, bool exclusive)
{
@@ -1241,6 +1399,9 @@ struct ttm_bo_driver nouveau_bo_driver = {
.ttm_tt_create = &nouveau_ttm_tt_create,
.ttm_tt_populate = &nouveau_ttm_tt_populate,
.ttm_tt_unpopulate = &nouveau_ttm_tt_unpopulate,
+ .ttm_tt_bind = &nouveau_ttm_tt_bind,
+ .ttm_tt_unbind = &nouveau_ttm_tt_unbind,
+ .ttm_tt_destroy = &nouveau_ttm_tt_destroy,
.eviction_valuable = ttm_bo_eviction_valuable,
.evict_flags = nouveau_bo_evict_flags,
.move_notify = nouveau_bo_move_ntfy,
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h
index aecb7481df0d..2a23c8207436 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.h
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.h
@@ -18,6 +18,7 @@ struct nouveau_bo {
bool force_coherent;
struct ttm_bo_kmap_obj kmap;
struct list_head head;
+ struct list_head io_reserve_lru;
/* protected by ttm_bo_reserve() */
struct drm_file *reserved_by;
@@ -76,10 +77,10 @@ extern struct ttm_bo_driver nouveau_bo_driver;
void nouveau_bo_move_init(struct nouveau_drm *);
struct nouveau_bo *nouveau_bo_alloc(struct nouveau_cli *, u64 *size, int *align,
- u32 flags, u32 tile_mode, u32 tile_flags);
-int nouveau_bo_init(struct nouveau_bo *, u64 size, int align, u32 flags,
+ u32 domain, u32 tile_mode, u32 tile_flags);
+int nouveau_bo_init(struct nouveau_bo *, u64 size, int align, u32 domain,
struct sg_table *sg, struct dma_resv *robj);
-int nouveau_bo_new(struct nouveau_cli *, u64 size, int align, u32 flags,
+int nouveau_bo_new(struct nouveau_cli *, u64 size, int align, u32 domain,
u32 tile_mode, u32 tile_flags, struct sg_table *sg,
struct dma_resv *robj,
struct nouveau_bo **);
@@ -96,6 +97,8 @@ int nouveau_bo_validate(struct nouveau_bo *, bool interruptible,
bool no_wait_gpu);
void nouveau_bo_sync_for_device(struct nouveau_bo *nvbo);
void nouveau_bo_sync_for_cpu(struct nouveau_bo *nvbo);
+void nouveau_bo_add_io_reserve_lru(struct ttm_buffer_object *bo);
+void nouveau_bo_del_io_reserve_lru(struct ttm_buffer_object *bo);
/* TODO: submit equivalent to TTM generic API upstream? */
static inline void __iomem *
@@ -119,13 +122,13 @@ nouveau_bo_unmap_unpin_unref(struct nouveau_bo **pnvbo)
}
static inline int
-nouveau_bo_new_pin_map(struct nouveau_cli *cli, u64 size, int align, u32 flags,
+nouveau_bo_new_pin_map(struct nouveau_cli *cli, u64 size, int align, u32 domain,
struct nouveau_bo **pnvbo)
{
- int ret = nouveau_bo_new(cli, size, align, flags,
+ int ret = nouveau_bo_new(cli, size, align, domain,
0, 0, NULL, NULL, pnvbo);
if (ret == 0) {
- ret = nouveau_bo_pin(*pnvbo, flags, true);
+ ret = nouveau_bo_pin(*pnvbo, domain, true);
if (ret == 0) {
ret = nouveau_bo_map(*pnvbo);
if (ret == 0)
diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c
index b80e4ebf14a6..8f099601d2f2 100644
--- a/drivers/gpu/drm/nouveau/nouveau_chan.c
+++ b/drivers/gpu/drm/nouveau/nouveau_chan.c
@@ -163,9 +163,9 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nvif_device *device,
atomic_set(&chan->killed, 0);
/* allocate memory for dma push buffer */
- target = TTM_PL_FLAG_TT | TTM_PL_FLAG_UNCACHED;
+ target = NOUVEAU_GEM_DOMAIN_GART | NOUVEAU_GEM_DOMAIN_COHERENT;
if (nouveau_vram_pushbuf)
- target = TTM_PL_FLAG_VRAM;
+ target = NOUVEAU_GEM_DOMAIN_VRAM;
ret = nouveau_bo_new(cli, size, 0, target, 0, 0, NULL, NULL,
&chan->push.buffer);
diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c b/drivers/gpu/drm/nouveau/nouveau_dmem.c
index 4e8112fde3e6..af70ef8e5471 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dmem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c
@@ -254,12 +254,12 @@ nouveau_dmem_chunk_alloc(struct nouveau_drm *drm, struct page **ppage)
chunk->pagemap.owner = drm->dev;
ret = nouveau_bo_new(&drm->client, DMEM_CHUNK_SIZE, 0,
- TTM_PL_FLAG_VRAM, 0, 0, NULL, NULL,
+ NOUVEAU_GEM_DOMAIN_VRAM, 0, 0, NULL, NULL,
&chunk->bo);
if (ret)
goto out_release;
- ret = nouveau_bo_pin(chunk->bo, TTM_PL_FLAG_VRAM, false);
+ ret = nouveau_bo_pin(chunk->bo, NOUVEAU_GEM_DOMAIN_VRAM, false);
if (ret)
goto out_bo_free;
@@ -346,7 +346,7 @@ nouveau_dmem_resume(struct nouveau_drm *drm)
mutex_lock(&drm->dmem->mutex);
list_for_each_entry(chunk, &drm->dmem->chunks, list) {
- ret = nouveau_bo_pin(chunk->bo, TTM_PL_FLAG_VRAM, false);
+ ret = nouveau_bo_pin(chunk->bo, NOUVEAU_GEM_DOMAIN_VRAM, false);
/* FIXME handle pin failure */
WARN_ON(ret);
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 73ebf5fba2fc..b8025507a9e4 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -164,6 +164,8 @@ struct nouveau_drm {
int type_vram;
int type_host[2];
int type_ncoh[2];
+ struct mutex io_reserve_mutex;
+ struct list_head io_reserve_lru;
} ttm;
/* GEM interface support */
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index fad8030ec1f8..24ec5339efb4 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -341,7 +341,7 @@ nouveau_fbcon_create(struct drm_fb_helper *helper,
if (ret)
goto out_unref;
- ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM, false);
+ ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, false);
if (ret) {
NV_ERROR(drm, "failed to pin fb: %d\n", ret);
goto out_unref;
@@ -378,8 +378,7 @@ nouveau_fbcon_create(struct drm_fb_helper *helper,
FBINFO_HWACCEL_FILLRECT |
FBINFO_HWACCEL_IMAGEBLIT;
info->fbops = &nouveau_fbcon_sw_ops;
- info->fix.smem_start = nvbo->bo.mem.bus.base +
- nvbo->bo.mem.bus.offset;
+ info->fix.smem_start = nvbo->bo.mem.bus.offset;
info->fix.smem_len = nvbo->bo.mem.num_pages << PAGE_SHIFT;
info->screen_base = nvbo_kmap_obj_iovirtual(nvbo);
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index 81f111ad3f4f..89adadf4706b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -176,20 +176,12 @@ nouveau_gem_new(struct nouveau_cli *cli, u64 size, int align, uint32_t domain,
{
struct nouveau_drm *drm = cli->drm;
struct nouveau_bo *nvbo;
- u32 flags = 0;
int ret;
- if (domain & NOUVEAU_GEM_DOMAIN_VRAM)
- flags |= TTM_PL_FLAG_VRAM;
- if (domain & NOUVEAU_GEM_DOMAIN_GART)
- flags |= TTM_PL_FLAG_TT;
- if (!flags || domain & NOUVEAU_GEM_DOMAIN_CPU)
- flags |= TTM_PL_FLAG_SYSTEM;
+ if (!(domain & (NOUVEAU_GEM_DOMAIN_VRAM | NOUVEAU_GEM_DOMAIN_GART)))
+ domain |= NOUVEAU_GEM_DOMAIN_CPU;
- if (domain & NOUVEAU_GEM_DOMAIN_COHERENT)
- flags |= TTM_PL_FLAG_UNCACHED;
-
- nvbo = nouveau_bo_alloc(cli, &size, &align, flags, tile_mode,
+ nvbo = nouveau_bo_alloc(cli, &size, &align, domain, tile_mode,
tile_flags);
if (IS_ERR(nvbo))
return PTR_ERR(nvbo);
@@ -202,7 +194,7 @@ nouveau_gem_new(struct nouveau_cli *cli, u64 size, int align, uint32_t domain,
return ret;
}
- ret = nouveau_bo_init(nvbo, size, align, flags, NULL, NULL);
+ ret = nouveau_bo_init(nvbo, size, align, domain, NULL, NULL);
if (ret) {
nouveau_bo_ref(NULL, &nvbo);
return ret;
@@ -296,32 +288,28 @@ nouveau_gem_set_domain(struct drm_gem_object *gem, uint32_t read_domains,
struct ttm_buffer_object *bo = &nvbo->bo;
uint32_t domains = valid_domains & nvbo->valid_domains &
(write_domains ? write_domains : read_domains);
- uint32_t pref_flags = 0, valid_flags = 0;
+ uint32_t pref_domains = 0;;
if (!domains)
return -EINVAL;
- if (valid_domains & NOUVEAU_GEM_DOMAIN_VRAM)
- valid_flags |= TTM_PL_FLAG_VRAM;
-
- if (valid_domains & NOUVEAU_GEM_DOMAIN_GART)
- valid_flags |= TTM_PL_FLAG_TT;
+ valid_domains &= ~(NOUVEAU_GEM_DOMAIN_VRAM | NOUVEAU_GEM_DOMAIN_GART);
if ((domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
bo->mem.mem_type == TTM_PL_VRAM)
- pref_flags |= TTM_PL_FLAG_VRAM;
+ pref_domains |= NOUVEAU_GEM_DOMAIN_VRAM;
else if ((domains & NOUVEAU_GEM_DOMAIN_GART) &&
bo->mem.mem_type == TTM_PL_TT)
- pref_flags |= TTM_PL_FLAG_TT;
+ pref_domains |= NOUVEAU_GEM_DOMAIN_GART;
else if (domains & NOUVEAU_GEM_DOMAIN_VRAM)
- pref_flags |= TTM_PL_FLAG_VRAM;
+ pref_domains |= NOUVEAU_GEM_DOMAIN_VRAM;
else
- pref_flags |= TTM_PL_FLAG_TT;
+ pref_domains |= NOUVEAU_GEM_DOMAIN_GART;
- nouveau_bo_placement_set(nvbo, pref_flags, valid_flags);
+ nouveau_bo_placement_set(nvbo, pref_domains, valid_domains);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c
index bae6a3eccee0..b2ecb91f8ddc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_prime.c
+++ b/drivers/gpu/drm/nouveau/nouveau_prime.c
@@ -32,7 +32,7 @@ struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *obj)
struct nouveau_bo *nvbo = nouveau_gem_object(obj);
int npages = nvbo->bo.num_pages;
- return drm_prime_pages_to_sg(nvbo->bo.ttm->pages, npages);
+ return drm_prime_pages_to_sg(obj->dev, nvbo->bo.ttm->pages, npages);
}
void *nouveau_gem_prime_vmap(struct drm_gem_object *obj)
@@ -64,14 +64,12 @@ struct drm_gem_object *nouveau_gem_prime_import_sg_table(struct drm_device *dev,
struct nouveau_bo *nvbo;
struct dma_resv *robj = attach->dmabuf->resv;
u64 size = attach->dmabuf->size;
- u32 flags = 0;
int align = 0;
int ret;
- flags = TTM_PL_FLAG_TT;
-
dma_resv_lock(robj, NULL);
- nvbo = nouveau_bo_alloc(&drm->client, &size, &align, flags, 0, 0);
+ nvbo = nouveau_bo_alloc(&drm->client, &size, &align,
+ NOUVEAU_GEM_DOMAIN_GART, 0, 0);
if (IS_ERR(nvbo)) {
obj = ERR_CAST(nvbo);
goto unlock;
@@ -88,7 +86,8 @@ struct drm_gem_object *nouveau_gem_prime_import_sg_table(struct drm_device *dev,
goto unlock;
}
- ret = nouveau_bo_init(nvbo, size, align, flags, sg, robj);
+ ret = nouveau_bo_init(nvbo, size, align, NOUVEAU_GEM_DOMAIN_GART,
+ sg, robj);
if (ret) {
nouveau_bo_ref(NULL, &nvbo);
obj = ERR_PTR(ret);
@@ -108,7 +107,7 @@ int nouveau_gem_prime_pin(struct drm_gem_object *obj)
int ret;
/* pin buffer into GTT */
- ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_TT, false);
+ ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_GART, false);
if (ret)
return -EINVAL;
diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
index eef75c53a197..806d9ec310f5 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
@@ -14,87 +14,65 @@ struct nouveau_sgdma_be {
struct nouveau_mem *mem;
};
-static void
-nouveau_sgdma_destroy(struct ttm_tt *ttm)
+void
+nouveau_sgdma_destroy(struct ttm_bo_device *bdev, struct ttm_tt *ttm)
{
struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm;
if (ttm) {
+ nouveau_sgdma_unbind(bdev, ttm);
+ ttm_tt_destroy_common(bdev, ttm);
ttm_dma_tt_fini(&nvbe->ttm);
kfree(nvbe);
}
}
-static int
-nv04_sgdma_bind(struct ttm_tt *ttm, struct ttm_resource *reg)
+int
+nouveau_sgdma_bind(struct ttm_bo_device *bdev, struct ttm_tt *ttm, struct ttm_resource *reg)
{
struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm;
+ struct nouveau_drm *drm = nouveau_bdev(bdev);
struct nouveau_mem *mem = nouveau_mem(reg);
int ret;
+ if (nvbe->mem)
+ return 0;
+
ret = nouveau_mem_host(reg, &nvbe->ttm);
if (ret)
return ret;
- ret = nouveau_mem_map(mem, &mem->cli->vmm.vmm, &mem->vma[0]);
- if (ret) {
- nouveau_mem_fini(mem);
- return ret;
+ if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) {
+ ret = nouveau_mem_map(mem, &mem->cli->vmm.vmm, &mem->vma[0]);
+ if (ret) {
+ nouveau_mem_fini(mem);
+ return ret;
+ }
}
nvbe->mem = mem;
return 0;
}
-static void
-nv04_sgdma_unbind(struct ttm_tt *ttm)
+void
+nouveau_sgdma_unbind(struct ttm_bo_device *bdev, struct ttm_tt *ttm)
{
struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm;
- nouveau_mem_fini(nvbe->mem);
-}
-
-static struct ttm_backend_func nv04_sgdma_backend = {
- .bind = nv04_sgdma_bind,
- .unbind = nv04_sgdma_unbind,
- .destroy = nouveau_sgdma_destroy
-};
-
-static int
-nv50_sgdma_bind(struct ttm_tt *ttm, struct ttm_resource *reg)
-{
- struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm;
- struct nouveau_mem *mem = nouveau_mem(reg);
- int ret;
-
- ret = nouveau_mem_host(reg, &nvbe->ttm);
- if (ret)
- return ret;
-
- nvbe->mem = mem;
- return 0;
+ if (nvbe->mem) {
+ nouveau_mem_fini(nvbe->mem);
+ nvbe->mem = NULL;
+ }
}
-static struct ttm_backend_func nv50_sgdma_backend = {
- .bind = nv50_sgdma_bind,
- .unbind = nv04_sgdma_unbind,
- .destroy = nouveau_sgdma_destroy
-};
-
struct ttm_tt *
nouveau_sgdma_create_ttm(struct ttm_buffer_object *bo, uint32_t page_flags)
{
- struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
struct nouveau_sgdma_be *nvbe;
nvbe = kzalloc(sizeof(*nvbe), GFP_KERNEL);
if (!nvbe)
return NULL;
- if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA)
- nvbe->ttm.ttm.func = &nv04_sgdma_backend;
- else
- nvbe->ttm.ttm.func = &nv50_sgdma_backend;
-
if (ttm_dma_tt_init(&nvbe->ttm, bo, page_flags)) {
kfree(nvbe);
return NULL;
diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c
index 53c6f8827322..427341753441 100644
--- a/drivers/gpu/drm/nouveau/nouveau_ttm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c
@@ -123,13 +123,51 @@ const struct ttm_resource_manager_func nv04_gart_manager = {
.free = nouveau_manager_del,
};
+static vm_fault_t nouveau_ttm_fault(struct vm_fault *vmf)
+{
+ struct vm_area_struct *vma = vmf->vma;
+ struct ttm_buffer_object *bo = vma->vm_private_data;
+ pgprot_t prot;
+ vm_fault_t ret;
+
+ ret = ttm_bo_vm_reserve(bo, vmf);
+ if (ret)
+ return ret;
+
+ nouveau_bo_del_io_reserve_lru(bo);
+
+ prot = vm_get_page_prot(vma->vm_flags);
+ ret = ttm_bo_vm_fault_reserved(vmf, prot, TTM_BO_VM_NUM_PREFAULT, 1);
+ if (ret == VM_FAULT_RETRY && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT))
+ return ret;
+
+ nouveau_bo_add_io_reserve_lru(bo);
+
+ dma_resv_unlock(bo->base.resv);
+
+ return ret;
+}
+
+static struct vm_operations_struct nouveau_ttm_vm_ops = {
+ .fault = nouveau_ttm_fault,
+ .open = ttm_bo_vm_open,
+ .close = ttm_bo_vm_close,
+ .access = ttm_bo_vm_access
+};
+
int
nouveau_ttm_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct drm_file *file_priv = filp->private_data;
struct nouveau_drm *drm = nouveau_drm(file_priv->minor->dev);
+ int ret;
- return ttm_bo_mmap(filp, vma, &drm->ttm.bdev);
+ ret = ttm_bo_mmap(filp, vma, &drm->ttm.bdev);
+ if (ret)
+ return ret;
+
+ vma->vm_ops = &nouveau_ttm_vm_ops;
+ return 0;
}
static int
@@ -156,24 +194,13 @@ nouveau_ttm_init_host(struct nouveau_drm *drm, u8 kind)
static int
nouveau_ttm_init_vram(struct nouveau_drm *drm)
{
- struct nvif_mmu *mmu = &drm->client.mmu;
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) {
- /* Some BARs do not support being ioremapped WC */
- const u8 type = mmu->type[drm->ttm.type_vram].type;
struct ttm_resource_manager *man = kzalloc(sizeof(*man), GFP_KERNEL);
+
if (!man)
return -ENOMEM;
- man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC;
- man->default_caching = TTM_PL_FLAG_WC;
-
- if (type & NVIF_MEM_UNCACHED) {
- man->available_caching = TTM_PL_FLAG_UNCACHED;
- man->default_caching = TTM_PL_FLAG_UNCACHED;
- }
-
man->func = &nouveau_vram_manager;
- man->use_io_reserve_lru = true;
ttm_resource_manager_init(man,
drm->gem.vram_available >> PAGE_SHIFT);
@@ -181,9 +208,7 @@ nouveau_ttm_init_vram(struct nouveau_drm *drm)
ttm_resource_manager_set_used(man, true);
return 0;
} else {
- return ttm_range_man_init(&drm->ttm.bdev, TTM_PL_VRAM,
- TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC,
- TTM_PL_FLAG_WC, false,
+ return ttm_range_man_init(&drm->ttm.bdev, TTM_PL_VRAM, false,
drm->gem.vram_available >> PAGE_SHIFT);
}
}
@@ -208,25 +233,14 @@ nouveau_ttm_init_gtt(struct nouveau_drm *drm)
{
struct ttm_resource_manager *man;
unsigned long size_pages = drm->gem.gart_available >> PAGE_SHIFT;
- unsigned available_caching, default_caching;
const struct ttm_resource_manager_func *func = NULL;
- if (drm->agp.bridge) {
- available_caching = TTM_PL_FLAG_UNCACHED |
- TTM_PL_FLAG_WC;
- default_caching = TTM_PL_FLAG_WC;
- } else {
- available_caching = TTM_PL_MASK_CACHING;
- default_caching = TTM_PL_FLAG_CACHED;
- }
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA)
func = &nouveau_gart_manager;
else if (!drm->agp.bridge)
func = &nv04_gart_manager;
else
- return ttm_range_man_init(&drm->ttm.bdev, TTM_PL_TT,
- available_caching, default_caching,
- true,
+ return ttm_range_man_init(&drm->ttm.bdev, TTM_PL_TT, true,
size_pages);
man = kzalloc(sizeof(*man), GFP_KERNEL);
@@ -234,8 +248,6 @@ nouveau_ttm_init_gtt(struct nouveau_drm *drm)
return -ENOMEM;
man->func = func;
- man->available_caching = available_caching;
- man->default_caching = default_caching;
man->use_tt = true;
ttm_resource_manager_init(man, size_pages);
ttm_set_driver_manager(&drm->ttm.bdev, TTM_PL_TT, man);
@@ -339,6 +351,9 @@ nouveau_ttm_init(struct nouveau_drm *drm)
return ret;
}
+ mutex_init(&drm->ttm.io_reserve_mutex);
+ INIT_LIST_HEAD(&drm->ttm.io_reserve_lru);
+
NV_INFO(drm, "VRAM: %d MiB\n", (u32)(drm->gem.vram_available >> 20));
NV_INFO(drm, "GART: %d MiB\n", (u32)(drm->gem.gart_available >> 20));
return 0;
diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.h b/drivers/gpu/drm/nouveau/nouveau_ttm.h
index eaf25461cd91..69552049bb96 100644
--- a/drivers/gpu/drm/nouveau/nouveau_ttm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_ttm.h
@@ -22,4 +22,7 @@ int nouveau_ttm_mmap(struct file *, struct vm_area_struct *);
int nouveau_ttm_global_init(struct nouveau_drm *);
void nouveau_ttm_global_release(struct nouveau_drm *);
+int nouveau_sgdma_bind(struct ttm_bo_device *bdev, struct ttm_tt *ttm, struct ttm_resource *reg);
+void nouveau_sgdma_unbind(struct ttm_bo_device *bdev, struct ttm_tt *ttm);
+void nouveau_sgdma_destroy(struct ttm_bo_device *bdev, struct ttm_tt *ttm);
#endif
diff --git a/drivers/gpu/drm/nouveau/nv17_fence.c b/drivers/gpu/drm/nouveau/nv17_fence.c
index 6b697ee6bc0e..1253fdec712d 100644
--- a/drivers/gpu/drm/nouveau/nv17_fence.c
+++ b/drivers/gpu/drm/nouveau/nv17_fence.c
@@ -130,10 +130,11 @@ nv17_fence_create(struct nouveau_drm *drm)
priv->base.context_del = nv10_fence_context_del;
spin_lock_init(&priv->lock);
- ret = nouveau_bo_new(&drm->client, 4096, 0x1000, TTM_PL_FLAG_VRAM,
+ ret = nouveau_bo_new(&drm->client, 4096, 0x1000,
+ NOUVEAU_GEM_DOMAIN_VRAM,
0, 0x0000, NULL, NULL, &priv->bo);
if (!ret) {
- ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM, false);
+ ret = nouveau_bo_pin(priv->bo, NOUVEAU_GEM_DOMAIN_VRAM, false);
if (!ret) {
ret = nouveau_bo_map(priv->bo);
if (ret)
diff --git a/drivers/gpu/drm/nouveau/nv50_fence.c b/drivers/gpu/drm/nouveau/nv50_fence.c
index 49b46f51073c..447238e3cbe7 100644
--- a/drivers/gpu/drm/nouveau/nv50_fence.c
+++ b/drivers/gpu/drm/nouveau/nv50_fence.c
@@ -81,10 +81,11 @@ nv50_fence_create(struct nouveau_drm *drm)
priv->base.context_del = nv10_fence_context_del;
spin_lock_init(&priv->lock);
- ret = nouveau_bo_new(&drm->client, 4096, 0x1000, TTM_PL_FLAG_VRAM,
+ ret = nouveau_bo_new(&drm->client, 4096, 0x1000,
+ NOUVEAU_GEM_DOMAIN_VRAM,
0, 0x0000, NULL, NULL, &priv->bo);
if (!ret) {
- ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM, false);
+ ret = nouveau_bo_pin(priv->bo, NOUVEAU_GEM_DOMAIN_VRAM, false);
if (!ret) {
ret = nouveau_bo_map(priv->bo);
if (ret)
diff --git a/drivers/gpu/drm/nouveau/nv84_fence.c b/drivers/gpu/drm/nouveau/nv84_fence.c
index 7ed36b3a6b7d..7c9c928c3196 100644
--- a/drivers/gpu/drm/nouveau/nv84_fence.c
+++ b/drivers/gpu/drm/nouveau/nv84_fence.c
@@ -209,12 +209,13 @@ nv84_fence_create(struct nouveau_drm *drm)
mutex_init(&priv->mutex);
/* Use VRAM if there is any ; otherwise fallback to system memory */
- domain = drm->client.device.info.ram_size != 0 ? TTM_PL_FLAG_VRAM :
- /*
- * fences created in sysmem must be non-cached or we
- * will lose CPU/GPU coherency!
- */
- TTM_PL_FLAG_TT | TTM_PL_FLAG_UNCACHED;
+ domain = drm->client.device.info.ram_size != 0 ?
+ NOUVEAU_GEM_DOMAIN_VRAM :
+ /*
+ * fences created in sysmem must be non-cached or we
+ * will lose CPU/GPU coherency!
+ */
+ NOUVEAU_GEM_DOMAIN_GART | NOUVEAU_GEM_DOMAIN_COHERENT;
ret = nouveau_bo_new(&drm->client, 16 * drm->chan.nr, 0,
domain, 0, 0, NULL, NULL, &priv->bo);
if (ret == 0) {
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 8d97d07c5871..b9dbedf8f15e 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -324,13 +324,30 @@ config DRM_PANEL_SAMSUNG_S6E63J0X03
select VIDEOMODE_HELPERS
config DRM_PANEL_SAMSUNG_S6E63M0
- tristate "Samsung S6E63M0 RGB/SPI panel"
+ tristate "Samsung S6E63M0 RGB panel"
depends on OF
- depends on SPI
depends on BACKLIGHT_CLASS_DEVICE
help
Say Y here if you want to enable support for Samsung S6E63M0
- AMOLED LCD panel.
+ AMOLED LCD panel. This panel can be accessed using SPI or
+ DSI.
+
+config DRM_PANEL_SAMSUNG_S6E63M0_SPI
+ tristate "Samsung S6E63M0 RGB SPI interface"
+ depends on SPI
+ depends on DRM_PANEL_SAMSUNG_S6E63M0
+ default DRM_PANEL_SAMSUNG_S6E63M0
+ help
+ Say Y here if you want to be able to access the Samsung
+ S6E63M0 panel using SPI.
+
+config DRM_PANEL_SAMSUNG_S6E63M0_DSI
+ tristate "Samsung S6E63M0 RGB DSI interface"
+ depends on DRM_MIPI_DSI
+ depends on DRM_PANEL_SAMSUNG_S6E63M0
+ help
+ Say Y here if you want to be able to access the Samsung
+ S6E63M0 panel using DSI.
config DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01
tristate "Samsung AMS452EF01 panel with S6E88A0 DSI video mode controller"
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 15a4e7752951..2ba560bca61d 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -34,6 +34,8 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0) += panel-samsung-s6e63m0.o
+obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_SPI) += panel-samsung-s6e63m0-spi.o
+obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_DSI) += panel-samsung-s6e63m0-dsi.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams452ef01.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c
new file mode 100644
index 000000000000..eec74c10ddda
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DSI interface to the Samsung S6E63M0 panel.
+ * (C) 2019 Linus Walleij
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_print.h>
+
+#include "panel-samsung-s6e63m0.h"
+
+#define MCS_GLOBAL_PARAM 0xb0
+#define S6E63M0_DSI_MAX_CHUNK 15 /* CMD + 15 bytes max */
+
+static int s6e63m0_dsi_dcs_read(struct device *dev, const u8 cmd, u8 *data)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
+ int ret;
+
+ ret = mipi_dsi_dcs_read(dsi, cmd, data, 1);
+ if (ret < 0) {
+ dev_err(dev, "could not read DCS CMD %02x\n", cmd);
+ return ret;
+ }
+
+ dev_info(dev, "DSI read CMD %02x = %02x\n", cmd, *data);
+
+ return 0;
+}
+
+static int s6e63m0_dsi_dcs_write(struct device *dev, const u8 *data, size_t len)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
+ const u8 *seqp = data;
+ u8 cmd;
+ u8 cmdwritten;
+ int remain;
+ int chunk;
+ int ret;
+
+ dev_info(dev, "DSI writing dcs seq: %*ph\n", (int)len, data);
+
+ /* Pick out and skip past the DCS command */
+ cmd = *seqp;
+ seqp++;
+ cmdwritten = 0;
+ remain = len - 1;
+ chunk = remain;
+
+ /* Send max S6E63M0_DSI_MAX_CHUNK bytes at a time */
+ if (chunk > S6E63M0_DSI_MAX_CHUNK)
+ chunk = S6E63M0_DSI_MAX_CHUNK;
+ ret = mipi_dsi_dcs_write(dsi, cmd, seqp, chunk);
+ if (ret < 0) {
+ dev_err(dev, "error sending DCS command seq cmd %02x\n", cmd);
+ return ret;
+ }
+ cmdwritten += chunk;
+ seqp += chunk;
+
+ while (cmdwritten < remain) {
+ chunk = remain - cmdwritten;
+ if (chunk > S6E63M0_DSI_MAX_CHUNK)
+ chunk = S6E63M0_DSI_MAX_CHUNK;
+ ret = mipi_dsi_dcs_write(dsi, MCS_GLOBAL_PARAM, &cmdwritten, 1);
+ if (ret < 0) {
+ dev_err(dev, "error sending CMD %02x global param %02x\n",
+ cmd, cmdwritten);
+ return ret;
+ }
+ ret = mipi_dsi_dcs_write(dsi, cmd, seqp, chunk);
+ if (ret < 0) {
+ dev_err(dev, "error sending CMD %02x chunk\n", cmd);
+ return ret;
+ }
+ cmdwritten += chunk;
+ seqp += chunk;
+ }
+ dev_info(dev, "sent command %02x %02x bytes\n", cmd, cmdwritten);
+
+ usleep_range(8000, 9000);
+
+ return 0;
+}
+
+static int s6e63m0_dsi_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ dsi->lanes = 2;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->hs_rate = 349440000;
+ dsi->lp_rate = 9600000;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
+ MIPI_DSI_MODE_EOT_PACKET |
+ MIPI_DSI_MODE_VIDEO_BURST;
+
+ ret = s6e63m0_probe(dev, s6e63m0_dsi_dcs_read, s6e63m0_dsi_dcs_write,
+ true);
+ if (ret)
+ return ret;
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0)
+ s6e63m0_remove(dev);
+
+ return ret;
+}
+
+static int s6e63m0_dsi_remove(struct mipi_dsi_device *dsi)
+{
+ mipi_dsi_detach(dsi);
+ return s6e63m0_remove(&dsi->dev);
+}
+
+static const struct of_device_id s6e63m0_dsi_of_match[] = {
+ { .compatible = "samsung,s6e63m0" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, s6e63m0_dsi_of_match);
+
+static struct mipi_dsi_driver s6e63m0_dsi_driver = {
+ .probe = s6e63m0_dsi_probe,
+ .remove = s6e63m0_dsi_remove,
+ .driver = {
+ .name = "panel-samsung-s6e63m0",
+ .of_match_table = s6e63m0_dsi_of_match,
+ },
+};
+module_mipi_dsi_driver(s6e63m0_dsi_driver);
+
+MODULE_AUTHOR("Linus Walleij <linusw@kernel.org>");
+MODULE_DESCRIPTION("s6e63m0 LCD DSI Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c
new file mode 100644
index 000000000000..d298d780220d
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+
+#include <drm/drm_print.h>
+
+#include "panel-samsung-s6e63m0.h"
+
+#define DATA_MASK 0x100
+
+static int s6e63m0_spi_dcs_read(struct device *dev, const u8 cmd, u8 *data)
+{
+ /*
+ * FIXME: implement reading DCS commands over SPI so we can
+ * properly identify which physical panel is connected.
+ */
+ *data = 0;
+
+ return 0;
+}
+
+static int s6e63m0_spi_write_word(struct device *dev, u16 data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct spi_transfer xfer = {
+ .len = 2,
+ .tx_buf = &data,
+ };
+ struct spi_message msg;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ return spi_sync(spi, &msg);
+}
+
+static int s6e63m0_spi_dcs_write(struct device *dev, const u8 *data, size_t len)
+{
+ int ret = 0;
+
+ dev_dbg(dev, "SPI writing dcs seq: %*ph\n", (int)len, data);
+ ret = s6e63m0_spi_write_word(dev, *data);
+
+ while (!ret && --len) {
+ ++data;
+ ret = s6e63m0_spi_write_word(dev, *data | DATA_MASK);
+ }
+
+ if (ret) {
+ dev_err(dev, "SPI error %d writing dcs seq: %*ph\n", ret,
+ (int)len, data);
+ }
+
+ usleep_range(300, 310);
+
+ return ret;
+}
+
+static int s6e63m0_spi_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ int ret;
+
+ spi->bits_per_word = 9;
+ spi->mode = SPI_MODE_3;
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(dev, "spi setup failed.\n");
+ return ret;
+ }
+ return s6e63m0_probe(dev, s6e63m0_spi_dcs_read, s6e63m0_spi_dcs_write,
+ false);
+}
+
+static int s6e63m0_spi_remove(struct spi_device *spi)
+{
+ return s6e63m0_remove(&spi->dev);
+}
+
+static const struct of_device_id s6e63m0_spi_of_match[] = {
+ { .compatible = "samsung,s6e63m0" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, s6e63m0_spi_of_match);
+
+static struct spi_driver s6e63m0_spi_driver = {
+ .probe = s6e63m0_spi_probe,
+ .remove = s6e63m0_spi_remove,
+ .driver = {
+ .name = "panel-samsung-s6e63m0",
+ .of_match_table = s6e63m0_spi_of_match,
+ },
+};
+module_spi_driver(s6e63m0_spi_driver);
+
+MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
+MODULE_DESCRIPTION("s6e63m0 LCD SPI Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
index 2cc772fdc456..3eee67e2d86a 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
@@ -16,25 +16,34 @@
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
-#include <linux/spi/spi.h>
#include <video/mipi_display.h>
+#include "panel-samsung-s6e63m0.h"
+
/* Manufacturer Command Set */
#define MCS_ELVSS_ON 0xb1
#define MCS_MIECTL1 0xc0
#define MCS_BCMODE 0xc1
+#define MCS_ERROR_CHECK 0xd5
+#define MCS_READ_ID1 0xda
+#define MCS_READ_ID2 0xdb
+#define MCS_READ_ID3 0xdc
+#define MCS_LEVEL_2_KEY 0xf0
+#define MCS_MTP_KEY 0xf1
#define MCS_DISCTL 0xf2
#define MCS_SRCCTL 0xf6
#define MCS_IFCTL 0xf7
#define MCS_PANELCTL 0xF8
#define MCS_PGAMMACTL 0xfa
+#define S6E63M0_LCD_ID_VALUE_M2 0xA4
+#define S6E63M0_LCD_ID_VALUE_SM2 0xB4
+#define S6E63M0_LCD_ID_VALUE_SM2_1 0xB6
+
#define NUM_GAMMA_LEVELS 11
#define GAMMA_TABLE_COUNT 23
-#define DATA_MASK 0x100
-
#define MAX_BRIGHTNESS (NUM_GAMMA_LEVELS - 1)
/* array of gamma tables for gamma value 2.2 */
@@ -87,8 +96,11 @@ static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = {
struct s6e63m0 {
struct device *dev;
+ int (*dcs_read)(struct device *dev, const u8 cmd, u8 *val);
+ int (*dcs_write)(struct device *dev, const u8 *data, size_t len);
struct drm_panel panel;
struct backlight_device *bl_dev;
+ u8 lcd_type;
struct regulator_bulk_data supplies[2];
struct gpio_desc *reset_gpio;
@@ -134,42 +146,20 @@ static int s6e63m0_clear_error(struct s6e63m0 *ctx)
return ret;
}
-static int s6e63m0_spi_write_word(struct s6e63m0 *ctx, u16 data)
+static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)
{
- struct spi_device *spi = to_spi_device(ctx->dev);
- struct spi_transfer xfer = {
- .len = 2,
- .tx_buf = &data,
- };
- struct spi_message msg;
-
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
+ if (ctx->error < 0)
+ return;
- return spi_sync(spi, &msg);
+ ctx->error = ctx->dcs_read(ctx->dev, cmd, data);
}
static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
{
- int ret = 0;
-
if (ctx->error < 0 || len == 0)
return;
- dev_dbg(ctx->dev, "writing dcs seq: %*ph\n", (int)len, data);
- ret = s6e63m0_spi_write_word(ctx, *data);
-
- while (!ret && --len) {
- ++data;
- ret = s6e63m0_spi_write_word(ctx, *data | DATA_MASK);
- }
-
- if (ret) {
- dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, (int)len, data);
- ctx->error = ret;
- }
-
- usleep_range(300, 310);
+ ctx->error = ctx->dcs_write(ctx->dev, data, len);
}
#define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
@@ -178,6 +168,43 @@ static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
})
+static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx)
+{
+ u8 id1, id2, id3;
+ int ret;
+
+ s6e63m0_dcs_read(ctx, MCS_READ_ID1, &id1);
+ s6e63m0_dcs_read(ctx, MCS_READ_ID2, &id2);
+ s6e63m0_dcs_read(ctx, MCS_READ_ID3, &id3);
+
+ ret = s6e63m0_clear_error(ctx);
+ if (ret) {
+ dev_err(ctx->dev, "error checking LCD type (%d)\n", ret);
+ ctx->lcd_type = 0x00;
+ return ret;
+ }
+
+ dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
+
+ /* We attempt to detect what panel is mounted on the controller */
+ switch (id2) {
+ case S6E63M0_LCD_ID_VALUE_M2:
+ dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n");
+ break;
+ case S6E63M0_LCD_ID_VALUE_SM2:
+ case S6E63M0_LCD_ID_VALUE_SM2_1:
+ dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI SM2\n");
+ break;
+ default:
+ dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2);
+ break;
+ }
+
+ ctx->lcd_type = id2;
+
+ return 0;
+}
+
static void s6e63m0_init(struct s6e63m0 *ctx)
{
s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
@@ -249,8 +276,6 @@ static void s6e63m0_init(struct s6e63m0 *ctx)
s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
0x0b);
-
- s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
}
static int s6e63m0_power_on(struct s6e63m0 *ctx)
@@ -263,6 +288,9 @@ static int s6e63m0_power_on(struct s6e63m0 *ctx)
msleep(25);
+ /* Be sure to send a reset pulse */
+ gpiod_set_value(ctx->reset_gpio, 1);
+ msleep(5);
gpiod_set_value(ctx->reset_gpio, 0);
msleep(120);
@@ -292,8 +320,10 @@ static int s6e63m0_disable(struct drm_panel *panel)
backlight_disable(ctx->bl_dev);
+ s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
+ msleep(10);
s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
- msleep(200);
+ msleep(120);
ctx->enabled = false;
@@ -331,6 +361,15 @@ static int s6e63m0_prepare(struct drm_panel *panel)
if (ret < 0)
return ret;
+ /* Magic to unlock level 2 control of the display */
+ s6e63m0_dcs_write_seq_static(ctx, MCS_LEVEL_2_KEY, 0x5a, 0x5a);
+ /* Magic to unlock MTP reading */
+ s6e63m0_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0x5a, 0x5a);
+
+ ret = s6e63m0_check_lcd_type(ctx);
+ if (ret < 0)
+ return ret;
+
s6e63m0_init(ctx);
ret = s6e63m0_clear_error(ctx);
@@ -350,7 +389,15 @@ static int s6e63m0_enable(struct drm_panel *panel)
if (ctx->enabled)
return 0;
+ s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
+ msleep(120);
s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
+ msleep(10);
+
+ s6e63m0_dcs_write_seq_static(ctx, MCS_ERROR_CHECK,
+ 0xE7, 0x14, 0x60, 0x17, 0x0A, 0x49, 0xC3,
+ 0x8F, 0x19, 0x64, 0x91, 0x84, 0x76, 0x20,
+ 0x0F, 0x00);
backlight_enable(ctx->bl_dev);
@@ -429,9 +476,11 @@ static int s6e63m0_backlight_register(struct s6e63m0 *ctx)
return ret;
}
-static int s6e63m0_probe(struct spi_device *spi)
+int s6e63m0_probe(struct device *dev,
+ int (*dcs_read)(struct device *dev, const u8 cmd, u8 *val),
+ int (*dcs_write)(struct device *dev, const u8 *data, size_t len),
+ bool dsi_mode)
{
- struct device *dev = &spi->dev;
struct s6e63m0 *ctx;
int ret;
@@ -439,7 +488,9 @@ static int s6e63m0_probe(struct spi_device *spi)
if (!ctx)
return -ENOMEM;
- spi_set_drvdata(spi, ctx);
+ ctx->dcs_read = dcs_read;
+ ctx->dcs_write = dcs_write;
+ dev_set_drvdata(dev, ctx);
ctx->dev = dev;
ctx->enabled = false;
@@ -460,15 +511,8 @@ static int s6e63m0_probe(struct spi_device *spi)
return PTR_ERR(ctx->reset_gpio);
}
- spi->bits_per_word = 9;
- spi->mode = SPI_MODE_3;
- ret = spi_setup(spi);
- if (ret < 0) {
- dev_err(dev, "spi setup failed.\n");
- return ret;
- }
-
drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
+ dsi_mode ? DRM_MODE_CONNECTOR_DSI :
DRM_MODE_CONNECTOR_DPI);
ret = s6e63m0_backlight_register(ctx);
@@ -479,31 +523,17 @@ static int s6e63m0_probe(struct spi_device *spi)
return 0;
}
+EXPORT_SYMBOL_GPL(s6e63m0_probe);
-static int s6e63m0_remove(struct spi_device *spi)
+int s6e63m0_remove(struct device *dev)
{
- struct s6e63m0 *ctx = spi_get_drvdata(spi);
+ struct s6e63m0 *ctx = dev_get_drvdata(dev);
drm_panel_remove(&ctx->panel);
return 0;
}
-
-static const struct of_device_id s6e63m0_of_match[] = {
- { .compatible = "samsung,s6e63m0" },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, s6e63m0_of_match);
-
-static struct spi_driver s6e63m0_driver = {
- .probe = s6e63m0_probe,
- .remove = s6e63m0_remove,
- .driver = {
- .name = "panel-samsung-s6e63m0",
- .of_match_table = s6e63m0_of_match,
- },
-};
-module_spi_driver(s6e63m0_driver);
+EXPORT_SYMBOL_GPL(s6e63m0_remove);
MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
MODULE_DESCRIPTION("s6e63m0 LCD Driver");
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h
new file mode 100644
index 000000000000..c669fec91763
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PANEL_SAMSUNG_S6E63M0_H
+#define _PANEL_SAMSUNG_S6E63M0_H
+
+int s6e63m0_probe(struct device *dev,
+ int (*dcs_read)(struct device *dev, const u8 cmd, u8 *val),
+ int (*dcs_write)(struct device *dev, const u8 *data,
+ size_t len),
+ bool dsi_mode);
+int s6e63m0_remove(struct device *dev);
+
+#endif /* _PANEL_SAMSUNG_S6E63M0_H */
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index 953f7536a773..2e9cbd1c4a58 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -70,6 +70,9 @@ struct panfrost_compatible {
int num_pm_domains;
/* Only required if num_pm_domains > 1. */
const char * const *pm_domain_names;
+
+ /* Vendor implementation quirks callback */
+ void (*vendor_quirk)(struct panfrost_device *pfdev);
};
struct panfrost_device {
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 36463c89e966..37d4cb7a5491 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -656,7 +656,18 @@ static const struct panfrost_compatible default_data = {
.pm_domain_names = NULL,
};
+static const struct panfrost_compatible amlogic_data = {
+ .num_supplies = ARRAY_SIZE(default_supplies),
+ .supply_names = default_supplies,
+ .vendor_quirk = panfrost_gpu_amlogic_quirk,
+};
+
static const struct of_device_id dt_match[] = {
+ /* Set first to probe before the generic compatibles */
+ { .compatible = "amlogic,meson-gxm-mali",
+ .data = &amlogic_data, },
+ { .compatible = "amlogic,meson-g12a-mali",
+ .data = &amlogic_data, },
{ .compatible = "arm,mali-t604", .data = &default_data, },
{ .compatible = "arm,mali-t624", .data = &default_data, },
{ .compatible = "arm,mali-t628", .data = &default_data, },
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
index e0f190e43813..e1b2a3376624 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
@@ -76,6 +76,17 @@ int panfrost_gpu_soft_reset(struct panfrost_device *pfdev)
return 0;
}
+void panfrost_gpu_amlogic_quirk(struct panfrost_device *pfdev)
+{
+ /*
+ * The Amlogic integrated Mali-T820, Mali-G31 & Mali-G52 needs
+ * these undocumented bits in GPU_PWR_OVERRIDE1 to be set in order
+ * to operate correctly.
+ */
+ gpu_write(pfdev, GPU_PWR_KEY, GPU_PWR_KEY_UNLOCK);
+ gpu_write(pfdev, GPU_PWR_OVERRIDE1, 0xfff | (0x20 << 16));
+}
+
static void panfrost_gpu_init_quirks(struct panfrost_device *pfdev)
{
u32 quirks = 0;
@@ -136,6 +147,10 @@ static void panfrost_gpu_init_quirks(struct panfrost_device *pfdev)
if (quirks)
gpu_write(pfdev, GPU_JM_CONFIG, quirks);
+
+ /* Here goes platform specific quirks */
+ if (pfdev->comp->vendor_quirk)
+ pfdev->comp->vendor_quirk(pfdev);
}
#define MAX_HW_REVS 6
@@ -305,6 +320,8 @@ void panfrost_gpu_power_on(struct panfrost_device *pfdev)
int ret;
u32 val;
+ panfrost_gpu_init_quirks(pfdev);
+
/* Just turn on everything for now */
gpu_write(pfdev, L2_PWRON_LO, pfdev->features.l2_present);
ret = readl_relaxed_poll_timeout(pfdev->iomem + L2_READY_LO,
@@ -344,6 +361,7 @@ int panfrost_gpu_init(struct panfrost_device *pfdev)
dma_set_mask_and_coherent(pfdev->dev,
DMA_BIT_MASK(FIELD_GET(0xff00, pfdev->features.mmu_features)));
+ dma_set_max_seg_size(pfdev->dev, UINT_MAX);
irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "gpu");
if (irq <= 0)
@@ -356,7 +374,6 @@ int panfrost_gpu_init(struct panfrost_device *pfdev)
return err;
}
- panfrost_gpu_init_quirks(pfdev);
panfrost_gpu_power_on(pfdev);
return 0;
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.h b/drivers/gpu/drm/panfrost/panfrost_gpu.h
index 4112412087b2..468c51e7e46d 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.h
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.h
@@ -16,4 +16,6 @@ int panfrost_gpu_soft_reset(struct panfrost_device *pfdev);
void panfrost_gpu_power_on(struct panfrost_device *pfdev);
void panfrost_gpu_power_off(struct panfrost_device *pfdev);
+void panfrost_gpu_amlogic_quirk(struct panfrost_device *pfdev);
+
#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_regs.h b/drivers/gpu/drm/panfrost/panfrost_regs.h
index ea38ac60581c..eddaa62ad8b0 100644
--- a/drivers/gpu/drm/panfrost/panfrost_regs.h
+++ b/drivers/gpu/drm/panfrost/panfrost_regs.h
@@ -51,6 +51,10 @@
#define GPU_STATUS 0x34
#define GPU_STATUS_PRFCNT_ACTIVE BIT(2)
#define GPU_LATEST_FLUSH_ID 0x38
+#define GPU_PWR_KEY 0x50 /* (WO) Power manager key register */
+#define GPU_PWR_KEY_UNLOCK 0x2968A819
+#define GPU_PWR_OVERRIDE0 0x54 /* (RW) Power manager override settings */
+#define GPU_PWR_OVERRIDE1 0x58 /* (RW) Power manager override settings */
#define GPU_FAULT_STATUS 0x3C
#define GPU_FAULT_ADDRESS_LO 0x40
#define GPU_FAULT_ADDRESS_HI 0x44
diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c
index f838b6d689aa..2bc364412e8b 100644
--- a/drivers/gpu/drm/qxl/qxl_object.c
+++ b/drivers/gpu/drm/qxl/qxl_object.c
@@ -64,16 +64,24 @@ void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned)
qbo->placement.placement = qbo->placements;
qbo->placement.busy_placement = qbo->placements;
- if (domain == QXL_GEM_DOMAIN_VRAM)
- qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM | pflag;
+ if (domain == QXL_GEM_DOMAIN_VRAM) {
+ qbo->placements[c].mem_type = TTM_PL_VRAM;
+ qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | pflag;
+ }
if (domain == QXL_GEM_DOMAIN_SURFACE) {
- qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_PRIV | pflag;
- qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM | pflag;
+ qbo->placements[c].mem_type = TTM_PL_PRIV;
+ qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | pflag;
+ qbo->placements[c].mem_type = TTM_PL_VRAM;
+ qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | pflag;
+ }
+ if (domain == QXL_GEM_DOMAIN_CPU) {
+ qbo->placements[c].mem_type = TTM_PL_SYSTEM;
+ qbo->placements[c++].flags = TTM_PL_MASK_CACHING | pflag;
+ }
+ if (!c) {
+ qbo->placements[c].mem_type = TTM_PL_SYSTEM;
+ qbo->placements[c++].flags = TTM_PL_MASK_CACHING;
}
- if (domain == QXL_GEM_DOMAIN_CPU)
- qbo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM | pflag;
- if (!c)
- qbo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
qbo->placement.num_placement = c;
qbo->placement.num_busy_placement = c;
for (i = 0; i < c; ++i) {
@@ -167,6 +175,7 @@ int qxl_bo_kmap(struct qxl_bo *bo, void **ptr)
void *qxl_bo_kmap_atomic_page(struct qxl_device *qdev,
struct qxl_bo *bo, int page_offset)
{
+ unsigned long offset;
void *rptr;
int ret;
struct io_mapping *map;
@@ -178,9 +187,8 @@ void *qxl_bo_kmap_atomic_page(struct qxl_device *qdev,
else
goto fallback;
- ret = qxl_ttm_io_mem_reserve(bo->tbo.bdev, &bo->tbo.mem);
-
- return io_mapping_map_atomic_wc(map, bo->tbo.mem.bus.offset + page_offset);
+ offset = bo->tbo.mem.start << PAGE_SHIFT;
+ return io_mapping_map_atomic_wc(map, offset + page_offset);
fallback:
if (bo->kptr) {
rptr = bo->kptr + (page_offset * PAGE_SIZE);
diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c
index 7aae0a96f043..fd691fff8394 100644
--- a/drivers/gpu/drm/qxl/qxl_ttm.c
+++ b/drivers/gpu/drm/qxl/qxl_ttm.c
@@ -55,7 +55,8 @@ static void qxl_evict_flags(struct ttm_buffer_object *bo,
static const struct ttm_place placements = {
.fpfn = 0,
.lpfn = 0,
- .flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM
+ .mem_type = TTM_PL_SYSTEM,
+ .flags = TTM_PL_MASK_CACHING
};
if (!qxl_ttm_bo_is_qxl_bo(bo)) {
@@ -81,13 +82,12 @@ int qxl_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
return 0;
case TTM_PL_VRAM:
mem->bus.is_iomem = true;
- mem->bus.base = qdev->vram_base;
- mem->bus.offset = mem->start << PAGE_SHIFT;
+ mem->bus.offset = (mem->start << PAGE_SHIFT) + qdev->vram_base;
break;
case TTM_PL_PRIV:
mem->bus.is_iomem = true;
- mem->bus.base = qdev->surfaceram_base;
- mem->bus.offset = mem->start << PAGE_SHIFT;
+ mem->bus.offset = (mem->start << PAGE_SHIFT) +
+ qdev->surfaceram_base;
break;
default:
return -EINVAL;
@@ -104,7 +104,8 @@ struct qxl_ttm_tt {
u64 offset;
};
-static int qxl_ttm_backend_bind(struct ttm_tt *ttm,
+static int qxl_ttm_backend_bind(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm,
struct ttm_resource *bo_mem)
{
struct qxl_ttm_tt *gtt = (void *)ttm;
@@ -118,25 +119,22 @@ static int qxl_ttm_backend_bind(struct ttm_tt *ttm,
return -1;
}
-static void qxl_ttm_backend_unbind(struct ttm_tt *ttm)
+static void qxl_ttm_backend_unbind(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm)
{
/* Not implemented */
}
-static void qxl_ttm_backend_destroy(struct ttm_tt *ttm)
+static void qxl_ttm_backend_destroy(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm)
{
struct qxl_ttm_tt *gtt = (void *)ttm;
+ ttm_tt_destroy_common(bdev, ttm);
ttm_tt_fini(&gtt->ttm);
kfree(gtt);
}
-static struct ttm_backend_func qxl_backend_func = {
- .bind = &qxl_ttm_backend_bind,
- .unbind = &qxl_ttm_backend_unbind,
- .destroy = &qxl_ttm_backend_destroy,
-};
-
static struct ttm_tt *qxl_ttm_tt_create(struct ttm_buffer_object *bo,
uint32_t page_flags)
{
@@ -147,7 +145,6 @@ static struct ttm_tt *qxl_ttm_tt_create(struct ttm_buffer_object *bo,
gtt = kzalloc(sizeof(struct qxl_ttm_tt), GFP_KERNEL);
if (gtt == NULL)
return NULL;
- gtt->ttm.func = &qxl_backend_func;
gtt->qdev = qdev;
if (ttm_tt_init(&gtt->ttm, bo, page_flags)) {
kfree(gtt);
@@ -156,16 +153,6 @@ static struct ttm_tt *qxl_ttm_tt_create(struct ttm_buffer_object *bo,
return &gtt->ttm;
}
-static void qxl_move_null(struct ttm_buffer_object *bo,
- struct ttm_resource *new_mem)
-{
- struct ttm_resource *old_mem = &bo->mem;
-
- BUG_ON(old_mem->mm_node != NULL);
- *old_mem = *new_mem;
- new_mem->mm_node = NULL;
-}
-
static int qxl_bo_move(struct ttm_buffer_object *bo, bool evict,
struct ttm_operation_ctx *ctx,
struct ttm_resource *new_mem)
@@ -178,7 +165,7 @@ static int qxl_bo_move(struct ttm_buffer_object *bo, bool evict,
return ret;
if (old_mem->mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) {
- qxl_move_null(bo, new_mem);
+ ttm_bo_move_null(bo, new_mem);
return 0;
}
return ttm_bo_move_memcpy(bo, ctx, new_mem);
@@ -202,6 +189,9 @@ static void qxl_bo_move_notify(struct ttm_buffer_object *bo,
static struct ttm_bo_driver qxl_bo_driver = {
.ttm_tt_create = &qxl_ttm_tt_create,
+ .ttm_tt_bind = &qxl_ttm_backend_bind,
+ .ttm_tt_destroy = &qxl_ttm_backend_destroy,
+ .ttm_tt_unbind = &qxl_ttm_backend_unbind,
.eviction_valuable = ttm_bo_eviction_valuable,
.evict_flags = &qxl_evict_flags,
.move = &qxl_bo_move,
@@ -213,8 +203,7 @@ static int qxl_ttm_init_mem_type(struct qxl_device *qdev,
unsigned int type,
uint64_t size)
{
- return ttm_range_man_init(&qdev->mman.bdev, type, TTM_PL_MASK_CACHING,
- TTM_PL_FLAG_CACHED, false, size);
+ return ttm_range_man_init(&qdev->mman.bdev, type, false, size);
}
int qxl_ttm_init(struct qxl_device *qdev)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index cc4f58d16589..a6d8de01194a 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -2815,10 +2815,12 @@ extern void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enabl
extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable);
extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain);
extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo);
-extern int radeon_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr,
+extern int radeon_ttm_tt_set_userptr(struct radeon_device *rdev,
+ struct ttm_tt *ttm, uint64_t addr,
uint32_t flags);
-extern bool radeon_ttm_tt_has_userptr(struct ttm_tt *ttm);
-extern bool radeon_ttm_tt_is_readonly(struct ttm_tt *ttm);
+extern bool radeon_ttm_tt_has_userptr(struct radeon_device *rdev, struct ttm_tt *ttm);
+extern bool radeon_ttm_tt_is_readonly(struct radeon_device *rdev, struct ttm_tt *ttm);
+bool radeon_ttm_tt_is_bound(struct ttm_bo_device *bdev, struct ttm_tt *ttm);
extern void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 base);
extern void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
extern int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index 33ae1b883268..21ce2f9502c0 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -160,7 +160,7 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
p->relocs[i].allowed_domains = domain;
}
- if (radeon_ttm_tt_has_userptr(p->relocs[i].robj->tbo.ttm)) {
+ if (radeon_ttm_tt_has_userptr(p->rdev, p->relocs[i].robj->tbo.ttm)) {
uint32_t domain = p->relocs[i].preferred_domains;
if (!(domain & RADEON_GEM_DOMAIN_GTT)) {
DRM_ERROR("Only RADEON_GEM_DOMAIN_GTT is "
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index 7f5dfe04789e..e5c4271e64ed 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -331,7 +331,7 @@ int radeon_gem_userptr_ioctl(struct drm_device *dev, void *data,
goto handle_lockup;
bo = gem_to_radeon_bo(gobj);
- r = radeon_ttm_tt_set_userptr(bo->tbo.ttm, args->addr, args->flags);
+ r = radeon_ttm_tt_set_userptr(rdev, bo->tbo.ttm, args->addr, args->flags);
if (r)
goto release_object;
@@ -420,7 +420,7 @@ int radeon_mode_dumb_mmap(struct drm_file *filp,
return -ENOENT;
}
robj = gem_to_radeon_bo(gobj);
- if (radeon_ttm_tt_has_userptr(robj->tbo.ttm)) {
+ if (radeon_ttm_tt_has_userptr(robj->rdev, robj->tbo.ttm)) {
drm_gem_object_put(gobj);
return -EPERM;
}
@@ -721,7 +721,7 @@ int radeon_gem_op_ioctl(struct drm_device *dev, void *data,
robj = gem_to_radeon_bo(gobj);
r = -EPERM;
- if (radeon_ttm_tt_has_userptr(robj->tbo.ttm))
+ if (radeon_ttm_tt_has_userptr(robj->rdev, robj->tbo.ttm))
goto out;
r = radeon_bo_reserve(robj, false);
diff --git a/drivers/gpu/drm/radeon/radeon_mn.c b/drivers/gpu/drm/radeon/radeon_mn.c
index f93829f08a4d..97b9b6dd6dd3 100644
--- a/drivers/gpu/drm/radeon/radeon_mn.c
+++ b/drivers/gpu/drm/radeon/radeon_mn.c
@@ -53,7 +53,7 @@ static bool radeon_mn_invalidate(struct mmu_interval_notifier *mn,
struct ttm_operation_ctx ctx = { false, false };
long r;
- if (!bo->tbo.ttm || bo->tbo.ttm->state != tt_bound)
+ if (!bo->tbo.ttm || !radeon_ttm_tt_is_bound(bo->tbo.bdev, bo->tbo.ttm))
return true;
if (!mmu_notifier_range_blockable(range))
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index bb7582afd803..316e35d3f8a9 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -112,58 +112,58 @@ void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain)
rbo->rdev->mc.visible_vram_size < rbo->rdev->mc.real_vram_size) {
rbo->placements[c].fpfn =
rbo->rdev->mc.visible_vram_size >> PAGE_SHIFT;
+ rbo->placements[c].mem_type = TTM_PL_VRAM;
rbo->placements[c++].flags = TTM_PL_FLAG_WC |
- TTM_PL_FLAG_UNCACHED |
- TTM_PL_FLAG_VRAM;
+ TTM_PL_FLAG_UNCACHED;
}
rbo->placements[c].fpfn = 0;
+ rbo->placements[c].mem_type = TTM_PL_VRAM;
rbo->placements[c++].flags = TTM_PL_FLAG_WC |
- TTM_PL_FLAG_UNCACHED |
- TTM_PL_FLAG_VRAM;
+ TTM_PL_FLAG_UNCACHED;
}
if (domain & RADEON_GEM_DOMAIN_GTT) {
if (rbo->flags & RADEON_GEM_GTT_UC) {
rbo->placements[c].fpfn = 0;
- rbo->placements[c++].flags = TTM_PL_FLAG_UNCACHED |
- TTM_PL_FLAG_TT;
+ rbo->placements[c].mem_type = TTM_PL_TT;
+ rbo->placements[c++].flags = TTM_PL_FLAG_UNCACHED;
} else if ((rbo->flags & RADEON_GEM_GTT_WC) ||
(rbo->rdev->flags & RADEON_IS_AGP)) {
rbo->placements[c].fpfn = 0;
+ rbo->placements[c].mem_type = TTM_PL_TT;
rbo->placements[c++].flags = TTM_PL_FLAG_WC |
- TTM_PL_FLAG_UNCACHED |
- TTM_PL_FLAG_TT;
+ TTM_PL_FLAG_UNCACHED;
} else {
rbo->placements[c].fpfn = 0;
- rbo->placements[c++].flags = TTM_PL_FLAG_CACHED |
- TTM_PL_FLAG_TT;
+ rbo->placements[c].mem_type = TTM_PL_TT;
+ rbo->placements[c++].flags = TTM_PL_FLAG_CACHED;
}
}
if (domain & RADEON_GEM_DOMAIN_CPU) {
if (rbo->flags & RADEON_GEM_GTT_UC) {
rbo->placements[c].fpfn = 0;
- rbo->placements[c++].flags = TTM_PL_FLAG_UNCACHED |
- TTM_PL_FLAG_SYSTEM;
+ rbo->placements[c].mem_type = TTM_PL_SYSTEM;
+ rbo->placements[c++].flags = TTM_PL_FLAG_UNCACHED;
} else if ((rbo->flags & RADEON_GEM_GTT_WC) ||
rbo->rdev->flags & RADEON_IS_AGP) {
rbo->placements[c].fpfn = 0;
+ rbo->placements[c].mem_type = TTM_PL_SYSTEM;
rbo->placements[c++].flags = TTM_PL_FLAG_WC |
- TTM_PL_FLAG_UNCACHED |
- TTM_PL_FLAG_SYSTEM;
+ TTM_PL_FLAG_UNCACHED;
} else {
rbo->placements[c].fpfn = 0;
- rbo->placements[c++].flags = TTM_PL_FLAG_CACHED |
- TTM_PL_FLAG_SYSTEM;
+ rbo->placements[c].mem_type = TTM_PL_SYSTEM;
+ rbo->placements[c++].flags = TTM_PL_FLAG_CACHED;
}
}
if (!c) {
rbo->placements[c].fpfn = 0;
- rbo->placements[c++].flags = TTM_PL_MASK_CACHING |
- TTM_PL_FLAG_SYSTEM;
+ rbo->placements[c].mem_type = TTM_PL_SYSTEM;
+ rbo->placements[c++].flags = TTM_PL_MASK_CACHING;
}
rbo->placement.num_placement = c;
@@ -171,7 +171,7 @@ void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain)
for (i = 0; i < c; ++i) {
if ((rbo->flags & RADEON_GEM_CPU_ACCESS) &&
- (rbo->placements[i].flags & TTM_PL_FLAG_VRAM) &&
+ (rbo->placements[i].mem_type == TTM_PL_VRAM) &&
!rbo->placements[i].fpfn)
rbo->placements[i].lpfn =
rbo->rdev->mc.visible_vram_size >> PAGE_SHIFT;
@@ -331,7 +331,7 @@ int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain, u64 max_offset,
struct ttm_operation_ctx ctx = { false, false };
int r, i;
- if (radeon_ttm_tt_has_userptr(bo->tbo.ttm))
+ if (radeon_ttm_tt_has_userptr(bo->rdev, bo->tbo.ttm))
return -EPERM;
if (bo->pin_count) {
@@ -360,7 +360,7 @@ int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain, u64 max_offset,
radeon_ttm_placement_from_domain(bo, domain);
for (i = 0; i < bo->placement.num_placement; i++) {
/* force to pin into visible video ram */
- if ((bo->placements[i].flags & TTM_PL_FLAG_VRAM) &&
+ if ((bo->placements[i].mem_type == TTM_PL_VRAM) &&
!(bo->flags & RADEON_GEM_NO_CPU_ACCESS) &&
(!max_offset || max_offset > bo->rdev->mc.visible_vram_size))
bo->placements[i].lpfn =
@@ -824,7 +824,7 @@ int radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo)
lpfn = rdev->mc.visible_vram_size >> PAGE_SHIFT;
for (i = 0; i < rbo->placement.num_placement; i++) {
/* Force into visible VRAM */
- if ((rbo->placements[i].flags & TTM_PL_FLAG_VRAM) &&
+ if ((rbo->placements[i].mem_type == TTM_PL_VRAM) &&
(!rbo->placements[i].lpfn || rbo->placements[i].lpfn > lpfn))
rbo->placements[i].lpfn = lpfn;
}
diff --git a/drivers/gpu/drm/radeon/radeon_prime.c b/drivers/gpu/drm/radeon/radeon_prime.c
index b906e8fbd5f3..b9de0e51c0be 100644
--- a/drivers/gpu/drm/radeon/radeon_prime.c
+++ b/drivers/gpu/drm/radeon/radeon_prime.c
@@ -36,7 +36,7 @@ struct sg_table *radeon_gem_prime_get_sg_table(struct drm_gem_object *obj)
struct radeon_bo *bo = gem_to_radeon_bo(obj);
int npages = bo->tbo.num_pages;
- return drm_prime_pages_to_sg(bo->tbo.ttm->pages, npages);
+ return drm_prime_pages_to_sg(obj->dev, bo->tbo.ttm->pages, npages);
}
void *radeon_gem_prime_vmap(struct drm_gem_object *obj)
@@ -121,7 +121,7 @@ struct dma_buf *radeon_gem_prime_export(struct drm_gem_object *gobj,
int flags)
{
struct radeon_bo *bo = gem_to_radeon_bo(gobj);
- if (radeon_ttm_tt_has_userptr(bo->tbo.ttm))
+ if (radeon_ttm_tt_has_userptr(bo->rdev, bo->tbo.ttm))
return ERR_PTR(-EPERM);
return drm_gem_prime_export(gobj, flags);
}
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index 74ad50c7491c..36150b7f31a9 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -56,6 +56,10 @@
static int radeon_ttm_debugfs_init(struct radeon_device *rdev);
static void radeon_ttm_debugfs_fini(struct radeon_device *rdev);
+static int radeon_ttm_tt_bind(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm,
+ struct ttm_resource *bo_mem);
+
struct radeon_device *radeon_get_rdev(struct ttm_bo_device *bdev)
{
struct radeon_mman *mman;
@@ -69,17 +73,13 @@ struct radeon_device *radeon_get_rdev(struct ttm_bo_device *bdev)
static int radeon_ttm_init_vram(struct radeon_device *rdev)
{
return ttm_range_man_init(&rdev->mman.bdev, TTM_PL_VRAM,
- TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC,
- TTM_PL_FLAG_WC, false,
- rdev->mc.real_vram_size >> PAGE_SHIFT);
+ false, rdev->mc.real_vram_size >> PAGE_SHIFT);
}
static int radeon_ttm_init_gtt(struct radeon_device *rdev)
{
return ttm_range_man_init(&rdev->mman.bdev, TTM_PL_TT,
- TTM_PL_MASK_CACHING,
- TTM_PL_FLAG_CACHED, true,
- rdev->mc.gtt_size >> PAGE_SHIFT);
+ true, rdev->mc.gtt_size >> PAGE_SHIFT);
}
static void radeon_evict_flags(struct ttm_buffer_object *bo,
@@ -88,7 +88,8 @@ static void radeon_evict_flags(struct ttm_buffer_object *bo,
static const struct ttm_place placements = {
.fpfn = 0,
.lpfn = 0,
- .flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM
+ .mem_type = TTM_PL_SYSTEM,
+ .flags = TTM_PL_MASK_CACHING
};
struct radeon_bo *rbo;
@@ -119,7 +120,7 @@ static void radeon_evict_flags(struct ttm_buffer_object *bo,
RADEON_GEM_DOMAIN_GTT);
rbo->placement.num_busy_placement = 0;
for (i = 0; i < rbo->placement.num_placement; i++) {
- if (rbo->placements[i].flags & TTM_PL_FLAG_VRAM) {
+ if (rbo->placements[i].mem_type == TTM_PL_VRAM) {
if (rbo->placements[i].fpfn < fpfn)
rbo->placements[i].fpfn = fpfn;
} else {
@@ -141,23 +142,14 @@ static void radeon_evict_flags(struct ttm_buffer_object *bo,
static int radeon_verify_access(struct ttm_buffer_object *bo, struct file *filp)
{
struct radeon_bo *rbo = container_of(bo, struct radeon_bo, tbo);
+ struct radeon_device *rdev = radeon_get_rdev(bo->bdev);
- if (radeon_ttm_tt_has_userptr(bo->ttm))
+ if (radeon_ttm_tt_has_userptr(rdev, bo->ttm))
return -EPERM;
return drm_vma_node_verify_access(&rbo->tbo.base.vma_node,
filp->private_data);
}
-static void radeon_move_null(struct ttm_buffer_object *bo,
- struct ttm_resource *new_mem)
-{
- struct ttm_resource *old_mem = &bo->mem;
-
- BUG_ON(old_mem->mm_node != NULL);
- *old_mem = *new_mem;
- new_mem->mm_node = NULL;
-}
-
static int radeon_move_blit(struct ttm_buffer_object *bo,
bool evict, bool no_wait_gpu,
struct ttm_resource *new_mem,
@@ -208,7 +200,7 @@ static int radeon_move_blit(struct ttm_buffer_object *bo,
if (IS_ERR(fence))
return PTR_ERR(fence);
- r = ttm_bo_move_accel_cleanup(bo, &fence->base, evict, new_mem);
+ r = ttm_bo_move_accel_cleanup(bo, &fence->base, evict, false, new_mem);
radeon_fence_unref(&fence);
return r;
}
@@ -233,7 +225,8 @@ static int radeon_move_vram_ram(struct ttm_buffer_object *bo,
placement.busy_placement = &placements;
placements.fpfn = 0;
placements.lpfn = 0;
- placements.flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
+ placements.mem_type = TTM_PL_TT;
+ placements.flags = TTM_PL_MASK_CACHING;
r = ttm_bo_mem_space(bo, &placement, &tmp_mem, &ctx);
if (unlikely(r)) {
return r;
@@ -244,7 +237,12 @@ static int radeon_move_vram_ram(struct ttm_buffer_object *bo,
goto out_cleanup;
}
- r = ttm_tt_bind(bo->ttm, &tmp_mem, &ctx);
+ r = ttm_tt_populate(bo->bdev, bo->ttm, &ctx);
+ if (unlikely(r)) {
+ goto out_cleanup;
+ }
+
+ r = radeon_ttm_tt_bind(bo->bdev, bo->ttm, &tmp_mem);
if (unlikely(r)) {
goto out_cleanup;
}
@@ -278,7 +276,8 @@ static int radeon_move_ram_vram(struct ttm_buffer_object *bo,
placement.busy_placement = &placements;
placements.fpfn = 0;
placements.lpfn = 0;
- placements.flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
+ placements.mem_type = TTM_PL_TT;
+ placements.flags = TTM_PL_MASK_CACHING;
r = ttm_bo_mem_space(bo, &placement, &tmp_mem, &ctx);
if (unlikely(r)) {
return r;
@@ -316,7 +315,7 @@ static int radeon_bo_move(struct ttm_buffer_object *bo, bool evict,
rdev = radeon_get_rdev(bo->bdev);
if (old_mem->mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) {
- radeon_move_null(bo, new_mem);
+ ttm_bo_move_null(bo, new_mem);
return 0;
}
if ((old_mem->mem_type == TTM_PL_TT &&
@@ -324,7 +323,7 @@ static int radeon_bo_move(struct ttm_buffer_object *bo, bool evict,
(old_mem->mem_type == TTM_PL_SYSTEM &&
new_mem->mem_type == TTM_PL_TT)) {
/* bind is enough */
- radeon_move_null(bo, new_mem);
+ ttm_bo_move_null(bo, new_mem);
return 0;
}
if (!rdev->ring[radeon_copy_ring_index(rdev)].ready ||
@@ -372,8 +371,8 @@ static int radeon_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_reso
#if IS_ENABLED(CONFIG_AGP)
if (rdev->flags & RADEON_IS_AGP) {
/* RADEON_IS_AGP is set only if AGP is active */
- mem->bus.offset = mem->start << PAGE_SHIFT;
- mem->bus.base = rdev->mc.agp_base;
+ mem->bus.offset = (mem->start << PAGE_SHIFT) +
+ rdev->mc.agp_base;
mem->bus.is_iomem = !rdev->ddev->agp->cant_use_aperture;
}
#endif
@@ -383,7 +382,7 @@ static int radeon_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_reso
/* check if it's visible */
if ((mem->bus.offset + bus_size) > rdev->mc.visible_vram_size)
return -EINVAL;
- mem->bus.base = rdev->mc.aper_base;
+ mem->bus.offset += rdev->mc.aper_base;
mem->bus.is_iomem = true;
#ifdef __alpha__
/*
@@ -392,12 +391,10 @@ static int radeon_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_reso
*/
if (mem->placement & TTM_PL_FLAG_WC)
mem->bus.addr =
- ioremap_wc(mem->bus.base + mem->bus.offset,
- bus_size);
+ ioremap_wc(mem->bus.offset, bus_size);
else
mem->bus.addr =
- ioremap(mem->bus.base + mem->bus.offset,
- bus_size);
+ ioremap(mem->bus.offset, bus_size);
if (!mem->bus.addr)
return -ENOMEM;
@@ -407,7 +404,7 @@ static int radeon_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_reso
* It then can be used to build PTEs for VRAM
* access, as done in ttm_bo_vm_fault().
*/
- mem->bus.base = (mem->bus.base & 0x0ffffffffUL) +
+ mem->bus.offset = (mem->bus.offset & 0x0ffffffffUL) +
rdev->ddev->hose->dense_mem_base;
#endif
break;
@@ -427,12 +424,13 @@ struct radeon_ttm_tt {
uint64_t userptr;
struct mm_struct *usermm;
uint32_t userflags;
+ bool bound;
};
/* prepare the sg table with the user pages */
-static int radeon_ttm_tt_pin_userptr(struct ttm_tt *ttm)
+static int radeon_ttm_tt_pin_userptr(struct ttm_bo_device *bdev, struct ttm_tt *ttm)
{
- struct radeon_device *rdev = radeon_get_rdev(ttm->bdev);
+ struct radeon_device *rdev = radeon_get_rdev(bdev);
struct radeon_ttm_tt *gtt = (void *)ttm;
unsigned pinned = 0;
int r;
@@ -491,9 +489,9 @@ release_pages:
return r;
}
-static void radeon_ttm_tt_unpin_userptr(struct ttm_tt *ttm)
+static void radeon_ttm_tt_unpin_userptr(struct ttm_bo_device *bdev, struct ttm_tt *ttm)
{
- struct radeon_device *rdev = radeon_get_rdev(ttm->bdev);
+ struct radeon_device *rdev = radeon_get_rdev(bdev);
struct radeon_ttm_tt *gtt = (void *)ttm;
struct sg_page_iter sg_iter;
@@ -520,17 +518,28 @@ static void radeon_ttm_tt_unpin_userptr(struct ttm_tt *ttm)
sg_free_table(ttm->sg);
}
-static int radeon_ttm_backend_bind(struct ttm_tt *ttm,
+static bool radeon_ttm_backend_is_bound(struct ttm_tt *ttm)
+{
+ struct radeon_ttm_tt *gtt = (void*)ttm;
+
+ return (gtt->bound);
+}
+
+static int radeon_ttm_backend_bind(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm,
struct ttm_resource *bo_mem)
{
struct radeon_ttm_tt *gtt = (void*)ttm;
- struct radeon_device *rdev = radeon_get_rdev(ttm->bdev);
+ struct radeon_device *rdev = radeon_get_rdev(bdev);
uint32_t flags = RADEON_GART_PAGE_VALID | RADEON_GART_PAGE_READ |
RADEON_GART_PAGE_WRITE;
int r;
+ if (gtt->bound)
+ return 0;
+
if (gtt->userptr) {
- radeon_ttm_tt_pin_userptr(ttm);
+ radeon_ttm_tt_pin_userptr(bdev, ttm);
flags &= ~RADEON_GART_PAGE_WRITE;
}
@@ -548,34 +557,36 @@ static int radeon_ttm_backend_bind(struct ttm_tt *ttm,
ttm->num_pages, (unsigned)gtt->offset);
return r;
}
+ gtt->bound = true;
return 0;
}
-static void radeon_ttm_backend_unbind(struct ttm_tt *ttm)
+static void radeon_ttm_backend_unbind(struct ttm_bo_device *bdev, struct ttm_tt *ttm)
{
struct radeon_ttm_tt *gtt = (void *)ttm;
- struct radeon_device *rdev = radeon_get_rdev(ttm->bdev);
+ struct radeon_device *rdev = radeon_get_rdev(bdev);
+
+ if (!gtt->bound)
+ return;
radeon_gart_unbind(rdev, gtt->offset, ttm->num_pages);
if (gtt->userptr)
- radeon_ttm_tt_unpin_userptr(ttm);
+ radeon_ttm_tt_unpin_userptr(bdev, ttm);
+ gtt->bound = false;
}
-static void radeon_ttm_backend_destroy(struct ttm_tt *ttm)
+static void radeon_ttm_backend_destroy(struct ttm_bo_device *bdev, struct ttm_tt *ttm)
{
struct radeon_ttm_tt *gtt = (void *)ttm;
+ radeon_ttm_backend_unbind(bdev, ttm);
+ ttm_tt_destroy_common(bdev, ttm);
+
ttm_dma_tt_fini(&gtt->ttm);
kfree(gtt);
}
-static struct ttm_backend_func radeon_backend_func = {
- .bind = &radeon_ttm_backend_bind,
- .unbind = &radeon_ttm_backend_unbind,
- .destroy = &radeon_ttm_backend_destroy,
-};
-
static struct ttm_tt *radeon_ttm_tt_create(struct ttm_buffer_object *bo,
uint32_t page_flags)
{
@@ -594,7 +605,6 @@ static struct ttm_tt *radeon_ttm_tt_create(struct ttm_buffer_object *bo,
if (gtt == NULL) {
return NULL;
}
- gtt->ttm.ttm.func = &radeon_backend_func;
if (ttm_dma_tt_init(&gtt->ttm, bo, page_flags)) {
kfree(gtt);
return NULL;
@@ -602,18 +612,25 @@ static struct ttm_tt *radeon_ttm_tt_create(struct ttm_buffer_object *bo,
return &gtt->ttm.ttm;
}
-static struct radeon_ttm_tt *radeon_ttm_tt_to_gtt(struct ttm_tt *ttm)
+static struct radeon_ttm_tt *radeon_ttm_tt_to_gtt(struct radeon_device *rdev,
+ struct ttm_tt *ttm)
{
- if (!ttm || ttm->func != &radeon_backend_func)
+#if IS_ENABLED(CONFIG_AGP)
+ if (rdev->flags & RADEON_IS_AGP)
return NULL;
- return (struct radeon_ttm_tt *)ttm;
+#endif
+
+ if (!ttm)
+ return NULL;
+ return container_of(ttm, struct radeon_ttm_tt, ttm.ttm);
}
-static int radeon_ttm_tt_populate(struct ttm_tt *ttm,
- struct ttm_operation_ctx *ctx)
+static int radeon_ttm_tt_populate(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm,
+ struct ttm_operation_ctx *ctx)
{
- struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(ttm);
- struct radeon_device *rdev;
+ struct radeon_device *rdev = radeon_get_rdev(bdev);
+ struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(rdev, ttm);
bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG);
if (gtt && gtt->userptr) {
@@ -622,21 +639,20 @@ static int radeon_ttm_tt_populate(struct ttm_tt *ttm,
return -ENOMEM;
ttm->page_flags |= TTM_PAGE_FLAG_SG;
- ttm->state = tt_unbound;
+ ttm_tt_set_populated(ttm);
return 0;
}
if (slave && ttm->sg) {
drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages,
gtt->ttm.dma_address, ttm->num_pages);
- ttm->state = tt_unbound;
+ ttm_tt_set_populated(ttm);
return 0;
}
- rdev = radeon_get_rdev(ttm->bdev);
#if IS_ENABLED(CONFIG_AGP)
if (rdev->flags & RADEON_IS_AGP) {
- return ttm_agp_tt_populate(ttm, ctx);
+ return ttm_pool_populate(ttm, ctx);
}
#endif
@@ -649,10 +665,10 @@ static int radeon_ttm_tt_populate(struct ttm_tt *ttm,
return ttm_populate_and_map_pages(rdev->dev, &gtt->ttm, ctx);
}
-static void radeon_ttm_tt_unpopulate(struct ttm_tt *ttm)
+static void radeon_ttm_tt_unpopulate(struct ttm_bo_device *bdev, struct ttm_tt *ttm)
{
- struct radeon_device *rdev;
- struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(ttm);
+ struct radeon_device *rdev = radeon_get_rdev(bdev);
+ struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(rdev, ttm);
bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG);
if (gtt && gtt->userptr) {
@@ -664,10 +680,9 @@ static void radeon_ttm_tt_unpopulate(struct ttm_tt *ttm)
if (slave)
return;
- rdev = radeon_get_rdev(ttm->bdev);
#if IS_ENABLED(CONFIG_AGP)
if (rdev->flags & RADEON_IS_AGP) {
- ttm_agp_tt_unpopulate(ttm);
+ ttm_pool_unpopulate(ttm);
return;
}
#endif
@@ -682,10 +697,11 @@ static void radeon_ttm_tt_unpopulate(struct ttm_tt *ttm)
ttm_unmap_and_unpopulate_pages(rdev->dev, &gtt->ttm);
}
-int radeon_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr,
+int radeon_ttm_tt_set_userptr(struct radeon_device *rdev,
+ struct ttm_tt *ttm, uint64_t addr,
uint32_t flags)
{
- struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(ttm);
+ struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(rdev, ttm);
if (gtt == NULL)
return -EINVAL;
@@ -696,9 +712,69 @@ int radeon_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr,
return 0;
}
-bool radeon_ttm_tt_has_userptr(struct ttm_tt *ttm)
+bool radeon_ttm_tt_is_bound(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm)
+{
+#if IS_ENABLED(CONFIG_AGP)
+ struct radeon_device *rdev = radeon_get_rdev(bdev);
+ if (rdev->flags & RADEON_IS_AGP)
+ return ttm_agp_is_bound(ttm);
+#endif
+ return radeon_ttm_backend_is_bound(ttm);
+}
+
+static int radeon_ttm_tt_bind(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm,
+ struct ttm_resource *bo_mem)
+{
+#if IS_ENABLED(CONFIG_AGP)
+ struct radeon_device *rdev = radeon_get_rdev(bdev);
+#endif
+
+ if (!bo_mem)
+ return -EINVAL;
+#if IS_ENABLED(CONFIG_AGP)
+ if (rdev->flags & RADEON_IS_AGP)
+ return ttm_agp_bind(ttm, bo_mem);
+#endif
+
+ return radeon_ttm_backend_bind(bdev, ttm, bo_mem);
+}
+
+static void radeon_ttm_tt_unbind(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm)
+{
+#if IS_ENABLED(CONFIG_AGP)
+ struct radeon_device *rdev = radeon_get_rdev(bdev);
+
+ if (rdev->flags & RADEON_IS_AGP) {
+ ttm_agp_unbind(ttm);
+ return;
+ }
+#endif
+ radeon_ttm_backend_unbind(bdev, ttm);
+}
+
+static void radeon_ttm_tt_destroy(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm)
+{
+#if IS_ENABLED(CONFIG_AGP)
+ struct radeon_device *rdev = radeon_get_rdev(bdev);
+
+ if (rdev->flags & RADEON_IS_AGP) {
+ ttm_agp_unbind(ttm);
+ ttm_tt_destroy_common(bdev, ttm);
+ ttm_agp_destroy(ttm);
+ return;
+ }
+#endif
+ radeon_ttm_backend_destroy(bdev, ttm);
+}
+
+bool radeon_ttm_tt_has_userptr(struct radeon_device *rdev,
+ struct ttm_tt *ttm)
{
- struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(ttm);
+ struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(rdev, ttm);
if (gtt == NULL)
return false;
@@ -706,9 +782,10 @@ bool radeon_ttm_tt_has_userptr(struct ttm_tt *ttm)
return !!gtt->userptr;
}
-bool radeon_ttm_tt_is_readonly(struct ttm_tt *ttm)
+bool radeon_ttm_tt_is_readonly(struct radeon_device *rdev,
+ struct ttm_tt *ttm)
{
- struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(ttm);
+ struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(rdev, ttm);
if (gtt == NULL)
return false;
@@ -720,6 +797,9 @@ static struct ttm_bo_driver radeon_bo_driver = {
.ttm_tt_create = &radeon_ttm_tt_create,
.ttm_tt_populate = &radeon_ttm_tt_populate,
.ttm_tt_unpopulate = &radeon_ttm_tt_unpopulate,
+ .ttm_tt_bind = &radeon_ttm_tt_bind,
+ .ttm_tt_unbind = &radeon_ttm_tt_unbind,
+ .ttm_tt_destroy = &radeon_ttm_tt_destroy,
.eviction_valuable = ttm_bo_eviction_valuable,
.evict_flags = &radeon_evict_flags,
.move = &radeon_bo_move,
diff --git a/drivers/gpu/drm/radeon/radeon_vm.c b/drivers/gpu/drm/radeon/radeon_vm.c
index 71e2c3785ab9..ebad27c91a0d 100644
--- a/drivers/gpu/drm/radeon/radeon_vm.c
+++ b/drivers/gpu/drm/radeon/radeon_vm.c
@@ -942,7 +942,7 @@ int radeon_vm_bo_update(struct radeon_device *rdev,
bo_va->flags &= ~RADEON_VM_PAGE_VALID;
bo_va->flags &= ~RADEON_VM_PAGE_SYSTEM;
bo_va->flags &= ~RADEON_VM_PAGE_SNOOPED;
- if (bo_va->bo && radeon_ttm_tt_is_readonly(bo_va->bo->tbo.ttm))
+ if (bo_va->bo && radeon_ttm_tt_is_readonly(rdev, bo_va->bo->tbo.ttm))
bo_va->flags &= ~RADEON_VM_PAGE_WRITEABLE;
if (mem) {
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
index cb50f2ba2e46..62e5d0970525 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -85,7 +85,8 @@ static int rockchip_gem_get_pages(struct rockchip_gem_object *rk_obj)
rk_obj->num_pages = rk_obj->base.size >> PAGE_SHIFT;
- rk_obj->sgt = drm_prime_pages_to_sg(rk_obj->pages, rk_obj->num_pages);
+ rk_obj->sgt = drm_prime_pages_to_sg(rk_obj->base.dev,
+ rk_obj->pages, rk_obj->num_pages);
if (IS_ERR(rk_obj->sgt)) {
ret = PTR_ERR(rk_obj->sgt);
goto err_put_pages;
@@ -441,7 +442,7 @@ struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
int ret;
if (rk_obj->pages)
- return drm_prime_pages_to_sg(rk_obj->pages, rk_obj->num_pages);
+ return drm_prime_pages_to_sg(obj->dev, rk_obj->pages, rk_obj->num_pages);
sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
if (!sgt)
diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c
index 01d94befab11..a2bac20ff19d 100644
--- a/drivers/gpu/drm/tegra/gem.c
+++ b/drivers/gpu/drm/tegra/gem.c
@@ -282,7 +282,7 @@ static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo)
bo->num_pages = bo->gem.size >> PAGE_SHIFT;
- bo->sgt = drm_prime_pages_to_sg(bo->pages, bo->num_pages);
+ bo->sgt = drm_prime_pages_to_sg(bo->gem.dev, bo->pages, bo->num_pages);
if (IS_ERR(bo->sgt)) {
err = PTR_ERR(bo->sgt);
goto put_pages;
diff --git a/drivers/gpu/drm/ttm/ttm_agp_backend.c b/drivers/gpu/drm/ttm/ttm_agp_backend.c
index 09fe80e215c5..a98fd795b752 100644
--- a/drivers/gpu/drm/ttm/ttm_agp_backend.c
+++ b/drivers/gpu/drm/ttm/ttm_agp_backend.c
@@ -48,7 +48,7 @@ struct ttm_agp_backend {
struct agp_bridge_data *bridge;
};
-static int ttm_agp_bind(struct ttm_tt *ttm, struct ttm_resource *bo_mem)
+int ttm_agp_bind(struct ttm_tt *ttm, struct ttm_resource *bo_mem)
{
struct ttm_agp_backend *agp_be = container_of(ttm, struct ttm_agp_backend, ttm);
struct page *dummy_read_page = ttm_bo_glob.dummy_read_page;
@@ -57,6 +57,9 @@ static int ttm_agp_bind(struct ttm_tt *ttm, struct ttm_resource *bo_mem)
int ret, cached = (bo_mem->placement & TTM_PL_FLAG_CACHED);
unsigned i;
+ if (agp_be->mem)
+ return 0;
+
mem = agp_allocate_memory(agp_be->bridge, ttm->num_pages, AGP_USER_MEMORY);
if (unlikely(mem == NULL))
return -ENOMEM;
@@ -81,8 +84,9 @@ static int ttm_agp_bind(struct ttm_tt *ttm, struct ttm_resource *bo_mem)
return ret;
}
+EXPORT_SYMBOL(ttm_agp_bind);
-static void ttm_agp_unbind(struct ttm_tt *ttm)
+void ttm_agp_unbind(struct ttm_tt *ttm)
{
struct ttm_agp_backend *agp_be = container_of(ttm, struct ttm_agp_backend, ttm);
@@ -95,8 +99,20 @@ static void ttm_agp_unbind(struct ttm_tt *ttm)
agp_be->mem = NULL;
}
}
+EXPORT_SYMBOL(ttm_agp_unbind);
+
+bool ttm_agp_is_bound(struct ttm_tt *ttm)
+{
+ struct ttm_agp_backend *agp_be = container_of(ttm, struct ttm_agp_backend, ttm);
+
+ if (!ttm)
+ return false;
+
+ return (agp_be->mem != NULL);
+}
+EXPORT_SYMBOL(ttm_agp_is_bound);
-static void ttm_agp_destroy(struct ttm_tt *ttm)
+void ttm_agp_destroy(struct ttm_tt *ttm)
{
struct ttm_agp_backend *agp_be = container_of(ttm, struct ttm_agp_backend, ttm);
@@ -105,12 +121,7 @@ static void ttm_agp_destroy(struct ttm_tt *ttm)
ttm_tt_fini(ttm);
kfree(agp_be);
}
-
-static struct ttm_backend_func ttm_agp_func = {
- .bind = ttm_agp_bind,
- .unbind = ttm_agp_unbind,
- .destroy = ttm_agp_destroy,
-};
+EXPORT_SYMBOL(ttm_agp_destroy);
struct ttm_tt *ttm_agp_tt_create(struct ttm_buffer_object *bo,
struct agp_bridge_data *bridge,
@@ -124,7 +135,6 @@ struct ttm_tt *ttm_agp_tt_create(struct ttm_buffer_object *bo,
agp_be->mem = NULL;
agp_be->bridge = bridge;
- agp_be->ttm.func = &ttm_agp_func;
if (ttm_tt_init(&agp_be->ttm, bo, page_flags)) {
kfree(agp_be);
@@ -134,18 +144,3 @@ struct ttm_tt *ttm_agp_tt_create(struct ttm_buffer_object *bo,
return &agp_be->ttm;
}
EXPORT_SYMBOL(ttm_agp_tt_create);
-
-int ttm_agp_tt_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)
-{
- if (ttm->state != tt_unpopulated)
- return 0;
-
- return ttm_pool_populate(ttm, ctx);
-}
-EXPORT_SYMBOL(ttm_agp_tt_populate);
-
-void ttm_agp_tt_unpopulate(struct ttm_tt *ttm)
-{
- ttm_pool_unpopulate(ttm);
-}
-EXPORT_SYMBOL(ttm_agp_tt_unpopulate);
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index e3931e515906..70b3bee27850 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -64,34 +64,18 @@ static void ttm_bo_default_destroy(struct ttm_buffer_object *bo)
kfree(bo);
}
-static inline int ttm_mem_type_from_place(const struct ttm_place *place,
- uint32_t *mem_type)
-{
- int pos;
-
- pos = ffs(place->flags & TTM_PL_MASK_MEM);
- if (unlikely(!pos))
- return -EINVAL;
-
- *mem_type = pos - 1;
- return 0;
-}
-
static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo,
struct ttm_placement *placement)
{
struct drm_printer p = drm_debug_printer(TTM_PFX);
- int i, ret, mem_type;
struct ttm_resource_manager *man;
+ int i, mem_type;
drm_printf(&p, "No space for %p (%lu pages, %luK, %luM)\n",
bo, bo->mem.num_pages, bo->mem.size >> 10,
bo->mem.size >> 20);
for (i = 0; i < placement->num_placement; i++) {
- ret = ttm_mem_type_from_place(&placement->placement[i],
- &mem_type);
- if (ret)
- return;
+ mem_type = placement->placement[i].mem_type;
drm_printf(&p, " placement[%d]=0x%08X (%d)\n",
i, placement->placement[i].flags, mem_type);
man = ttm_manager_type(bo->bdev, mem_type);
@@ -125,12 +109,6 @@ static struct kobj_type ttm_bo_glob_kobj_type = {
.default_attrs = ttm_bo_global_attrs
};
-
-static inline uint32_t ttm_bo_type_flags(unsigned type)
-{
- return 1 << (type);
-}
-
static void ttm_bo_add_mem_to_lru(struct ttm_buffer_object *bo,
struct ttm_resource *mem)
{
@@ -263,11 +241,7 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
struct ttm_resource_manager *new_man = ttm_manager_type(bdev, mem->mem_type);
int ret;
- ret = ttm_mem_io_lock(old_man, true);
- if (unlikely(ret != 0))
- goto out_err;
- ttm_bo_unmap_virtual_locked(bo);
- ttm_mem_io_unlock(old_man);
+ ttm_bo_unmap_virtual(bo);
/*
* Create and bind a ttm if required.
@@ -286,7 +260,11 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
goto out_err;
if (mem->mem_type != TTM_PL_SYSTEM) {
- ret = ttm_tt_bind(bo->ttm, mem, ctx);
+ ret = ttm_tt_populate(bdev, bo->ttm, ctx);
+ if (ret)
+ goto out_err;
+
+ ret = ttm_bo_tt_bind(bo, mem);
if (ret)
goto out_err;
}
@@ -320,17 +298,13 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
}
moved:
- bo->evicted = false;
-
ctx->bytes_moved += bo->num_pages << PAGE_SHIFT;
return 0;
out_err:
new_man = ttm_manager_type(bdev, bo->mem.mem_type);
- if (!new_man->use_tt) {
- ttm_tt_destroy(bo->ttm);
- bo->ttm = NULL;
- }
+ if (!new_man->use_tt)
+ ttm_bo_tt_destroy(bo);
return ret;
}
@@ -348,8 +322,7 @@ static void ttm_bo_cleanup_memtype_use(struct ttm_buffer_object *bo)
if (bo->bdev->driver->move_notify)
bo->bdev->driver->move_notify(bo, false, NULL);
- ttm_tt_destroy(bo->ttm);
- bo->ttm = NULL;
+ ttm_bo_tt_destroy(bo);
ttm_resource_free(bo, &bo->mem);
}
@@ -538,7 +511,6 @@ static void ttm_bo_release(struct kref *kref)
struct ttm_buffer_object *bo =
container_of(kref, struct ttm_buffer_object, kref);
struct ttm_bo_device *bdev = bo->bdev;
- struct ttm_resource_manager *man = ttm_manager_type(bdev, bo->mem.mem_type);
size_t acc_size = bo->acc_size;
int ret;
@@ -556,9 +528,7 @@ static void ttm_bo_release(struct kref *kref)
bo->bdev->driver->release_notify(bo);
drm_vma_offset_remove(bdev->vma_manager, &bo->base.vma_node);
- ttm_mem_io_lock(man, false);
- ttm_mem_io_free_vm(bo);
- ttm_mem_io_unlock(man);
+ ttm_mem_io_free(bdev, &bo->mem);
}
if (!dma_resv_test_signaled_rcu(bo->base.resv, true) ||
@@ -648,8 +618,8 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo,
evict_mem = bo->mem;
evict_mem.mm_node = NULL;
- evict_mem.bus.io_reserved_vm = false;
- evict_mem.bus.io_reserved_count = 0;
+ evict_mem.bus.offset = 0;
+ evict_mem.bus.addr = NULL;
ret = ttm_bo_mem_space(bo, &placement, &evict_mem, ctx);
if (ret) {
@@ -666,9 +636,7 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo,
if (ret != -ERESTARTSYS)
pr_err("Buffer eviction failed\n");
ttm_resource_free(bo, &evict_mem);
- goto out;
}
- bo->evicted = true;
out:
return ret;
}
@@ -905,8 +873,6 @@ static uint32_t ttm_bo_select_caching(struct ttm_resource_manager *man,
if ((cur_placement & caching) != 0)
result |= (cur_placement & caching);
- else if ((man->default_caching & caching) != 0)
- result |= man->default_caching;
else if ((TTM_PL_FLAG_CACHED & caching) != 0)
result |= TTM_PL_FLAG_CACHED;
else if ((TTM_PL_FLAG_WC & caching) != 0)
@@ -917,25 +883,6 @@ static uint32_t ttm_bo_select_caching(struct ttm_resource_manager *man,
return result;
}
-static bool ttm_bo_mt_compatible(struct ttm_resource_manager *man,
- uint32_t mem_type,
- const struct ttm_place *place,
- uint32_t *masked_placement)
-{
- uint32_t cur_flags = ttm_bo_type_flags(mem_type);
-
- if ((cur_flags & place->flags & TTM_PL_MASK_MEM) == 0)
- return false;
-
- if ((place->flags & man->available_caching) == 0)
- return false;
-
- cur_flags |= (place->flags & man->available_caching);
-
- *masked_placement = cur_flags;
- return true;
-}
-
/**
* ttm_bo_mem_placement - check if placement is compatible
* @bo: BO to find memory for
@@ -953,30 +900,18 @@ static int ttm_bo_mem_placement(struct ttm_buffer_object *bo,
struct ttm_operation_ctx *ctx)
{
struct ttm_bo_device *bdev = bo->bdev;
- uint32_t mem_type = TTM_PL_SYSTEM;
struct ttm_resource_manager *man;
uint32_t cur_flags = 0;
- int ret;
- ret = ttm_mem_type_from_place(place, &mem_type);
- if (ret)
- return ret;
-
- man = ttm_manager_type(bdev, mem_type);
+ man = ttm_manager_type(bdev, place->mem_type);
if (!man || !ttm_resource_manager_used(man))
return -EBUSY;
- if (!ttm_bo_mt_compatible(man, mem_type, place, &cur_flags))
- return -EBUSY;
-
- cur_flags = ttm_bo_select_caching(man, bo->mem.placement, cur_flags);
- /*
- * Use the access and other non-mapping-related flag bits from
- * the memory placement flags to the current flags
- */
- ttm_flag_masked(&cur_flags, place->flags, ~TTM_PL_MASK_MEMTYPE);
+ cur_flags = ttm_bo_select_caching(man, bo->mem.placement,
+ place->flags);
+ cur_flags |= place->flags & ~TTM_PL_MASK_CACHING;
- mem->mem_type = mem_type;
+ mem->mem_type = place->mem_type;
mem->placement = cur_flags;
spin_lock(&ttm_bo_glob.lru_lock);
@@ -1013,10 +948,8 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo,
struct ttm_resource_manager *man;
ret = ttm_bo_mem_placement(bo, place, mem, ctx);
- if (ret == -EBUSY)
- continue;
if (ret)
- goto error;
+ continue;
type_found = true;
ret = ttm_resource_alloc(bo, place, mem);
@@ -1041,10 +974,8 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo,
const struct ttm_place *place = &placement->busy_placement[i];
ret = ttm_bo_mem_placement(bo, place, mem, ctx);
- if (ret == -EBUSY)
- continue;
if (ret)
- goto error;
+ continue;
type_found = true;
ret = ttm_bo_mem_force_space(bo, place, mem, ctx);
@@ -1082,8 +1013,8 @@ static int ttm_bo_move_buffer(struct ttm_buffer_object *bo,
mem.num_pages = bo->num_pages;
mem.size = mem.num_pages << PAGE_SHIFT;
mem.page_alignment = bo->mem.page_alignment;
- mem.bus.io_reserved_vm = false;
- mem.bus.io_reserved_count = 0;
+ mem.bus.offset = 0;
+ mem.bus.addr = NULL;
mem.mm_node = NULL;
/*
@@ -1115,7 +1046,7 @@ static bool ttm_bo_places_compat(const struct ttm_place *places,
*new_flags = heap->flags;
if ((*new_flags & mem->placement & TTM_PL_MASK_CACHING) &&
- (*new_flags & mem->placement & TTM_PL_MASK_MEM) &&
+ (mem->mem_type == heap->mem_type) &&
(!(*new_flags & TTM_PL_FLAG_CONTIGUOUS) ||
(mem->placement & TTM_PL_FLAG_CONTIGUOUS)))
return true;
@@ -1170,12 +1101,8 @@ int ttm_bo_validate(struct ttm_buffer_object *bo,
if (ret)
return ret;
} else {
- /*
- * Use the access and other non-mapping-related flag bits from
- * the compatible memory placement flags to the active flags
- */
- ttm_flag_masked(&bo->mem.placement, new_flags,
- ~TTM_PL_MASK_MEMTYPE);
+ bo->mem.placement &= TTM_PL_MASK_CACHING;
+ bo->mem.placement |= new_flags & ~TTM_PL_MASK_CACHING;
}
/*
* We might need to add a TTM.
@@ -1232,7 +1159,6 @@ int ttm_bo_init_reserved(struct ttm_bo_device *bdev,
INIT_LIST_HEAD(&bo->lru);
INIT_LIST_HEAD(&bo->ddestroy);
INIT_LIST_HEAD(&bo->swap);
- INIT_LIST_HEAD(&bo->io_reserve_lru);
bo->bdev = bdev;
bo->type = type;
bo->num_pages = num_pages;
@@ -1241,10 +1167,10 @@ int ttm_bo_init_reserved(struct ttm_bo_device *bdev,
bo->mem.num_pages = bo->num_pages;
bo->mem.mm_node = NULL;
bo->mem.page_alignment = page_alignment;
- bo->mem.bus.io_reserved_vm = false;
- bo->mem.bus.io_reserved_count = 0;
+ bo->mem.bus.offset = 0;
+ bo->mem.bus.addr = NULL;
bo->moving = NULL;
- bo->mem.placement = (TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED);
+ bo->mem.placement = TTM_PL_FLAG_CACHED;
bo->acc_size = acc_size;
bo->sg = sg;
if (resv) {
@@ -1325,9 +1251,9 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
}
EXPORT_SYMBOL(ttm_bo_init);
-size_t ttm_bo_acc_size(struct ttm_bo_device *bdev,
- unsigned long bo_size,
- unsigned struct_size)
+static size_t ttm_bo_acc_size(struct ttm_bo_device *bdev,
+ unsigned long bo_size,
+ unsigned struct_size)
{
unsigned npages = (PAGE_ALIGN(bo_size)) >> PAGE_SHIFT;
size_t size = 0;
@@ -1337,7 +1263,6 @@ size_t ttm_bo_acc_size(struct ttm_bo_device *bdev,
size += ttm_round_pot(sizeof(struct ttm_tt));
return size;
}
-EXPORT_SYMBOL(ttm_bo_acc_size);
size_t ttm_bo_dma_acc_size(struct ttm_bo_device *bdev,
unsigned long bo_size,
@@ -1500,8 +1425,6 @@ static void ttm_bo_init_sysman(struct ttm_bo_device *bdev)
* Other types need to be driver / IOCTL initialized.
*/
man->use_tt = true;
- man->available_caching = TTM_PL_MASK_CACHING;
- man->default_caching = TTM_PL_FLAG_CACHED;
ttm_resource_manager_init(man, 0);
ttm_set_driver_manager(bdev, TTM_PL_SYSTEM, man);
@@ -1545,25 +1468,13 @@ EXPORT_SYMBOL(ttm_bo_device_init);
* buffer object vm functions.
*/
-void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo)
-{
- struct ttm_bo_device *bdev = bo->bdev;
-
- drm_vma_node_unmap(&bo->base.vma_node, bdev->dev_mapping);
- ttm_mem_io_free_vm(bo);
-}
-
void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo)
{
struct ttm_bo_device *bdev = bo->bdev;
- struct ttm_resource_manager *man = ttm_manager_type(bdev, bo->mem.mem_type);
- ttm_mem_io_lock(man, false);
- ttm_bo_unmap_virtual_locked(bo);
- ttm_mem_io_unlock(man);
+ drm_vma_node_unmap(&bo->base.vma_node, bdev->dev_mapping);
+ ttm_mem_io_free(bdev, &bo->mem);
}
-
-
EXPORT_SYMBOL(ttm_bo_unmap_virtual);
int ttm_bo_wait(struct ttm_buffer_object *bo,
@@ -1647,7 +1558,7 @@ int ttm_bo_swapout(struct ttm_bo_global *glob, struct ttm_operation_ctx *ctx)
evict_mem = bo->mem;
evict_mem.mm_node = NULL;
- evict_mem.placement = TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED;
+ evict_mem.placement = TTM_PL_FLAG_CACHED;
evict_mem.mem_type = TTM_PL_SYSTEM;
ret = ttm_bo_handle_move_mem(bo, &evict_mem, true, &ctx);
@@ -1673,7 +1584,7 @@ int ttm_bo_swapout(struct ttm_bo_global *glob, struct ttm_operation_ctx *ctx)
if (bo->bdev->driver->swap_notify)
bo->bdev->driver->swap_notify(bo);
- ret = ttm_tt_swapout(bo->ttm, bo->persistent_swap_storage);
+ ret = ttm_tt_swapout(bo->bdev, bo->ttm, bo->persistent_swap_storage);
out:
/**
@@ -1698,3 +1609,22 @@ void ttm_bo_swapout_all(void)
while (ttm_bo_swapout(&ttm_bo_glob, &ctx) == 0);
}
EXPORT_SYMBOL(ttm_bo_swapout_all);
+
+void ttm_bo_tt_destroy(struct ttm_buffer_object *bo)
+{
+ if (bo->ttm == NULL)
+ return;
+
+ ttm_tt_destroy(bo->bdev, bo->ttm);
+ bo->ttm = NULL;
+}
+
+int ttm_bo_tt_bind(struct ttm_buffer_object *bo, struct ttm_resource *mem)
+{
+ return bo->bdev->driver->ttm_tt_bind(bo->bdev, bo->ttm, mem);
+}
+
+void ttm_bo_tt_unbind(struct ttm_buffer_object *bo)
+{
+ bo->bdev->driver->ttm_tt_unbind(bo->bdev, bo->ttm);
+}
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index ee04716b2603..fb2a25f8408f 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -67,10 +67,8 @@ int ttm_bo_move_ttm(struct ttm_buffer_object *bo,
return ret;
}
- ttm_tt_unbind(ttm);
+ ttm_bo_tt_unbind(bo);
ttm_bo_free_old_node(bo);
- ttm_flag_masked(&old_mem->placement, TTM_PL_FLAG_SYSTEM,
- TTM_PL_MASK_MEM);
old_mem->mem_type = TTM_PL_SYSTEM;
}
@@ -79,134 +77,56 @@ int ttm_bo_move_ttm(struct ttm_buffer_object *bo,
return ret;
if (new_mem->mem_type != TTM_PL_SYSTEM) {
- ret = ttm_tt_bind(ttm, new_mem, ctx);
+
+ ret = ttm_tt_populate(bo->bdev, ttm, ctx);
if (unlikely(ret != 0))
return ret;
- }
- *old_mem = *new_mem;
- new_mem->mm_node = NULL;
+ ret = ttm_bo_tt_bind(bo, new_mem);
+ if (unlikely(ret != 0))
+ return ret;
+ }
+ ttm_bo_assign_mem(bo, new_mem);
return 0;
}
EXPORT_SYMBOL(ttm_bo_move_ttm);
-int ttm_mem_io_lock(struct ttm_resource_manager *man, bool interruptible)
-{
- if (likely(!man->use_io_reserve_lru))
- return 0;
-
- if (interruptible)
- return mutex_lock_interruptible(&man->io_reserve_mutex);
-
- mutex_lock(&man->io_reserve_mutex);
- return 0;
-}
-
-void ttm_mem_io_unlock(struct ttm_resource_manager *man)
-{
- if (likely(!man->use_io_reserve_lru))
- return;
-
- mutex_unlock(&man->io_reserve_mutex);
-}
-
-static int ttm_mem_io_evict(struct ttm_resource_manager *man)
-{
- struct ttm_buffer_object *bo;
-
- bo = list_first_entry_or_null(&man->io_reserve_lru,
- struct ttm_buffer_object,
- io_reserve_lru);
- if (!bo)
- return -ENOSPC;
-
- list_del_init(&bo->io_reserve_lru);
- ttm_bo_unmap_virtual_locked(bo);
- return 0;
-}
-
int ttm_mem_io_reserve(struct ttm_bo_device *bdev,
struct ttm_resource *mem)
{
- struct ttm_resource_manager *man = ttm_manager_type(bdev, mem->mem_type);
- int ret;
-
- if (mem->bus.io_reserved_count++)
+ if (mem->bus.offset || mem->bus.addr)
return 0;
+ mem->bus.is_iomem = false;
if (!bdev->driver->io_mem_reserve)
return 0;
- mem->bus.addr = NULL;
- mem->bus.offset = 0;
- mem->bus.base = 0;
- mem->bus.is_iomem = false;
-retry:
- ret = bdev->driver->io_mem_reserve(bdev, mem);
- if (ret == -ENOSPC) {
- ret = ttm_mem_io_evict(man);
- if (ret == 0)
- goto retry;
- }
- return ret;
+ return bdev->driver->io_mem_reserve(bdev, mem);
}
void ttm_mem_io_free(struct ttm_bo_device *bdev,
struct ttm_resource *mem)
{
- if (--mem->bus.io_reserved_count)
+ if (!mem->bus.offset && !mem->bus.addr)
return;
- if (!bdev->driver->io_mem_free)
- return;
-
- bdev->driver->io_mem_free(bdev, mem);
-}
-
-int ttm_mem_io_reserve_vm(struct ttm_buffer_object *bo)
-{
- struct ttm_resource_manager *man = ttm_manager_type(bo->bdev, bo->mem.mem_type);
- struct ttm_resource *mem = &bo->mem;
- int ret;
-
- if (mem->bus.io_reserved_vm)
- return 0;
-
- ret = ttm_mem_io_reserve(bo->bdev, mem);
- if (unlikely(ret != 0))
- return ret;
- mem->bus.io_reserved_vm = true;
- if (man->use_io_reserve_lru)
- list_add_tail(&bo->io_reserve_lru,
- &man->io_reserve_lru);
- return 0;
-}
+ if (bdev->driver->io_mem_free)
+ bdev->driver->io_mem_free(bdev, mem);
-void ttm_mem_io_free_vm(struct ttm_buffer_object *bo)
-{
- struct ttm_resource *mem = &bo->mem;
-
- if (!mem->bus.io_reserved_vm)
- return;
-
- mem->bus.io_reserved_vm = false;
- list_del_init(&bo->io_reserve_lru);
- ttm_mem_io_free(bo->bdev, mem);
+ mem->bus.offset = 0;
+ mem->bus.addr = NULL;
}
static int ttm_resource_ioremap(struct ttm_bo_device *bdev,
struct ttm_resource *mem,
void **virtual)
{
- struct ttm_resource_manager *man = ttm_manager_type(bdev, mem->mem_type);
int ret;
void *addr;
*virtual = NULL;
- (void) ttm_mem_io_lock(man, false);
ret = ttm_mem_io_reserve(bdev, mem);
- ttm_mem_io_unlock(man);
if (ret || !mem->bus.is_iomem)
return ret;
@@ -216,15 +136,11 @@ static int ttm_resource_ioremap(struct ttm_bo_device *bdev,
size_t bus_size = (size_t)mem->num_pages << PAGE_SHIFT;
if (mem->placement & TTM_PL_FLAG_WC)
- addr = ioremap_wc(mem->bus.base + mem->bus.offset,
- bus_size);
+ addr = ioremap_wc(mem->bus.offset, bus_size);
else
- addr = ioremap(mem->bus.base + mem->bus.offset,
- bus_size);
+ addr = ioremap(mem->bus.offset, bus_size);
if (!addr) {
- (void) ttm_mem_io_lock(man, false);
ttm_mem_io_free(bdev, mem);
- ttm_mem_io_unlock(man);
return -ENOMEM;
}
}
@@ -236,15 +152,9 @@ static void ttm_resource_iounmap(struct ttm_bo_device *bdev,
struct ttm_resource *mem,
void *virtual)
{
- struct ttm_resource_manager *man;
-
- man = ttm_manager_type(bdev, mem->mem_type);
-
if (virtual && mem->bus.addr == NULL)
iounmap(virtual);
- (void) ttm_mem_io_lock(man, false);
ttm_mem_io_free(bdev, mem);
- ttm_mem_io_unlock(man);
}
static int ttm_copy_io_page(void *dst, void *src, unsigned long page)
@@ -342,7 +252,7 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo,
* Don't move nonexistent data. Clear destination instead.
*/
if (old_iomap == NULL &&
- (ttm == NULL || (ttm->state == tt_unpopulated &&
+ (ttm == NULL || (!ttm_tt_is_populated(ttm) &&
!(ttm->page_flags & TTM_PAGE_FLAG_SWAPPED)))) {
memset_io(new_iomap, 0, new_mem->num_pages*PAGE_SIZE);
goto out2;
@@ -352,7 +262,7 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo,
* TTM might be null for moves within the same region.
*/
if (ttm) {
- ret = ttm_tt_populate(ttm, ctx);
+ ret = ttm_tt_populate(bdev, ttm, ctx);
if (ret)
goto out1;
}
@@ -387,13 +297,11 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo,
mb();
out2:
old_copy = *old_mem;
- *old_mem = *new_mem;
- new_mem->mm_node = NULL;
- if (!man->use_tt) {
- ttm_tt_destroy(ttm);
- bo->ttm = NULL;
- }
+ ttm_bo_assign_mem(bo, new_mem);
+
+ if (!man->use_tt)
+ ttm_bo_tt_destroy(bo);
out1:
ttm_resource_iounmap(bdev, old_mem, new_iomap);
@@ -458,7 +366,6 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
INIT_LIST_HEAD(&fbo->base.ddestroy);
INIT_LIST_HEAD(&fbo->base.lru);
INIT_LIST_HEAD(&fbo->base.swap);
- INIT_LIST_HEAD(&fbo->base.io_reserve_lru);
fbo->base.moving = NULL;
drm_vma_node_reset(&fbo->base.base.vma_node);
@@ -516,12 +423,10 @@ static int ttm_bo_ioremap(struct ttm_buffer_object *bo,
} else {
map->bo_kmap_type = ttm_bo_map_iomap;
if (mem->placement & TTM_PL_FLAG_WC)
- map->virtual = ioremap_wc(bo->mem.bus.base +
- bo->mem.bus.offset + offset,
+ map->virtual = ioremap_wc(bo->mem.bus.offset + offset,
size);
else
- map->virtual = ioremap(bo->mem.bus.base +
- bo->mem.bus.offset + offset,
+ map->virtual = ioremap(bo->mem.bus.offset + offset,
size);
}
return (!map->virtual) ? -ENOMEM : 0;
@@ -543,7 +448,7 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo,
BUG_ON(!ttm);
- ret = ttm_tt_populate(ttm, &ctx);
+ ret = ttm_tt_populate(bo->bdev, ttm, &ctx);
if (ret)
return ret;
@@ -573,8 +478,6 @@ int ttm_bo_kmap(struct ttm_buffer_object *bo,
unsigned long start_page, unsigned long num_pages,
struct ttm_bo_kmap_obj *map)
{
- struct ttm_resource_manager *man =
- ttm_manager_type(bo->bdev, bo->mem.mem_type);
unsigned long offset, size;
int ret;
@@ -585,9 +488,7 @@ int ttm_bo_kmap(struct ttm_buffer_object *bo,
if (start_page > bo->num_pages)
return -EINVAL;
- (void) ttm_mem_io_lock(man, false);
ret = ttm_mem_io_reserve(bo->bdev, &bo->mem);
- ttm_mem_io_unlock(man);
if (ret)
return ret;
if (!bo->mem.bus.is_iomem) {
@@ -602,10 +503,6 @@ EXPORT_SYMBOL(ttm_bo_kmap);
void ttm_bo_kunmap(struct ttm_bo_kmap_obj *map)
{
- struct ttm_buffer_object *bo = map->bo;
- struct ttm_resource_manager *man =
- ttm_manager_type(bo->bdev, bo->mem.mem_type);
-
if (!map->virtual)
return;
switch (map->bo_kmap_type) {
@@ -623,167 +520,116 @@ void ttm_bo_kunmap(struct ttm_bo_kmap_obj *map)
default:
BUG();
}
- (void) ttm_mem_io_lock(man, false);
ttm_mem_io_free(map->bo->bdev, &map->bo->mem);
- ttm_mem_io_unlock(man);
map->virtual = NULL;
map->page = NULL;
}
EXPORT_SYMBOL(ttm_bo_kunmap);
-int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,
- struct dma_fence *fence,
- bool evict,
- struct ttm_resource *new_mem)
+static int ttm_bo_wait_free_node(struct ttm_buffer_object *bo,
+ bool dst_use_tt)
{
- struct ttm_bo_device *bdev = bo->bdev;
- struct ttm_resource_manager *man = ttm_manager_type(bdev, new_mem->mem_type);
- struct ttm_resource *old_mem = &bo->mem;
int ret;
- struct ttm_buffer_object *ghost_obj;
-
- dma_resv_add_excl_fence(bo->base.resv, fence);
- if (evict) {
- ret = ttm_bo_wait(bo, false, false);
- if (ret)
- return ret;
-
- if (!man->use_tt) {
- ttm_tt_destroy(bo->ttm);
- bo->ttm = NULL;
- }
- ttm_bo_free_old_node(bo);
- } else {
- /**
- * This should help pipeline ordinary buffer moves.
- *
- * Hang old buffer memory on a new buffer object,
- * and leave it to be released when the GPU
- * 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);
-
- /**
- * If we're not moving to fixed memory, the TTM object
- * needs to stay alive. Otherwhise hang it on the ghost
- * bo to be unbound and destroyed.
- */
-
- if (man->use_tt)
- ghost_obj->ttm = NULL;
- else
- bo->ttm = NULL;
-
- dma_resv_unlock(&ghost_obj->base._resv);
- ttm_bo_put(ghost_obj);
- }
-
- *old_mem = *new_mem;
- new_mem->mm_node = NULL;
+ ret = ttm_bo_wait(bo, false, false);
+ if (ret)
+ return ret;
+ if (!dst_use_tt)
+ ttm_bo_tt_destroy(bo);
+ ttm_bo_free_old_node(bo);
return 0;
}
-EXPORT_SYMBOL(ttm_bo_move_accel_cleanup);
-int ttm_bo_pipeline_move(struct ttm_buffer_object *bo,
- struct dma_fence *fence, bool evict,
- struct ttm_resource *new_mem)
+static int ttm_bo_move_to_ghost(struct ttm_buffer_object *bo,
+ struct dma_fence *fence,
+ bool dst_use_tt)
{
- struct ttm_bo_device *bdev = bo->bdev;
- struct ttm_resource *old_mem = &bo->mem;
-
- struct ttm_resource_manager *from = ttm_manager_type(bdev, old_mem->mem_type);
- struct ttm_resource_manager *to = ttm_manager_type(bdev, new_mem->mem_type);
-
+ struct ttm_buffer_object *ghost_obj;
int ret;
- dma_resv_add_excl_fence(bo->base.resv, fence);
-
- if (!evict) {
- struct ttm_buffer_object *ghost_obj;
-
- /**
- * This should help pipeline ordinary buffer moves.
- *
- * Hang old buffer memory on a new buffer object,
- * and leave it to be released when the GPU
- * operation has completed.
- */
+ /**
+ * This should help pipeline ordinary buffer moves.
+ *
+ * Hang old buffer memory on a new buffer object,
+ * and leave it to be released when the GPU
+ * operation has completed.
+ */
- dma_fence_put(bo->moving);
- bo->moving = dma_fence_get(fence);
+ 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);
+ ret = ttm_buffer_object_transfer(bo, &ghost_obj);
+ if (ret)
+ return ret;
- /**
- * If we're not moving to fixed memory, the TTM object
- * needs to stay alive. Otherwhise hang it on the ghost
- * bo to be unbound and destroyed.
- */
+ dma_resv_add_excl_fence(&ghost_obj->base._resv, fence);
- if (to->use_tt)
- ghost_obj->ttm = NULL;
- else
- bo->ttm = NULL;
+ /**
+ * If we're not moving to fixed memory, the TTM object
+ * needs to stay alive. Otherwhise hang it on the ghost
+ * bo to be unbound and destroyed.
+ */
- dma_resv_unlock(&ghost_obj->base._resv);
- ttm_bo_put(ghost_obj);
+ if (dst_use_tt)
+ ghost_obj->ttm = NULL;
+ else
+ bo->ttm = NULL;
- } else if (!from->use_tt) {
+ dma_resv_unlock(&ghost_obj->base._resv);
+ ttm_bo_put(ghost_obj);
+ return 0;
+}
- /**
- * BO doesn't have a TTM we need to bind/unbind. Just remember
- * this eviction and free up the allocation
- */
+static void ttm_bo_move_pipeline_evict(struct ttm_buffer_object *bo,
+ struct dma_fence *fence)
+{
+ struct ttm_bo_device *bdev = bo->bdev;
+ struct ttm_resource_manager *from = ttm_manager_type(bdev, bo->mem.mem_type);
- spin_lock(&from->move_lock);
- if (!from->move || dma_fence_is_later(fence, from->move)) {
- dma_fence_put(from->move);
- from->move = dma_fence_get(fence);
- }
- spin_unlock(&from->move_lock);
+ /**
+ * BO doesn't have a TTM we need to bind/unbind. Just remember
+ * this eviction and free up the allocation
+ */
+ spin_lock(&from->move_lock);
+ if (!from->move || dma_fence_is_later(fence, from->move)) {
+ dma_fence_put(from->move);
+ from->move = dma_fence_get(fence);
+ }
+ spin_unlock(&from->move_lock);
- ttm_bo_free_old_node(bo);
+ ttm_bo_free_old_node(bo);
- dma_fence_put(bo->moving);
- bo->moving = dma_fence_get(fence);
+ dma_fence_put(bo->moving);
+ bo->moving = dma_fence_get(fence);
+}
- } else {
- /**
- * Last resort, wait for the move to be completed.
- *
- * Should never happen in pratice.
- */
+int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,
+ struct dma_fence *fence,
+ bool evict,
+ bool pipeline,
+ struct ttm_resource *new_mem)
+{
+ struct ttm_bo_device *bdev = bo->bdev;
+ struct ttm_resource_manager *from = ttm_manager_type(bdev, bo->mem.mem_type);
+ struct ttm_resource_manager *man = ttm_manager_type(bdev, new_mem->mem_type);
+ int ret = 0;
- ret = ttm_bo_wait(bo, false, false);
- if (ret)
- return ret;
+ dma_resv_add_excl_fence(bo->base.resv, fence);
+ if (!evict)
+ ret = ttm_bo_move_to_ghost(bo, fence, man->use_tt);
+ else if (!from->use_tt && pipeline)
+ ttm_bo_move_pipeline_evict(bo, fence);
+ else
+ ret = ttm_bo_wait_free_node(bo, man->use_tt);
- if (!to->use_tt) {
- ttm_tt_destroy(bo->ttm);
- bo->ttm = NULL;
- }
- ttm_bo_free_old_node(bo);
- }
+ if (ret)
+ return ret;
- *old_mem = *new_mem;
- new_mem->mm_node = NULL;
+ ttm_bo_assign_mem(bo, new_mem);
return 0;
}
-EXPORT_SYMBOL(ttm_bo_pipeline_move);
+EXPORT_SYMBOL(ttm_bo_move_accel_cleanup);
int ttm_bo_pipeline_gutting(struct ttm_buffer_object *bo)
{
diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
index 01693e8f24b7..98a006fc30a5 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
@@ -101,8 +101,7 @@ static unsigned long ttm_bo_io_mem_pfn(struct ttm_buffer_object *bo,
if (bdev->driver->io_mem_pfn)
return bdev->driver->io_mem_pfn(bo, page_offset);
- return ((bo->mem.bus.base + bo->mem.bus.offset) >> PAGE_SHIFT)
- + page_offset;
+ return (bo->mem.bus.offset >> PAGE_SHIFT) + page_offset;
}
/**
@@ -281,8 +280,6 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf,
pgoff_t i;
vm_fault_t ret = VM_FAULT_NOPAGE;
unsigned long address = vmf->address;
- struct ttm_resource_manager *man =
- ttm_manager_type(bdev, bo->mem.mem_type);
/*
* Refuse to fault imported pages. This should be handled
@@ -321,24 +318,17 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf,
if (unlikely(ret != 0))
return ret;
- err = ttm_mem_io_lock(man, true);
+ err = ttm_mem_io_reserve(bdev, &bo->mem);
if (unlikely(err != 0))
- return VM_FAULT_NOPAGE;
- err = ttm_mem_io_reserve_vm(bo);
- if (unlikely(err != 0)) {
- ret = VM_FAULT_SIGBUS;
- goto out_io_unlock;
- }
+ return VM_FAULT_SIGBUS;
page_offset = ((address - vma->vm_start) >> PAGE_SHIFT) +
vma->vm_pgoff - drm_vma_node_start(&bo->base.vma_node);
page_last = vma_pages(vma) + vma->vm_pgoff -
drm_vma_node_start(&bo->base.vma_node);
- if (unlikely(page_offset >= bo->num_pages)) {
- ret = VM_FAULT_SIGBUS;
- goto out_io_unlock;
- }
+ if (unlikely(page_offset >= bo->num_pages))
+ return VM_FAULT_SIGBUS;
prot = ttm_io_prot(bo->mem.placement, prot);
if (!bo->mem.bus.is_iomem) {
@@ -350,21 +340,17 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf,
};
ttm = bo->ttm;
- if (ttm_tt_populate(bo->ttm, &ctx)) {
- ret = VM_FAULT_OOM;
- goto out_io_unlock;
- }
+ if (ttm_tt_populate(bdev, bo->ttm, &ctx))
+ return VM_FAULT_OOM;
} else {
/* Iomem should not be marked encrypted */
prot = pgprot_decrypted(prot);
}
/* We don't prefault on huge faults. Yet. */
- if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && fault_page_size != 1) {
- ret = ttm_bo_vm_insert_huge(vmf, bo, page_offset,
- fault_page_size, prot);
- goto out_io_unlock;
- }
+ if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && fault_page_size != 1)
+ return ttm_bo_vm_insert_huge(vmf, bo, page_offset,
+ fault_page_size, prot);
/*
* Speculatively prefault a number of pages. Only error on
@@ -376,8 +362,7 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf,
} else {
page = ttm->pages[page_offset];
if (unlikely(!page && i == 0)) {
- ret = VM_FAULT_OOM;
- goto out_io_unlock;
+ return VM_FAULT_OOM;
} else if (unlikely(!page)) {
break;
}
@@ -404,7 +389,7 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf,
/* Never error on prefaulted PTEs */
if (unlikely((ret & VM_FAULT_ERROR))) {
if (i == 0)
- goto out_io_unlock;
+ return VM_FAULT_NOPAGE;
else
break;
}
@@ -413,9 +398,6 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf,
if (unlikely(++page_offset >= page_last))
break;
}
- ret = VM_FAULT_NOPAGE;
-out_io_unlock:
- ttm_mem_io_unlock(man);
return ret;
}
EXPORT_SYMBOL(ttm_bo_vm_fault_reserved);
diff --git a/drivers/gpu/drm/ttm/ttm_memory.c b/drivers/gpu/drm/ttm/ttm_memory.c
index acd63b70d814..89d50f38c0f2 100644
--- a/drivers/gpu/drm/ttm/ttm_memory.c
+++ b/drivers/gpu/drm/ttm/ttm_memory.c
@@ -259,7 +259,7 @@ static bool ttm_zones_above_swap_target(struct ttm_mem_global *glob,
return false;
}
-/**
+/*
* At this point we only support a single shrink callback.
* Extend this if needed, perhaps using a linked list of callbacks.
* Note that this function is reentrant:
@@ -554,7 +554,6 @@ ttm_check_under_lowerlimit(struct ttm_mem_global *glob,
return false;
}
-EXPORT_SYMBOL(ttm_check_under_lowerlimit);
static int ttm_mem_global_reserve(struct ttm_mem_global *glob,
struct ttm_mem_zone *single_zone,
@@ -682,9 +681,3 @@ size_t ttm_round_pot(size_t size)
return 0;
}
EXPORT_SYMBOL(ttm_round_pot);
-
-uint64_t ttm_get_kernel_zone_memory_size(struct ttm_mem_global *glob)
-{
- return glob->zone_kernel->max_mem;
-}
-EXPORT_SYMBOL(ttm_get_kernel_zone_memory_size);
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c
index b40a4678c296..14660f723f71 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c
@@ -1044,7 +1044,7 @@ ttm_pool_unpopulate_helper(struct ttm_tt *ttm, unsigned mem_count_update)
put_pages:
ttm_put_pages(ttm->pages, ttm->num_pages, ttm->page_flags,
ttm->caching_state);
- ttm->state = tt_unpopulated;
+ ttm_tt_set_unpopulated(ttm);
}
int ttm_pool_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)
@@ -1053,7 +1053,7 @@ int ttm_pool_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)
unsigned i;
int ret;
- if (ttm->state != tt_unpopulated)
+ if (ttm_tt_is_populated(ttm))
return 0;
if (ttm_check_under_lowerlimit(mem_glob, ttm->num_pages, ctx))
@@ -1083,7 +1083,7 @@ int ttm_pool_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)
}
}
- ttm->state = tt_unbound;
+ ttm_tt_set_populated(ttm);
return 0;
}
EXPORT_SYMBOL(ttm_pool_populate);
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
index faefaaef7909..5e2df11685e7 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
@@ -894,7 +894,7 @@ int ttm_dma_populate(struct ttm_dma_tt *ttm_dma, struct device *dev,
unsigned i;
int ret;
- if (ttm->state != tt_unpopulated)
+ if (ttm_tt_is_populated(ttm))
return 0;
if (ttm_check_under_lowerlimit(mem_glob, num_pages, ctx))
@@ -982,7 +982,7 @@ skip_huge:
}
}
- ttm->state = tt_unbound;
+ ttm_tt_set_populated(ttm);
return 0;
}
EXPORT_SYMBOL_GPL(ttm_dma_populate);
@@ -1076,7 +1076,7 @@ void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, struct device *dev)
/* shrink pool if necessary (only on !is_cached pools)*/
if (npages)
ttm_dma_page_pool_free(pool, npages, false);
- ttm->state = tt_unpopulated;
+ ttm_tt_set_unpopulated(ttm);
}
EXPORT_SYMBOL_GPL(ttm_dma_unpopulate);
diff --git a/drivers/gpu/drm/ttm/ttm_range_manager.c b/drivers/gpu/drm/ttm/ttm_range_manager.c
index 770c8988c139..1da0e277c511 100644
--- a/drivers/gpu/drm/ttm/ttm_range_manager.c
+++ b/drivers/gpu/drm/ttm/ttm_range_manager.c
@@ -113,10 +113,7 @@ static void ttm_range_man_free(struct ttm_resource_manager *man,
static const struct ttm_resource_manager_func ttm_range_manager_func;
int ttm_range_man_init(struct ttm_bo_device *bdev,
- unsigned type,
- uint32_t available_caching,
- uint32_t default_caching,
- bool use_tt,
+ unsigned type, bool use_tt,
unsigned long p_size)
{
struct ttm_resource_manager *man;
@@ -127,8 +124,6 @@ int ttm_range_man_init(struct ttm_bo_device *bdev,
return -ENOMEM;
man = &rman->manager;
- man->available_caching = available_caching;
- man->default_caching = default_caching;
man->use_tt = use_tt;
man->func = &ttm_range_manager_func;
diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c
index 33b642532e5c..b325b9264203 100644
--- a/drivers/gpu/drm/ttm/ttm_resource.c
+++ b/drivers/gpu/drm/ttm/ttm_resource.c
@@ -65,10 +65,7 @@ void ttm_resource_manager_init(struct ttm_resource_manager *man,
{
unsigned i;
- man->use_io_reserve_lru = false;
- mutex_init(&man->io_reserve_mutex);
spin_lock_init(&man->move_lock);
- INIT_LIST_HEAD(&man->io_reserve_lru);
man->size = p_size;
for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i)
@@ -143,8 +140,6 @@ void ttm_resource_manager_debug(struct ttm_resource_manager *man,
drm_printf(p, " use_type: %d\n", man->use_type);
drm_printf(p, " use_tt: %d\n", man->use_tt);
drm_printf(p, " size: %llu\n", man->size);
- drm_printf(p, " available_caching: 0x%08X\n", man->available_caching);
- drm_printf(p, " default_caching: 0x%08X\n", man->default_caching);
if (man->func && man->func->debug)
(*man->func->debug)(man, p);
}
diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c
index 1ccf1ef050d6..f43fa69a1e65 100644
--- a/drivers/gpu/drm/ttm/ttm_tt.c
+++ b/drivers/gpu/drm/ttm/ttm_tt.c
@@ -156,7 +156,7 @@ static int ttm_tt_set_caching(struct ttm_tt *ttm,
if (ttm->caching_state == c_state)
return 0;
- if (ttm->state == tt_unpopulated) {
+ if (!ttm_tt_is_populated(ttm)) {
/* Change caching but don't populate */
ttm->caching_state = c_state;
return 0;
@@ -207,33 +207,31 @@ int ttm_tt_set_placement_caching(struct ttm_tt *ttm, uint32_t placement)
}
EXPORT_SYMBOL(ttm_tt_set_placement_caching);
-void ttm_tt_destroy(struct ttm_tt *ttm)
+void ttm_tt_destroy_common(struct ttm_bo_device *bdev, struct ttm_tt *ttm)
{
- if (ttm == NULL)
- return;
-
- ttm_tt_unbind(ttm);
-
- if (ttm->state == tt_unbound)
- ttm_tt_unpopulate(ttm);
+ ttm_tt_unpopulate(bdev, ttm);
if (!(ttm->page_flags & TTM_PAGE_FLAG_PERSISTENT_SWAP) &&
ttm->swap_storage)
fput(ttm->swap_storage);
ttm->swap_storage = NULL;
- ttm->func->destroy(ttm);
+}
+EXPORT_SYMBOL(ttm_tt_destroy_common);
+
+void ttm_tt_destroy(struct ttm_bo_device *bdev, struct ttm_tt *ttm)
+{
+ bdev->driver->ttm_tt_destroy(bdev, ttm);
}
static void ttm_tt_init_fields(struct ttm_tt *ttm,
struct ttm_buffer_object *bo,
uint32_t page_flags)
{
- ttm->bdev = bo->bdev;
ttm->num_pages = bo->num_pages;
ttm->caching_state = tt_cached;
ttm->page_flags = page_flags;
- ttm->state = tt_unpopulated;
+ ttm_tt_set_unpopulated(ttm);
ttm->swap_storage = NULL;
ttm->sg = bo->sg;
}
@@ -308,39 +306,6 @@ void ttm_dma_tt_fini(struct ttm_dma_tt *ttm_dma)
}
EXPORT_SYMBOL(ttm_dma_tt_fini);
-void ttm_tt_unbind(struct ttm_tt *ttm)
-{
- if (ttm->state == tt_bound) {
- ttm->func->unbind(ttm);
- ttm->state = tt_unbound;
- }
-}
-
-int ttm_tt_bind(struct ttm_tt *ttm, struct ttm_resource *bo_mem,
- struct ttm_operation_ctx *ctx)
-{
- int ret = 0;
-
- if (!ttm)
- return -EINVAL;
-
- if (ttm->state == tt_bound)
- return 0;
-
- ret = ttm_tt_populate(ttm, ctx);
- if (ret)
- return ret;
-
- ret = ttm->func->bind(ttm, bo_mem);
- if (unlikely(ret != 0))
- return ret;
-
- ttm->state = tt_bound;
-
- return 0;
-}
-EXPORT_SYMBOL(ttm_tt_bind);
-
int ttm_tt_swapin(struct ttm_tt *ttm)
{
struct address_space *swap_space;
@@ -383,7 +348,8 @@ out_err:
return ret;
}
-int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage)
+int ttm_tt_swapout(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm, struct file *persistent_swap_storage)
{
struct address_space *swap_space;
struct file *swap_storage;
@@ -392,7 +358,6 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage)
int i;
int ret = -ENOMEM;
- BUG_ON(ttm->state != tt_unbound && ttm->state != tt_unpopulated);
BUG_ON(ttm->caching_state != tt_cached);
if (!persistent_swap_storage) {
@@ -429,7 +394,7 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage)
put_page(to_page);
}
- ttm_tt_unpopulate(ttm);
+ ttm_tt_unpopulate(bdev, ttm);
ttm->swap_storage = swap_storage;
ttm->page_flags |= TTM_PAGE_FLAG_SWAPPED;
if (persistent_swap_storage)
@@ -443,7 +408,7 @@ out_err:
return ret;
}
-static void ttm_tt_add_mapping(struct ttm_tt *ttm)
+static void ttm_tt_add_mapping(struct ttm_bo_device *bdev, struct ttm_tt *ttm)
{
pgoff_t i;
@@ -451,24 +416,29 @@ static void ttm_tt_add_mapping(struct ttm_tt *ttm)
return;
for (i = 0; i < ttm->num_pages; ++i)
- ttm->pages[i]->mapping = ttm->bdev->dev_mapping;
+ ttm->pages[i]->mapping = bdev->dev_mapping;
}
-int ttm_tt_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)
+int ttm_tt_populate(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)
{
int ret;
- if (ttm->state != tt_unpopulated)
+ if (!ttm)
+ return -EINVAL;
+
+ if (ttm_tt_is_populated(ttm))
return 0;
- if (ttm->bdev->driver->ttm_tt_populate)
- ret = ttm->bdev->driver->ttm_tt_populate(ttm, ctx);
+ if (bdev->driver->ttm_tt_populate)
+ ret = bdev->driver->ttm_tt_populate(bdev, ttm, ctx);
else
ret = ttm_pool_populate(ttm, ctx);
if (!ret)
- ttm_tt_add_mapping(ttm);
+ ttm_tt_add_mapping(bdev, ttm);
return ret;
}
+EXPORT_SYMBOL(ttm_tt_populate);
static void ttm_tt_clear_mapping(struct ttm_tt *ttm)
{
@@ -484,14 +454,15 @@ static void ttm_tt_clear_mapping(struct ttm_tt *ttm)
}
}
-void ttm_tt_unpopulate(struct ttm_tt *ttm)
+void ttm_tt_unpopulate(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm)
{
- if (ttm->state == tt_unpopulated)
+ if (!ttm_tt_is_populated(ttm))
return;
ttm_tt_clear_mapping(ttm);
- if (ttm->bdev->driver->ttm_tt_unpopulate)
- ttm->bdev->driver->ttm_tt_unpopulate(ttm);
+ if (bdev->driver->ttm_tt_unpopulate)
+ bdev->driver->ttm_tt_unpopulate(bdev, ttm);
else
ttm_pool_unpopulate(ttm);
}
diff --git a/drivers/gpu/drm/vboxvideo/vbox_mode.c b/drivers/gpu/drm/vboxvideo/vbox_mode.c
index d9a5af62af89..4fcc0a542b8a 100644
--- a/drivers/gpu/drm/vboxvideo/vbox_mode.c
+++ b/drivers/gpu/drm/vboxvideo/vbox_mode.c
@@ -397,11 +397,13 @@ static void vbox_cursor_atomic_update(struct drm_plane *plane,
vbox_crtc->cursor_enabled = true;
- /* pinning is done in prepare/cleanup framebuffer */
- src = drm_gem_vram_kmap(gbo, true, NULL);
+ src = drm_gem_vram_vmap(gbo);
if (IS_ERR(src)) {
+ /*
+ * BUG: we should have pinned the BO in prepare_fb().
+ */
mutex_unlock(&vbox->hw_mutex);
- DRM_WARN("Could not kmap cursor bo, skipping update\n");
+ DRM_WARN("Could not map cursor bo, skipping update\n");
return;
}
@@ -414,7 +416,7 @@ static void vbox_cursor_atomic_update(struct drm_plane *plane,
data_size = width * height * 4 + mask_size;
copy_cursor_image(src, vbox->cursor_data, width, height, mask_size);
- drm_gem_vram_kunmap(gbo);
+ drm_gem_vram_vunmap(gbo, src);
flags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE |
VBOX_MOUSE_POINTER_ALPHA;
diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile
index b303703bc7f3..d0163e18e9ca 100644
--- a/drivers/gpu/drm/vc4/Makefile
+++ b/drivers/gpu/drm/vc4/Makefile
@@ -12,6 +12,7 @@ vc4-y := \
vc4_kms.o \
vc4_gem.o \
vc4_hdmi.o \
+ vc4_hdmi_phy.o \
vc4_vec.o \
vc4_hvs.o \
vc4_irq.o \
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 6d8fa6118fc1..a393f93390a2 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -65,6 +65,20 @@ static const struct debugfs_reg32 crtc_regs[] = {
VC4_REG32(PV_HACT_ACT),
};
+static unsigned int
+vc4_crtc_get_cob_allocation(struct vc4_dev *vc4, unsigned int channel)
+{
+ u32 dispbase = HVS_READ(SCALER_DISPBASEX(channel));
+ /* Top/base are supposed to be 4-pixel aligned, but the
+ * Raspberry Pi firmware fills the low bits (which are
+ * presumably ignored).
+ */
+ u32 top = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_TOP) & ~3;
+ u32 base = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_BASE) & ~3;
+
+ return top - base + 4;
+}
+
static bool vc4_crtc_get_scanout_position(struct drm_crtc *crtc,
bool in_vblank_irq,
int *vpos, int *hpos,
@@ -74,6 +88,8 @@ static bool vc4_crtc_get_scanout_position(struct drm_crtc *crtc,
struct drm_device *dev = crtc->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state);
+ unsigned int cob_size;
u32 val;
int fifo_lines;
int vblank_lines;
@@ -89,7 +105,7 @@ static bool vc4_crtc_get_scanout_position(struct drm_crtc *crtc,
* Read vertical scanline which is currently composed for our
* pixelvalve by the HVS, and also the scaler status.
*/
- val = HVS_READ(SCALER_DISPSTATX(vc4_crtc->channel));
+ val = HVS_READ(SCALER_DISPSTATX(vc4_crtc_state->assigned_channel));
/* Get optional system timestamp after query. */
if (etime)
@@ -109,8 +125,9 @@ static bool vc4_crtc_get_scanout_position(struct drm_crtc *crtc,
*hpos += mode->crtc_htotal / 2;
}
+ cob_size = vc4_crtc_get_cob_allocation(vc4, vc4_crtc_state->assigned_channel);
/* This is the offset we need for translating hvs -> pv scanout pos. */
- fifo_lines = vc4_crtc->cob_size / mode->crtc_hdisplay;
+ fifo_lines = cob_size / mode->crtc_hdisplay;
if (fifo_lines > 0)
ret = true;
@@ -189,10 +206,22 @@ void vc4_crtc_destroy(struct drm_crtc *crtc)
drm_crtc_cleanup(crtc);
}
-static u32 vc4_get_fifo_full_level(u32 format)
+static u32 vc4_get_fifo_full_level(struct vc4_crtc *vc4_crtc, u32 format)
{
- static const u32 fifo_len_bytes = 64;
+ const struct vc4_crtc_data *crtc_data = vc4_crtc_to_vc4_crtc_data(vc4_crtc);
+ const struct vc4_pv_data *pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc);
+ u32 fifo_len_bytes = pv_data->fifo_depth;
+ /*
+ * Pixels are pulled from the HVS if the number of bytes is
+ * lower than the FIFO full level.
+ *
+ * The latency of the pixel fetch mechanism is 6 pixels, so we
+ * need to convert those 6 pixels in bytes, depending on the
+ * format, and then subtract that from the length of the FIFO
+ * to make sure we never end up in a situation where the FIFO
+ * is full.
+ */
switch (format) {
case PV_CONTROL_FORMAT_DSIV_16:
case PV_CONTROL_FORMAT_DSIC_16:
@@ -202,10 +231,30 @@ static u32 vc4_get_fifo_full_level(u32 format)
case PV_CONTROL_FORMAT_24:
case PV_CONTROL_FORMAT_DSIV_24:
default:
+ /*
+ * For some reason, the pixelvalve4 doesn't work with
+ * the usual formula and will only work with 32.
+ */
+ if (crtc_data->hvs_output == 5)
+ return 32;
+
return fifo_len_bytes - 3 * HVS_FIFO_LATENCY_PIX;
}
}
+static u32 vc4_crtc_get_fifo_full_level_bits(struct vc4_crtc *vc4_crtc,
+ u32 format)
+{
+ u32 level = vc4_get_fifo_full_level(vc4_crtc, format);
+ u32 ret = 0;
+
+ ret |= VC4_SET_FIELD((level >> 6),
+ PV5_CONTROL_FIFO_LEVEL_HIGH);
+
+ return ret | VC4_SET_FIELD(level & 0x3f,
+ PV_CONTROL_FIFO_LEVEL);
+}
+
/*
* Returns the encoder attached to the CRTC.
*
@@ -230,11 +279,23 @@ static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc)
return NULL;
}
+static void vc4_crtc_pixelvalve_reset(struct drm_crtc *crtc)
+{
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+
+ /* The PV needs to be disabled before it can be flushed */
+ CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) & ~PV_CONTROL_EN);
+ CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_FIFO_CLR);
+}
+
static void vc4_crtc_config_pv(struct drm_crtc *crtc)
{
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc);
struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ const struct vc4_pv_data *pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc);
struct drm_crtc_state *state = crtc->state;
struct drm_display_mode *mode = &state->adjusted_mode;
bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
@@ -242,24 +303,29 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc)
bool is_dsi = (vc4_encoder->type == VC4_ENCODER_TYPE_DSI0 ||
vc4_encoder->type == VC4_ENCODER_TYPE_DSI1);
u32 format = is_dsi ? PV_CONTROL_FORMAT_DSIV_24 : PV_CONTROL_FORMAT_24;
+ u8 ppc = pv_data->pixels_per_clock;
+ bool debug_dump_regs = false;
- /* Reset the PV fifo. */
- CRTC_WRITE(PV_CONTROL, 0);
- CRTC_WRITE(PV_CONTROL, PV_CONTROL_FIFO_CLR | PV_CONTROL_EN);
- CRTC_WRITE(PV_CONTROL, 0);
+ if (debug_dump_regs) {
+ struct drm_printer p = drm_info_printer(&vc4_crtc->pdev->dev);
+ dev_info(&vc4_crtc->pdev->dev, "CRTC %d regs before:\n",
+ drm_crtc_index(crtc));
+ drm_print_regset32(&p, &vc4_crtc->regset);
+ }
+
+ vc4_crtc_pixelvalve_reset(crtc);
CRTC_WRITE(PV_HORZA,
- VC4_SET_FIELD((mode->htotal -
- mode->hsync_end) * pixel_rep,
+ VC4_SET_FIELD((mode->htotal - mode->hsync_end) * pixel_rep / ppc,
PV_HORZA_HBP) |
- VC4_SET_FIELD((mode->hsync_end -
- mode->hsync_start) * pixel_rep,
+ VC4_SET_FIELD((mode->hsync_end - mode->hsync_start) * pixel_rep / ppc,
PV_HORZA_HSYNC));
+
CRTC_WRITE(PV_HORZB,
- VC4_SET_FIELD((mode->hsync_start -
- mode->hdisplay) * pixel_rep,
+ VC4_SET_FIELD((mode->hsync_start - mode->hdisplay) * pixel_rep / ppc,
PV_HORZB_HFP) |
- VC4_SET_FIELD(mode->hdisplay * pixel_rep, PV_HORZB_HACTIVE));
+ VC4_SET_FIELD(mode->hdisplay * pixel_rep / ppc,
+ PV_HORZB_HACTIVE));
CRTC_WRITE(PV_VERTA,
VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
@@ -306,35 +372,20 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc)
if (is_dsi)
CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep);
- CRTC_WRITE(PV_CONTROL,
+ if (vc4->hvs->hvs5)
+ CRTC_WRITE(PV_MUX_CFG,
+ VC4_SET_FIELD(PV_MUX_CFG_RGB_PIXEL_MUX_MODE_NO_SWAP,
+ PV_MUX_CFG_RGB_PIXEL_MUX_MODE));
+
+ CRTC_WRITE(PV_CONTROL, PV_CONTROL_FIFO_CLR |
+ vc4_crtc_get_fifo_full_level_bits(vc4_crtc, format) |
VC4_SET_FIELD(format, PV_CONTROL_FORMAT) |
- VC4_SET_FIELD(vc4_get_fifo_full_level(format),
- PV_CONTROL_FIFO_LEVEL) |
VC4_SET_FIELD(pixel_rep - 1, PV_CONTROL_PIXEL_REP) |
PV_CONTROL_CLR_AT_START |
PV_CONTROL_TRIGGER_UNDERFLOW |
PV_CONTROL_WAIT_HSTART |
VC4_SET_FIELD(vc4_encoder->clock_select,
- PV_CONTROL_CLK_SELECT) |
- PV_CONTROL_FIFO_CLR |
- PV_CONTROL_EN);
-}
-
-static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
-{
- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
- bool debug_dump_regs = false;
-
- if (debug_dump_regs) {
- struct drm_printer p = drm_info_printer(&vc4_crtc->pdev->dev);
- dev_info(&vc4_crtc->pdev->dev, "CRTC %d regs before:\n",
- drm_crtc_index(crtc));
- drm_print_regset32(&p, &vc4_crtc->regset);
- }
-
- vc4_crtc_config_pv(crtc);
-
- vc4_hvs_mode_set_nofb(crtc);
+ PV_CONTROL_CLK_SELECT));
if (debug_dump_regs) {
struct drm_printer p = drm_info_printer(&vc4_crtc->pdev->dev);
@@ -352,24 +403,86 @@ static void require_hvs_enabled(struct drm_device *dev)
SCALER_DISPCTRL_ENABLE);
}
+static int vc4_crtc_disable(struct drm_crtc *crtc, unsigned int channel)
+{
+ struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc);
+ struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ int ret;
+
+ CRTC_WRITE(PV_V_CONTROL,
+ CRTC_READ(PV_V_CONTROL) & ~PV_VCONTROL_VIDEN);
+ ret = wait_for(!(CRTC_READ(PV_V_CONTROL) & PV_VCONTROL_VIDEN), 1);
+ WARN_ONCE(ret, "Timeout waiting for !PV_VCONTROL_VIDEN\n");
+
+ /*
+ * This delay is needed to avoid to get a pixel stuck in an
+ * unflushable FIFO between the pixelvalve and the HDMI
+ * controllers on the BCM2711.
+ *
+ * Timing is fairly sensitive here, so mdelay is the safest
+ * approach.
+ *
+ * If it was to be reworked, the stuck pixel happens on a
+ * BCM2711 when changing mode with a good probability, so a
+ * script that changes mode on a regular basis should trigger
+ * the bug after less than 10 attempts. It manifests itself with
+ * every pixels being shifted by one to the right, and thus the
+ * last pixel of a line actually being displayed as the first
+ * pixel on the next line.
+ */
+ mdelay(20);
+
+ if (vc4_encoder && vc4_encoder->post_crtc_disable)
+ vc4_encoder->post_crtc_disable(encoder);
+
+ vc4_crtc_pixelvalve_reset(crtc);
+ vc4_hvs_stop_channel(dev, channel);
+
+ if (vc4_encoder && vc4_encoder->post_crtc_powerdown)
+ vc4_encoder->post_crtc_powerdown(encoder);
+
+ return 0;
+}
+
+int vc4_crtc_disable_at_boot(struct drm_crtc *crtc)
+{
+ struct drm_device *drm = crtc->dev;
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ int channel;
+
+ if (!(of_device_is_compatible(vc4_crtc->pdev->dev.of_node,
+ "brcm,bcm2711-pixelvalve2") ||
+ of_device_is_compatible(vc4_crtc->pdev->dev.of_node,
+ "brcm,bcm2711-pixelvalve4")))
+ return 0;
+
+ if (!(CRTC_READ(PV_CONTROL) & PV_CONTROL_EN))
+ return 0;
+
+ if (!(CRTC_READ(PV_V_CONTROL) & PV_VCONTROL_VIDEN))
+ return 0;
+
+ channel = vc4_hvs_get_fifo_from_output(drm, vc4_crtc->data->hvs_output);
+ if (channel < 0)
+ return 0;
+
+ return vc4_crtc_disable(crtc, channel);
+}
+
static void vc4_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
+ struct vc4_crtc_state *old_vc4_state = to_vc4_crtc_state(old_state);
struct drm_device *dev = crtc->dev;
- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
- int ret;
require_hvs_enabled(dev);
/* Disable vblank irq handling before crtc is disabled. */
drm_crtc_vblank_off(crtc);
- CRTC_WRITE(PV_V_CONTROL,
- CRTC_READ(PV_V_CONTROL) & ~PV_VCONTROL_VIDEN);
- ret = wait_for(!(CRTC_READ(PV_V_CONTROL) & PV_VCONTROL_VIDEN), 1);
- WARN_ONCE(ret, "Timeout waiting for !PV_VCONTROL_VIDEN\n");
-
- vc4_hvs_atomic_disable(crtc, old_state);
+ vc4_crtc_disable(crtc, old_vc4_state->assigned_channel);
/*
* Make sure we issue a vblank event after disabling the CRTC if
@@ -390,6 +503,8 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
{
struct drm_device *dev = crtc->dev;
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc);
+ struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
require_hvs_enabled(dev);
@@ -400,11 +515,24 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
vc4_hvs_atomic_enable(crtc, old_state);
+ if (vc4_encoder->pre_crtc_configure)
+ vc4_encoder->pre_crtc_configure(encoder);
+
+ vc4_crtc_config_pv(crtc);
+
+ CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_EN);
+
+ if (vc4_encoder->pre_crtc_enable)
+ vc4_encoder->pre_crtc_enable(encoder);
+
/* When feeding the transposer block the pixelvalve is unneeded and
* should not be enabled.
*/
CRTC_WRITE(PV_V_CONTROL,
CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN);
+
+ if (vc4_encoder->post_crtc_enable)
+ vc4_encoder->post_crtc_enable(encoder);
}
static enum drm_mode_status vc4_crtc_mode_valid(struct drm_crtc *crtc,
@@ -499,7 +627,7 @@ static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
struct drm_device *dev = crtc->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
- u32 chan = vc4_crtc->channel;
+ u32 chan = vc4_state->assigned_channel;
unsigned long flags;
spin_lock_irqsave(&dev->event_lock, flags);
@@ -516,7 +644,7 @@ static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
* the CRTC and encoder already reconfigured, leading to
* underruns. This can be seen when reconfiguring the CRTC.
*/
- vc4_hvs_unmask_underrun(dev, vc4_crtc->channel);
+ vc4_hvs_unmask_underrun(dev, chan);
}
spin_unlock_irqrestore(&dev->event_lock, flags);
}
@@ -698,6 +826,7 @@ struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc)
old_vc4_state = to_vc4_crtc_state(crtc->state);
vc4_state->feed_txp = old_vc4_state->feed_txp;
vc4_state->margins = old_vc4_state->margins;
+ vc4_state->assigned_channel = old_vc4_state->assigned_channel;
__drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base);
return &vc4_state->base;
@@ -747,7 +876,6 @@ static const struct drm_crtc_funcs vc4_crtc_funcs = {
};
static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
- .mode_set_nofb = vc4_crtc_mode_set_nofb,
.mode_valid = vc4_crtc_mode_valid,
.atomic_check = vc4_crtc_atomic_check,
.atomic_flush = vc4_hvs_atomic_flush,
@@ -758,9 +886,12 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
static const struct vc4_pv_data bcm2835_pv0_data = {
.base = {
- .hvs_channel = 0,
+ .hvs_available_channels = BIT(0),
+ .hvs_output = 0,
},
.debugfs_name = "crtc0_regs",
+ .fifo_depth = 64,
+ .pixels_per_clock = 1,
.encoder_types = {
[PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI0,
[PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_DPI,
@@ -769,9 +900,12 @@ static const struct vc4_pv_data bcm2835_pv0_data = {
static const struct vc4_pv_data bcm2835_pv1_data = {
.base = {
- .hvs_channel = 2,
+ .hvs_available_channels = BIT(2),
+ .hvs_output = 2,
},
.debugfs_name = "crtc1_regs",
+ .fifo_depth = 64,
+ .pixels_per_clock = 1,
.encoder_types = {
[PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI1,
[PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_SMI,
@@ -780,19 +914,94 @@ static const struct vc4_pv_data bcm2835_pv1_data = {
static const struct vc4_pv_data bcm2835_pv2_data = {
.base = {
- .hvs_channel = 1,
+ .hvs_available_channels = BIT(1),
+ .hvs_output = 1,
},
.debugfs_name = "crtc2_regs",
+ .fifo_depth = 64,
+ .pixels_per_clock = 1,
.encoder_types = {
- [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_HDMI,
+ [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_HDMI0,
[PV_CONTROL_CLK_SELECT_VEC] = VC4_ENCODER_TYPE_VEC,
},
};
+static const struct vc4_pv_data bcm2711_pv0_data = {
+ .base = {
+ .hvs_available_channels = BIT(0),
+ .hvs_output = 0,
+ },
+ .debugfs_name = "crtc0_regs",
+ .fifo_depth = 64,
+ .pixels_per_clock = 1,
+ .encoder_types = {
+ [0] = VC4_ENCODER_TYPE_DSI0,
+ [1] = VC4_ENCODER_TYPE_DPI,
+ },
+};
+
+static const struct vc4_pv_data bcm2711_pv1_data = {
+ .base = {
+ .hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
+ .hvs_output = 3,
+ },
+ .debugfs_name = "crtc1_regs",
+ .fifo_depth = 64,
+ .pixels_per_clock = 1,
+ .encoder_types = {
+ [0] = VC4_ENCODER_TYPE_DSI1,
+ [1] = VC4_ENCODER_TYPE_SMI,
+ },
+};
+
+static const struct vc4_pv_data bcm2711_pv2_data = {
+ .base = {
+ .hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
+ .hvs_output = 4,
+ },
+ .debugfs_name = "crtc2_regs",
+ .fifo_depth = 256,
+ .pixels_per_clock = 2,
+ .encoder_types = {
+ [0] = VC4_ENCODER_TYPE_HDMI0,
+ },
+};
+
+static const struct vc4_pv_data bcm2711_pv3_data = {
+ .base = {
+ .hvs_available_channels = BIT(1),
+ .hvs_output = 1,
+ },
+ .debugfs_name = "crtc3_regs",
+ .fifo_depth = 64,
+ .pixels_per_clock = 1,
+ .encoder_types = {
+ [0] = VC4_ENCODER_TYPE_VEC,
+ },
+};
+
+static const struct vc4_pv_data bcm2711_pv4_data = {
+ .base = {
+ .hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
+ .hvs_output = 5,
+ },
+ .debugfs_name = "crtc4_regs",
+ .fifo_depth = 64,
+ .pixels_per_clock = 2,
+ .encoder_types = {
+ [0] = VC4_ENCODER_TYPE_HDMI1,
+ },
+};
+
static const struct of_device_id vc4_crtc_dt_match[] = {
{ .compatible = "brcm,bcm2835-pixelvalve0", .data = &bcm2835_pv0_data },
{ .compatible = "brcm,bcm2835-pixelvalve1", .data = &bcm2835_pv1_data },
{ .compatible = "brcm,bcm2835-pixelvalve2", .data = &bcm2835_pv2_data },
+ { .compatible = "brcm,bcm2711-pixelvalve0", .data = &bcm2711_pv0_data },
+ { .compatible = "brcm,bcm2711-pixelvalve1", .data = &bcm2711_pv1_data },
+ { .compatible = "brcm,bcm2711-pixelvalve2", .data = &bcm2711_pv2_data },
+ { .compatible = "brcm,bcm2711-pixelvalve3", .data = &bcm2711_pv3_data },
+ { .compatible = "brcm,bcm2711-pixelvalve4", .data = &bcm2711_pv4_data },
{}
};
@@ -819,26 +1028,11 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm,
}
}
-static void
-vc4_crtc_get_cob_allocation(struct vc4_crtc *vc4_crtc)
-{
- struct drm_device *drm = vc4_crtc->base.dev;
- struct vc4_dev *vc4 = to_vc4_dev(drm);
- u32 dispbase = HVS_READ(SCALER_DISPBASEX(vc4_crtc->channel));
- /* Top/base are supposed to be 4-pixel aligned, but the
- * Raspberry Pi firmware fills the low bits (which are
- * presumably ignored).
- */
- u32 top = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_TOP) & ~3;
- u32 base = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_BASE) & ~3;
-
- vc4_crtc->cob_size = top - base + 4;
-}
-
int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
const struct drm_crtc_funcs *crtc_funcs,
const struct drm_crtc_helper_funcs *crtc_helper_funcs)
{
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
struct drm_crtc *crtc = &vc4_crtc->base;
struct drm_plane *primary_plane;
unsigned int i;
@@ -858,15 +1052,17 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
crtc_funcs, NULL);
drm_crtc_helper_add(crtc, crtc_helper_funcs);
- vc4_crtc->channel = vc4_crtc->data->hvs_channel;
- drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));
- drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size);
- /* We support CTM, but only for one CRTC at a time. It's therefore
- * implemented as private driver state in vc4_kms, not here.
- */
- drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size);
- vc4_crtc_get_cob_allocation(vc4_crtc);
+ if (!vc4->hvs->hvs5) {
+ drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));
+
+ drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size);
+
+ /* We support CTM, but only for one CRTC at a time. It's therefore
+ * implemented as private driver state in vc4_kms, not here.
+ */
+ drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size);
+ }
for (i = 0; i < crtc->gamma_size; i++) {
vc4_crtc->lut_r[i] = i;
@@ -915,7 +1111,9 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
CRTC_WRITE(PV_INTEN, 0);
CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
- vc4_crtc_irq_handler, 0, "vc4 crtc", vc4_crtc);
+ vc4_crtc_irq_handler,
+ IRQF_SHARED,
+ "vc4 crtc", vc4_crtc);
if (ret)
goto err_destroy_planes;
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index 38343d2fb4fb..f1a5fd5dab6f 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -252,6 +252,7 @@ static int vc4_drm_bind(struct device *dev)
struct drm_device *drm;
struct vc4_dev *vc4;
struct device_node *node;
+ struct drm_crtc *crtc;
int ret = 0;
dev->coherent_dma_mask = DMA_BIT_MASK(32);
@@ -298,6 +299,9 @@ static int vc4_drm_bind(struct device *dev)
if (ret < 0)
goto unbind_all;
+ drm_for_each_crtc(crtc, drm)
+ vc4_crtc_disable_at_boot(crtc);
+
ret = drm_dev_register(drm, 0);
if (ret < 0)
goto unbind_all;
@@ -368,6 +372,7 @@ static int vc4_platform_drm_remove(struct platform_device *pdev)
}
static const struct of_device_id vc4_of_match[] = {
+ { .compatible = "brcm,bcm2711-vc5", },
{ .compatible = "brcm,bcm2835-vc4", },
{ .compatible = "brcm,cygnus-vc4", },
{},
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index fa19160c801f..8c8d96b6289f 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -73,7 +73,6 @@ struct vc4_perfmon {
struct vc4_dev {
struct drm_device *dev;
- struct vc4_hdmi *hdmi;
struct vc4_hvs *hvs;
struct vc4_v3d *v3d;
struct vc4_dpi *dpi;
@@ -201,6 +200,9 @@ struct vc4_dev {
int power_refcount;
+ /* Set to true when the load tracker is supported. */
+ bool load_tracker_available;
+
/* Set to true when the load tracker is active. */
bool load_tracker_enabled;
@@ -320,6 +322,8 @@ struct vc4_hvs {
void __iomem *regs;
u32 __iomem *dlist;
+ struct clk *core_clk;
+
/* Memory manager for CRTCs to allocate space in the display
* list. Units are dwords.
*/
@@ -329,7 +333,11 @@ struct vc4_hvs {
spinlock_t mm_lock;
struct drm_mm_node mitchell_netravali_filter;
+
struct debugfs_regset32 regset;
+
+ /* HVS version 5 flag, therefore requires updated dlist structures */
+ bool hvs5;
};
struct vc4_plane {
@@ -420,7 +428,8 @@ to_vc4_plane_state(struct drm_plane_state *state)
enum vc4_encoder_type {
VC4_ENCODER_TYPE_NONE,
- VC4_ENCODER_TYPE_HDMI,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_DSI1,
@@ -432,6 +441,13 @@ struct vc4_encoder {
struct drm_encoder base;
enum vc4_encoder_type type;
u32 clock_select;
+
+ void (*pre_crtc_configure)(struct drm_encoder *encoder);
+ void (*pre_crtc_enable)(struct drm_encoder *encoder);
+ void (*post_crtc_enable)(struct drm_encoder *encoder);
+
+ void (*post_crtc_disable)(struct drm_encoder *encoder);
+ void (*post_crtc_powerdown)(struct drm_encoder *encoder);
};
static inline struct vc4_encoder *
@@ -441,13 +457,22 @@ to_vc4_encoder(struct drm_encoder *encoder)
}
struct vc4_crtc_data {
- /* Which channel of the HVS this pixelvalve sources from. */
- int hvs_channel;
+ /* Bitmask of channels (FIFOs) of the HVS that the output can source from */
+ unsigned int hvs_available_channels;
+
+ /* Which output of the HVS this pixelvalve sources from. */
+ int hvs_output;
};
struct vc4_pv_data {
struct vc4_crtc_data base;
+ /* Depth of the PixelValve FIFO in bytes */
+ unsigned int fifo_depth;
+
+ /* Number of pixels output per clock period */
+ u8 pixels_per_clock;
+
enum vc4_encoder_type encoder_types[4];
const char *debugfs_name;
@@ -462,14 +487,9 @@ struct vc4_crtc {
/* Timestamp at start of vblank irq - unaffected by lock delays. */
ktime_t t_vblank;
- /* Which HVS channel we're using for our CRTC. */
- int channel;
-
u8 lut_r[256];
u8 lut_g[256];
u8 lut_b[256];
- /* Size in pixels of the COB memory allocated to this CRTC. */
- u32 cob_size;
struct drm_pending_vblank_event *event;
@@ -502,6 +522,7 @@ struct vc4_crtc_state {
struct drm_mm_node mm;
bool feed_txp;
bool txp_armed;
+ unsigned int assigned_channel;
struct {
unsigned int left;
@@ -794,6 +815,7 @@ void vc4_bo_remove_from_purgeable_pool(struct vc4_bo *bo);
/* vc4_crtc.c */
extern struct platform_driver vc4_crtc_driver;
+int vc4_crtc_disable_at_boot(struct drm_crtc *crtc);
int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
const struct drm_crtc_funcs *crtc_funcs,
const struct drm_crtc_helper_funcs *crtc_helper_funcs);
@@ -888,11 +910,12 @@ void vc4_irq_reset(struct drm_device *dev);
/* vc4_hvs.c */
extern struct platform_driver vc4_hvs_driver;
+void vc4_hvs_stop_channel(struct drm_device *dev, unsigned int output);
+int vc4_hvs_get_fifo_from_output(struct drm_device *dev, unsigned int output);
int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state);
void vc4_hvs_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
void vc4_hvs_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
void vc4_hvs_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *state);
-void vc4_hvs_mode_set_nofb(struct drm_crtc *crtc);
void vc4_hvs_dump_state(struct drm_device *dev);
void vc4_hvs_unmask_underrun(struct drm_device *dev, int channel);
void vc4_hvs_mask_underrun(struct drm_device *dev, int channel);
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 15a11cd4de25..03825596a308 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -43,177 +43,101 @@
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/rational.h>
+#include <linux/reset.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_drm_eld.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "media/cec.h"
#include "vc4_drv.h"
+#include "vc4_hdmi.h"
+#include "vc4_hdmi_regs.h"
#include "vc4_regs.h"
-#define HSM_CLOCK_FREQ 163682864
-#define CEC_CLOCK_FREQ 40000
-#define CEC_CLOCK_DIV (HSM_CLOCK_FREQ / CEC_CLOCK_FREQ)
-
-/* HDMI audio information */
-struct vc4_hdmi_audio {
- struct snd_soc_card card;
- struct snd_soc_dai_link link;
- struct snd_soc_dai_link_component cpu;
- struct snd_soc_dai_link_component codec;
- struct snd_soc_dai_link_component platform;
- int samplerate;
- int channels;
- struct snd_dmaengine_dai_dma_data dma_data;
- struct snd_pcm_substream *substream;
-};
+#define VC5_HDMI_HORZA_HFP_SHIFT 16
+#define VC5_HDMI_HORZA_HFP_MASK VC4_MASK(28, 16)
+#define VC5_HDMI_HORZA_VPOS BIT(15)
+#define VC5_HDMI_HORZA_HPOS BIT(14)
+#define VC5_HDMI_HORZA_HAP_SHIFT 0
+#define VC5_HDMI_HORZA_HAP_MASK VC4_MASK(13, 0)
-/* General HDMI hardware state. */
-struct vc4_hdmi {
- struct platform_device *pdev;
-
- struct drm_encoder *encoder;
- struct drm_connector *connector;
+#define VC5_HDMI_HORZB_HBP_SHIFT 16
+#define VC5_HDMI_HORZB_HBP_MASK VC4_MASK(26, 16)
+#define VC5_HDMI_HORZB_HSP_SHIFT 0
+#define VC5_HDMI_HORZB_HSP_MASK VC4_MASK(10, 0)
- struct vc4_hdmi_audio audio;
+#define VC5_HDMI_VERTA_VSP_SHIFT 24
+#define VC5_HDMI_VERTA_VSP_MASK VC4_MASK(28, 24)
+#define VC5_HDMI_VERTA_VFP_SHIFT 16
+#define VC5_HDMI_VERTA_VFP_MASK VC4_MASK(22, 16)
+#define VC5_HDMI_VERTA_VAL_SHIFT 0
+#define VC5_HDMI_VERTA_VAL_MASK VC4_MASK(12, 0)
- struct i2c_adapter *ddc;
- void __iomem *hdmicore_regs;
- void __iomem *hd_regs;
- int hpd_gpio;
- bool hpd_active_low;
+#define VC5_HDMI_VERTB_VSPO_SHIFT 16
+#define VC5_HDMI_VERTB_VSPO_MASK VC4_MASK(29, 16)
- struct cec_adapter *cec_adap;
- struct cec_msg cec_rx_msg;
- bool cec_tx_ok;
- bool cec_irq_was_rx;
+# define VC4_HD_M_SW_RST BIT(2)
+# define VC4_HD_M_ENABLE BIT(0)
- struct clk *pixel_clock;
- struct clk *hsm_clock;
+#define CEC_CLOCK_FREQ 40000
+#define VC4_HSM_MID_CLOCK 149985000
- struct debugfs_regset32 hdmi_regset;
- struct debugfs_regset32 hd_regset;
-};
+static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
+{
+ struct drm_info_node *node = (struct drm_info_node *)m->private;
+ struct vc4_hdmi *vc4_hdmi = node->info_ent->data;
+ struct drm_printer p = drm_seq_file_printer(m);
-#define HDMI_READ(offset) readl(vc4->hdmi->hdmicore_regs + offset)
-#define HDMI_WRITE(offset, val) writel(val, vc4->hdmi->hdmicore_regs + offset)
-#define HD_READ(offset) readl(vc4->hdmi->hd_regs + offset)
-#define HD_WRITE(offset, val) writel(val, vc4->hdmi->hd_regs + offset)
+ drm_print_regset32(&p, &vc4_hdmi->hdmi_regset);
+ drm_print_regset32(&p, &vc4_hdmi->hd_regset);
-/* VC4 HDMI encoder KMS struct */
-struct vc4_hdmi_encoder {
- struct vc4_encoder base;
- bool hdmi_monitor;
- bool limited_rgb_range;
-};
+ return 0;
+}
-static inline struct vc4_hdmi_encoder *
-to_vc4_hdmi_encoder(struct drm_encoder *encoder)
+static void vc4_hdmi_reset(struct vc4_hdmi *vc4_hdmi)
{
- return container_of(encoder, struct vc4_hdmi_encoder, base.base);
-}
+ HDMI_WRITE(HDMI_M_CTL, VC4_HD_M_SW_RST);
+ udelay(1);
+ HDMI_WRITE(HDMI_M_CTL, 0);
-/* VC4 HDMI connector KMS struct */
-struct vc4_hdmi_connector {
- struct drm_connector base;
+ HDMI_WRITE(HDMI_M_CTL, VC4_HD_M_ENABLE);
- /* Since the connector is attached to just the one encoder,
- * this is the reference to it so we can do the best_encoder()
- * hook.
- */
- struct drm_encoder *encoder;
-};
+ HDMI_WRITE(HDMI_SW_RESET_CONTROL,
+ VC4_HDMI_SW_RESET_HDMI |
+ VC4_HDMI_SW_RESET_FORMAT_DETECT);
-static inline struct vc4_hdmi_connector *
-to_vc4_hdmi_connector(struct drm_connector *connector)
-{
- return container_of(connector, struct vc4_hdmi_connector, base);
+ HDMI_WRITE(HDMI_SW_RESET_CONTROL, 0);
}
-static const struct debugfs_reg32 hdmi_regs[] = {
- VC4_REG32(VC4_HDMI_CORE_REV),
- VC4_REG32(VC4_HDMI_SW_RESET_CONTROL),
- VC4_REG32(VC4_HDMI_HOTPLUG_INT),
- VC4_REG32(VC4_HDMI_HOTPLUG),
- VC4_REG32(VC4_HDMI_MAI_CHANNEL_MAP),
- VC4_REG32(VC4_HDMI_MAI_CONFIG),
- VC4_REG32(VC4_HDMI_MAI_FORMAT),
- VC4_REG32(VC4_HDMI_AUDIO_PACKET_CONFIG),
- VC4_REG32(VC4_HDMI_RAM_PACKET_CONFIG),
- VC4_REG32(VC4_HDMI_HORZA),
- VC4_REG32(VC4_HDMI_HORZB),
- VC4_REG32(VC4_HDMI_FIFO_CTL),
- VC4_REG32(VC4_HDMI_SCHEDULER_CONTROL),
- VC4_REG32(VC4_HDMI_VERTA0),
- VC4_REG32(VC4_HDMI_VERTA1),
- VC4_REG32(VC4_HDMI_VERTB0),
- VC4_REG32(VC4_HDMI_VERTB1),
- VC4_REG32(VC4_HDMI_TX_PHY_RESET_CTL),
- VC4_REG32(VC4_HDMI_TX_PHY_CTL0),
-
- VC4_REG32(VC4_HDMI_CEC_CNTRL_1),
- VC4_REG32(VC4_HDMI_CEC_CNTRL_2),
- VC4_REG32(VC4_HDMI_CEC_CNTRL_3),
- VC4_REG32(VC4_HDMI_CEC_CNTRL_4),
- VC4_REG32(VC4_HDMI_CEC_CNTRL_5),
- VC4_REG32(VC4_HDMI_CPU_STATUS),
- VC4_REG32(VC4_HDMI_CPU_MASK_STATUS),
-
- VC4_REG32(VC4_HDMI_CEC_RX_DATA_1),
- VC4_REG32(VC4_HDMI_CEC_RX_DATA_2),
- VC4_REG32(VC4_HDMI_CEC_RX_DATA_3),
- VC4_REG32(VC4_HDMI_CEC_RX_DATA_4),
- VC4_REG32(VC4_HDMI_CEC_TX_DATA_1),
- VC4_REG32(VC4_HDMI_CEC_TX_DATA_2),
- VC4_REG32(VC4_HDMI_CEC_TX_DATA_3),
- VC4_REG32(VC4_HDMI_CEC_TX_DATA_4),
-};
-
-static const struct debugfs_reg32 hd_regs[] = {
- VC4_REG32(VC4_HD_M_CTL),
- VC4_REG32(VC4_HD_MAI_CTL),
- VC4_REG32(VC4_HD_MAI_THR),
- VC4_REG32(VC4_HD_MAI_FMT),
- VC4_REG32(VC4_HD_MAI_SMP),
- VC4_REG32(VC4_HD_VID_CTL),
- VC4_REG32(VC4_HD_CSC_CTL),
- VC4_REG32(VC4_HD_FRAME_COUNT),
-};
-
-static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
+static void vc5_hdmi_reset(struct vc4_hdmi *vc4_hdmi)
{
- struct drm_info_node *node = (struct drm_info_node *)m->private;
- struct drm_device *dev = node->minor->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct vc4_hdmi *hdmi = vc4->hdmi;
- struct drm_printer p = drm_seq_file_printer(m);
+ reset_control_reset(vc4_hdmi->reset);
- drm_print_regset32(&p, &hdmi->hdmi_regset);
- drm_print_regset32(&p, &hdmi->hd_regset);
+ HDMI_WRITE(HDMI_DVP_CTL, 0);
- return 0;
+ HDMI_WRITE(HDMI_CLOCK_STOP,
+ HDMI_READ(HDMI_CLOCK_STOP) | VC4_DVP_HT_CLOCK_STOP_PIXEL);
}
static enum drm_connector_status
vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
{
- struct drm_device *dev = connector->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
- if (vc4->hdmi->hpd_gpio) {
- if (gpio_get_value_cansleep(vc4->hdmi->hpd_gpio) ^
- vc4->hdmi->hpd_active_low)
+ if (vc4_hdmi->hpd_gpio) {
+ if (gpio_get_value_cansleep(vc4_hdmi->hpd_gpio) ^
+ vc4_hdmi->hpd_active_low)
return connector_status_connected;
- cec_phys_addr_invalidate(vc4->hdmi->cec_adap);
+ cec_phys_addr_invalidate(vc4_hdmi->cec_adap);
return connector_status_disconnected;
}
- if (drm_probe_ddc(vc4->hdmi->ddc))
+ if (drm_probe_ddc(vc4_hdmi->ddc))
return connector_status_connected;
- if (HDMI_READ(VC4_HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED)
+ if (HDMI_READ(HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED)
return connector_status_connected;
- cec_phys_addr_invalidate(vc4->hdmi->cec_adap);
+ cec_phys_addr_invalidate(vc4_hdmi->cec_adap);
return connector_status_disconnected;
}
@@ -225,17 +149,13 @@ static void vc4_hdmi_connector_destroy(struct drm_connector *connector)
static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
{
- struct vc4_hdmi_connector *vc4_connector =
- to_vc4_hdmi_connector(connector);
- struct drm_encoder *encoder = vc4_connector->encoder;
- struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
- struct drm_device *dev = connector->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
+ struct vc4_hdmi_encoder *vc4_encoder = &vc4_hdmi->encoder;
int ret = 0;
struct edid *edid;
- edid = drm_get_edid(connector, vc4->hdmi->ddc);
- cec_s_phys_addr_from_edid(vc4->hdmi->cec_adap, edid);
+ edid = drm_get_edid(connector, vc4_hdmi->ddc);
+ cec_s_phys_addr_from_edid(vc4_hdmi->cec_adap, edid);
if (!edid)
return -ENODEV;
@@ -267,32 +187,23 @@ static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs =
.get_modes = vc4_hdmi_connector_get_modes,
};
-static struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
- struct drm_encoder *encoder,
- struct i2c_adapter *ddc)
+static int vc4_hdmi_connector_init(struct drm_device *dev,
+ struct vc4_hdmi *vc4_hdmi)
{
- struct drm_connector *connector;
- struct vc4_hdmi_connector *hdmi_connector;
+ struct drm_connector *connector = &vc4_hdmi->connector;
+ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
int ret;
- hdmi_connector = devm_kzalloc(dev->dev, sizeof(*hdmi_connector),
- GFP_KERNEL);
- if (!hdmi_connector)
- return ERR_PTR(-ENOMEM);
- connector = &hdmi_connector->base;
-
- hdmi_connector->encoder = encoder;
-
drm_connector_init_with_ddc(dev, connector,
&vc4_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA,
- ddc);
+ vc4_hdmi->ddc);
drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs);
/* Create and attach TV margin props to this connector. */
ret = drm_mode_create_tv_margin_properties(dev);
if (ret)
- return ERR_PTR(ret);
+ return ret;
drm_connector_attach_tv_margin_properties(connector);
@@ -304,35 +215,37 @@ static struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
drm_connector_attach_encoder(connector, encoder);
- return connector;
+ return 0;
}
static int vc4_hdmi_stop_packet(struct drm_encoder *encoder,
enum hdmi_infoframe_type type)
{
- struct drm_device *dev = encoder->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
u32 packet_id = type - 0x80;
- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
- HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id));
+ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
+ HDMI_READ(HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id));
- return wait_for(!(HDMI_READ(VC4_HDMI_RAM_PACKET_STATUS) &
+ return wait_for(!(HDMI_READ(HDMI_RAM_PACKET_STATUS) &
BIT(packet_id)), 100);
}
static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
union hdmi_infoframe *frame)
{
- struct drm_device *dev = encoder->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
u32 packet_id = frame->any.type - 0x80;
- u32 packet_reg = VC4_HDMI_RAM_PACKET(packet_id);
+ const struct vc4_hdmi_register *ram_packet_start =
+ &vc4_hdmi->variant->registers[HDMI_RAM_PACKET_START];
+ u32 packet_reg = ram_packet_start->offset + VC4_HDMI_PACKET_STRIDE * packet_id;
+ void __iomem *base = __vc4_hdmi_get_field_base(vc4_hdmi,
+ ram_packet_start->reg);
uint8_t buffer[VC4_HDMI_PACKET_STRIDE];
ssize_t len, i;
int ret;
- WARN_ONCE(!(HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) &
+ WARN_ONCE(!(HDMI_READ(HDMI_RAM_PACKET_CONFIG) &
VC4_HDMI_RAM_PACKET_ENABLE),
"Packet RAM has to be on to store the packet.");
@@ -347,23 +260,23 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
}
for (i = 0; i < len; i += 7) {
- HDMI_WRITE(packet_reg,
- buffer[i + 0] << 0 |
- buffer[i + 1] << 8 |
- buffer[i + 2] << 16);
+ writel(buffer[i + 0] << 0 |
+ buffer[i + 1] << 8 |
+ buffer[i + 2] << 16,
+ base + packet_reg);
packet_reg += 4;
- HDMI_WRITE(packet_reg,
- buffer[i + 3] << 0 |
- buffer[i + 4] << 8 |
- buffer[i + 5] << 16 |
- buffer[i + 6] << 24);
+ writel(buffer[i + 3] << 0 |
+ buffer[i + 4] << 8 |
+ buffer[i + 5] << 16 |
+ buffer[i + 6] << 24,
+ base + packet_reg);
packet_reg += 4;
}
- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
- HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) | BIT(packet_id));
- ret = wait_for((HDMI_READ(VC4_HDMI_RAM_PACKET_STATUS) &
+ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
+ HDMI_READ(HDMI_RAM_PACKET_CONFIG) | BIT(packet_id));
+ ret = wait_for((HDMI_READ(HDMI_RAM_PACKET_STATUS) &
BIT(packet_id)), 100);
if (ret)
DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret);
@@ -371,24 +284,24 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
{
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
- struct vc4_dev *vc4 = encoder->dev->dev_private;
- struct vc4_hdmi *hdmi = vc4->hdmi;
- struct drm_connector_state *cstate = hdmi->connector->state;
+ struct drm_connector *connector = &vc4_hdmi->connector;
+ struct drm_connector_state *cstate = connector->state;
struct drm_crtc *crtc = encoder->crtc;
const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
union hdmi_infoframe frame;
int ret;
ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
- hdmi->connector, mode);
+ connector, mode);
if (ret < 0) {
DRM_ERROR("couldn't fill AVI infoframe\n");
return;
}
drm_hdmi_avi_infoframe_quant_range(&frame.avi,
- hdmi->connector, mode,
+ connector, mode,
vc4_encoder->limited_rgb_range ?
HDMI_QUANTIZATION_RANGE_LIMITED :
HDMI_QUANTIZATION_RANGE_FULL);
@@ -416,9 +329,7 @@ static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder)
{
- struct drm_device *drm = encoder->dev;
- struct vc4_dev *vc4 = drm->dev_private;
- struct vc4_hdmi *hdmi = vc4->hdmi;
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
union hdmi_infoframe frame;
int ret;
@@ -427,45 +338,139 @@ static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder)
frame.audio.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
frame.audio.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
frame.audio.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
- frame.audio.channels = hdmi->audio.channels;
+ frame.audio.channels = vc4_hdmi->audio.channels;
vc4_hdmi_write_infoframe(encoder, &frame);
}
static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder)
{
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+
vc4_hdmi_set_avi_infoframe(encoder);
vc4_hdmi_set_spd_infoframe(encoder);
+ /*
+ * If audio was streaming, then we need to reenabled the audio
+ * infoframe here during encoder_enable.
+ */
+ if (vc4_hdmi->audio.streaming)
+ vc4_hdmi_set_audio_infoframe(encoder);
}
-static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
+static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder)
{
- struct drm_device *dev = encoder->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct vc4_hdmi *hdmi = vc4->hdmi;
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+
+ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0);
+
+ HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) |
+ VC4_HD_VID_CTL_CLRRGB | VC4_HD_VID_CTL_CLRSYNC);
+
+ HDMI_WRITE(HDMI_VID_CTL,
+ HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_BLANKPIX);
+}
+
+static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder)
+{
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
int ret;
- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
+ if (vc4_hdmi->variant->phy_disable)
+ vc4_hdmi->variant->phy_disable(vc4_hdmi);
- HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
- HD_WRITE(VC4_HD_VID_CTL,
- HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
+ HDMI_WRITE(HDMI_VID_CTL,
+ HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
- clk_disable_unprepare(hdmi->pixel_clock);
+ clk_disable_unprepare(vc4_hdmi->pixel_bvb_clock);
+ clk_disable_unprepare(vc4_hdmi->hsm_clock);
+ clk_disable_unprepare(vc4_hdmi->pixel_clock);
- ret = pm_runtime_put(&hdmi->pdev->dev);
+ ret = pm_runtime_put(&vc4_hdmi->pdev->dev);
if (ret < 0)
DRM_ERROR("Failed to release power domain: %d\n", ret);
}
-static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
+static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+}
+
+static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable)
+{
+ u32 csc_ctl;
+
+ csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR,
+ VC4_HD_CSC_CTL_ORDER);
+
+ if (enable) {
+ /* CEA VICs other than #1 requre limited range RGB
+ * output unless overridden by an AVI infoframe.
+ * Apply a colorspace conversion to squash 0-255 down
+ * to 16-235. The matrix here is:
+ *
+ * [ 0 0 0.8594 16]
+ * [ 0 0.8594 0 16]
+ * [ 0.8594 0 0 16]
+ * [ 0 0 0 1]
+ */
+ csc_ctl |= VC4_HD_CSC_CTL_ENABLE;
+ csc_ctl |= VC4_HD_CSC_CTL_RGB2YCC;
+ csc_ctl |= VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM,
+ VC4_HD_CSC_CTL_MODE);
+
+ HDMI_WRITE(HDMI_CSC_12_11, (0x000 << 16) | 0x000);
+ HDMI_WRITE(HDMI_CSC_14_13, (0x100 << 16) | 0x6e0);
+ HDMI_WRITE(HDMI_CSC_22_21, (0x6e0 << 16) | 0x000);
+ HDMI_WRITE(HDMI_CSC_24_23, (0x100 << 16) | 0x000);
+ HDMI_WRITE(HDMI_CSC_32_31, (0x000 << 16) | 0x6e0);
+ HDMI_WRITE(HDMI_CSC_34_33, (0x100 << 16) | 0x000);
+ }
+
+ /* The RGB order applies even when CSC is disabled. */
+ HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
+}
+
+static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable)
+{
+ u32 csc_ctl;
+
+ csc_ctl = 0x07; /* RGB_CONVERT_MODE = custom matrix, || USE_RGB_TO_YCBCR */
+
+ if (enable) {
+ /* CEA VICs other than #1 requre limited range RGB
+ * output unless overridden by an AVI infoframe.
+ * Apply a colorspace conversion to squash 0-255 down
+ * to 16-235. The matrix here is:
+ *
+ * [ 0.8594 0 0 16]
+ * [ 0 0.8594 0 16]
+ * [ 0 0 0.8594 16]
+ * [ 0 0 0 1]
+ * Matrix is signed 2p13 fixed point, with signed 9p6 offsets
+ */
+ HDMI_WRITE(HDMI_CSC_12_11, (0x0000 << 16) | 0x1b80);
+ HDMI_WRITE(HDMI_CSC_14_13, (0x0400 << 16) | 0x0000);
+ HDMI_WRITE(HDMI_CSC_22_21, (0x1b80 << 16) | 0x0000);
+ HDMI_WRITE(HDMI_CSC_24_23, (0x0400 << 16) | 0x0000);
+ HDMI_WRITE(HDMI_CSC_32_31, (0x0000 << 16) | 0x0000);
+ HDMI_WRITE(HDMI_CSC_34_33, (0x0400 << 16) | 0x1b80);
+ } else {
+ /* Still use the matrix for full range, but make it unity.
+ * Matrix is signed 2p13 fixed point, with signed 9p6 offsets
+ */
+ HDMI_WRITE(HDMI_CSC_12_11, (0x0000 << 16) | 0x2000);
+ HDMI_WRITE(HDMI_CSC_14_13, (0x0000 << 16) | 0x0000);
+ HDMI_WRITE(HDMI_CSC_22_21, (0x2000 << 16) | 0x0000);
+ HDMI_WRITE(HDMI_CSC_24_23, (0x0000 << 16) | 0x0000);
+ HDMI_WRITE(HDMI_CSC_32_31, (0x0000 << 16) | 0x0000);
+ HDMI_WRITE(HDMI_CSC_34_33, (0x0000 << 16) | 0x2000);
+ }
+
+ HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
+}
+
+static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
+ struct drm_display_mode *mode)
{
- struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
- struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
- struct drm_device *dev = encoder->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct vc4_hdmi *hdmi = vc4->hdmi;
- bool debug_dump_regs = false;
bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
@@ -483,213 +488,285 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
mode->crtc_vsync_end -
interlaced,
VC4_HDMI_VERTB_VBP));
- u32 csc_ctl;
+
+ HDMI_WRITE(HDMI_HORZA,
+ (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) |
+ (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0) |
+ VC4_SET_FIELD(mode->hdisplay * pixel_rep,
+ VC4_HDMI_HORZA_HAP));
+
+ HDMI_WRITE(HDMI_HORZB,
+ VC4_SET_FIELD((mode->htotal -
+ mode->hsync_end) * pixel_rep,
+ VC4_HDMI_HORZB_HBP) |
+ VC4_SET_FIELD((mode->hsync_end -
+ mode->hsync_start) * pixel_rep,
+ VC4_HDMI_HORZB_HSP) |
+ VC4_SET_FIELD((mode->hsync_start -
+ mode->hdisplay) * pixel_rep,
+ VC4_HDMI_HORZB_HFP));
+
+ HDMI_WRITE(HDMI_VERTA0, verta);
+ HDMI_WRITE(HDMI_VERTA1, verta);
+
+ HDMI_WRITE(HDMI_VERTB0, vertb_even);
+ HDMI_WRITE(HDMI_VERTB1, vertb);
+}
+static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
+ struct drm_display_mode *mode)
+{
+ bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
+ bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
+ bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
+ u32 pixel_rep = (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1;
+ u32 verta = (VC4_SET_FIELD(mode->crtc_vsync_end - mode->crtc_vsync_start,
+ VC5_HDMI_VERTA_VSP) |
+ VC4_SET_FIELD(mode->crtc_vsync_start - mode->crtc_vdisplay,
+ VC5_HDMI_VERTA_VFP) |
+ VC4_SET_FIELD(mode->crtc_vdisplay, VC5_HDMI_VERTA_VAL));
+ u32 vertb = (VC4_SET_FIELD(0, VC5_HDMI_VERTB_VSPO) |
+ VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
+ VC4_HDMI_VERTB_VBP));
+ u32 vertb_even = (VC4_SET_FIELD(0, VC5_HDMI_VERTB_VSPO) |
+ VC4_SET_FIELD(mode->crtc_vtotal -
+ mode->crtc_vsync_end -
+ interlaced,
+ VC4_HDMI_VERTB_VBP));
+
+ HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, 0x354021);
+ HDMI_WRITE(HDMI_HORZA,
+ (vsync_pos ? VC5_HDMI_HORZA_VPOS : 0) |
+ (hsync_pos ? VC5_HDMI_HORZA_HPOS : 0) |
+ VC4_SET_FIELD(mode->hdisplay * pixel_rep,
+ VC5_HDMI_HORZA_HAP) |
+ VC4_SET_FIELD((mode->hsync_start -
+ mode->hdisplay) * pixel_rep,
+ VC5_HDMI_HORZA_HFP));
+
+ HDMI_WRITE(HDMI_HORZB,
+ VC4_SET_FIELD((mode->htotal -
+ mode->hsync_end) * pixel_rep,
+ VC5_HDMI_HORZB_HBP) |
+ VC4_SET_FIELD((mode->hsync_end -
+ mode->hsync_start) * pixel_rep,
+ VC5_HDMI_HORZB_HSP));
+
+ HDMI_WRITE(HDMI_VERTA0, verta);
+ HDMI_WRITE(HDMI_VERTA1, verta);
+
+ HDMI_WRITE(HDMI_VERTB0, vertb_even);
+ HDMI_WRITE(HDMI_VERTB1, vertb);
+
+ HDMI_WRITE(HDMI_CLOCK_STOP, 0);
+}
+
+static void vc4_hdmi_recenter_fifo(struct vc4_hdmi *vc4_hdmi)
+{
+ u32 drift;
+ int ret;
+
+ drift = HDMI_READ(HDMI_FIFO_CTL);
+ drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK;
+
+ HDMI_WRITE(HDMI_FIFO_CTL,
+ drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
+ HDMI_WRITE(HDMI_FIFO_CTL,
+ drift | VC4_HDMI_FIFO_CTL_RECENTER);
+ usleep_range(1000, 1100);
+ HDMI_WRITE(HDMI_FIFO_CTL,
+ drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
+ HDMI_WRITE(HDMI_FIFO_CTL,
+ drift | VC4_HDMI_FIFO_CTL_RECENTER);
+
+ ret = wait_for(HDMI_READ(HDMI_FIFO_CTL) &
+ VC4_HDMI_FIFO_CTL_RECENTER_DONE, 1);
+ WARN_ONCE(ret, "Timeout waiting for "
+ "VC4_HDMI_FIFO_CTL_RECENTER_DONE");
+}
+
+static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder)
+{
+ struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ unsigned long pixel_rate, hsm_rate;
int ret;
- ret = pm_runtime_get_sync(&hdmi->pdev->dev);
+ ret = pm_runtime_get_sync(&vc4_hdmi->pdev->dev);
if (ret < 0) {
DRM_ERROR("Failed to retain power domain: %d\n", ret);
return;
}
- ret = clk_set_rate(hdmi->pixel_clock,
- mode->clock * 1000 *
- ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1));
+ pixel_rate = mode->clock * 1000 * ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1);
+ ret = clk_set_rate(vc4_hdmi->pixel_clock, pixel_rate);
if (ret) {
DRM_ERROR("Failed to set pixel clock rate: %d\n", ret);
return;
}
- ret = clk_prepare_enable(hdmi->pixel_clock);
+ ret = clk_prepare_enable(vc4_hdmi->pixel_clock);
if (ret) {
DRM_ERROR("Failed to turn on pixel clock: %d\n", ret);
return;
}
- HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL,
- VC4_HDMI_SW_RESET_HDMI |
- VC4_HDMI_SW_RESET_FORMAT_DETECT);
-
- HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0);
-
- /* PHY should be in reset, like
- * vc4_hdmi_encoder_disable() does.
+ /*
+ * As stated in RPi's vc4 firmware "HDMI state machine (HSM) clock must
+ * be faster than pixel clock, infinitesimally faster, tested in
+ * simulation. Otherwise, exact value is unimportant for HDMI
+ * operation." This conflicts with bcm2835's vc4 documentation, which
+ * states HSM's clock has to be at least 108% of the pixel clock.
+ *
+ * Real life tests reveal that vc4's firmware statement holds up, and
+ * users are able to use pixel clocks closer to HSM's, namely for
+ * 1920x1200@60Hz. So it was decided to have leave a 1% margin between
+ * both clocks. Which, for RPi0-3 implies a maximum pixel clock of
+ * 162MHz.
+ *
+ * Additionally, the AXI clock needs to be at least 25% of
+ * pixel clock, but HSM ends up being the limiting factor.
*/
- HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+ hsm_rate = max_t(unsigned long, 120000000, (pixel_rate / 100) * 101);
+ ret = clk_set_min_rate(vc4_hdmi->hsm_clock, hsm_rate);
+ if (ret) {
+ DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
+ return;
+ }
- HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
+ ret = clk_prepare_enable(vc4_hdmi->hsm_clock);
+ if (ret) {
+ DRM_ERROR("Failed to turn on HSM clock: %d\n", ret);
+ clk_disable_unprepare(vc4_hdmi->pixel_clock);
+ return;
+ }
- if (debug_dump_regs) {
- struct drm_printer p = drm_info_printer(&hdmi->pdev->dev);
+ /*
+ * FIXME: When the pixel freq is 594MHz (4k60), this needs to be setup
+ * at 300MHz.
+ */
+ ret = clk_set_min_rate(vc4_hdmi->pixel_bvb_clock,
+ (hsm_rate > VC4_HSM_MID_CLOCK ? 150000000 : 75000000));
+ if (ret) {
+ DRM_ERROR("Failed to set pixel bvb clock rate: %d\n", ret);
+ clk_disable_unprepare(vc4_hdmi->hsm_clock);
+ clk_disable_unprepare(vc4_hdmi->pixel_clock);
+ return;
+ }
- dev_info(&hdmi->pdev->dev, "HDMI regs before:\n");
- drm_print_regset32(&p, &hdmi->hdmi_regset);
- drm_print_regset32(&p, &hdmi->hd_regset);
+ ret = clk_prepare_enable(vc4_hdmi->pixel_bvb_clock);
+ if (ret) {
+ DRM_ERROR("Failed to turn on pixel bvb clock: %d\n", ret);
+ clk_disable_unprepare(vc4_hdmi->hsm_clock);
+ clk_disable_unprepare(vc4_hdmi->pixel_clock);
+ return;
}
- HD_WRITE(VC4_HD_VID_CTL, 0);
+ if (vc4_hdmi->variant->reset)
+ vc4_hdmi->variant->reset(vc4_hdmi);
- HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
- HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
+ if (vc4_hdmi->variant->phy_init)
+ vc4_hdmi->variant->phy_init(vc4_hdmi, mode);
+
+ HDMI_WRITE(HDMI_SCHEDULER_CONTROL,
+ HDMI_READ(HDMI_SCHEDULER_CONTROL) |
VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS);
- HDMI_WRITE(VC4_HDMI_HORZA,
- (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) |
- (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0) |
- VC4_SET_FIELD(mode->hdisplay * pixel_rep,
- VC4_HDMI_HORZA_HAP));
-
- HDMI_WRITE(VC4_HDMI_HORZB,
- VC4_SET_FIELD((mode->htotal -
- mode->hsync_end) * pixel_rep,
- VC4_HDMI_HORZB_HBP) |
- VC4_SET_FIELD((mode->hsync_end -
- mode->hsync_start) * pixel_rep,
- VC4_HDMI_HORZB_HSP) |
- VC4_SET_FIELD((mode->hsync_start -
- mode->hdisplay) * pixel_rep,
- VC4_HDMI_HORZB_HFP));
-
- HDMI_WRITE(VC4_HDMI_VERTA0, verta);
- HDMI_WRITE(VC4_HDMI_VERTA1, verta);
-
- HDMI_WRITE(VC4_HDMI_VERTB0, vertb_even);
- HDMI_WRITE(VC4_HDMI_VERTB1, vertb);
-
- HD_WRITE(VC4_HD_VID_CTL,
- (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) |
- (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW));
+ if (vc4_hdmi->variant->set_timings)
+ vc4_hdmi->variant->set_timings(vc4_hdmi, mode);
+}
- csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR,
- VC4_HD_CSC_CTL_ORDER);
+static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder)
+{
+ struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
+ struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
if (vc4_encoder->hdmi_monitor &&
- drm_default_rgb_quant_range(mode) ==
- HDMI_QUANTIZATION_RANGE_LIMITED) {
- /* CEA VICs other than #1 requre limited range RGB
- * output unless overridden by an AVI infoframe.
- * Apply a colorspace conversion to squash 0-255 down
- * to 16-235. The matrix here is:
- *
- * [ 0 0 0.8594 16]
- * [ 0 0.8594 0 16]
- * [ 0.8594 0 0 16]
- * [ 0 0 0 1]
- */
- csc_ctl |= VC4_HD_CSC_CTL_ENABLE;
- csc_ctl |= VC4_HD_CSC_CTL_RGB2YCC;
- csc_ctl |= VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM,
- VC4_HD_CSC_CTL_MODE);
+ drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED) {
+ if (vc4_hdmi->variant->csc_setup)
+ vc4_hdmi->variant->csc_setup(vc4_hdmi, true);
- HD_WRITE(VC4_HD_CSC_12_11, (0x000 << 16) | 0x000);
- HD_WRITE(VC4_HD_CSC_14_13, (0x100 << 16) | 0x6e0);
- HD_WRITE(VC4_HD_CSC_22_21, (0x6e0 << 16) | 0x000);
- HD_WRITE(VC4_HD_CSC_24_23, (0x100 << 16) | 0x000);
- HD_WRITE(VC4_HD_CSC_32_31, (0x000 << 16) | 0x6e0);
- HD_WRITE(VC4_HD_CSC_34_33, (0x100 << 16) | 0x000);
vc4_encoder->limited_rgb_range = true;
} else {
+ if (vc4_hdmi->variant->csc_setup)
+ vc4_hdmi->variant->csc_setup(vc4_hdmi, false);
+
vc4_encoder->limited_rgb_range = false;
}
- /* The RGB order applies even when CSC is disabled. */
- HD_WRITE(VC4_HD_CSC_CTL, csc_ctl);
-
- HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
+ HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
+}
- if (debug_dump_regs) {
- struct drm_printer p = drm_info_printer(&hdmi->pdev->dev);
+static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder)
+{
+ struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+ bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
+ bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
+ int ret;
- dev_info(&hdmi->pdev->dev, "HDMI regs after:\n");
- drm_print_regset32(&p, &hdmi->hdmi_regset);
- drm_print_regset32(&p, &hdmi->hd_regset);
- }
+ HDMI_WRITE(HDMI_VID_CTL,
+ VC4_HD_VID_CTL_ENABLE |
+ VC4_HD_VID_CTL_UNDERFLOW_ENABLE |
+ VC4_HD_VID_CTL_FRAME_COUNTER_RESET |
+ (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) |
+ (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW));
- HD_WRITE(VC4_HD_VID_CTL,
- HD_READ(VC4_HD_VID_CTL) |
- VC4_HD_VID_CTL_ENABLE |
- VC4_HD_VID_CTL_UNDERFLOW_ENABLE |
- VC4_HD_VID_CTL_FRAME_COUNTER_RESET);
+ HDMI_WRITE(HDMI_VID_CTL,
+ HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_BLANKPIX);
if (vc4_encoder->hdmi_monitor) {
- HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
- HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
+ HDMI_WRITE(HDMI_SCHEDULER_CONTROL,
+ HDMI_READ(HDMI_SCHEDULER_CONTROL) |
VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
- ret = wait_for(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
+ ret = wait_for(HDMI_READ(HDMI_SCHEDULER_CONTROL) &
VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE, 1000);
WARN_ONCE(ret, "Timeout waiting for "
"VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE\n");
} else {
- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
- HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) &
+ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
+ HDMI_READ(HDMI_RAM_PACKET_CONFIG) &
~(VC4_HDMI_RAM_PACKET_ENABLE));
- HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
- HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
+ HDMI_WRITE(HDMI_SCHEDULER_CONTROL,
+ HDMI_READ(HDMI_SCHEDULER_CONTROL) &
~VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
- ret = wait_for(!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
+ ret = wait_for(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) &
VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE), 1000);
WARN_ONCE(ret, "Timeout waiting for "
"!VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE\n");
}
if (vc4_encoder->hdmi_monitor) {
- u32 drift;
-
- WARN_ON(!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
+ WARN_ON(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) &
VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE));
- HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
- HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
+ HDMI_WRITE(HDMI_SCHEDULER_CONTROL,
+ HDMI_READ(HDMI_SCHEDULER_CONTROL) |
VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT);
- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
+ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
VC4_HDMI_RAM_PACKET_ENABLE);
vc4_hdmi_set_infoframes(encoder);
-
- drift = HDMI_READ(VC4_HDMI_FIFO_CTL);
- drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK;
-
- HDMI_WRITE(VC4_HDMI_FIFO_CTL,
- drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
- HDMI_WRITE(VC4_HDMI_FIFO_CTL,
- drift | VC4_HDMI_FIFO_CTL_RECENTER);
- usleep_range(1000, 1100);
- HDMI_WRITE(VC4_HDMI_FIFO_CTL,
- drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
- HDMI_WRITE(VC4_HDMI_FIFO_CTL,
- drift | VC4_HDMI_FIFO_CTL_RECENTER);
-
- ret = wait_for(HDMI_READ(VC4_HDMI_FIFO_CTL) &
- VC4_HDMI_FIFO_CTL_RECENTER_DONE, 1);
- WARN_ONCE(ret, "Timeout waiting for "
- "VC4_HDMI_FIFO_CTL_RECENTER_DONE");
}
+
+ vc4_hdmi_recenter_fifo(vc4_hdmi);
+}
+
+static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
}
static enum drm_mode_status
-vc4_hdmi_encoder_mode_valid(struct drm_encoder *crtc,
+vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder,
const struct drm_display_mode *mode)
{
- /*
- * As stated in RPi's vc4 firmware "HDMI state machine (HSM) clock must
- * be faster than pixel clock, infinitesimally faster, tested in
- * simulation. Otherwise, exact value is unimportant for HDMI
- * operation." This conflicts with bcm2835's vc4 documentation, which
- * states HSM's clock has to be at least 108% of the pixel clock.
- *
- * Real life tests reveal that vc4's firmware statement holds up, and
- * users are able to use pixel clocks closer to HSM's, namely for
- * 1920x1200@60Hz. So it was decided to have leave a 1% margin between
- * both clocks. Which, for RPi0-3 implies a maximum pixel clock of
- * 162MHz.
- *
- * Additionally, the AXI clock needs to be at least 25% of
- * pixel clock, but HSM ends up being the limiting factor.
- */
- if (mode->clock > HSM_CLOCK_FREQ / (1000 * 101 / 100))
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+
+ if ((mode->clock * 1000) > vc4_hdmi->variant->max_pixel_clock)
return MODE_CLOCK_HIGH;
return MODE_OK;
@@ -701,34 +778,54 @@ static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
.enable = vc4_hdmi_encoder_enable,
};
+static u32 vc4_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask)
+{
+ int i;
+ u32 channel_map = 0;
+
+ for (i = 0; i < 8; i++) {
+ if (channel_mask & BIT(i))
+ channel_map |= i << (3 * i);
+ }
+ return channel_map;
+}
+
+static u32 vc5_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask)
+{
+ int i;
+ u32 channel_map = 0;
+
+ for (i = 0; i < 8; i++) {
+ if (channel_mask & BIT(i))
+ channel_map |= i << (4 * i);
+ }
+ return channel_map;
+}
+
/* HDMI audio codec callbacks */
-static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *hdmi)
+static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi)
{
- struct drm_device *drm = hdmi->encoder->dev;
- struct vc4_dev *vc4 = to_vc4_dev(drm);
- u32 hsm_clock = clk_get_rate(hdmi->hsm_clock);
+ u32 hsm_clock = clk_get_rate(vc4_hdmi->audio_clock);
unsigned long n, m;
- rational_best_approximation(hsm_clock, hdmi->audio.samplerate,
+ rational_best_approximation(hsm_clock, vc4_hdmi->audio.samplerate,
VC4_HD_MAI_SMP_N_MASK >>
VC4_HD_MAI_SMP_N_SHIFT,
(VC4_HD_MAI_SMP_M_MASK >>
VC4_HD_MAI_SMP_M_SHIFT) + 1,
&n, &m);
- HD_WRITE(VC4_HD_MAI_SMP,
- VC4_SET_FIELD(n, VC4_HD_MAI_SMP_N) |
- VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M));
+ HDMI_WRITE(HDMI_MAI_SMP,
+ VC4_SET_FIELD(n, VC4_HD_MAI_SMP_N) |
+ VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M));
}
-static void vc4_hdmi_set_n_cts(struct vc4_hdmi *hdmi)
+static void vc4_hdmi_set_n_cts(struct vc4_hdmi *vc4_hdmi)
{
- struct drm_encoder *encoder = hdmi->encoder;
+ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
struct drm_crtc *crtc = encoder->crtc;
- struct drm_device *drm = encoder->dev;
- struct vc4_dev *vc4 = to_vc4_dev(drm);
const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
- u32 samplerate = hdmi->audio.samplerate;
+ u32 samplerate = vc4_hdmi->audio.samplerate;
u32 n, cts;
u64 tmp;
@@ -737,7 +834,7 @@ static void vc4_hdmi_set_n_cts(struct vc4_hdmi *hdmi)
do_div(tmp, 128 * samplerate);
cts = tmp;
- HDMI_WRITE(VC4_HDMI_CRP_CFG,
+ HDMI_WRITE(HDMI_CRP_CFG,
VC4_HDMI_CRP_CFG_EXTERNAL_CTS_EN |
VC4_SET_FIELD(n, VC4_HDMI_CRP_CFG_N));
@@ -746,8 +843,8 @@ static void vc4_hdmi_set_n_cts(struct vc4_hdmi *hdmi)
* providing a CTS_1 value. The two CTS values are alternated
* between based on the period fields
*/
- HDMI_WRITE(VC4_HDMI_CTS_0, cts);
- HDMI_WRITE(VC4_HDMI_CTS_1, cts);
+ HDMI_WRITE(HDMI_CTS_0, cts);
+ HDMI_WRITE(HDMI_CTS_1, cts);
}
static inline struct vc4_hdmi *dai_to_hdmi(struct snd_soc_dai *dai)
@@ -760,26 +857,25 @@ static inline struct vc4_hdmi *dai_to_hdmi(struct snd_soc_dai *dai)
static int vc4_hdmi_audio_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
- struct drm_encoder *encoder = hdmi->encoder;
- struct vc4_dev *vc4 = to_vc4_dev(encoder->dev);
+ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
+ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+ struct drm_connector *connector = &vc4_hdmi->connector;
int ret;
- if (hdmi->audio.substream && hdmi->audio.substream != substream)
+ if (vc4_hdmi->audio.substream && vc4_hdmi->audio.substream != substream)
return -EINVAL;
- hdmi->audio.substream = substream;
+ vc4_hdmi->audio.substream = substream;
/*
* If the HDMI encoder hasn't probed, or the encoder is
* currently in DVI mode, treat the codec dai as missing.
*/
- if (!encoder->crtc || !(HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) &
+ if (!encoder->crtc || !(HDMI_READ(HDMI_RAM_PACKET_CONFIG) &
VC4_HDMI_RAM_PACKET_ENABLE))
return -ENODEV;
- ret = snd_pcm_hw_constraint_eld(substream->runtime,
- hdmi->connector->eld);
+ ret = snd_pcm_hw_constraint_eld(substream->runtime, connector->eld);
if (ret)
return ret;
@@ -791,34 +887,33 @@ static int vc4_hdmi_audio_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
-static void vc4_hdmi_audio_reset(struct vc4_hdmi *hdmi)
+static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi)
{
- struct drm_encoder *encoder = hdmi->encoder;
- struct drm_device *drm = encoder->dev;
- struct device *dev = &hdmi->pdev->dev;
- struct vc4_dev *vc4 = to_vc4_dev(drm);
+ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+ struct device *dev = &vc4_hdmi->pdev->dev;
int ret;
+ vc4_hdmi->audio.streaming = false;
ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO);
if (ret)
dev_err(dev, "Failed to stop audio infoframe: %d\n", ret);
- HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_RESET);
- HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_ERRORF);
- HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_FLUSH);
+ HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_RESET);
+ HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_ERRORF);
+ HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_FLUSH);
}
static void vc4_hdmi_audio_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
+ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
- if (substream != hdmi->audio.substream)
+ if (substream != vc4_hdmi->audio.substream)
return;
- vc4_hdmi_audio_reset(hdmi);
+ vc4_hdmi_audio_reset(vc4_hdmi);
- hdmi->audio.substream = NULL;
+ vc4_hdmi->audio.substream = NULL;
}
/* HDMI audio codec callbacks */
@@ -826,72 +921,65 @@ static int vc4_hdmi_audio_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
- struct drm_encoder *encoder = hdmi->encoder;
- struct drm_device *drm = encoder->dev;
- struct device *dev = &hdmi->pdev->dev;
- struct vc4_dev *vc4 = to_vc4_dev(drm);
+ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
+ struct device *dev = &vc4_hdmi->pdev->dev;
u32 audio_packet_config, channel_mask;
- u32 channel_map, i;
+ u32 channel_map;
- if (substream != hdmi->audio.substream)
+ if (substream != vc4_hdmi->audio.substream)
return -EINVAL;
dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
params_rate(params), params_width(params),
params_channels(params));
- hdmi->audio.channels = params_channels(params);
- hdmi->audio.samplerate = params_rate(params);
+ vc4_hdmi->audio.channels = params_channels(params);
+ vc4_hdmi->audio.samplerate = params_rate(params);
- HD_WRITE(VC4_HD_MAI_CTL,
- VC4_HD_MAI_CTL_RESET |
- VC4_HD_MAI_CTL_FLUSH |
- VC4_HD_MAI_CTL_DLATE |
- VC4_HD_MAI_CTL_ERRORE |
- VC4_HD_MAI_CTL_ERRORF);
+ HDMI_WRITE(HDMI_MAI_CTL,
+ VC4_HD_MAI_CTL_RESET |
+ VC4_HD_MAI_CTL_FLUSH |
+ VC4_HD_MAI_CTL_DLATE |
+ VC4_HD_MAI_CTL_ERRORE |
+ VC4_HD_MAI_CTL_ERRORF);
- vc4_hdmi_audio_set_mai_clock(hdmi);
+ vc4_hdmi_audio_set_mai_clock(vc4_hdmi);
+ /* The B frame identifier should match the value used by alsa-lib (8) */
audio_packet_config =
VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT |
VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS |
- VC4_SET_FIELD(0xf, VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER);
+ VC4_SET_FIELD(0x8, VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER);
- channel_mask = GENMASK(hdmi->audio.channels - 1, 0);
+ channel_mask = GENMASK(vc4_hdmi->audio.channels - 1, 0);
audio_packet_config |= VC4_SET_FIELD(channel_mask,
VC4_HDMI_AUDIO_PACKET_CEA_MASK);
/* Set the MAI threshold. This logic mimics the firmware's. */
- if (hdmi->audio.samplerate > 96000) {
- HD_WRITE(VC4_HD_MAI_THR,
- VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQHIGH) |
- VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW));
- } else if (hdmi->audio.samplerate > 48000) {
- HD_WRITE(VC4_HD_MAI_THR,
- VC4_SET_FIELD(0x14, VC4_HD_MAI_THR_DREQHIGH) |
- VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW));
+ if (vc4_hdmi->audio.samplerate > 96000) {
+ HDMI_WRITE(HDMI_MAI_THR,
+ VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQHIGH) |
+ VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW));
+ } else if (vc4_hdmi->audio.samplerate > 48000) {
+ HDMI_WRITE(HDMI_MAI_THR,
+ VC4_SET_FIELD(0x14, VC4_HD_MAI_THR_DREQHIGH) |
+ VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW));
} else {
- HD_WRITE(VC4_HD_MAI_THR,
- VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICHIGH) |
- VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICLOW) |
- VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQHIGH) |
- VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQLOW));
+ HDMI_WRITE(HDMI_MAI_THR,
+ VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICHIGH) |
+ VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICLOW) |
+ VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQHIGH) |
+ VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQLOW));
}
- HDMI_WRITE(VC4_HDMI_MAI_CONFIG,
+ HDMI_WRITE(HDMI_MAI_CONFIG,
VC4_HDMI_MAI_CONFIG_BIT_REVERSE |
VC4_SET_FIELD(channel_mask, VC4_HDMI_MAI_CHANNEL_MASK));
- channel_map = 0;
- for (i = 0; i < 8; i++) {
- if (channel_mask & BIT(i))
- channel_map |= i << (3 * i);
- }
-
- HDMI_WRITE(VC4_HDMI_MAI_CHANNEL_MAP, channel_map);
- HDMI_WRITE(VC4_HDMI_AUDIO_PACKET_CONFIG, audio_packet_config);
- vc4_hdmi_set_n_cts(hdmi);
+ channel_map = vc4_hdmi->variant->channel_map(vc4_hdmi, channel_mask);
+ HDMI_WRITE(HDMI_MAI_CHANNEL_MAP, channel_map);
+ HDMI_WRITE(HDMI_AUDIO_PACKET_CONFIG, audio_packet_config);
+ vc4_hdmi_set_n_cts(vc4_hdmi);
return 0;
}
@@ -899,30 +987,33 @@ static int vc4_hdmi_audio_hw_params(struct snd_pcm_substream *substream,
static int vc4_hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
- struct drm_encoder *encoder = hdmi->encoder;
- struct drm_device *drm = encoder->dev;
- struct vc4_dev *vc4 = to_vc4_dev(drm);
+ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
+ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
vc4_hdmi_set_audio_infoframe(encoder);
- HDMI_WRITE(VC4_HDMI_TX_PHY_CTL0,
- HDMI_READ(VC4_HDMI_TX_PHY_CTL0) &
- ~VC4_HDMI_TX_PHY_RNG_PWRDN);
- HD_WRITE(VC4_HD_MAI_CTL,
- VC4_SET_FIELD(hdmi->audio.channels,
- VC4_HD_MAI_CTL_CHNUM) |
- VC4_HD_MAI_CTL_ENABLE);
+ vc4_hdmi->audio.streaming = true;
+
+ if (vc4_hdmi->variant->phy_rng_enable)
+ vc4_hdmi->variant->phy_rng_enable(vc4_hdmi);
+
+ HDMI_WRITE(HDMI_MAI_CTL,
+ VC4_SET_FIELD(vc4_hdmi->audio.channels,
+ VC4_HD_MAI_CTL_CHNUM) |
+ VC4_HD_MAI_CTL_ENABLE);
break;
case SNDRV_PCM_TRIGGER_STOP:
- HD_WRITE(VC4_HD_MAI_CTL,
- VC4_HD_MAI_CTL_DLATE |
- VC4_HD_MAI_CTL_ERRORE |
- VC4_HD_MAI_CTL_ERRORF);
- HDMI_WRITE(VC4_HDMI_TX_PHY_CTL0,
- HDMI_READ(VC4_HDMI_TX_PHY_CTL0) |
- VC4_HDMI_TX_PHY_RNG_PWRDN);
+ HDMI_WRITE(HDMI_MAI_CTL,
+ VC4_HD_MAI_CTL_DLATE |
+ VC4_HD_MAI_CTL_ERRORE |
+ VC4_HD_MAI_CTL_ERRORF);
+
+ if (vc4_hdmi->variant->phy_rng_disable)
+ vc4_hdmi->variant->phy_rng_disable(vc4_hdmi);
+
+ vc4_hdmi->audio.streaming = false;
+
break;
default:
break;
@@ -943,10 +1034,11 @@ static int vc4_hdmi_audio_eld_ctl_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct vc4_hdmi *hdmi = snd_component_to_hdmi(component);
+ struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component);
+ struct drm_connector *connector = &vc4_hdmi->connector;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- uinfo->count = sizeof(hdmi->connector->eld);
+ uinfo->count = sizeof(connector->eld);
return 0;
}
@@ -955,10 +1047,11 @@ static int vc4_hdmi_audio_eld_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct vc4_hdmi *hdmi = snd_component_to_hdmi(component);
+ struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component);
+ struct drm_connector *connector = &vc4_hdmi->connector;
- memcpy(ucontrol->value.bytes.data, hdmi->connector->eld,
- sizeof(hdmi->connector->eld));
+ memcpy(ucontrol->value.bytes.data, connector->eld,
+ sizeof(connector->eld));
return 0;
}
@@ -1023,9 +1116,9 @@ static const struct snd_soc_component_driver vc4_hdmi_audio_cpu_dai_comp = {
static int vc4_hdmi_audio_cpu_dai_probe(struct snd_soc_dai *dai)
{
- struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
+ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
- snd_soc_dai_init_dma_data(dai, &hdmi->audio.dma_data, NULL);
+ snd_soc_dai_init_dma_data(dai, &vc4_hdmi->audio.dma_data, NULL);
return 0;
}
@@ -1051,12 +1144,15 @@ static const struct snd_dmaengine_pcm_config pcm_conf = {
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
};
-static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi)
+static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
{
- struct snd_soc_dai_link *dai_link = &hdmi->audio.link;
- struct snd_soc_card *card = &hdmi->audio.card;
- struct device *dev = &hdmi->pdev->dev;
+ const struct vc4_hdmi_register *mai_data =
+ &vc4_hdmi->variant->registers[HDMI_MAI_DATA];
+ struct snd_soc_dai_link *dai_link = &vc4_hdmi->audio.link;
+ struct snd_soc_card *card = &vc4_hdmi->audio.card;
+ struct device *dev = &vc4_hdmi->pdev->dev;
const __be32 *addr;
+ int index;
int ret;
if (!of_find_property(dev->of_node, "dmas", NULL)) {
@@ -1065,6 +1161,11 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi)
return 0;
}
+ if (mai_data->reg != VC4_HD) {
+ WARN_ONCE(true, "MAI isn't in the HD block\n");
+ return -EINVAL;
+ }
+
/*
* Get the physical address of VC4_HD_MAI_DATA. We need to retrieve
* the bus address specified in the DT, because the physical address
@@ -1072,10 +1173,16 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi)
* for DMA transfers.
* This VC/MMU should probably be exposed to avoid this kind of hacks.
*/
- addr = of_get_address(dev->of_node, 1, NULL, NULL);
- hdmi->audio.dma_data.addr = be32_to_cpup(addr) + VC4_HD_MAI_DATA;
- hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- hdmi->audio.dma_data.maxburst = 2;
+ index = of_property_match_string(dev->of_node, "reg-names", "hd");
+ /* Before BCM2711, we don't have a named register range */
+ if (index < 0)
+ index = 1;
+
+ addr = of_get_address(dev->of_node, index, NULL, NULL);
+
+ vc4_hdmi->audio.dma_data.addr = be32_to_cpup(addr) + mai_data->offset;
+ vc4_hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ vc4_hdmi->audio.dma_data.maxburst = 2;
ret = devm_snd_dmaengine_pcm_register(dev, &pcm_conf, 0);
if (ret) {
@@ -1098,9 +1205,9 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi)
return ret;
}
- dai_link->cpus = &hdmi->audio.cpu;
- dai_link->codecs = &hdmi->audio.codec;
- dai_link->platforms = &hdmi->audio.platform;
+ dai_link->cpus = &vc4_hdmi->audio.cpu;
+ dai_link->codecs = &vc4_hdmi->audio.codec;
+ dai_link->platforms = &vc4_hdmi->audio.platform;
dai_link->num_cpus = 1;
dai_link->num_codecs = 1;
@@ -1115,7 +1222,7 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi)
card->dai_link = dai_link;
card->num_links = 1;
- card->name = "vc4-hdmi";
+ card->name = vc4_hdmi->variant->card_name;
card->dev = dev;
/*
@@ -1125,7 +1232,7 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi)
* now stored in card->drvdata and should be retrieved with
* snd_soc_card_get_drvdata() if needed.
*/
- snd_soc_card_set_drvdata(card, hdmi);
+ snd_soc_card_set_drvdata(card, vc4_hdmi);
ret = devm_snd_soc_register_card(dev, card);
if (ret)
dev_err(dev, "Could not register sound card: %d\n", ret);
@@ -1137,35 +1244,35 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi)
#ifdef CONFIG_DRM_VC4_HDMI_CEC
static irqreturn_t vc4_cec_irq_handler_thread(int irq, void *priv)
{
- struct vc4_dev *vc4 = priv;
- struct vc4_hdmi *hdmi = vc4->hdmi;
-
- if (hdmi->cec_irq_was_rx) {
- if (hdmi->cec_rx_msg.len)
- cec_received_msg(hdmi->cec_adap, &hdmi->cec_rx_msg);
- } else if (hdmi->cec_tx_ok) {
- cec_transmit_done(hdmi->cec_adap, CEC_TX_STATUS_OK,
+ struct vc4_hdmi *vc4_hdmi = priv;
+
+ if (vc4_hdmi->cec_irq_was_rx) {
+ if (vc4_hdmi->cec_rx_msg.len)
+ cec_received_msg(vc4_hdmi->cec_adap,
+ &vc4_hdmi->cec_rx_msg);
+ } else if (vc4_hdmi->cec_tx_ok) {
+ cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_OK,
0, 0, 0, 0);
} else {
/*
* This CEC implementation makes 1 retry, so if we
* get a NACK, then that means it made 2 attempts.
*/
- cec_transmit_done(hdmi->cec_adap, CEC_TX_STATUS_NACK,
+ cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_NACK,
0, 2, 0, 0);
}
return IRQ_HANDLED;
}
-static void vc4_cec_read_msg(struct vc4_dev *vc4, u32 cntrl1)
+static void vc4_cec_read_msg(struct vc4_hdmi *vc4_hdmi, u32 cntrl1)
{
- struct cec_msg *msg = &vc4->hdmi->cec_rx_msg;
+ struct cec_msg *msg = &vc4_hdmi->cec_rx_msg;
unsigned int i;
msg->len = 1 + ((cntrl1 & VC4_HDMI_CEC_REC_WRD_CNT_MASK) >>
VC4_HDMI_CEC_REC_WRD_CNT_SHIFT);
for (i = 0; i < msg->len; i += 4) {
- u32 val = HDMI_READ(VC4_HDMI_CEC_RX_DATA_1 + i);
+ u32 val = HDMI_READ(HDMI_CEC_RX_DATA_1 + i);
msg->msg[i] = val & 0xff;
msg->msg[i + 1] = (val >> 8) & 0xff;
@@ -1176,38 +1283,37 @@ static void vc4_cec_read_msg(struct vc4_dev *vc4, u32 cntrl1)
static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
{
- struct vc4_dev *vc4 = priv;
- struct vc4_hdmi *hdmi = vc4->hdmi;
- u32 stat = HDMI_READ(VC4_HDMI_CPU_STATUS);
+ struct vc4_hdmi *vc4_hdmi = priv;
+ u32 stat = HDMI_READ(HDMI_CEC_CPU_STATUS);
u32 cntrl1, cntrl5;
if (!(stat & VC4_HDMI_CPU_CEC))
return IRQ_NONE;
- hdmi->cec_rx_msg.len = 0;
- cntrl1 = HDMI_READ(VC4_HDMI_CEC_CNTRL_1);
- cntrl5 = HDMI_READ(VC4_HDMI_CEC_CNTRL_5);
- hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT;
- if (hdmi->cec_irq_was_rx) {
- vc4_cec_read_msg(vc4, cntrl1);
+ vc4_hdmi->cec_rx_msg.len = 0;
+ cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1);
+ cntrl5 = HDMI_READ(HDMI_CEC_CNTRL_5);
+ vc4_hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT;
+ if (vc4_hdmi->cec_irq_was_rx) {
+ vc4_cec_read_msg(vc4_hdmi, cntrl1);
cntrl1 |= VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1);
+ HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1);
cntrl1 &= ~VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
} else {
- hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD;
+ vc4_hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD;
cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
}
- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1);
- HDMI_WRITE(VC4_HDMI_CPU_CLEAR, VC4_HDMI_CPU_CEC);
+ HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1);
+ HDMI_WRITE(HDMI_CEC_CPU_CLEAR, VC4_HDMI_CPU_CEC);
return IRQ_WAKE_THREAD;
}
static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
{
- struct vc4_dev *vc4 = cec_get_drvdata(adap);
+ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
/* clock period in microseconds */
const u32 usecs = 1000000 / CEC_CLOCK_FREQ;
- u32 val = HDMI_READ(VC4_HDMI_CEC_CNTRL_5);
+ u32 val = HDMI_READ(HDMI_CEC_CNTRL_5);
val &= ~(VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET |
VC4_HDMI_CEC_CNT_TO_4700_US_MASK |
@@ -1216,30 +1322,30 @@ static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
((4500 / usecs) << VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT);
if (enable) {
- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_5, val |
+ HDMI_WRITE(HDMI_CEC_CNTRL_5, val |
VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_5, val);
- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_2,
- ((1500 / usecs) << VC4_HDMI_CEC_CNT_TO_1500_US_SHIFT) |
- ((1300 / usecs) << VC4_HDMI_CEC_CNT_TO_1300_US_SHIFT) |
- ((800 / usecs) << VC4_HDMI_CEC_CNT_TO_800_US_SHIFT) |
- ((600 / usecs) << VC4_HDMI_CEC_CNT_TO_600_US_SHIFT) |
- ((400 / usecs) << VC4_HDMI_CEC_CNT_TO_400_US_SHIFT));
- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_3,
- ((2750 / usecs) << VC4_HDMI_CEC_CNT_TO_2750_US_SHIFT) |
- ((2400 / usecs) << VC4_HDMI_CEC_CNT_TO_2400_US_SHIFT) |
- ((2050 / usecs) << VC4_HDMI_CEC_CNT_TO_2050_US_SHIFT) |
- ((1700 / usecs) << VC4_HDMI_CEC_CNT_TO_1700_US_SHIFT));
- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_4,
- ((4300 / usecs) << VC4_HDMI_CEC_CNT_TO_4300_US_SHIFT) |
- ((3900 / usecs) << VC4_HDMI_CEC_CNT_TO_3900_US_SHIFT) |
- ((3600 / usecs) << VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT) |
- ((3500 / usecs) << VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT));
-
- HDMI_WRITE(VC4_HDMI_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC);
+ HDMI_WRITE(HDMI_CEC_CNTRL_5, val);
+ HDMI_WRITE(HDMI_CEC_CNTRL_2,
+ ((1500 / usecs) << VC4_HDMI_CEC_CNT_TO_1500_US_SHIFT) |
+ ((1300 / usecs) << VC4_HDMI_CEC_CNT_TO_1300_US_SHIFT) |
+ ((800 / usecs) << VC4_HDMI_CEC_CNT_TO_800_US_SHIFT) |
+ ((600 / usecs) << VC4_HDMI_CEC_CNT_TO_600_US_SHIFT) |
+ ((400 / usecs) << VC4_HDMI_CEC_CNT_TO_400_US_SHIFT));
+ HDMI_WRITE(HDMI_CEC_CNTRL_3,
+ ((2750 / usecs) << VC4_HDMI_CEC_CNT_TO_2750_US_SHIFT) |
+ ((2400 / usecs) << VC4_HDMI_CEC_CNT_TO_2400_US_SHIFT) |
+ ((2050 / usecs) << VC4_HDMI_CEC_CNT_TO_2050_US_SHIFT) |
+ ((1700 / usecs) << VC4_HDMI_CEC_CNT_TO_1700_US_SHIFT));
+ HDMI_WRITE(HDMI_CEC_CNTRL_4,
+ ((4300 / usecs) << VC4_HDMI_CEC_CNT_TO_4300_US_SHIFT) |
+ ((3900 / usecs) << VC4_HDMI_CEC_CNT_TO_3900_US_SHIFT) |
+ ((3600 / usecs) << VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT) |
+ ((3500 / usecs) << VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT));
+
+ HDMI_WRITE(HDMI_CEC_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC);
} else {
- HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, VC4_HDMI_CPU_CEC);
- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_5, val |
+ HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, VC4_HDMI_CPU_CEC);
+ HDMI_WRITE(HDMI_CEC_CNTRL_5, val |
VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
}
return 0;
@@ -1247,10 +1353,10 @@ static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
{
- struct vc4_dev *vc4 = cec_get_drvdata(adap);
+ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1,
- (HDMI_READ(VC4_HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) |
+ HDMI_WRITE(HDMI_CEC_CNTRL_1,
+ (HDMI_READ(HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) |
(log_addr & 0xf) << VC4_HDMI_CEC_ADDR_SHIFT);
return 0;
}
@@ -1258,25 +1364,25 @@ static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
u32 signal_free_time, struct cec_msg *msg)
{
- struct vc4_dev *vc4 = cec_get_drvdata(adap);
+ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
u32 val;
unsigned int i;
for (i = 0; i < msg->len; i += 4)
- HDMI_WRITE(VC4_HDMI_CEC_TX_DATA_1 + i,
+ HDMI_WRITE(HDMI_CEC_TX_DATA_1 + i,
(msg->msg[i]) |
(msg->msg[i + 1] << 8) |
(msg->msg[i + 2] << 16) |
(msg->msg[i + 3] << 24));
- val = HDMI_READ(VC4_HDMI_CEC_CNTRL_1);
+ val = HDMI_READ(HDMI_CEC_CNTRL_1);
val &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, val);
+ HDMI_WRITE(HDMI_CEC_CNTRL_1, val);
val &= ~VC4_HDMI_CEC_MESSAGE_LENGTH_MASK;
val |= (msg->len - 1) << VC4_HDMI_CEC_MESSAGE_LENGTH_SHIFT;
val |= VC4_HDMI_CEC_START_XMIT_BEGIN;
- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, val);
+ HDMI_WRITE(HDMI_CEC_CNTRL_1, val);
return 0;
}
@@ -1285,61 +1391,275 @@ static const struct cec_adap_ops vc4_hdmi_cec_adap_ops = {
.adap_log_addr = vc4_hdmi_cec_adap_log_addr,
.adap_transmit = vc4_hdmi_cec_adap_transmit,
};
-#endif
-static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
+static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
{
-#ifdef CONFIG_DRM_VC4_HDMI_CEC
struct cec_connector_info conn_info;
-#endif
- struct platform_device *pdev = to_platform_device(dev);
- struct drm_device *drm = dev_get_drvdata(master);
- struct vc4_dev *vc4 = drm->dev_private;
- struct vc4_hdmi *hdmi;
- struct vc4_hdmi_encoder *vc4_hdmi_encoder;
- struct device_node *ddc_node;
+ struct platform_device *pdev = vc4_hdmi->pdev;
u32 value;
int ret;
- hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
- if (!hdmi)
+ if (!vc4_hdmi->variant->cec_available)
+ return 0;
+
+ vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
+ vc4_hdmi, "vc4",
+ CEC_CAP_DEFAULTS |
+ CEC_CAP_CONNECTOR_INFO, 1);
+ ret = PTR_ERR_OR_ZERO(vc4_hdmi->cec_adap);
+ if (ret < 0)
+ return ret;
+
+ cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector);
+ cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info);
+
+ HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff);
+ value = HDMI_READ(HDMI_CEC_CNTRL_1);
+ value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK;
+ /*
+ * Set the logical address to Unregistered and set the clock
+ * divider: the hsm_clock rate and this divider setting will
+ * give a 40 kHz CEC clock.
+ */
+ value |= VC4_HDMI_CEC_ADDR_MASK |
+ (4091 << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT);
+ HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
+ ret = devm_request_threaded_irq(&pdev->dev, platform_get_irq(pdev, 0),
+ vc4_cec_irq_handler,
+ vc4_cec_irq_handler_thread, 0,
+ "vc4 hdmi cec", vc4_hdmi);
+ if (ret)
+ goto err_delete_cec_adap;
+
+ ret = cec_register_adapter(vc4_hdmi->cec_adap, &pdev->dev);
+ if (ret < 0)
+ goto err_delete_cec_adap;
+
+ return 0;
+
+err_delete_cec_adap:
+ cec_delete_adapter(vc4_hdmi->cec_adap);
+
+ return ret;
+}
+
+static void vc4_hdmi_cec_exit(struct vc4_hdmi *vc4_hdmi)
+{
+ cec_unregister_adapter(vc4_hdmi->cec_adap);
+}
+#else
+static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
+{
+ return 0;
+}
+
+static void vc4_hdmi_cec_exit(struct vc4_hdmi *vc4_hdmi) {};
+
+#endif
+
+static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi,
+ struct debugfs_regset32 *regset,
+ enum vc4_hdmi_regs reg)
+{
+ const struct vc4_hdmi_variant *variant = vc4_hdmi->variant;
+ struct debugfs_reg32 *regs, *new_regs;
+ unsigned int count = 0;
+ unsigned int i;
+
+ regs = kcalloc(variant->num_registers, sizeof(*regs),
+ GFP_KERNEL);
+ if (!regs)
return -ENOMEM;
- vc4_hdmi_encoder = devm_kzalloc(dev, sizeof(*vc4_hdmi_encoder),
- GFP_KERNEL);
- if (!vc4_hdmi_encoder)
+ for (i = 0; i < variant->num_registers; i++) {
+ const struct vc4_hdmi_register *field = &variant->registers[i];
+
+ if (field->reg != reg)
+ continue;
+
+ regs[count].name = field->name;
+ regs[count].offset = field->offset;
+ count++;
+ }
+
+ new_regs = krealloc(regs, count * sizeof(*regs), GFP_KERNEL);
+ if (!new_regs)
return -ENOMEM;
- vc4_hdmi_encoder->base.type = VC4_ENCODER_TYPE_HDMI;
- hdmi->encoder = &vc4_hdmi_encoder->base.base;
-
- hdmi->pdev = pdev;
- hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0);
- if (IS_ERR(hdmi->hdmicore_regs))
- return PTR_ERR(hdmi->hdmicore_regs);
-
- hdmi->hd_regs = vc4_ioremap_regs(pdev, 1);
- if (IS_ERR(hdmi->hd_regs))
- return PTR_ERR(hdmi->hd_regs);
-
- hdmi->hdmi_regset.base = hdmi->hdmicore_regs;
- hdmi->hdmi_regset.regs = hdmi_regs;
- hdmi->hdmi_regset.nregs = ARRAY_SIZE(hdmi_regs);
- hdmi->hd_regset.base = hdmi->hd_regs;
- hdmi->hd_regset.regs = hd_regs;
- hdmi->hd_regset.nregs = ARRAY_SIZE(hd_regs);
-
- hdmi->pixel_clock = devm_clk_get(dev, "pixel");
- if (IS_ERR(hdmi->pixel_clock)) {
- ret = PTR_ERR(hdmi->pixel_clock);
+
+ regset->base = __vc4_hdmi_get_field_base(vc4_hdmi, reg);
+ regset->regs = new_regs;
+ regset->nregs = count;
+
+ return 0;
+}
+
+static int vc4_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi)
+{
+ struct platform_device *pdev = vc4_hdmi->pdev;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ vc4_hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0);
+ if (IS_ERR(vc4_hdmi->hdmicore_regs))
+ return PTR_ERR(vc4_hdmi->hdmicore_regs);
+
+ vc4_hdmi->hd_regs = vc4_ioremap_regs(pdev, 1);
+ if (IS_ERR(vc4_hdmi->hd_regs))
+ return PTR_ERR(vc4_hdmi->hd_regs);
+
+ ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hd_regset, VC4_HD);
+ if (ret)
+ return ret;
+
+ ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hdmi_regset, VC4_HDMI);
+ if (ret)
+ return ret;
+
+ vc4_hdmi->pixel_clock = devm_clk_get(dev, "pixel");
+ if (IS_ERR(vc4_hdmi->pixel_clock)) {
+ ret = PTR_ERR(vc4_hdmi->pixel_clock);
if (ret != -EPROBE_DEFER)
DRM_ERROR("Failed to get pixel clock\n");
return ret;
}
- hdmi->hsm_clock = devm_clk_get(dev, "hdmi");
- if (IS_ERR(hdmi->hsm_clock)) {
+
+ vc4_hdmi->hsm_clock = devm_clk_get(dev, "hdmi");
+ if (IS_ERR(vc4_hdmi->hsm_clock)) {
DRM_ERROR("Failed to get HDMI state machine clock\n");
- return PTR_ERR(hdmi->hsm_clock);
+ return PTR_ERR(vc4_hdmi->hsm_clock);
}
+ vc4_hdmi->audio_clock = vc4_hdmi->hsm_clock;
+
+ return 0;
+}
+
+static int vc5_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi)
+{
+ struct platform_device *pdev = vc4_hdmi->pdev;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi");
+ if (!res)
+ return -ENODEV;
+
+ vc4_hdmi->hdmicore_regs = devm_ioremap(dev, res->start,
+ resource_size(res));
+ if (!vc4_hdmi->hdmicore_regs)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hd");
+ if (!res)
+ return -ENODEV;
+
+ vc4_hdmi->hd_regs = devm_ioremap(dev, res->start, resource_size(res));
+ if (!vc4_hdmi->hd_regs)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cec");
+ if (!res)
+ return -ENODEV;
+
+ vc4_hdmi->cec_regs = devm_ioremap(dev, res->start, resource_size(res));
+ if (!vc4_hdmi->cec_regs)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csc");
+ if (!res)
+ return -ENODEV;
+
+ vc4_hdmi->csc_regs = devm_ioremap(dev, res->start, resource_size(res));
+ if (!vc4_hdmi->csc_regs)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dvp");
+ if (!res)
+ return -ENODEV;
+
+ vc4_hdmi->dvp_regs = devm_ioremap(dev, res->start, resource_size(res));
+ if (!vc4_hdmi->dvp_regs)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
+ if (!res)
+ return -ENODEV;
+
+ vc4_hdmi->phy_regs = devm_ioremap(dev, res->start, resource_size(res));
+ if (!vc4_hdmi->phy_regs)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "packet");
+ if (!res)
+ return -ENODEV;
+
+ vc4_hdmi->ram_regs = devm_ioremap(dev, res->start, resource_size(res));
+ if (!vc4_hdmi->ram_regs)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rm");
+ if (!res)
+ return -ENODEV;
+
+ vc4_hdmi->rm_regs = devm_ioremap(dev, res->start, resource_size(res));
+ if (!vc4_hdmi->rm_regs)
+ return -ENOMEM;
+
+ vc4_hdmi->hsm_clock = devm_clk_get(dev, "hdmi");
+ if (IS_ERR(vc4_hdmi->hsm_clock)) {
+ DRM_ERROR("Failed to get HDMI state machine clock\n");
+ return PTR_ERR(vc4_hdmi->hsm_clock);
+ }
+
+ vc4_hdmi->pixel_bvb_clock = devm_clk_get(dev, "bvb");
+ if (IS_ERR(vc4_hdmi->pixel_bvb_clock)) {
+ DRM_ERROR("Failed to get pixel bvb clock\n");
+ return PTR_ERR(vc4_hdmi->pixel_bvb_clock);
+ }
+
+ vc4_hdmi->audio_clock = devm_clk_get(dev, "audio");
+ if (IS_ERR(vc4_hdmi->audio_clock)) {
+ DRM_ERROR("Failed to get audio clock\n");
+ return PTR_ERR(vc4_hdmi->audio_clock);
+ }
+
+ vc4_hdmi->reset = devm_reset_control_get(dev, NULL);
+ if (IS_ERR(vc4_hdmi->reset)) {
+ DRM_ERROR("Failed to get HDMI reset line\n");
+ return PTR_ERR(vc4_hdmi->reset);
+ }
+
+ return 0;
+}
+
+static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+ const struct vc4_hdmi_variant *variant = of_device_get_match_data(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm = dev_get_drvdata(master);
+ struct vc4_hdmi *vc4_hdmi;
+ struct drm_encoder *encoder;
+ struct device_node *ddc_node;
+ u32 value;
+ int ret;
+
+ vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL);
+ if (!vc4_hdmi)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, vc4_hdmi);
+ encoder = &vc4_hdmi->encoder.base.base;
+ vc4_hdmi->encoder.base.type = variant->encoder_type;
+ vc4_hdmi->encoder.base.pre_crtc_configure = vc4_hdmi_encoder_pre_crtc_configure;
+ vc4_hdmi->encoder.base.pre_crtc_enable = vc4_hdmi_encoder_pre_crtc_enable;
+ vc4_hdmi->encoder.base.post_crtc_enable = vc4_hdmi_encoder_post_crtc_enable;
+ vc4_hdmi->encoder.base.post_crtc_disable = vc4_hdmi_encoder_post_crtc_disable;
+ vc4_hdmi->encoder.base.post_crtc_powerdown = vc4_hdmi_encoder_post_crtc_powerdown;
+ vc4_hdmi->pdev = pdev;
+ vc4_hdmi->variant = variant;
+
+ ret = variant->init_resources(vc4_hdmi);
+ if (ret)
+ return ret;
ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
if (!ddc_node) {
@@ -1347,123 +1667,62 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
return -ENODEV;
}
- hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
+ vc4_hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
- if (!hdmi->ddc) {
+ if (!vc4_hdmi->ddc) {
DRM_DEBUG("Failed to get ddc i2c adapter by node\n");
return -EPROBE_DEFER;
}
- /* This is the rate that is set by the firmware. The number
- * needs to be a bit higher than the pixel clock rate
- * (generally 148.5Mhz).
- */
- ret = clk_set_rate(hdmi->hsm_clock, HSM_CLOCK_FREQ);
- if (ret) {
- DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
- goto err_put_i2c;
- }
-
- ret = clk_prepare_enable(hdmi->hsm_clock);
- if (ret) {
- DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n",
- ret);
- goto err_put_i2c;
- }
-
/* Only use the GPIO HPD pin if present in the DT, otherwise
* we'll use the HDMI core's register.
*/
if (of_find_property(dev->of_node, "hpd-gpios", &value)) {
enum of_gpio_flags hpd_gpio_flags;
- hdmi->hpd_gpio = of_get_named_gpio_flags(dev->of_node,
- "hpd-gpios", 0,
- &hpd_gpio_flags);
- if (hdmi->hpd_gpio < 0) {
- ret = hdmi->hpd_gpio;
+ vc4_hdmi->hpd_gpio = of_get_named_gpio_flags(dev->of_node,
+ "hpd-gpios", 0,
+ &hpd_gpio_flags);
+ if (vc4_hdmi->hpd_gpio < 0) {
+ ret = vc4_hdmi->hpd_gpio;
goto err_unprepare_hsm;
}
- hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW;
+ vc4_hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW;
}
- vc4->hdmi = hdmi;
-
- /* HDMI core must be enabled. */
- if (!(HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE)) {
- HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
- udelay(1);
- HD_WRITE(VC4_HD_M_CTL, 0);
-
- HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE);
- }
pm_runtime_enable(dev);
- drm_simple_encoder_init(drm, hdmi->encoder, DRM_MODE_ENCODER_TMDS);
- drm_encoder_helper_add(hdmi->encoder, &vc4_hdmi_encoder_helper_funcs);
+ drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+ drm_encoder_helper_add(encoder, &vc4_hdmi_encoder_helper_funcs);
- hdmi->connector =
- vc4_hdmi_connector_init(drm, hdmi->encoder, hdmi->ddc);
- if (IS_ERR(hdmi->connector)) {
- ret = PTR_ERR(hdmi->connector);
+ ret = vc4_hdmi_connector_init(drm, vc4_hdmi);
+ if (ret)
goto err_destroy_encoder;
- }
-#ifdef CONFIG_DRM_VC4_HDMI_CEC
- hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
- vc4, "vc4",
- CEC_CAP_DEFAULTS |
- CEC_CAP_CONNECTOR_INFO, 1);
- ret = PTR_ERR_OR_ZERO(hdmi->cec_adap);
- if (ret < 0)
- goto err_destroy_conn;
- cec_fill_conn_info_from_drm(&conn_info, hdmi->connector);
- cec_s_conn_info(hdmi->cec_adap, &conn_info);
-
- HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, 0xffffffff);
- value = HDMI_READ(VC4_HDMI_CEC_CNTRL_1);
- value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK;
- /*
- * Set the logical address to Unregistered and set the clock
- * divider: the hsm_clock rate and this divider setting will
- * give a 40 kHz CEC clock.
- */
- value |= VC4_HDMI_CEC_ADDR_MASK |
- (4091 << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT);
- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, value);
- ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
- vc4_cec_irq_handler,
- vc4_cec_irq_handler_thread, 0,
- "vc4 hdmi cec", vc4);
+ ret = vc4_hdmi_cec_init(vc4_hdmi);
if (ret)
- goto err_delete_cec_adap;
- ret = cec_register_adapter(hdmi->cec_adap, dev);
- if (ret < 0)
- goto err_delete_cec_adap;
-#endif
+ goto err_destroy_conn;
- ret = vc4_hdmi_audio_init(hdmi);
+ ret = vc4_hdmi_audio_init(vc4_hdmi);
if (ret)
- goto err_destroy_encoder;
+ goto err_free_cec;
- vc4_debugfs_add_file(drm, "hdmi_regs", vc4_hdmi_debugfs_regs, hdmi);
+ vc4_debugfs_add_file(drm, variant->debugfs_name,
+ vc4_hdmi_debugfs_regs,
+ vc4_hdmi);
return 0;
-#ifdef CONFIG_DRM_VC4_HDMI_CEC
-err_delete_cec_adap:
- cec_delete_adapter(hdmi->cec_adap);
+err_free_cec:
+ vc4_hdmi_cec_exit(vc4_hdmi);
err_destroy_conn:
- vc4_hdmi_connector_destroy(hdmi->connector);
-#endif
+ vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
err_destroy_encoder:
- drm_encoder_cleanup(hdmi->encoder);
+ drm_encoder_cleanup(encoder);
err_unprepare_hsm:
- clk_disable_unprepare(hdmi->hsm_clock);
pm_runtime_disable(dev);
-err_put_i2c:
- put_device(&hdmi->ddc->dev);
+ put_device(&vc4_hdmi->ddc->dev);
return ret;
}
@@ -1471,20 +1730,39 @@ err_put_i2c:
static void vc4_hdmi_unbind(struct device *dev, struct device *master,
void *data)
{
- struct drm_device *drm = dev_get_drvdata(master);
- struct vc4_dev *vc4 = drm->dev_private;
- struct vc4_hdmi *hdmi = vc4->hdmi;
+ struct vc4_hdmi *vc4_hdmi;
- cec_unregister_adapter(hdmi->cec_adap);
- vc4_hdmi_connector_destroy(hdmi->connector);
- drm_encoder_cleanup(hdmi->encoder);
+ /*
+ * ASoC makes it a bit hard to retrieve a pointer to the
+ * vc4_hdmi structure. Registering the card will overwrite our
+ * device drvdata with a pointer to the snd_soc_card structure,
+ * which can then be used to retrieve whatever drvdata we want
+ * to associate.
+ *
+ * However, that doesn't fly in the case where we wouldn't
+ * register an ASoC card (because of an old DT that is missing
+ * the dmas properties for example), then the card isn't
+ * registered and the device drvdata wouldn't be set.
+ *
+ * We can deal with both cases by making sure a snd_soc_card
+ * pointer and a vc4_hdmi structure are pointing to the same
+ * memory address, so we can treat them indistinctly without any
+ * issue.
+ */
+ BUILD_BUG_ON(offsetof(struct vc4_hdmi_audio, card) != 0);
+ BUILD_BUG_ON(offsetof(struct vc4_hdmi, audio) != 0);
+ vc4_hdmi = dev_get_drvdata(dev);
- clk_disable_unprepare(hdmi->hsm_clock);
- pm_runtime_disable(dev);
+ kfree(vc4_hdmi->hdmi_regset.regs);
+ kfree(vc4_hdmi->hd_regset.regs);
- put_device(&hdmi->ddc->dev);
+ vc4_hdmi_cec_exit(vc4_hdmi);
+ vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
+ drm_encoder_cleanup(&vc4_hdmi->encoder.base.base);
+
+ pm_runtime_disable(dev);
- vc4->hdmi = NULL;
+ put_device(&vc4_hdmi->ddc->dev);
}
static const struct component_ops vc4_hdmi_ops = {
@@ -1503,8 +1781,80 @@ static int vc4_hdmi_dev_remove(struct platform_device *pdev)
return 0;
}
+static const struct vc4_hdmi_variant bcm2835_variant = {
+ .encoder_type = VC4_ENCODER_TYPE_HDMI0,
+ .debugfs_name = "hdmi_regs",
+ .card_name = "vc4-hdmi",
+ .max_pixel_clock = 162000000,
+ .cec_available = true,
+ .registers = vc4_hdmi_fields,
+ .num_registers = ARRAY_SIZE(vc4_hdmi_fields),
+
+ .init_resources = vc4_hdmi_init_resources,
+ .csc_setup = vc4_hdmi_csc_setup,
+ .reset = vc4_hdmi_reset,
+ .set_timings = vc4_hdmi_set_timings,
+ .phy_init = vc4_hdmi_phy_init,
+ .phy_disable = vc4_hdmi_phy_disable,
+ .phy_rng_enable = vc4_hdmi_phy_rng_enable,
+ .phy_rng_disable = vc4_hdmi_phy_rng_disable,
+ .channel_map = vc4_hdmi_channel_map,
+};
+
+static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = {
+ .encoder_type = VC4_ENCODER_TYPE_HDMI0,
+ .debugfs_name = "hdmi0_regs",
+ .card_name = "vc4-hdmi-0",
+ .max_pixel_clock = 297000000,
+ .registers = vc5_hdmi_hdmi0_fields,
+ .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi0_fields),
+ .phy_lane_mapping = {
+ PHY_LANE_0,
+ PHY_LANE_1,
+ PHY_LANE_2,
+ PHY_LANE_CK,
+ },
+
+ .init_resources = vc5_hdmi_init_resources,
+ .csc_setup = vc5_hdmi_csc_setup,
+ .reset = vc5_hdmi_reset,
+ .set_timings = vc5_hdmi_set_timings,
+ .phy_init = vc5_hdmi_phy_init,
+ .phy_disable = vc5_hdmi_phy_disable,
+ .phy_rng_enable = vc5_hdmi_phy_rng_enable,
+ .phy_rng_disable = vc5_hdmi_phy_rng_disable,
+ .channel_map = vc5_hdmi_channel_map,
+};
+
+static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = {
+ .encoder_type = VC4_ENCODER_TYPE_HDMI1,
+ .debugfs_name = "hdmi1_regs",
+ .card_name = "vc4-hdmi-1",
+ .max_pixel_clock = 297000000,
+ .registers = vc5_hdmi_hdmi1_fields,
+ .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi1_fields),
+ .phy_lane_mapping = {
+ PHY_LANE_1,
+ PHY_LANE_0,
+ PHY_LANE_CK,
+ PHY_LANE_2,
+ },
+
+ .init_resources = vc5_hdmi_init_resources,
+ .csc_setup = vc5_hdmi_csc_setup,
+ .reset = vc5_hdmi_reset,
+ .set_timings = vc5_hdmi_set_timings,
+ .phy_init = vc5_hdmi_phy_init,
+ .phy_disable = vc5_hdmi_phy_disable,
+ .phy_rng_enable = vc5_hdmi_phy_rng_enable,
+ .phy_rng_disable = vc5_hdmi_phy_rng_disable,
+ .channel_map = vc5_hdmi_channel_map,
+};
+
static const struct of_device_id vc4_hdmi_dt_match[] = {
- { .compatible = "brcm,bcm2835-hdmi" },
+ { .compatible = "brcm,bcm2835-hdmi", .data = &bcm2835_variant },
+ { .compatible = "brcm,bcm2711-hdmi0", .data = &bcm2711_hdmi0_variant },
+ { .compatible = "brcm,bcm2711-hdmi1", .data = &bcm2711_hdmi1_variant },
{}
};
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h
new file mode 100644
index 000000000000..63c6f8bddf1d
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
@@ -0,0 +1,184 @@
+#ifndef _VC4_HDMI_H_
+#define _VC4_HDMI_H_
+
+#include <drm/drm_connector.h>
+#include <media/cec.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/soc.h>
+
+#include "vc4_drv.h"
+
+/* VC4 HDMI encoder KMS struct */
+struct vc4_hdmi_encoder {
+ struct vc4_encoder base;
+ bool hdmi_monitor;
+ bool limited_rgb_range;
+};
+
+static inline struct vc4_hdmi_encoder *
+to_vc4_hdmi_encoder(struct drm_encoder *encoder)
+{
+ return container_of(encoder, struct vc4_hdmi_encoder, base.base);
+}
+
+struct drm_display_mode;
+
+struct vc4_hdmi;
+struct vc4_hdmi_register;
+
+enum vc4_hdmi_phy_channel {
+ PHY_LANE_0 = 0,
+ PHY_LANE_1,
+ PHY_LANE_2,
+ PHY_LANE_CK,
+};
+
+struct vc4_hdmi_variant {
+ /* Encoder Type for that controller */
+ enum vc4_encoder_type encoder_type;
+
+ /* ALSA card name */
+ const char *card_name;
+
+ /* Filename to expose the registers in debugfs */
+ const char *debugfs_name;
+
+ /* Set to true when the CEC support is available */
+ bool cec_available;
+
+ /* Maximum pixel clock supported by the controller (in Hz) */
+ unsigned long long max_pixel_clock;
+
+ /* List of the registers available on that variant */
+ const struct vc4_hdmi_register *registers;
+
+ /* Number of registers on that variant */
+ unsigned int num_registers;
+
+ /* BCM2711 Only.
+ * The variants don't map the lane in the same order in the
+ * PHY, so this is an array mapping the HDMI channel (index)
+ * to the PHY lane (value).
+ */
+ enum vc4_hdmi_phy_channel phy_lane_mapping[4];
+
+ /* Callback to get the resources (memory region, interrupts,
+ * clocks, etc) for that variant.
+ */
+ int (*init_resources)(struct vc4_hdmi *vc4_hdmi);
+
+ /* Callback to reset the HDMI block */
+ void (*reset)(struct vc4_hdmi *vc4_hdmi);
+
+ /* Callback to enable / disable the CSC */
+ void (*csc_setup)(struct vc4_hdmi *vc4_hdmi, bool enable);
+
+ /* Callback to configure the video timings in the HDMI block */
+ void (*set_timings)(struct vc4_hdmi *vc4_hdmi,
+ struct drm_display_mode *mode);
+
+ /* Callback to initialize the PHY according to the mode */
+ void (*phy_init)(struct vc4_hdmi *vc4_hdmi,
+ struct drm_display_mode *mode);
+
+ /* Callback to disable the PHY */
+ void (*phy_disable)(struct vc4_hdmi *vc4_hdmi);
+
+ /* Callback to enable the RNG in the PHY */
+ void (*phy_rng_enable)(struct vc4_hdmi *vc4_hdmi);
+
+ /* Callback to disable the RNG in the PHY */
+ void (*phy_rng_disable)(struct vc4_hdmi *vc4_hdmi);
+
+ /* Callback to get channel map */
+ u32 (*channel_map)(struct vc4_hdmi *vc4_hdmi, u32 channel_mask);
+};
+
+/* HDMI audio information */
+struct vc4_hdmi_audio {
+ struct snd_soc_card card;
+ struct snd_soc_dai_link link;
+ struct snd_soc_dai_link_component cpu;
+ struct snd_soc_dai_link_component codec;
+ struct snd_soc_dai_link_component platform;
+ int samplerate;
+ int channels;
+ struct snd_dmaengine_dai_dma_data dma_data;
+ struct snd_pcm_substream *substream;
+
+ bool streaming;
+};
+
+/* General HDMI hardware state. */
+struct vc4_hdmi {
+ struct vc4_hdmi_audio audio;
+
+ struct platform_device *pdev;
+ const struct vc4_hdmi_variant *variant;
+
+ struct vc4_hdmi_encoder encoder;
+ struct drm_connector connector;
+
+ struct i2c_adapter *ddc;
+ void __iomem *hdmicore_regs;
+ void __iomem *hd_regs;
+
+ /* VC5 Only */
+ void __iomem *cec_regs;
+ /* VC5 Only */
+ void __iomem *csc_regs;
+ /* VC5 Only */
+ void __iomem *dvp_regs;
+ /* VC5 Only */
+ void __iomem *phy_regs;
+ /* VC5 Only */
+ void __iomem *ram_regs;
+ /* VC5 Only */
+ void __iomem *rm_regs;
+
+ int hpd_gpio;
+ bool hpd_active_low;
+
+ struct cec_adapter *cec_adap;
+ struct cec_msg cec_rx_msg;
+ bool cec_tx_ok;
+ bool cec_irq_was_rx;
+
+ struct clk *pixel_clock;
+ struct clk *hsm_clock;
+ struct clk *audio_clock;
+ struct clk *pixel_bvb_clock;
+
+ struct reset_control *reset;
+
+ struct debugfs_regset32 hdmi_regset;
+ struct debugfs_regset32 hd_regset;
+};
+
+static inline struct vc4_hdmi *
+connector_to_vc4_hdmi(struct drm_connector *connector)
+{
+ return container_of(connector, struct vc4_hdmi, connector);
+}
+
+static inline struct vc4_hdmi *
+encoder_to_vc4_hdmi(struct drm_encoder *encoder)
+{
+ struct vc4_hdmi_encoder *_encoder = to_vc4_hdmi_encoder(encoder);
+
+ return container_of(_encoder, struct vc4_hdmi, encoder);
+}
+
+void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
+ struct drm_display_mode *mode);
+void vc4_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi);
+void vc4_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi);
+void vc4_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi);
+
+void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
+ struct drm_display_mode *mode);
+void vc5_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi);
+void vc5_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi);
+void vc5_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi);
+
+#endif /* _VC4_HDMI_H_ */
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
new file mode 100644
index 000000000000..057796b54c51
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
@@ -0,0 +1,521 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015 Broadcom
+ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ */
+
+#include "vc4_hdmi.h"
+#include "vc4_regs.h"
+#include "vc4_hdmi_regs.h"
+
+#define VC4_HDMI_TX_PHY_RESET_CTL_PLL_RESETB BIT(5)
+#define VC4_HDMI_TX_PHY_RESET_CTL_PLLDIV_RESETB BIT(4)
+#define VC4_HDMI_TX_PHY_RESET_CTL_TX_CK_RESET BIT(3)
+#define VC4_HDMI_TX_PHY_RESET_CTL_TX_2_RESET BIT(2)
+#define VC4_HDMI_TX_PHY_RESET_CTL_TX_1_RESET BIT(1)
+#define VC4_HDMI_TX_PHY_RESET_CTL_TX_0_RESET BIT(0)
+
+#define VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN BIT(4)
+
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_PREEMP_SHIFT 29
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_PREEMP_MASK VC4_MASK(31, 29)
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_MAINDRV_SHIFT 24
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_MAINDRV_MASK VC4_MASK(28, 24)
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_PREEMP_SHIFT 21
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_PREEMP_MASK VC4_MASK(23, 21)
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_MAINDRV_SHIFT 16
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_MAINDRV_MASK VC4_MASK(20, 16)
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_PREEMP_SHIFT 13
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_PREEMP_MASK VC4_MASK(15, 13)
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_MAINDRV_SHIFT 8
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_MAINDRV_MASK VC4_MASK(12, 8)
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_PREEMP_SHIFT 5
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_PREEMP_MASK VC4_MASK(7, 5)
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_MAINDRV_SHIFT 0
+#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_MAINDRV_MASK VC4_MASK(4, 0)
+
+#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA2_SHIFT 15
+#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA2_MASK VC4_MASK(19, 15)
+#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA1_SHIFT 10
+#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA1_MASK VC4_MASK(14, 10)
+#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA0_SHIFT 5
+#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA0_MASK VC4_MASK(9, 5)
+#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_CK_SHIFT 0
+#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_CK_MASK VC4_MASK(4, 0)
+
+#define VC4_HDMI_TX_PHY_CTL_2_VCO_GAIN_SHIFT 16
+#define VC4_HDMI_TX_PHY_CTL_2_VCO_GAIN_MASK VC4_MASK(19, 16)
+#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA2_SHIFT 12
+#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA2_MASK VC4_MASK(15, 12)
+#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA1_SHIFT 8
+#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA1_MASK VC4_MASK(11, 8)
+#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA0_SHIFT 4
+#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA0_MASK VC4_MASK(7, 4)
+#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELCK_SHIFT 0
+#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELCK_MASK VC4_MASK(3, 0)
+
+#define VC4_HDMI_TX_PHY_CTL_3_RP_SHIFT 17
+#define VC4_HDMI_TX_PHY_CTL_3_RP_MASK VC4_MASK(19, 17)
+#define VC4_HDMI_TX_PHY_CTL_3_RZ_SHIFT 12
+#define VC4_HDMI_TX_PHY_CTL_3_RZ_MASK VC4_MASK(16, 12)
+#define VC4_HDMI_TX_PHY_CTL_3_CP1_SHIFT 10
+#define VC4_HDMI_TX_PHY_CTL_3_CP1_MASK VC4_MASK(11, 10)
+#define VC4_HDMI_TX_PHY_CTL_3_CP_SHIFT 8
+#define VC4_HDMI_TX_PHY_CTL_3_CP_MASK VC4_MASK(9, 8)
+#define VC4_HDMI_TX_PHY_CTL_3_CZ_SHIFT 6
+#define VC4_HDMI_TX_PHY_CTL_3_CZ_MASK VC4_MASK(7, 6)
+#define VC4_HDMI_TX_PHY_CTL_3_ICP_SHIFT 0
+#define VC4_HDMI_TX_PHY_CTL_3_ICP_MASK VC4_MASK(5, 0)
+
+#define VC4_HDMI_TX_PHY_PLL_CTL_0_MASH11_MODE BIT(13)
+#define VC4_HDMI_TX_PHY_PLL_CTL_0_VC_RANGE_EN BIT(12)
+#define VC4_HDMI_TX_PHY_PLL_CTL_0_EMULATE_VC_LOW BIT(11)
+#define VC4_HDMI_TX_PHY_PLL_CTL_0_EMULATE_VC_HIGH BIT(10)
+#define VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_SEL_SHIFT 9
+#define VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_SEL_MASK VC4_MASK(9, 9)
+#define VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_FB_DIV2 BIT(8)
+#define VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_POST_DIV2 BIT(7)
+#define VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_CONT_EN BIT(6)
+#define VC4_HDMI_TX_PHY_PLL_CTL_0_ENA_VCO_CLK BIT(5)
+
+#define VC4_HDMI_TX_PHY_PLL_CTL_1_CPP_SHIFT 16
+#define VC4_HDMI_TX_PHY_PLL_CTL_1_CPP_MASK VC4_MASK(27, 16)
+#define VC4_HDMI_TX_PHY_PLL_CTL_1_FREQ_DOUBLER_DELAY_SHIFT 14
+#define VC4_HDMI_TX_PHY_PLL_CTL_1_FREQ_DOUBLER_DELAY_MASK VC4_MASK(15, 14)
+#define VC4_HDMI_TX_PHY_PLL_CTL_1_FREQ_DOUBLER_ENABLE BIT(13)
+#define VC4_HDMI_TX_PHY_PLL_CTL_1_POST_RST_SEL_SHIFT 11
+#define VC4_HDMI_TX_PHY_PLL_CTL_1_POST_RST_SEL_MASK VC4_MASK(12, 11)
+
+#define VC4_HDMI_TX_PHY_CLK_DIV_VCO_SHIFT 8
+#define VC4_HDMI_TX_PHY_CLK_DIV_VCO_MASK VC4_MASK(15, 8)
+
+#define VC4_HDMI_TX_PHY_PLL_CFG_PDIV_SHIFT 0
+#define VC4_HDMI_TX_PHY_PLL_CFG_PDIV_MASK VC4_MASK(3, 0)
+
+#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TXCK_OUT_SEL_MASK VC4_MASK(13, 12)
+#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TXCK_OUT_SEL_SHIFT 12
+#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX2_OUT_SEL_MASK VC4_MASK(9, 8)
+#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX2_OUT_SEL_SHIFT 8
+#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX1_OUT_SEL_MASK VC4_MASK(5, 4)
+#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX1_OUT_SEL_SHIFT 4
+#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX0_OUT_SEL_MASK VC4_MASK(1, 0)
+#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX0_OUT_SEL_SHIFT 0
+
+#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1_MIN_LIMIT_MASK VC4_MASK(27, 0)
+#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1_MIN_LIMIT_SHIFT 0
+
+#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2_MAX_LIMIT_MASK VC4_MASK(27, 0)
+#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2_MAX_LIMIT_SHIFT 0
+
+#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_STABLE_THRESHOLD_MASK VC4_MASK(31, 16)
+#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_STABLE_THRESHOLD_SHIFT 16
+#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_HOLD_THRESHOLD_MASK VC4_MASK(15, 0)
+#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_HOLD_THRESHOLD_SHIFT 0
+
+#define VC4_HDMI_RM_CONTROL_EN_FREEZE_COUNTERS BIT(19)
+#define VC4_HDMI_RM_CONTROL_EN_LOAD_INTEGRATOR BIT(17)
+#define VC4_HDMI_RM_CONTROL_FREE_RUN BIT(4)
+
+#define VC4_HDMI_RM_OFFSET_ONLY BIT(31)
+#define VC4_HDMI_RM_OFFSET_OFFSET_SHIFT 0
+#define VC4_HDMI_RM_OFFSET_OFFSET_MASK VC4_MASK(30, 0)
+
+#define VC4_HDMI_RM_FORMAT_SHIFT_SHIFT 24
+#define VC4_HDMI_RM_FORMAT_SHIFT_MASK VC4_MASK(25, 24)
+
+#define OSCILLATOR_FREQUENCY 54000000
+
+void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, struct drm_display_mode *mode)
+{
+ /* PHY should be in reset, like
+ * vc4_hdmi_encoder_disable() does.
+ */
+
+ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0);
+}
+
+void vc4_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi)
+{
+ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+}
+
+void vc4_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi)
+{
+ HDMI_WRITE(HDMI_TX_PHY_CTL_0,
+ HDMI_READ(HDMI_TX_PHY_CTL_0) &
+ ~VC4_HDMI_TX_PHY_RNG_PWRDN);
+}
+
+void vc4_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi)
+{
+ HDMI_WRITE(HDMI_TX_PHY_CTL_0,
+ HDMI_READ(HDMI_TX_PHY_CTL_0) |
+ VC4_HDMI_TX_PHY_RNG_PWRDN);
+}
+
+static unsigned long long
+phy_get_vco_freq(unsigned long long clock, u8 *vco_sel, u8 *vco_div)
+{
+ unsigned long long vco_freq = clock;
+ unsigned int _vco_div = 0;
+ unsigned int _vco_sel = 0;
+
+ while (vco_freq < 3000000000ULL) {
+ _vco_div++;
+ vco_freq = clock * _vco_div * 10;
+ }
+
+ if (vco_freq > 4500000000ULL)
+ _vco_sel = 1;
+
+ *vco_sel = _vco_sel;
+ *vco_div = _vco_div;
+
+ return vco_freq;
+}
+
+static u8 phy_get_cp_current(unsigned long vco_freq)
+{
+ if (vco_freq < 3700000000ULL)
+ return 0x1c;
+
+ return 0x18;
+}
+
+static u32 phy_get_rm_offset(unsigned long long vco_freq)
+{
+ unsigned long long fref = OSCILLATOR_FREQUENCY;
+ u64 offset = 0;
+
+ /* RM offset is stored as 9.22 format */
+ offset = vco_freq * 2;
+ offset = offset << 22;
+ do_div(offset, fref);
+ offset >>= 2;
+
+ return offset;
+}
+
+static u8 phy_get_vco_gain(unsigned long long vco_freq)
+{
+ if (vco_freq < 3350000000ULL)
+ return 0xf;
+
+ if (vco_freq < 3700000000ULL)
+ return 0xc;
+
+ if (vco_freq < 4050000000ULL)
+ return 0x6;
+
+ if (vco_freq < 4800000000ULL)
+ return 0x5;
+
+ if (vco_freq < 5200000000ULL)
+ return 0x7;
+
+ return 0x2;
+}
+
+struct phy_lane_settings {
+ struct {
+ u8 preemphasis;
+ u8 main_driver;
+ } amplitude;
+
+ u8 res_sel_data;
+ u8 term_res_sel_data;
+};
+
+struct phy_settings {
+ unsigned long long min_rate;
+ unsigned long long max_rate;
+ struct phy_lane_settings channel[3];
+ struct phy_lane_settings clock;
+};
+
+static const struct phy_settings vc5_hdmi_phy_settings[] = {
+ {
+ 0, 50000000,
+ {
+ {{0x0, 0x0A}, 0x12, 0x0},
+ {{0x0, 0x0A}, 0x12, 0x0},
+ {{0x0, 0x0A}, 0x12, 0x0}
+ },
+ {{0x0, 0x0A}, 0x18, 0x0},
+ },
+ {
+ 50000001, 75000000,
+ {
+ {{0x0, 0x09}, 0x12, 0x0},
+ {{0x0, 0x09}, 0x12, 0x0},
+ {{0x0, 0x09}, 0x12, 0x0}
+ },
+ {{0x0, 0x0C}, 0x18, 0x3},
+ },
+ {
+ 75000001, 165000000,
+ {
+ {{0x0, 0x09}, 0x12, 0x0},
+ {{0x0, 0x09}, 0x12, 0x0},
+ {{0x0, 0x09}, 0x12, 0x0}
+ },
+ {{0x0, 0x0C}, 0x18, 0x3},
+ },
+ {
+ 165000001, 250000000,
+ {
+ {{0x0, 0x0F}, 0x12, 0x1},
+ {{0x0, 0x0F}, 0x12, 0x1},
+ {{0x0, 0x0F}, 0x12, 0x1}
+ },
+ {{0x0, 0x0C}, 0x18, 0x3},
+ },
+ {
+ 250000001, 340000000,
+ {
+ {{0x2, 0x0D}, 0x12, 0x1},
+ {{0x2, 0x0D}, 0x12, 0x1},
+ {{0x2, 0x0D}, 0x12, 0x1}
+ },
+ {{0x0, 0x0C}, 0x18, 0xF},
+ },
+ {
+ 340000001, 450000000,
+ {
+ {{0x0, 0x1B}, 0x12, 0xF},
+ {{0x0, 0x1B}, 0x12, 0xF},
+ {{0x0, 0x1B}, 0x12, 0xF}
+ },
+ {{0x0, 0x0A}, 0x12, 0xF},
+ },
+ {
+ 450000001, 600000000,
+ {
+ {{0x0, 0x1C}, 0x12, 0xF},
+ {{0x0, 0x1C}, 0x12, 0xF},
+ {{0x0, 0x1C}, 0x12, 0xF}
+ },
+ {{0x0, 0x0B}, 0x13, 0xF},
+ },
+};
+
+static const struct phy_settings *phy_get_settings(unsigned long long tmds_rate)
+{
+ unsigned int count = ARRAY_SIZE(vc5_hdmi_phy_settings);
+ unsigned int i;
+
+ for (i = 0; i < count; i++) {
+ const struct phy_settings *s = &vc5_hdmi_phy_settings[i];
+
+ if (tmds_rate >= s->min_rate && tmds_rate <= s->max_rate)
+ return s;
+ }
+
+ /*
+ * If the pixel clock exceeds our max setting, try the max
+ * setting anyway.
+ */
+ return &vc5_hdmi_phy_settings[count - 1];
+}
+
+static const struct phy_lane_settings *
+phy_get_channel_settings(enum vc4_hdmi_phy_channel chan,
+ unsigned long long tmds_rate)
+{
+ const struct phy_settings *settings = phy_get_settings(tmds_rate);
+
+ if (chan == PHY_LANE_CK)
+ return &settings->clock;
+
+ return &settings->channel[chan];
+}
+
+static void vc5_hdmi_reset_phy(struct vc4_hdmi *vc4_hdmi)
+{
+ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0x0f);
+ HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL, BIT(10));
+}
+
+void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, struct drm_display_mode *mode)
+{
+ const struct phy_lane_settings *chan0_settings, *chan1_settings, *chan2_settings, *clock_settings;
+ const struct vc4_hdmi_variant *variant = vc4_hdmi->variant;
+ unsigned long long pixel_freq = mode->clock * 1000;
+ unsigned long long vco_freq;
+ unsigned char word_sel;
+ u8 vco_sel, vco_div;
+
+ vco_freq = phy_get_vco_freq(pixel_freq, &vco_sel, &vco_div);
+
+ vc5_hdmi_reset_phy(vc4_hdmi);
+
+ HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL,
+ VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN);
+
+ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL,
+ HDMI_READ(HDMI_TX_PHY_RESET_CTL) &
+ ~VC4_HDMI_TX_PHY_RESET_CTL_TX_0_RESET &
+ ~VC4_HDMI_TX_PHY_RESET_CTL_TX_1_RESET &
+ ~VC4_HDMI_TX_PHY_RESET_CTL_TX_2_RESET &
+ ~VC4_HDMI_TX_PHY_RESET_CTL_TX_CK_RESET);
+
+ HDMI_WRITE(HDMI_RM_CONTROL,
+ HDMI_READ(HDMI_RM_CONTROL) |
+ VC4_HDMI_RM_CONTROL_EN_FREEZE_COUNTERS |
+ VC4_HDMI_RM_CONTROL_EN_LOAD_INTEGRATOR |
+ VC4_HDMI_RM_CONTROL_FREE_RUN);
+
+ HDMI_WRITE(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1,
+ (HDMI_READ(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1) &
+ ~VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1_MIN_LIMIT_MASK) |
+ VC4_SET_FIELD(0, VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1_MIN_LIMIT));
+
+ HDMI_WRITE(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2,
+ (HDMI_READ(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2) &
+ ~VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2_MAX_LIMIT_MASK) |
+ VC4_SET_FIELD(0, VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2_MAX_LIMIT));
+
+ HDMI_WRITE(HDMI_RM_OFFSET,
+ VC4_SET_FIELD(phy_get_rm_offset(vco_freq),
+ VC4_HDMI_RM_OFFSET_OFFSET) |
+ VC4_HDMI_RM_OFFSET_ONLY);
+
+ HDMI_WRITE(HDMI_TX_PHY_CLK_DIV,
+ VC4_SET_FIELD(vco_div, VC4_HDMI_TX_PHY_CLK_DIV_VCO));
+
+ HDMI_WRITE(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4,
+ VC4_SET_FIELD(0xe147, VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_HOLD_THRESHOLD) |
+ VC4_SET_FIELD(0xe14, VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_STABLE_THRESHOLD));
+
+ HDMI_WRITE(HDMI_TX_PHY_PLL_CTL_0,
+ VC4_HDMI_TX_PHY_PLL_CTL_0_ENA_VCO_CLK |
+ VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_CONT_EN |
+ VC4_HDMI_TX_PHY_PLL_CTL_0_MASH11_MODE |
+ VC4_SET_FIELD(vco_sel, VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_SEL));
+
+ HDMI_WRITE(HDMI_TX_PHY_PLL_CTL_1,
+ HDMI_READ(HDMI_TX_PHY_PLL_CTL_1) |
+ VC4_HDMI_TX_PHY_PLL_CTL_1_FREQ_DOUBLER_ENABLE |
+ VC4_SET_FIELD(3, VC4_HDMI_TX_PHY_PLL_CTL_1_POST_RST_SEL) |
+ VC4_SET_FIELD(1, VC4_HDMI_TX_PHY_PLL_CTL_1_FREQ_DOUBLER_DELAY) |
+ VC4_SET_FIELD(0x8a, VC4_HDMI_TX_PHY_PLL_CTL_1_CPP));
+
+ HDMI_WRITE(HDMI_RM_FORMAT,
+ HDMI_READ(HDMI_RM_FORMAT) |
+ VC4_SET_FIELD(2, VC4_HDMI_RM_FORMAT_SHIFT));
+
+ HDMI_WRITE(HDMI_TX_PHY_PLL_CFG,
+ HDMI_READ(HDMI_TX_PHY_PLL_CFG) |
+ VC4_SET_FIELD(1, VC4_HDMI_TX_PHY_PLL_CFG_PDIV));
+
+ if (pixel_freq >= 340000000)
+ word_sel = 3;
+ else
+ word_sel = 0;
+ HDMI_WRITE(HDMI_TX_PHY_TMDS_CLK_WORD_SEL, word_sel);
+
+ HDMI_WRITE(HDMI_TX_PHY_CTL_3,
+ VC4_SET_FIELD(phy_get_cp_current(vco_freq),
+ VC4_HDMI_TX_PHY_CTL_3_ICP) |
+ VC4_SET_FIELD(1, VC4_HDMI_TX_PHY_CTL_3_CP) |
+ VC4_SET_FIELD(1, VC4_HDMI_TX_PHY_CTL_3_CP1) |
+ VC4_SET_FIELD(3, VC4_HDMI_TX_PHY_CTL_3_CZ) |
+ VC4_SET_FIELD(4, VC4_HDMI_TX_PHY_CTL_3_RP) |
+ VC4_SET_FIELD(6, VC4_HDMI_TX_PHY_CTL_3_RZ));
+
+ chan0_settings =
+ phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_0],
+ pixel_freq);
+ chan1_settings =
+ phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_1],
+ pixel_freq);
+ chan2_settings =
+ phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_2],
+ pixel_freq);
+ clock_settings =
+ phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_CK],
+ pixel_freq);
+
+ HDMI_WRITE(HDMI_TX_PHY_CTL_0,
+ VC4_SET_FIELD(chan0_settings->amplitude.preemphasis,
+ VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_PREEMP) |
+ VC4_SET_FIELD(chan0_settings->amplitude.main_driver,
+ VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_MAINDRV) |
+ VC4_SET_FIELD(chan1_settings->amplitude.preemphasis,
+ VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_PREEMP) |
+ VC4_SET_FIELD(chan1_settings->amplitude.main_driver,
+ VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_MAINDRV) |
+ VC4_SET_FIELD(chan2_settings->amplitude.preemphasis,
+ VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_PREEMP) |
+ VC4_SET_FIELD(chan2_settings->amplitude.main_driver,
+ VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_MAINDRV) |
+ VC4_SET_FIELD(clock_settings->amplitude.preemphasis,
+ VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_PREEMP) |
+ VC4_SET_FIELD(clock_settings->amplitude.main_driver,
+ VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_MAINDRV));
+
+ HDMI_WRITE(HDMI_TX_PHY_CTL_1,
+ HDMI_READ(HDMI_TX_PHY_CTL_1) |
+ VC4_SET_FIELD(chan0_settings->res_sel_data,
+ VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA0) |
+ VC4_SET_FIELD(chan1_settings->res_sel_data,
+ VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA1) |
+ VC4_SET_FIELD(chan2_settings->res_sel_data,
+ VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA2) |
+ VC4_SET_FIELD(clock_settings->res_sel_data,
+ VC4_HDMI_TX_PHY_CTL_1_RES_SEL_CK));
+
+ HDMI_WRITE(HDMI_TX_PHY_CTL_2,
+ VC4_SET_FIELD(chan0_settings->term_res_sel_data,
+ VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA0) |
+ VC4_SET_FIELD(chan1_settings->term_res_sel_data,
+ VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA1) |
+ VC4_SET_FIELD(chan2_settings->term_res_sel_data,
+ VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA2) |
+ VC4_SET_FIELD(clock_settings->term_res_sel_data,
+ VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELCK) |
+ VC4_SET_FIELD(phy_get_vco_gain(vco_freq),
+ VC4_HDMI_TX_PHY_CTL_2_VCO_GAIN));
+
+ HDMI_WRITE(HDMI_TX_PHY_CHANNEL_SWAP,
+ VC4_SET_FIELD(variant->phy_lane_mapping[PHY_LANE_0],
+ VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX0_OUT_SEL) |
+ VC4_SET_FIELD(variant->phy_lane_mapping[PHY_LANE_1],
+ VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX1_OUT_SEL) |
+ VC4_SET_FIELD(variant->phy_lane_mapping[PHY_LANE_2],
+ VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX2_OUT_SEL) |
+ VC4_SET_FIELD(variant->phy_lane_mapping[PHY_LANE_CK],
+ VC4_HDMI_TX_PHY_CHANNEL_SWAP_TXCK_OUT_SEL));
+
+ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL,
+ HDMI_READ(HDMI_TX_PHY_RESET_CTL) &
+ ~(VC4_HDMI_TX_PHY_RESET_CTL_PLL_RESETB |
+ VC4_HDMI_TX_PHY_RESET_CTL_PLLDIV_RESETB));
+
+ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL,
+ HDMI_READ(HDMI_TX_PHY_RESET_CTL) |
+ VC4_HDMI_TX_PHY_RESET_CTL_PLL_RESETB |
+ VC4_HDMI_TX_PHY_RESET_CTL_PLLDIV_RESETB);
+}
+
+void vc5_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi)
+{
+ vc5_hdmi_reset_phy(vc4_hdmi);
+}
+
+void vc5_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi)
+{
+ HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL,
+ HDMI_READ(HDMI_TX_PHY_POWERDOWN_CTL) &
+ ~VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN);
+}
+
+void vc5_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi)
+{
+ HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL,
+ HDMI_READ(HDMI_TX_PHY_POWERDOWN_CTL) |
+ VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN);
+}
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
new file mode 100644
index 000000000000..7c6b4818f245
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
@@ -0,0 +1,442 @@
+#ifndef _VC4_HDMI_REGS_H_
+#define _VC4_HDMI_REGS_H_
+
+#include "vc4_hdmi.h"
+
+#define VC4_HDMI_PACKET_STRIDE 0x24
+
+enum vc4_hdmi_regs {
+ VC4_INVALID = 0,
+ VC4_HDMI,
+ VC4_HD,
+ VC5_CEC,
+ VC5_CSC,
+ VC5_DVP,
+ VC5_PHY,
+ VC5_RAM,
+ VC5_RM,
+};
+
+enum vc4_hdmi_field {
+ HDMI_AUDIO_PACKET_CONFIG,
+ HDMI_CEC_CNTRL_1,
+ HDMI_CEC_CNTRL_2,
+ HDMI_CEC_CNTRL_3,
+ HDMI_CEC_CNTRL_4,
+ HDMI_CEC_CNTRL_5,
+ HDMI_CEC_CPU_CLEAR,
+ HDMI_CEC_CPU_MASK_CLEAR,
+ HDMI_CEC_CPU_MASK_SET,
+ HDMI_CEC_CPU_MASK_STATUS,
+ HDMI_CEC_CPU_STATUS,
+
+ /*
+ * Transmit data, first byte is low byte of the 32-bit reg.
+ * MSB of each byte transmitted first.
+ */
+ HDMI_CEC_RX_DATA_1,
+ HDMI_CEC_RX_DATA_2,
+ HDMI_CEC_RX_DATA_3,
+ HDMI_CEC_RX_DATA_4,
+ HDMI_CEC_TX_DATA_1,
+ HDMI_CEC_TX_DATA_2,
+ HDMI_CEC_TX_DATA_3,
+ HDMI_CEC_TX_DATA_4,
+ HDMI_CLOCK_STOP,
+ HDMI_CORE_REV,
+ HDMI_CRP_CFG,
+ HDMI_CSC_12_11,
+ HDMI_CSC_14_13,
+ HDMI_CSC_22_21,
+ HDMI_CSC_24_23,
+ HDMI_CSC_32_31,
+ HDMI_CSC_34_33,
+ HDMI_CSC_CTL,
+
+ /*
+ * 20-bit fields containing CTS values to be transmitted if
+ * !EXTERNAL_CTS_EN
+ */
+ HDMI_CTS_0,
+ HDMI_CTS_1,
+ HDMI_DVP_CTL,
+ HDMI_FIFO_CTL,
+ HDMI_FRAME_COUNT,
+ HDMI_HORZA,
+ HDMI_HORZB,
+ HDMI_HOTPLUG,
+ HDMI_HOTPLUG_INT,
+
+ /*
+ * 3 bits per field, where each field maps from that
+ * corresponding MAI bus channel to the given HDMI channel.
+ */
+ HDMI_MAI_CHANNEL_MAP,
+ HDMI_MAI_CONFIG,
+ HDMI_MAI_CTL,
+
+ /*
+ * Register for DMAing in audio data to be transported over
+ * the MAI bus to the Falcon core.
+ */
+ HDMI_MAI_DATA,
+
+ /* Format header to be placed on the MAI data. Unused. */
+ HDMI_MAI_FMT,
+
+ /* Last received format word on the MAI bus. */
+ HDMI_MAI_FORMAT,
+ HDMI_MAI_SMP,
+ HDMI_MAI_THR,
+ HDMI_M_CTL,
+ HDMI_RAM_PACKET_CONFIG,
+ HDMI_RAM_PACKET_START,
+ HDMI_RAM_PACKET_STATUS,
+ HDMI_RM_CONTROL,
+ HDMI_RM_FORMAT,
+ HDMI_RM_OFFSET,
+ HDMI_SCHEDULER_CONTROL,
+ HDMI_SW_RESET_CONTROL,
+ HDMI_TX_PHY_CHANNEL_SWAP,
+ HDMI_TX_PHY_CLK_DIV,
+ HDMI_TX_PHY_CTL_0,
+ HDMI_TX_PHY_CTL_1,
+ HDMI_TX_PHY_CTL_2,
+ HDMI_TX_PHY_CTL_3,
+ HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1,
+ HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2,
+ HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4,
+ HDMI_TX_PHY_PLL_CFG,
+ HDMI_TX_PHY_PLL_CTL_0,
+ HDMI_TX_PHY_PLL_CTL_1,
+ HDMI_TX_PHY_POWERDOWN_CTL,
+ HDMI_TX_PHY_RESET_CTL,
+ HDMI_TX_PHY_TMDS_CLK_WORD_SEL,
+ HDMI_VEC_INTERFACE_XBAR,
+ HDMI_VERTA0,
+ HDMI_VERTA1,
+ HDMI_VERTB0,
+ HDMI_VERTB1,
+ HDMI_VID_CTL,
+};
+
+struct vc4_hdmi_register {
+ char *name;
+ enum vc4_hdmi_regs reg;
+ unsigned int offset;
+};
+
+#define _VC4_REG(_base, _reg, _offset) \
+ [_reg] = { \
+ .name = #_reg, \
+ .reg = _base, \
+ .offset = _offset, \
+ }
+
+#define VC4_HD_REG(reg, offset) _VC4_REG(VC4_HD, reg, offset)
+#define VC4_HDMI_REG(reg, offset) _VC4_REG(VC4_HDMI, reg, offset)
+#define VC5_CEC_REG(reg, offset) _VC4_REG(VC5_CEC, reg, offset)
+#define VC5_CSC_REG(reg, offset) _VC4_REG(VC5_CSC, reg, offset)
+#define VC5_DVP_REG(reg, offset) _VC4_REG(VC5_DVP, reg, offset)
+#define VC5_PHY_REG(reg, offset) _VC4_REG(VC5_PHY, reg, offset)
+#define VC5_RAM_REG(reg, offset) _VC4_REG(VC5_RAM, reg, offset)
+#define VC5_RM_REG(reg, offset) _VC4_REG(VC5_RM, reg, offset)
+
+static const struct vc4_hdmi_register vc4_hdmi_fields[] = {
+ VC4_HD_REG(HDMI_M_CTL, 0x000c),
+ VC4_HD_REG(HDMI_MAI_CTL, 0x0014),
+ VC4_HD_REG(HDMI_MAI_THR, 0x0018),
+ VC4_HD_REG(HDMI_MAI_FMT, 0x001c),
+ VC4_HD_REG(HDMI_MAI_DATA, 0x0020),
+ VC4_HD_REG(HDMI_MAI_SMP, 0x002c),
+ VC4_HD_REG(HDMI_VID_CTL, 0x0038),
+ VC4_HD_REG(HDMI_CSC_CTL, 0x0040),
+ VC4_HD_REG(HDMI_CSC_12_11, 0x0044),
+ VC4_HD_REG(HDMI_CSC_14_13, 0x0048),
+ VC4_HD_REG(HDMI_CSC_22_21, 0x004c),
+ VC4_HD_REG(HDMI_CSC_24_23, 0x0050),
+ VC4_HD_REG(HDMI_CSC_32_31, 0x0054),
+ VC4_HD_REG(HDMI_CSC_34_33, 0x0058),
+ VC4_HD_REG(HDMI_FRAME_COUNT, 0x0068),
+
+ VC4_HDMI_REG(HDMI_CORE_REV, 0x0000),
+ VC4_HDMI_REG(HDMI_SW_RESET_CONTROL, 0x0004),
+ VC4_HDMI_REG(HDMI_HOTPLUG_INT, 0x0008),
+ VC4_HDMI_REG(HDMI_HOTPLUG, 0x000c),
+ VC4_HDMI_REG(HDMI_FIFO_CTL, 0x005c),
+ VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x0090),
+ VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0094),
+ VC4_HDMI_REG(HDMI_MAI_FORMAT, 0x0098),
+ VC4_HDMI_REG(HDMI_AUDIO_PACKET_CONFIG, 0x009c),
+ VC4_HDMI_REG(HDMI_RAM_PACKET_CONFIG, 0x00a0),
+ VC4_HDMI_REG(HDMI_RAM_PACKET_STATUS, 0x00a4),
+ VC4_HDMI_REG(HDMI_CRP_CFG, 0x00a8),
+ VC4_HDMI_REG(HDMI_CTS_0, 0x00ac),
+ VC4_HDMI_REG(HDMI_CTS_1, 0x00b0),
+ VC4_HDMI_REG(HDMI_SCHEDULER_CONTROL, 0x00c0),
+ VC4_HDMI_REG(HDMI_HORZA, 0x00c4),
+ VC4_HDMI_REG(HDMI_HORZB, 0x00c8),
+ VC4_HDMI_REG(HDMI_VERTA0, 0x00cc),
+ VC4_HDMI_REG(HDMI_VERTB0, 0x00d0),
+ VC4_HDMI_REG(HDMI_VERTA1, 0x00d4),
+ VC4_HDMI_REG(HDMI_VERTB1, 0x00d8),
+ VC4_HDMI_REG(HDMI_CEC_CNTRL_1, 0x00e8),
+ VC4_HDMI_REG(HDMI_CEC_CNTRL_2, 0x00ec),
+ VC4_HDMI_REG(HDMI_CEC_CNTRL_3, 0x00f0),
+ VC4_HDMI_REG(HDMI_CEC_CNTRL_4, 0x00f4),
+ VC4_HDMI_REG(HDMI_CEC_CNTRL_5, 0x00f8),
+ VC4_HDMI_REG(HDMI_CEC_TX_DATA_1, 0x00fc),
+ VC4_HDMI_REG(HDMI_CEC_TX_DATA_2, 0x0100),
+ VC4_HDMI_REG(HDMI_CEC_TX_DATA_3, 0x0104),
+ VC4_HDMI_REG(HDMI_CEC_TX_DATA_4, 0x0108),
+ VC4_HDMI_REG(HDMI_CEC_RX_DATA_1, 0x010c),
+ VC4_HDMI_REG(HDMI_CEC_RX_DATA_2, 0x0110),
+ VC4_HDMI_REG(HDMI_CEC_RX_DATA_3, 0x0114),
+ VC4_HDMI_REG(HDMI_CEC_RX_DATA_4, 0x0118),
+ VC4_HDMI_REG(HDMI_TX_PHY_RESET_CTL, 0x02c0),
+ VC4_HDMI_REG(HDMI_TX_PHY_CTL_0, 0x02c4),
+ VC4_HDMI_REG(HDMI_CEC_CPU_STATUS, 0x0340),
+ VC4_HDMI_REG(HDMI_CEC_CPU_CLEAR, 0x0348),
+ VC4_HDMI_REG(HDMI_CEC_CPU_MASK_STATUS, 0x034c),
+ VC4_HDMI_REG(HDMI_CEC_CPU_MASK_SET, 0x034c),
+ VC4_HDMI_REG(HDMI_CEC_CPU_MASK_CLEAR, 0x0354),
+ VC4_HDMI_REG(HDMI_RAM_PACKET_START, 0x0400),
+};
+
+static const struct vc4_hdmi_register vc5_hdmi_hdmi0_fields[] = {
+ VC4_HD_REG(HDMI_DVP_CTL, 0x0000),
+ VC4_HD_REG(HDMI_MAI_CTL, 0x0010),
+ VC4_HD_REG(HDMI_MAI_THR, 0x0014),
+ VC4_HD_REG(HDMI_MAI_FMT, 0x0018),
+ VC4_HD_REG(HDMI_MAI_DATA, 0x001c),
+ VC4_HD_REG(HDMI_MAI_SMP, 0x0020),
+ VC4_HD_REG(HDMI_VID_CTL, 0x0044),
+ VC4_HD_REG(HDMI_FRAME_COUNT, 0x0060),
+
+ VC4_HDMI_REG(HDMI_FIFO_CTL, 0x074),
+ VC4_HDMI_REG(HDMI_AUDIO_PACKET_CONFIG, 0x0b8),
+ VC4_HDMI_REG(HDMI_RAM_PACKET_CONFIG, 0x0bc),
+ VC4_HDMI_REG(HDMI_RAM_PACKET_STATUS, 0x0c4),
+ VC4_HDMI_REG(HDMI_CRP_CFG, 0x0c8),
+ VC4_HDMI_REG(HDMI_CTS_0, 0x0cc),
+ VC4_HDMI_REG(HDMI_CTS_1, 0x0d0),
+ VC4_HDMI_REG(HDMI_SCHEDULER_CONTROL, 0x0e0),
+ VC4_HDMI_REG(HDMI_HORZA, 0x0e4),
+ VC4_HDMI_REG(HDMI_HORZB, 0x0e8),
+ VC4_HDMI_REG(HDMI_VERTA0, 0x0ec),
+ VC4_HDMI_REG(HDMI_VERTB0, 0x0f0),
+ VC4_HDMI_REG(HDMI_VERTA1, 0x0f4),
+ VC4_HDMI_REG(HDMI_VERTB1, 0x0f8),
+ VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x09c),
+ VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0a0),
+ VC4_HDMI_REG(HDMI_HOTPLUG, 0x1a8),
+
+ VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc),
+ VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0),
+
+ VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000),
+ VC5_PHY_REG(HDMI_TX_PHY_POWERDOWN_CTL, 0x004),
+ VC5_PHY_REG(HDMI_TX_PHY_CTL_0, 0x008),
+ VC5_PHY_REG(HDMI_TX_PHY_CTL_1, 0x00c),
+ VC5_PHY_REG(HDMI_TX_PHY_CTL_2, 0x010),
+ VC5_PHY_REG(HDMI_TX_PHY_CTL_3, 0x014),
+ VC5_PHY_REG(HDMI_TX_PHY_PLL_CTL_0, 0x01c),
+ VC5_PHY_REG(HDMI_TX_PHY_PLL_CTL_1, 0x020),
+ VC5_PHY_REG(HDMI_TX_PHY_CLK_DIV, 0x028),
+ VC5_PHY_REG(HDMI_TX_PHY_PLL_CFG, 0x034),
+ VC5_PHY_REG(HDMI_TX_PHY_TMDS_CLK_WORD_SEL, 0x044),
+ VC5_PHY_REG(HDMI_TX_PHY_CHANNEL_SWAP, 0x04c),
+ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1, 0x050),
+ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2, 0x054),
+ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4, 0x05c),
+
+ VC5_RM_REG(HDMI_RM_CONTROL, 0x000),
+ VC5_RM_REG(HDMI_RM_OFFSET, 0x018),
+ VC5_RM_REG(HDMI_RM_FORMAT, 0x01c),
+
+ VC5_RAM_REG(HDMI_RAM_PACKET_START, 0x000),
+
+ VC5_CEC_REG(HDMI_CEC_CNTRL_1, 0x010),
+ VC5_CEC_REG(HDMI_CEC_CNTRL_2, 0x014),
+ VC5_CEC_REG(HDMI_CEC_CNTRL_3, 0x018),
+ VC5_CEC_REG(HDMI_CEC_CNTRL_4, 0x01c),
+ VC5_CEC_REG(HDMI_CEC_CNTRL_5, 0x020),
+ VC5_CEC_REG(HDMI_CEC_TX_DATA_1, 0x028),
+ VC5_CEC_REG(HDMI_CEC_TX_DATA_2, 0x02c),
+ VC5_CEC_REG(HDMI_CEC_TX_DATA_3, 0x030),
+ VC5_CEC_REG(HDMI_CEC_TX_DATA_4, 0x034),
+ VC5_CEC_REG(HDMI_CEC_RX_DATA_1, 0x038),
+ VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c),
+ VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040),
+ VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044),
+
+ VC5_CSC_REG(HDMI_CSC_CTL, 0x000),
+ VC5_CSC_REG(HDMI_CSC_12_11, 0x004),
+ VC5_CSC_REG(HDMI_CSC_14_13, 0x008),
+ VC5_CSC_REG(HDMI_CSC_22_21, 0x00c),
+ VC5_CSC_REG(HDMI_CSC_24_23, 0x010),
+ VC5_CSC_REG(HDMI_CSC_32_31, 0x014),
+ VC5_CSC_REG(HDMI_CSC_34_33, 0x018),
+};
+
+static const struct vc4_hdmi_register vc5_hdmi_hdmi1_fields[] = {
+ VC4_HD_REG(HDMI_DVP_CTL, 0x0000),
+ VC4_HD_REG(HDMI_MAI_CTL, 0x0030),
+ VC4_HD_REG(HDMI_MAI_THR, 0x0034),
+ VC4_HD_REG(HDMI_MAI_FMT, 0x0038),
+ VC4_HD_REG(HDMI_MAI_DATA, 0x003c),
+ VC4_HD_REG(HDMI_MAI_SMP, 0x0040),
+ VC4_HD_REG(HDMI_VID_CTL, 0x0048),
+ VC4_HD_REG(HDMI_FRAME_COUNT, 0x0064),
+
+ VC4_HDMI_REG(HDMI_FIFO_CTL, 0x074),
+ VC4_HDMI_REG(HDMI_AUDIO_PACKET_CONFIG, 0x0b8),
+ VC4_HDMI_REG(HDMI_RAM_PACKET_CONFIG, 0x0bc),
+ VC4_HDMI_REG(HDMI_RAM_PACKET_STATUS, 0x0c4),
+ VC4_HDMI_REG(HDMI_CRP_CFG, 0x0c8),
+ VC4_HDMI_REG(HDMI_CTS_0, 0x0cc),
+ VC4_HDMI_REG(HDMI_CTS_1, 0x0d0),
+ VC4_HDMI_REG(HDMI_SCHEDULER_CONTROL, 0x0e0),
+ VC4_HDMI_REG(HDMI_HORZA, 0x0e4),
+ VC4_HDMI_REG(HDMI_HORZB, 0x0e8),
+ VC4_HDMI_REG(HDMI_VERTA0, 0x0ec),
+ VC4_HDMI_REG(HDMI_VERTB0, 0x0f0),
+ VC4_HDMI_REG(HDMI_VERTA1, 0x0f4),
+ VC4_HDMI_REG(HDMI_VERTB1, 0x0f8),
+ VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x09c),
+ VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0a0),
+ VC4_HDMI_REG(HDMI_HOTPLUG, 0x1a8),
+
+ VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc),
+ VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0),
+
+ VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000),
+ VC5_PHY_REG(HDMI_TX_PHY_POWERDOWN_CTL, 0x004),
+ VC5_PHY_REG(HDMI_TX_PHY_CTL_0, 0x008),
+ VC5_PHY_REG(HDMI_TX_PHY_CTL_1, 0x00c),
+ VC5_PHY_REG(HDMI_TX_PHY_CTL_2, 0x010),
+ VC5_PHY_REG(HDMI_TX_PHY_CTL_3, 0x014),
+ VC5_PHY_REG(HDMI_TX_PHY_PLL_CTL_0, 0x01c),
+ VC5_PHY_REG(HDMI_TX_PHY_PLL_CTL_1, 0x020),
+ VC5_PHY_REG(HDMI_TX_PHY_CLK_DIV, 0x028),
+ VC5_PHY_REG(HDMI_TX_PHY_PLL_CFG, 0x034),
+ VC5_PHY_REG(HDMI_TX_PHY_CHANNEL_SWAP, 0x04c),
+ VC5_PHY_REG(HDMI_TX_PHY_TMDS_CLK_WORD_SEL, 0x044),
+ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1, 0x050),
+ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2, 0x054),
+ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4, 0x05c),
+
+ VC5_RM_REG(HDMI_RM_CONTROL, 0x000),
+ VC5_RM_REG(HDMI_RM_OFFSET, 0x018),
+ VC5_RM_REG(HDMI_RM_FORMAT, 0x01c),
+
+ VC5_RAM_REG(HDMI_RAM_PACKET_START, 0x000),
+
+ VC5_CEC_REG(HDMI_CEC_CNTRL_1, 0x010),
+ VC5_CEC_REG(HDMI_CEC_CNTRL_2, 0x014),
+ VC5_CEC_REG(HDMI_CEC_CNTRL_3, 0x018),
+ VC5_CEC_REG(HDMI_CEC_CNTRL_4, 0x01c),
+ VC5_CEC_REG(HDMI_CEC_CNTRL_5, 0x020),
+ VC5_CEC_REG(HDMI_CEC_TX_DATA_1, 0x028),
+ VC5_CEC_REG(HDMI_CEC_TX_DATA_2, 0x02c),
+ VC5_CEC_REG(HDMI_CEC_TX_DATA_3, 0x030),
+ VC5_CEC_REG(HDMI_CEC_TX_DATA_4, 0x034),
+ VC5_CEC_REG(HDMI_CEC_RX_DATA_1, 0x038),
+ VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c),
+ VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040),
+ VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044),
+
+ VC5_CSC_REG(HDMI_CSC_CTL, 0x000),
+ VC5_CSC_REG(HDMI_CSC_12_11, 0x004),
+ VC5_CSC_REG(HDMI_CSC_14_13, 0x008),
+ VC5_CSC_REG(HDMI_CSC_22_21, 0x00c),
+ VC5_CSC_REG(HDMI_CSC_24_23, 0x010),
+ VC5_CSC_REG(HDMI_CSC_32_31, 0x014),
+ VC5_CSC_REG(HDMI_CSC_34_33, 0x018),
+};
+
+static inline
+void __iomem *__vc4_hdmi_get_field_base(struct vc4_hdmi *hdmi,
+ enum vc4_hdmi_regs reg)
+{
+ switch (reg) {
+ case VC4_HD:
+ return hdmi->hd_regs;
+
+ case VC4_HDMI:
+ return hdmi->hdmicore_regs;
+
+ case VC5_CSC:
+ return hdmi->csc_regs;
+
+ case VC5_CEC:
+ return hdmi->cec_regs;
+
+ case VC5_DVP:
+ return hdmi->dvp_regs;
+
+ case VC5_PHY:
+ return hdmi->phy_regs;
+
+ case VC5_RAM:
+ return hdmi->ram_regs;
+
+ case VC5_RM:
+ return hdmi->rm_regs;
+
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static inline u32 vc4_hdmi_read(struct vc4_hdmi *hdmi,
+ enum vc4_hdmi_field reg)
+{
+ const struct vc4_hdmi_register *field;
+ const struct vc4_hdmi_variant *variant = hdmi->variant;
+ void __iomem *base;
+
+ if (reg >= variant->num_registers) {
+ dev_warn(&hdmi->pdev->dev,
+ "Invalid register ID %u\n", reg);
+ return 0;
+ }
+
+ field = &variant->registers[reg];
+ base = __vc4_hdmi_get_field_base(hdmi, field->reg);
+ if (!base) {
+ dev_warn(&hdmi->pdev->dev,
+ "Unknown register ID %u\n", reg);
+ return 0;
+ }
+
+ return readl(base + field->offset);
+}
+#define HDMI_READ(reg) vc4_hdmi_read(vc4_hdmi, reg)
+
+static inline void vc4_hdmi_write(struct vc4_hdmi *hdmi,
+ enum vc4_hdmi_field reg,
+ u32 value)
+{
+ const struct vc4_hdmi_register *field;
+ const struct vc4_hdmi_variant *variant = hdmi->variant;
+ void __iomem *base;
+
+ if (reg >= variant->num_registers) {
+ dev_warn(&hdmi->pdev->dev,
+ "Invalid register ID %u\n", reg);
+ return;
+ }
+
+ field = &variant->registers[reg];
+ base = __vc4_hdmi_get_field_base(hdmi, field->reg);
+ if (!base)
+ return;
+
+ writel(value, base + field->offset);
+}
+#define HDMI_WRITE(reg, val) vc4_hdmi_write(vc4_hdmi, reg, val)
+
+#endif /* _VC4_HDMI_REGS_H_ */
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index 2d2bf59c0503..4d0a833366ce 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -19,6 +19,8 @@
* each CRTC.
*/
+#include <linux/bitfield.h>
+#include <linux/clk.h>
#include <linux/component.h>
#include <linux/platform_device.h>
@@ -160,6 +162,7 @@ static void vc4_hvs_lut_load(struct drm_crtc *crtc)
struct drm_device *dev = crtc->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
u32 i;
/* The LUT memory is laid out with each HVS channel in order,
@@ -168,7 +171,7 @@ static void vc4_hvs_lut_load(struct drm_crtc *crtc)
*/
HVS_WRITE(SCALER_GAMADDR,
SCALER_GAMADDR_AUTOINC |
- (vc4_crtc->channel * 3 * crtc->gamma_size));
+ (vc4_state->assigned_channel * 3 * crtc->gamma_size));
for (i = 0; i < crtc->gamma_size; i++)
HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_r[i]);
@@ -194,6 +197,135 @@ static void vc4_hvs_update_gamma_lut(struct drm_crtc *crtc)
vc4_hvs_lut_load(crtc);
}
+int vc4_hvs_get_fifo_from_output(struct drm_device *dev, unsigned int output)
+{
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ u32 reg;
+ int ret;
+
+ if (!vc4->hvs->hvs5)
+ return output;
+
+ switch (output) {
+ case 0:
+ return 0;
+
+ case 1:
+ return 1;
+
+ case 2:
+ reg = HVS_READ(SCALER_DISPECTRL);
+ ret = FIELD_GET(SCALER_DISPECTRL_DSP2_MUX_MASK, reg);
+ if (ret == 0)
+ return 2;
+
+ return 0;
+
+ case 3:
+ reg = HVS_READ(SCALER_DISPCTRL);
+ ret = FIELD_GET(SCALER_DISPCTRL_DSP3_MUX_MASK, reg);
+ if (ret == 3)
+ return -EPIPE;
+
+ return ret;
+
+ case 4:
+ reg = HVS_READ(SCALER_DISPEOLN);
+ ret = FIELD_GET(SCALER_DISPEOLN_DSP4_MUX_MASK, reg);
+ if (ret == 3)
+ return -EPIPE;
+
+ return ret;
+
+ case 5:
+ reg = HVS_READ(SCALER_DISPDITHER);
+ ret = FIELD_GET(SCALER_DISPDITHER_DSP5_MUX_MASK, reg);
+ if (ret == 3)
+ return -EPIPE;
+
+ return ret;
+
+ default:
+ return -EPIPE;
+ }
+}
+
+static int vc4_hvs_init_channel(struct vc4_dev *vc4, struct drm_crtc *crtc,
+ struct drm_display_mode *mode, bool oneshot)
+{
+ struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state);
+ unsigned int chan = vc4_crtc_state->assigned_channel;
+ bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
+ u32 dispbkgndx;
+ u32 dispctrl;
+
+ HVS_WRITE(SCALER_DISPCTRLX(chan), 0);
+ HVS_WRITE(SCALER_DISPCTRLX(chan), SCALER_DISPCTRLX_RESET);
+ HVS_WRITE(SCALER_DISPCTRLX(chan), 0);
+
+ /* Turn on the scaler, which will wait for vstart to start
+ * compositing.
+ * When feeding the transposer, we should operate in oneshot
+ * mode.
+ */
+ dispctrl = SCALER_DISPCTRLX_ENABLE;
+
+ if (!vc4->hvs->hvs5)
+ dispctrl |= VC4_SET_FIELD(mode->hdisplay,
+ SCALER_DISPCTRLX_WIDTH) |
+ VC4_SET_FIELD(mode->vdisplay,
+ SCALER_DISPCTRLX_HEIGHT) |
+ (oneshot ? SCALER_DISPCTRLX_ONESHOT : 0);
+ else
+ dispctrl |= VC4_SET_FIELD(mode->hdisplay,
+ SCALER5_DISPCTRLX_WIDTH) |
+ VC4_SET_FIELD(mode->vdisplay,
+ SCALER5_DISPCTRLX_HEIGHT) |
+ (oneshot ? SCALER5_DISPCTRLX_ONESHOT : 0);
+
+ HVS_WRITE(SCALER_DISPCTRLX(chan), dispctrl);
+
+ dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(chan));
+ dispbkgndx &= ~SCALER_DISPBKGND_GAMMA;
+ dispbkgndx &= ~SCALER_DISPBKGND_INTERLACE;
+
+ HVS_WRITE(SCALER_DISPBKGNDX(chan), dispbkgndx |
+ SCALER_DISPBKGND_AUTOHS |
+ ((!vc4->hvs->hvs5) ? SCALER_DISPBKGND_GAMMA : 0) |
+ (interlace ? SCALER_DISPBKGND_INTERLACE : 0));
+
+ /* Reload the LUT, since the SRAMs would have been disabled if
+ * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once.
+ */
+ vc4_hvs_lut_load(crtc);
+
+ return 0;
+}
+
+void vc4_hvs_stop_channel(struct drm_device *dev, unsigned int chan)
+{
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+ if (HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_ENABLE)
+ return;
+
+ HVS_WRITE(SCALER_DISPCTRLX(chan),
+ HVS_READ(SCALER_DISPCTRLX(chan)) | SCALER_DISPCTRLX_RESET);
+ HVS_WRITE(SCALER_DISPCTRLX(chan),
+ HVS_READ(SCALER_DISPCTRLX(chan)) & ~SCALER_DISPCTRLX_ENABLE);
+
+ /* Once we leave, the scaler should be disabled and its fifo empty. */
+ WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_RESET);
+
+ WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(chan)),
+ SCALER_DISPSTATX_MODE) !=
+ SCALER_DISPSTATX_MODE_DISABLED);
+
+ WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) &
+ (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) !=
+ SCALER_DISPSTATX_EMPTY);
+}
+
int vc4_hvs_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
@@ -248,12 +380,12 @@ static void vc4_hvs_update_dlist(struct drm_crtc *crtc)
crtc->state->event = NULL;
}
- HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
+ HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
vc4_state->mm.start);
spin_unlock_irqrestore(&dev->event_lock, flags);
} else {
- HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
+ HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
vc4_state->mm.start);
}
}
@@ -263,59 +395,22 @@ void vc4_hvs_atomic_enable(struct drm_crtc *crtc,
{
struct drm_device *dev = crtc->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
bool oneshot = vc4_state->feed_txp;
- u32 dispctrl;
vc4_hvs_update_dlist(crtc);
-
- /* Turn on the scaler, which will wait for vstart to start
- * compositing.
- * When feeding the transposer, we should operate in oneshot
- * mode.
- */
- dispctrl = SCALER_DISPCTRLX_ENABLE;
- dispctrl |= VC4_SET_FIELD(mode->hdisplay,
- SCALER_DISPCTRLX_WIDTH) |
- VC4_SET_FIELD(mode->vdisplay,
- SCALER_DISPCTRLX_HEIGHT) |
- (oneshot ? SCALER_DISPCTRLX_ONESHOT : 0);
-
- HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), dispctrl);
+ vc4_hvs_init_channel(vc4, crtc, mode, oneshot);
}
void vc4_hvs_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
struct drm_device *dev = crtc->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
- u32 chan = vc4_crtc->channel;
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(old_state);
+ unsigned int chan = vc4_state->assigned_channel;
- if (HVS_READ(SCALER_DISPCTRLX(chan)) &
- SCALER_DISPCTRLX_ENABLE) {
- HVS_WRITE(SCALER_DISPCTRLX(chan),
- SCALER_DISPCTRLX_RESET);
-
- /* While the docs say that reset is self-clearing, it
- * seems it doesn't actually.
- */
- HVS_WRITE(SCALER_DISPCTRLX(chan), 0);
- }
-
- /* Once we leave, the scaler should be disabled and its fifo empty. */
-
- WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_RESET);
-
- WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(chan)),
- SCALER_DISPSTATX_MODE) !=
- SCALER_DISPSTATX_MODE_DISABLED);
-
- WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) &
- (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) !=
- SCALER_DISPSTATX_EMPTY);
+ vc4_hvs_stop_channel(dev, chan);
}
void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
@@ -323,7 +418,6 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
{
struct drm_device *dev = crtc->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
struct drm_plane *plane;
struct vc4_plane_state *vc4_plane_state;
@@ -365,8 +459,8 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
/* This sets a black background color fill, as is the case
* with other DRM drivers.
*/
- HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel),
- HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel)) |
+ HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel),
+ HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel)) |
SCALER_DISPBKGND_FILL);
/* Only update DISPLIST if the CRTC was already running and is not
@@ -380,7 +474,7 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
vc4_hvs_update_dlist(crtc);
if (crtc->state->color_mgmt_changed) {
- u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel));
+ u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel));
if (crtc->state->gamma_lut) {
vc4_hvs_update_gamma_lut(crtc);
@@ -392,7 +486,7 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
*/
dispbkgndx &= ~SCALER_DISPBKGND_GAMMA;
}
- HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), dispbkgndx);
+ HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel), dispbkgndx);
}
if (debug_dump_regs) {
@@ -401,50 +495,6 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
}
}
-void vc4_hvs_mode_set_nofb(struct drm_crtc *crtc)
-{
- struct drm_device *dev = crtc->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
- struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
- struct drm_display_mode *mode = &crtc->state->adjusted_mode;
- bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
-
- if (vc4_crtc->data->hvs_channel == 2) {
- u32 dispctrl;
- u32 dsp3_mux;
-
- /*
- * SCALER_DISPCTRL_DSP3 = X, where X < 2 means 'connect DSP3 to
- * FIFO X'.
- * SCALER_DISPCTRL_DSP3 = 3 means 'disable DSP 3'.
- *
- * DSP3 is connected to FIFO2 unless the transposer is
- * enabled. In this case, FIFO 2 is directly accessed by the
- * TXP IP, and we need to disable the FIFO2 -> pixelvalve1
- * route.
- */
- if (vc4_state->feed_txp)
- dsp3_mux = VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX);
- else
- dsp3_mux = VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX);
-
- dispctrl = HVS_READ(SCALER_DISPCTRL) &
- ~SCALER_DISPCTRL_DSP3_MUX_MASK;
- HVS_WRITE(SCALER_DISPCTRL, dispctrl | dsp3_mux);
- }
-
- HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel),
- SCALER_DISPBKGND_AUTOHS |
- SCALER_DISPBKGND_GAMMA |
- (interlace ? SCALER_DISPBKGND_INTERLACE : 0));
-
- /* Reload the LUT, since the SRAMs would have been disabled if
- * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once.
- */
- vc4_hvs_lut_load(crtc);
-}
-
void vc4_hvs_mask_underrun(struct drm_device *dev, int channel)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
@@ -521,6 +571,9 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
hvs->pdev = pdev;
+ if (of_device_is_compatible(pdev->dev.of_node, "brcm,bcm2711-hvs"))
+ hvs->hvs5 = true;
+
hvs->regs = vc4_ioremap_regs(pdev, 0);
if (IS_ERR(hvs->regs))
return PTR_ERR(hvs->regs);
@@ -529,7 +582,24 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
hvs->regset.regs = hvs_regs;
hvs->regset.nregs = ARRAY_SIZE(hvs_regs);
- hvs->dlist = hvs->regs + SCALER_DLIST_START;
+ if (hvs->hvs5) {
+ hvs->core_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(hvs->core_clk)) {
+ dev_err(&pdev->dev, "Couldn't get core clock\n");
+ return PTR_ERR(hvs->core_clk);
+ }
+
+ ret = clk_prepare_enable(hvs->core_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't enable the core clock\n");
+ return ret;
+ }
+ }
+
+ if (!hvs->hvs5)
+ hvs->dlist = hvs->regs + SCALER_DLIST_START;
+ else
+ hvs->dlist = hvs->regs + SCALER5_DLIST_START;
spin_lock_init(&hvs->mm_lock);
@@ -547,7 +617,12 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
* between planes when they don't overlap on the screen, but
* for now we just allocate globally.
*/
- drm_mm_init(&hvs->lbm_mm, 0, 96 * 1024);
+ if (!hvs->hvs5)
+ /* 96kB */
+ drm_mm_init(&hvs->lbm_mm, 0, 96 * 1024);
+ else
+ /* 70k words */
+ drm_mm_init(&hvs->lbm_mm, 0, 70 * 2 * 1024);
/* Upload filter kernels. We only have the one for now, so we
* keep it around for the lifetime of the driver.
@@ -605,6 +680,7 @@ static void vc4_hvs_unbind(struct device *dev, struct device *master,
{
struct drm_device *drm = dev_get_drvdata(master);
struct vc4_dev *vc4 = drm->dev_private;
+ struct vc4_hvs *hvs = vc4->hvs;
if (drm_mm_node_allocated(&vc4->hvs->mitchell_netravali_filter))
drm_mm_remove_node(&vc4->hvs->mitchell_netravali_filter);
@@ -612,6 +688,8 @@ static void vc4_hvs_unbind(struct device *dev, struct device *master,
drm_mm_takedown(&vc4->hvs->dlist_mm);
drm_mm_takedown(&vc4->hvs->lbm_mm);
+ clk_disable_unprepare(hvs->core_clk);
+
vc4->hvs = NULL;
}
@@ -632,6 +710,7 @@ static int vc4_hvs_dev_remove(struct platform_device *pdev)
}
static const struct of_device_id vc4_hvs_dt_match[] = {
+ { .compatible = "brcm,bcm2711-hvs" },
{ .compatible = "brcm,bcm2835-hvs" },
{}
};
diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c
index 08318e69061b..af3ee3dcdab6 100644
--- a/drivers/gpu/drm/vc4/vc4_kms.c
+++ b/drivers/gpu/drm/vc4/vc4_kms.c
@@ -11,6 +11,8 @@
* crtc, HDMI encoder).
*/
+#include <linux/clk.h>
+
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
@@ -144,22 +146,130 @@ vc4_ctm_commit(struct vc4_dev *vc4, struct drm_atomic_state *state)
VC4_SET_FIELD(ctm_state->fifo, SCALER_OLEDOFFS_DISPFIFO));
}
+static void vc4_hvs_pv_muxing_commit(struct vc4_dev *vc4,
+ struct drm_atomic_state *state)
+{
+ struct drm_crtc_state *crtc_state;
+ struct drm_crtc *crtc;
+ unsigned int i;
+
+ for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
+ u32 dispctrl;
+ u32 dsp3_mux;
+
+ if (!crtc_state->active)
+ continue;
+
+ if (vc4_state->assigned_channel != 2)
+ continue;
+
+ /*
+ * SCALER_DISPCTRL_DSP3 = X, where X < 2 means 'connect DSP3 to
+ * FIFO X'.
+ * SCALER_DISPCTRL_DSP3 = 3 means 'disable DSP 3'.
+ *
+ * DSP3 is connected to FIFO2 unless the transposer is
+ * enabled. In this case, FIFO 2 is directly accessed by the
+ * TXP IP, and we need to disable the FIFO2 -> pixelvalve1
+ * route.
+ */
+ if (vc4_state->feed_txp)
+ dsp3_mux = VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX);
+ else
+ dsp3_mux = VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX);
+
+ dispctrl = HVS_READ(SCALER_DISPCTRL) &
+ ~SCALER_DISPCTRL_DSP3_MUX_MASK;
+ HVS_WRITE(SCALER_DISPCTRL, dispctrl | dsp3_mux);
+ }
+}
+
+static void vc5_hvs_pv_muxing_commit(struct vc4_dev *vc4,
+ struct drm_atomic_state *state)
+{
+ struct drm_crtc_state *crtc_state;
+ struct drm_crtc *crtc;
+ unsigned char dsp2_mux = 0;
+ unsigned char dsp3_mux = 3;
+ unsigned char dsp4_mux = 3;
+ unsigned char dsp5_mux = 3;
+ unsigned int i;
+ u32 reg;
+
+ 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);
+
+ if (!crtc_state->active)
+ continue;
+
+ switch (vc4_crtc->data->hvs_output) {
+ case 2:
+ dsp2_mux = (vc4_state->assigned_channel == 2) ? 0 : 1;
+ break;
+
+ case 3:
+ dsp3_mux = vc4_state->assigned_channel;
+ break;
+
+ case 4:
+ dsp4_mux = vc4_state->assigned_channel;
+ break;
+
+ case 5:
+ dsp5_mux = vc4_state->assigned_channel;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ reg = HVS_READ(SCALER_DISPECTRL);
+ HVS_WRITE(SCALER_DISPECTRL,
+ (reg & ~SCALER_DISPECTRL_DSP2_MUX_MASK) |
+ VC4_SET_FIELD(dsp2_mux, SCALER_DISPECTRL_DSP2_MUX));
+
+ reg = HVS_READ(SCALER_DISPCTRL);
+ HVS_WRITE(SCALER_DISPCTRL,
+ (reg & ~SCALER_DISPCTRL_DSP3_MUX_MASK) |
+ VC4_SET_FIELD(dsp3_mux, SCALER_DISPCTRL_DSP3_MUX));
+
+ reg = HVS_READ(SCALER_DISPEOLN);
+ HVS_WRITE(SCALER_DISPEOLN,
+ (reg & ~SCALER_DISPEOLN_DSP4_MUX_MASK) |
+ VC4_SET_FIELD(dsp4_mux, SCALER_DISPEOLN_DSP4_MUX));
+
+ reg = HVS_READ(SCALER_DISPDITHER);
+ HVS_WRITE(SCALER_DISPDITHER,
+ (reg & ~SCALER_DISPDITHER_DSP5_MUX_MASK) |
+ VC4_SET_FIELD(dsp5_mux, SCALER_DISPDITHER_DSP5_MUX));
+}
+
static void
vc4_atomic_complete_commit(struct drm_atomic_state *state)
{
struct drm_device *dev = state->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct vc4_crtc *vc4_crtc;
+ struct vc4_hvs *hvs = vc4->hvs;
+ struct drm_crtc_state *new_crtc_state;
+ struct drm_crtc *crtc;
int i;
- for (i = 0; i < dev->mode_config.num_crtc; i++) {
- if (!state->crtcs[i].ptr || !state->crtcs[i].commit)
+ for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
+ struct vc4_crtc_state *vc4_crtc_state;
+
+ if (!new_crtc_state->commit)
continue;
- vc4_crtc = to_vc4_crtc(state->crtcs[i].ptr);
- vc4_hvs_mask_underrun(dev, vc4_crtc->channel);
+ vc4_crtc_state = to_vc4_crtc_state(new_crtc_state);
+ vc4_hvs_mask_underrun(dev, vc4_crtc_state->assigned_channel);
}
+ if (vc4->hvs->hvs5)
+ clk_set_min_rate(hvs->core_clk, 500000000);
+
drm_atomic_helper_wait_for_fences(dev, state, false);
drm_atomic_helper_wait_for_dependencies(state);
@@ -168,6 +278,11 @@ vc4_atomic_complete_commit(struct drm_atomic_state *state)
vc4_ctm_commit(vc4, state);
+ if (vc4->hvs->hvs5)
+ vc5_hvs_pv_muxing_commit(vc4, state);
+ else
+ vc4_hvs_pv_muxing_commit(vc4, state);
+
drm_atomic_helper_commit_planes(dev, state, 0);
drm_atomic_helper_commit_modeset_enables(dev, state);
@@ -182,6 +297,9 @@ vc4_atomic_complete_commit(struct drm_atomic_state *state)
drm_atomic_helper_commit_cleanup_done(state);
+ if (vc4->hvs->hvs5)
+ clk_set_min_rate(hvs->core_clk, 0);
+
drm_atomic_state_put(state);
up(&vc4->async_modeset);
@@ -374,8 +492,11 @@ vc4_ctm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
/* CTM is being enabled or the matrix changed. */
if (new_crtc_state->ctm) {
+ struct vc4_crtc_state *vc4_crtc_state =
+ to_vc4_crtc_state(new_crtc_state);
+
/* fifo is 1-based since 0 disables CTM. */
- int fifo = to_vc4_crtc(crtc)->channel + 1;
+ int fifo = vc4_crtc_state->assigned_channel + 1;
/* Check userland isn't trying to turn on CTM for more
* than one CRTC at a time.
@@ -415,6 +536,9 @@ static int vc4_load_tracker_atomic_check(struct drm_atomic_state *state)
struct drm_plane *plane;
int i;
+ if (!vc4->load_tracker_available)
+ return 0;
+
priv_state = drm_atomic_get_private_obj_state(state,
&vc4->load_tracker);
if (IS_ERR(priv_state))
@@ -485,10 +609,77 @@ static const struct drm_private_state_funcs vc4_load_tracker_state_funcs = {
.atomic_destroy_state = vc4_load_tracker_destroy_state,
};
+#define NUM_OUTPUTS 6
+#define NUM_CHANNELS 3
+
static int
vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
{
- int ret;
+ unsigned long unassigned_channels = GENMASK(NUM_CHANNELS - 1, 0);
+ struct drm_crtc_state *crtc_state;
+ struct drm_crtc *crtc;
+ int i, ret;
+
+ /*
+ * Since the HVS FIFOs are shared across all the pixelvalves and
+ * the TXP (and thus all the CRTCs), we need to pull the current
+ * state of all the enabled CRTCs so that an update to a single
+ * CRTC still keeps the previous FIFOs enabled and assigned to
+ * the same CRTCs, instead of evaluating only the CRTC being
+ * modified.
+ */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (!crtc->state->enable)
+ continue;
+
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+ }
+
+ for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
+ struct vc4_crtc_state *vc4_crtc_state =
+ to_vc4_crtc_state(crtc_state);
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ unsigned int matching_channels;
+
+ if (!crtc_state->active)
+ continue;
+
+ /*
+ * The problem we have to solve here is that we have
+ * up to 7 encoders, connected to up to 6 CRTCs.
+ *
+ * Those CRTCs, depending on the instance, can be
+ * routed to 1, 2 or 3 HVS FIFOs, and we need to set
+ * the change the muxing between FIFOs and outputs in
+ * the HVS accordingly.
+ *
+ * It would be pretty hard to come up with an
+ * algorithm that would generically solve
+ * this. However, the current routing trees we support
+ * allow us to simplify a bit the problem.
+ *
+ * Indeed, with the current supported layouts, if we
+ * try to assign in the ascending crtc index order the
+ * FIFOs, we can't fall into the situation where an
+ * earlier CRTC that had multiple routes is assigned
+ * one that was the only option for a later CRTC.
+ *
+ * If the layout changes and doesn't give us that in
+ * the future, we will need to have something smarter,
+ * but it works so far.
+ */
+ matching_channels = unassigned_channels & vc4_crtc->data->hvs_available_channels;
+ if (matching_channels) {
+ unsigned int channel = ffs(matching_channels) - 1;
+
+ vc4_crtc_state->assigned_channel = channel;
+ unassigned_channels &= ~BIT(channel);
+ } else {
+ return -EINVAL;
+ }
+ }
ret = vc4_ctm_atomic_check(dev, state);
if (ret < 0)
@@ -512,12 +703,18 @@ int vc4_kms_load(struct drm_device *dev)
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_ctm_state *ctm_state;
struct vc4_load_tracker_state *load_state;
+ bool is_vc5 = of_device_is_compatible(dev->dev->of_node,
+ "brcm,bcm2711-vc5");
int ret;
- /* Start with the load tracker enabled. Can be disabled through the
- * debugfs load_tracker file.
- */
- vc4->load_tracker_enabled = true;
+ if (!is_vc5) {
+ vc4->load_tracker_available = true;
+
+ /* Start with the load tracker enabled. Can be
+ * disabled through the debugfs load_tracker file.
+ */
+ vc4->load_tracker_enabled = true;
+ }
sema_init(&vc4->async_modeset, 1);
@@ -531,8 +728,14 @@ int vc4_kms_load(struct drm_device *dev)
return ret;
}
- dev->mode_config.max_width = 2048;
- dev->mode_config.max_height = 2048;
+ if (is_vc5) {
+ dev->mode_config.max_width = 7680;
+ dev->mode_config.max_height = 7680;
+ } else {
+ dev->mode_config.max_width = 2048;
+ dev->mode_config.max_height = 2048;
+ }
+
dev->mode_config.funcs = &vc4_mode_funcs;
dev->mode_config.preferred_depth = 24;
dev->mode_config.async_page_flip = true;
@@ -547,14 +750,17 @@ int vc4_kms_load(struct drm_device *dev)
drm_atomic_private_obj_init(dev, &vc4->ctm_manager, &ctm_state->base,
&vc4_ctm_state_funcs);
- load_state = kzalloc(sizeof(*load_state), GFP_KERNEL);
- if (!load_state) {
- drm_atomic_private_obj_fini(&vc4->ctm_manager);
- return -ENOMEM;
- }
+ if (vc4->load_tracker_available) {
+ load_state = kzalloc(sizeof(*load_state), GFP_KERNEL);
+ if (!load_state) {
+ drm_atomic_private_obj_fini(&vc4->ctm_manager);
+ return -ENOMEM;
+ }
- drm_atomic_private_obj_init(dev, &vc4->load_tracker, &load_state->base,
- &vc4_load_tracker_state_funcs);
+ drm_atomic_private_obj_init(dev, &vc4->load_tracker,
+ &load_state->base,
+ &vc4_load_tracker_state_funcs);
+ }
drm_mode_config_reset(dev);
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
index d040d9f12c6d..6b39cc2ca18d 100644
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
@@ -32,45 +32,60 @@ static const struct hvs_format {
u32 drm; /* DRM_FORMAT_* */
u32 hvs; /* HVS_FORMAT_* */
u32 pixel_order;
+ u32 pixel_order_hvs5;
} hvs_formats[] = {
{
- .drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
+ .drm = DRM_FORMAT_XRGB8888,
+ .hvs = HVS_PIXEL_FORMAT_RGBA8888,
.pixel_order = HVS_PIXEL_ORDER_ABGR,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
},
{
- .drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
+ .drm = DRM_FORMAT_ARGB8888,
+ .hvs = HVS_PIXEL_FORMAT_RGBA8888,
.pixel_order = HVS_PIXEL_ORDER_ABGR,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
},
{
- .drm = DRM_FORMAT_ABGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
+ .drm = DRM_FORMAT_ABGR8888,
+ .hvs = HVS_PIXEL_FORMAT_RGBA8888,
.pixel_order = HVS_PIXEL_ORDER_ARGB,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
},
{
- .drm = DRM_FORMAT_XBGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
+ .drm = DRM_FORMAT_XBGR8888,
+ .hvs = HVS_PIXEL_FORMAT_RGBA8888,
.pixel_order = HVS_PIXEL_ORDER_ARGB,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
},
{
- .drm = DRM_FORMAT_RGB565, .hvs = HVS_PIXEL_FORMAT_RGB565,
+ .drm = DRM_FORMAT_RGB565,
+ .hvs = HVS_PIXEL_FORMAT_RGB565,
.pixel_order = HVS_PIXEL_ORDER_XRGB,
},
{
- .drm = DRM_FORMAT_BGR565, .hvs = HVS_PIXEL_FORMAT_RGB565,
+ .drm = DRM_FORMAT_BGR565,
+ .hvs = HVS_PIXEL_FORMAT_RGB565,
.pixel_order = HVS_PIXEL_ORDER_XBGR,
},
{
- .drm = DRM_FORMAT_ARGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551,
+ .drm = DRM_FORMAT_ARGB1555,
+ .hvs = HVS_PIXEL_FORMAT_RGBA5551,
.pixel_order = HVS_PIXEL_ORDER_ABGR,
},
{
- .drm = DRM_FORMAT_XRGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551,
+ .drm = DRM_FORMAT_XRGB1555,
+ .hvs = HVS_PIXEL_FORMAT_RGBA5551,
.pixel_order = HVS_PIXEL_ORDER_ABGR,
},
{
- .drm = DRM_FORMAT_RGB888, .hvs = HVS_PIXEL_FORMAT_RGB888,
+ .drm = DRM_FORMAT_RGB888,
+ .hvs = HVS_PIXEL_FORMAT_RGB888,
.pixel_order = HVS_PIXEL_ORDER_XRGB,
},
{
- .drm = DRM_FORMAT_BGR888, .hvs = HVS_PIXEL_FORMAT_RGB888,
+ .drm = DRM_FORMAT_BGR888,
+ .hvs = HVS_PIXEL_FORMAT_RGB888,
.pixel_order = HVS_PIXEL_ORDER_XBGR,
},
{
@@ -422,10 +437,7 @@ static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst)
static u32 vc4_lbm_size(struct drm_plane_state *state)
{
struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
- /* This is the worst case number. One of the two sizes will
- * be used depending on the scaling configuration.
- */
- u32 pix_per_line = max(vc4_state->src_w[0], (u32)vc4_state->crtc_w);
+ u32 pix_per_line;
u32 lbm;
/* LBM is not needed when there's no vertical scaling. */
@@ -433,6 +445,18 @@ static u32 vc4_lbm_size(struct drm_plane_state *state)
vc4_state->y_scaling[1] == VC4_SCALING_NONE)
return 0;
+ /*
+ * This can be further optimized in the RGB/YUV444 case if the PPF
+ * decimation factor is between 0.5 and 1.0 by using crtc_w.
+ *
+ * It's not an issue though, since in that case since src_w[0] is going
+ * to be greater than or equal to crtc_w.
+ */
+ if (vc4_state->x_scaling[0] == VC4_SCALING_TPZ)
+ pix_per_line = vc4_state->crtc_w;
+ else
+ pix_per_line = vc4_state->src_w[0];
+
if (!vc4_state->is_yuv) {
if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ)
lbm = pix_per_line * 8;
@@ -492,6 +516,11 @@ static void vc4_plane_calc_load(struct drm_plane_state *state)
struct vc4_plane_state *vc4_state;
struct drm_crtc_state *crtc_state;
unsigned int vscale_factor;
+ struct vc4_dev *vc4;
+
+ vc4 = to_vc4_dev(state->plane->dev);
+ if (!vc4->load_tracker_available)
+ return;
vc4_state = to_vc4_plane_state(state);
crtc_state = drm_atomic_get_existing_crtc_state(state->state,
@@ -563,7 +592,9 @@ static int vc4_plane_allocate_lbm(struct drm_plane_state *state)
spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm,
&vc4_state->lbm,
- lbm_size, 32, 0, 0);
+ lbm_size,
+ vc4->hvs->hvs5 ? 64 : 32,
+ 0, 0);
spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
if (ret)
@@ -776,35 +807,6 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
return -EINVAL;
}
- /* Control word */
- vc4_dlist_write(vc4_state,
- SCALER_CTL0_VALID |
- (rotation & DRM_MODE_REFLECT_X ? SCALER_CTL0_HFLIP : 0) |
- (rotation & DRM_MODE_REFLECT_Y ? SCALER_CTL0_VFLIP : 0) |
- VC4_SET_FIELD(SCALER_CTL0_RGBA_EXPAND_ROUND, SCALER_CTL0_RGBA_EXPAND) |
- (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) |
- (hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
- VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) |
- (vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) |
- VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) |
- VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1));
-
- /* Position Word 0: Image Positions and Alpha Value */
- vc4_state->pos0_offset = vc4_state->dlist_count;
- vc4_dlist_write(vc4_state,
- VC4_SET_FIELD(state->alpha >> 8, SCALER_POS0_FIXED_ALPHA) |
- VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) |
- VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y));
-
- /* Position Word 1: Scaled Image Dimensions. */
- if (!vc4_state->is_unity) {
- vc4_dlist_write(vc4_state,
- VC4_SET_FIELD(vc4_state->crtc_w,
- SCALER_POS1_SCL_WIDTH) |
- VC4_SET_FIELD(vc4_state->crtc_h,
- SCALER_POS1_SCL_HEIGHT));
- }
-
/* Don't waste cycles mixing with plane alpha if the set alpha
* is opaque or there is no per-pixel alpha information.
* In any case we use the alpha property value as the fixed alpha.
@@ -812,20 +814,120 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
mix_plane_alpha = state->alpha != DRM_BLEND_ALPHA_OPAQUE &&
fb->format->has_alpha;
- /* Position Word 2: Source Image Size, Alpha */
- vc4_state->pos2_offset = vc4_state->dlist_count;
- vc4_dlist_write(vc4_state,
- VC4_SET_FIELD(fb->format->has_alpha ?
- SCALER_POS2_ALPHA_MODE_PIPELINE :
- SCALER_POS2_ALPHA_MODE_FIXED,
- SCALER_POS2_ALPHA_MODE) |
- (mix_plane_alpha ? SCALER_POS2_ALPHA_MIX : 0) |
- (fb->format->has_alpha ? SCALER_POS2_ALPHA_PREMULT : 0) |
- VC4_SET_FIELD(vc4_state->src_w[0], SCALER_POS2_WIDTH) |
- VC4_SET_FIELD(vc4_state->src_h[0], SCALER_POS2_HEIGHT));
+ if (!vc4->hvs->hvs5) {
+ /* Control word */
+ vc4_dlist_write(vc4_state,
+ SCALER_CTL0_VALID |
+ (rotation & DRM_MODE_REFLECT_X ? SCALER_CTL0_HFLIP : 0) |
+ (rotation & DRM_MODE_REFLECT_Y ? SCALER_CTL0_VFLIP : 0) |
+ VC4_SET_FIELD(SCALER_CTL0_RGBA_EXPAND_ROUND, SCALER_CTL0_RGBA_EXPAND) |
+ (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) |
+ (hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
+ VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) |
+ (vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) |
+ VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) |
+ VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1));
+
+ /* Position Word 0: Image Positions and Alpha Value */
+ vc4_state->pos0_offset = vc4_state->dlist_count;
+ vc4_dlist_write(vc4_state,
+ VC4_SET_FIELD(state->alpha >> 8, SCALER_POS0_FIXED_ALPHA) |
+ VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) |
+ VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y));
+
+ /* Position Word 1: Scaled Image Dimensions. */
+ if (!vc4_state->is_unity) {
+ vc4_dlist_write(vc4_state,
+ VC4_SET_FIELD(vc4_state->crtc_w,
+ SCALER_POS1_SCL_WIDTH) |
+ VC4_SET_FIELD(vc4_state->crtc_h,
+ SCALER_POS1_SCL_HEIGHT));
+ }
+
+ /* Position Word 2: Source Image Size, Alpha */
+ vc4_state->pos2_offset = vc4_state->dlist_count;
+ vc4_dlist_write(vc4_state,
+ VC4_SET_FIELD(fb->format->has_alpha ?
+ SCALER_POS2_ALPHA_MODE_PIPELINE :
+ SCALER_POS2_ALPHA_MODE_FIXED,
+ SCALER_POS2_ALPHA_MODE) |
+ (mix_plane_alpha ? SCALER_POS2_ALPHA_MIX : 0) |
+ (fb->format->has_alpha ?
+ SCALER_POS2_ALPHA_PREMULT : 0) |
+ VC4_SET_FIELD(vc4_state->src_w[0],
+ SCALER_POS2_WIDTH) |
+ VC4_SET_FIELD(vc4_state->src_h[0],
+ SCALER_POS2_HEIGHT));
+
+ /* Position Word 3: Context. Written by the HVS. */
+ vc4_dlist_write(vc4_state, 0xc0c0c0c0);
- /* Position Word 3: Context. Written by the HVS. */
- vc4_dlist_write(vc4_state, 0xc0c0c0c0);
+ } else {
+ u32 hvs_pixel_order = format->pixel_order;
+
+ if (format->pixel_order_hvs5)
+ hvs_pixel_order = format->pixel_order_hvs5;
+
+ /* Control word */
+ vc4_dlist_write(vc4_state,
+ SCALER_CTL0_VALID |
+ (hvs_pixel_order << SCALER_CTL0_ORDER_SHIFT) |
+ (hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
+ VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) |
+ (vc4_state->is_unity ?
+ SCALER5_CTL0_UNITY : 0) |
+ VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) |
+ VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1) |
+ SCALER5_CTL0_ALPHA_EXPAND |
+ SCALER5_CTL0_RGB_EXPAND);
+
+ /* Position Word 0: Image Positions and Alpha Value */
+ vc4_state->pos0_offset = vc4_state->dlist_count;
+ vc4_dlist_write(vc4_state,
+ (rotation & DRM_MODE_REFLECT_Y ?
+ SCALER5_POS0_VFLIP : 0) |
+ VC4_SET_FIELD(vc4_state->crtc_x,
+ SCALER_POS0_START_X) |
+ (rotation & DRM_MODE_REFLECT_X ?
+ SCALER5_POS0_HFLIP : 0) |
+ VC4_SET_FIELD(vc4_state->crtc_y,
+ SCALER5_POS0_START_Y)
+ );
+
+ /* Control Word 2 */
+ vc4_dlist_write(vc4_state,
+ VC4_SET_FIELD(state->alpha >> 4,
+ SCALER5_CTL2_ALPHA) |
+ (fb->format->has_alpha ?
+ SCALER5_CTL2_ALPHA_PREMULT : 0) |
+ (mix_plane_alpha ?
+ SCALER5_CTL2_ALPHA_MIX : 0) |
+ VC4_SET_FIELD(fb->format->has_alpha ?
+ SCALER5_CTL2_ALPHA_MODE_PIPELINE :
+ SCALER5_CTL2_ALPHA_MODE_FIXED,
+ SCALER5_CTL2_ALPHA_MODE)
+ );
+
+ /* Position Word 1: Scaled Image Dimensions. */
+ if (!vc4_state->is_unity) {
+ vc4_dlist_write(vc4_state,
+ VC4_SET_FIELD(vc4_state->crtc_w,
+ SCALER_POS1_SCL_WIDTH) |
+ VC4_SET_FIELD(vc4_state->crtc_h,
+ SCALER_POS1_SCL_HEIGHT));
+ }
+
+ /* Position Word 2: Source Image Size */
+ vc4_state->pos2_offset = vc4_state->dlist_count;
+ vc4_dlist_write(vc4_state,
+ VC4_SET_FIELD(vc4_state->src_w[0],
+ SCALER5_POS2_WIDTH) |
+ VC4_SET_FIELD(vc4_state->src_h[0],
+ SCALER5_POS2_HEIGHT));
+
+ /* Position Word 3: Context. Written by the HVS. */
+ vc4_dlist_write(vc4_state, 0xc0c0c0c0);
+ }
/* Pointer Word 0/1/2: RGB / Y / Cb / Cr Pointers
@@ -1203,6 +1305,10 @@ static bool vc4_format_mod_supported(struct drm_plane *plane,
default:
return false;
}
+ case DRM_FORMAT_RGBX1010102:
+ case DRM_FORMAT_BGRX1010102:
+ case DRM_FORMAT_RGBA1010102:
+ case DRM_FORMAT_BGRA1010102:
case DRM_FORMAT_YUV422:
case DRM_FORMAT_YVU422:
case DRM_FORMAT_YUV420:
@@ -1255,6 +1361,8 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
&vc4_plane_funcs,
formats, ARRAY_SIZE(formats),
modifiers, type, NULL);
+ if (ret)
+ return ERR_PTR(ret);
drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
@@ -1283,7 +1391,7 @@ int vc4_plane_create_additional_planes(struct drm_device *drm)
* modest number of planes to expose, that should hopefully
* still cover any sane usecase.
*/
- for (i = 0; i < 8; i++) {
+ for (i = 0; i < 16; i++) {
struct drm_plane *plane =
vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY);
diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h
index 324462cc9cd4..be2c32a519b3 100644
--- a/drivers/gpu/drm/vc4/vc4_regs.h
+++ b/drivers/gpu/drm/vc4/vc4_regs.h
@@ -129,6 +129,8 @@
#define V3D_ERRSTAT 0x00f20
#define PV_CONTROL 0x00
+# define PV5_CONTROL_FIFO_LEVEL_HIGH_MASK VC4_MASK(26, 25)
+# define PV5_CONTROL_FIFO_LEVEL_HIGH_SHIFT 25
# define PV_CONTROL_FORMAT_MASK VC4_MASK(23, 21)
# define PV_CONTROL_FORMAT_SHIFT 21
# define PV_CONTROL_FORMAT_24 0
@@ -208,6 +210,11 @@
#define PV_HACT_ACT 0x30
+#define PV_MUX_CFG 0x34
+# define PV_MUX_CFG_RGB_PIXEL_MUX_MODE_MASK VC4_MASK(5, 2)
+# define PV_MUX_CFG_RGB_PIXEL_MUX_MODE_SHIFT 2
+# define PV_MUX_CFG_RGB_PIXEL_MUX_MODE_NO_SWAP 8
+
#define SCALER_CHANNELS_COUNT 3
#define SCALER_DISPCTRL 0x00000000
@@ -286,9 +293,19 @@
#define SCALER_DISPID 0x00000008
#define SCALER_DISPECTRL 0x0000000c
+# define SCALER_DISPECTRL_DSP2_MUX_SHIFT 31
+# define SCALER_DISPECTRL_DSP2_MUX_MASK VC4_MASK(31, 31)
+
#define SCALER_DISPPROF 0x00000010
+
#define SCALER_DISPDITHER 0x00000014
+# define SCALER_DISPDITHER_DSP5_MUX_SHIFT 30
+# define SCALER_DISPDITHER_DSP5_MUX_MASK VC4_MASK(31, 30)
+
#define SCALER_DISPEOLN 0x00000018
+# define SCALER_DISPEOLN_DSP4_MUX_SHIFT 30
+# define SCALER_DISPEOLN_DSP4_MUX_MASK VC4_MASK(31, 30)
+
#define SCALER_DISPLIST0 0x00000020
#define SCALER_DISPLIST1 0x00000024
#define SCALER_DISPLIST2 0x00000028
@@ -327,6 +344,20 @@
# define SCALER_DISPCTRLX_HEIGHT_MASK VC4_MASK(11, 0)
# define SCALER_DISPCTRLX_HEIGHT_SHIFT 0
+# define SCALER5_DISPCTRLX_WIDTH_MASK VC4_MASK(28, 16)
+# define SCALER5_DISPCTRLX_WIDTH_SHIFT 16
+/* Generates a single frame when VSTART is seen and stops at the last
+ * pixel read from the FIFO.
+ */
+# define SCALER5_DISPCTRLX_ONESHOT BIT(15)
+/* Processes a single context in the dlist and then task switch,
+ * instead of an entire line.
+ */
+# define SCALER5_DISPCTRLX_ONECTX_MASK VC4_MASK(14, 13)
+# define SCALER5_DISPCTRLX_ONECTX_SHIFT 13
+# define SCALER5_DISPCTRLX_HEIGHT_MASK VC4_MASK(12, 0)
+# define SCALER5_DISPCTRLX_HEIGHT_SHIFT 0
+
#define SCALER_DISPBKGND0 0x00000044
# define SCALER_DISPBKGND_AUTOHS BIT(31)
# define SCALER_DISPBKGND_INTERLACE BIT(30)
@@ -460,32 +491,18 @@
#define SCALER_DLIST_START 0x00002000
#define SCALER_DLIST_SIZE 0x00004000
-#define VC4_HDMI_CORE_REV 0x000
+#define SCALER5_DLIST_START 0x00004000
-#define VC4_HDMI_SW_RESET_CONTROL 0x004
# define VC4_HDMI_SW_RESET_FORMAT_DETECT BIT(1)
# define VC4_HDMI_SW_RESET_HDMI BIT(0)
-#define VC4_HDMI_HOTPLUG_INT 0x008
-
-#define VC4_HDMI_HOTPLUG 0x00c
# define VC4_HDMI_HOTPLUG_CONNECTED BIT(0)
-/* 3 bits per field, where each field maps from that corresponding MAI
- * bus channel to the given HDMI channel.
- */
-#define VC4_HDMI_MAI_CHANNEL_MAP 0x090
-
-#define VC4_HDMI_MAI_CONFIG 0x094
# define VC4_HDMI_MAI_CONFIG_FORMAT_REVERSE BIT(27)
# define VC4_HDMI_MAI_CONFIG_BIT_REVERSE BIT(26)
# define VC4_HDMI_MAI_CHANNEL_MASK_MASK VC4_MASK(15, 0)
# define VC4_HDMI_MAI_CHANNEL_MASK_SHIFT 0
-/* Last received format word on the MAI bus. */
-#define VC4_HDMI_MAI_FORMAT 0x098
-
-#define VC4_HDMI_AUDIO_PACKET_CONFIG 0x09c
# define VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT BIT(29)
# define VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS BIT(24)
# define VC4_HDMI_AUDIO_PACKET_FORCE_SAMPLE_PRESENT BIT(19)
@@ -499,12 +516,8 @@
# define VC4_HDMI_AUDIO_PACKET_CEA_MASK_MASK VC4_MASK(7, 0)
# define VC4_HDMI_AUDIO_PACKET_CEA_MASK_SHIFT 0
-#define VC4_HDMI_RAM_PACKET_CONFIG 0x0a0
# define VC4_HDMI_RAM_PACKET_ENABLE BIT(16)
-#define VC4_HDMI_RAM_PACKET_STATUS 0x0a4
-
-#define VC4_HDMI_CRP_CFG 0x0a8
/* When set, the CTS_PERIOD counts based on MAI bus sync pulse instead
* of pixel clock.
*/
@@ -518,23 +531,12 @@
# define VC4_HDMI_CRP_CFG_N_MASK VC4_MASK(19, 0)
# define VC4_HDMI_CRP_CFG_N_SHIFT 0
-/* 20-bit fields containing CTS values to be transmitted if !EXTERNAL_CTS_EN */
-#define VC4_HDMI_CTS_0 0x0ac
-#define VC4_HDMI_CTS_1 0x0b0
-/* 20-bit fields containing number of clocks to send CTS0/1 before
- * switching to the other one.
- */
-#define VC4_HDMI_CTS_PERIOD_0 0x0b4
-#define VC4_HDMI_CTS_PERIOD_1 0x0b8
-
-#define VC4_HDMI_HORZA 0x0c4
# define VC4_HDMI_HORZA_VPOS BIT(14)
# define VC4_HDMI_HORZA_HPOS BIT(13)
/* Horizontal active pixels (hdisplay). */
# define VC4_HDMI_HORZA_HAP_MASK VC4_MASK(12, 0)
# define VC4_HDMI_HORZA_HAP_SHIFT 0
-#define VC4_HDMI_HORZB 0x0c8
/* Horizontal pack porch (htotal - hsync_end). */
# define VC4_HDMI_HORZB_HBP_MASK VC4_MASK(29, 20)
# define VC4_HDMI_HORZB_HBP_SHIFT 20
@@ -545,7 +547,6 @@
# define VC4_HDMI_HORZB_HFP_MASK VC4_MASK(9, 0)
# define VC4_HDMI_HORZB_HFP_SHIFT 0
-#define VC4_HDMI_FIFO_CTL 0x05c
# define VC4_HDMI_FIFO_CTL_RECENTER_DONE BIT(14)
# define VC4_HDMI_FIFO_CTL_USE_EMPTY BIT(13)
# define VC4_HDMI_FIFO_CTL_ON_VB BIT(7)
@@ -558,15 +559,12 @@
# define VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N BIT(0)
# define VC4_HDMI_FIFO_VALID_WRITE_MASK 0xefff
-#define VC4_HDMI_SCHEDULER_CONTROL 0x0c0
# define VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT BIT(15)
# define VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS BIT(5)
# define VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT BIT(3)
# define VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE BIT(1)
# define VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI BIT(0)
-#define VC4_HDMI_VERTA0 0x0cc
-#define VC4_HDMI_VERTA1 0x0d4
/* Vertical sync pulse (vsync_end - vsync_start). */
# define VC4_HDMI_VERTA_VSP_MASK VC4_MASK(24, 20)
# define VC4_HDMI_VERTA_VSP_SHIFT 20
@@ -577,8 +575,6 @@
# define VC4_HDMI_VERTA_VAL_MASK VC4_MASK(12, 0)
# define VC4_HDMI_VERTA_VAL_SHIFT 0
-#define VC4_HDMI_VERTB0 0x0d0
-#define VC4_HDMI_VERTB1 0x0d8
/* Vertical sync pulse offset (for interlaced) */
# define VC4_HDMI_VERTB_VSPO_MASK VC4_MASK(21, 9)
# define VC4_HDMI_VERTB_VSPO_SHIFT 9
@@ -586,7 +582,6 @@
# define VC4_HDMI_VERTB_VBP_MASK VC4_MASK(8, 0)
# define VC4_HDMI_VERTB_VBP_SHIFT 0
-#define VC4_HDMI_CEC_CNTRL_1 0x0e8
/* Set when the transmission has ended. */
# define VC4_HDMI_CEC_TX_EOM BIT(31)
/* If set, transmission was acked on the 1st or 2nd attempt (only one
@@ -627,7 +622,6 @@
/* Set these fields to how many bit clock cycles get to that many
* microseconds.
*/
-#define VC4_HDMI_CEC_CNTRL_2 0x0ec
# define VC4_HDMI_CEC_CNT_TO_1500_US_MASK VC4_MASK(30, 24)
# define VC4_HDMI_CEC_CNT_TO_1500_US_SHIFT 24
# define VC4_HDMI_CEC_CNT_TO_1300_US_MASK VC4_MASK(23, 17)
@@ -639,7 +633,6 @@
# define VC4_HDMI_CEC_CNT_TO_400_US_MASK VC4_MASK(4, 0)
# define VC4_HDMI_CEC_CNT_TO_400_US_SHIFT 0
-#define VC4_HDMI_CEC_CNTRL_3 0x0f0
# define VC4_HDMI_CEC_CNT_TO_2750_US_MASK VC4_MASK(31, 24)
# define VC4_HDMI_CEC_CNT_TO_2750_US_SHIFT 24
# define VC4_HDMI_CEC_CNT_TO_2400_US_MASK VC4_MASK(23, 16)
@@ -649,7 +642,6 @@
# define VC4_HDMI_CEC_CNT_TO_1700_US_MASK VC4_MASK(7, 0)
# define VC4_HDMI_CEC_CNT_TO_1700_US_SHIFT 0
-#define VC4_HDMI_CEC_CNTRL_4 0x0f4
# define VC4_HDMI_CEC_CNT_TO_4300_US_MASK VC4_MASK(31, 24)
# define VC4_HDMI_CEC_CNT_TO_4300_US_SHIFT 24
# define VC4_HDMI_CEC_CNT_TO_3900_US_MASK VC4_MASK(23, 16)
@@ -659,7 +651,6 @@
# define VC4_HDMI_CEC_CNT_TO_3500_US_MASK VC4_MASK(7, 0)
# define VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT 0
-#define VC4_HDMI_CEC_CNTRL_5 0x0f8
# define VC4_HDMI_CEC_TX_SW_RESET BIT(27)
# define VC4_HDMI_CEC_RX_SW_RESET BIT(26)
# define VC4_HDMI_CEC_PAD_SW_RESET BIT(25)
@@ -672,39 +663,11 @@
# define VC4_HDMI_CEC_CNT_TO_4500_US_MASK VC4_MASK(7, 0)
# define VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT 0
-/* Transmit data, first byte is low byte of the 32-bit reg. MSB of
- * each byte transmitted first.
- */
-#define VC4_HDMI_CEC_TX_DATA_1 0x0fc
-#define VC4_HDMI_CEC_TX_DATA_2 0x100
-#define VC4_HDMI_CEC_TX_DATA_3 0x104
-#define VC4_HDMI_CEC_TX_DATA_4 0x108
-#define VC4_HDMI_CEC_RX_DATA_1 0x10c
-#define VC4_HDMI_CEC_RX_DATA_2 0x110
-#define VC4_HDMI_CEC_RX_DATA_3 0x114
-#define VC4_HDMI_CEC_RX_DATA_4 0x118
-
-#define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0
-
-#define VC4_HDMI_TX_PHY_CTL0 0x2c4
# define VC4_HDMI_TX_PHY_RNG_PWRDN BIT(25)
-/* Interrupt status bits */
-#define VC4_HDMI_CPU_STATUS 0x340
-#define VC4_HDMI_CPU_SET 0x344
-#define VC4_HDMI_CPU_CLEAR 0x348
# define VC4_HDMI_CPU_CEC BIT(6)
# define VC4_HDMI_CPU_HOTPLUG BIT(0)
-#define VC4_HDMI_CPU_MASK_STATUS 0x34c
-#define VC4_HDMI_CPU_MASK_SET 0x350
-#define VC4_HDMI_CPU_MASK_CLEAR 0x354
-
-#define VC4_HDMI_GCP(x) (0x400 + ((x) * 0x4))
-#define VC4_HDMI_RAM_PACKET(x) (0x400 + ((x) * 0x24))
-#define VC4_HDMI_PACKET_STRIDE 0x24
-
-#define VC4_HD_M_CTL 0x00c
/* Debug: Current receive value on the CEC pad. */
# define VC4_HD_CECRXD BIT(9)
/* Debug: Override CEC output to 0. */
@@ -714,7 +677,6 @@
# define VC4_HD_M_SW_RST BIT(2)
# define VC4_HD_M_ENABLE BIT(0)
-#define VC4_HD_MAI_CTL 0x014
/* Set when audio stream is received at a slower rate than the
* sampling period, so MAI fifo goes empty. Write 1 to clear.
*/
@@ -739,7 +701,6 @@
/* Single-shot reset bit. Read value is undefined. */
# define VC4_HD_MAI_CTL_RESET BIT(0)
-#define VC4_HD_MAI_THR 0x018
# define VC4_HD_MAI_THR_PANICHIGH_MASK VC4_MASK(29, 24)
# define VC4_HD_MAI_THR_PANICHIGH_SHIFT 24
# define VC4_HD_MAI_THR_PANICLOW_MASK VC4_MASK(21, 16)
@@ -749,31 +710,23 @@
# define VC4_HD_MAI_THR_DREQLOW_MASK VC4_MASK(5, 0)
# define VC4_HD_MAI_THR_DREQLOW_SHIFT 0
-/* Format header to be placed on the MAI data. Unused. */
-#define VC4_HD_MAI_FMT 0x01c
-
-/* Register for DMAing in audio data to be transported over the MAI
- * bus to the Falcon core.
- */
-#define VC4_HD_MAI_DATA 0x020
-
/* Divider from HDMI HSM clock to MAI serial clock. Sampling period
* converges to N / (M + 1) cycles.
*/
-#define VC4_HD_MAI_SMP 0x02c
# define VC4_HD_MAI_SMP_N_MASK VC4_MASK(31, 8)
# define VC4_HD_MAI_SMP_N_SHIFT 8
# define VC4_HD_MAI_SMP_M_MASK VC4_MASK(7, 0)
# define VC4_HD_MAI_SMP_M_SHIFT 0
-#define VC4_HD_VID_CTL 0x038
# define VC4_HD_VID_CTL_ENABLE BIT(31)
# define VC4_HD_VID_CTL_UNDERFLOW_ENABLE BIT(30)
# define VC4_HD_VID_CTL_FRAME_COUNTER_RESET BIT(29)
# define VC4_HD_VID_CTL_VSYNC_LOW BIT(28)
# define VC4_HD_VID_CTL_HSYNC_LOW BIT(27)
+# define VC4_HD_VID_CTL_CLRSYNC BIT(24)
+# define VC4_HD_VID_CTL_CLRRGB BIT(23)
+# define VC4_HD_VID_CTL_BLANKPIX BIT(18)
-#define VC4_HD_CSC_CTL 0x040
# define VC4_HD_CSC_CTL_ORDER_MASK VC4_MASK(7, 5)
# define VC4_HD_CSC_CTL_ORDER_SHIFT 5
# define VC4_HD_CSC_CTL_ORDER_RGB 0
@@ -791,14 +744,7 @@
# define VC4_HD_CSC_CTL_RGB2YCC BIT(1)
# define VC4_HD_CSC_CTL_ENABLE BIT(0)
-#define VC4_HD_CSC_12_11 0x044
-#define VC4_HD_CSC_14_13 0x048
-#define VC4_HD_CSC_22_21 0x04c
-#define VC4_HD_CSC_24_23 0x050
-#define VC4_HD_CSC_32_31 0x054
-#define VC4_HD_CSC_34_33 0x058
-
-#define VC4_HD_FRAME_COUNT 0x068
+# define VC4_DVP_HT_CLOCK_STOP_PIXEL BIT(1)
/* HVS display list information. */
#define HVS_BOOTLOADER_DLIST_END 32
@@ -825,6 +771,8 @@ enum hvs_pixel_format {
HVS_PIXEL_FORMAT_PALETTE = 13,
HVS_PIXEL_FORMAT_YUV444_RGB = 14,
HVS_PIXEL_FORMAT_AYUV444_RGB = 15,
+ HVS_PIXEL_FORMAT_RGBA1010102 = 16,
+ HVS_PIXEL_FORMAT_YCBCR_10BIT = 17,
};
/* Note: the LSB is the rightmost character shown. Only valid for
@@ -879,6 +827,10 @@ enum hvs_pixel_format {
#define SCALER_CTL0_RGBA_EXPAND_MSB 2
#define SCALER_CTL0_RGBA_EXPAND_ROUND 3
+#define SCALER5_CTL0_ALPHA_EXPAND BIT(12)
+
+#define SCALER5_CTL0_RGB_EXPAND BIT(11)
+
#define SCALER_CTL0_SCL1_MASK VC4_MASK(10, 8)
#define SCALER_CTL0_SCL1_SHIFT 8
@@ -896,10 +848,13 @@ enum hvs_pixel_format {
/* Set to indicate no scaling. */
#define SCALER_CTL0_UNITY BIT(4)
+#define SCALER5_CTL0_UNITY BIT(15)
#define SCALER_CTL0_PIXEL_FORMAT_MASK VC4_MASK(3, 0)
#define SCALER_CTL0_PIXEL_FORMAT_SHIFT 0
+#define SCALER5_CTL0_PIXEL_FORMAT_MASK VC4_MASK(4, 0)
+
#define SCALER_POS0_FIXED_ALPHA_MASK VC4_MASK(31, 24)
#define SCALER_POS0_FIXED_ALPHA_SHIFT 24
@@ -909,12 +864,48 @@ enum hvs_pixel_format {
#define SCALER_POS0_START_X_MASK VC4_MASK(11, 0)
#define SCALER_POS0_START_X_SHIFT 0
+#define SCALER5_POS0_START_Y_MASK VC4_MASK(27, 16)
+#define SCALER5_POS0_START_Y_SHIFT 16
+
+#define SCALER5_POS0_START_X_MASK VC4_MASK(13, 0)
+#define SCALER5_POS0_START_X_SHIFT 0
+
+#define SCALER5_POS0_VFLIP BIT(31)
+#define SCALER5_POS0_HFLIP BIT(15)
+
+#define SCALER5_CTL2_ALPHA_MODE_MASK VC4_MASK(31, 30)
+#define SCALER5_CTL2_ALPHA_MODE_SHIFT 30
+#define SCALER5_CTL2_ALPHA_MODE_PIPELINE 0
+#define SCALER5_CTL2_ALPHA_MODE_FIXED 1
+#define SCALER5_CTL2_ALPHA_MODE_FIXED_NONZERO 2
+#define SCALER5_CTL2_ALPHA_MODE_FIXED_OVER_0x07 3
+
+#define SCALER5_CTL2_ALPHA_PREMULT BIT(29)
+
+#define SCALER5_CTL2_ALPHA_MIX BIT(28)
+
+#define SCALER5_CTL2_ALPHA_LOC BIT(25)
+
+#define SCALER5_CTL2_MAP_SEL_MASK VC4_MASK(18, 17)
+#define SCALER5_CTL2_MAP_SEL_SHIFT 17
+
+#define SCALER5_CTL2_GAMMA BIT(16)
+
+#define SCALER5_CTL2_ALPHA_MASK VC4_MASK(15, 4)
+#define SCALER5_CTL2_ALPHA_SHIFT 4
+
#define SCALER_POS1_SCL_HEIGHT_MASK VC4_MASK(27, 16)
#define SCALER_POS1_SCL_HEIGHT_SHIFT 16
#define SCALER_POS1_SCL_WIDTH_MASK VC4_MASK(11, 0)
#define SCALER_POS1_SCL_WIDTH_SHIFT 0
+#define SCALER5_POS1_SCL_HEIGHT_MASK VC4_MASK(28, 16)
+#define SCALER5_POS1_SCL_HEIGHT_SHIFT 16
+
+#define SCALER5_POS1_SCL_WIDTH_MASK VC4_MASK(12, 0)
+#define SCALER5_POS1_SCL_WIDTH_SHIFT 0
+
#define SCALER_POS2_ALPHA_MODE_MASK VC4_MASK(31, 30)
#define SCALER_POS2_ALPHA_MODE_SHIFT 30
#define SCALER_POS2_ALPHA_MODE_PIPELINE 0
@@ -930,6 +921,12 @@ enum hvs_pixel_format {
#define SCALER_POS2_WIDTH_MASK VC4_MASK(11, 0)
#define SCALER_POS2_WIDTH_SHIFT 0
+#define SCALER5_POS2_HEIGHT_MASK VC4_MASK(28, 16)
+#define SCALER5_POS2_HEIGHT_SHIFT 16
+
+#define SCALER5_POS2_WIDTH_MASK VC4_MASK(12, 0)
+#define SCALER5_POS2_WIDTH_SHIFT 0
+
/* Color Space Conversion words. Some values are S2.8 signed
* integers, except that the 2 integer bits map as {0x0: 0, 0x1: 1,
* 0x2: 2, 0x3: -1}
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index a7c3af0005a0..849dcafbfff1 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -436,7 +436,6 @@ static const struct drm_crtc_helper_funcs vc4_txp_crtc_helper_funcs = {
.atomic_flush = vc4_hvs_atomic_flush,
.atomic_enable = vc4_txp_atomic_enable,
.atomic_disable = vc4_txp_atomic_disable,
- .mode_set_nofb = vc4_hvs_mode_set_nofb,
};
static irqreturn_t vc4_txp_interrupt(int irq, void *data)
@@ -452,7 +451,8 @@ static irqreturn_t vc4_txp_interrupt(int irq, void *data)
}
static const struct vc4_crtc_data vc4_txp_crtc_data = {
- .hvs_channel = 2,
+ .hvs_available_channels = BIT(2),
+ .hvs_output = 2,
};
static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c
index 313339bbff90..cb884c890065 100644
--- a/drivers/gpu/drm/vgem/vgem_drv.c
+++ b/drivers/gpu/drm/vgem/vgem_drv.c
@@ -321,7 +321,7 @@ static struct sg_table *vgem_prime_get_sg_table(struct drm_gem_object *obj)
{
struct drm_vgem_gem_object *bo = to_vgem_bo(obj);
- return drm_prime_pages_to_sg(bo->pages, bo->base.size >> PAGE_SHIFT);
+ return drm_prime_pages_to_sg(obj->dev, bo->pages, bo->base.size >> PAGE_SHIFT);
}
static struct drm_gem_object* vgem_prime_import(struct drm_device *dev,
@@ -401,16 +401,8 @@ static int vgem_prime_mmap(struct drm_gem_object *obj,
return 0;
}
-static void vgem_release(struct drm_device *dev)
-{
- struct vgem_device *vgem = container_of(dev, typeof(*vgem), drm);
-
- platform_device_unregister(vgem->platform);
-}
-
static struct drm_driver vgem_driver = {
.driver_features = DRIVER_GEM | DRIVER_RENDER,
- .release = vgem_release,
.open = vgem_open,
.postclose = vgem_postclose,
.gem_free_object_unlocked = vgem_gem_free_object,
@@ -442,48 +434,49 @@ static struct drm_driver vgem_driver = {
static int __init vgem_init(void)
{
int ret;
+ struct platform_device *pdev;
- vgem_device = kzalloc(sizeof(*vgem_device), GFP_KERNEL);
- if (!vgem_device)
- return -ENOMEM;
+ pdev = platform_device_register_simple("vgem", -1, NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
- vgem_device->platform =
- platform_device_register_simple("vgem", -1, NULL, 0);
- if (IS_ERR(vgem_device->platform)) {
- ret = PTR_ERR(vgem_device->platform);
- goto out_free;
+ if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) {
+ ret = -ENOMEM;
+ goto out_unregister;
}
- dma_coerce_mask_and_coherent(&vgem_device->platform->dev,
+ dma_coerce_mask_and_coherent(&pdev->dev,
DMA_BIT_MASK(64));
- ret = drm_dev_init(&vgem_device->drm, &vgem_driver,
- &vgem_device->platform->dev);
- if (ret)
- goto out_unregister;
- drmm_add_final_kfree(&vgem_device->drm, vgem_device);
+
+ vgem_device = devm_drm_dev_alloc(&pdev->dev, &vgem_driver,
+ struct vgem_device, drm);
+ if (IS_ERR(vgem_device)) {
+ ret = PTR_ERR(vgem_device);
+ goto out_devres;
+ }
+ vgem_device->platform = pdev;
/* Final step: expose the device/driver to userspace */
ret = drm_dev_register(&vgem_device->drm, 0);
if (ret)
- goto out_put;
+ goto out_devres;
return 0;
-out_put:
- drm_dev_put(&vgem_device->drm);
- platform_device_unregister(vgem_device->platform);
- return ret;
+out_devres:
+ devres_release_group(&pdev->dev, NULL);
out_unregister:
- platform_device_unregister(vgem_device->platform);
-out_free:
- kfree(vgem_device);
+ platform_device_unregister(pdev);
return ret;
}
static void __exit vgem_exit(void)
{
+ struct platform_device *pdev = vgem_device->platform;
+
drm_dev_unregister(&vgem_device->drm);
- drm_dev_put(&vgem_device->drm);
+ devres_release_group(&pdev->dev, NULL);
+ platform_device_unregister(pdev);
}
module_init(vgem_init);
diff --git a/drivers/gpu/drm/virtio/virtgpu_debugfs.c b/drivers/gpu/drm/virtio/virtgpu_debugfs.c
index 3221520f61f0..d5b0c543bd6d 100644
--- a/drivers/gpu/drm/virtio/virtgpu_debugfs.c
+++ b/drivers/gpu/drm/virtio/virtgpu_debugfs.c
@@ -48,6 +48,7 @@ static int virtio_gpu_features(struct seq_file *m, void *data)
virtio_add_bool(m, "virgl", vgdev->has_virgl_3d);
virtio_add_bool(m, "edid", vgdev->has_edid);
virtio_add_bool(m, "indirect", vgdev->has_indirect);
+ virtio_add_bool(m, "resource uuid", vgdev->has_resource_assign_uuid);
virtio_add_int(m, "cap sets", vgdev->num_capsets);
virtio_add_int(m, "scanouts", vgdev->num_scanouts);
return 0;
diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c
index effea07abe62..f84b7e61311b 100644
--- a/drivers/gpu/drm/virtio/virtgpu_display.c
+++ b/drivers/gpu/drm/virtio/virtgpu_display.c
@@ -325,11 +325,14 @@ static const struct drm_mode_config_funcs virtio_gpu_mode_funcs = {
.atomic_commit = drm_atomic_helper_commit,
};
-void virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev)
+int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev)
{
- int i;
+ int i, ret;
+
+ ret = drmm_mode_config_init(vgdev->ddev);
+ if (ret)
+ return ret;
- drm_mode_config_init(vgdev->ddev);
vgdev->ddev->mode_config.quirk_addfb_prefer_host_byte_order = true;
vgdev->ddev->mode_config.funcs = &virtio_gpu_mode_funcs;
@@ -343,6 +346,7 @@ void virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev)
vgdev_output_init(vgdev, i);
drm_mode_config_reset(vgdev->ddev);
+ return 0;
}
void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev)
@@ -351,5 +355,4 @@ void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev)
for (i = 0 ; i < vgdev->num_scanouts; ++i)
kfree(vgdev->outputs[i].edid);
- drm_mode_config_cleanup(vgdev->ddev);
}
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index a52b7a39f286..55c34b4fc3e9 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -352,7 +352,7 @@ virtio_gpu_cmd_resource_assign_uuid(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object_array *objs);
/* virtgpu_display.c */
-void virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev);
+int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev);
void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev);
/* virtgpu_plane.c */
diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
index 75d0dc2f6d28..eed57a931309 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -80,8 +80,10 @@ static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev,
vgdev->capsets[i].id > 0, 5 * HZ);
if (ret == 0) {
DRM_ERROR("timed out waiting for cap set %d\n", i);
+ spin_lock(&vgdev->display_info_lock);
kfree(vgdev->capsets);
vgdev->capsets = NULL;
+ spin_unlock(&vgdev->display_info_lock);
return;
}
DRM_INFO("cap set %d: id %d, max-version %d, max-size %d\n",
@@ -103,7 +105,7 @@ int virtio_gpu_init(struct drm_device *dev)
/* this will expand later */
struct virtqueue *vqs[2];
u32 num_scanouts, num_capsets;
- int ret;
+ int ret = 0;
if (!virtio_has_feature(dev_to_virtio(dev->dev), VIRTIO_F_VERSION_1))
return -ENODEV;
@@ -184,7 +186,11 @@ int virtio_gpu_init(struct drm_device *dev)
num_capsets, &num_capsets);
DRM_INFO("number of cap sets: %d\n", num_capsets);
- virtio_gpu_modeset_init(vgdev);
+ ret = virtio_gpu_modeset_init(vgdev);
+ if (ret) {
+ DRM_ERROR("modeset init failed\n");
+ goto err_scanouts;
+ }
virtio_device_ready(vgdev->vdev);
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index 651d1b0e8e8d..07945ca238e2 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -320,13 +320,13 @@ static struct sg_table *vmalloc_to_sgt(char *data, uint32_t size, int *sg_ents)
return sgt;
}
-static void virtio_gpu_queue_ctrl_sgs(struct virtio_gpu_device *vgdev,
- struct virtio_gpu_vbuffer *vbuf,
- struct virtio_gpu_fence *fence,
- int elemcnt,
- struct scatterlist **sgs,
- int outcnt,
- int incnt)
+static int virtio_gpu_queue_ctrl_sgs(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_vbuffer *vbuf,
+ struct virtio_gpu_fence *fence,
+ int elemcnt,
+ struct scatterlist **sgs,
+ int outcnt,
+ int incnt)
{
struct virtqueue *vq = vgdev->ctrlq.vq;
int ret, idx;
@@ -335,7 +335,7 @@ static void virtio_gpu_queue_ctrl_sgs(struct virtio_gpu_device *vgdev,
if (fence && vbuf->objs)
virtio_gpu_array_unlock_resv(vbuf->objs);
free_vbuf(vgdev, vbuf);
- return;
+ return -1;
}
if (vgdev->has_indirect)
@@ -373,15 +373,16 @@ again:
spin_unlock(&vgdev->ctrlq.qlock);
drm_dev_exit(idx);
+ return 0;
}
-static void virtio_gpu_queue_fenced_ctrl_buffer(struct virtio_gpu_device *vgdev,
- struct virtio_gpu_vbuffer *vbuf,
- struct virtio_gpu_fence *fence)
+static int virtio_gpu_queue_fenced_ctrl_buffer(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_vbuffer *vbuf,
+ struct virtio_gpu_fence *fence)
{
struct scatterlist *sgs[3], vcmd, vout, vresp;
struct sg_table *sgt = NULL;
- int elemcnt = 0, outcnt = 0, incnt = 0;
+ int elemcnt = 0, outcnt = 0, incnt = 0, ret;
/* set up vcmd */
sg_init_one(&vcmd, vbuf->buf, vbuf->size);
@@ -398,7 +399,7 @@ static void virtio_gpu_queue_fenced_ctrl_buffer(struct virtio_gpu_device *vgdev,
if (!sgt) {
if (fence && vbuf->objs)
virtio_gpu_array_unlock_resv(vbuf->objs);
- return;
+ return -1;
}
elemcnt += sg_ents;
@@ -419,13 +420,14 @@ static void virtio_gpu_queue_fenced_ctrl_buffer(struct virtio_gpu_device *vgdev,
incnt++;
}
- virtio_gpu_queue_ctrl_sgs(vgdev, vbuf, fence, elemcnt, sgs, outcnt,
- incnt);
+ ret = virtio_gpu_queue_ctrl_sgs(vgdev, vbuf, fence, elemcnt, sgs, outcnt,
+ incnt);
if (sgt) {
sg_free_table(sgt);
kfree(sgt);
}
+ return ret;
}
void virtio_gpu_notify(struct virtio_gpu_device *vgdev)
@@ -444,10 +446,10 @@ void virtio_gpu_notify(struct virtio_gpu_device *vgdev)
virtqueue_notify(vgdev->ctrlq.vq);
}
-static void virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev,
- struct virtio_gpu_vbuffer *vbuf)
+static int virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_vbuffer *vbuf)
{
- virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, NULL);
+ return virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, NULL);
}
static void virtio_gpu_queue_cursor(struct virtio_gpu_device *vgdev,
@@ -534,6 +536,7 @@ void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
{
struct virtio_gpu_resource_unref *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
+ int ret;
cmd_p = virtio_gpu_alloc_cmd_cb(vgdev, &vbuf, sizeof(*cmd_p),
virtio_gpu_cmd_unref_cb);
@@ -543,7 +546,9 @@ void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
cmd_p->resource_id = cpu_to_le32(bo->hw_res_handle);
vbuf->resp_cb_data = bo;
- virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+ ret = virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+ if (ret < 0)
+ virtio_gpu_cleanup_object(bo);
}
void virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev,
@@ -683,9 +688,13 @@ static void virtio_gpu_cmd_get_capset_info_cb(struct virtio_gpu_device *vgdev,
int i = le32_to_cpu(cmd->capset_index);
spin_lock(&vgdev->display_info_lock);
- vgdev->capsets[i].id = le32_to_cpu(resp->capset_id);
- vgdev->capsets[i].max_version = le32_to_cpu(resp->capset_max_version);
- vgdev->capsets[i].max_size = le32_to_cpu(resp->capset_max_size);
+ if (vgdev->capsets) {
+ vgdev->capsets[i].id = le32_to_cpu(resp->capset_id);
+ vgdev->capsets[i].max_version = le32_to_cpu(resp->capset_max_version);
+ vgdev->capsets[i].max_size = le32_to_cpu(resp->capset_max_size);
+ } else {
+ DRM_ERROR("invalid capset memory.");
+ }
spin_unlock(&vgdev->display_info_lock);
wake_up(&vgdev->resp_wq);
}
diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile
index 0b767d7efa24..333d3cead0e3 100644
--- a/drivers/gpu/drm/vkms/Makefile
+++ b/drivers/gpu/drm/vkms/Makefile
@@ -1,4 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
-vkms-y := vkms_drv.o vkms_plane.o vkms_output.o vkms_crtc.o vkms_gem.o vkms_composer.o
+vkms-y := \
+ vkms_drv.o \
+ vkms_plane.o \
+ vkms_output.o \
+ vkms_crtc.o \
+ vkms_gem.o \
+ vkms_composer.o \
+ vkms_writeback.o
obj-$(CONFIG_DRM_VKMS) += vkms.o
diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c
index eaecc5a6c5db..33c031f27c2c 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -9,31 +9,41 @@
#include "vkms_drv.h"
+static u32 get_pixel_from_buffer(int x, int y, const u8 *buffer,
+ const struct vkms_composer *composer)
+{
+ u32 pixel;
+ int src_offset = composer->offset + (y * composer->pitch)
+ + (x * composer->cpp);
+
+ pixel = *(u32 *)&buffer[src_offset];
+
+ return pixel;
+}
+
/**
* compute_crc - Compute CRC value on output frame
*
- * @vaddr_out: address to final framebuffer
+ * @vaddr: address to final framebuffer
* @composer: framebuffer's metadata
*
* returns CRC value computed using crc32 on the visible portion of
* the final framebuffer at vaddr_out
*/
-static uint32_t compute_crc(void *vaddr_out, struct vkms_composer *composer)
+static uint32_t compute_crc(const u8 *vaddr,
+ const struct vkms_composer *composer)
{
- int i, j, src_offset;
+ int x, y;
+ u32 crc = 0, pixel = 0;
int x_src = composer->src.x1 >> 16;
int y_src = composer->src.y1 >> 16;
int h_src = drm_rect_height(&composer->src) >> 16;
int w_src = drm_rect_width(&composer->src) >> 16;
- u32 crc = 0;
-
- for (i = y_src; i < y_src + h_src; ++i) {
- for (j = x_src; j < x_src + w_src; ++j) {
- src_offset = composer->offset
- + (i * composer->pitch)
- + (j * composer->cpp);
- crc = crc32_le(crc, vaddr_out + src_offset,
- sizeof(u32));
+
+ for (y = y_src; y < y_src + h_src; ++y) {
+ for (x = x_src; x < x_src + w_src; ++x) {
+ pixel = get_pixel_from_buffer(x, y, vaddr, composer);
+ crc = crc32_le(crc, (void *)&pixel, sizeof(u32));
}
}
@@ -131,35 +141,31 @@ static void compose_cursor(struct vkms_composer *cursor_composer,
primary_composer, cursor_composer);
}
-static uint32_t _vkms_get_crc(struct vkms_composer *primary_composer,
- struct vkms_composer *cursor_composer)
+static int compose_planes(void **vaddr_out,
+ struct vkms_composer *primary_composer,
+ struct vkms_composer *cursor_composer)
{
struct drm_framebuffer *fb = &primary_composer->fb;
struct drm_gem_object *gem_obj = drm_gem_fb_get_obj(fb, 0);
struct vkms_gem_object *vkms_obj = drm_gem_to_vkms_gem(gem_obj);
- void *vaddr_out = kzalloc(vkms_obj->gem.size, GFP_KERNEL);
- u32 crc = 0;
- if (!vaddr_out) {
- DRM_ERROR("Failed to allocate memory for output frame.");
- return 0;
+ if (!*vaddr_out) {
+ *vaddr_out = kzalloc(vkms_obj->gem.size, GFP_KERNEL);
+ if (!*vaddr_out) {
+ DRM_ERROR("Cannot allocate memory for output frame.");
+ return -ENOMEM;
+ }
}
- if (WARN_ON(!vkms_obj->vaddr)) {
- kfree(vaddr_out);
- return crc;
- }
+ if (WARN_ON(!vkms_obj->vaddr))
+ return -EINVAL;
- memcpy(vaddr_out, vkms_obj->vaddr, vkms_obj->gem.size);
+ memcpy(*vaddr_out, vkms_obj->vaddr, vkms_obj->gem.size);
if (cursor_composer)
- compose_cursor(cursor_composer, primary_composer, vaddr_out);
-
- crc = compute_crc(vaddr_out, primary_composer);
-
- kfree(vaddr_out);
+ compose_cursor(cursor_composer, primary_composer, *vaddr_out);
- return crc;
+ return 0;
}
/**
@@ -180,14 +186,17 @@ void vkms_composer_worker(struct work_struct *work)
struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
struct vkms_composer *primary_composer = NULL;
struct vkms_composer *cursor_composer = NULL;
+ bool crc_pending, wb_pending;
+ void *vaddr_out = NULL;
u32 crc32 = 0;
u64 frame_start, frame_end;
- bool crc_pending;
+ int ret;
spin_lock_irq(&out->composer_lock);
frame_start = crtc_state->frame_start;
frame_end = crtc_state->frame_end;
crc_pending = crtc_state->crc_pending;
+ wb_pending = crtc_state->wb_pending;
crtc_state->frame_start = 0;
crtc_state->frame_end = 0;
crtc_state->crc_pending = false;
@@ -206,8 +215,29 @@ void vkms_composer_worker(struct work_struct *work)
if (crtc_state->num_active_planes == 2)
cursor_composer = crtc_state->active_planes[1]->composer;
- if (primary_composer)
- crc32 = _vkms_get_crc(primary_composer, cursor_composer);
+ if (!primary_composer)
+ return;
+
+ if (wb_pending)
+ vaddr_out = crtc_state->active_writeback;
+
+ ret = compose_planes(&vaddr_out, primary_composer, cursor_composer);
+ if (ret) {
+ if (ret == -EINVAL && !wb_pending)
+ kfree(vaddr_out);
+ return;
+ }
+
+ crc32 = compute_crc(vaddr_out, primary_composer);
+
+ if (wb_pending) {
+ drm_writeback_signal_completion(&out->wb_connector, 0);
+ spin_lock_irq(&out->composer_lock);
+ crtc_state->wb_pending = false;
+ spin_unlock_irq(&out->composer_lock);
+ } else {
+ kfree(vaddr_out);
+ }
/*
* The worker can fall behind the vblank hrtimer, make sure we catch up.
@@ -256,7 +286,7 @@ int vkms_verify_crc_source(struct drm_crtc *crtc, const char *src_name,
return 0;
}
-static void vkms_set_composer(struct vkms_output *out, bool enabled)
+void vkms_set_composer(struct vkms_output *out, bool enabled)
{
bool old_enabled;
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
index 83dd5567de8b..cb0b6230c22c 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.c
+++ b/drivers/gpu/drm/vkms/vkms_drv.c
@@ -61,9 +61,6 @@ static void vkms_release(struct drm_device *dev)
{
struct vkms_device *vkms = container_of(dev, struct vkms_device, drm);
- platform_device_unregister(vkms->platform);
- drm_atomic_helper_shutdown(&vkms->drm);
- drm_mode_config_cleanup(&vkms->drm);
destroy_workqueue(vkms->output.composer_workq);
}
@@ -144,30 +141,31 @@ static int vkms_modeset_init(struct vkms_device *vkmsdev)
static int __init vkms_init(void)
{
int ret;
+ struct platform_device *pdev;
- vkms_device = kzalloc(sizeof(*vkms_device), GFP_KERNEL);
- if (!vkms_device)
- return -ENOMEM;
+ pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
- vkms_device->platform =
- platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
- if (IS_ERR(vkms_device->platform)) {
- ret = PTR_ERR(vkms_device->platform);
- goto out_free;
+ if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) {
+ ret = -ENOMEM;
+ goto out_unregister;
}
- ret = drm_dev_init(&vkms_device->drm, &vkms_driver,
- &vkms_device->platform->dev);
- if (ret)
- goto out_unregister;
- drmm_add_final_kfree(&vkms_device->drm, vkms_device);
+ vkms_device = devm_drm_dev_alloc(&pdev->dev, &vkms_driver,
+ struct vkms_device, drm);
+ if (IS_ERR(vkms_device)) {
+ ret = PTR_ERR(vkms_device);
+ goto out_devres;
+ }
+ vkms_device->platform = pdev;
ret = dma_coerce_mask_and_coherent(vkms_device->drm.dev,
DMA_BIT_MASK(64));
if (ret) {
DRM_ERROR("Could not initialize DMA support\n");
- goto out_put;
+ goto out_devres;
}
vkms_device->drm.irq_enabled = true;
@@ -175,39 +173,41 @@ static int __init vkms_init(void)
ret = drm_vblank_init(&vkms_device->drm, 1);
if (ret) {
DRM_ERROR("Failed to vblank\n");
- goto out_put;
+ goto out_devres;
}
ret = vkms_modeset_init(vkms_device);
if (ret)
- goto out_put;
+ goto out_devres;
ret = drm_dev_register(&vkms_device->drm, 0);
if (ret)
- goto out_put;
+ goto out_devres;
return 0;
-out_put:
- drm_dev_put(&vkms_device->drm);
- platform_device_unregister(vkms_device->platform);
- return ret;
+out_devres:
+ devres_release_group(&pdev->dev, NULL);
out_unregister:
- platform_device_unregister(vkms_device->platform);
-out_free:
- kfree(vkms_device);
+ platform_device_unregister(pdev);
return ret;
}
static void __exit vkms_exit(void)
{
+ struct platform_device *pdev;
+
if (!vkms_device) {
DRM_INFO("vkms_device is NULL.\n");
return;
}
+ pdev = vkms_device->platform;
+
drm_dev_unregister(&vkms_device->drm);
- drm_dev_put(&vkms_device->drm);
+ drm_atomic_helper_shutdown(&vkms_device->drm);
+ devres_release_group(&pdev->dev, NULL);
+ platform_device_unregister(pdev);
}
module_init(vkms_init);
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index f4036bb0b9a8..380a8f27e156 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -8,6 +8,7 @@
#include <drm/drm.h>
#include <drm/drm_gem.h>
#include <drm/drm_encoder.h>
+#include <drm/drm_writeback.h>
#define XRES_MIN 20
#define YRES_MIN 20
@@ -52,9 +53,11 @@ struct vkms_crtc_state {
int num_active_planes;
/* stack of active planes for crc computation, should be in z order */
struct vkms_plane_state **active_planes;
+ void *active_writeback;
- /* below three are protected by vkms_output.composer_lock */
+ /* below four are protected by vkms_output.composer_lock */
bool crc_pending;
+ bool wb_pending;
u64 frame_start;
u64 frame_end;
};
@@ -63,6 +66,7 @@ struct vkms_output {
struct drm_crtc crtc;
struct drm_encoder encoder;
struct drm_connector connector;
+ struct drm_writeback_connector wb_connector;
struct hrtimer vblank_hrtimer;
ktime_t period_ns;
struct drm_pending_vblank_event *event;
@@ -143,5 +147,9 @@ int vkms_verify_crc_source(struct drm_crtc *crtc, const char *source_name,
/* Composer Support */
void vkms_composer_worker(struct work_struct *work);
+void vkms_set_composer(struct vkms_output *out, bool enabled);
+
+/* Writeback */
+int vkms_enable_writeback_connector(struct vkms_device *vkmsdev);
#endif /* _VKMS_DRV_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
index 85afb77e97f0..4a1848b0318f 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -80,6 +80,10 @@ int vkms_output_init(struct vkms_device *vkmsdev, int index)
goto err_attach;
}
+ ret = vkms_enable_writeback_connector(vkmsdev);
+ if (ret)
+ DRM_ERROR("Failed to init writeback connector\n");
+
drm_mode_config_reset(dev);
return 0;
diff --git a/drivers/gpu/drm/vkms/vkms_writeback.c b/drivers/gpu/drm/vkms/vkms_writeback.c
new file mode 100644
index 000000000000..094fa4aa061d
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_writeback.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include "vkms_drv.h"
+#include <drm/drm_fourcc.h>
+#include <drm/drm_writeback.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+
+static const u32 vkms_wb_formats[] = {
+ DRM_FORMAT_XRGB8888,
+};
+
+static const struct drm_connector_funcs vkms_wb_connector_funcs = {
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = drm_connector_cleanup,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int vkms_wb_encoder_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct drm_framebuffer *fb;
+ const struct drm_display_mode *mode = &crtc_state->mode;
+
+ if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
+ return 0;
+
+ fb = conn_state->writeback_job->fb;
+ if (fb->width != mode->hdisplay || fb->height != mode->vdisplay) {
+ DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n",
+ fb->width, fb->height);
+ return -EINVAL;
+ }
+
+ if (fb->format->format != vkms_wb_formats[0]) {
+ struct drm_format_name_buf format_name;
+
+ DRM_DEBUG_KMS("Invalid pixel format %s\n",
+ drm_get_format_name(fb->format->format,
+ &format_name));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct drm_encoder_helper_funcs vkms_wb_encoder_helper_funcs = {
+ .atomic_check = vkms_wb_encoder_atomic_check,
+};
+
+static int vkms_wb_connector_get_modes(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+
+ return drm_add_modes_noedid(connector, dev->mode_config.max_width,
+ dev->mode_config.max_height);
+}
+
+static int vkms_wb_prepare_job(struct drm_writeback_connector *wb_connector,
+ struct drm_writeback_job *job)
+{
+ struct vkms_gem_object *vkms_obj;
+ struct drm_gem_object *gem_obj;
+ int ret;
+
+ if (!job->fb)
+ return 0;
+
+ gem_obj = drm_gem_fb_get_obj(job->fb, 0);
+ ret = vkms_gem_vmap(gem_obj);
+ if (ret) {
+ DRM_ERROR("vmap failed: %d\n", ret);
+ return ret;
+ }
+
+ vkms_obj = drm_gem_to_vkms_gem(gem_obj);
+ job->priv = vkms_obj->vaddr;
+
+ return 0;
+}
+
+static void vkms_wb_cleanup_job(struct drm_writeback_connector *connector,
+ struct drm_writeback_job *job)
+{
+ struct drm_gem_object *gem_obj;
+ struct vkms_device *vkmsdev;
+
+ if (!job->fb)
+ return;
+
+ gem_obj = drm_gem_fb_get_obj(job->fb, 0);
+ vkms_gem_vunmap(gem_obj);
+
+ vkmsdev = drm_device_to_vkms_device(gem_obj->dev);
+ vkms_set_composer(&vkmsdev->output, false);
+}
+
+static void vkms_wb_atomic_commit(struct drm_connector *conn,
+ struct drm_connector_state *state)
+{
+ struct vkms_device *vkmsdev = drm_device_to_vkms_device(conn->dev);
+ struct vkms_output *output = &vkmsdev->output;
+ struct drm_writeback_connector *wb_conn = &output->wb_connector;
+ struct drm_connector_state *conn_state = wb_conn->base.state;
+ struct vkms_crtc_state *crtc_state = output->composer_state;
+
+ if (!conn_state)
+ return;
+
+ vkms_set_composer(&vkmsdev->output, true);
+
+ spin_lock_irq(&output->composer_lock);
+ crtc_state->active_writeback = conn_state->writeback_job->priv;
+ crtc_state->wb_pending = true;
+ spin_unlock_irq(&output->composer_lock);
+ drm_writeback_queue_job(wb_conn, state);
+}
+
+static const struct drm_connector_helper_funcs vkms_wb_conn_helper_funcs = {
+ .get_modes = vkms_wb_connector_get_modes,
+ .prepare_writeback_job = vkms_wb_prepare_job,
+ .cleanup_writeback_job = vkms_wb_cleanup_job,
+ .atomic_commit = vkms_wb_atomic_commit,
+};
+
+int vkms_enable_writeback_connector(struct vkms_device *vkmsdev)
+{
+ struct drm_writeback_connector *wb = &vkmsdev->output.wb_connector;
+
+ vkmsdev->output.wb_connector.encoder.possible_crtcs = 1;
+ drm_connector_helper_add(&wb->base, &vkms_wb_conn_helper_funcs);
+
+ return drm_writeback_connector_init(&vkmsdev->drm, wb,
+ &vkms_wb_connector_funcs,
+ &vkms_wb_encoder_helper_funcs,
+ vkms_wb_formats,
+ ARRAY_SIZE(vkms_wb_formats));
+}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
index 1629427d5734..e8d66182cd7b 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
@@ -464,14 +464,14 @@ int vmw_bo_cpu_blit(struct ttm_buffer_object *dst,
if (!(src->mem.placement & TTM_PL_FLAG_NO_EVICT))
dma_resv_assert_held(src->base.resv);
- if (dst->ttm->state == tt_unpopulated) {
- ret = dst->ttm->bdev->driver->ttm_tt_populate(dst->ttm, &ctx);
+ if (!ttm_tt_is_populated(dst->ttm)) {
+ ret = dst->bdev->driver->ttm_tt_populate(dst->bdev, dst->ttm, &ctx);
if (ret)
return ret;
}
- if (src->ttm->state == tt_unpopulated) {
- ret = src->ttm->bdev->driver->ttm_tt_populate(src->ttm, &ctx);
+ if (!ttm_tt_is_populated(src->ttm)) {
+ ret = src->bdev->driver->ttm_tt_populate(src->bdev, src->ttm, &ctx);
if (ret)
return ret;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
index 3229451d0706..813f1b148094 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
@@ -354,10 +354,12 @@ void vmw_bo_pin_reserved(struct vmw_buffer_object *vbo, bool pin)
pl.fpfn = 0;
pl.lpfn = 0;
- pl.flags = TTM_PL_FLAG_VRAM | VMW_PL_FLAG_GMR | VMW_PL_FLAG_MOB
- | TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED;
+ pl.mem_type = bo->mem.mem_type;
+ pl.flags = bo->mem.placement;
if (pin)
pl.flags |= TTM_PL_FLAG_NO_EVICT;
+ else
+ pl.flags &= ~TTM_PL_FLAG_NO_EVICT;
memset(&placement, 0, sizeof(placement));
placement.num_placement = 1;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index e60012886065..31e3e5c9f362 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -626,9 +626,8 @@ static int vmw_vram_manager_init(struct vmw_private *dev_priv)
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
ret = vmw_thp_init(dev_priv);
#else
- ret = ttm_range_man_init(&dev_priv->bdev, TTM_PL_VRAM,
- TTM_PL_FLAG_CACHED, TTM_PL_FLAG_CACHED,
- false, dev_priv->vram_size >> PAGE_SHIFT);
+ ret = ttm_range_man_init(&dev_priv->bdev, TTM_PL_VRAM, false,
+ dev_priv->vram_size >> PAGE_SHIFT);
#endif
ttm_resource_manager_set_used(ttm_manager_type(&dev_priv->bdev, TTM_PL_VRAM), false);
return ret;
@@ -882,8 +881,6 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
DRM_ERROR("Failed initializing TTM buffer object driver.\n");
goto out_no_bdev;
}
- ttm_manager_type(&dev_priv->bdev, TTM_PL_SYSTEM)->available_caching =
- TTM_PL_FLAG_CACHED;
/*
* Enable VRAM, but initially don't use it until SVGA is enabled and
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
index 871ad738dadb..1523b51a7284 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
@@ -82,9 +82,7 @@
VMWGFX_NUM_GB_SCREEN_TARGET)
#define VMW_PL_GMR (TTM_PL_PRIV + 0)
-#define VMW_PL_FLAG_GMR (TTM_PL_FLAG_PRIV << 0)
#define VMW_PL_MOB (TTM_PL_PRIV + 1)
-#define VMW_PL_FLAG_MOB (TTM_PL_FLAG_PRIV << 1)
#define VMW_RES_CONTEXT ttm_driver_type0
#define VMW_RES_SURFACE ttm_driver_type1
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
index bb76acb5b0fc..db64c3a90285 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
@@ -112,8 +112,6 @@ int vmw_gmrid_man_init(struct vmw_private *dev_priv, int type)
man = &gman->manager;
man->func = &vmw_gmrid_manager_func;
- man->available_caching = TTM_PL_FLAG_CACHED;
- man->default_caching = TTM_PL_FLAG_CACHED;
/* TODO: This is most likely not correct */
man->use_tt = true;
ttm_resource_manager_init(man, 0);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_thp.c b/drivers/gpu/drm/vmwgfx/vmwgfx_thp.c
index 3c00a9e7cfcc..63fe7da4cbf4 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_thp.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_thp.c
@@ -123,25 +123,20 @@ static void vmw_thp_put_node(struct ttm_resource_manager *man,
int vmw_thp_init(struct vmw_private *dev_priv)
{
- struct ttm_resource_manager *man;
struct vmw_thp_manager *rman;
rman = kzalloc(sizeof(*rman), GFP_KERNEL);
if (!rman)
return -ENOMEM;
- man = &rman->manager;
- man->available_caching = TTM_PL_FLAG_CACHED;
- man->default_caching = TTM_PL_FLAG_CACHED;
-
- ttm_resource_manager_init(man,
+ ttm_resource_manager_init(&rman->manager,
dev_priv->vram_size >> PAGE_SHIFT);
- drm_mm_init(&rman->mm, 0, man->size);
+ drm_mm_init(&rman->mm, 0, rman->manager.size);
spin_lock_init(&rman->lock);
ttm_set_driver_manager(&dev_priv->bdev, TTM_PL_VRAM, &rman->manager);
- ttm_resource_manager_set_used(man, true);
+ ttm_resource_manager_set_used(&rman->manager, true);
return 0;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c
index 13c31e2d7254..7f0310441da1 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c
@@ -33,49 +33,57 @@
static const struct ttm_place vram_placement_flags = {
.fpfn = 0,
.lpfn = 0,
- .flags = TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED
+ .mem_type = TTM_PL_VRAM,
+ .flags = TTM_PL_FLAG_CACHED
};
static const struct ttm_place vram_ne_placement_flags = {
.fpfn = 0,
.lpfn = 0,
- .flags = TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT
+ .mem_type = TTM_PL_VRAM,
+ .flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT
};
static const struct ttm_place sys_placement_flags = {
.fpfn = 0,
.lpfn = 0,
- .flags = TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED
+ .mem_type = TTM_PL_SYSTEM,
+ .flags = TTM_PL_FLAG_CACHED
};
static const struct ttm_place sys_ne_placement_flags = {
.fpfn = 0,
.lpfn = 0,
- .flags = TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT
+ .mem_type = TTM_PL_SYSTEM,
+ .flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT
};
static const struct ttm_place gmr_placement_flags = {
.fpfn = 0,
.lpfn = 0,
- .flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED
+ .mem_type = VMW_PL_GMR,
+ .flags = TTM_PL_FLAG_CACHED
};
static const struct ttm_place gmr_ne_placement_flags = {
.fpfn = 0,
.lpfn = 0,
- .flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT
+ .mem_type = VMW_PL_GMR,
+ .flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT
};
static const struct ttm_place mob_placement_flags = {
.fpfn = 0,
.lpfn = 0,
- .flags = VMW_PL_FLAG_MOB | TTM_PL_FLAG_CACHED
+ .mem_type = VMW_PL_MOB,
+ .flags = TTM_PL_FLAG_CACHED
};
static const struct ttm_place mob_ne_placement_flags = {
.fpfn = 0,
.lpfn = 0,
- .flags = VMW_PL_FLAG_MOB | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT
+ .mem_type = VMW_PL_MOB,
+ .flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT
};
struct ttm_placement vmw_vram_placement = {
@@ -89,11 +97,13 @@ static const struct ttm_place vram_gmr_placement_flags[] = {
{
.fpfn = 0,
.lpfn = 0,
- .flags = TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED
+ .mem_type = TTM_PL_VRAM,
+ .flags = TTM_PL_FLAG_CACHED
}, {
.fpfn = 0,
.lpfn = 0,
- .flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED
+ .mem_type = VMW_PL_GMR,
+ .flags = TTM_PL_FLAG_CACHED
}
};
@@ -101,11 +111,13 @@ static const struct ttm_place gmr_vram_placement_flags[] = {
{
.fpfn = 0,
.lpfn = 0,
- .flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED
+ .mem_type = VMW_PL_GMR,
+ .flags = TTM_PL_FLAG_CACHED
}, {
.fpfn = 0,
.lpfn = 0,
- .flags = TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED
+ .mem_type = TTM_PL_VRAM,
+ .flags = TTM_PL_FLAG_CACHED
}
};
@@ -120,12 +132,14 @@ static const struct ttm_place vram_gmr_ne_placement_flags[] = {
{
.fpfn = 0,
.lpfn = 0,
- .flags = TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED |
+ .mem_type = TTM_PL_VRAM,
+ .flags = TTM_PL_FLAG_CACHED |
TTM_PL_FLAG_NO_EVICT
}, {
.fpfn = 0,
.lpfn = 0,
- .flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED |
+ .mem_type = VMW_PL_GMR,
+ .flags = TTM_PL_FLAG_CACHED |
TTM_PL_FLAG_NO_EVICT
}
};
@@ -169,19 +183,23 @@ static const struct ttm_place evictable_placement_flags[] = {
{
.fpfn = 0,
.lpfn = 0,
- .flags = TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED
+ .mem_type = TTM_PL_SYSTEM,
+ .flags = TTM_PL_FLAG_CACHED
}, {
.fpfn = 0,
.lpfn = 0,
- .flags = TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED
+ .mem_type = TTM_PL_VRAM,
+ .flags = TTM_PL_FLAG_CACHED
}, {
.fpfn = 0,
.lpfn = 0,
- .flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED
+ .mem_type = VMW_PL_GMR,
+ .flags = TTM_PL_FLAG_CACHED
}, {
.fpfn = 0,
.lpfn = 0,
- .flags = VMW_PL_FLAG_MOB | TTM_PL_FLAG_CACHED
+ .mem_type = VMW_PL_MOB,
+ .flags = TTM_PL_FLAG_CACHED
}
};
@@ -189,15 +207,18 @@ static const struct ttm_place nonfixed_placement_flags[] = {
{
.fpfn = 0,
.lpfn = 0,
- .flags = TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED
+ .mem_type = TTM_PL_SYSTEM,
+ .flags = TTM_PL_FLAG_CACHED
}, {
.fpfn = 0,
.lpfn = 0,
- .flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED
+ .mem_type = VMW_PL_GMR,
+ .flags = TTM_PL_FLAG_CACHED
}, {
.fpfn = 0,
.lpfn = 0,
- .flags = VMW_PL_FLAG_MOB | TTM_PL_FLAG_CACHED
+ .mem_type = VMW_PL_MOB,
+ .flags = TTM_PL_FLAG_CACHED
}
};
@@ -246,6 +267,7 @@ struct vmw_ttm_tt {
struct vmw_sg_table vsgt;
uint64_t sg_alloc_size;
bool mapped;
+ bool bound;
};
const size_t vmw_tt_size = sizeof(struct vmw_ttm_tt);
@@ -530,11 +552,18 @@ const struct vmw_sg_table *vmw_bo_sg_table(struct ttm_buffer_object *bo)
}
-static int vmw_ttm_bind(struct ttm_tt *ttm, struct ttm_resource *bo_mem)
+static int vmw_ttm_bind(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm, struct ttm_resource *bo_mem)
{
struct vmw_ttm_tt *vmw_be =
container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm);
- int ret;
+ int ret = 0;
+
+ if (!bo_mem)
+ return -EINVAL;
+
+ if (vmw_be->bound)
+ return 0;
ret = vmw_ttm_map_dma(vmw_be);
if (unlikely(ret != 0))
@@ -545,8 +574,9 @@ static int vmw_ttm_bind(struct ttm_tt *ttm, struct ttm_resource *bo_mem)
switch (bo_mem->mem_type) {
case VMW_PL_GMR:
- return vmw_gmr_bind(vmw_be->dev_priv, &vmw_be->vsgt,
+ ret = vmw_gmr_bind(vmw_be->dev_priv, &vmw_be->vsgt,
ttm->num_pages, vmw_be->gmr_id);
+ break;
case VMW_PL_MOB:
if (unlikely(vmw_be->mob == NULL)) {
vmw_be->mob =
@@ -555,20 +585,26 @@ static int vmw_ttm_bind(struct ttm_tt *ttm, struct ttm_resource *bo_mem)
return -ENOMEM;
}
- return vmw_mob_bind(vmw_be->dev_priv, vmw_be->mob,
+ ret = vmw_mob_bind(vmw_be->dev_priv, vmw_be->mob,
&vmw_be->vsgt, ttm->num_pages,
vmw_be->gmr_id);
+ break;
default:
BUG();
}
- return 0;
+ vmw_be->bound = true;
+ return ret;
}
-static void vmw_ttm_unbind(struct ttm_tt *ttm)
+static void vmw_ttm_unbind(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm)
{
struct vmw_ttm_tt *vmw_be =
container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm);
+ if (!vmw_be->bound)
+ return;
+
switch (vmw_be->mem_type) {
case VMW_PL_GMR:
vmw_gmr_unbind(vmw_be->dev_priv, vmw_be->gmr_id);
@@ -582,14 +618,17 @@ static void vmw_ttm_unbind(struct ttm_tt *ttm)
if (vmw_be->dev_priv->map_mode == vmw_dma_map_bind)
vmw_ttm_unmap_dma(vmw_be);
+ vmw_be->bound = false;
}
-static void vmw_ttm_destroy(struct ttm_tt *ttm)
+static void vmw_ttm_destroy(struct ttm_bo_device *bdev, struct ttm_tt *ttm)
{
struct vmw_ttm_tt *vmw_be =
container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm);
+ vmw_ttm_unbind(bdev, ttm);
+ ttm_tt_destroy_common(bdev, ttm);
vmw_ttm_unmap_dma(vmw_be);
if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent)
ttm_dma_tt_fini(&vmw_be->dma_ttm);
@@ -603,7 +642,8 @@ static void vmw_ttm_destroy(struct ttm_tt *ttm)
}
-static int vmw_ttm_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)
+static int vmw_ttm_populate(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)
{
struct vmw_ttm_tt *vmw_tt =
container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm);
@@ -611,7 +651,7 @@ static int vmw_ttm_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)
struct ttm_mem_global *glob = vmw_mem_glob(dev_priv);
int ret;
- if (ttm->state != tt_unpopulated)
+ if (ttm_tt_is_populated(ttm))
return 0;
if (dev_priv->map_mode == vmw_dma_alloc_coherent) {
@@ -631,7 +671,8 @@ static int vmw_ttm_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)
return ret;
}
-static void vmw_ttm_unpopulate(struct ttm_tt *ttm)
+static void vmw_ttm_unpopulate(struct ttm_bo_device *bdev,
+ struct ttm_tt *ttm)
{
struct vmw_ttm_tt *vmw_tt = container_of(ttm, struct vmw_ttm_tt,
dma_ttm.ttm);
@@ -655,12 +696,6 @@ static void vmw_ttm_unpopulate(struct ttm_tt *ttm)
ttm_pool_unpopulate(ttm);
}
-static struct ttm_backend_func vmw_ttm_func = {
- .bind = vmw_ttm_bind,
- .unbind = vmw_ttm_unbind,
- .destroy = vmw_ttm_destroy,
-};
-
static struct ttm_tt *vmw_ttm_tt_create(struct ttm_buffer_object *bo,
uint32_t page_flags)
{
@@ -671,7 +706,6 @@ static struct ttm_tt *vmw_ttm_tt_create(struct ttm_buffer_object *bo,
if (!vmw_be)
return NULL;
- vmw_be->dma_ttm.ttm.func = &vmw_ttm_func;
vmw_be->dev_priv = container_of(bo->bdev, struct vmw_private, bdev);
vmw_be->mob = NULL;
@@ -712,8 +746,8 @@ static int vmw_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_resourc
case VMW_PL_MOB:
return 0;
case TTM_PL_VRAM:
- mem->bus.offset = mem->start << PAGE_SHIFT;
- mem->bus.base = dev_priv->vram_start;
+ mem->bus.offset = (mem->start << PAGE_SHIFT) +
+ dev_priv->vram_start;
mem->bus.is_iomem = true;
break;
default:
@@ -757,6 +791,9 @@ struct ttm_bo_driver vmw_bo_driver = {
.ttm_tt_create = &vmw_ttm_tt_create,
.ttm_tt_populate = &vmw_ttm_populate,
.ttm_tt_unpopulate = &vmw_ttm_unpopulate,
+ .ttm_tt_bind = &vmw_ttm_bind,
+ .ttm_tt_unbind = &vmw_ttm_unbind,
+ .ttm_tt_destroy = &vmw_ttm_destroy,
.eviction_valuable = ttm_bo_eviction_valuable,
.evict_flags = vmw_evict_flags,
.move = NULL,
@@ -787,7 +824,7 @@ int vmw_bo_create_and_populate(struct vmw_private *dev_priv,
ret = ttm_bo_reserve(bo, false, true, NULL);
BUG_ON(ret != 0);
- ret = vmw_ttm_populate(bo->ttm, &ctx);
+ ret = vmw_ttm_populate(bo->bdev, bo->ttm, &ctx);
if (likely(ret == 0)) {
struct vmw_ttm_tt *vmw_tt =
container_of(bo->ttm, struct vmw_ttm_tt, dma_ttm.ttm);
diff --git a/drivers/gpu/drm/xen/xen_drm_front_gem.c b/drivers/gpu/drm/xen/xen_drm_front_gem.c
index a487384d5cb7..2f464ef2d53e 100644
--- a/drivers/gpu/drm/xen/xen_drm_front_gem.c
+++ b/drivers/gpu/drm/xen/xen_drm_front_gem.c
@@ -180,7 +180,8 @@ struct sg_table *xen_drm_front_gem_get_sg_table(struct drm_gem_object *gem_obj)
if (!xen_obj->pages)
return ERR_PTR(-ENOMEM);
- return drm_prime_pages_to_sg(xen_obj->pages, xen_obj->num_pages);
+ return drm_prime_pages_to_sg(gem_obj->dev,
+ xen_obj->pages, xen_obj->num_pages);
}
struct drm_gem_object *
diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index a455cfc1bee5..98bd48f13fd1 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -242,12 +242,6 @@ static const u32 scaling_factors_565[] = {
ZYNQMP_DISP_AV_BUF_5BIT_SF,
};
-static const u32 scaling_factors_666[] = {
- ZYNQMP_DISP_AV_BUF_6BIT_SF,
- ZYNQMP_DISP_AV_BUF_6BIT_SF,
- ZYNQMP_DISP_AV_BUF_6BIT_SF,
-};
-
static const u32 scaling_factors_888[] = {
ZYNQMP_DISP_AV_BUF_8BIT_SF,
ZYNQMP_DISP_AV_BUF_8BIT_SF,
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
index 26328c76305b..8e69303aad3f 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
@@ -111,7 +111,7 @@ static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
/* Initialize mode config, vblank and the KMS poll helper. */
ret = drmm_mode_config_init(drm);
if (ret < 0)
- goto err_dev_put;
+ return ret;
drm->mode_config.funcs = &zynqmp_dpsub_mode_config_funcs;
drm->mode_config.min_width = 0;
@@ -121,7 +121,7 @@ static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
ret = drm_vblank_init(drm, 1);
if (ret)
- goto err_dev_put;
+ return ret;
drm->irq_enabled = 1;
@@ -154,8 +154,6 @@ static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
err_poll_fini:
drm_kms_helper_poll_fini(drm);
-err_dev_put:
- drm_dev_put(drm);
return ret;
}
@@ -208,27 +206,16 @@ static int zynqmp_dpsub_probe(struct platform_device *pdev)
int ret;
/* Allocate private data. */
- dpsub = kzalloc(sizeof(*dpsub), GFP_KERNEL);
- if (!dpsub)
- return -ENOMEM;
+ dpsub = devm_drm_dev_alloc(&pdev->dev, &zynqmp_dpsub_drm_driver,
+ struct zynqmp_dpsub, drm);
+ if (IS_ERR(dpsub))
+ return PTR_ERR(dpsub);
dpsub->dev = &pdev->dev;
platform_set_drvdata(pdev, dpsub);
dma_set_mask(dpsub->dev, DMA_BIT_MASK(ZYNQMP_DISP_MAX_DMA_BIT));
- /*
- * Initialize the DRM device early, as the DRM core mandates usage of
- * the managed memory helpers tied to the DRM device.
- */
- ret = drm_dev_init(&dpsub->drm, &zynqmp_dpsub_drm_driver, &pdev->dev);
- if (ret < 0) {
- kfree(dpsub);
- return ret;
- }
-
- drmm_add_final_kfree(&dpsub->drm, dpsub);
-
/* Try the reserved memory. Proceed if there's none. */
of_reserved_mem_device_init(&pdev->dev);
@@ -286,8 +273,6 @@ static int zynqmp_dpsub_remove(struct platform_device *pdev)
clk_disable_unprepare(dpsub->apb_clk);
of_reserved_mem_device_release(&pdev->dev);
- drm_dev_put(drm);
-
return 0;
}