diff options
Diffstat (limited to 'drivers/gpu')
404 files changed, 28731 insertions, 20755 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 64f2a44..483059a 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -108,33 +108,13 @@ config DRM_KMS_CMA_HELPER source "drivers/gpu/drm/i2c/Kconfig" -config DRM_TDFX - tristate "3dfx Banshee/Voodoo3+" - depends on DRM && PCI - help - Choose this option if you have a 3dfx Banshee or Voodoo3 (or later), - graphics card. If M is selected, the module will be called tdfx. - source "drivers/gpu/drm/arm/Kconfig" -config DRM_R128 - tristate "ATI Rage 128" - depends on DRM && PCI - select FW_LOADER - help - Choose this option if you have an ATI Rage 128 graphics card. If M - is selected, the module will be called r128. AGP support for - this card is strongly suggested (unless you have a PCI version). - config DRM_RADEON tristate "ATI Radeon" depends on DRM && PCI - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT select FW_LOADER select DRM_KMS_HELPER - select DRM_KMS_FB_HELPER select DRM_TTM select POWER_SUPPLY select HWMON @@ -153,12 +133,8 @@ source "drivers/gpu/drm/radeon/Kconfig" config DRM_AMDGPU tristate "AMD GPU" depends on DRM && PCI - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT select FW_LOADER select DRM_KMS_HELPER - select DRM_KMS_FB_HELPER select DRM_TTM select POWER_SUPPLY select HWMON @@ -174,49 +150,8 @@ source "drivers/gpu/drm/amd/amdgpu/Kconfig" source "drivers/gpu/drm/nouveau/Kconfig" -config DRM_I810 - tristate "Intel I810" - # !PREEMPT because of missing ioctl locking - depends on DRM && AGP && AGP_INTEL && (!PREEMPT || BROKEN) - help - Choose this option if you have an Intel I810 graphics card. If M is - selected, the module will be called i810. AGP support is required - for this driver to work. - source "drivers/gpu/drm/i915/Kconfig" -config DRM_MGA - tristate "Matrox g200/g400" - depends on DRM && PCI - select FW_LOADER - help - Choose this option if you have a Matrox G200, G400 or G450 graphics - card. If M is selected, the module will be called mga. AGP - support is required for this driver to work. - -config DRM_SIS - tristate "SiS video cards" - depends on DRM && AGP - depends on FB_SIS || FB_SIS=n - help - Choose this option if you have a SiS 630 or compatible video - chipset. If M is selected the module will be called sis. AGP - support is required for this driver to work. - -config DRM_VIA - tristate "Via unichrome video cards" - depends on DRM && PCI - help - Choose this option if you have a Via unichrome or compatible video - chipset. If M is selected the module will be called via. - -config DRM_SAVAGE - tristate "Savage video cards" - depends on DRM && PCI - help - Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister - chipset. If M is selected the module will be called savage. - config DRM_VGEM tristate "Virtual GEM provider" depends on DRM @@ -287,3 +222,81 @@ source "drivers/gpu/drm/arc/Kconfig" source "drivers/gpu/drm/hisilicon/Kconfig" source "drivers/gpu/drm/mediatek/Kconfig" + +# Keep legacy drivers last + +menuconfig DRM_LEGACY + bool "Enable legacy drivers (DANGEROUS)" + depends on DRM + help + Enable legacy DRI1 drivers. Those drivers expose unsafe and dangerous + APIs to user-space, which can be used to circumvent access + restrictions and other security measures. For backwards compatibility + those drivers are still available, but their use is highly + inadvisable and might harm your system. + + You are recommended to use the safe modeset-only drivers instead, and + perform 3D emulation in user-space. + + Unless you have strong reasons to go rogue, say "N". + +if DRM_LEGACY + +config DRM_TDFX + tristate "3dfx Banshee/Voodoo3+" + depends on DRM && PCI + help + Choose this option if you have a 3dfx Banshee or Voodoo3 (or later), + graphics card. If M is selected, the module will be called tdfx. + +config DRM_R128 + tristate "ATI Rage 128" + depends on DRM && PCI + select FW_LOADER + help + Choose this option if you have an ATI Rage 128 graphics card. If M + is selected, the module will be called r128. AGP support for + this card is strongly suggested (unless you have a PCI version). + +config DRM_I810 + tristate "Intel I810" + # !PREEMPT because of missing ioctl locking + depends on DRM && AGP && AGP_INTEL && (!PREEMPT || BROKEN) + help + Choose this option if you have an Intel I810 graphics card. If M is + selected, the module will be called i810. AGP support is required + for this driver to work. + +config DRM_MGA + tristate "Matrox g200/g400" + depends on DRM && PCI + select FW_LOADER + help + Choose this option if you have a Matrox G200, G400 or G450 graphics + card. If M is selected, the module will be called mga. AGP + support is required for this driver to work. + +config DRM_SIS + tristate "SiS video cards" + depends on DRM && AGP + depends on FB_SIS || FB_SIS=n + help + Choose this option if you have a SiS 630 or compatible video + chipset. If M is selected the module will be called sis. AGP + support is required for this driver to work. + +config DRM_VIA + tristate "Via unichrome video cards" + depends on DRM && PCI + help + Choose this option if you have a Via unichrome or compatible video + chipset. If M is selected the module will be called via. + +config DRM_SAVAGE + tristate "Savage video cards" + depends on DRM && PCI + help + Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister + chipset. If M is selected the module will be called savage. + +endif # DRM_LEGACY diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 0238bf8..25c7204 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -12,7 +12,10 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_info.o drm_debugfs.o drm_encoder_slave.o \ drm_trace_points.o drm_global.o drm_prime.o \ drm_rect.o drm_vma_manager.o drm_flip_work.o \ - drm_modeset_lock.o drm_atomic.o drm_bridge.o + drm_modeset_lock.o drm_atomic.o drm_bridge.o \ + drm_framebuffer.o drm_connector.o drm_blend.o \ + drm_encoder.o drm_mode_object.o drm_property.o \ + drm_plane.o drm_color_mgmt.o drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o @@ -24,7 +27,7 @@ drm-$(CONFIG_AGP) += drm_agpsupport.o drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \ drm_kms_helper_common.o drm_dp_dual_mode_helper.o \ - drm_simple_kms_helper.o drm_blend.o + drm_simple_kms_helper.o drm_modeset_helper.o drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o @@ -46,7 +49,7 @@ obj-$(CONFIG_DRM_RADEON)+= radeon/ obj-$(CONFIG_DRM_AMDGPU)+= amd/amdgpu/ obj-$(CONFIG_DRM_MGA) += mga/ obj-$(CONFIG_DRM_I810) += i810/ -obj-$(CONFIG_DRM_I915) += i915/ +obj-$(CONFIG_DRM_I915) += i915/ obj-$(CONFIG_DRM_MGAG200) += mgag200/ obj-$(CONFIG_DRM_VC4) += vc4/ obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 4f29f84..235f390 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -407,7 +407,6 @@ unsigned amdgpu_fence_count_emitted(struct amdgpu_ring *ring); /* * BO. */ - struct amdgpu_bo_list_entry { struct amdgpu_bo *robj; struct ttm_validate_buffer tv; @@ -618,9 +617,9 @@ int amdgpu_gart_table_vram_pin(struct amdgpu_device *adev); void amdgpu_gart_table_vram_unpin(struct amdgpu_device *adev); int amdgpu_gart_init(struct amdgpu_device *adev); void amdgpu_gart_fini(struct amdgpu_device *adev); -void amdgpu_gart_unbind(struct amdgpu_device *adev, unsigned offset, +void amdgpu_gart_unbind(struct amdgpu_device *adev, uint64_t offset, int pages); -int amdgpu_gart_bind(struct amdgpu_device *adev, unsigned offset, +int amdgpu_gart_bind(struct amdgpu_device *adev, uint64_t offset, int pages, struct page **pagelist, dma_addr_t *dma_addr, uint32_t flags); int amdgpu_ttm_recover_gart(struct amdgpu_device *adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v7.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v7.c index 362bedc..1a0a5f7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v7.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v7.c @@ -103,11 +103,11 @@ static bool kgd_hqd_is_occupied(struct kgd_dev *kgd, uint64_t queue_address, uint32_t pipe_id, uint32_t queue_id); static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type, - unsigned int timeout, uint32_t pipe_id, + unsigned int utimeout, uint32_t pipe_id, uint32_t queue_id); static bool kgd_hqd_sdma_is_occupied(struct kgd_dev *kgd, void *mqd); static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd, - unsigned int timeout); + unsigned int utimeout); static int kgd_address_watch_disable(struct kgd_dev *kgd); static int kgd_address_watch_execute(struct kgd_dev *kgd, unsigned int watch_point_id, @@ -437,11 +437,12 @@ static bool kgd_hqd_sdma_is_occupied(struct kgd_dev *kgd, void *mqd) } static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type, - unsigned int timeout, uint32_t pipe_id, + unsigned int utimeout, uint32_t pipe_id, uint32_t queue_id) { struct amdgpu_device *adev = get_amdgpu_device(kgd); uint32_t temp; + int timeout = utimeout; acquire_queue(kgd, pipe_id, queue_id); WREG32(mmCP_HQD_PQ_DOORBELL_CONTROL, 0); @@ -452,9 +453,8 @@ static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type, temp = RREG32(mmCP_HQD_ACTIVE); if (temp & CP_HQD_ACTIVE__ACTIVE_MASK) break; - if (timeout == 0) { - pr_err("kfd: cp queue preemption time out (%dms)\n", - temp); + if (timeout <= 0) { + pr_err("kfd: cp queue preemption time out.\n"); release_queue(kgd); return -ETIME; } @@ -467,12 +467,13 @@ static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type, } static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd, - unsigned int timeout) + unsigned int utimeout) { struct amdgpu_device *adev = get_amdgpu_device(kgd); struct cik_sdma_rlc_registers *m; uint32_t sdma_base_addr; uint32_t temp; + int timeout = utimeout; m = get_sdma_mqd(mqd); sdma_base_addr = get_sdma_base_addr(m); @@ -485,7 +486,7 @@ static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd, temp = RREG32(sdma_base_addr + mmSDMA0_RLC0_CONTEXT_STATUS); if (temp & SDMA0_STATUS_REG__RB_CMD_IDLE__SHIFT) break; - if (timeout == 0) + if (timeout <= 0) return -ETIME; msleep(20); timeout -= 20; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v8.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v8.c index 04b744d..6697612 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v8.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v8.c @@ -62,10 +62,10 @@ static bool kgd_hqd_is_occupied(struct kgd_dev *kgd, uint64_t queue_address, uint32_t pipe_id, uint32_t queue_id); static bool kgd_hqd_sdma_is_occupied(struct kgd_dev *kgd, void *mqd); static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type, - unsigned int timeout, uint32_t pipe_id, + unsigned int utimeout, uint32_t pipe_id, uint32_t queue_id); static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd, - unsigned int timeout); + unsigned int utimeout); static void write_vmid_invalidate_request(struct kgd_dev *kgd, uint8_t vmid); static int kgd_address_watch_disable(struct kgd_dev *kgd); static int kgd_address_watch_execute(struct kgd_dev *kgd, @@ -349,11 +349,12 @@ static bool kgd_hqd_sdma_is_occupied(struct kgd_dev *kgd, void *mqd) } static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type, - unsigned int timeout, uint32_t pipe_id, + unsigned int utimeout, uint32_t pipe_id, uint32_t queue_id) { struct amdgpu_device *adev = get_amdgpu_device(kgd); uint32_t temp; + int timeout = utimeout; acquire_queue(kgd, pipe_id, queue_id); @@ -363,9 +364,8 @@ static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type, temp = RREG32(mmCP_HQD_ACTIVE); if (temp & CP_HQD_ACTIVE__ACTIVE_MASK) break; - if (timeout == 0) { - pr_err("kfd: cp queue preemption time out (%dms)\n", - temp); + if (timeout <= 0) { + pr_err("kfd: cp queue preemption time out.\n"); release_queue(kgd); return -ETIME; } @@ -378,12 +378,13 @@ static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type, } static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd, - unsigned int timeout) + unsigned int utimeout) { struct amdgpu_device *adev = get_amdgpu_device(kgd); struct cik_sdma_rlc_registers *m; uint32_t sdma_base_addr; uint32_t temp; + int timeout = utimeout; m = get_sdma_mqd(mqd); sdma_base_addr = get_sdma_base_addr(m); @@ -396,7 +397,7 @@ static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd, temp = RREG32(sdma_base_addr + mmSDMA0_RLC0_CONTEXT_STATUS); if (temp & SDMA0_STATUS_REG__RB_CMD_IDLE__SHIFT) break; - if (timeout == 0) + if (timeout <= 0) return -ETIME; msleep(20); timeout -= 20; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c index 59961db..8e6bf54 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c @@ -348,6 +348,19 @@ bool amdgpu_atombios_get_connector_info_from_object_table(struct amdgpu_device * (le16_to_cpu(path->usConnObjectId) & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; + /* Skip TV/CV support */ + if ((le16_to_cpu(path->usDeviceTag) == + ATOM_DEVICE_TV1_SUPPORT) || + (le16_to_cpu(path->usDeviceTag) == + ATOM_DEVICE_CV_SUPPORT)) + continue; + + if (con_obj_id >= ARRAY_SIZE(object_connector_convert)) { + DRM_ERROR("invalid con_obj_id %d for device tag 0x%04x\n", + con_obj_id, le16_to_cpu(path->usDeviceTag)); + continue; + } + connector_type = object_connector_convert[con_obj_id]; connector_object_id = con_obj_id; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c index 550c5ee..dae35a9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c @@ -205,16 +205,7 @@ static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx) atpx->is_hybrid = false; if (valid_bits & ATPX_MS_HYBRID_GFX_SUPPORTED) { printk("ATPX Hybrid Graphics\n"); -#if 1 - /* This is a temporary hack until the D3 cold support - * makes it upstream. The ATPX power_control method seems - * to still work on even if the system should be using - * the new standardized hybrid D3 cold ACPI interface. - */ - atpx->functions.power_cntl = true; -#else atpx->functions.power_cntl = false; -#endif atpx->is_hybrid = true; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 28c6ea8..dbe89fb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -436,7 +436,7 @@ static int amdgpu_kick_out_firmware_fb(struct pci_dev *pdev) #ifdef CONFIG_X86 primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; #endif - remove_conflicting_framebuffers(ap, "amdgpudrmfb", primary); + drm_fb_helper_remove_conflicting_framebuffers(ap, "amdgpudrmfb", primary); kfree(ap); return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c index aa4d15b..9fb8aa4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c @@ -25,7 +25,6 @@ */ #include <linux/module.h> #include <linux/slab.h> -#include <linux/fb.h> #include <linux/pm_runtime.h> #include <drm/drmP.h> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c index c977773..21a1242 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c @@ -221,7 +221,7 @@ void amdgpu_gart_table_vram_free(struct amdgpu_device *adev) * Unbinds the requested pages from the gart page table and * replaces them with the dummy page (all asics). */ -void amdgpu_gart_unbind(struct amdgpu_device *adev, unsigned offset, +void amdgpu_gart_unbind(struct amdgpu_device *adev, uint64_t offset, int pages) { unsigned t; @@ -268,7 +268,7 @@ void amdgpu_gart_unbind(struct amdgpu_device *adev, unsigned offset, * (all asics). * Returns 0 for success, -EINVAL for failure. */ -int amdgpu_gart_bind(struct amdgpu_device *adev, unsigned offset, +int amdgpu_gart_bind(struct amdgpu_device *adev, uint64_t offset, int pages, struct page **pagelist, dma_addr_t *dma_addr, uint32_t flags) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c index 4127e7c..6a6c86c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c @@ -295,7 +295,7 @@ void amdgpu_ib_pool_fini(struct amdgpu_device *adev) int amdgpu_ib_ring_tests(struct amdgpu_device *adev) { unsigned i; - int r; + int r, ret = 0; for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { struct amdgpu_ring *ring = adev->rings[i]; @@ -316,10 +316,11 @@ int amdgpu_ib_ring_tests(struct amdgpu_device *adev) } else { /* still not good, but we can live with it */ DRM_ERROR("amdgpu: failed testing IB on ring %d (%d).\n", i, r); + ret = r; } } } - return 0; + return ret; } /* diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index c80bdc7..887483b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -246,7 +246,8 @@ static int amdgpu_verify_access(struct ttm_buffer_object *bo, struct file *filp) if (amdgpu_ttm_tt_get_usermm(bo->ttm)) return -EPERM; - return drm_vma_node_verify_access(&abo->gem_base.vma_node, filp); + return drm_vma_node_verify_access(&abo->gem_base.vma_node, + filp->private_data); } static void amdgpu_move_null(struct ttm_buffer_object *bo, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 4656f1b..e3281ca 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -1154,7 +1154,8 @@ int amdgpu_uvd_ring_test_ib(struct amdgpu_ring *ring, long timeout) r = 0; } -error: fence_put(fence); + +error: return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 1405d69..bc4b22c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1623,7 +1623,7 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm) r = amd_sched_entity_init(&ring->sched, &vm->entity, rq, amdgpu_sched_jobs); if (r) - return r; + goto err; vm->page_directory_fence = NULL; @@ -1665,6 +1665,9 @@ error_free_page_directory: error_free_sched_entity: amd_sched_entity_fini(&ring->sched, &vm->entity); +err: + drm_free_large(vm->page_tables); + return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c index e6d7bf9..cb952ac 100644 --- a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c @@ -52,6 +52,7 @@ static void cik_sdma_set_ring_funcs(struct amdgpu_device *adev); static void cik_sdma_set_irq_funcs(struct amdgpu_device *adev); static void cik_sdma_set_buffer_funcs(struct amdgpu_device *adev); static void cik_sdma_set_vm_pte_funcs(struct amdgpu_device *adev); +static int cik_sdma_soft_reset(void *handle); MODULE_FIRMWARE("radeon/bonaire_sdma.bin"); MODULE_FIRMWARE("radeon/bonaire_sdma1.bin"); @@ -1014,6 +1015,8 @@ static int cik_sdma_resume(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; + cik_sdma_soft_reset(handle); + return cik_sdma_hw_init(adev); } diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index 98f4bad..613ebb7 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -2115,6 +2115,7 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc, u32 tmp, viewport_w, viewport_h; int r; bool bypass_lut = false; + char *format_name; /* no fb bound */ if (!atomic && !crtc->primary->fb) { @@ -2226,8 +2227,9 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc, bypass_lut = true; break; default: - DRM_ERROR("Unsupported screen format %s\n", - drm_get_format_name(target_fb->pixel_format)); + format_name = drm_get_format_name(target_fb->pixel_format); + DRM_ERROR("Unsupported screen format %s\n", format_name); + kfree(format_name); return -EINVAL; } diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c index e8a6919..678f5eb 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c @@ -2096,6 +2096,7 @@ static int dce_v11_0_crtc_do_set_base(struct drm_crtc *crtc, u32 tmp, viewport_w, viewport_h; int r; bool bypass_lut = false; + char *format_name; /* no fb bound */ if (!atomic && !crtc->primary->fb) { @@ -2207,8 +2208,9 @@ static int dce_v11_0_crtc_do_set_base(struct drm_crtc *crtc, bypass_lut = true; break; default: - DRM_ERROR("Unsupported screen format %s\n", - drm_get_format_name(target_fb->pixel_format)); + format_name = drm_get_format_name(target_fb->pixel_format); + DRM_ERROR("Unsupported screen format %s\n", format_name); + kfree(format_name); return -EINVAL; } diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c index c5b2866..5966166 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c @@ -2030,6 +2030,7 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc, u32 viewport_w, viewport_h; int r; bool bypass_lut = false; + char *format_name; /* no fb bound */ if (!atomic && !crtc->primary->fb) { @@ -2134,8 +2135,9 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc, bypass_lut = true; break; default: - DRM_ERROR("Unsupported screen format %s\n", - drm_get_format_name(target_fb->pixel_format)); + format_name = drm_get_format_name(target_fb->pixel_format); + DRM_ERROR("Unsupported screen format %s\n", format_name); + kfree(format_name); return -EINVAL; } diff --git a/drivers/gpu/drm/amd/amdgpu/dce_virtual.c b/drivers/gpu/drm/amd/amdgpu/dce_virtual.c index 23ff9f2..a754f25 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_virtual.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_virtual.c @@ -466,11 +466,7 @@ static int dce_virtual_suspend(void *handle) static int dce_virtual_resume(void *handle) { - int ret; - - ret = dce_virtual_hw_init(handle); - - return ret; + return dce_virtual_hw_init(handle); } static bool dce_virtual_is_idle(void *handle) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index 32a6762..71116da 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -2927,8 +2927,7 @@ static int gfx_v7_0_cp_compute_resume(struct amdgpu_device *adev) u64 wb_gpu_addr; u32 *buf; struct bonaire_mqd *mqd; - - gfx_v7_0_cp_compute_enable(adev, true); + struct amdgpu_ring *ring; /* fix up chicken bits */ tmp = RREG32(mmCP_CPF_DEBUG); @@ -2963,7 +2962,7 @@ static int gfx_v7_0_cp_compute_resume(struct amdgpu_device *adev) /* init the queues. Just two for now. */ for (i = 0; i < adev->gfx.num_compute_rings; i++) { - struct amdgpu_ring *ring = &adev->gfx.compute_ring[i]; + ring = &adev->gfx.compute_ring[i]; if (ring->mqd_obj == NULL) { r = amdgpu_bo_create(adev, @@ -3142,6 +3141,13 @@ static int gfx_v7_0_cp_compute_resume(struct amdgpu_device *adev) amdgpu_bo_unreserve(ring->mqd_obj); ring->ready = true; + } + + gfx_v7_0_cp_compute_enable(adev, true); + + for (i = 0; i < adev->gfx.num_compute_rings; i++) { + ring = &adev->gfx.compute_ring[i]; + r = amdgpu_ring_test_ring(ring); if (r) ring->ready = false; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index f490691..6c6ff57 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -270,7 +270,8 @@ static const u32 tonga_mgcg_cgcg_init[] = static const u32 golden_settings_polaris11_a11[] = { - mmCB_HW_CONTROL, 0xfffdf3cf, 0x00006208, + mmCB_HW_CONTROL, 0x0000f3cf, 0x00007208, + mmCB_HW_CONTROL_2, 0x0f000000, 0x0f000000, mmCB_HW_CONTROL_3, 0x000001ff, 0x00000040, mmDB_DEBUG2, 0xf00fffff, 0x00000400, mmPA_SC_ENHANCE, 0xffffffff, 0x20000001, @@ -279,7 +280,7 @@ static const u32 golden_settings_polaris11_a11[] = mmPA_SC_RASTER_CONFIG_1, 0x0000003f, 0x00000000, mmRLC_CGCG_CGLS_CTRL, 0x00000003, 0x0001003c, mmRLC_CGCG_CGLS_CTRL_3D, 0xffffffff, 0x0001003c, - mmSQ_CONFIG, 0x07f80000, 0x07180000, + mmSQ_CONFIG, 0x07f80000, 0x01180000, mmTA_CNTL_AUX, 0x000f000f, 0x000b0000, mmTCC_CTRL, 0x00100000, 0xf31fff7f, mmTCP_ADDR_CONFIG, 0x000003ff, 0x000000f3, @@ -301,8 +302,8 @@ static const u32 polaris11_golden_common_all[] = static const u32 golden_settings_polaris10_a11[] = { mmATC_MISC_CG, 0x000c0fc0, 0x000c0200, - mmCB_HW_CONTROL, 0xfffdf3cf, 0x00007208, - mmCB_HW_CONTROL_2, 0, 0x0f000000, + mmCB_HW_CONTROL, 0x0001f3cf, 0x00007208, + mmCB_HW_CONTROL_2, 0x0f000000, 0x0f000000, mmCB_HW_CONTROL_3, 0x000001ff, 0x00000040, mmDB_DEBUG2, 0xf00fffff, 0x00000400, mmPA_SC_ENHANCE, 0xffffffff, 0x20000001, @@ -409,6 +410,7 @@ static const u32 golden_settings_iceland_a11[] = mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000, mmPA_SC_RASTER_CONFIG, 0x3f3fffff, 0x00000002, mmPA_SC_RASTER_CONFIG_1, 0x0000003f, 0x00000000, + mmRLC_CGCG_CGLS_CTRL, 0x00000003, 0x0000003c, mmSQ_RANDOM_WAVE_PRI, 0x001fffff, 0x000006fd, mmTA_CNTL_AUX, 0x000f000f, 0x000b0000, mmTCC_CTRL, 0x00100000, 0xf31fff7f, @@ -505,8 +507,10 @@ static const u32 cz_golden_settings_a11[] = mmGB_GPU_ID, 0x0000000f, 0x00000000, mmPA_SC_ENHANCE, 0xffffffff, 0x00000001, mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000, + mmRLC_CGCG_CGLS_CTRL, 0x00000003, 0x0000003c, mmSQ_RANDOM_WAVE_PRI, 0x001fffff, 0x000006fd, mmTA_CNTL_AUX, 0x000f000f, 0x00010000, + mmTCC_CTRL, 0x00100000, 0xf31fff7f, mmTCC_EXE_DISABLE, 0x00000002, 0x00000002, mmTCP_ADDR_CONFIG, 0x0000000f, 0x000000f3, mmTCP_CHAN_STEER_LO, 0xffffffff, 0x00001302 diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c index 6ec8e01..1b319f5 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c @@ -103,6 +103,11 @@ static const u32 stoney_mgcg_cgcg_init[] = mmMC_MEM_POWER_LS, 0xffffffff, 0x00000104 }; +static const u32 golden_settings_stoney_common[] = +{ + mmMC_HUB_RDREQ_UVD, MC_HUB_RDREQ_UVD__PRESCALE_MASK, 0x00000004, + mmMC_RD_GRP_OTH, MC_RD_GRP_OTH__UVD_MASK, 0x00600000 +}; static void gmc_v8_0_init_golden_registers(struct amdgpu_device *adev) { @@ -142,6 +147,9 @@ static void gmc_v8_0_init_golden_registers(struct amdgpu_device *adev) amdgpu_program_register_sequence(adev, stoney_mgcg_cgcg_init, (const u32)ARRAY_SIZE(stoney_mgcg_cgcg_init)); + amdgpu_program_register_sequence(adev, + golden_settings_stoney_common, + (const u32)ARRAY_SIZE(golden_settings_stoney_common)); break; default: break; diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c index 82c731b..565dab3 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c @@ -190,12 +190,8 @@ out: */ static uint32_t sdma_v2_4_ring_get_rptr(struct amdgpu_ring *ring) { - u32 rptr; - /* XXX check if swapping is necessary on BE */ - rptr = ring->adev->wb.wb[ring->rptr_offs] >> 2; - - return rptr; + return ring->adev->wb.wb[ring->rptr_offs] >> 2; } /** @@ -714,7 +710,7 @@ static int sdma_v2_4_ring_test_ib(struct amdgpu_ring *ring, long timeout) DRM_ERROR("amdgpu: IB test timed out\n"); r = -ETIMEDOUT; goto err1; - } else if (r) { + } else if (r < 0) { DRM_ERROR("amdgpu: fence wait failed (%ld).\n", r); goto err1; } diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c index 62e2f4c..f325fd8 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c @@ -335,12 +335,8 @@ out: */ static uint32_t sdma_v3_0_ring_get_rptr(struct amdgpu_ring *ring) { - u32 rptr; - /* XXX check if swapping is necessary on BE */ - rptr = ring->adev->wb.wb[ring->rptr_offs] >> 2; - - return rptr; + return ring->adev->wb.wb[ring->rptr_offs] >> 2; } /** diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c index e621eba..453c5d6 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c @@ -142,13 +142,15 @@ int kfd_doorbell_mmap(struct kfd_process *process, struct vm_area_struct *vma) vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - pr_debug("mapping doorbell page:\n"); - pr_debug(" target user address == 0x%08llX\n", - (unsigned long long) vma->vm_start); - pr_debug(" physical address == 0x%08llX\n", address); - pr_debug(" vm_flags == 0x%04lX\n", vma->vm_flags); - pr_debug(" size == 0x%04lX\n", - doorbell_process_allocation()); + pr_debug("kfd: mapping doorbell page in %s\n" + " target user address == 0x%08llX\n" + " physical address == 0x%08llX\n" + " vm_flags == 0x%04lX\n" + " size == 0x%04lX\n", + __func__, + (unsigned long long) vma->vm_start, address, vma->vm_flags, + doorbell_process_allocation()); + return io_remap_pfn_range(vma, vma->vm_start, @@ -184,7 +186,7 @@ u32 __iomem *kfd_get_kernel_doorbell(struct kfd_dev *kfd, sizeof(u32)) + inx; pr_debug("kfd: get kernel queue doorbell\n" - " doorbell offset == 0x%08d\n" + " doorbell offset == 0x%08X\n" " kernel address == 0x%08lX\n", *doorbell_off, (uintptr_t)(kfd->doorbell_kernel_ptr + inx)); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c index 9beae87..d135cd0 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c @@ -47,6 +47,9 @@ static bool initialize(struct kernel_queue *kq, struct kfd_dev *dev, pr_debug("amdkfd: In func %s initializing queue type %d size %d\n", __func__, KFD_QUEUE_TYPE_HIQ, queue_size); + memset(&prop, 0, sizeof(prop)); + memset(&nop, 0, sizeof(nop)); + nop.opcode = IT_NOP; nop.type = PM4_TYPE_3; nop.u32all |= PM4_COUNT_ZERO; @@ -121,7 +124,7 @@ static bool initialize(struct kernel_queue *kq, struct kfd_dev *dev, prop.eop_ring_buffer_address = kq->eop_gpu_addr; prop.eop_ring_buffer_size = PAGE_SIZE; - if (init_queue(&kq->queue, prop) != 0) + if (init_queue(&kq->queue, &prop) != 0) goto err_init_queue; kq->queue->device = dev; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index 80113c3..4750cab 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -619,7 +619,7 @@ int kfd_init_apertures(struct kfd_process *process); /* Queue Context Management */ struct cik_sdma_rlc_registers *get_sdma_mqd(void *mqd); -int init_queue(struct queue **q, struct queue_properties properties); +int init_queue(struct queue **q, const struct queue_properties *properties); void uninit_queue(struct queue *q); void print_queue_properties(struct queue_properties *q); void print_queue(struct queue *q); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index 4f3849a..ef7c8de 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -404,58 +404,47 @@ void kfd_unbind_process_from_device(struct kfd_dev *dev, unsigned int pasid) { struct kfd_process *p; struct kfd_process_device *pdd; - int idx, i; BUG_ON(dev == NULL); - idx = srcu_read_lock(&kfd_processes_srcu); - /* * Look for the process that matches the pasid. If there is no such * process, we either released it in amdkfd's own notifier, or there * is a bug. Unfortunately, there is no way to tell... */ - hash_for_each_rcu(kfd_processes_table, i, p, kfd_processes) - if (p->pasid == pasid) { - - srcu_read_unlock(&kfd_processes_srcu, idx); - - pr_debug("Unbinding process %d from IOMMU\n", pasid); + p = kfd_lookup_process_by_pasid(pasid); + if (!p) + return; - mutex_lock(&p->mutex); - - if ((dev->dbgmgr) && (dev->dbgmgr->pasid == p->pasid)) - kfd_dbgmgr_destroy(dev->dbgmgr); - - pqm_uninit(&p->pqm); + pr_debug("Unbinding process %d from IOMMU\n", pasid); - pdd = kfd_get_process_device_data(dev, p); + if ((dev->dbgmgr) && (dev->dbgmgr->pasid == p->pasid)) + kfd_dbgmgr_destroy(dev->dbgmgr); - if (!pdd) { - mutex_unlock(&p->mutex); - return; - } + pqm_uninit(&p->pqm); - if (pdd->reset_wavefronts) { - dbgdev_wave_reset_wavefronts(pdd->dev, p); - pdd->reset_wavefronts = false; - } + pdd = kfd_get_process_device_data(dev, p); - /* - * Just mark pdd as unbound, because we still need it - * to call amd_iommu_unbind_pasid() in when the - * process exits. - * We don't call amd_iommu_unbind_pasid() here - * because the IOMMU called us. - */ - pdd->bound = false; + if (!pdd) { + mutex_unlock(&p->mutex); + return; + } - mutex_unlock(&p->mutex); + if (pdd->reset_wavefronts) { + dbgdev_wave_reset_wavefronts(pdd->dev, p); + pdd->reset_wavefronts = false; + } - return; - } + /* + * Just mark pdd as unbound, because we still need it + * to call amd_iommu_unbind_pasid() in when the + * process exits. + * We don't call amd_iommu_unbind_pasid() here + * because the IOMMU called us. + */ + pdd->bound = false; - srcu_read_unlock(&kfd_processes_srcu, idx); + mutex_unlock(&p->mutex); } struct kfd_process_device *kfd_get_first_process_device_data(struct kfd_process *p) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index 7b69070..e1fb40b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -129,7 +129,7 @@ static int create_cp_queue(struct process_queue_manager *pqm, q_properties->vmid = 0; q_properties->queue_id = qid; - retval = init_queue(q, *q_properties); + retval = init_queue(q, q_properties); if (retval != 0) goto err_init_queue; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c index 9a0c90b..0ab1970 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c @@ -63,7 +63,7 @@ void print_queue(struct queue *q) pr_debug("Queue Device Address: 0x%p\n", q->device); } -int init_queue(struct queue **q, struct queue_properties properties) +int init_queue(struct queue **q, const struct queue_properties *properties) { struct queue *tmp; @@ -73,7 +73,7 @@ int init_queue(struct queue **q, struct queue_properties properties) if (!tmp) return -ENOMEM; - memcpy(&tmp->properties, &properties, sizeof(struct queue_properties)); + memcpy(&tmp->properties, properties, sizeof(struct queue_properties)); *q = tmp; return 0; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c index 884c96f..1e50647 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c @@ -1090,19 +1090,21 @@ static uint32_t kfd_generate_gpu_id(struct kfd_dev *gpu) { uint32_t hashout; uint32_t buf[7]; + uint64_t local_mem_size; int i; if (!gpu) return 0; + local_mem_size = gpu->kfd2kgd->get_vmem_size(gpu->kgd); + buf[0] = gpu->pdev->devfn; buf[1] = gpu->pdev->subsystem_vendor; buf[2] = gpu->pdev->subsystem_device; buf[3] = gpu->pdev->device; buf[4] = gpu->pdev->bus->number; - buf[5] = (uint32_t)(gpu->kfd2kgd->get_vmem_size(gpu->kgd) - & 0xffffffff); - buf[6] = (uint32_t)(gpu->kfd2kgd->get_vmem_size(gpu->kgd) >> 32); + buf[5] = lower_32_bits(local_mem_size); + buf[6] = upper_32_bits(local_mem_size); for (i = 0, hashout = 0; i < 7; i++) hashout ^= hash_32(buf[i], KFD_GPU_ID_HASH_WIDTH); diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c index 26f3e30..1126bd4 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c @@ -22,7 +22,6 @@ */ #include <linux/module.h> #include <linux/slab.h> -#include <linux/fb.h> #include "ppatomctrl.h" #include "atombios.h" diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c b/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c index 3373c32..7de701d 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c @@ -22,7 +22,6 @@ */ #include <linux/module.h> #include <linux/slab.h> -#include <linux/fb.h> #include "process_pptables_v1_0.h" #include "ppatomctrl.h" diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c index ef312bb..963a24d 100644 --- a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c +++ b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c @@ -405,7 +405,7 @@ void amd_sched_job_recovery(struct amd_gpu_scheduler *sched) spin_lock(&sched->job_list_lock); s_job = list_first_entry_or_null(&sched->ring_mirror_list, struct amd_sched_job, node); - if (s_job) + if (s_job && sched->timeout != MAX_SCHEDULE_TIMEOUT) schedule_delayed_work(&s_job->work_tdr, sched->timeout); list_for_each_entry_safe(s_job, tmp, &sched->ring_mirror_list, node) { diff --git a/drivers/gpu/drm/arc/arcpgu_crtc.c b/drivers/gpu/drm/arc/arcpgu_crtc.c index ee0a61c..7130b04 100644 --- a/drivers/gpu/drm/arc/arcpgu_crtc.c +++ b/drivers/gpu/drm/arc/arcpgu_crtc.c @@ -183,8 +183,6 @@ static void arc_pgu_plane_atomic_update(struct drm_plane *plane, } static const struct drm_plane_helper_funcs arc_pgu_plane_helper_funcs = { - .prepare_fb = NULL, - .cleanup_fb = NULL, .atomic_update = arc_pgu_plane_atomic_update, }; diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c index 6d4ff34..28e6471 100644 --- a/drivers/gpu/drm/arc/arcpgu_drv.c +++ b/drivers/gpu/drm/arc/arcpgu_drv.c @@ -198,8 +198,8 @@ static int arcpgu_probe(struct platform_device *pdev) int ret; drm = drm_dev_alloc(&arcpgu_drm_driver, &pdev->dev); - if (!drm) - return -ENOMEM; + if (IS_ERR(drm)) + return PTR_ERR(drm); ret = arcpgu_load(drm); if (ret) diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index d83b46a..fb6a418 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -326,8 +326,8 @@ static int hdlcd_drm_bind(struct device *dev) return -ENOMEM; drm = drm_dev_alloc(&hdlcd_driver, dev); - if (!drm) - return -ENOMEM; + if (IS_ERR(drm)) + return PTR_ERR(drm); drm->dev_private = hdlcd; dev_set_drvdata(dev, drm); diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index 82171d2..9280358 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -91,7 +91,8 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state) drm_atomic_helper_commit_modeset_disables(drm, state); drm_atomic_helper_commit_modeset_enables(drm, state); - drm_atomic_helper_commit_planes(drm, state, true); + drm_atomic_helper_commit_planes(drm, state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); malidp_atomic_commit_hw_done(state); @@ -310,8 +311,8 @@ static int malidp_bind(struct device *dev) return ret; drm = drm_dev_alloc(&malidp_driver, dev); - if (!drm) { - ret = -ENOMEM; + if (IS_ERR(drm)) { + ret = PTR_ERR(drm); goto alloc_fail; } diff --git a/drivers/gpu/drm/arm/malidp_drv.h b/drivers/gpu/drm/arm/malidp_drv.h index 95558fd..271d2fb 100644 --- a/drivers/gpu/drm/arm/malidp_drv.h +++ b/drivers/gpu/drm/arm/malidp_drv.h @@ -49,6 +49,6 @@ void malidp_de_planes_destroy(struct drm_device *drm); int malidp_crtc_init(struct drm_device *drm); /* often used combination of rotational bits */ -#define MALIDP_ROTATED_MASK (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270)) +#define MALIDP_ROTATED_MASK (DRM_ROTATE_90 | DRM_ROTATE_270) #endif /* __MALIDP_DRV_H__ */ diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index 725098d..82c193e 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -108,7 +108,7 @@ static int malidp_de_plane_check(struct drm_plane *plane, return -EINVAL; /* packed RGB888 / BGR888 can't be rotated or flipped */ - if (state->rotation != BIT(DRM_ROTATE_0) && + if (state->rotation != DRM_ROTATE_0 && (state->fb->pixel_format == DRM_FORMAT_RGB888 || state->fb->pixel_format == DRM_FORMAT_BGR888)) return -EINVAL; @@ -188,9 +188,9 @@ static void malidp_de_plane_update(struct drm_plane *plane, /* setup the rotation and axis flip bits */ if (plane->state->rotation & DRM_ROTATE_MASK) val = ilog2(plane->state->rotation & DRM_ROTATE_MASK) << LAYER_ROT_OFFSET; - if (plane->state->rotation & BIT(DRM_REFLECT_X)) + if (plane->state->rotation & DRM_REFLECT_X) val |= LAYER_V_FLIP; - if (plane->state->rotation & BIT(DRM_REFLECT_Y)) + if (plane->state->rotation & DRM_REFLECT_Y) val |= LAYER_H_FLIP; /* set the 'enable layer' bit */ @@ -255,12 +255,12 @@ int malidp_de_planes_init(struct drm_device *drm) goto cleanup; if (!drm->mode_config.rotation_property) { - unsigned long flags = BIT(DRM_ROTATE_0) | - BIT(DRM_ROTATE_90) | - BIT(DRM_ROTATE_180) | - BIT(DRM_ROTATE_270) | - BIT(DRM_REFLECT_X) | - BIT(DRM_REFLECT_Y); + unsigned long flags = DRM_ROTATE_0 | + DRM_ROTATE_90 | + DRM_ROTATE_180 | + DRM_ROTATE_270 | + DRM_REFLECT_X | + DRM_REFLECT_Y; drm->mode_config.rotation_property = drm_mode_create_rotation_property(drm, flags); } @@ -268,7 +268,7 @@ int malidp_de_planes_init(struct drm_device *drm) if (drm->mode_config.rotation_property && (id != DE_SMART)) drm_object_attach_property(&plane->base.base, drm->mode_config.rotation_property, - BIT(DRM_ROTATE_0)); + DRM_ROTATE_0); drm_plane_helper_add(&plane->base, &malidp_de_plane_helper_funcs); diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index f5ebdd6..1e0e68f 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -211,7 +211,7 @@ static struct drm_driver armada_drm_driver = { .desc = "Armada SoC DRM", .date = "20120730", .driver_features = DRIVER_GEM | DRIVER_MODESET | - DRIVER_HAVE_IRQ | DRIVER_PRIME, + DRIVER_PRIME, .ioctls = armada_ioctls, .fops = &armada_drm_fops, }; diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index 7d03c51..ca73ad8 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -7,7 +7,6 @@ * published by the Free Software Foundation. */ #include <linux/errno.h> -#include <linux/fb.h> #include <linux/kernel.h> #include <linux/module.h> diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c index 1ee707e..152b4e7 100644 --- a/drivers/gpu/drm/armada/armada_overlay.c +++ b/drivers/gpu/drm/armada/armada_overlay.c @@ -121,7 +121,7 @@ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, int ret; ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip, - BIT(DRM_ROTATE_0), + DRM_ROTATE_0, 0, INT_MAX, true, false, &visible); if (ret) return ret; diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index c017a93..7a86e24 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -33,7 +33,6 @@ #include <linux/tty.h> #include <linux/sysrq.h> #include <linux/delay.h> -#include <linux/fb.h> #include <linux/init.h> diff --git a/drivers/gpu/drm/ast/ast_ttm.c b/drivers/gpu/drm/ast/ast_ttm.c index b29a412..608df4c 100644 --- a/drivers/gpu/drm/ast/ast_ttm.c +++ b/drivers/gpu/drm/ast/ast_ttm.c @@ -150,7 +150,8 @@ static int ast_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp) { struct ast_bo *astbo = ast_bo(bo); - return drm_vma_node_verify_access(&astbo->gem.vma_node, filp); + return drm_vma_node_verify_access(&astbo->gem.vma_node, + filp->private_data); } static int ast_ttm_io_mem_reserve(struct ttm_bo_device *bdev, diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index a978381..9b17a66 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -387,7 +387,7 @@ void atmel_hlcdc_crtc_irq(struct drm_crtc *c) atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c)); } -void atmel_hlcdc_crtc_reset(struct drm_crtc *crtc) +static void atmel_hlcdc_crtc_reset(struct drm_crtc *crtc) { struct atmel_hlcdc_crtc_state *state; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index d4a3d61..5f48431 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -457,7 +457,7 @@ atmel_hlcdc_dc_atomic_complete(struct atmel_hlcdc_dc_commit *commit) /* Apply the atomic update. */ drm_atomic_helper_commit_modeset_disables(dev, old_state); - drm_atomic_helper_commit_planes(dev, old_state, false); + drm_atomic_helper_commit_planes(dev, old_state, 0); drm_atomic_helper_commit_modeset_enables(dev, old_state); drm_atomic_helper_wait_for_vblanks(dev, old_state); @@ -797,8 +797,8 @@ static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) int ret; ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev); - if (!ddev) - return -ENOMEM; + if (IS_ERR(ddev)) + return PTR_ERR(ddev); ret = atmel_hlcdc_dc_load(ddev); if (ret) diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index 016c191..9d4c030 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -320,19 +320,19 @@ atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane, u32 *coeff_tab = heo_upscaling_ycoef; u32 max_memsize; - if (state->crtc_w < state->src_w) + if (state->crtc_h < state->src_h) coeff_tab = heo_downscaling_ycoef; for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++) atmel_hlcdc_layer_update_cfg(&plane->layer, 33 + i, 0xffffffff, coeff_tab[i]); - factor = ((8 * 256 * state->src_w) - (256 * 4)) / - state->crtc_w; + factor = ((8 * 256 * state->src_h) - (256 * 4)) / + state->crtc_h; factor++; - max_memsize = ((factor * state->crtc_w) + (256 * 4)) / + max_memsize = ((factor * state->crtc_h) + (256 * 4)) / 2048; - if (max_memsize > state->src_w) + if (max_memsize > state->src_h) factor--; factor_reg |= (factor << 16) | 0x80000000; } @@ -393,7 +393,7 @@ static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane, if ((state->base.fb->pixel_format == DRM_FORMAT_YUV422 || state->base.fb->pixel_format == DRM_FORMAT_NV61) && - (state->base.rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270)))) + (state->base.rotation & (DRM_ROTATE_90 | DRM_ROTATE_270))) cfg |= ATMEL_HLCDC_YUV422ROT; atmel_hlcdc_layer_update_cfg(&plane->layer, @@ -628,7 +628,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, /* * Swap width and size in case of 90 or 270 degrees rotation */ - if (state->base.rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) { + if (state->base.rotation & (DRM_ROTATE_90 | DRM_ROTATE_270)) { tmp = state->crtc_w; state->crtc_w = state->crtc_h; state->crtc_h = tmp; @@ -677,7 +677,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, return -EINVAL; switch (state->base.rotation & DRM_ROTATE_MASK) { - case BIT(DRM_ROTATE_90): + case DRM_ROTATE_90: offset = ((y_offset + state->src_y + patched_src_w - 1) / ydiv) * fb->pitches[i]; offset += ((x_offset + state->src_x) / xdiv) * @@ -686,7 +686,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, fb->pitches[i]; state->pstride[i] = -fb->pitches[i] - state->bpp[i]; break; - case BIT(DRM_ROTATE_180): + case DRM_ROTATE_180: offset = ((y_offset + state->src_y + patched_src_h - 1) / ydiv) * fb->pitches[i]; offset += ((x_offset + state->src_x + patched_src_w - 1) / @@ -695,7 +695,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, state->bpp[i]) - fb->pitches[i]; state->pstride[i] = -2 * state->bpp[i]; break; - case BIT(DRM_ROTATE_270): + case DRM_ROTATE_270: offset = ((y_offset + state->src_y) / ydiv) * fb->pitches[i]; offset += ((x_offset + state->src_x + patched_src_h - 1) / @@ -705,7 +705,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, (2 * state->bpp[i]); state->pstride[i] = fb->pitches[i] - state->bpp[i]; break; - case BIT(DRM_ROTATE_0): + case DRM_ROTATE_0: default: offset = ((y_offset + state->src_y) / ydiv) * fb->pitches[i]; @@ -755,7 +755,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, } static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p, - const struct drm_plane_state *new_state) + struct drm_plane_state *new_state) { /* * FIXME: we should avoid this const -> non-const cast but it's @@ -780,7 +780,7 @@ static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p, } static void atmel_hlcdc_plane_cleanup_fb(struct drm_plane *p, - const struct drm_plane_state *old_state) + struct drm_plane_state *old_state) { /* * FIXME: we should avoid this const -> non-const cast but it's @@ -905,7 +905,7 @@ static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane, if (desc->layout.xstride && desc->layout.pstride) drm_object_attach_property(&plane->base.base, plane->base.dev->mode_config.rotation_property, - BIT(DRM_ROTATE_0)); + DRM_ROTATE_0); if (desc->layout.csc) { /* @@ -1056,10 +1056,10 @@ atmel_hlcdc_plane_create_properties(struct drm_device *dev) dev->mode_config.rotation_property = drm_mode_create_rotation_property(dev, - BIT(DRM_ROTATE_0) | - BIT(DRM_ROTATE_90) | - BIT(DRM_ROTATE_180) | - BIT(DRM_ROTATE_270)); + DRM_ROTATE_0 | + DRM_ROTATE_90 | + DRM_ROTATE_180 | + DRM_ROTATE_270); if (!dev->mode_config.rotation_property) return ERR_PTR(-ENOMEM); diff --git a/drivers/gpu/drm/bochs/bochs.h b/drivers/gpu/drm/bochs/bochs.h index 19b5ada..32dfe41 100644 --- a/drivers/gpu/drm/bochs/bochs.h +++ b/drivers/gpu/drm/bochs/bochs.h @@ -1,5 +1,4 @@ #include <linux/io.h> -#include <linux/fb.h> #include <linux/console.h> #include <drm/drmP.h> diff --git a/drivers/gpu/drm/bochs/bochs_drv.c b/drivers/gpu/drm/bochs/bochs_drv.c index abace82..534227d 100644 --- a/drivers/gpu/drm/bochs/bochs_drv.c +++ b/drivers/gpu/drm/bochs/bochs_drv.c @@ -8,6 +8,7 @@ #include <linux/mm.h> #include <linux/module.h> #include <linux/slab.h> +#include <drm/drm_fb_helper.h> #include "bochs.h" @@ -153,7 +154,7 @@ static int bochs_kick_out_firmware_fb(struct pci_dev *pdev) ap->ranges[0].base = pci_resource_start(pdev, 0); ap->ranges[0].size = pci_resource_len(pdev, 0); - remove_conflicting_framebuffers(ap, "bochsdrmfb", false); + drm_fb_helper_remove_conflicting_framebuffers(ap, "bochsdrmfb", false); kfree(ap); return 0; @@ -162,8 +163,15 @@ static int bochs_kick_out_firmware_fb(struct pci_dev *pdev) static int bochs_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + unsigned long fbsize; int ret; + fbsize = pci_resource_len(pdev, 0); + if (fbsize < 4 * 1024 * 1024) { + DRM_ERROR("less than 4 MB video memory, ignoring device\n"); + return -ENOMEM; + } + ret = bochs_kick_out_firmware_fb(pdev); if (ret) return ret; diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c index 207a2cb..0b4e5d1 100644 --- a/drivers/gpu/drm/bochs/bochs_kms.c +++ b/drivers/gpu/drm/bochs/bochs_kms.c @@ -178,7 +178,7 @@ static void bochs_encoder_init(struct drm_device *dev) } -int bochs_connector_get_modes(struct drm_connector *connector) +static int bochs_connector_get_modes(struct drm_connector *connector) { int count; diff --git a/drivers/gpu/drm/bochs/bochs_mm.c b/drivers/gpu/drm/bochs/bochs_mm.c index 5c5638a..269cfca9 100644 --- a/drivers/gpu/drm/bochs/bochs_mm.c +++ b/drivers/gpu/drm/bochs/bochs_mm.c @@ -128,7 +128,8 @@ static int bochs_bo_verify_access(struct ttm_buffer_object *bo, { struct bochs_bo *bochsbo = bochs_bo(bo); - return drm_vma_node_verify_access(&bochsbo->gem.vma_node, filp); + return drm_vma_node_verify_access(&bochsbo->gem.vma_node, + filp->private_data); } static int bochs_ttm_io_mem_reserve(struct ttm_bo_device *bdev, diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index ec8fb2e..8ed3906 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -922,15 +922,13 @@ static int adv7511_parse_dt(struct device_node *np, return 0; } -static const int edid_i2c_addr = 0x7e; -static const int packet_i2c_addr = 0x70; -static const int cec_i2c_addr = 0x78; - static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct adv7511_link_config link_config; struct adv7511 *adv7511; struct device *dev = &i2c->dev; + unsigned int main_i2c_addr = i2c->addr << 1; + unsigned int edid_i2c_addr = main_i2c_addr + 4; unsigned int val; int ret; @@ -991,8 +989,10 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR, edid_i2c_addr); regmap_write(adv7511->regmap, ADV7511_REG_PACKET_I2C_ADDR, - packet_i2c_addr); - regmap_write(adv7511->regmap, ADV7511_REG_CEC_I2C_ADDR, cec_i2c_addr); + main_i2c_addr - 0xa); + regmap_write(adv7511->regmap, ADV7511_REG_CEC_I2C_ADDR, + main_i2c_addr - 2); + adv7511_packet_disable(adv7511, 0xffff); adv7511->i2c_main = i2c; diff --git a/drivers/gpu/drm/bridge/adv7511/adv7533.c b/drivers/gpu/drm/bridge/adv7511/adv7533.c index 5eebd15..d7f7b7c 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7533.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7533.c @@ -149,13 +149,12 @@ void adv7533_uninit_cec(struct adv7511 *adv) i2c_unregister_device(adv->i2c_cec); } -static const int cec_i2c_addr = 0x78; - int adv7533_init_cec(struct adv7511 *adv) { int ret; - adv->i2c_cec = i2c_new_dummy(adv->i2c_main->adapter, cec_i2c_addr >> 1); + adv->i2c_cec = i2c_new_dummy(adv->i2c_main->adapter, + adv->i2c_main->addr - 1); if (!adv->i2c_cec) return -ENOMEM; diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 32715da..0f2e423 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -31,6 +31,7 @@ #include <drm/bridge/analogix_dp.h> #include "analogix_dp_core.h" +#include "analogix_dp_reg.h" #define to_dp(nm) container_of(nm, struct analogix_dp_device, nm) @@ -97,133 +98,81 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp) return 0; } -static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data) +int analogix_dp_enable_psr(struct device *dev) { - int i; - unsigned char sum = 0; + struct analogix_dp_device *dp = dev_get_drvdata(dev); + struct edp_vsc_psr psr_vsc; + + if (!dp->psr_support) + return -EINVAL; - for (i = 0; i < EDID_BLOCK_LENGTH; i++) - sum = sum + edid_data[i]; + /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */ + memset(&psr_vsc, 0, sizeof(psr_vsc)); + psr_vsc.sdp_header.HB0 = 0; + psr_vsc.sdp_header.HB1 = 0x7; + psr_vsc.sdp_header.HB2 = 0x2; + psr_vsc.sdp_header.HB3 = 0x8; - return sum; + psr_vsc.DB0 = 0; + psr_vsc.DB1 = EDP_VSC_PSR_STATE_ACTIVE | EDP_VSC_PSR_CRC_VALUES_VALID; + + analogix_dp_send_psr_spd(dp, &psr_vsc); + return 0; } +EXPORT_SYMBOL_GPL(analogix_dp_enable_psr); -static int analogix_dp_read_edid(struct analogix_dp_device *dp) +int analogix_dp_disable_psr(struct device *dev) { - unsigned char *edid = dp->edid; - unsigned int extend_block = 0; - unsigned char sum; - unsigned char test_vector; - int retval; + struct analogix_dp_device *dp = dev_get_drvdata(dev); + struct edp_vsc_psr psr_vsc; - /* - * EDID device address is 0x50. - * However, if necessary, you must have set upper address - * into E-EDID in I2C device, 0x30. - */ + if (!dp->psr_support) + return -EINVAL; - /* Read Extension Flag, Number of 128-byte EDID extension blocks */ - retval = analogix_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR, - EDID_EXTENSION_FLAG, - &extend_block); - if (retval) - return retval; + /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */ + memset(&psr_vsc, 0, sizeof(psr_vsc)); + psr_vsc.sdp_header.HB0 = 0; + psr_vsc.sdp_header.HB1 = 0x7; + psr_vsc.sdp_header.HB2 = 0x2; + psr_vsc.sdp_header.HB3 = 0x8; - if (extend_block > 0) { - dev_dbg(dp->dev, "EDID data includes a single extension!\n"); - - /* Read EDID data */ - retval = analogix_dp_read_bytes_from_i2c(dp, - I2C_EDID_DEVICE_ADDR, - EDID_HEADER_PATTERN, - EDID_BLOCK_LENGTH, - &edid[EDID_HEADER_PATTERN]); - if (retval != 0) { - dev_err(dp->dev, "EDID Read failed!\n"); - return -EIO; - } - sum = analogix_dp_calc_edid_check_sum(edid); - if (sum != 0) { - dev_err(dp->dev, "EDID bad checksum!\n"); - return -EIO; - } + psr_vsc.DB0 = 0; + psr_vsc.DB1 = 0; - /* Read additional EDID data */ - retval = analogix_dp_read_bytes_from_i2c(dp, - I2C_EDID_DEVICE_ADDR, - EDID_BLOCK_LENGTH, - EDID_BLOCK_LENGTH, - &edid[EDID_BLOCK_LENGTH]); - if (retval != 0) { - dev_err(dp->dev, "EDID Read failed!\n"); - return -EIO; - } - sum = analogix_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]); - if (sum != 0) { - dev_err(dp->dev, "EDID bad checksum!\n"); - return -EIO; - } + analogix_dp_send_psr_spd(dp, &psr_vsc); + return 0; +} +EXPORT_SYMBOL_GPL(analogix_dp_disable_psr); - analogix_dp_read_byte_from_dpcd(dp, DP_TEST_REQUEST, - &test_vector); - if (test_vector & DP_TEST_LINK_EDID_READ) { - analogix_dp_write_byte_to_dpcd(dp, - DP_TEST_EDID_CHECKSUM, - edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]); - analogix_dp_write_byte_to_dpcd(dp, - DP_TEST_RESPONSE, - DP_TEST_EDID_CHECKSUM_WRITE); - } - } else { - dev_info(dp->dev, "EDID data does not include any extensions.\n"); - - /* Read EDID data */ - retval = analogix_dp_read_bytes_from_i2c(dp, - I2C_EDID_DEVICE_ADDR, EDID_HEADER_PATTERN, - EDID_BLOCK_LENGTH, &edid[EDID_HEADER_PATTERN]); - if (retval != 0) { - dev_err(dp->dev, "EDID Read failed!\n"); - return -EIO; - } - sum = analogix_dp_calc_edid_check_sum(edid); - if (sum != 0) { - dev_err(dp->dev, "EDID bad checksum!\n"); - return -EIO; - } +static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp) +{ + unsigned char psr_version; - analogix_dp_read_byte_from_dpcd(dp, DP_TEST_REQUEST, - &test_vector); - if (test_vector & DP_TEST_LINK_EDID_READ) { - analogix_dp_write_byte_to_dpcd(dp, - DP_TEST_EDID_CHECKSUM, edid[EDID_CHECKSUM]); - analogix_dp_write_byte_to_dpcd(dp, - DP_TEST_RESPONSE, DP_TEST_EDID_CHECKSUM_WRITE); - } - } + drm_dp_dpcd_readb(&dp->aux, DP_PSR_SUPPORT, &psr_version); + dev_dbg(dp->dev, "Panel PSR version : %x\n", psr_version); - dev_dbg(dp->dev, "EDID Read success!\n"); - return 0; + return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false; } -static int analogix_dp_handle_edid(struct analogix_dp_device *dp) +static void analogix_dp_enable_sink_psr(struct analogix_dp_device *dp) { - u8 buf[12]; - int i; - int retval; + unsigned char psr_en; - /* Read DPCD DP_DPCD_REV~RECEIVE_PORT1_CAP_1 */ - retval = analogix_dp_read_bytes_from_dpcd(dp, DP_DPCD_REV, 12, buf); - if (retval) - return retval; + /* Disable psr function */ + drm_dp_dpcd_readb(&dp->aux, DP_PSR_EN_CFG, &psr_en); + psr_en &= ~DP_PSR_ENABLE; + drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en); - /* Read EDID */ - for (i = 0; i < 3; i++) { - retval = analogix_dp_read_edid(dp); - if (!retval) - break; - } + /* Main-Link transmitter remains active during PSR active states */ + psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION; + drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en); - return retval; + /* Enable psr function */ + psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE | + DP_PSR_CRC_VERIFICATION; + drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en); + + analogix_dp_enable_psr_crc(dp); } static void @@ -232,15 +181,15 @@ analogix_dp_enable_rx_to_enhanced_mode(struct analogix_dp_device *dp, { u8 data; - analogix_dp_read_byte_from_dpcd(dp, DP_LANE_COUNT_SET, &data); + drm_dp_dpcd_readb(&dp->aux, DP_LANE_COUNT_SET, &data); if (enable) - analogix_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET, - DP_LANE_COUNT_ENHANCED_FRAME_EN | - DPCD_LANE_COUNT_SET(data)); + drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET, + DP_LANE_COUNT_ENHANCED_FRAME_EN | + DPCD_LANE_COUNT_SET(data)); else - analogix_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET, - DPCD_LANE_COUNT_SET(data)); + drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET, + DPCD_LANE_COUNT_SET(data)); } static int analogix_dp_is_enhanced_mode_available(struct analogix_dp_device *dp) @@ -248,7 +197,7 @@ static int analogix_dp_is_enhanced_mode_available(struct analogix_dp_device *dp) u8 data; int retval; - analogix_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data); + drm_dp_dpcd_readb(&dp->aux, DP_MAX_LANE_COUNT, &data); retval = DPCD_ENHANCED_FRAME_CAP(data); return retval; @@ -267,8 +216,8 @@ static void analogix_dp_training_pattern_dis(struct analogix_dp_device *dp) { analogix_dp_set_training_pattern(dp, DP_NONE); - analogix_dp_write_byte_to_dpcd(dp, DP_TRAINING_PATTERN_SET, - DP_TRAINING_PATTERN_DISABLE); + drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_DISABLE); } static void @@ -313,8 +262,8 @@ static int analogix_dp_link_start(struct analogix_dp_device *dp) /* Setup RX configuration */ buf[0] = dp->link_train.link_rate; buf[1] = dp->link_train.lane_count; - retval = analogix_dp_write_bytes_to_dpcd(dp, DP_LINK_BW_SET, 2, buf); - if (retval) + retval = drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, buf, 2); + if (retval < 0) return retval; /* Set TX pre-emphasis to minimum */ @@ -338,20 +287,22 @@ static int analogix_dp_link_start(struct analogix_dp_device *dp) analogix_dp_set_training_pattern(dp, TRAINING_PTN1); /* Set RX training pattern */ - retval = analogix_dp_write_byte_to_dpcd(dp, - DP_TRAINING_PATTERN_SET, - DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1); - if (retval) + retval = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, + DP_LINK_SCRAMBLING_DISABLE | + DP_TRAINING_PATTERN_1); + if (retval < 0) return retval; for (lane = 0; lane < lane_count; lane++) buf[lane] = DP_TRAIN_PRE_EMPH_LEVEL_0 | DP_TRAIN_VOLTAGE_SWING_LEVEL_0; - retval = analogix_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET, - lane_count, buf); + retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, buf, + lane_count); + if (retval < 0) + return retval; - return retval; + return 0; } static unsigned char analogix_dp_get_lane_status(u8 link_status[2], int lane) @@ -503,25 +454,23 @@ static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp) lane_count = dp->link_train.lane_count; - retval = analogix_dp_read_bytes_from_dpcd(dp, - DP_LANE0_1_STATUS, 2, link_status); - if (retval) + retval = drm_dp_dpcd_read(&dp->aux, DP_LANE0_1_STATUS, link_status, 2); + if (retval < 0) return retval; - retval = analogix_dp_read_bytes_from_dpcd(dp, - DP_ADJUST_REQUEST_LANE0_1, 2, adjust_request); - if (retval) + retval = drm_dp_dpcd_read(&dp->aux, DP_ADJUST_REQUEST_LANE0_1, + adjust_request, 2); + if (retval < 0) return retval; if (analogix_dp_clock_recovery_ok(link_status, lane_count) == 0) { /* set training pattern 2 for EQ */ analogix_dp_set_training_pattern(dp, TRAINING_PTN2); - retval = analogix_dp_write_byte_to_dpcd(dp, - DP_TRAINING_PATTERN_SET, - DP_LINK_SCRAMBLING_DISABLE | - DP_TRAINING_PATTERN_2); - if (retval) + retval = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, + DP_LINK_SCRAMBLING_DISABLE | + DP_TRAINING_PATTERN_2); + if (retval < 0) return retval; dev_info(dp->dev, "Link Training Clock Recovery success\n"); @@ -559,13 +508,12 @@ static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp) analogix_dp_set_lane_link_training(dp, dp->link_train.training_lane[lane], lane); - retval = analogix_dp_write_bytes_to_dpcd(dp, - DP_TRAINING_LANE0_SET, lane_count, - dp->link_train.training_lane); - if (retval) + retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, + dp->link_train.training_lane, lane_count); + if (retval < 0) return retval; - return retval; + return 0; } static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp) @@ -578,9 +526,8 @@ static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp) lane_count = dp->link_train.lane_count; - retval = analogix_dp_read_bytes_from_dpcd(dp, - DP_LANE0_1_STATUS, 2, link_status); - if (retval) + retval = drm_dp_dpcd_read(&dp->aux, DP_LANE0_1_STATUS, link_status, 2); + if (retval < 0) return retval; if (analogix_dp_clock_recovery_ok(link_status, lane_count)) { @@ -588,14 +535,14 @@ static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp) return -EIO; } - retval = analogix_dp_read_bytes_from_dpcd(dp, - DP_ADJUST_REQUEST_LANE0_1, 2, adjust_request); - if (retval) + retval = drm_dp_dpcd_read(&dp->aux, DP_ADJUST_REQUEST_LANE0_1, + adjust_request, 2); + if (retval < 0) return retval; - retval = analogix_dp_read_byte_from_dpcd(dp, - DP_LANE_ALIGN_STATUS_UPDATED, &link_align); - if (retval) + retval = drm_dp_dpcd_readb(&dp->aux, DP_LANE_ALIGN_STATUS_UPDATED, + &link_align); + if (retval < 0) return retval; analogix_dp_get_adjust_training_lane(dp, adjust_request); @@ -636,10 +583,12 @@ static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp) analogix_dp_set_lane_link_training(dp, dp->link_train.training_lane[lane], lane); - retval = analogix_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET, - lane_count, dp->link_train.training_lane); + retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, + dp->link_train.training_lane, lane_count); + if (retval < 0) + return retval; - return retval; + return 0; } static void analogix_dp_get_max_rx_bandwidth(struct analogix_dp_device *dp, @@ -653,7 +602,7 @@ static void analogix_dp_get_max_rx_bandwidth(struct analogix_dp_device *dp, * For DP rev.1.2, Maximum link rate of Main Link lanes * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps, 0x14 = 5.4Gbps */ - analogix_dp_read_byte_from_dpcd(dp, DP_MAX_LINK_RATE, &data); + drm_dp_dpcd_readb(&dp->aux, DP_MAX_LINK_RATE, &data); *bandwidth = data; } @@ -666,7 +615,7 @@ static void analogix_dp_get_max_rx_lane_count(struct analogix_dp_device *dp, * For DP rev.1.1, Maximum number of Main Link lanes * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes */ - analogix_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data); + drm_dp_dpcd_readb(&dp->aux, DP_MAX_LANE_COUNT, &data); *lane_count = DPCD_MAX_LANE_COUNT(data); } @@ -835,19 +784,15 @@ static void analogix_dp_enable_scramble(struct analogix_dp_device *dp, if (enable) { analogix_dp_enable_scrambling(dp); - analogix_dp_read_byte_from_dpcd(dp, DP_TRAINING_PATTERN_SET, - &data); - analogix_dp_write_byte_to_dpcd(dp, - DP_TRAINING_PATTERN_SET, - (u8)(data & ~DP_LINK_SCRAMBLING_DISABLE)); + drm_dp_dpcd_readb(&dp->aux, DP_TRAINING_PATTERN_SET, &data); + drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, + (u8)(data & ~DP_LINK_SCRAMBLING_DISABLE)); } else { analogix_dp_disable_scrambling(dp); - analogix_dp_read_byte_from_dpcd(dp, DP_TRAINING_PATTERN_SET, - &data); - analogix_dp_write_byte_to_dpcd(dp, - DP_TRAINING_PATTERN_SET, - (u8)(data | DP_LINK_SCRAMBLING_DISABLE)); + drm_dp_dpcd_readb(&dp->aux, DP_TRAINING_PATTERN_SET, &data); + drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, + (u8)(data | DP_LINK_SCRAMBLING_DISABLE)); } } @@ -921,21 +866,85 @@ static void analogix_dp_commit(struct analogix_dp_device *dp) /* Enable video */ analogix_dp_start_video(dp); + + dp->psr_support = analogix_dp_detect_sink_psr(dp); + if (dp->psr_support) + analogix_dp_enable_sink_psr(dp); } -int analogix_dp_get_modes(struct drm_connector *connector) +/* + * This function is a bit of a catch-all for panel preparation, hopefully + * simplifying the logic of functions that need to prepare/unprepare the panel + * below. + * + * If @prepare is true, this function will prepare the panel. Conversely, if it + * is false, the panel will be unprepared. + * + * If @is_modeset_prepare is true, the function will disregard the current state + * of the panel and either prepare/unprepare the panel based on @prepare. Once + * it finishes, it will update dp->panel_is_modeset to reflect the current state + * of the panel. + */ +static int analogix_dp_prepare_panel(struct analogix_dp_device *dp, + bool prepare, bool is_modeset_prepare) { - struct analogix_dp_device *dp = to_dp(connector); - struct edid *edid = (struct edid *)dp->edid; - int num_modes = 0; + int ret = 0; - if (analogix_dp_handle_edid(dp) == 0) { - drm_mode_connector_update_edid_property(&dp->connector, edid); - num_modes += drm_add_edid_modes(&dp->connector, edid); - } + if (!dp->plat_data->panel) + return 0; - if (dp->plat_data->panel) + mutex_lock(&dp->panel_lock); + + /* + * Exit early if this is a temporary prepare/unprepare and we're already + * modeset (since we neither want to prepare twice or unprepare early). + */ + if (dp->panel_is_modeset && !is_modeset_prepare) + goto out; + + if (prepare) + ret = drm_panel_prepare(dp->plat_data->panel); + else + ret = drm_panel_unprepare(dp->plat_data->panel); + + if (ret) + goto out; + + if (is_modeset_prepare) + dp->panel_is_modeset = prepare; + +out: + mutex_unlock(&dp->panel_lock); + return ret; +} + +static int analogix_dp_get_modes(struct drm_connector *connector) +{ + struct analogix_dp_device *dp = to_dp(connector); + struct edid *edid; + int ret, num_modes = 0; + + if (dp->plat_data->panel) { num_modes += drm_panel_get_modes(dp->plat_data->panel); + } else { + ret = analogix_dp_prepare_panel(dp, true, false); + if (ret) { + DRM_ERROR("Failed to prepare panel (%d)\n", ret); + return 0; + } + + edid = drm_get_edid(connector, &dp->aux.ddc); + if (edid) { + drm_mode_connector_update_edid_property(&dp->connector, + edid); + num_modes += drm_add_edid_modes(&dp->connector, edid); + kfree(edid); + } + + ret = analogix_dp_prepare_panel(dp, false, false); + if (ret) + DRM_ERROR("Failed to unprepare panel (%d)\n", ret); + } if (dp->plat_data->get_modes) num_modes += dp->plat_data->get_modes(dp->plat_data, connector); @@ -956,15 +965,30 @@ static const struct drm_connector_helper_funcs analogix_dp_connector_helper_func .best_encoder = analogix_dp_best_encoder, }; -enum drm_connector_status +static enum drm_connector_status analogix_dp_detect(struct drm_connector *connector, bool force) { struct analogix_dp_device *dp = to_dp(connector); + enum drm_connector_status status = connector_status_disconnected; + int ret; + + if (dp->plat_data->panel) + return connector_status_connected; - if (analogix_dp_detect_hpd(dp)) + ret = analogix_dp_prepare_panel(dp, true, false); + if (ret) { + DRM_ERROR("Failed to prepare panel (%d)\n", ret); return connector_status_disconnected; + } + + if (!analogix_dp_detect_hpd(dp)) + status = connector_status_connected; - return connector_status_connected; + ret = analogix_dp_prepare_panel(dp, false, false); + if (ret) + DRM_ERROR("Failed to unprepare panel (%d)\n", ret); + + return status; } static void analogix_dp_connector_destroy(struct drm_connector *connector) @@ -1035,6 +1059,16 @@ static int analogix_dp_bridge_attach(struct drm_bridge *bridge) return 0; } +static void analogix_dp_bridge_pre_enable(struct drm_bridge *bridge) +{ + struct analogix_dp_device *dp = bridge->driver_private; + int ret; + + ret = analogix_dp_prepare_panel(dp, true, true); + if (ret) + DRM_ERROR("failed to setup the panel ret = %d\n", ret); +} + static void analogix_dp_bridge_enable(struct drm_bridge *bridge) { struct analogix_dp_device *dp = bridge->driver_private; @@ -1058,6 +1092,7 @@ static void analogix_dp_bridge_enable(struct drm_bridge *bridge) static void analogix_dp_bridge_disable(struct drm_bridge *bridge) { struct analogix_dp_device *dp = bridge->driver_private; + int ret; if (dp->dpms_mode != DRM_MODE_DPMS_ON) return; @@ -1077,6 +1112,10 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge) pm_runtime_put_sync(dp->dev); + ret = analogix_dp_prepare_panel(dp, false, true); + if (ret) + DRM_ERROR("failed to setup the panel ret = %d\n", ret); + dp->dpms_mode = DRM_MODE_DPMS_OFF; } @@ -1165,9 +1204,9 @@ static void analogix_dp_bridge_nop(struct drm_bridge *bridge) } static const struct drm_bridge_funcs analogix_dp_bridge_funcs = { + .pre_enable = analogix_dp_bridge_pre_enable, .enable = analogix_dp_bridge_enable, .disable = analogix_dp_bridge_disable, - .pre_enable = analogix_dp_bridge_nop, .post_disable = analogix_dp_bridge_nop, .mode_set = analogix_dp_bridge_mode_set, .attach = analogix_dp_bridge_attach, @@ -1231,6 +1270,14 @@ static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp) return 0; } +static ssize_t analogix_dpaux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + struct analogix_dp_device *dp = to_dp(aux); + + return analogix_dp_transfer(dp, msg); +} + int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, struct analogix_dp_plat_data *plat_data) { @@ -1254,6 +1301,9 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, dp->dev = &pdev->dev; dp->dpms_mode = DRM_MODE_DPMS_OFF; + mutex_init(&dp->panel_lock); + dp->panel_is_modeset = false; + /* * platform dp driver need containor_of the plat_data to get * the driver private data, so we need to store the point of @@ -1333,13 +1383,6 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, phy_power_on(dp->phy); - if (dp->plat_data->panel) { - if (drm_panel_prepare(dp->plat_data->panel)) { - DRM_ERROR("failed to setup the panel\n"); - return -EBUSY; - } - } - analogix_dp_init_dp(dp); ret = devm_request_threaded_irq(&pdev->dev, dp->irq, @@ -1355,6 +1398,14 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev, dp->drm_dev = drm_dev; dp->encoder = dp->plat_data->encoder; + dp->aux.name = "DP-AUX"; + dp->aux.transfer = analogix_dpaux_transfer; + dp->aux.dev = &pdev->dev; + + ret = drm_dp_aux_register(&dp->aux); + if (ret) + goto err_disable_pm_runtime; + ret = analogix_dp_create_bridge(drm_dev, dp); if (ret) { DRM_ERROR("failed to create bridge (%d)\n", ret); diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h index b456380..5c6a288 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h @@ -20,15 +20,6 @@ #define MAX_CR_LOOP 5 #define MAX_EQ_LOOP 5 -/* I2C EDID Chip ID, Slave Address */ -#define I2C_EDID_DEVICE_ADDR 0x50 -#define I2C_E_EDID_DEVICE_ADDR 0x30 - -#define EDID_BLOCK_LENGTH 0x80 -#define EDID_HEADER_PATTERN 0x00 -#define EDID_EXTENSION_FLAG 0x7e -#define EDID_CHECKSUM 0x7f - /* DP_MAX_LANE_COUNT */ #define DPCD_ENHANCED_FRAME_CAP(x) (((x) >> 7) & 0x1) #define DPCD_MAX_LANE_COUNT(x) ((x) & 0x1f) @@ -166,6 +157,7 @@ struct analogix_dp_device { struct drm_device *drm_dev; struct drm_connector connector; struct drm_bridge *bridge; + struct drm_dp_aux aux; struct clk *clock; unsigned int irq; void __iomem *reg_base; @@ -176,7 +168,10 @@ struct analogix_dp_device { int dpms_mode; int hpd_gpio; bool force_hpd; - unsigned char edid[EDID_BLOCK_LENGTH * 2]; + bool psr_support; + + struct mutex panel_lock; + bool panel_is_modeset; struct analogix_dp_plat_data *plat_data; }; @@ -206,33 +201,6 @@ void analogix_dp_reset_aux(struct analogix_dp_device *dp); void analogix_dp_init_aux(struct analogix_dp_device *dp); int analogix_dp_get_plug_in_status(struct analogix_dp_device *dp); void analogix_dp_enable_sw_function(struct analogix_dp_device *dp); -int analogix_dp_start_aux_transaction(struct analogix_dp_device *dp); -int analogix_dp_write_byte_to_dpcd(struct analogix_dp_device *dp, - unsigned int reg_addr, - unsigned char data); -int analogix_dp_read_byte_from_dpcd(struct analogix_dp_device *dp, - unsigned int reg_addr, - unsigned char *data); -int analogix_dp_write_bytes_to_dpcd(struct analogix_dp_device *dp, - unsigned int reg_addr, - unsigned int count, - unsigned char data[]); -int analogix_dp_read_bytes_from_dpcd(struct analogix_dp_device *dp, - unsigned int reg_addr, - unsigned int count, - unsigned char data[]); -int analogix_dp_select_i2c_device(struct analogix_dp_device *dp, - unsigned int device_addr, - unsigned int reg_addr); -int analogix_dp_read_byte_from_i2c(struct analogix_dp_device *dp, - unsigned int device_addr, - unsigned int reg_addr, - unsigned int *data); -int analogix_dp_read_bytes_from_i2c(struct analogix_dp_device *dp, - unsigned int device_addr, - unsigned int reg_addr, - unsigned int count, - unsigned char edid[]); void analogix_dp_set_link_bandwidth(struct analogix_dp_device *dp, u32 bwtype); void analogix_dp_get_link_bandwidth(struct analogix_dp_device *dp, u32 *bwtype); void analogix_dp_set_lane_count(struct analogix_dp_device *dp, u32 count); @@ -278,4 +246,10 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp); void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp); void analogix_dp_enable_scrambling(struct analogix_dp_device *dp); void analogix_dp_disable_scrambling(struct analogix_dp_device *dp); +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp); +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, + struct edp_vsc_psr *vsc); +ssize_t analogix_dp_transfer(struct analogix_dp_device *dp, + struct drm_dp_aux_msg *msg); + #endif /* _ANALOGIX_DP_CORE_H */ diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c index 48030f0..cd37ac0 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c @@ -585,330 +585,6 @@ int analogix_dp_write_byte_to_dpcd(struct analogix_dp_device *dp, return retval; } -int analogix_dp_read_byte_from_dpcd(struct analogix_dp_device *dp, - unsigned int reg_addr, - unsigned char *data) -{ - u32 reg; - int i; - int retval; - - for (i = 0; i < 3; i++) { - /* Clear AUX CH data buffer */ - reg = BUF_CLR; - writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL); - - /* Select DPCD device address */ - reg = AUX_ADDR_7_0(reg_addr); - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0); - reg = AUX_ADDR_15_8(reg_addr); - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8); - reg = AUX_ADDR_19_16(reg_addr); - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16); - - /* - * Set DisplayPort transaction and read 1 byte - * If bit 3 is 1, DisplayPort transaction. - * If Bit 3 is 0, I2C transaction. - */ - reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1); - - /* Start AUX transaction */ - retval = analogix_dp_start_aux_transaction(dp); - if (retval == 0) - break; - - dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", __func__); - } - - /* Read data buffer */ - reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0); - *data = (unsigned char)(reg & 0xff); - - return retval; -} - -int analogix_dp_write_bytes_to_dpcd(struct analogix_dp_device *dp, - unsigned int reg_addr, - unsigned int count, - unsigned char data[]) -{ - u32 reg; - unsigned int start_offset; - unsigned int cur_data_count; - unsigned int cur_data_idx; - int i; - int retval = 0; - - /* Clear AUX CH data buffer */ - reg = BUF_CLR; - writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL); - - start_offset = 0; - while (start_offset < count) { - /* Buffer size of AUX CH is 16 * 4bytes */ - if ((count - start_offset) > 16) - cur_data_count = 16; - else - cur_data_count = count - start_offset; - - for (i = 0; i < 3; i++) { - /* Select DPCD device address */ - reg = AUX_ADDR_7_0(reg_addr + start_offset); - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0); - reg = AUX_ADDR_15_8(reg_addr + start_offset); - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8); - reg = AUX_ADDR_19_16(reg_addr + start_offset); - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16); - - for (cur_data_idx = 0; cur_data_idx < cur_data_count; - cur_data_idx++) { - reg = data[start_offset + cur_data_idx]; - writel(reg, dp->reg_base + - ANALOGIX_DP_BUF_DATA_0 + - 4 * cur_data_idx); - } - - /* - * Set DisplayPort transaction and write - * If bit 3 is 1, DisplayPort transaction. - * If Bit 3 is 0, I2C transaction. - */ - reg = AUX_LENGTH(cur_data_count) | - AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1); - - /* Start AUX transaction */ - retval = analogix_dp_start_aux_transaction(dp); - if (retval == 0) - break; - - dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", - __func__); - } - - start_offset += cur_data_count; - } - - return retval; -} - -int analogix_dp_read_bytes_from_dpcd(struct analogix_dp_device *dp, - unsigned int reg_addr, - unsigned int count, - unsigned char data[]) -{ - u32 reg; - unsigned int start_offset; - unsigned int cur_data_count; - unsigned int cur_data_idx; - int i; - int retval = 0; - - /* Clear AUX CH data buffer */ - reg = BUF_CLR; - writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL); - - start_offset = 0; - while (start_offset < count) { - /* Buffer size of AUX CH is 16 * 4bytes */ - if ((count - start_offset) > 16) - cur_data_count = 16; - else - cur_data_count = count - start_offset; - - /* AUX CH Request Transaction process */ - for (i = 0; i < 3; i++) { - /* Select DPCD device address */ - reg = AUX_ADDR_7_0(reg_addr + start_offset); - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0); - reg = AUX_ADDR_15_8(reg_addr + start_offset); - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8); - reg = AUX_ADDR_19_16(reg_addr + start_offset); - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16); - - /* - * Set DisplayPort transaction and read - * If bit 3 is 1, DisplayPort transaction. - * If Bit 3 is 0, I2C transaction. - */ - reg = AUX_LENGTH(cur_data_count) | - AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1); - - /* Start AUX transaction */ - retval = analogix_dp_start_aux_transaction(dp); - if (retval == 0) - break; - - dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", - __func__); - } - - for (cur_data_idx = 0; cur_data_idx < cur_data_count; - cur_data_idx++) { - reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0 - + 4 * cur_data_idx); - data[start_offset + cur_data_idx] = - (unsigned char)reg; - } - - start_offset += cur_data_count; - } - - return retval; -} - -int analogix_dp_select_i2c_device(struct analogix_dp_device *dp, - unsigned int device_addr, - unsigned int reg_addr) -{ - u32 reg; - int retval; - - /* Set EDID device address */ - reg = device_addr; - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0); - writel(0x0, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8); - writel(0x0, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16); - - /* Set offset from base address of EDID device */ - writel(reg_addr, dp->reg_base + ANALOGIX_DP_BUF_DATA_0); - - /* - * Set I2C transaction and write address - * If bit 3 is 1, DisplayPort transaction. - * If Bit 3 is 0, I2C transaction. - */ - reg = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT | - AUX_TX_COMM_WRITE; - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1); - - /* Start AUX transaction */ - retval = analogix_dp_start_aux_transaction(dp); - if (retval != 0) - dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", __func__); - - return retval; -} - -int analogix_dp_read_byte_from_i2c(struct analogix_dp_device *dp, - unsigned int device_addr, - unsigned int reg_addr, - unsigned int *data) -{ - u32 reg; - int i; - int retval; - - for (i = 0; i < 3; i++) { - /* Clear AUX CH data buffer */ - reg = BUF_CLR; - writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL); - - /* Select EDID device */ - retval = analogix_dp_select_i2c_device(dp, device_addr, - reg_addr); - if (retval != 0) - continue; - - /* - * Set I2C transaction and read data - * If bit 3 is 1, DisplayPort transaction. - * If Bit 3 is 0, I2C transaction. - */ - reg = AUX_TX_COMM_I2C_TRANSACTION | - AUX_TX_COMM_READ; - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1); - - /* Start AUX transaction */ - retval = analogix_dp_start_aux_transaction(dp); - if (retval == 0) - break; - - dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", __func__); - } - - /* Read data */ - if (retval == 0) - *data = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0); - - return retval; -} - -int analogix_dp_read_bytes_from_i2c(struct analogix_dp_device *dp, - unsigned int device_addr, - unsigned int reg_addr, - unsigned int count, - unsigned char edid[]) -{ - u32 reg; - unsigned int i, j; - unsigned int cur_data_idx; - unsigned int defer = 0; - int retval = 0; - - for (i = 0; i < count; i += 16) { - for (j = 0; j < 3; j++) { - /* Clear AUX CH data buffer */ - reg = BUF_CLR; - writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL); - - /* Set normal AUX CH command */ - reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2); - reg &= ~ADDR_ONLY; - writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2); - - /* - * If Rx sends defer, Tx sends only reads - * request without sending address - */ - if (!defer) - retval = analogix_dp_select_i2c_device(dp, - device_addr, reg_addr + i); - else - defer = 0; - - if (retval == 0) { - /* - * Set I2C transaction and write data - * If bit 3 is 1, DisplayPort transaction. - * If Bit 3 is 0, I2C transaction. - */ - reg = AUX_LENGTH(16) | - AUX_TX_COMM_I2C_TRANSACTION | - AUX_TX_COMM_READ; - writel(reg, dp->reg_base + - ANALOGIX_DP_AUX_CH_CTL_1); - - /* Start AUX transaction */ - retval = analogix_dp_start_aux_transaction(dp); - if (retval == 0) - break; - - dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", - __func__); - } - /* Check if Rx sends defer */ - reg = readl(dp->reg_base + ANALOGIX_DP_AUX_RX_COMM); - if (reg == AUX_RX_COMM_AUX_DEFER || - reg == AUX_RX_COMM_I2C_DEFER) { - dev_err(dp->dev, "Defer: %d\n\n", reg); - defer = 1; - } - } - - for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) { - reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0 - + 4 * cur_data_idx); - edid[i + cur_data_idx] = (unsigned char)reg; - } - } - - return retval; -} - void analogix_dp_set_link_bandwidth(struct analogix_dp_device *dp, u32 bwtype) { u32 reg; @@ -1073,34 +749,22 @@ void analogix_dp_set_lane3_link_training(struct analogix_dp_device *dp, u32 analogix_dp_get_lane0_link_training(struct analogix_dp_device *dp) { - u32 reg; - - reg = readl(dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL); - return reg; + return readl(dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL); } u32 analogix_dp_get_lane1_link_training(struct analogix_dp_device *dp) { - u32 reg; - - reg = readl(dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL); - return reg; + return readl(dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL); } u32 analogix_dp_get_lane2_link_training(struct analogix_dp_device *dp) { - u32 reg; - - reg = readl(dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL); - return reg; + return readl(dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL); } u32 analogix_dp_get_lane3_link_training(struct analogix_dp_device *dp) { - u32 reg; - - reg = readl(dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL); - return reg; + return readl(dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL); } void analogix_dp_reset_macro(struct analogix_dp_device *dp) @@ -1322,3 +986,181 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp) reg |= SCRAMBLING_DISABLE; writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET); } + +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp) +{ + writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON); +} + +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, + struct edp_vsc_psr *vsc) +{ + unsigned int val; + + /* don't send info frame */ + val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL); + val &= ~IF_EN; + writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL); + + /* configure single frame update mode */ + writel(PSR_FRAME_UP_TYPE_BURST | PSR_CRC_SEL_HARDWARE, + dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL); + + /* configure VSC HB0~HB3 */ + writel(vsc->sdp_header.HB0, dp->reg_base + ANALOGIX_DP_SPD_HB0); + writel(vsc->sdp_header.HB1, dp->reg_base + ANALOGIX_DP_SPD_HB1); + writel(vsc->sdp_header.HB2, dp->reg_base + ANALOGIX_DP_SPD_HB2); + writel(vsc->sdp_header.HB3, dp->reg_base + ANALOGIX_DP_SPD_HB3); + + /* configure reused VSC PB0~PB3, magic number from vendor */ + writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0); + writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1); + writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2); + writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3); + + /* configure DB0 / DB1 values */ + writel(vsc->DB0, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0); + writel(vsc->DB1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1); + + /* set reuse spd inforframe */ + val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3); + val |= REUSE_SPD_EN; + writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3); + + /* mark info frame update */ + val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL); + val = (val | IF_UP) & ~IF_EN; + writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL); + + /* send info frame */ + val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL); + val |= IF_EN; + writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL); +} + +ssize_t analogix_dp_transfer(struct analogix_dp_device *dp, + struct drm_dp_aux_msg *msg) +{ + u32 reg; + u8 *buffer = msg->buffer; + int timeout_loop = 0; + unsigned int i; + int num_transferred = 0; + + /* Buffer size of AUX CH is 16 bytes */ + if (WARN_ON(msg->size > 16)) + return -E2BIG; + + /* Clear AUX CH data buffer */ + reg = BUF_CLR; + writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL); + + switch (msg->request & ~DP_AUX_I2C_MOT) { + case DP_AUX_I2C_WRITE: + reg = AUX_TX_COMM_WRITE | AUX_TX_COMM_I2C_TRANSACTION; + if (msg->request & DP_AUX_I2C_MOT) + reg |= AUX_TX_COMM_MOT; + break; + + case DP_AUX_I2C_READ: + reg = AUX_TX_COMM_READ | AUX_TX_COMM_I2C_TRANSACTION; + if (msg->request & DP_AUX_I2C_MOT) + reg |= AUX_TX_COMM_MOT; + break; + + case DP_AUX_NATIVE_WRITE: + reg = AUX_TX_COMM_WRITE | AUX_TX_COMM_DP_TRANSACTION; + break; + + case DP_AUX_NATIVE_READ: + reg = AUX_TX_COMM_READ | AUX_TX_COMM_DP_TRANSACTION; + break; + + default: + return -EINVAL; + } + + reg |= AUX_LENGTH(msg->size); + writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1); + + /* Select DPCD device address */ + reg = AUX_ADDR_7_0(msg->address); + writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0); + reg = AUX_ADDR_15_8(msg->address); + writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8); + reg = AUX_ADDR_19_16(msg->address); + writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16); + + if (!(msg->request & DP_AUX_I2C_READ)) { + for (i = 0; i < msg->size; i++) { + reg = buffer[i]; + writel(reg, dp->reg_base + ANALOGIX_DP_BUF_DATA_0 + + 4 * i); + num_transferred++; + } + } + + /* Enable AUX CH operation */ + reg = AUX_EN; + + /* Zero-sized messages specify address-only transactions. */ + if (msg->size < 1) + reg |= ADDR_ONLY; + + writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2); + + /* Is AUX CH command reply received? */ + /* TODO: Wait for an interrupt instead of looping? */ + reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA); + while (!(reg & RPLY_RECEIV)) { + timeout_loop++; + if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) { + dev_err(dp->dev, "AUX CH command reply failed!\n"); + return -ETIMEDOUT; + } + reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA); + usleep_range(10, 11); + } + + /* Clear interrupt source for AUX CH command reply */ + writel(RPLY_RECEIV, dp->reg_base + ANALOGIX_DP_INT_STA); + + /* Clear interrupt source for AUX CH access error */ + reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA); + if (reg & AUX_ERR) { + writel(AUX_ERR, dp->reg_base + ANALOGIX_DP_INT_STA); + return -EREMOTEIO; + } + + /* Check AUX CH error access status */ + reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_STA); + if ((reg & AUX_STATUS_MASK)) { + dev_err(dp->dev, "AUX CH error happened: %d\n\n", + reg & AUX_STATUS_MASK); + return -EREMOTEIO; + } + + if (msg->request & DP_AUX_I2C_READ) { + for (i = 0; i < msg->size; i++) { + reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0 + + 4 * i); + buffer[i] = (unsigned char)reg; + num_transferred++; + } + } + + /* Check if Rx sends defer */ + reg = readl(dp->reg_base + ANALOGIX_DP_AUX_RX_COMM); + if (reg == AUX_RX_COMM_AUX_DEFER) + msg->reply = DP_AUX_NATIVE_REPLY_DEFER; + else if (reg == AUX_RX_COMM_I2C_DEFER) + msg->reply = DP_AUX_I2C_REPLY_DEFER; + else if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_WRITE || + (msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_READ) + msg->reply = DP_AUX_I2C_REPLY_ACK; + else if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_NATIVE_WRITE || + (msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_NATIVE_READ) + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + + return num_transferred; +} diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h index cdcc6c5..40200c6 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h @@ -22,6 +22,8 @@ #define ANALOGIX_DP_VIDEO_CTL_8 0x3C #define ANALOGIX_DP_VIDEO_CTL_10 0x44 +#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0 0xD8 + #define ANALOGIX_DP_PLL_REG_1 0xfc #define ANALOGIX_DP_PLL_REG_2 0x9e4 #define ANALOGIX_DP_PLL_REG_3 0x9e8 @@ -30,6 +32,21 @@ #define ANALOGIX_DP_PD 0x12c +#define ANALOGIX_DP_IF_TYPE 0x244 +#define ANALOGIX_DP_IF_PKT_DB1 0x254 +#define ANALOGIX_DP_IF_PKT_DB2 0x258 +#define ANALOGIX_DP_SPD_HB0 0x2F8 +#define ANALOGIX_DP_SPD_HB1 0x2FC +#define ANALOGIX_DP_SPD_HB2 0x300 +#define ANALOGIX_DP_SPD_HB3 0x304 +#define ANALOGIX_DP_SPD_PB0 0x308 +#define ANALOGIX_DP_SPD_PB1 0x30C +#define ANALOGIX_DP_SPD_PB2 0x310 +#define ANALOGIX_DP_SPD_PB3 0x314 +#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL 0x318 +#define ANALOGIX_DP_VSC_SHADOW_DB0 0x31C +#define ANALOGIX_DP_VSC_SHADOW_DB1 0x320 + #define ANALOGIX_DP_LANE_MAP 0x35C #define ANALOGIX_DP_ANALOG_CTL_1 0x370 @@ -103,6 +120,8 @@ #define ANALOGIX_DP_SOC_GENERAL_CTL 0x800 +#define ANALOGIX_DP_CRC_CON 0x890 + /* ANALOGIX_DP_TX_SW_RESET */ #define RESET_DP_TX (0x1 << 0) @@ -151,6 +170,7 @@ #define VID_CHK_UPDATE_TYPE_SHIFT (4) #define VID_CHK_UPDATE_TYPE_1 (0x1 << 4) #define VID_CHK_UPDATE_TYPE_0 (0x0 << 4) +#define REUSE_SPD_EN (0x1 << 3) /* ANALOGIX_DP_VIDEO_CTL_8 */ #define VID_HRES_TH(x) (((x) & 0xf) << 4) @@ -167,6 +187,12 @@ #define REF_CLK_27M (0x0 << 0) #define REF_CLK_MASK (0x1 << 0) +/* ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL */ +#define PSR_FRAME_UP_TYPE_BURST (0x1 << 0) +#define PSR_FRAME_UP_TYPE_SINGLE (0x0 << 0) +#define PSR_CRC_SEL_HARDWARE (0x1 << 1) +#define PSR_CRC_SEL_MANUALLY (0x0 << 1) + /* ANALOGIX_DP_LANE_MAP */ #define LANE3_MAP_LOGIC_LANE_0 (0x0 << 6) #define LANE3_MAP_LOGIC_LANE_1 (0x1 << 6) @@ -376,4 +402,12 @@ #define VIDEO_MODE_SLAVE_MODE (0x1 << 0) #define VIDEO_MODE_MASTER_MODE (0x0 << 0) +/* ANALOGIX_DP_PKT_SEND_CTL */ +#define IF_UP (0x1 << 4) +#define IF_EN (0x1 << 0) + +/* ANALOGIX_DP_CRC_CON */ +#define PSR_VID_CRC_FLUSH (0x1 << 2) +#define PSR_VID_CRC_ENABLE (0x1 << 0) + #endif /* _ANALOGIX_DP_REG_H */ diff --git a/drivers/gpu/drm/bridge/dw-hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/dw-hdmi-ahb-audio.c index 122bb01..8f2d137 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi-ahb-audio.c +++ b/drivers/gpu/drm/bridge/dw-hdmi-ahb-audio.c @@ -640,7 +640,6 @@ static struct platform_driver snd_dw_hdmi_driver = { .remove = snd_dw_hdmi_remove, .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, .pm = PM_OPS, }, }; diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 77ab473..66ad8e6 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -940,10 +940,11 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode) */ /* - * AVI data byte 1 differences: Colorspace in bits 4,5 rather than 5,6, - * active aspect present in bit 6 rather than 4. + * AVI data byte 1 differences: Colorspace in bits 0,1 rather than 5,6, + * scan info in bits 4,5 rather than 0,1 and active aspect present in + * bit 6 rather than 4. */ - val = (frame.colorspace & 3) << 4 | (frame.scan_mode & 0x3); + val = (frame.scan_mode & 3) << 4 | (frame.colorspace & 3); if (frame.active_aspect & 15) val |= HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT; if (frame.top_bar || frame.bottom_bar) @@ -1812,9 +1813,6 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data) /* Disable all interrupts */ hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); - hdmi->connector.funcs->destroy(&hdmi->connector); - hdmi->encoder->funcs->destroy(hdmi->encoder); - clk_disable_unprepare(hdmi->iahb_clk); clk_disable_unprepare(hdmi->isfr_clk); i2c_put_adapter(hdmi->ddc); diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c index 583b8ce..f1b39a2 100644 --- a/drivers/gpu/drm/bridge/parade-ps8622.c +++ b/drivers/gpu/drm/bridge/parade-ps8622.c @@ -16,7 +16,6 @@ #include <linux/backlight.h> #include <linux/delay.h> #include <linux/err.h> -#include <linux/fb.h> #include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index b05f7ea..6c76d12 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -57,7 +57,7 @@ static int cirrus_kick_out_firmware_fb(struct pci_dev *pdev) #ifdef CONFIG_X86 primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; #endif - remove_conflicting_framebuffers(ap, "cirrusdrmfb", primary); + drm_fb_helper_remove_conflicting_framebuffers(ap, "cirrusdrmfb", primary); kfree(ap); return 0; diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 3b5be72..daecf1a 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -13,8 +13,6 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_crtc_helper.h> -#include <linux/fb.h> - #include "cirrus_drv.h" static void cirrus_dirty_update(struct cirrus_fbdev *afbdev, diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c index 80446e2..76bcb43 100644 --- a/drivers/gpu/drm/cirrus/cirrus_main.c +++ b/drivers/gpu/drm/cirrus/cirrus_main.c @@ -185,14 +185,23 @@ int cirrus_driver_load(struct drm_device *dev, unsigned long flags) goto out; } + /* + * cirrus_modeset_init() is initializing/registering the emulated fbdev + * and DRM internals can access/test some of the fields in + * mode_config->funcs as part of the fbdev registration process. + * Make sure dev->mode_config.funcs is properly set to avoid + * dereferencing a NULL pointer. + * FIXME: mode_config.funcs assignment should probably be done in + * cirrus_modeset_init() (that's a common pattern seen in other DRM + * drivers). + */ + dev->mode_config.funcs = &cirrus_mode_funcs; r = cirrus_modeset_init(cdev); if (r) { dev_err(&dev->pdev->dev, "Fatal error during modeset init: %d\n", r); goto out; } - dev->mode_config.funcs = (void *)&cirrus_mode_funcs; - return 0; out: cirrus_driver_unload(dev); diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c index 1cc9ee6..bb2438d 100644 --- a/drivers/gpu/drm/cirrus/cirrus_ttm.c +++ b/drivers/gpu/drm/cirrus/cirrus_ttm.c @@ -150,7 +150,8 @@ static int cirrus_bo_verify_access(struct ttm_buffer_object *bo, struct file *fi { struct cirrus_bo *cirrusbo = cirrus_bo(bo); - return drm_vma_node_verify_access(&cirrusbo->gem.vma_node, filp); + return drm_vma_node_verify_access(&cirrusbo->gem.vma_node, + filp->private_data); } static int cirrus_ttm_io_mem_reserve(struct ttm_bo_device *bdev, diff --git a/drivers/gpu/drm/drm_agpsupport.c b/drivers/gpu/drm/drm_agpsupport.c index 605bd24..d621c8a 100644 --- a/drivers/gpu/drm/drm_agpsupport.c +++ b/drivers/gpu/drm/drm_agpsupport.c @@ -430,9 +430,7 @@ struct drm_agp_head *drm_agp_init(struct drm_device *dev) * intact so it can still be used. It is safe to call this if AGP is disabled or * was already removed. * - * If DRIVER_MODESET is active, nothing is done to protect the modesetting - * resources from getting destroyed. Drivers are responsible of cleaning them up - * during device shutdown. + * Cleanup is only done for drivers who have DRIVER_LEGACY set. */ void drm_legacy_agp_clear(struct drm_device *dev) { @@ -440,7 +438,7 @@ void drm_legacy_agp_clear(struct drm_device *dev) if (!dev->agp) return; - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) return; list_for_each_entry_safe(entry, tempe, &dev->agp->memory, head) { diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index fa39307..2373960 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -475,7 +475,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc, val, -1, &replaced); - state->color_mgmt_changed = replaced; + state->color_mgmt_changed |= replaced; return ret; } else if (property == config->ctm_property) { ret = drm_atomic_replace_property_blob_from_id(crtc, @@ -483,7 +483,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc, val, sizeof(struct drm_color_ctm), &replaced); - state->color_mgmt_changed = replaced; + state->color_mgmt_changed |= replaced; return ret; } else if (property == config->gamma_lut_property) { ret = drm_atomic_replace_property_blob_from_id(crtc, @@ -491,7 +491,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc, val, -1, &replaced); - state->color_mgmt_changed = replaced; + state->color_mgmt_changed |= replaced; return ret; } else if (crtc->funcs->atomic_set_property) return crtc->funcs->atomic_set_property(crtc, state, property, val); @@ -837,8 +837,9 @@ static int drm_atomic_plane_check(struct drm_plane *plane, /* Check whether this plane supports the fb pixel format. */ ret = drm_plane_check_pixel_format(plane, state->fb->pixel_format); if (ret) { - DRM_DEBUG_ATOMIC("Invalid pixel format %s\n", - drm_get_format_name(state->fb->pixel_format)); + char *format_name = drm_get_format_name(state->fb->pixel_format); + DRM_DEBUG_ATOMIC("Invalid pixel format %s\n", format_name); + kfree(format_name); return ret; } @@ -1690,7 +1691,7 @@ retry: goto out; } - prop = drm_property_find(dev, prop_id); + prop = drm_mode_obj_find_prop_id(obj, prop_id); if (!prop) { drm_mode_object_unreference(obj); ret = -ENOENT; diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 20be86d..c3f8347 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -594,7 +594,7 @@ drm_atomic_helper_check_planes(struct drm_device *dev, struct drm_plane_state *plane_state; int i, ret = 0; - ret = drm_atomic_helper_normalize_zpos(dev, state); + ret = drm_atomic_normalize_zpos(dev, state); if (ret) return ret; @@ -749,6 +749,8 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) /* Right function depends upon target state. */ if (crtc->state->enable && funcs->prepare) funcs->prepare(crtc); + else if (funcs->atomic_disable) + funcs->atomic_disable(crtc, old_crtc_state); else if (funcs->disable) funcs->disable(crtc); else @@ -886,8 +888,12 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) * Each encoder has at most one connector (since we always steal * it away), so we won't call mode_set hooks twice. */ - if (funcs && funcs->mode_set) + if (funcs && funcs->atomic_mode_set) { + funcs->atomic_mode_set(encoder, new_crtc_state, + connector->state); + } else if (funcs && funcs->mode_set) { funcs->mode_set(encoder, mode, adjusted_mode); + } drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode); } @@ -1003,29 +1009,46 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables); * drm_atomic_helper_wait_for_fences - wait for fences stashed in plane state * @dev: DRM device * @state: atomic state object with old state structures + * @pre_swap: if true, do an interruptible wait * * For implicit sync, driver should fish the exclusive fence out from the * incoming fb's and stash it in the drm_plane_state. This is called after * drm_atomic_helper_swap_state() so it uses the current plane state (and * just uses the atomic state to find the changed planes) + * + * Returns zero if success or < 0 if fence_wait() fails. */ -void drm_atomic_helper_wait_for_fences(struct drm_device *dev, - struct drm_atomic_state *state) +int drm_atomic_helper_wait_for_fences(struct drm_device *dev, + struct drm_atomic_state *state, + bool pre_swap) { struct drm_plane *plane; struct drm_plane_state *plane_state; - int i; + int i, ret; for_each_plane_in_state(state, plane, plane_state, i) { - if (!plane->state->fence) + if (!pre_swap) + plane_state = plane->state; + + if (!plane_state->fence) continue; - WARN_ON(!plane->state->fb); + WARN_ON(!plane_state->fb); + + /* + * If waiting for fences pre-swap (ie: nonblock), userspace can + * still interrupt the operation. Instead of blocking until the + * timer expires, make the wait interruptible. + */ + ret = fence_wait(plane_state->fence, pre_swap); + if (ret) + return ret; - fence_wait(plane->state->fence, false); - fence_put(plane->state->fence); - plane->state->fence = NULL; + fence_put(plane_state->fence); + plane_state->fence = NULL; } + + return 0; } EXPORT_SYMBOL(drm_atomic_helper_wait_for_fences); @@ -1142,7 +1165,8 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks); * * drm_atomic_helper_commit_modeset_enables(dev, state); * - * drm_atomic_helper_commit_planes(dev, state, true); + * drm_atomic_helper_commit_planes(dev, state, + * DRM_PLANE_COMMIT_ACTIVE_ONLY); * * for committing the atomic update to hardware. See the kerneldoc entries for * these three functions for more details. @@ -1153,7 +1177,7 @@ void drm_atomic_helper_commit_tail(struct drm_atomic_state *state) drm_atomic_helper_commit_modeset_disables(dev, state); - drm_atomic_helper_commit_planes(dev, state, false); + drm_atomic_helper_commit_planes(dev, state, 0); drm_atomic_helper_commit_modeset_enables(dev, state); @@ -1172,7 +1196,7 @@ static void commit_tail(struct drm_atomic_state *state) funcs = dev->mode_config.helper_private; - drm_atomic_helper_wait_for_fences(dev, state); + drm_atomic_helper_wait_for_fences(dev, state, false); drm_atomic_helper_wait_for_dependencies(state); @@ -1231,6 +1255,12 @@ int drm_atomic_helper_commit(struct drm_device *dev, if (ret) return ret; + if (!nonblock) { + ret = drm_atomic_helper_wait_for_fences(dev, state, true); + if (ret) + return ret; + } + /* * This is the point of no return - everything below never fails except * when the hw goes bonghits. Which means we can commit the new state on @@ -1631,6 +1661,9 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev, funcs = plane->helper_private; + if (!drm_atomic_helper_framebuffer_changed(dev, state, plane_state->crtc)) + continue; + if (funcs->prepare_fb) { ret = funcs->prepare_fb(plane, plane_state); if (ret) @@ -1647,18 +1680,20 @@ fail: if (j >= i) continue; + if (!drm_atomic_helper_framebuffer_changed(dev, state, plane_state->crtc)) + continue; + funcs = plane->helper_private; if (funcs->cleanup_fb) funcs->cleanup_fb(plane, plane_state); - } return ret; } EXPORT_SYMBOL(drm_atomic_helper_prepare_planes); -bool plane_crtc_active(struct drm_plane_state *state) +static bool plane_crtc_active(const struct drm_plane_state *state) { return state->crtc && state->crtc->state->active; } @@ -1667,7 +1702,7 @@ bool plane_crtc_active(struct drm_plane_state *state) * drm_atomic_helper_commit_planes - commit plane state * @dev: DRM device * @old_state: atomic state object with old state structures - * @active_only: Only commit on active CRTC if set + * @flags: flags for committing plane state * * This function commits the new plane state using the plane and atomic helper * functions for planes and crtcs. It assumes that the atomic state has already @@ -1687,25 +1722,34 @@ bool plane_crtc_active(struct drm_plane_state *state) * most drivers don't need to be immediately notified of plane updates for a * disabled CRTC. * - * Unless otherwise needed, drivers are advised to set the @active_only - * parameters to true in order not to receive plane update notifications related - * to a disabled CRTC. This avoids the need to manually ignore plane updates in + * Unless otherwise needed, drivers are advised to set the ACTIVE_ONLY flag in + * @flags in order not to receive plane update notifications related to a + * disabled CRTC. This avoids the need to manually ignore plane updates in * driver code when the driver and/or hardware can't or just don't need to deal * with updates on disabled CRTCs, for example when supporting runtime PM. * - * The drm_atomic_helper_commit() default implementation only sets @active_only - * to false to most closely match the behaviour of the legacy helpers. This should - * not be copied blindly by drivers. + * Drivers may set the NO_DISABLE_AFTER_MODESET flag in @flags if the relevant + * display controllers require to disable a CRTC's planes when the CRTC is + * disabled. This function would skip the ->atomic_disable call for a plane if + * the CRTC of the old plane state needs a modesetting operation. Of course, + * the drivers need to disable the planes in their CRTC disable callbacks + * since no one else would do that. + * + * The drm_atomic_helper_commit() default implementation doesn't set the + * ACTIVE_ONLY flag to most closely match the behaviour of the legacy helpers. + * This should not be copied blindly by drivers. */ void drm_atomic_helper_commit_planes(struct drm_device *dev, struct drm_atomic_state *old_state, - bool active_only) + uint32_t flags) { struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; struct drm_plane *plane; struct drm_plane_state *old_plane_state; int i; + bool active_only = flags & DRM_PLANE_COMMIT_ACTIVE_ONLY; + bool no_disable = flags & DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET; for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; @@ -1749,10 +1793,19 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, /* * Special-case disabling the plane if drivers support it. */ - if (disabling && funcs->atomic_disable) + if (disabling && funcs->atomic_disable) { + struct drm_crtc_state *crtc_state; + + crtc_state = old_plane_state->crtc->state; + + if (drm_atomic_crtc_needs_modeset(crtc_state) && + no_disable) + continue; + funcs->atomic_disable(plane, old_plane_state); - else if (plane->state->crtc || disabling) + } else if (plane->state->crtc || disabling) { funcs->atomic_update(plane, old_plane_state); + } } for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { @@ -1831,12 +1884,12 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc); /** * drm_atomic_helper_disable_planes_on_crtc - helper to disable CRTC's planes - * @crtc: CRTC + * @old_crtc_state: atomic state object with the old CRTC state * @atomic: if set, synchronize with CRTC's atomic_begin/flush hooks * * Disables all planes associated with the given CRTC. This can be - * used for instance in the CRTC helper disable callback to disable - * all planes before shutting down the display pipeline. + * used for instance in the CRTC helper atomic_disable callback to disable + * all planes. * * If the atomic-parameter is set the function calls the CRTC's * atomic_begin hook before and atomic_flush hook after disabling the @@ -1845,9 +1898,11 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc); * It is a bug to call this function without having implemented the * ->atomic_disable() plane hook. */ -void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc, - bool atomic) +void +drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc_state *old_crtc_state, + bool atomic) { + struct drm_crtc *crtc = old_crtc_state->crtc; const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; struct drm_plane *plane; @@ -1855,11 +1910,11 @@ void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc, if (atomic && crtc_funcs && crtc_funcs->atomic_begin) crtc_funcs->atomic_begin(crtc, NULL); - drm_for_each_plane(plane, crtc->dev) { + drm_atomic_crtc_state_for_each_plane(plane, old_crtc_state) { const struct drm_plane_helper_funcs *plane_funcs = plane->helper_private; - if (plane->state->crtc != crtc || !plane_funcs) + if (!plane_funcs) continue; WARN_ON(!plane_funcs->atomic_disable); @@ -1894,6 +1949,9 @@ void drm_atomic_helper_cleanup_planes(struct drm_device *dev, for_each_plane_in_state(old_state, plane, plane_state, i) { const struct drm_plane_helper_funcs *funcs; + if (!drm_atomic_helper_framebuffer_changed(dev, old_state, plane_state->crtc)) + continue; + funcs = plane->helper_private; if (funcs->cleanup_fb) @@ -2354,7 +2412,7 @@ int __drm_atomic_helper_set_config(struct drm_mode_set *set, primary_state->crtc_h = vdisplay; primary_state->src_x = set->x << 16; primary_state->src_y = set->y << 16; - if (primary_state->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) { + if (primary_state->rotation & (DRM_ROTATE_90 | DRM_ROTATE_270)) { primary_state->src_w = vdisplay << 16; primary_state->src_h = hdisplay << 16; } else { @@ -3039,7 +3097,7 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane) if (plane->state) { plane->state->plane = plane; - plane->state->rotation = BIT(DRM_ROTATE_0); + plane->state->rotation = DRM_ROTATE_0; } } EXPORT_SYMBOL(drm_atomic_helper_plane_reset); diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index 4153e8a..6b14351 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -251,7 +251,7 @@ void drm_master_release(struct drm_file *file_priv) if (!drm_is_current_master(file_priv)) goto out; - if (!drm_core_check_feature(dev, DRIVER_MODESET)) { + if (drm_core_check_feature(dev, DRIVER_LEGACY)) { /* * Since the master is disappearing, so is the * possibility to lock. diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c index f3c0942..85172a9 100644 --- a/drivers/gpu/drm/drm_blend.c +++ b/drivers/gpu/drm/drm_blend.c @@ -25,12 +25,173 @@ */ #include <drm/drmP.h> #include <drm/drm_atomic.h> -#include <drm/drm_crtc.h> +#include <drm/drm_blend.h> #include <linux/export.h> #include <linux/slab.h> #include <linux/sort.h> -#include "drm_internal.h" +#include "drm_crtc_internal.h" + +/** + * DOC: overview + * + * The basic plane composition model supported by standard plane properties only + * has a source rectangle (in logical pixels within the &drm_framebuffer), with + * sub-pixel accuracy, which is scaled up to a pixel-aligned destination + * rectangle in the visible area of a &drm_crtc. The visible area of a CRTC is + * defined by the horizontal and vertical visible pixels (stored in @hdisplay + * and @vdisplay) of the requested mode (stored in @mode in the + * &drm_crtc_state). These two rectangles are both stored in the + * &drm_plane_state. + * + * For the atomic ioctl the following standard (atomic) properties on the plane object + * encode the basic plane composition model: + * + * SRC_X: + * X coordinate offset for the source rectangle within the + * &drm_framebuffer, in 16.16 fixed point. Must be positive. + * SRC_Y: + * Y coordinate offset for the source rectangle within the + * &drm_framebuffer, in 16.16 fixed point. Must be positive. + * SRC_W: + * Width for the source rectangle within the &drm_framebuffer, in 16.16 + * fixed point. SRC_X plus SRC_W must be within the width of the source + * framebuffer. Must be positive. + * SRC_H: + * Height for the source rectangle within the &drm_framebuffer, in 16.16 + * fixed point. SRC_Y plus SRC_H must be within the height of the source + * framebuffer. Must be positive. + * CRTC_X: + * X coordinate offset for the destination rectangle. Can be negative. + * CRTC_Y: + * Y coordinate offset for the destination rectangle. Can be negative. + * CRTC_W: + * Width for the destination rectangle. CRTC_X plus CRTC_W can extend past + * the currently visible horizontal area of the &drm_crtc. + * CRTC_H: + * Height for the destination rectangle. CRTC_Y plus CRTC_H can extend past + * the currently visible vertical area of the &drm_crtc. + * FB_ID: + * Mode object ID of the &drm_framebuffer this plane should scan out. + * CRTC_ID: + * Mode object ID of the &drm_crtc this plane should be connected to. + * + * Note that the source rectangle must fully lie within the bounds of the + * &drm_framebuffer. The destination rectangle can lie outside of the visible + * area of the current mode of the CRTC. It must be apprpriately clipped by the + * driver, which can be done by calling drm_plane_helper_check_update(). Drivers + * are also allowed to round the subpixel sampling positions appropriately, but + * only to the next full pixel. No pixel outside of the source rectangle may + * ever be sampled, which is important when applying more sophisticated + * filtering than just a bilinear one when scaling. The filtering mode when + * scaling is unspecified. + * + * On top of this basic transformation additional properties can be exposed by + * the driver: + * + * - Rotation is set up with drm_mode_create_rotation_property(). It adds a + * rotation and reflection step between the source and destination rectangles. + * Without this property the rectangle is only scaled, but not rotated or + * reflected. + * + * - Z position is set up with drm_plane_create_zpos_immutable_property() and + * drm_plane_create_zpos_property(). It controls the visibility of overlapping + * planes. Without this property the primary plane is always below the cursor + * plane, and ordering between all other planes is undefined. + * + * Note that all the property extensions described here apply either to the + * plane or the CRTC (e.g. for the background color, which currently is not + * exposed and assumed to be black). + */ + +/** + * drm_mode_create_rotation_property - create a new rotation property + * @dev: DRM device + * @supported_rotations: bitmask of supported rotations and reflections + * + * This creates a new property with the selected support for transformations. + * The resulting property should be stored in @rotation_property in + * &drm_mode_config. It then must be attached to each plane which supports + * rotations using drm_object_attach_property(). + * + * FIXME: Probably better if the rotation property is created on each plane, + * like the zpos property. Otherwise it's not possible to allow different + * rotation modes on different planes. + * + * Since a rotation by 180° degress is the same as reflecting both along the x + * and the y axis the rotation property is somewhat redundant. Drivers can use + * drm_rotation_simplify() to normalize values of this property. + * + * The property exposed to userspace is a bitmask property (see + * drm_property_create_bitmask()) called "rotation" and has the following + * bitmask enumaration values: + * + * DRM_ROTATE_0: + * "rotate-0" + * DRM_ROTATE_90: + * "rotate-90" + * DRM_ROTATE_180: + * "rotate-180" + * DRM_ROTATE_270: + * "rotate-270" + * DRM_REFLECT_X: + * "reflect-x" + * DRM_REFELCT_Y: + * "reflect-y" + * + * Rotation is the specified amount in degrees in counter clockwise direction, + * the X and Y axis are within the source rectangle, i.e. the X/Y axis before + * rotation. After reflection, the rotation is applied to the image sampled from + * the source rectangle, before scaling it to fit the destination rectangle. + */ +struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev, + unsigned int supported_rotations) +{ + static const struct drm_prop_enum_list props[] = { + { __builtin_ffs(DRM_ROTATE_0) - 1, "rotate-0" }, + { __builtin_ffs(DRM_ROTATE_90) - 1, "rotate-90" }, + { __builtin_ffs(DRM_ROTATE_180) - 1, "rotate-180" }, + { __builtin_ffs(DRM_ROTATE_270) - 1, "rotate-270" }, + { __builtin_ffs(DRM_REFLECT_X) - 1, "reflect-x" }, + { __builtin_ffs(DRM_REFLECT_Y) - 1, "reflect-y" }, + }; + + return drm_property_create_bitmask(dev, 0, "rotation", + props, ARRAY_SIZE(props), + supported_rotations); +} +EXPORT_SYMBOL(drm_mode_create_rotation_property); + +/** + * drm_rotation_simplify() - Try to simplify the rotation + * @rotation: Rotation to be simplified + * @supported_rotations: Supported rotations + * + * Attempt to simplify the rotation to a form that is supported. + * Eg. if the hardware supports everything except DRM_REFLECT_X + * one could call this function like this: + * + * drm_rotation_simplify(rotation, DRM_ROTATE_0 | + * DRM_ROTATE_90 | DRM_ROTATE_180 | + * DRM_ROTATE_270 | DRM_REFLECT_Y); + * + * to eliminate the DRM_ROTATE_X flag. Depending on what kind of + * transforms the hardware supports, this function may not + * be able to produce a supported transform, so the caller should + * check the result afterwards. + */ +unsigned int drm_rotation_simplify(unsigned int rotation, + unsigned int supported_rotations) +{ + if (rotation & ~supported_rotations) { + rotation ^= DRM_REFLECT_X | DRM_REFLECT_Y; + rotation = (rotation & DRM_REFLECT_MASK) | + BIT((ffs(rotation & DRM_ROTATE_MASK) + 1) % 4); + } + + return rotation; +} +EXPORT_SYMBOL(drm_rotation_simplify); /** * drm_plane_create_zpos_property - create mutable zpos property @@ -49,10 +210,14 @@ * If zpos of some planes cannot be changed (like fixed background or * cursor/topmost planes), driver should adjust min/max values and assign those * planes immutable zpos property with lower or higher values (for more - * information, see drm_mode_create_zpos_immutable_property() function). In such + * information, see drm_plane_create_zpos_immutable_property() function). In such * case driver should also assign proper initial zpos values for all planes in * its plane_reset() callback, so the planes will be always sorted properly. * + * See also drm_atomic_normalize_zpos(). + * + * The property exposed to userspace is called "zpos". + * * Returns: * Zero on success, negative errno on failure. */ @@ -88,7 +253,9 @@ EXPORT_SYMBOL(drm_plane_create_zpos_property); * support for it in drm core. Using this property driver lets userspace * to get the arrangement of the planes for blending operation and notifies * it that the hardware (or driver) doesn't support changing of the planes' - * order. + * order. For mutable zpos see drm_plane_create_zpos_property(). + * + * The property exposed to userspace is called "zpos". * * Returns: * Zero on success, negative errno on failure. @@ -127,20 +294,6 @@ static int drm_atomic_state_zpos_cmp(const void *a, const void *b) return sa->plane->base.id - sb->plane->base.id; } -/** - * drm_atomic_helper_crtc_normalize_zpos - calculate normalized zpos values - * @crtc: crtc with planes, which have to be considered for normalization - * @crtc_state: new atomic state to apply - * - * This function checks new states of all planes assigned to given crtc and - * calculates normalized zpos value for them. Planes are compared first by their - * zpos values, then by plane id (if zpos equals). Plane with lowest zpos value - * is at the bottom. The plane_state->normalized_zpos is then filled with unique - * values from 0 to number of active planes in crtc minus one. - * - * RETURNS - * Zero for success or -errno - */ static int drm_atomic_helper_crtc_normalize_zpos(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) { @@ -193,20 +346,25 @@ done: } /** - * drm_atomic_helper_normalize_zpos - calculate normalized zpos values for all - * crtcs + * drm_atomic_normalize_zpos - calculate normalized zpos values for all crtcs * @dev: DRM device * @state: atomic state of DRM device * * This function calculates normalized zpos value for all modified planes in - * the provided atomic state of DRM device. For more information, see - * drm_atomic_helper_crtc_normalize_zpos() function. + * the provided atomic state of DRM device. + * + * For every CRTC this function checks new states of all planes assigned to + * it and calculates normalized zpos value for these planes. Planes are compared + * first by their zpos values, then by plane id (if zpos is equal). The plane + * with lowest zpos value is at the bottom. The plane_state->normalized_zpos is + * then filled with unique values from 0 to number of active planes in crtc + * minus one. * * RETURNS * Zero for success or -errno */ -int drm_atomic_helper_normalize_zpos(struct drm_device *dev, - struct drm_atomic_state *state) +int drm_atomic_normalize_zpos(struct drm_device *dev, + struct drm_atomic_state *state) { struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; @@ -236,3 +394,4 @@ int drm_atomic_helper_normalize_zpos(struct drm_device *dev, } return 0; } +EXPORT_SYMBOL(drm_atomic_normalize_zpos); diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 2555430..0ee052b 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -23,10 +23,9 @@ #include <linux/err.h> #include <linux/module.h> +#include <linux/mutex.h> -#include <drm/drm_crtc.h> - -#include "drm/drmP.h" +#include <drm/drm_bridge.h> /** * DOC: overview @@ -98,11 +97,11 @@ EXPORT_SYMBOL(drm_bridge_remove); * @dev: DRM device * @bridge: bridge control structure * - * called by a kms driver to link one of our encoder/bridge to the given + * Called by a kms driver to link one of our encoder/bridge to the given * bridge. * * Note that setting up links between the bridge and our encoder/bridge - * objects needs to be handled by the kms driver itself + * objects needs to be handled by the kms driver itself. * * RETURNS: * Zero on success, error code on failure @@ -125,6 +124,31 @@ int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge) EXPORT_SYMBOL(drm_bridge_attach); /** + * drm_bridge_detach - deassociate given bridge from its DRM device + * + * @bridge: bridge control structure + * + * Called by a kms driver to unlink the given bridge from its DRM device. + * + * Note that tearing down links between the bridge and our encoder/bridge + * objects needs to be handled by the kms driver itself. + */ +void drm_bridge_detach(struct drm_bridge *bridge) +{ + if (WARN_ON(!bridge)) + return; + + if (WARN_ON(!bridge->dev)) + return; + + if (bridge->funcs->detach) + bridge->funcs->detach(bridge); + + bridge->dev = NULL; +} +EXPORT_SYMBOL(drm_bridge_detach); + +/** * DOC: bridge callbacks * * The &drm_bridge_funcs ops are populated by the bridge driver. The DRM diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index c3a12cd..adb1dd7 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -397,7 +397,7 @@ int drm_legacy_addmap_ioctl(struct drm_device *dev, void *data, return -EPERM; if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; err = drm_addmap_core(dev, map->offset, map->size, map->type, @@ -443,7 +443,7 @@ int drm_legacy_getmap_ioctl(struct drm_device *dev, void *data, int i; if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; idx = map->offset; @@ -545,7 +545,7 @@ EXPORT_SYMBOL(drm_legacy_rmmap_locked); void drm_legacy_rmmap(struct drm_device *dev, struct drm_local_map *map) { if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return; mutex_lock(&dev->struct_mutex); @@ -558,7 +558,7 @@ void drm_legacy_master_rmmaps(struct drm_device *dev, struct drm_master *master) { struct drm_map_list *r_list, *list_temp; - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) return; mutex_lock(&dev->struct_mutex); @@ -595,7 +595,7 @@ int drm_legacy_rmmap_ioctl(struct drm_device *dev, void *data, int ret; if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; mutex_lock(&dev->struct_mutex); @@ -755,7 +755,7 @@ int drm_legacy_addbufs_agp(struct drm_device *dev, return -EINVAL; } - entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL); + entry->buflist = kcalloc(count, sizeof(*entry->buflist), GFP_KERNEL); if (!entry->buflist) { mutex_unlock(&dev->struct_mutex); atomic_dec(&dev->buf_alloc); @@ -905,14 +905,14 @@ int drm_legacy_addbufs_pci(struct drm_device *dev, return -EINVAL; } - entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL); + entry->buflist = kcalloc(count, sizeof(*entry->buflist), GFP_KERNEL); if (!entry->buflist) { mutex_unlock(&dev->struct_mutex); atomic_dec(&dev->buf_alloc); return -ENOMEM; } - entry->seglist = kzalloc(count * sizeof(*entry->seglist), GFP_KERNEL); + entry->seglist = kcalloc(count, sizeof(*entry->seglist), GFP_KERNEL); if (!entry->seglist) { kfree(entry->buflist); mutex_unlock(&dev->struct_mutex); @@ -923,8 +923,9 @@ int drm_legacy_addbufs_pci(struct drm_device *dev, /* Keep the original pagelist until we know all the allocations * have succeeded */ - temp_pagelist = kmalloc((dma->page_count + (count << page_order)) * - sizeof(*dma->pagelist), GFP_KERNEL); + temp_pagelist = kmalloc_array(dma->page_count + (count << page_order), + sizeof(*dma->pagelist), + GFP_KERNEL); if (!temp_pagelist) { kfree(entry->buflist); kfree(entry->seglist); @@ -1116,8 +1117,7 @@ static int drm_legacy_addbufs_sg(struct drm_device *dev, return -EINVAL; } - entry->buflist = kzalloc(count * sizeof(*entry->buflist), - GFP_KERNEL); + entry->buflist = kcalloc(count, sizeof(*entry->buflist), GFP_KERNEL); if (!entry->buflist) { mutex_unlock(&dev->struct_mutex); atomic_dec(&dev->buf_alloc); @@ -1220,7 +1220,7 @@ int drm_legacy_addbufs(struct drm_device *dev, void *data, struct drm_buf_desc *request = data; int ret; - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) @@ -1266,7 +1266,7 @@ int drm_legacy_infobufs(struct drm_device *dev, void *data, int i; int count; - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) @@ -1347,7 +1347,7 @@ int drm_legacy_markbufs(struct drm_device *dev, void *data, int order; struct drm_buf_entry *entry; - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) @@ -1395,7 +1395,7 @@ int drm_legacy_freebufs(struct drm_device *dev, void *data, int idx; struct drm_buf *buf; - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) @@ -1450,7 +1450,7 @@ int drm_legacy_mapbufs(struct drm_device *dev, void *data, struct drm_buf_map *request = data; int i; - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) @@ -1530,7 +1530,7 @@ int drm_legacy_mapbufs(struct drm_device *dev, void *data, int drm_legacy_dma_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; if (dev->driver->dma_ioctl) diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c new file mode 100644 index 0000000..d28ffdd --- /dev/null +++ b/drivers/gpu/drm/drm_color_mgmt.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_color_mgmt.h> + +#include "drm_crtc_internal.h" + +/** + * DOC: overview + * + * Color management or color space adjustments is supported through a set of 5 + * properties on the &drm_crtc object. They are set up by calling + * drm_crtc_enable_color_mgmt(). + * + * "DEGAMMA_LUT”: + * Blob property to set the degamma lookup table (LUT) mapping pixel data + * from the framebuffer before it is given to the transformation matrix. + * The data is interpreted as an array of struct &drm_color_lut elements. + * Hardware might choose not to use the full precision of the LUT elements + * nor use all the elements of the LUT (for example the hardware might + * choose to interpolate between LUT[0] and LUT[4]). + * + * “DEGAMMA_LUT_SIZE”: + * Unsinged range property to give the size of the lookup table to be set + * on the DEGAMMA_LUT property (the size depends on the underlying + * hardware). If drivers support multiple LUT sizes then they should + * publish the largest size, and sub-sample smaller sized LUTs (e.g. for + * split-gamma modes) appropriately. + * + * “CTM”: + * Blob property to set the current transformation matrix (CTM) apply to + * pixel data after the lookup through the degamma LUT and before the + * lookup through the gamma LUT. The data is interpreted as a struct + * &drm_color_ctm. + * + * “GAMMA_LUT”: + * Blob property to set the gamma lookup table (LUT) mapping pixel data + * after the transformation matrix to data sent to the connector. The + * data is interpreted as an array of struct &drm_color_lut elements. + * Hardware might choose not to use the full precision of the LUT elements + * nor use all the elements of the LUT (for example the hardware might + * choose to interpolate between LUT[0] and LUT[4]). + * + * “GAMMA_LUT_SIZE”: + * Unsigned range property to give the size of the lookup table to be set + * on the GAMMA_LUT property (the size depends on the underlying hardware). + * If drivers support multiple LUT sizes then they should publish the + * largest size, and sub-sample smaller sized LUTs (e.g. for split-gamma + * modes) appropriately. + * + * There is also support for a legacy gamma table, which is set up by calling + * drm_mode_crtc_set_gamma_size(). Drivers which support both should use + * drm_atomic_helper_legacy_gamma_set() to alias the legacy gamma ramp with the + * "GAMMA_LUT" property above. + */ + +/** + * drm_crtc_enable_color_mgmt - enable color management properties + * @crtc: DRM CRTC + * @degamma_lut_size: the size of the degamma lut (before CSC) + * @has_ctm: whether to attach ctm_property for CSC matrix + * @gamma_lut_size: the size of the gamma lut (after CSC) + * + * This function lets the driver enable the color correction + * properties on a CRTC. This includes 3 degamma, csc and gamma + * properties that userspace can set and 2 size properties to inform + * the userspace of the lut sizes. Each of the properties are + * optional. The gamma and degamma properties are only attached if + * their size is not 0 and ctm_property is only attached if has_ctm is + * true. + */ +void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc, + uint degamma_lut_size, + bool has_ctm, + uint gamma_lut_size) +{ + struct drm_device *dev = crtc->dev; + struct drm_mode_config *config = &dev->mode_config; + + if (degamma_lut_size) { + drm_object_attach_property(&crtc->base, + config->degamma_lut_property, 0); + drm_object_attach_property(&crtc->base, + config->degamma_lut_size_property, + degamma_lut_size); + } + + if (has_ctm) + drm_object_attach_property(&crtc->base, + config->ctm_property, 0); + + if (gamma_lut_size) { + drm_object_attach_property(&crtc->base, + config->gamma_lut_property, 0); + drm_object_attach_property(&crtc->base, + config->gamma_lut_size_property, + gamma_lut_size); + } +} +EXPORT_SYMBOL(drm_crtc_enable_color_mgmt); + +/** + * drm_mode_crtc_set_gamma_size - set the gamma table size + * @crtc: CRTC to set the gamma table size for + * @gamma_size: size of the gamma table + * + * Drivers which support gamma tables should set this to the supported gamma + * table size when initializing the CRTC. Currently the drm core only supports a + * fixed gamma table size. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, + int gamma_size) +{ + uint16_t *r_base, *g_base, *b_base; + int i; + + crtc->gamma_size = gamma_size; + + crtc->gamma_store = kcalloc(gamma_size, sizeof(uint16_t) * 3, + GFP_KERNEL); + if (!crtc->gamma_store) { + crtc->gamma_size = 0; + return -ENOMEM; + } + + r_base = crtc->gamma_store; + g_base = r_base + gamma_size; + b_base = g_base + gamma_size; + for (i = 0; i < gamma_size; i++) { + r_base[i] = i << 8; + g_base[i] = i << 8; + b_base[i] = i << 8; + } + + + return 0; +} +EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); + +/** + * drm_mode_gamma_set_ioctl - set the gamma table + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * Set the gamma table of a CRTC to the one passed in by the user. Userspace can + * inquire the required gamma table size through drm_mode_gamma_get_ioctl. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_gamma_set_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_crtc_lut *crtc_lut = data; + struct drm_crtc *crtc; + void *r_base, *g_base, *b_base; + int size; + int ret = 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + drm_modeset_lock_all(dev); + crtc = drm_crtc_find(dev, crtc_lut->crtc_id); + if (!crtc) { + ret = -ENOENT; + goto out; + } + + if (crtc->funcs->gamma_set == NULL) { + ret = -ENOSYS; + goto out; + } + + /* memcpy into gamma store */ + if (crtc_lut->gamma_size != crtc->gamma_size) { + ret = -EINVAL; + goto out; + } + + size = crtc_lut->gamma_size * (sizeof(uint16_t)); + r_base = crtc->gamma_store; + if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) { + ret = -EFAULT; + goto out; + } + + g_base = r_base + size; + if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) { + ret = -EFAULT; + goto out; + } + + b_base = g_base + size; + if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) { + ret = -EFAULT; + goto out; + } + + ret = crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size); + +out: + drm_modeset_unlock_all(dev); + return ret; + +} + +/** + * drm_mode_gamma_get_ioctl - get the gamma table + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * Copy the current gamma table into the storage provided. This also provides + * the gamma table size the driver expects, which can be used to size the + * allocated storage. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_gamma_get_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_crtc_lut *crtc_lut = data; + struct drm_crtc *crtc; + void *r_base, *g_base, *b_base; + int size; + int ret = 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + drm_modeset_lock_all(dev); + crtc = drm_crtc_find(dev, crtc_lut->crtc_id); + if (!crtc) { + ret = -ENOENT; + goto out; + } + + /* memcpy into gamma store */ + if (crtc_lut->gamma_size != crtc->gamma_size) { + ret = -EINVAL; + goto out; + } + + size = crtc_lut->gamma_size * (sizeof(uint16_t)); + r_base = crtc->gamma_store; + if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) { + ret = -EFAULT; + goto out; + } + + g_base = r_base + size; + if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) { + ret = -EFAULT; + goto out; + } + + b_base = g_base + size; + if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) { + ret = -EFAULT; + goto out; + } +out: + drm_modeset_unlock_all(dev); + return ret; +} diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c new file mode 100644 index 0000000..26bb78c7 --- /dev/null +++ b/drivers/gpu/drm/drm_connector.c @@ -0,0 +1,1123 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <drm/drmP.h> +#include <drm/drm_connector.h> +#include <drm/drm_edid.h> + +#include "drm_crtc_internal.h" +#include "drm_internal.h" + +/** + * DOC: overview + * + * In DRM connectors are the general abstraction for display sinks, and include + * als fixed panels or anything else that can display pixels in some form. As + * opposed to all other KMS objects representing hardware (like CRTC, encoder or + * plane abstractions) connectors can be hotplugged and unplugged at runtime. + * Hence they are reference-counted using drm_connector_reference() and + * drm_connector_unreference(). + * + * KMS driver must create, initialize, register and attach at a struct + * &drm_connector for each such sink. The instance is created as other KMS + * objects and initialized by setting the following fields. + * + * The connector is then registered with a call to drm_connector_init() with a + * pointer to the connector functions and a connector type, and exposed through + * sysfs with a call to drm_connector_register(). + * + * Connectors must be attached to an encoder to be used. For devices that map + * connectors to encoders 1:1, the connector should be attached at + * initialization time with a call to drm_mode_connector_attach_encoder(). The + * driver must also set the struct &drm_connector encoder field to point to the + * attached encoder. + * + * For connectors which are not fixed (like built-in panels) the driver needs to + * support hotplug notifications. The simplest way to do that is by using the + * probe helpers, see drm_kms_helper_poll_init() for connectors which don't have + * hardware support for hotplug interrupts. Connectors with hardware hotplug + * support can instead use e.g. drm_helper_hpd_irq_event(). + */ + +struct drm_conn_prop_enum_list { + int type; + const char *name; + struct ida ida; +}; + +/* + * Connector and encoder types. + */ +static struct drm_conn_prop_enum_list drm_connector_enum_list[] = { + { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, + { DRM_MODE_CONNECTOR_VGA, "VGA" }, + { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, + { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, + { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, + { DRM_MODE_CONNECTOR_Composite, "Composite" }, + { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" }, + { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, + { DRM_MODE_CONNECTOR_Component, "Component" }, + { DRM_MODE_CONNECTOR_9PinDIN, "DIN" }, + { DRM_MODE_CONNECTOR_DisplayPort, "DP" }, + { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, + { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, + { DRM_MODE_CONNECTOR_TV, "TV" }, + { DRM_MODE_CONNECTOR_eDP, "eDP" }, + { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" }, + { DRM_MODE_CONNECTOR_DSI, "DSI" }, + { DRM_MODE_CONNECTOR_DPI, "DPI" }, +}; + +void drm_connector_ida_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) + ida_init(&drm_connector_enum_list[i].ida); +} + +void drm_connector_ida_destroy(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) + ida_destroy(&drm_connector_enum_list[i].ida); +} + +/** + * drm_connector_get_cmdline_mode - reads the user's cmdline mode + * @connector: connector to quwery + * + * The kernel supports per-connector configuration of its consoles through + * use of the video= parameter. This function parses that option and + * extracts the user's specified mode (or enable/disable status) for a + * particular connector. This is typically only used during the early fbdev + * setup. + */ +static void drm_connector_get_cmdline_mode(struct drm_connector *connector) +{ + struct drm_cmdline_mode *mode = &connector->cmdline_mode; + char *option = NULL; + + if (fb_get_options(connector->name, &option)) + return; + + if (!drm_mode_parse_command_line_for_connector(option, + connector, + mode)) + return; + + if (mode->force) { + const char *s; + + switch (mode->force) { + case DRM_FORCE_OFF: + s = "OFF"; + break; + case DRM_FORCE_ON_DIGITAL: + s = "ON - dig"; + break; + default: + case DRM_FORCE_ON: + s = "ON"; + break; + } + + DRM_INFO("forcing %s connector %s\n", connector->name, s); + connector->force = mode->force; + } + + DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", + connector->name, + mode->xres, mode->yres, + mode->refresh_specified ? mode->refresh : 60, + mode->rb ? " reduced blanking" : "", + mode->margins ? " with margins" : "", + mode->interlace ? " interlaced" : ""); +} + +static void drm_connector_free(struct kref *kref) +{ + struct drm_connector *connector = + container_of(kref, struct drm_connector, base.refcount); + struct drm_device *dev = connector->dev; + + drm_mode_object_unregister(dev, &connector->base); + connector->funcs->destroy(connector); +} + +/** + * drm_connector_init - Init a preallocated connector + * @dev: DRM device + * @connector: the connector to init + * @funcs: callbacks for this connector + * @connector_type: user visible type of the connector + * + * Initialises a preallocated connector. Connectors should be + * subclassed as part of driver connector objects. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_connector_init(struct drm_device *dev, + struct drm_connector *connector, + const struct drm_connector_funcs *funcs, + int connector_type) +{ + struct drm_mode_config *config = &dev->mode_config; + int ret; + struct ida *connector_ida = + &drm_connector_enum_list[connector_type].ida; + + drm_modeset_lock_all(dev); + + ret = drm_mode_object_get_reg(dev, &connector->base, + DRM_MODE_OBJECT_CONNECTOR, + false, drm_connector_free); + if (ret) + goto out_unlock; + + connector->base.properties = &connector->properties; + connector->dev = dev; + connector->funcs = funcs; + + ret = ida_simple_get(&config->connector_ida, 0, 0, GFP_KERNEL); + if (ret < 0) + goto out_put; + connector->index = ret; + ret = 0; + + connector->connector_type = connector_type; + connector->connector_type_id = + ida_simple_get(connector_ida, 1, 0, GFP_KERNEL); + if (connector->connector_type_id < 0) { + ret = connector->connector_type_id; + goto out_put_id; + } + connector->name = + kasprintf(GFP_KERNEL, "%s-%d", + drm_connector_enum_list[connector_type].name, + connector->connector_type_id); + if (!connector->name) { + ret = -ENOMEM; + goto out_put_type_id; + } + + INIT_LIST_HEAD(&connector->probed_modes); + INIT_LIST_HEAD(&connector->modes); + connector->edid_blob_ptr = NULL; + connector->status = connector_status_unknown; + + drm_connector_get_cmdline_mode(connector); + + /* We should add connectors at the end to avoid upsetting the connector + * index too much. */ + list_add_tail(&connector->head, &config->connector_list); + config->num_connector++; + + if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) + drm_object_attach_property(&connector->base, + config->edid_property, + 0); + + drm_object_attach_property(&connector->base, + config->dpms_property, 0); + + if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { + drm_object_attach_property(&connector->base, config->prop_crtc_id, 0); + } + + connector->debugfs_entry = NULL; +out_put_type_id: + if (ret) + ida_remove(connector_ida, connector->connector_type_id); +out_put_id: + if (ret) + ida_remove(&config->connector_ida, connector->index); +out_put: + if (ret) + drm_mode_object_unregister(dev, &connector->base); + +out_unlock: + drm_modeset_unlock_all(dev); + + return ret; +} +EXPORT_SYMBOL(drm_connector_init); + +/** + * drm_mode_connector_attach_encoder - attach a connector to an encoder + * @connector: connector to attach + * @encoder: encoder to attach @connector to + * + * This function links up a connector to an encoder. Note that the routing + * restrictions between encoders and crtcs are exposed to userspace through the + * possible_clones and possible_crtcs bitmasks. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_connector_attach_encoder(struct drm_connector *connector, + struct drm_encoder *encoder) +{ + int i; + + /* + * In the past, drivers have attempted to model the static association + * of connector to encoder in simple connector/encoder devices using a + * direct assignment of connector->encoder = encoder. This connection + * is a logical one and the responsibility of the core, so drivers are + * expected not to mess with this. + * + * Note that the error return should've been enough here, but a large + * majority of drivers ignores the return value, so add in a big WARN + * to get people's attention. + */ + if (WARN_ON(connector->encoder)) + return -EINVAL; + + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] == 0) { + connector->encoder_ids[i] = encoder->base.id; + return 0; + } + } + return -ENOMEM; +} +EXPORT_SYMBOL(drm_mode_connector_attach_encoder); + +static void drm_mode_remove(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + list_del(&mode->head); + drm_mode_destroy(connector->dev, mode); +} + +/** + * drm_connector_cleanup - cleans up an initialised connector + * @connector: connector to cleanup + * + * Cleans up the connector but doesn't free the object. + */ +void drm_connector_cleanup(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_display_mode *mode, *t; + + /* The connector should have been removed from userspace long before + * it is finally destroyed. + */ + if (WARN_ON(connector->registered)) + drm_connector_unregister(connector); + + if (connector->tile_group) { + drm_mode_put_tile_group(dev, connector->tile_group); + connector->tile_group = NULL; + } + + list_for_each_entry_safe(mode, t, &connector->probed_modes, head) + drm_mode_remove(connector, mode); + + list_for_each_entry_safe(mode, t, &connector->modes, head) + drm_mode_remove(connector, mode); + + ida_remove(&drm_connector_enum_list[connector->connector_type].ida, + connector->connector_type_id); + + ida_remove(&dev->mode_config.connector_ida, + connector->index); + + kfree(connector->display_info.bus_formats); + drm_mode_object_unregister(dev, &connector->base); + kfree(connector->name); + connector->name = NULL; + list_del(&connector->head); + dev->mode_config.num_connector--; + + WARN_ON(connector->state && !connector->funcs->atomic_destroy_state); + if (connector->state && connector->funcs->atomic_destroy_state) + connector->funcs->atomic_destroy_state(connector, + connector->state); + + memset(connector, 0, sizeof(*connector)); +} +EXPORT_SYMBOL(drm_connector_cleanup); + +/** + * drm_connector_register - register a connector + * @connector: the connector to register + * + * Register userspace interfaces for a connector + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_connector_register(struct drm_connector *connector) +{ + int ret; + + if (connector->registered) + return 0; + + ret = drm_sysfs_connector_add(connector); + if (ret) + return ret; + + ret = drm_debugfs_connector_add(connector); + if (ret) { + goto err_sysfs; + } + + if (connector->funcs->late_register) { + ret = connector->funcs->late_register(connector); + if (ret) + goto err_debugfs; + } + + drm_mode_object_register(connector->dev, &connector->base); + + connector->registered = true; + return 0; + +err_debugfs: + drm_debugfs_connector_remove(connector); +err_sysfs: + drm_sysfs_connector_remove(connector); + return ret; +} +EXPORT_SYMBOL(drm_connector_register); + +/** + * drm_connector_unregister - unregister a connector + * @connector: the connector to unregister + * + * Unregister userspace interfaces for a connector + */ +void drm_connector_unregister(struct drm_connector *connector) +{ + if (!connector->registered) + return; + + if (connector->funcs->early_unregister) + connector->funcs->early_unregister(connector); + + drm_sysfs_connector_remove(connector); + drm_debugfs_connector_remove(connector); + + connector->registered = false; +} +EXPORT_SYMBOL(drm_connector_unregister); + +void drm_connector_unregister_all(struct drm_device *dev) +{ + struct drm_connector *connector; + + /* FIXME: taking the mode config mutex ends up in a clash with sysfs */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + drm_connector_unregister(connector); +} + +int drm_connector_register_all(struct drm_device *dev) +{ + struct drm_connector *connector; + int ret; + + /* FIXME: taking the mode config mutex ends up in a clash with + * fbcon/backlight registration */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + ret = drm_connector_register(connector); + if (ret) + goto err; + } + + return 0; + +err: + mutex_unlock(&dev->mode_config.mutex); + drm_connector_unregister_all(dev); + return ret; +} + +/** + * drm_get_connector_status_name - return a string for connector status + * @status: connector status to compute name of + * + * In contrast to the other drm_get_*_name functions this one here returns a + * const pointer and hence is threadsafe. + */ +const char *drm_get_connector_status_name(enum drm_connector_status status) +{ + if (status == connector_status_connected) + return "connected"; + else if (status == connector_status_disconnected) + return "disconnected"; + else + return "unknown"; +} +EXPORT_SYMBOL(drm_get_connector_status_name); + +static const struct drm_prop_enum_list drm_subpixel_enum_list[] = { + { SubPixelUnknown, "Unknown" }, + { SubPixelHorizontalRGB, "Horizontal RGB" }, + { SubPixelHorizontalBGR, "Horizontal BGR" }, + { SubPixelVerticalRGB, "Vertical RGB" }, + { SubPixelVerticalBGR, "Vertical BGR" }, + { SubPixelNone, "None" }, +}; + +/** + * drm_get_subpixel_order_name - return a string for a given subpixel enum + * @order: enum of subpixel_order + * + * Note you could abuse this and return something out of bounds, but that + * would be a caller error. No unscrubbed user data should make it here. + */ +const char *drm_get_subpixel_order_name(enum subpixel_order order) +{ + return drm_subpixel_enum_list[order].name; +} +EXPORT_SYMBOL(drm_get_subpixel_order_name); + +static const struct drm_prop_enum_list drm_dpms_enum_list[] = { + { DRM_MODE_DPMS_ON, "On" }, + { DRM_MODE_DPMS_STANDBY, "Standby" }, + { DRM_MODE_DPMS_SUSPEND, "Suspend" }, + { DRM_MODE_DPMS_OFF, "Off" } +}; +DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) + +/** + * drm_display_info_set_bus_formats - set the supported bus formats + * @info: display info to store bus formats in + * @formats: array containing the supported bus formats + * @num_formats: the number of entries in the fmts array + * + * Store the supported bus formats in display info structure. + * See MEDIA_BUS_FMT_* definitions in include/uapi/linux/media-bus-format.h for + * a full list of available formats. + */ +int drm_display_info_set_bus_formats(struct drm_display_info *info, + const u32 *formats, + unsigned int num_formats) +{ + u32 *fmts = NULL; + + if (!formats && num_formats) + return -EINVAL; + + if (formats && num_formats) { + fmts = kmemdup(formats, sizeof(*formats) * num_formats, + GFP_KERNEL); + if (!fmts) + return -ENOMEM; + } + + kfree(info->bus_formats); + info->bus_formats = fmts; + info->num_bus_formats = num_formats; + + return 0; +} +EXPORT_SYMBOL(drm_display_info_set_bus_formats); + +/* Optional connector properties. */ +static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = { + { DRM_MODE_SCALE_NONE, "None" }, + { DRM_MODE_SCALE_FULLSCREEN, "Full" }, + { DRM_MODE_SCALE_CENTER, "Center" }, + { DRM_MODE_SCALE_ASPECT, "Full aspect" }, +}; + +static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = { + { DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" }, + { DRM_MODE_PICTURE_ASPECT_4_3, "4:3" }, + { DRM_MODE_PICTURE_ASPECT_16_9, "16:9" }, +}; + +static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = { + { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ + { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ + { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ +}; +DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list) + +static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = { + { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ + { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ + { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ +}; +DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, + drm_dvi_i_subconnector_enum_list) + +static const struct drm_prop_enum_list drm_tv_select_enum_list[] = { + { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ + { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ + { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ + { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ + { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ +}; +DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list) + +static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = { + { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ + { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ + { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ + { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ + { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ +}; +DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, + drm_tv_subconnector_enum_list) + +int drm_connector_create_standard_properties(struct drm_device *dev) +{ + struct drm_property *prop; + + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | + DRM_MODE_PROP_IMMUTABLE, + "EDID", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.edid_property = prop; + + prop = drm_property_create_enum(dev, 0, + "DPMS", drm_dpms_enum_list, + ARRAY_SIZE(drm_dpms_enum_list)); + if (!prop) + return -ENOMEM; + dev->mode_config.dpms_property = prop; + + prop = drm_property_create(dev, + DRM_MODE_PROP_BLOB | + DRM_MODE_PROP_IMMUTABLE, + "PATH", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.path_property = prop; + + prop = drm_property_create(dev, + DRM_MODE_PROP_BLOB | + DRM_MODE_PROP_IMMUTABLE, + "TILE", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.tile_property = prop; + + return 0; +} + +/** + * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties + * @dev: DRM device + * + * Called by a driver the first time a DVI-I connector is made. + */ +int drm_mode_create_dvi_i_properties(struct drm_device *dev) +{ + struct drm_property *dvi_i_selector; + struct drm_property *dvi_i_subconnector; + + if (dev->mode_config.dvi_i_select_subconnector_property) + return 0; + + dvi_i_selector = + drm_property_create_enum(dev, 0, + "select subconnector", + drm_dvi_i_select_enum_list, + ARRAY_SIZE(drm_dvi_i_select_enum_list)); + dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector; + + dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, + "subconnector", + drm_dvi_i_subconnector_enum_list, + ARRAY_SIZE(drm_dvi_i_subconnector_enum_list)); + dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector; + + return 0; +} +EXPORT_SYMBOL(drm_mode_create_dvi_i_properties); + +/** + * drm_create_tv_properties - create TV specific connector properties + * @dev: DRM device + * @num_modes: number of different TV formats (modes) supported + * @modes: array of pointers to strings containing name of each format + * + * Called by a driver's TV initialization routine, this function creates + * the TV specific connector properties for a given device. Caller is + * responsible for allocating a list of format names and passing them to + * this routine. + */ +int drm_mode_create_tv_properties(struct drm_device *dev, + unsigned int num_modes, + const char * const modes[]) +{ + struct drm_property *tv_selector; + struct drm_property *tv_subconnector; + unsigned int i; + + if (dev->mode_config.tv_select_subconnector_property) + return 0; + + /* + * Basic connector properties + */ + tv_selector = drm_property_create_enum(dev, 0, + "select subconnector", + drm_tv_select_enum_list, + ARRAY_SIZE(drm_tv_select_enum_list)); + if (!tv_selector) + goto nomem; + + dev->mode_config.tv_select_subconnector_property = tv_selector; + + tv_subconnector = + drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, + "subconnector", + drm_tv_subconnector_enum_list, + ARRAY_SIZE(drm_tv_subconnector_enum_list)); + if (!tv_subconnector) + goto nomem; + dev->mode_config.tv_subconnector_property = tv_subconnector; + + /* + * Other, TV specific properties: margins & TV modes. + */ + dev->mode_config.tv_left_margin_property = + drm_property_create_range(dev, 0, "left margin", 0, 100); + if (!dev->mode_config.tv_left_margin_property) + goto nomem; + + dev->mode_config.tv_right_margin_property = + drm_property_create_range(dev, 0, "right margin", 0, 100); + if (!dev->mode_config.tv_right_margin_property) + goto nomem; + + dev->mode_config.tv_top_margin_property = + drm_property_create_range(dev, 0, "top margin", 0, 100); + if (!dev->mode_config.tv_top_margin_property) + goto nomem; + + dev->mode_config.tv_bottom_margin_property = + drm_property_create_range(dev, 0, "bottom margin", 0, 100); + if (!dev->mode_config.tv_bottom_margin_property) + goto nomem; + + dev->mode_config.tv_mode_property = + drm_property_create(dev, DRM_MODE_PROP_ENUM, + "mode", num_modes); + if (!dev->mode_config.tv_mode_property) + goto nomem; + + for (i = 0; i < num_modes; i++) + drm_property_add_enum(dev->mode_config.tv_mode_property, i, + i, modes[i]); + + dev->mode_config.tv_brightness_property = + drm_property_create_range(dev, 0, "brightness", 0, 100); + if (!dev->mode_config.tv_brightness_property) + goto nomem; + + dev->mode_config.tv_contrast_property = + drm_property_create_range(dev, 0, "contrast", 0, 100); + if (!dev->mode_config.tv_contrast_property) + goto nomem; + + dev->mode_config.tv_flicker_reduction_property = + drm_property_create_range(dev, 0, "flicker reduction", 0, 100); + if (!dev->mode_config.tv_flicker_reduction_property) + goto nomem; + + dev->mode_config.tv_overscan_property = + drm_property_create_range(dev, 0, "overscan", 0, 100); + if (!dev->mode_config.tv_overscan_property) + goto nomem; + + dev->mode_config.tv_saturation_property = + drm_property_create_range(dev, 0, "saturation", 0, 100); + if (!dev->mode_config.tv_saturation_property) + goto nomem; + + dev->mode_config.tv_hue_property = + drm_property_create_range(dev, 0, "hue", 0, 100); + if (!dev->mode_config.tv_hue_property) + goto nomem; + + return 0; +nomem: + return -ENOMEM; +} +EXPORT_SYMBOL(drm_mode_create_tv_properties); + +/** + * drm_mode_create_scaling_mode_property - create scaling mode property + * @dev: DRM device + * + * Called by a driver the first time it's needed, must be attached to desired + * connectors. + */ +int drm_mode_create_scaling_mode_property(struct drm_device *dev) +{ + struct drm_property *scaling_mode; + + if (dev->mode_config.scaling_mode_property) + return 0; + + scaling_mode = + drm_property_create_enum(dev, 0, "scaling mode", + drm_scaling_mode_enum_list, + ARRAY_SIZE(drm_scaling_mode_enum_list)); + + dev->mode_config.scaling_mode_property = scaling_mode; + + return 0; +} +EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); + +/** + * drm_mode_create_aspect_ratio_property - create aspect ratio property + * @dev: DRM device + * + * Called by a driver the first time it's needed, must be attached to desired + * connectors. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_create_aspect_ratio_property(struct drm_device *dev) +{ + if (dev->mode_config.aspect_ratio_property) + return 0; + + dev->mode_config.aspect_ratio_property = + drm_property_create_enum(dev, 0, "aspect ratio", + drm_aspect_ratio_enum_list, + ARRAY_SIZE(drm_aspect_ratio_enum_list)); + + if (dev->mode_config.aspect_ratio_property == NULL) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property); + +/** + * drm_mode_create_suggested_offset_properties - create suggests offset properties + * @dev: DRM device + * + * Create the the suggested x/y offset property for connectors. + */ +int drm_mode_create_suggested_offset_properties(struct drm_device *dev) +{ + if (dev->mode_config.suggested_x_property && dev->mode_config.suggested_y_property) + return 0; + + dev->mode_config.suggested_x_property = + drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested X", 0, 0xffffffff); + + dev->mode_config.suggested_y_property = + drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested Y", 0, 0xffffffff); + + if (dev->mode_config.suggested_x_property == NULL || + dev->mode_config.suggested_y_property == NULL) + return -ENOMEM; + return 0; +} +EXPORT_SYMBOL(drm_mode_create_suggested_offset_properties); + +/** + * drm_mode_connector_set_path_property - set tile property on connector + * @connector: connector to set property on. + * @path: path to use for property; must not be NULL. + * + * This creates a property to expose to userspace to specify a + * connector path. This is mainly used for DisplayPort MST where + * connectors have a topology and we want to allow userspace to give + * them more meaningful names. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_connector_set_path_property(struct drm_connector *connector, + const char *path) +{ + struct drm_device *dev = connector->dev; + int ret; + + ret = drm_property_replace_global_blob(dev, + &connector->path_blob_ptr, + strlen(path) + 1, + path, + &connector->base, + dev->mode_config.path_property); + return ret; +} +EXPORT_SYMBOL(drm_mode_connector_set_path_property); + +/** + * drm_mode_connector_set_tile_property - set tile property on connector + * @connector: connector to set property on. + * + * This looks up the tile information for a connector, and creates a + * property for userspace to parse if it exists. The property is of + * the form of 8 integers using ':' as a separator. + * + * Returns: + * Zero on success, errno on failure. + */ +int drm_mode_connector_set_tile_property(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + char tile[256]; + int ret; + + if (!connector->has_tile) { + ret = drm_property_replace_global_blob(dev, + &connector->tile_blob_ptr, + 0, + NULL, + &connector->base, + dev->mode_config.tile_property); + return ret; + } + + snprintf(tile, 256, "%d:%d:%d:%d:%d:%d:%d:%d", + connector->tile_group->id, connector->tile_is_single_monitor, + connector->num_h_tile, connector->num_v_tile, + connector->tile_h_loc, connector->tile_v_loc, + connector->tile_h_size, connector->tile_v_size); + + ret = drm_property_replace_global_blob(dev, + &connector->tile_blob_ptr, + strlen(tile) + 1, + tile, + &connector->base, + dev->mode_config.tile_property); + return ret; +} +EXPORT_SYMBOL(drm_mode_connector_set_tile_property); + +/** + * drm_mode_connector_update_edid_property - update the edid property of a connector + * @connector: drm connector + * @edid: new value of the edid property + * + * This function creates a new blob modeset object and assigns its id to the + * connector's edid property. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_connector_update_edid_property(struct drm_connector *connector, + const struct edid *edid) +{ + struct drm_device *dev = connector->dev; + size_t size = 0; + int ret; + + /* ignore requests to set edid when overridden */ + if (connector->override_edid) + return 0; + + if (edid) + size = EDID_LENGTH * (1 + edid->extensions); + + ret = drm_property_replace_global_blob(dev, + &connector->edid_blob_ptr, + size, + edid, + &connector->base, + dev->mode_config.edid_property); + return ret; +} +EXPORT_SYMBOL(drm_mode_connector_update_edid_property); + +int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t value) +{ + int ret = -EINVAL; + struct drm_connector *connector = obj_to_connector(obj); + + /* Do DPMS ourselves */ + if (property == connector->dev->mode_config.dpms_property) { + ret = (*connector->funcs->dpms)(connector, (int)value); + } else if (connector->funcs->set_property) + ret = connector->funcs->set_property(connector, property, value); + + /* store the property value if successful */ + if (!ret) + drm_object_property_set_value(&connector->base, property, value); + return ret; +} + +int drm_mode_connector_property_set_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_connector_set_property *conn_set_prop = data; + struct drm_mode_obj_set_property obj_set_prop = { + .value = conn_set_prop->value, + .prop_id = conn_set_prop->prop_id, + .obj_id = conn_set_prop->connector_id, + .obj_type = DRM_MODE_OBJECT_CONNECTOR + }; + + /* It does all the locking and checking we need */ + return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); +} + +static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector) +{ + /* For atomic drivers only state objects are synchronously updated and + * protected by modeset locks, so check those first. */ + if (connector->state) + return connector->state->best_encoder; + return connector->encoder; +} + +static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode, + const struct drm_file *file_priv) +{ + /* + * If user-space hasn't configured the driver to expose the stereo 3D + * modes, don't expose them. + */ + if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode)) + return false; + + return true; +} + +int drm_mode_getconnector(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_mode_get_connector *out_resp = data; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct drm_display_mode *mode; + int mode_count = 0; + int encoders_count = 0; + int ret = 0; + int copied = 0; + int i; + struct drm_mode_modeinfo u_mode; + struct drm_mode_modeinfo __user *mode_ptr; + uint32_t __user *encoder_ptr; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); + + mutex_lock(&dev->mode_config.mutex); + + connector = drm_connector_lookup(dev, out_resp->connector_id); + if (!connector) { + ret = -ENOENT; + goto out_unlock; + } + + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) + if (connector->encoder_ids[i] != 0) + encoders_count++; + + if (out_resp->count_modes == 0) { + connector->funcs->fill_modes(connector, + dev->mode_config.max_width, + dev->mode_config.max_height); + } + + /* 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, file_priv)) + mode_count++; + + out_resp->connector_id = connector->base.id; + out_resp->connector_type = connector->connector_type; + out_resp->connector_type_id = connector->connector_type_id; + out_resp->mm_width = connector->display_info.width_mm; + out_resp->mm_height = connector->display_info.height_mm; + out_resp->subpixel = connector->display_info.subpixel_order; + out_resp->connection = connector->status; + + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + encoder = drm_connector_get_encoder(connector); + if (encoder) + out_resp->encoder_id = encoder->base.id; + else + out_resp->encoder_id = 0; + + /* + * This ioctl is called twice, once to determine how much space is + * needed, and the 2nd time to fill it. + */ + 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, &connector->modes, head) { + if (!drm_mode_expose_to_userspace(mode, file_priv)) + continue; + + drm_mode_convert_to_umode(&u_mode, mode); + if (copy_to_user(mode_ptr + copied, + &u_mode, sizeof(u_mode))) { + ret = -EFAULT; + goto out; + } + copied++; + } + } + out_resp->count_modes = mode_count; + + ret = drm_mode_object_get_properties(&connector->base, file_priv->atomic, + (uint32_t __user *)(unsigned long)(out_resp->props_ptr), + (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr), + &out_resp->count_props); + if (ret) + goto out; + + if ((out_resp->count_encoders >= encoders_count) && encoders_count) { + copied = 0; + encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr); + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] != 0) { + if (put_user(connector->encoder_ids[i], + encoder_ptr + copied)) { + ret = -EFAULT; + goto out; + } + copied++; + } + } + } + out_resp->count_encoders = encoders_count; + +out: + drm_modeset_unlock(&dev->mode_config.connection_mutex); + + drm_connector_unreference(connector); +out_unlock: + mutex_unlock(&dev->mode_config.mutex); + + return ret; +} + diff --git a/drivers/gpu/drm/drm_context.c b/drivers/gpu/drm/drm_context.c index 192a5f9..3c4000f 100644 --- a/drivers/gpu/drm/drm_context.c +++ b/drivers/gpu/drm/drm_context.c @@ -54,7 +54,7 @@ struct drm_ctx_list { void drm_legacy_ctxbitmap_free(struct drm_device * dev, int ctx_handle) { if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return; mutex_lock(&dev->struct_mutex); @@ -92,7 +92,7 @@ static int drm_legacy_ctxbitmap_next(struct drm_device * dev) void drm_legacy_ctxbitmap_init(struct drm_device * dev) { if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return; idr_init(&dev->ctx_idr); @@ -109,7 +109,7 @@ void drm_legacy_ctxbitmap_init(struct drm_device * dev) void drm_legacy_ctxbitmap_cleanup(struct drm_device * dev) { if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return; mutex_lock(&dev->struct_mutex); @@ -131,7 +131,7 @@ void drm_legacy_ctxbitmap_flush(struct drm_device *dev, struct drm_file *file) struct drm_ctx_list *pos, *tmp; if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return; mutex_lock(&dev->ctxlist_mutex); @@ -177,7 +177,7 @@ int drm_legacy_getsareactx(struct drm_device *dev, void *data, struct drm_map_list *_entry; if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; mutex_lock(&dev->struct_mutex); @@ -225,7 +225,7 @@ int drm_legacy_setsareactx(struct drm_device *dev, void *data, struct drm_map_list *r_list = NULL; if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; mutex_lock(&dev->struct_mutex); @@ -329,7 +329,7 @@ int drm_legacy_resctx(struct drm_device *dev, void *data, int i; if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; if (res->count >= DRM_RESERVED_CONTEXTS) { @@ -363,7 +363,7 @@ int drm_legacy_addctx(struct drm_device *dev, void *data, struct drm_ctx *ctx = data; if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; ctx->handle = drm_legacy_ctxbitmap_next(dev); @@ -410,7 +410,7 @@ int drm_legacy_getctx(struct drm_device *dev, void *data, struct drm_ctx *ctx = data; if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; /* This is 0, because we don't handle any context flags */ @@ -436,7 +436,7 @@ int drm_legacy_switchctx(struct drm_device *dev, void *data, struct drm_ctx *ctx = data; if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; DRM_DEBUG("%d\n", ctx->handle); @@ -460,7 +460,7 @@ int drm_legacy_newctx(struct drm_device *dev, void *data, struct drm_ctx *ctx = data; if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; DRM_DEBUG("%d\n", ctx->handle); @@ -486,7 +486,7 @@ int drm_legacy_rmctx(struct drm_device *dev, void *data, struct drm_ctx *ctx = data; if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && - drm_core_check_feature(dev, DRIVER_MODESET)) + !drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; DRM_DEBUG("%d\n", ctx->handle); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 93b9821..2d7bedf 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -40,39 +40,14 @@ #include <drm/drm_modeset_lock.h> #include <drm/drm_atomic.h> #include <drm/drm_auth.h> +#include <drm/drm_framebuffer.h> #include "drm_crtc_internal.h" #include "drm_internal.h" -static struct drm_framebuffer * -internal_framebuffer_create(struct drm_device *dev, - const struct drm_mode_fb_cmd2 *r, - struct drm_file *file_priv); - -/* Avoid boilerplate. I'm tired of typing. */ -#define DRM_ENUM_NAME_FN(fnname, list) \ - const char *fnname(int val) \ - { \ - int i; \ - for (i = 0; i < ARRAY_SIZE(list); i++) { \ - if (list[i].type == val) \ - return list[i].name; \ - } \ - return "(unknown)"; \ - } - /* * Global properties */ -static const struct drm_prop_enum_list drm_dpms_enum_list[] = { - { DRM_MODE_DPMS_ON, "On" }, - { DRM_MODE_DPMS_STANDBY, "Standby" }, - { DRM_MODE_DPMS_SUSPEND, "Suspend" }, - { DRM_MODE_DPMS_OFF, "Off" } -}; - -DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) - static const struct drm_prop_enum_list drm_plane_type_enum_list[] = { { DRM_PLANE_TYPE_OVERLAY, "Overlay" }, { DRM_PLANE_TYPE_PRIMARY, "Primary" }, @@ -82,320 +57,6 @@ static const struct drm_prop_enum_list drm_plane_type_enum_list[] = { /* * Optional properties */ -static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = { - { DRM_MODE_SCALE_NONE, "None" }, - { DRM_MODE_SCALE_FULLSCREEN, "Full" }, - { DRM_MODE_SCALE_CENTER, "Center" }, - { DRM_MODE_SCALE_ASPECT, "Full aspect" }, -}; - -static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = { - { DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" }, - { DRM_MODE_PICTURE_ASPECT_4_3, "4:3" }, - { DRM_MODE_PICTURE_ASPECT_16_9, "16:9" }, -}; - -/* - * Non-global properties, but "required" for certain connectors. - */ -static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = { - { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ - { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ - { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ -}; - -DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list) - -static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = { - { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ - { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ - { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ -}; - -DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, - drm_dvi_i_subconnector_enum_list) - -static const struct drm_prop_enum_list drm_tv_select_enum_list[] = { - { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ - { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ - { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ - { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ - { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ -}; - -DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list) - -static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = { - { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ - { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ - { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ - { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ - { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ -}; - -DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, - drm_tv_subconnector_enum_list) - -static const struct drm_prop_enum_list drm_dirty_info_enum_list[] = { - { DRM_MODE_DIRTY_OFF, "Off" }, - { DRM_MODE_DIRTY_ON, "On" }, - { DRM_MODE_DIRTY_ANNOTATE, "Annotate" }, -}; - -struct drm_conn_prop_enum_list { - int type; - const char *name; - struct ida ida; -}; - -/* - * Connector and encoder types. - */ -static struct drm_conn_prop_enum_list drm_connector_enum_list[] = { - { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, - { DRM_MODE_CONNECTOR_VGA, "VGA" }, - { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, - { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, - { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, - { DRM_MODE_CONNECTOR_Composite, "Composite" }, - { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" }, - { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, - { DRM_MODE_CONNECTOR_Component, "Component" }, - { DRM_MODE_CONNECTOR_9PinDIN, "DIN" }, - { DRM_MODE_CONNECTOR_DisplayPort, "DP" }, - { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, - { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, - { DRM_MODE_CONNECTOR_TV, "TV" }, - { DRM_MODE_CONNECTOR_eDP, "eDP" }, - { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" }, - { DRM_MODE_CONNECTOR_DSI, "DSI" }, - { DRM_MODE_CONNECTOR_DPI, "DPI" }, -}; - -static const struct drm_prop_enum_list drm_encoder_enum_list[] = { - { DRM_MODE_ENCODER_NONE, "None" }, - { DRM_MODE_ENCODER_DAC, "DAC" }, - { DRM_MODE_ENCODER_TMDS, "TMDS" }, - { DRM_MODE_ENCODER_LVDS, "LVDS" }, - { DRM_MODE_ENCODER_TVDAC, "TV" }, - { DRM_MODE_ENCODER_VIRTUAL, "Virtual" }, - { DRM_MODE_ENCODER_DSI, "DSI" }, - { DRM_MODE_ENCODER_DPMST, "DP MST" }, - { DRM_MODE_ENCODER_DPI, "DPI" }, -}; - -static const struct drm_prop_enum_list drm_subpixel_enum_list[] = { - { SubPixelUnknown, "Unknown" }, - { SubPixelHorizontalRGB, "Horizontal RGB" }, - { SubPixelHorizontalBGR, "Horizontal BGR" }, - { SubPixelVerticalRGB, "Vertical RGB" }, - { SubPixelVerticalBGR, "Vertical BGR" }, - { SubPixelNone, "None" }, -}; - -void drm_connector_ida_init(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) - ida_init(&drm_connector_enum_list[i].ida); -} - -void drm_connector_ida_destroy(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) - ida_destroy(&drm_connector_enum_list[i].ida); -} - -/** - * drm_get_connector_status_name - return a string for connector status - * @status: connector status to compute name of - * - * In contrast to the other drm_get_*_name functions this one here returns a - * const pointer and hence is threadsafe. - */ -const char *drm_get_connector_status_name(enum drm_connector_status status) -{ - if (status == connector_status_connected) - return "connected"; - else if (status == connector_status_disconnected) - return "disconnected"; - else - return "unknown"; -} -EXPORT_SYMBOL(drm_get_connector_status_name); - -/** - * drm_get_subpixel_order_name - return a string for a given subpixel enum - * @order: enum of subpixel_order - * - * Note you could abuse this and return something out of bounds, but that - * would be a caller error. No unscrubbed user data should make it here. - */ -const char *drm_get_subpixel_order_name(enum subpixel_order order) -{ - return drm_subpixel_enum_list[order].name; -} -EXPORT_SYMBOL(drm_get_subpixel_order_name); - -/* - * Internal function to assign a slot in the object idr and optionally - * register the object into the idr. - */ -static int drm_mode_object_get_reg(struct drm_device *dev, - struct drm_mode_object *obj, - uint32_t obj_type, - bool register_obj, - void (*obj_free_cb)(struct kref *kref)) -{ - int ret; - - mutex_lock(&dev->mode_config.idr_mutex); - ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL); - if (ret >= 0) { - /* - * Set up the object linking under the protection of the idr - * lock so that other users can't see inconsistent state. - */ - obj->id = ret; - obj->type = obj_type; - if (obj_free_cb) { - obj->free_cb = obj_free_cb; - kref_init(&obj->refcount); - } - } - mutex_unlock(&dev->mode_config.idr_mutex); - - return ret < 0 ? ret : 0; -} - -/** - * drm_mode_object_get - allocate a new modeset identifier - * @dev: DRM device - * @obj: object pointer, used to generate unique ID - * @obj_type: object type - * - * Create a unique identifier based on @ptr in @dev's identifier space. Used - * for tracking modes, CRTCs and connectors. Note that despite the _get postfix - * modeset identifiers are _not_ reference counted. Hence don't use this for - * reference counted modeset objects like framebuffers. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_mode_object_get(struct drm_device *dev, - struct drm_mode_object *obj, uint32_t obj_type) -{ - return drm_mode_object_get_reg(dev, obj, obj_type, true, NULL); -} - -static void drm_mode_object_register(struct drm_device *dev, - struct drm_mode_object *obj) -{ - mutex_lock(&dev->mode_config.idr_mutex); - idr_replace(&dev->mode_config.crtc_idr, obj, obj->id); - mutex_unlock(&dev->mode_config.idr_mutex); -} - -/** - * drm_mode_object_unregister - free a modeset identifer - * @dev: DRM device - * @object: object to free - * - * Free @id from @dev's unique identifier pool. - * This function can be called multiple times, and guards against - * multiple removals. - * These modeset identifiers are _not_ reference counted. Hence don't use this - * for reference counted modeset objects like framebuffers. - */ -void drm_mode_object_unregister(struct drm_device *dev, - struct drm_mode_object *object) -{ - mutex_lock(&dev->mode_config.idr_mutex); - if (object->id) { - idr_remove(&dev->mode_config.crtc_idr, object->id); - object->id = 0; - } - mutex_unlock(&dev->mode_config.idr_mutex); -} - -static struct drm_mode_object *_object_find(struct drm_device *dev, - uint32_t id, uint32_t type) -{ - struct drm_mode_object *obj = NULL; - - mutex_lock(&dev->mode_config.idr_mutex); - obj = idr_find(&dev->mode_config.crtc_idr, id); - if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type) - obj = NULL; - if (obj && obj->id != id) - obj = NULL; - - if (obj && obj->free_cb) { - if (!kref_get_unless_zero(&obj->refcount)) - obj = NULL; - } - mutex_unlock(&dev->mode_config.idr_mutex); - - return obj; -} - -/** - * drm_mode_object_find - look up a drm object with static lifetime - * @dev: drm device - * @id: id of the mode object - * @type: type of the mode object - * - * This function is used to look up a modeset object. It will acquire a - * reference for reference counted objects. This reference must be dropped again - * by callind drm_mode_object_unreference(). - */ -struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, - uint32_t id, uint32_t type) -{ - struct drm_mode_object *obj = NULL; - - obj = _object_find(dev, id, type); - return obj; -} -EXPORT_SYMBOL(drm_mode_object_find); - -/** - * drm_mode_object_unreference - decr the object refcnt - * @obj: mode_object - * - * This functions decrements the object's refcount if it is a refcounted modeset - * object. It is a no-op on any other object. This is used to drop references - * acquired with drm_mode_object_reference(). - */ -void drm_mode_object_unreference(struct drm_mode_object *obj) -{ - if (obj->free_cb) { - DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount)); - kref_put(&obj->refcount, obj->free_cb); - } -} -EXPORT_SYMBOL(drm_mode_object_unreference); - -/** - * drm_mode_object_reference - incr the object refcnt - * @obj: mode_object - * - * This functions increments the object's refcount if it is a refcounted modeset - * object. It is a no-op on any other object. References should be dropped again - * by calling drm_mode_object_unreference(). - */ -void drm_mode_object_reference(struct drm_mode_object *obj) -{ - if (obj->free_cb) { - DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount)); - kref_get(&obj->refcount); - } -} -EXPORT_SYMBOL(drm_mode_object_reference); - /** * drm_crtc_force_disable - Forcibly turn off a CRTC * @crtc: CRTC to turn off @@ -441,199 +102,6 @@ out: } EXPORT_SYMBOL(drm_crtc_force_disable_all); -static void drm_framebuffer_free(struct kref *kref) -{ - struct drm_framebuffer *fb = - container_of(kref, struct drm_framebuffer, base.refcount); - struct drm_device *dev = fb->dev; - - /* - * The lookup idr holds a weak reference, which has not necessarily been - * removed at this point. Check for that. - */ - drm_mode_object_unregister(dev, &fb->base); - - fb->funcs->destroy(fb); -} - -/** - * drm_framebuffer_init - initialize a framebuffer - * @dev: DRM device - * @fb: framebuffer to be initialized - * @funcs: ... with these functions - * - * Allocates an ID for the framebuffer's parent mode object, sets its mode - * functions & device file and adds it to the master fd list. - * - * IMPORTANT: - * This functions publishes the fb and makes it available for concurrent access - * by other users. Which means by this point the fb _must_ be fully set up - - * since all the fb attributes are invariant over its lifetime, no further - * locking but only correct reference counting is required. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, - const struct drm_framebuffer_funcs *funcs) -{ - int ret; - - INIT_LIST_HEAD(&fb->filp_head); - fb->dev = dev; - fb->funcs = funcs; - - ret = drm_mode_object_get_reg(dev, &fb->base, DRM_MODE_OBJECT_FB, - false, drm_framebuffer_free); - if (ret) - goto out; - - mutex_lock(&dev->mode_config.fb_lock); - dev->mode_config.num_fb++; - list_add(&fb->head, &dev->mode_config.fb_list); - mutex_unlock(&dev->mode_config.fb_lock); - - drm_mode_object_register(dev, &fb->base); -out: - return ret; -} -EXPORT_SYMBOL(drm_framebuffer_init); - -/** - * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference - * @dev: drm device - * @id: id of the fb object - * - * If successful, this grabs an additional reference to the framebuffer - - * callers need to make sure to eventually unreference the returned framebuffer - * again, using @drm_framebuffer_unreference. - */ -struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, - uint32_t id) -{ - struct drm_mode_object *obj; - struct drm_framebuffer *fb = NULL; - - obj = _object_find(dev, id, DRM_MODE_OBJECT_FB); - if (obj) - fb = obj_to_fb(obj); - return fb; -} -EXPORT_SYMBOL(drm_framebuffer_lookup); - -/** - * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr - * @fb: fb to unregister - * - * Drivers need to call this when cleaning up driver-private framebuffers, e.g. - * those used for fbdev. Note that the caller must hold a reference of it's own, - * i.e. the object may not be destroyed through this call (since it'll lead to a - * locking inversion). - */ -void drm_framebuffer_unregister_private(struct drm_framebuffer *fb) -{ - struct drm_device *dev; - - if (!fb) - return; - - dev = fb->dev; - - /* Mark fb as reaped and drop idr ref. */ - drm_mode_object_unregister(dev, &fb->base); -} -EXPORT_SYMBOL(drm_framebuffer_unregister_private); - -/** - * drm_framebuffer_cleanup - remove a framebuffer object - * @fb: framebuffer to remove - * - * Cleanup framebuffer. This function is intended to be used from the drivers - * ->destroy callback. It can also be used to clean up driver private - * framebuffers embedded into a larger structure. - * - * Note that this function does not remove the fb from active usuage - if it is - * still used anywhere, hilarity can ensue since userspace could call getfb on - * the id and get back -EINVAL. Obviously no concern at driver unload time. - * - * Also, the framebuffer will not be removed from the lookup idr - for - * user-created framebuffers this will happen in in the rmfb ioctl. For - * driver-private objects (e.g. for fbdev) drivers need to explicitly call - * drm_framebuffer_unregister_private. - */ -void drm_framebuffer_cleanup(struct drm_framebuffer *fb) -{ - struct drm_device *dev = fb->dev; - - mutex_lock(&dev->mode_config.fb_lock); - list_del(&fb->head); - dev->mode_config.num_fb--; - mutex_unlock(&dev->mode_config.fb_lock); -} -EXPORT_SYMBOL(drm_framebuffer_cleanup); - -/** - * drm_framebuffer_remove - remove and unreference a framebuffer object - * @fb: framebuffer to remove - * - * Scans all the CRTCs and planes in @dev's mode_config. If they're - * using @fb, removes it, setting it to NULL. Then drops the reference to the - * passed-in framebuffer. Might take the modeset locks. - * - * Note that this function optimizes the cleanup away if the caller holds the - * last reference to the framebuffer. It is also guaranteed to not take the - * modeset locks in this case. - */ -void drm_framebuffer_remove(struct drm_framebuffer *fb) -{ - struct drm_device *dev; - struct drm_crtc *crtc; - struct drm_plane *plane; - - if (!fb) - return; - - dev = fb->dev; - - WARN_ON(!list_empty(&fb->filp_head)); - - /* - * drm ABI mandates that we remove any deleted framebuffers from active - * useage. But since most sane clients only remove framebuffers they no - * longer need, try to optimize this away. - * - * Since we're holding a reference ourselves, observing a refcount of 1 - * means that we're the last holder and can skip it. Also, the refcount - * can never increase from 1 again, so we don't need any barriers or - * locks. - * - * Note that userspace could try to race with use and instate a new - * usage _after_ we've cleared all current ones. End result will be an - * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot - * in this manner. - */ - if (drm_framebuffer_read_refcount(fb) > 1) { - drm_modeset_lock_all(dev); - /* remove from any CRTC */ - drm_for_each_crtc(crtc, dev) { - if (crtc->primary->fb == fb) { - /* should turn off the crtc */ - if (drm_crtc_force_disable(crtc)) - DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); - } - } - - drm_for_each_plane(plane, dev) { - if (plane->fb == fb) - drm_plane_force_disable(plane); - } - drm_modeset_unlock_all(dev); - } - - drm_framebuffer_unreference(fb); -} -EXPORT_SYMBOL(drm_framebuffer_remove); - DEFINE_WW_CLASS(crtc_ww_class); static unsigned int drm_num_crtcs(struct drm_device *dev) @@ -683,7 +151,11 @@ static void drm_crtc_unregister_all(struct drm_device *dev) * @funcs: callbacks for the new CRTC * @name: printf style format string for the CRTC name, or NULL for default name * - * Inits a new object created as base part of a driver crtc object. + * Inits a new object created as base part of a driver crtc object. Drivers + * should use this function instead of drm_crtc_init(), which is only provided + * for backwards compatibility with drivers which do not yet support universal + * planes). For really simple hardware which has only 1 plane look at + * drm_simple_display_pipe_init() instead. * * Returns: * Zero on success, error code on failure. @@ -783,720 +255,6 @@ void drm_crtc_cleanup(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_crtc_cleanup); -/* - * drm_mode_remove - remove and free a mode - * @connector: connector list to modify - * @mode: mode to remove - * - * Remove @mode from @connector's mode list, then free it. - */ -static void drm_mode_remove(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - list_del(&mode->head); - drm_mode_destroy(connector->dev, mode); -} - -/** - * drm_display_info_set_bus_formats - set the supported bus formats - * @info: display info to store bus formats in - * @formats: array containing the supported bus formats - * @num_formats: the number of entries in the fmts array - * - * Store the supported bus formats in display info structure. - * See MEDIA_BUS_FMT_* definitions in include/uapi/linux/media-bus-format.h for - * a full list of available formats. - */ -int drm_display_info_set_bus_formats(struct drm_display_info *info, - const u32 *formats, - unsigned int num_formats) -{ - u32 *fmts = NULL; - - if (!formats && num_formats) - return -EINVAL; - - if (formats && num_formats) { - fmts = kmemdup(formats, sizeof(*formats) * num_formats, - GFP_KERNEL); - if (!fmts) - return -ENOMEM; - } - - kfree(info->bus_formats); - info->bus_formats = fmts; - info->num_bus_formats = num_formats; - - return 0; -} -EXPORT_SYMBOL(drm_display_info_set_bus_formats); - -/** - * drm_connector_get_cmdline_mode - reads the user's cmdline mode - * @connector: connector to quwery - * - * The kernel supports per-connector configration of its consoles through - * use of the video= parameter. This function parses that option and - * extracts the user's specified mode (or enable/disable status) for a - * particular connector. This is typically only used during the early fbdev - * setup. - */ -static void drm_connector_get_cmdline_mode(struct drm_connector *connector) -{ - struct drm_cmdline_mode *mode = &connector->cmdline_mode; - char *option = NULL; - - if (fb_get_options(connector->name, &option)) - return; - - if (!drm_mode_parse_command_line_for_connector(option, - connector, - mode)) - return; - - if (mode->force) { - const char *s; - - switch (mode->force) { - case DRM_FORCE_OFF: - s = "OFF"; - break; - case DRM_FORCE_ON_DIGITAL: - s = "ON - dig"; - break; - default: - case DRM_FORCE_ON: - s = "ON"; - break; - } - - DRM_INFO("forcing %s connector %s\n", connector->name, s); - connector->force = mode->force; - } - - DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", - connector->name, - mode->xres, mode->yres, - mode->refresh_specified ? mode->refresh : 60, - mode->rb ? " reduced blanking" : "", - mode->margins ? " with margins" : "", - mode->interlace ? " interlaced" : ""); -} - -static void drm_connector_free(struct kref *kref) -{ - struct drm_connector *connector = - container_of(kref, struct drm_connector, base.refcount); - struct drm_device *dev = connector->dev; - - drm_mode_object_unregister(dev, &connector->base); - connector->funcs->destroy(connector); -} - -/** - * drm_connector_init - Init a preallocated connector - * @dev: DRM device - * @connector: the connector to init - * @funcs: callbacks for this connector - * @connector_type: user visible type of the connector - * - * Initialises a preallocated connector. Connectors should be - * subclassed as part of driver connector objects. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_connector_init(struct drm_device *dev, - struct drm_connector *connector, - const struct drm_connector_funcs *funcs, - int connector_type) -{ - struct drm_mode_config *config = &dev->mode_config; - int ret; - struct ida *connector_ida = - &drm_connector_enum_list[connector_type].ida; - - drm_modeset_lock_all(dev); - - ret = drm_mode_object_get_reg(dev, &connector->base, - DRM_MODE_OBJECT_CONNECTOR, - false, drm_connector_free); - if (ret) - goto out_unlock; - - connector->base.properties = &connector->properties; - connector->dev = dev; - connector->funcs = funcs; - - ret = ida_simple_get(&config->connector_ida, 0, 0, GFP_KERNEL); - if (ret < 0) - goto out_put; - connector->index = ret; - ret = 0; - - connector->connector_type = connector_type; - connector->connector_type_id = - ida_simple_get(connector_ida, 1, 0, GFP_KERNEL); - if (connector->connector_type_id < 0) { - ret = connector->connector_type_id; - goto out_put_id; - } - connector->name = - kasprintf(GFP_KERNEL, "%s-%d", - drm_connector_enum_list[connector_type].name, - connector->connector_type_id); - if (!connector->name) { - ret = -ENOMEM; - goto out_put_type_id; - } - - INIT_LIST_HEAD(&connector->probed_modes); - INIT_LIST_HEAD(&connector->modes); - connector->edid_blob_ptr = NULL; - connector->status = connector_status_unknown; - - drm_connector_get_cmdline_mode(connector); - - /* We should add connectors at the end to avoid upsetting the connector - * index too much. */ - list_add_tail(&connector->head, &config->connector_list); - config->num_connector++; - - if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) - drm_object_attach_property(&connector->base, - config->edid_property, - 0); - - drm_object_attach_property(&connector->base, - config->dpms_property, 0); - - if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { - drm_object_attach_property(&connector->base, config->prop_crtc_id, 0); - } - - connector->debugfs_entry = NULL; -out_put_type_id: - if (ret) - ida_remove(connector_ida, connector->connector_type_id); -out_put_id: - if (ret) - ida_remove(&config->connector_ida, connector->index); -out_put: - if (ret) - drm_mode_object_unregister(dev, &connector->base); - -out_unlock: - drm_modeset_unlock_all(dev); - - return ret; -} -EXPORT_SYMBOL(drm_connector_init); - -/** - * drm_connector_cleanup - cleans up an initialised connector - * @connector: connector to cleanup - * - * Cleans up the connector but doesn't free the object. - */ -void drm_connector_cleanup(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - struct drm_display_mode *mode, *t; - - /* The connector should have been removed from userspace long before - * it is finally destroyed. - */ - if (WARN_ON(connector->registered)) - drm_connector_unregister(connector); - - if (connector->tile_group) { - drm_mode_put_tile_group(dev, connector->tile_group); - connector->tile_group = NULL; - } - - list_for_each_entry_safe(mode, t, &connector->probed_modes, head) - drm_mode_remove(connector, mode); - - list_for_each_entry_safe(mode, t, &connector->modes, head) - drm_mode_remove(connector, mode); - - ida_remove(&drm_connector_enum_list[connector->connector_type].ida, - connector->connector_type_id); - - ida_remove(&dev->mode_config.connector_ida, - connector->index); - - kfree(connector->display_info.bus_formats); - drm_mode_object_unregister(dev, &connector->base); - kfree(connector->name); - connector->name = NULL; - list_del(&connector->head); - dev->mode_config.num_connector--; - - WARN_ON(connector->state && !connector->funcs->atomic_destroy_state); - if (connector->state && connector->funcs->atomic_destroy_state) - connector->funcs->atomic_destroy_state(connector, - connector->state); - - memset(connector, 0, sizeof(*connector)); -} -EXPORT_SYMBOL(drm_connector_cleanup); - -/** - * drm_connector_register - register a connector - * @connector: the connector to register - * - * Register userspace interfaces for a connector - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_connector_register(struct drm_connector *connector) -{ - int ret; - - if (connector->registered) - return 0; - - ret = drm_sysfs_connector_add(connector); - if (ret) - return ret; - - ret = drm_debugfs_connector_add(connector); - if (ret) { - goto err_sysfs; - } - - if (connector->funcs->late_register) { - ret = connector->funcs->late_register(connector); - if (ret) - goto err_debugfs; - } - - drm_mode_object_register(connector->dev, &connector->base); - - connector->registered = true; - return 0; - -err_debugfs: - drm_debugfs_connector_remove(connector); -err_sysfs: - drm_sysfs_connector_remove(connector); - return ret; -} -EXPORT_SYMBOL(drm_connector_register); - -/** - * drm_connector_unregister - unregister a connector - * @connector: the connector to unregister - * - * Unregister userspace interfaces for a connector - */ -void drm_connector_unregister(struct drm_connector *connector) -{ - if (!connector->registered) - return; - - if (connector->funcs->early_unregister) - connector->funcs->early_unregister(connector); - - drm_sysfs_connector_remove(connector); - drm_debugfs_connector_remove(connector); - - connector->registered = false; -} -EXPORT_SYMBOL(drm_connector_unregister); - -static void drm_connector_unregister_all(struct drm_device *dev) -{ - struct drm_connector *connector; - - /* FIXME: taking the mode config mutex ends up in a clash with sysfs */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) - drm_connector_unregister(connector); -} - -static int drm_connector_register_all(struct drm_device *dev) -{ - struct drm_connector *connector; - int ret; - - /* FIXME: taking the mode config mutex ends up in a clash with - * fbcon/backlight registration */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - ret = drm_connector_register(connector); - if (ret) - goto err; - } - - return 0; - -err: - mutex_unlock(&dev->mode_config.mutex); - drm_connector_unregister_all(dev); - return ret; -} - -static int drm_encoder_register_all(struct drm_device *dev) -{ - struct drm_encoder *encoder; - int ret = 0; - - drm_for_each_encoder(encoder, dev) { - if (encoder->funcs->late_register) - ret = encoder->funcs->late_register(encoder); - if (ret) - return ret; - } - - return 0; -} - -static void drm_encoder_unregister_all(struct drm_device *dev) -{ - struct drm_encoder *encoder; - - drm_for_each_encoder(encoder, dev) { - if (encoder->funcs->early_unregister) - encoder->funcs->early_unregister(encoder); - } -} - -/** - * drm_encoder_init - Init a preallocated encoder - * @dev: drm device - * @encoder: the encoder to init - * @funcs: callbacks for this encoder - * @encoder_type: user visible type of the encoder - * @name: printf style format string for the encoder name, or NULL for default name - * - * Initialises a preallocated encoder. Encoder should be - * subclassed as part of driver encoder objects. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_encoder_init(struct drm_device *dev, - struct drm_encoder *encoder, - const struct drm_encoder_funcs *funcs, - int encoder_type, const char *name, ...) -{ - int ret; - - drm_modeset_lock_all(dev); - - ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); - if (ret) - goto out_unlock; - - encoder->dev = dev; - encoder->encoder_type = encoder_type; - encoder->funcs = funcs; - if (name) { - va_list ap; - - va_start(ap, name); - encoder->name = kvasprintf(GFP_KERNEL, name, ap); - va_end(ap); - } else { - encoder->name = kasprintf(GFP_KERNEL, "%s-%d", - drm_encoder_enum_list[encoder_type].name, - encoder->base.id); - } - if (!encoder->name) { - ret = -ENOMEM; - goto out_put; - } - - list_add_tail(&encoder->head, &dev->mode_config.encoder_list); - encoder->index = dev->mode_config.num_encoder++; - -out_put: - if (ret) - drm_mode_object_unregister(dev, &encoder->base); - -out_unlock: - drm_modeset_unlock_all(dev); - - return ret; -} -EXPORT_SYMBOL(drm_encoder_init); - -/** - * drm_encoder_cleanup - cleans up an initialised encoder - * @encoder: encoder to cleanup - * - * Cleans up the encoder but doesn't free the object. - */ -void drm_encoder_cleanup(struct drm_encoder *encoder) -{ - struct drm_device *dev = encoder->dev; - - /* Note that the encoder_list is considered to be static; should we - * remove the drm_encoder at runtime we would have to decrement all - * the indices on the drm_encoder after us in the encoder_list. - */ - - drm_modeset_lock_all(dev); - drm_mode_object_unregister(dev, &encoder->base); - kfree(encoder->name); - list_del(&encoder->head); - dev->mode_config.num_encoder--; - drm_modeset_unlock_all(dev); - - memset(encoder, 0, sizeof(*encoder)); -} -EXPORT_SYMBOL(drm_encoder_cleanup); - -static unsigned int drm_num_planes(struct drm_device *dev) -{ - unsigned int num = 0; - struct drm_plane *tmp; - - drm_for_each_plane(tmp, dev) { - num++; - } - - return num; -} - -/** - * drm_universal_plane_init - Initialize a new universal plane object - * @dev: DRM device - * @plane: plane object to init - * @possible_crtcs: bitmask of possible CRTCs - * @funcs: callbacks for the new plane - * @formats: array of supported formats (%DRM_FORMAT_*) - * @format_count: number of elements in @formats - * @type: type of plane (overlay, primary, cursor) - * @name: printf style format string for the plane name, or NULL for default name - * - * Initializes a plane object of type @type. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, - unsigned long possible_crtcs, - const struct drm_plane_funcs *funcs, - const uint32_t *formats, unsigned int format_count, - enum drm_plane_type type, - const char *name, ...) -{ - struct drm_mode_config *config = &dev->mode_config; - int ret; - - ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); - if (ret) - return ret; - - drm_modeset_lock_init(&plane->mutex); - - plane->base.properties = &plane->properties; - plane->dev = dev; - plane->funcs = funcs; - plane->format_types = kmalloc_array(format_count, sizeof(uint32_t), - GFP_KERNEL); - if (!plane->format_types) { - DRM_DEBUG_KMS("out of memory when allocating plane\n"); - drm_mode_object_unregister(dev, &plane->base); - return -ENOMEM; - } - - if (name) { - va_list ap; - - va_start(ap, name); - plane->name = kvasprintf(GFP_KERNEL, name, ap); - va_end(ap); - } else { - plane->name = kasprintf(GFP_KERNEL, "plane-%d", - drm_num_planes(dev)); - } - if (!plane->name) { - kfree(plane->format_types); - drm_mode_object_unregister(dev, &plane->base); - return -ENOMEM; - } - - memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); - plane->format_count = format_count; - plane->possible_crtcs = possible_crtcs; - plane->type = type; - - list_add_tail(&plane->head, &config->plane_list); - plane->index = config->num_total_plane++; - if (plane->type == DRM_PLANE_TYPE_OVERLAY) - config->num_overlay_plane++; - - drm_object_attach_property(&plane->base, - config->plane_type_property, - plane->type); - - if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { - drm_object_attach_property(&plane->base, config->prop_fb_id, 0); - drm_object_attach_property(&plane->base, config->prop_crtc_id, 0); - drm_object_attach_property(&plane->base, config->prop_crtc_x, 0); - drm_object_attach_property(&plane->base, config->prop_crtc_y, 0); - drm_object_attach_property(&plane->base, config->prop_crtc_w, 0); - drm_object_attach_property(&plane->base, config->prop_crtc_h, 0); - drm_object_attach_property(&plane->base, config->prop_src_x, 0); - drm_object_attach_property(&plane->base, config->prop_src_y, 0); - drm_object_attach_property(&plane->base, config->prop_src_w, 0); - drm_object_attach_property(&plane->base, config->prop_src_h, 0); - } - - return 0; -} -EXPORT_SYMBOL(drm_universal_plane_init); - -static int drm_plane_register_all(struct drm_device *dev) -{ - struct drm_plane *plane; - int ret = 0; - - drm_for_each_plane(plane, dev) { - if (plane->funcs->late_register) - ret = plane->funcs->late_register(plane); - if (ret) - return ret; - } - - return 0; -} - -static void drm_plane_unregister_all(struct drm_device *dev) -{ - struct drm_plane *plane; - - drm_for_each_plane(plane, dev) { - if (plane->funcs->early_unregister) - plane->funcs->early_unregister(plane); - } -} - -/** - * drm_plane_init - Initialize a legacy plane - * @dev: DRM device - * @plane: plane object to init - * @possible_crtcs: bitmask of possible CRTCs - * @funcs: callbacks for the new plane - * @formats: array of supported formats (%DRM_FORMAT_*) - * @format_count: number of elements in @formats - * @is_primary: plane type (primary vs overlay) - * - * Legacy API to initialize a DRM plane. - * - * New drivers should call drm_universal_plane_init() instead. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, - unsigned long possible_crtcs, - const struct drm_plane_funcs *funcs, - const uint32_t *formats, unsigned int format_count, - bool is_primary) -{ - enum drm_plane_type type; - - type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; - return drm_universal_plane_init(dev, plane, possible_crtcs, funcs, - formats, format_count, type, NULL); -} -EXPORT_SYMBOL(drm_plane_init); - -/** - * drm_plane_cleanup - Clean up the core plane usage - * @plane: plane to cleanup - * - * This function cleans up @plane and removes it from the DRM mode setting - * core. Note that the function does *not* free the plane structure itself, - * this is the responsibility of the caller. - */ -void drm_plane_cleanup(struct drm_plane *plane) -{ - struct drm_device *dev = plane->dev; - - drm_modeset_lock_all(dev); - kfree(plane->format_types); - drm_mode_object_unregister(dev, &plane->base); - - BUG_ON(list_empty(&plane->head)); - - /* Note that the plane_list is considered to be static; should we - * remove the drm_plane at runtime we would have to decrement all - * the indices on the drm_plane after us in the plane_list. - */ - - list_del(&plane->head); - dev->mode_config.num_total_plane--; - if (plane->type == DRM_PLANE_TYPE_OVERLAY) - dev->mode_config.num_overlay_plane--; - drm_modeset_unlock_all(dev); - - WARN_ON(plane->state && !plane->funcs->atomic_destroy_state); - if (plane->state && plane->funcs->atomic_destroy_state) - plane->funcs->atomic_destroy_state(plane, plane->state); - - kfree(plane->name); - - memset(plane, 0, sizeof(*plane)); -} -EXPORT_SYMBOL(drm_plane_cleanup); - -/** - * drm_plane_from_index - find the registered plane at an index - * @dev: DRM device - * @idx: index of registered plane to find for - * - * Given a plane index, return the registered plane from DRM device's - * list of planes with matching index. - */ -struct drm_plane * -drm_plane_from_index(struct drm_device *dev, int idx) -{ - struct drm_plane *plane; - - drm_for_each_plane(plane, dev) - if (idx == plane->index) - return plane; - - return NULL; -} -EXPORT_SYMBOL(drm_plane_from_index); - -/** - * drm_plane_force_disable - Forcibly disable a plane - * @plane: plane to disable - * - * Forces the plane to be disabled. - * - * Used when the plane's current framebuffer is destroyed, - * and when restoring fbdev mode. - */ -void drm_plane_force_disable(struct drm_plane *plane) -{ - int ret; - - if (!plane->fb) - return; - - plane->old_fb = plane->fb; - ret = plane->funcs->disable_plane(plane); - if (ret) { - DRM_ERROR("failed to disable plane with busy fb\n"); - plane->old_fb = NULL; - return; - } - /* disconnect the plane from the fb and crtc: */ - drm_framebuffer_unreference(plane->old_fb); - plane->old_fb = NULL; - plane->fb = NULL; - plane->crtc = NULL; -} -EXPORT_SYMBOL(drm_plane_force_disable); - int drm_modeset_register_all(struct drm_device *dev) { int ret; @@ -1540,39 +298,11 @@ void drm_modeset_unregister_all(struct drm_device *dev) static int drm_mode_create_standard_properties(struct drm_device *dev) { struct drm_property *prop; + int ret; - /* - * Standard properties (apply to all connectors) - */ - prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | - DRM_MODE_PROP_IMMUTABLE, - "EDID", 0); - if (!prop) - return -ENOMEM; - dev->mode_config.edid_property = prop; - - prop = drm_property_create_enum(dev, 0, - "DPMS", drm_dpms_enum_list, - ARRAY_SIZE(drm_dpms_enum_list)); - if (!prop) - return -ENOMEM; - dev->mode_config.dpms_property = prop; - - prop = drm_property_create(dev, - DRM_MODE_PROP_BLOB | - DRM_MODE_PROP_IMMUTABLE, - "PATH", 0); - if (!prop) - return -ENOMEM; - dev->mode_config.path_property = prop; - - prop = drm_property_create(dev, - DRM_MODE_PROP_BLOB | - DRM_MODE_PROP_IMMUTABLE, - "TILE", 0); - if (!prop) - return -ENOMEM; - dev->mode_config.tile_property = prop; + ret = drm_connector_create_standard_properties(dev); + if (ret) + return ret; prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, "type", drm_plane_type_enum_list, @@ -1693,250 +423,6 @@ static int drm_mode_create_standard_properties(struct drm_device *dev) } /** - * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties - * @dev: DRM device - * - * Called by a driver the first time a DVI-I connector is made. - */ -int drm_mode_create_dvi_i_properties(struct drm_device *dev) -{ - struct drm_property *dvi_i_selector; - struct drm_property *dvi_i_subconnector; - - if (dev->mode_config.dvi_i_select_subconnector_property) - return 0; - - dvi_i_selector = - drm_property_create_enum(dev, 0, - "select subconnector", - drm_dvi_i_select_enum_list, - ARRAY_SIZE(drm_dvi_i_select_enum_list)); - dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector; - - dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, - "subconnector", - drm_dvi_i_subconnector_enum_list, - ARRAY_SIZE(drm_dvi_i_subconnector_enum_list)); - dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector; - - return 0; -} -EXPORT_SYMBOL(drm_mode_create_dvi_i_properties); - -/** - * drm_create_tv_properties - create TV specific connector properties - * @dev: DRM device - * @num_modes: number of different TV formats (modes) supported - * @modes: array of pointers to strings containing name of each format - * - * Called by a driver's TV initialization routine, this function creates - * the TV specific connector properties for a given device. Caller is - * responsible for allocating a list of format names and passing them to - * this routine. - */ -int drm_mode_create_tv_properties(struct drm_device *dev, - unsigned int num_modes, - const char * const modes[]) -{ - struct drm_property *tv_selector; - struct drm_property *tv_subconnector; - unsigned int i; - - if (dev->mode_config.tv_select_subconnector_property) - return 0; - - /* - * Basic connector properties - */ - tv_selector = drm_property_create_enum(dev, 0, - "select subconnector", - drm_tv_select_enum_list, - ARRAY_SIZE(drm_tv_select_enum_list)); - if (!tv_selector) - goto nomem; - - dev->mode_config.tv_select_subconnector_property = tv_selector; - - tv_subconnector = - drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, - "subconnector", - drm_tv_subconnector_enum_list, - ARRAY_SIZE(drm_tv_subconnector_enum_list)); - if (!tv_subconnector) - goto nomem; - dev->mode_config.tv_subconnector_property = tv_subconnector; - - /* - * Other, TV specific properties: margins & TV modes. - */ - dev->mode_config.tv_left_margin_property = - drm_property_create_range(dev, 0, "left margin", 0, 100); - if (!dev->mode_config.tv_left_margin_property) - goto nomem; - - dev->mode_config.tv_right_margin_property = - drm_property_create_range(dev, 0, "right margin", 0, 100); - if (!dev->mode_config.tv_right_margin_property) - goto nomem; - - dev->mode_config.tv_top_margin_property = - drm_property_create_range(dev, 0, "top margin", 0, 100); - if (!dev->mode_config.tv_top_margin_property) - goto nomem; - - dev->mode_config.tv_bottom_margin_property = - drm_property_create_range(dev, 0, "bottom margin", 0, 100); - if (!dev->mode_config.tv_bottom_margin_property) - goto nomem; - - dev->mode_config.tv_mode_property = - drm_property_create(dev, DRM_MODE_PROP_ENUM, - "mode", num_modes); - if (!dev->mode_config.tv_mode_property) - goto nomem; - - for (i = 0; i < num_modes; i++) - drm_property_add_enum(dev->mode_config.tv_mode_property, i, - i, modes[i]); - - dev->mode_config.tv_brightness_property = - drm_property_create_range(dev, 0, "brightness", 0, 100); - if (!dev->mode_config.tv_brightness_property) - goto nomem; - - dev->mode_config.tv_contrast_property = - drm_property_create_range(dev, 0, "contrast", 0, 100); - if (!dev->mode_config.tv_contrast_property) - goto nomem; - - dev->mode_config.tv_flicker_reduction_property = - drm_property_create_range(dev, 0, "flicker reduction", 0, 100); - if (!dev->mode_config.tv_flicker_reduction_property) - goto nomem; - - dev->mode_config.tv_overscan_property = - drm_property_create_range(dev, 0, "overscan", 0, 100); - if (!dev->mode_config.tv_overscan_property) - goto nomem; - - dev->mode_config.tv_saturation_property = - drm_property_create_range(dev, 0, "saturation", 0, 100); - if (!dev->mode_config.tv_saturation_property) - goto nomem; - - dev->mode_config.tv_hue_property = - drm_property_create_range(dev, 0, "hue", 0, 100); - if (!dev->mode_config.tv_hue_property) - goto nomem; - - return 0; -nomem: - return -ENOMEM; -} -EXPORT_SYMBOL(drm_mode_create_tv_properties); - -/** - * drm_mode_create_scaling_mode_property - create scaling mode property - * @dev: DRM device - * - * Called by a driver the first time it's needed, must be attached to desired - * connectors. - */ -int drm_mode_create_scaling_mode_property(struct drm_device *dev) -{ - struct drm_property *scaling_mode; - - if (dev->mode_config.scaling_mode_property) - return 0; - - scaling_mode = - drm_property_create_enum(dev, 0, "scaling mode", - drm_scaling_mode_enum_list, - ARRAY_SIZE(drm_scaling_mode_enum_list)); - - dev->mode_config.scaling_mode_property = scaling_mode; - - return 0; -} -EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); - -/** - * drm_mode_create_aspect_ratio_property - create aspect ratio property - * @dev: DRM device - * - * Called by a driver the first time it's needed, must be attached to desired - * connectors. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_create_aspect_ratio_property(struct drm_device *dev) -{ - if (dev->mode_config.aspect_ratio_property) - return 0; - - dev->mode_config.aspect_ratio_property = - drm_property_create_enum(dev, 0, "aspect ratio", - drm_aspect_ratio_enum_list, - ARRAY_SIZE(drm_aspect_ratio_enum_list)); - - if (dev->mode_config.aspect_ratio_property == NULL) - return -ENOMEM; - - return 0; -} -EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property); - -/** - * drm_mode_create_dirty_property - create dirty property - * @dev: DRM device - * - * Called by a driver the first time it's needed, must be attached to desired - * connectors. - */ -int drm_mode_create_dirty_info_property(struct drm_device *dev) -{ - struct drm_property *dirty_info; - - if (dev->mode_config.dirty_info_property) - return 0; - - dirty_info = - drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, - "dirty", - drm_dirty_info_enum_list, - ARRAY_SIZE(drm_dirty_info_enum_list)); - dev->mode_config.dirty_info_property = dirty_info; - - return 0; -} -EXPORT_SYMBOL(drm_mode_create_dirty_info_property); - -/** - * drm_mode_create_suggested_offset_properties - create suggests offset properties - * @dev: DRM device - * - * Create the the suggested x/y offset property for connectors. - */ -int drm_mode_create_suggested_offset_properties(struct drm_device *dev) -{ - if (dev->mode_config.suggested_x_property && dev->mode_config.suggested_y_property) - return 0; - - dev->mode_config.suggested_x_property = - drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested X", 0, 0xffffffff); - - dev->mode_config.suggested_y_property = - drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested Y", 0, 0xffffffff); - - if (dev->mode_config.suggested_x_property == NULL || - dev->mode_config.suggested_y_property == NULL) - return -ENOMEM; - return 0; -} -EXPORT_SYMBOL(drm_mode_create_suggested_offset_properties); - -/** * drm_mode_getresources - get graphics configuration * @dev: drm device for the ioctl * @data: data pointer for the ioctl @@ -2123,599 +609,6 @@ int drm_mode_getcrtc(struct drm_device *dev, return 0; } -static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode, - const struct drm_file *file_priv) -{ - /* - * If user-space hasn't configured the driver to expose the stereo 3D - * modes, don't expose them. - */ - if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode)) - return false; - - return true; -} - -static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector) -{ - /* For atomic drivers only state objects are synchronously updated and - * protected by modeset locks, so check those first. */ - if (connector->state) - return connector->state->best_encoder; - return connector->encoder; -} - -/* helper for getconnector and getproperties ioctls */ -static int get_properties(struct drm_mode_object *obj, bool atomic, - uint32_t __user *prop_ptr, uint64_t __user *prop_values, - uint32_t *arg_count_props) -{ - int props_count; - int i, ret, copied; - - props_count = obj->properties->count; - if (!atomic) - props_count -= obj->properties->atomic_count; - - if ((*arg_count_props >= props_count) && props_count) { - for (i = 0, copied = 0; copied < props_count; i++) { - struct drm_property *prop = obj->properties->properties[i]; - uint64_t val; - - if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic) - continue; - - ret = drm_object_property_get_value(obj, prop, &val); - if (ret) - return ret; - - if (put_user(prop->base.id, prop_ptr + copied)) - return -EFAULT; - - if (put_user(val, prop_values + copied)) - return -EFAULT; - - copied++; - } - } - *arg_count_props = props_count; - - return 0; -} - -/** - * drm_mode_getconnector - get connector configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Construct a connector configuration structure to return to the user. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getconnector(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_get_connector *out_resp = data; - struct drm_connector *connector; - struct drm_encoder *encoder; - struct drm_display_mode *mode; - int mode_count = 0; - int encoders_count = 0; - int ret = 0; - int copied = 0; - int i; - struct drm_mode_modeinfo u_mode; - struct drm_mode_modeinfo __user *mode_ptr; - uint32_t __user *encoder_ptr; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); - - mutex_lock(&dev->mode_config.mutex); - - connector = drm_connector_lookup(dev, out_resp->connector_id); - if (!connector) { - ret = -ENOENT; - goto out_unlock; - } - - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) - if (connector->encoder_ids[i] != 0) - encoders_count++; - - if (out_resp->count_modes == 0) { - connector->funcs->fill_modes(connector, - dev->mode_config.max_width, - dev->mode_config.max_height); - } - - /* 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, file_priv)) - mode_count++; - - out_resp->connector_id = connector->base.id; - out_resp->connector_type = connector->connector_type; - out_resp->connector_type_id = connector->connector_type_id; - out_resp->mm_width = connector->display_info.width_mm; - out_resp->mm_height = connector->display_info.height_mm; - out_resp->subpixel = connector->display_info.subpixel_order; - out_resp->connection = connector->status; - - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - encoder = drm_connector_get_encoder(connector); - if (encoder) - out_resp->encoder_id = encoder->base.id; - else - out_resp->encoder_id = 0; - - /* - * This ioctl is called twice, once to determine how much space is - * needed, and the 2nd time to fill it. - */ - 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, &connector->modes, head) { - if (!drm_mode_expose_to_userspace(mode, file_priv)) - continue; - - drm_mode_convert_to_umode(&u_mode, mode); - if (copy_to_user(mode_ptr + copied, - &u_mode, sizeof(u_mode))) { - ret = -EFAULT; - goto out; - } - copied++; - } - } - out_resp->count_modes = mode_count; - - ret = get_properties(&connector->base, file_priv->atomic, - (uint32_t __user *)(unsigned long)(out_resp->props_ptr), - (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr), - &out_resp->count_props); - if (ret) - goto out; - - if ((out_resp->count_encoders >= encoders_count) && encoders_count) { - copied = 0; - encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr); - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] != 0) { - if (put_user(connector->encoder_ids[i], - encoder_ptr + copied)) { - ret = -EFAULT; - goto out; - } - copied++; - } - } - } - out_resp->count_encoders = encoders_count; - -out: - drm_modeset_unlock(&dev->mode_config.connection_mutex); - - drm_connector_unreference(connector); -out_unlock: - mutex_unlock(&dev->mode_config.mutex); - - return ret; -} - -static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder) -{ - struct drm_connector *connector; - struct drm_device *dev = encoder->dev; - bool uses_atomic = false; - - /* For atomic drivers only state objects are synchronously updated and - * protected by modeset locks, so check those first. */ - drm_for_each_connector(connector, dev) { - if (!connector->state) - continue; - - uses_atomic = true; - - if (connector->state->best_encoder != encoder) - continue; - - return connector->state->crtc; - } - - /* Don't return stale data (e.g. pending async disable). */ - if (uses_atomic) - return NULL; - - return encoder->crtc; -} - -/** - * drm_mode_getencoder - get encoder configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Construct a encoder configuration structure to return to the user. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getencoder(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_get_encoder *enc_resp = data; - struct drm_encoder *encoder; - struct drm_crtc *crtc; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - encoder = drm_encoder_find(dev, enc_resp->encoder_id); - if (!encoder) - return -ENOENT; - - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - crtc = drm_encoder_get_crtc(encoder); - if (crtc) - enc_resp->crtc_id = crtc->base.id; - else - enc_resp->crtc_id = 0; - drm_modeset_unlock(&dev->mode_config.connection_mutex); - - enc_resp->encoder_type = encoder->encoder_type; - enc_resp->encoder_id = encoder->base.id; - enc_resp->possible_crtcs = encoder->possible_crtcs; - enc_resp->possible_clones = encoder->possible_clones; - - return 0; -} - -/** - * drm_mode_getplane_res - enumerate all plane resources - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * Construct a list of plane ids to return to the user. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getplane_res(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_get_plane_res *plane_resp = data; - struct drm_mode_config *config; - struct drm_plane *plane; - uint32_t __user *plane_ptr; - int copied = 0; - unsigned num_planes; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - config = &dev->mode_config; - - if (file_priv->universal_planes) - num_planes = config->num_total_plane; - else - num_planes = config->num_overlay_plane; - - /* - * This ioctl is called twice, once to determine how much space is - * needed, and the 2nd time to fill it. - */ - if (num_planes && - (plane_resp->count_planes >= num_planes)) { - plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; - - /* Plane lists are invariant, no locking needed. */ - drm_for_each_plane(plane, dev) { - /* - * Unless userspace set the 'universal planes' - * capability bit, only advertise overlays. - */ - if (plane->type != DRM_PLANE_TYPE_OVERLAY && - !file_priv->universal_planes) - continue; - - if (put_user(plane->base.id, plane_ptr + copied)) - return -EFAULT; - copied++; - } - } - plane_resp->count_planes = num_planes; - - return 0; -} - -/** - * drm_mode_getplane - get plane configuration - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * Construct a plane configuration structure to return to the user. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getplane(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_get_plane *plane_resp = data; - struct drm_plane *plane; - uint32_t __user *format_ptr; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - plane = drm_plane_find(dev, plane_resp->plane_id); - if (!plane) - return -ENOENT; - - drm_modeset_lock(&plane->mutex, NULL); - if (plane->crtc) - plane_resp->crtc_id = plane->crtc->base.id; - else - plane_resp->crtc_id = 0; - - if (plane->fb) - plane_resp->fb_id = plane->fb->base.id; - else - plane_resp->fb_id = 0; - drm_modeset_unlock(&plane->mutex); - - plane_resp->plane_id = plane->base.id; - plane_resp->possible_crtcs = plane->possible_crtcs; - plane_resp->gamma_size = 0; - - /* - * This ioctl is called twice, once to determine how much space is - * needed, and the 2nd time to fill it. - */ - if (plane->format_count && - (plane_resp->count_format_types >= plane->format_count)) { - format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr; - if (copy_to_user(format_ptr, - plane->format_types, - sizeof(uint32_t) * plane->format_count)) { - return -EFAULT; - } - } - plane_resp->count_format_types = plane->format_count; - - return 0; -} - -/** - * drm_plane_check_pixel_format - Check if the plane supports the pixel format - * @plane: plane to check for format support - * @format: the pixel format - * - * Returns: - * Zero of @plane has @format in its list of supported pixel formats, -EINVAL - * otherwise. - */ -int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format) -{ - unsigned int i; - - for (i = 0; i < plane->format_count; i++) { - if (format == plane->format_types[i]) - return 0; - } - - return -EINVAL; -} - -static int check_src_coords(uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h, - const struct drm_framebuffer *fb) -{ - unsigned int fb_width, fb_height; - - fb_width = fb->width << 16; - fb_height = fb->height << 16; - - /* Make sure source coordinates are inside the fb. */ - if (src_w > fb_width || - src_x > fb_width - src_w || - src_h > fb_height || - src_y > fb_height - src_h) { - DRM_DEBUG_KMS("Invalid source coordinates " - "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", - src_w >> 16, ((src_w & 0xffff) * 15625) >> 10, - src_h >> 16, ((src_h & 0xffff) * 15625) >> 10, - src_x >> 16, ((src_x & 0xffff) * 15625) >> 10, - src_y >> 16, ((src_y & 0xffff) * 15625) >> 10); - return -ENOSPC; - } - - return 0; -} - -/* - * setplane_internal - setplane handler for internal callers - * - * Note that we assume an extra reference has already been taken on fb. If the - * update fails, this reference will be dropped before return; if it succeeds, - * the previous framebuffer (if any) will be unreferenced instead. - * - * src_{x,y,w,h} are provided in 16.16 fixed point format - */ -static int __setplane_internal(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int32_t crtc_x, int32_t crtc_y, - uint32_t crtc_w, uint32_t crtc_h, - /* src_{x,y,w,h} values are 16.16 fixed point */ - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) -{ - int ret = 0; - - /* No fb means shut it down */ - if (!fb) { - plane->old_fb = plane->fb; - ret = plane->funcs->disable_plane(plane); - if (!ret) { - plane->crtc = NULL; - plane->fb = NULL; - } else { - plane->old_fb = NULL; - } - goto out; - } - - /* Check whether this plane is usable on this CRTC */ - if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) { - DRM_DEBUG_KMS("Invalid crtc for plane\n"); - ret = -EINVAL; - goto out; - } - - /* Check whether this plane supports the fb pixel format. */ - ret = drm_plane_check_pixel_format(plane, fb->pixel_format); - if (ret) { - DRM_DEBUG_KMS("Invalid pixel format %s\n", - drm_get_format_name(fb->pixel_format)); - goto out; - } - - /* Give drivers some help against integer overflows */ - if (crtc_w > INT_MAX || - crtc_x > INT_MAX - (int32_t) crtc_w || - crtc_h > INT_MAX || - crtc_y > INT_MAX - (int32_t) crtc_h) { - DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", - crtc_w, crtc_h, crtc_x, crtc_y); - ret = -ERANGE; - goto out; - } - - ret = check_src_coords(src_x, src_y, src_w, src_h, fb); - if (ret) - goto out; - - plane->old_fb = plane->fb; - ret = plane->funcs->update_plane(plane, crtc, fb, - crtc_x, crtc_y, crtc_w, crtc_h, - src_x, src_y, src_w, src_h); - if (!ret) { - plane->crtc = crtc; - plane->fb = fb; - fb = NULL; - } else { - plane->old_fb = NULL; - } - -out: - if (fb) - drm_framebuffer_unreference(fb); - if (plane->old_fb) - drm_framebuffer_unreference(plane->old_fb); - plane->old_fb = NULL; - - return ret; -} - -static int setplane_internal(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int32_t crtc_x, int32_t crtc_y, - uint32_t crtc_w, uint32_t crtc_h, - /* src_{x,y,w,h} values are 16.16 fixed point */ - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) -{ - int ret; - - drm_modeset_lock_all(plane->dev); - ret = __setplane_internal(plane, crtc, fb, - crtc_x, crtc_y, crtc_w, crtc_h, - src_x, src_y, src_w, src_h); - drm_modeset_unlock_all(plane->dev); - - return ret; -} - -/** - * drm_mode_setplane - configure a plane's configuration - * @dev: DRM device - * @data: ioctl data* - * @file_priv: DRM file info - * - * Set plane configuration, including placement, fb, scaling, and other factors. - * Or pass a NULL fb to disable (planes may be disabled without providing a - * valid crtc). - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_setplane(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_set_plane *plane_req = data; - struct drm_plane *plane; - struct drm_crtc *crtc = NULL; - struct drm_framebuffer *fb = NULL; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - /* - * First, find the plane, crtc, and fb objects. If not available, - * we don't bother to call the driver. - */ - plane = drm_plane_find(dev, plane_req->plane_id); - if (!plane) { - DRM_DEBUG_KMS("Unknown plane ID %d\n", - plane_req->plane_id); - return -ENOENT; - } - - if (plane_req->fb_id) { - fb = drm_framebuffer_lookup(dev, plane_req->fb_id); - if (!fb) { - DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", - plane_req->fb_id); - return -ENOENT; - } - - crtc = drm_crtc_find(dev, plane_req->crtc_id); - if (!crtc) { - DRM_DEBUG_KMS("Unknown crtc ID %d\n", - plane_req->crtc_id); - return -ENOENT; - } - } - - /* - * setplane_internal will take care of deref'ing either the old or new - * framebuffer depending on success. - */ - return setplane_internal(plane, crtc, fb, - plane_req->crtc_x, plane_req->crtc_y, - plane_req->crtc_w, plane_req->crtc_h, - plane_req->src_x, plane_req->src_y, - plane_req->src_w, plane_req->src_h); -} - /** * drm_mode_set_config_internal - helper to call ->set_config * @set: modeset config to set @@ -2802,12 +695,13 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc, drm_crtc_get_hv_timing(mode, &hdisplay, &vdisplay); if (crtc->state && - crtc->primary->state->rotation & (BIT(DRM_ROTATE_90) | - BIT(DRM_ROTATE_270))) + crtc->primary->state->rotation & (DRM_ROTATE_90 | + DRM_ROTATE_270)) swap(hdisplay, vdisplay); - return check_src_coords(x << 16, y << 16, - hdisplay << 16, vdisplay << 16, fb); + return drm_framebuffer_check_src_coords(x << 16, y << 16, + hdisplay << 16, vdisplay << 16, + fb); } EXPORT_SYMBOL(drm_crtc_check_viewport); @@ -2902,8 +796,9 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, ret = drm_plane_check_pixel_format(crtc->primary, fb->pixel_format); if (ret) { - DRM_DEBUG_KMS("Invalid pixel format %s\n", - drm_get_format_name(fb->pixel_format)); + char *format_name = drm_get_format_name(fb->pixel_format); + DRM_DEBUG_KMS("Invalid pixel format %s\n", format_name); + kfree(format_name); goto out; } } @@ -2993,2004 +888,9 @@ out: return ret; } -/** - * drm_mode_cursor_universal - translate legacy cursor ioctl call into a - * universal plane handler call - * @crtc: crtc to update cursor for - * @req: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Legacy cursor ioctl's work directly with driver buffer handles. To - * translate legacy ioctl calls into universal plane handler calls, we need to - * wrap the native buffer handle in a drm_framebuffer. - * - * Note that we assume any handle passed to the legacy ioctls was a 32-bit ARGB - * buffer with a pitch of 4*width; the universal plane interface should be used - * directly in cases where the hardware can support other buffer settings and - * userspace wants to make use of these capabilities. - * - * Returns: - * Zero on success, negative errno on failure. - */ -static int drm_mode_cursor_universal(struct drm_crtc *crtc, - struct drm_mode_cursor2 *req, - struct drm_file *file_priv) -{ - struct drm_device *dev = crtc->dev; - struct drm_framebuffer *fb = NULL; - struct drm_mode_fb_cmd2 fbreq = { - .width = req->width, - .height = req->height, - .pixel_format = DRM_FORMAT_ARGB8888, - .pitches = { req->width * 4 }, - .handles = { req->handle }, - }; - int32_t crtc_x, crtc_y; - uint32_t crtc_w = 0, crtc_h = 0; - uint32_t src_w = 0, src_h = 0; - int ret = 0; - - BUG_ON(!crtc->cursor); - WARN_ON(crtc->cursor->crtc != crtc && crtc->cursor->crtc != NULL); - - /* - * Obtain fb we'll be using (either new or existing) and take an extra - * reference to it if fb != null. setplane will take care of dropping - * the reference if the plane update fails. - */ - if (req->flags & DRM_MODE_CURSOR_BO) { - if (req->handle) { - fb = internal_framebuffer_create(dev, &fbreq, file_priv); - if (IS_ERR(fb)) { - DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n"); - return PTR_ERR(fb); - } - fb->hot_x = req->hot_x; - fb->hot_y = req->hot_y; - } else { - fb = NULL; - } - } else { - fb = crtc->cursor->fb; - if (fb) - drm_framebuffer_reference(fb); - } - - if (req->flags & DRM_MODE_CURSOR_MOVE) { - crtc_x = req->x; - crtc_y = req->y; - } else { - crtc_x = crtc->cursor_x; - crtc_y = crtc->cursor_y; - } - - if (fb) { - crtc_w = fb->width; - crtc_h = fb->height; - src_w = fb->width << 16; - src_h = fb->height << 16; - } - - /* - * setplane_internal will take care of deref'ing either the old or new - * framebuffer depending on success. - */ - ret = __setplane_internal(crtc->cursor, crtc, fb, - crtc_x, crtc_y, crtc_w, crtc_h, - 0, 0, src_w, src_h); - - /* Update successful; save new cursor position, if necessary */ - if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) { - crtc->cursor_x = req->x; - crtc->cursor_y = req->y; - } - - return ret; -} - -static int drm_mode_cursor_common(struct drm_device *dev, - struct drm_mode_cursor2 *req, - struct drm_file *file_priv) -{ - struct drm_crtc *crtc; - int ret = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) - return -EINVAL; - - crtc = drm_crtc_find(dev, req->crtc_id); - if (!crtc) { - DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); - return -ENOENT; - } - - /* - * If this crtc has a universal cursor plane, call that plane's update - * handler rather than using legacy cursor handlers. - */ - drm_modeset_lock_crtc(crtc, crtc->cursor); - if (crtc->cursor) { - ret = drm_mode_cursor_universal(crtc, req, file_priv); - goto out; - } - - if (req->flags & DRM_MODE_CURSOR_BO) { - if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { - ret = -ENXIO; - goto out; - } - /* Turns off the cursor if handle is 0 */ - if (crtc->funcs->cursor_set2) - ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle, - req->width, req->height, req->hot_x, req->hot_y); - else - ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, - req->width, req->height); - } - - if (req->flags & DRM_MODE_CURSOR_MOVE) { - if (crtc->funcs->cursor_move) { - ret = crtc->funcs->cursor_move(crtc, req->x, req->y); - } else { - ret = -EFAULT; - goto out; - } - } -out: - drm_modeset_unlock_crtc(crtc); - - return ret; - -} - - -/** - * drm_mode_cursor_ioctl - set CRTC's cursor configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Set the cursor configuration based on user request. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_cursor_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_cursor *req = data; - struct drm_mode_cursor2 new_req; - - memcpy(&new_req, req, sizeof(struct drm_mode_cursor)); - new_req.hot_x = new_req.hot_y = 0; - - return drm_mode_cursor_common(dev, &new_req, file_priv); -} - -/** - * drm_mode_cursor2_ioctl - set CRTC's cursor configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Set the cursor configuration based on user request. This implements the 2nd - * version of the cursor ioctl, which allows userspace to additionally specify - * the hotspot of the pointer. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_cursor2_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_cursor2 *req = data; - - return drm_mode_cursor_common(dev, req, file_priv); -} - -/** - * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description - * @bpp: bits per pixels - * @depth: bit depth per pixel - * - * Computes a drm fourcc pixel format code for the given @bpp/@depth values. - * Useful in fbdev emulation code, since that deals in those values. - */ -uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) -{ - uint32_t fmt; - - switch (bpp) { - case 8: - fmt = DRM_FORMAT_C8; - break; - case 16: - if (depth == 15) - fmt = DRM_FORMAT_XRGB1555; - else - fmt = DRM_FORMAT_RGB565; - break; - case 24: - fmt = DRM_FORMAT_RGB888; - break; - case 32: - if (depth == 24) - fmt = DRM_FORMAT_XRGB8888; - else if (depth == 30) - fmt = DRM_FORMAT_XRGB2101010; - else - fmt = DRM_FORMAT_ARGB8888; - break; - default: - DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n"); - fmt = DRM_FORMAT_XRGB8888; - break; - } - - return fmt; -} -EXPORT_SYMBOL(drm_mode_legacy_fb_format); - -/** - * drm_mode_addfb - add an FB to the graphics configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Add a new FB to the specified CRTC, given a user request. This is the - * original addfb ioctl which only supported RGB formats. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_addfb(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_fb_cmd *or = data; - struct drm_mode_fb_cmd2 r = {}; - int ret; - - /* convert to new format and call new ioctl */ - r.fb_id = or->fb_id; - r.width = or->width; - r.height = or->height; - r.pitches[0] = or->pitch; - r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); - r.handles[0] = or->handle; - - ret = drm_mode_addfb2(dev, &r, file_priv); - if (ret) - return ret; - - or->fb_id = r.fb_id; - - return 0; -} - -static int format_check(const struct drm_mode_fb_cmd2 *r) -{ - uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN; - - switch (format) { - case DRM_FORMAT_C8: - case DRM_FORMAT_RGB332: - case DRM_FORMAT_BGR233: - case DRM_FORMAT_XRGB4444: - case DRM_FORMAT_XBGR4444: - case DRM_FORMAT_RGBX4444: - case DRM_FORMAT_BGRX4444: - case DRM_FORMAT_ARGB4444: - case DRM_FORMAT_ABGR4444: - case DRM_FORMAT_RGBA4444: - case DRM_FORMAT_BGRA4444: - case DRM_FORMAT_XRGB1555: - case DRM_FORMAT_XBGR1555: - case DRM_FORMAT_RGBX5551: - case DRM_FORMAT_BGRX5551: - case DRM_FORMAT_ARGB1555: - case DRM_FORMAT_ABGR1555: - case DRM_FORMAT_RGBA5551: - case DRM_FORMAT_BGRA5551: - case DRM_FORMAT_RGB565: - case DRM_FORMAT_BGR565: - case DRM_FORMAT_RGB888: - case DRM_FORMAT_BGR888: - case DRM_FORMAT_XRGB8888: - case DRM_FORMAT_XBGR8888: - case DRM_FORMAT_RGBX8888: - case DRM_FORMAT_BGRX8888: - case DRM_FORMAT_ARGB8888: - case DRM_FORMAT_ABGR8888: - case DRM_FORMAT_RGBA8888: - case DRM_FORMAT_BGRA8888: - case DRM_FORMAT_XRGB2101010: - case DRM_FORMAT_XBGR2101010: - case DRM_FORMAT_RGBX1010102: - case DRM_FORMAT_BGRX1010102: - case DRM_FORMAT_ARGB2101010: - case DRM_FORMAT_ABGR2101010: - case DRM_FORMAT_RGBA1010102: - case DRM_FORMAT_BGRA1010102: - case DRM_FORMAT_YUYV: - case DRM_FORMAT_YVYU: - case DRM_FORMAT_UYVY: - case DRM_FORMAT_VYUY: - case DRM_FORMAT_AYUV: - case DRM_FORMAT_NV12: - case DRM_FORMAT_NV21: - case DRM_FORMAT_NV16: - case DRM_FORMAT_NV61: - case DRM_FORMAT_NV24: - case DRM_FORMAT_NV42: - case DRM_FORMAT_YUV410: - case DRM_FORMAT_YVU410: - case DRM_FORMAT_YUV411: - case DRM_FORMAT_YVU411: - case DRM_FORMAT_YUV420: - case DRM_FORMAT_YVU420: - case DRM_FORMAT_YUV422: - case DRM_FORMAT_YVU422: - case DRM_FORMAT_YUV444: - case DRM_FORMAT_YVU444: - return 0; - default: - DRM_DEBUG_KMS("invalid pixel format %s\n", - drm_get_format_name(r->pixel_format)); - return -EINVAL; - } -} - -static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) -{ - int ret, hsub, vsub, num_planes, i; - - ret = format_check(r); - if (ret) { - DRM_DEBUG_KMS("bad framebuffer format %s\n", - drm_get_format_name(r->pixel_format)); - return ret; - } - - hsub = drm_format_horz_chroma_subsampling(r->pixel_format); - vsub = drm_format_vert_chroma_subsampling(r->pixel_format); - num_planes = drm_format_num_planes(r->pixel_format); - - if (r->width == 0 || r->width % hsub) { - DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width); - return -EINVAL; - } - - if (r->height == 0 || r->height % vsub) { - DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height); - return -EINVAL; - } - - for (i = 0; i < num_planes; i++) { - unsigned int width = r->width / (i != 0 ? hsub : 1); - unsigned int height = r->height / (i != 0 ? vsub : 1); - unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i); - - if (!r->handles[i]) { - DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i); - return -EINVAL; - } - - if ((uint64_t) width * cpp > UINT_MAX) - return -ERANGE; - - if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) - return -ERANGE; - - if (r->pitches[i] < width * cpp) { - DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); - return -EINVAL; - } - - if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) { - DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n", - r->modifier[i], i); - return -EINVAL; - } - - /* modifier specific checks: */ - switch (r->modifier[i]) { - case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE: - /* NOTE: the pitch restriction may be lifted later if it turns - * out that no hw has this restriction: - */ - if (r->pixel_format != DRM_FORMAT_NV12 || - width % 128 || height % 32 || - r->pitches[i] % 128) { - DRM_DEBUG_KMS("bad modifier data for plane %d\n", i); - return -EINVAL; - } - break; - - default: - break; - } - } - - for (i = num_planes; i < 4; i++) { - if (r->modifier[i]) { - DRM_DEBUG_KMS("non-zero modifier for unused plane %d\n", i); - return -EINVAL; - } - - /* Pre-FB_MODIFIERS userspace didn't clear the structs properly. */ - if (!(r->flags & DRM_MODE_FB_MODIFIERS)) - continue; - - if (r->handles[i]) { - DRM_DEBUG_KMS("buffer object handle for unused plane %d\n", i); - return -EINVAL; - } - - if (r->pitches[i]) { - DRM_DEBUG_KMS("non-zero pitch for unused plane %d\n", i); - return -EINVAL; - } - - if (r->offsets[i]) { - DRM_DEBUG_KMS("non-zero offset for unused plane %d\n", i); - return -EINVAL; - } - } - - return 0; -} - -static struct drm_framebuffer * -internal_framebuffer_create(struct drm_device *dev, - const struct drm_mode_fb_cmd2 *r, - struct drm_file *file_priv) -{ - struct drm_mode_config *config = &dev->mode_config; - struct drm_framebuffer *fb; - int ret; - - if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) { - DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags); - return ERR_PTR(-EINVAL); - } - - if ((config->min_width > r->width) || (r->width > config->max_width)) { - DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n", - r->width, config->min_width, config->max_width); - return ERR_PTR(-EINVAL); - } - if ((config->min_height > r->height) || (r->height > config->max_height)) { - DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n", - r->height, config->min_height, config->max_height); - return ERR_PTR(-EINVAL); - } - - if (r->flags & DRM_MODE_FB_MODIFIERS && - !dev->mode_config.allow_fb_modifiers) { - DRM_DEBUG_KMS("driver does not support fb modifiers\n"); - return ERR_PTR(-EINVAL); - } - - ret = framebuffer_check(r); - if (ret) - return ERR_PTR(ret); - - fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); - if (IS_ERR(fb)) { - DRM_DEBUG_KMS("could not create framebuffer\n"); - return fb; - } - - return fb; -} - -/** - * drm_mode_addfb2 - add an FB to the graphics configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Add a new FB to the specified CRTC, given a user request with format. This is - * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers - * and uses fourcc codes as pixel format specifiers. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_addfb2(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_fb_cmd2 *r = data; - struct drm_framebuffer *fb; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - fb = internal_framebuffer_create(dev, r, file_priv); - if (IS_ERR(fb)) - return PTR_ERR(fb); - - DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); - r->fb_id = fb->base.id; - - /* Transfer ownership to the filp for reaping on close */ - mutex_lock(&file_priv->fbs_lock); - list_add(&fb->filp_head, &file_priv->fbs); - mutex_unlock(&file_priv->fbs_lock); - - return 0; -} - -struct drm_mode_rmfb_work { - struct work_struct work; - struct list_head fbs; -}; - -static void drm_mode_rmfb_work_fn(struct work_struct *w) -{ - struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work); - - while (!list_empty(&arg->fbs)) { - struct drm_framebuffer *fb = - list_first_entry(&arg->fbs, typeof(*fb), filp_head); - - list_del_init(&fb->filp_head); - drm_framebuffer_remove(fb); - } -} - -/** - * drm_mode_rmfb - remove an FB from the configuration - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Remove the FB specified by the user. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_rmfb(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_framebuffer *fb = NULL; - struct drm_framebuffer *fbl = NULL; - uint32_t *id = data; - int found = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - fb = drm_framebuffer_lookup(dev, *id); - if (!fb) - return -ENOENT; - - mutex_lock(&file_priv->fbs_lock); - list_for_each_entry(fbl, &file_priv->fbs, filp_head) - if (fb == fbl) - found = 1; - if (!found) { - mutex_unlock(&file_priv->fbs_lock); - goto fail_unref; - } - - list_del_init(&fb->filp_head); - mutex_unlock(&file_priv->fbs_lock); - - /* drop the reference we picked up in framebuffer lookup */ - drm_framebuffer_unreference(fb); - - /* - * we now own the reference that was stored in the fbs list - * - * drm_framebuffer_remove may fail with -EINTR on pending signals, - * so run this in a separate stack as there's no way to correctly - * handle this after the fb is already removed from the lookup table. - */ - if (drm_framebuffer_read_refcount(fb) > 1) { - struct drm_mode_rmfb_work arg; - - INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn); - INIT_LIST_HEAD(&arg.fbs); - list_add_tail(&fb->filp_head, &arg.fbs); - - schedule_work(&arg.work); - flush_work(&arg.work); - destroy_work_on_stack(&arg.work); - } else - drm_framebuffer_unreference(fb); - - return 0; - -fail_unref: - drm_framebuffer_unreference(fb); - return -ENOENT; -} - -/** - * drm_mode_getfb - get FB info - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Lookup the FB given its ID and return info about it. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getfb(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_fb_cmd *r = data; - struct drm_framebuffer *fb; - int ret; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - fb = drm_framebuffer_lookup(dev, r->fb_id); - if (!fb) - return -ENOENT; - - r->height = fb->height; - r->width = fb->width; - r->depth = fb->depth; - r->bpp = fb->bits_per_pixel; - r->pitch = fb->pitches[0]; - if (fb->funcs->create_handle) { - if (drm_is_current_master(file_priv) || capable(CAP_SYS_ADMIN) || - drm_is_control_client(file_priv)) { - ret = fb->funcs->create_handle(fb, file_priv, - &r->handle); - } else { - /* GET_FB() is an unprivileged ioctl so we must not - * return a buffer-handle to non-master processes! For - * backwards-compatibility reasons, we cannot make - * GET_FB() privileged, so just return an invalid handle - * for non-masters. */ - r->handle = 0; - ret = 0; - } - } else { - ret = -ENODEV; - } - - drm_framebuffer_unreference(fb); - - return ret; -} - -/** - * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB - * @dev: drm device for the ioctl - * @data: data pointer for the ioctl - * @file_priv: drm file for the ioctl call - * - * Lookup the FB and flush out the damaged area supplied by userspace as a clip - * rectangle list. Generic userspace which does frontbuffer rendering must call - * this ioctl to flush out the changes on manual-update display outputs, e.g. - * usb display-link, mipi manual update panels or edp panel self refresh modes. - * - * Modesetting drivers which always update the frontbuffer do not need to - * implement the corresponding ->dirty framebuffer callback. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_dirtyfb_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_clip_rect __user *clips_ptr; - struct drm_clip_rect *clips = NULL; - struct drm_mode_fb_dirty_cmd *r = data; - struct drm_framebuffer *fb; - unsigned flags; - int num_clips; - int ret; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - fb = drm_framebuffer_lookup(dev, r->fb_id); - if (!fb) - return -ENOENT; - - num_clips = r->num_clips; - clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; - - if (!num_clips != !clips_ptr) { - ret = -EINVAL; - goto out_err1; - } - - flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags; - - /* If userspace annotates copy, clips must come in pairs */ - if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) { - ret = -EINVAL; - goto out_err1; - } - - if (num_clips && clips_ptr) { - if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) { - ret = -EINVAL; - goto out_err1; - } - clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL); - if (!clips) { - ret = -ENOMEM; - goto out_err1; - } - - ret = copy_from_user(clips, clips_ptr, - num_clips * sizeof(*clips)); - if (ret) { - ret = -EFAULT; - goto out_err2; - } - } - - if (fb->funcs->dirty) { - ret = fb->funcs->dirty(fb, file_priv, flags, r->color, - clips, num_clips); - } else { - ret = -ENOSYS; - } - -out_err2: - kfree(clips); -out_err1: - drm_framebuffer_unreference(fb); - - return ret; -} - -/** - * drm_fb_release - remove and free the FBs on this file - * @priv: drm file for the ioctl - * - * Destroy all the FBs associated with @filp. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -void drm_fb_release(struct drm_file *priv) -{ - struct drm_framebuffer *fb, *tfb; - struct drm_mode_rmfb_work arg; - - INIT_LIST_HEAD(&arg.fbs); - - /* - * When the file gets released that means no one else can access the fb - * list any more, so no need to grab fpriv->fbs_lock. And we need to - * avoid upsetting lockdep since the universal cursor code adds a - * framebuffer while holding mutex locks. - * - * Note that a real deadlock between fpriv->fbs_lock and the modeset - * locks is impossible here since no one else but this function can get - * at it any more. - */ - list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { - if (drm_framebuffer_read_refcount(fb) > 1) { - list_move_tail(&fb->filp_head, &arg.fbs); - } else { - list_del_init(&fb->filp_head); - - /* This drops the fpriv->fbs reference. */ - drm_framebuffer_unreference(fb); - } - } - - if (!list_empty(&arg.fbs)) { - INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn); - - schedule_work(&arg.work); - flush_work(&arg.work); - destroy_work_on_stack(&arg.work); - } -} - -static bool drm_property_type_valid(struct drm_property *property) -{ - if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) - return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE); - return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE); -} - -/** - * drm_property_create - create a new property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @num_values: number of pre-defined values - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Note that the DRM core keeps a per-device list of properties and that, if - * drm_mode_config_cleanup() is called, it will destroy all properties created - * by the driver. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create(struct drm_device *dev, int flags, - const char *name, int num_values) -{ - struct drm_property *property = NULL; - int ret; - - property = kzalloc(sizeof(struct drm_property), GFP_KERNEL); - if (!property) - return NULL; - - property->dev = dev; - - if (num_values) { - property->values = kcalloc(num_values, sizeof(uint64_t), - GFP_KERNEL); - if (!property->values) - goto fail; - } - - ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); - if (ret) - goto fail; - - property->flags = flags; - property->num_values = num_values; - INIT_LIST_HEAD(&property->enum_list); - - if (name) { - strncpy(property->name, name, DRM_PROP_NAME_LEN); - property->name[DRM_PROP_NAME_LEN-1] = '\0'; - } - - list_add_tail(&property->head, &dev->mode_config.property_list); - - WARN_ON(!drm_property_type_valid(property)); - - return property; -fail: - kfree(property->values); - kfree(property); - return NULL; -} -EXPORT_SYMBOL(drm_property_create); - -/** - * drm_property_create_enum - create a new enumeration property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @props: enumeration lists with property values - * @num_values: number of pre-defined values - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Userspace is only allowed to set one of the predefined values for enumeration - * properties. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, - const char *name, - const struct drm_prop_enum_list *props, - int num_values) -{ - struct drm_property *property; - int i, ret; - - flags |= DRM_MODE_PROP_ENUM; - - property = drm_property_create(dev, flags, name, num_values); - if (!property) - return NULL; - - for (i = 0; i < num_values; i++) { - ret = drm_property_add_enum(property, i, - props[i].type, - props[i].name); - if (ret) { - drm_property_destroy(dev, property); - return NULL; - } - } - - return property; -} -EXPORT_SYMBOL(drm_property_create_enum); - -/** - * drm_property_create_bitmask - create a new bitmask property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @props: enumeration lists with property bitflags - * @num_props: size of the @props array - * @supported_bits: bitmask of all supported enumeration values - * - * This creates a new bitmask drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Compared to plain enumeration properties userspace is allowed to set any - * or'ed together combination of the predefined property bitflag values - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_bitmask(struct drm_device *dev, - int flags, const char *name, - const struct drm_prop_enum_list *props, - int num_props, - uint64_t supported_bits) -{ - struct drm_property *property; - int i, ret, index = 0; - int num_values = hweight64(supported_bits); - - flags |= DRM_MODE_PROP_BITMASK; - - property = drm_property_create(dev, flags, name, num_values); - if (!property) - return NULL; - for (i = 0; i < num_props; i++) { - if (!(supported_bits & (1ULL << props[i].type))) - continue; - - if (WARN_ON(index >= num_values)) { - drm_property_destroy(dev, property); - return NULL; - } - - ret = drm_property_add_enum(property, index++, - props[i].type, - props[i].name); - if (ret) { - drm_property_destroy(dev, property); - return NULL; - } - } - - return property; -} -EXPORT_SYMBOL(drm_property_create_bitmask); - -static struct drm_property *property_create_range(struct drm_device *dev, - int flags, const char *name, - uint64_t min, uint64_t max) -{ - struct drm_property *property; - - property = drm_property_create(dev, flags, name, 2); - if (!property) - return NULL; - - property->values[0] = min; - property->values[1] = max; - - return property; -} - -/** - * drm_property_create_range - create a new unsigned ranged property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @min: minimum value of the property - * @max: maximum value of the property - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Userspace is allowed to set any unsigned integer value in the (min, max) - * range inclusive. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, - const char *name, - uint64_t min, uint64_t max) -{ - return property_create_range(dev, DRM_MODE_PROP_RANGE | flags, - name, min, max); -} -EXPORT_SYMBOL(drm_property_create_range); - -/** - * drm_property_create_signed_range - create a new signed ranged property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @min: minimum value of the property - * @max: maximum value of the property - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Userspace is allowed to set any signed integer value in the (min, max) - * range inclusive. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_signed_range(struct drm_device *dev, - int flags, const char *name, - int64_t min, int64_t max) -{ - return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags, - name, I642U64(min), I642U64(max)); -} -EXPORT_SYMBOL(drm_property_create_signed_range); - -/** - * drm_property_create_object - create a new object property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * @type: object type from DRM_MODE_OBJECT_* defines - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * Userspace is only allowed to set this to any property value of the given - * @type. Only useful for atomic properties, which is enforced. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_object(struct drm_device *dev, - int flags, const char *name, uint32_t type) -{ - struct drm_property *property; - - flags |= DRM_MODE_PROP_OBJECT; - - if (WARN_ON(!(flags & DRM_MODE_PROP_ATOMIC))) - return NULL; - - property = drm_property_create(dev, flags, name, 1); - if (!property) - return NULL; - - property->values[0] = type; - - return property; -} -EXPORT_SYMBOL(drm_property_create_object); - -/** - * drm_property_create_bool - create a new boolean property type - * @dev: drm device - * @flags: flags specifying the property type - * @name: name of the property - * - * This creates a new generic drm property which can then be attached to a drm - * object with drm_object_attach_property. The returned property object must be - * freed with drm_property_destroy. - * - * This is implemented as a ranged property with only {0, 1} as valid values. - * - * Returns: - * A pointer to the newly created property on success, NULL on failure. - */ -struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags, - const char *name) -{ - return drm_property_create_range(dev, flags, name, 0, 1); -} -EXPORT_SYMBOL(drm_property_create_bool); - -/** - * drm_property_add_enum - add a possible value to an enumeration property - * @property: enumeration property to change - * @index: index of the new enumeration - * @value: value of the new enumeration - * @name: symbolic name of the new enumeration - * - * This functions adds enumerations to a property. - * - * It's use is deprecated, drivers should use one of the more specific helpers - * to directly create the property with all enumerations already attached. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_property_add_enum(struct drm_property *property, int index, - uint64_t value, const char *name) -{ - struct drm_property_enum *prop_enum; - - if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) || - drm_property_type_is(property, DRM_MODE_PROP_BITMASK))) - return -EINVAL; - - /* - * Bitmask enum properties have the additional constraint of values - * from 0 to 63 - */ - if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) && - (value > 63)) - return -EINVAL; - - if (!list_empty(&property->enum_list)) { - list_for_each_entry(prop_enum, &property->enum_list, head) { - if (prop_enum->value == value) { - strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); - prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; - return 0; - } - } - } - - prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL); - if (!prop_enum) - return -ENOMEM; - - strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); - prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; - prop_enum->value = value; - - property->values[index] = value; - list_add_tail(&prop_enum->head, &property->enum_list); - return 0; -} -EXPORT_SYMBOL(drm_property_add_enum); - -/** - * drm_property_destroy - destroy a drm property - * @dev: drm device - * @property: property to destry - * - * This function frees a property including any attached resources like - * enumeration values. - */ -void drm_property_destroy(struct drm_device *dev, struct drm_property *property) -{ - struct drm_property_enum *prop_enum, *pt; - - list_for_each_entry_safe(prop_enum, pt, &property->enum_list, head) { - list_del(&prop_enum->head); - kfree(prop_enum); - } - - if (property->num_values) - kfree(property->values); - drm_mode_object_unregister(dev, &property->base); - list_del(&property->head); - kfree(property); -} -EXPORT_SYMBOL(drm_property_destroy); - -/** - * drm_object_attach_property - attach a property to a modeset object - * @obj: drm modeset object - * @property: property to attach - * @init_val: initial value of the property - * - * This attaches the given property to the modeset object with the given initial - * value. Currently this function cannot fail since the properties are stored in - * a statically sized array. - */ -void drm_object_attach_property(struct drm_mode_object *obj, - struct drm_property *property, - uint64_t init_val) -{ - int count = obj->properties->count; - - if (count == DRM_OBJECT_MAX_PROPERTY) { - WARN(1, "Failed to attach object property (type: 0x%x). Please " - "increase DRM_OBJECT_MAX_PROPERTY by 1 for each time " - "you see this message on the same object type.\n", - obj->type); - return; - } - - obj->properties->properties[count] = property; - obj->properties->values[count] = init_val; - obj->properties->count++; - if (property->flags & DRM_MODE_PROP_ATOMIC) - obj->properties->atomic_count++; -} -EXPORT_SYMBOL(drm_object_attach_property); - -/** - * drm_object_property_set_value - set the value of a property - * @obj: drm mode object to set property value for - * @property: property to set - * @val: value the property should be set to - * - * This functions sets a given property on a given object. This function only - * changes the software state of the property, it does not call into the - * driver's ->set_property callback. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_object_property_set_value(struct drm_mode_object *obj, - struct drm_property *property, uint64_t val) -{ - int i; - - for (i = 0; i < obj->properties->count; i++) { - if (obj->properties->properties[i] == property) { - obj->properties->values[i] = val; - return 0; - } - } - - return -EINVAL; -} -EXPORT_SYMBOL(drm_object_property_set_value); - -/** - * drm_object_property_get_value - retrieve the value of a property - * @obj: drm mode object to get property value from - * @property: property to retrieve - * @val: storage for the property value - * - * This function retrieves the softare state of the given property for the given - * property. Since there is no driver callback to retrieve the current property - * value this might be out of sync with the hardware, depending upon the driver - * and property. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_object_property_get_value(struct drm_mode_object *obj, - struct drm_property *property, uint64_t *val) -{ - int i; - - /* read-only properties bypass atomic mechanism and still store - * their value in obj->properties->values[].. mostly to avoid - * having to deal w/ EDID and similar props in atomic paths: - */ - if (drm_core_check_feature(property->dev, DRIVER_ATOMIC) && - !(property->flags & DRM_MODE_PROP_IMMUTABLE)) - return drm_atomic_get_property(obj, property, val); - - for (i = 0; i < obj->properties->count; i++) { - if (obj->properties->properties[i] == property) { - *val = obj->properties->values[i]; - return 0; - } - } - - return -EINVAL; -} -EXPORT_SYMBOL(drm_object_property_get_value); - -/** - * drm_mode_getproperty_ioctl - get the property metadata - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function retrieves the metadata for a given property, like the different - * possible values for an enum property or the limits for a range property. - * - * Blob properties are special - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getproperty_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_get_property *out_resp = data; - struct drm_property *property; - int enum_count = 0; - int value_count = 0; - int ret = 0, i; - int copied; - struct drm_property_enum *prop_enum; - struct drm_mode_property_enum __user *enum_ptr; - uint64_t __user *values_ptr; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - drm_modeset_lock_all(dev); - property = drm_property_find(dev, out_resp->prop_id); - if (!property) { - ret = -ENOENT; - goto done; - } - - if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || - drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { - list_for_each_entry(prop_enum, &property->enum_list, head) - enum_count++; - } - - value_count = property->num_values; - - strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); - out_resp->name[DRM_PROP_NAME_LEN-1] = 0; - out_resp->flags = property->flags; - - if ((out_resp->count_values >= value_count) && value_count) { - values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr; - for (i = 0; i < value_count; i++) { - if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { - ret = -EFAULT; - goto done; - } - } - } - out_resp->count_values = value_count; - - if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || - drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { - if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { - copied = 0; - enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; - list_for_each_entry(prop_enum, &property->enum_list, head) { - - if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { - ret = -EFAULT; - goto done; - } - - if (copy_to_user(&enum_ptr[copied].name, - &prop_enum->name, DRM_PROP_NAME_LEN)) { - ret = -EFAULT; - goto done; - } - copied++; - } - } - out_resp->count_enum_blobs = enum_count; - } - - /* - * NOTE: The idea seems to have been to use this to read all the blob - * property values. But nothing ever added them to the corresponding - * list, userspace always used the special-purpose get_blob ioctl to - * read the value for a blob property. It also doesn't make a lot of - * sense to return values here when everything else is just metadata for - * the property itself. - */ - if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) - out_resp->count_enum_blobs = 0; -done: - drm_modeset_unlock_all(dev); - return ret; -} - -static void drm_property_free_blob(struct kref *kref) -{ - struct drm_property_blob *blob = - container_of(kref, struct drm_property_blob, base.refcount); - - mutex_lock(&blob->dev->mode_config.blob_lock); - list_del(&blob->head_global); - mutex_unlock(&blob->dev->mode_config.blob_lock); - - drm_mode_object_unregister(blob->dev, &blob->base); - - kfree(blob); -} - -/** - * drm_property_create_blob - Create new blob property - * - * Creates a new blob property for a specified DRM device, optionally - * copying data. - * - * @dev: DRM device to create property for - * @length: Length to allocate for blob data - * @data: If specified, copies data into blob - * - * Returns: - * New blob property with a single reference on success, or an ERR_PTR - * value on failure. - */ -struct drm_property_blob * -drm_property_create_blob(struct drm_device *dev, size_t length, - const void *data) -{ - struct drm_property_blob *blob; - int ret; - - if (!length || length > ULONG_MAX - sizeof(struct drm_property_blob)) - return ERR_PTR(-EINVAL); - - blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); - if (!blob) - return ERR_PTR(-ENOMEM); - - /* This must be explicitly initialised, so we can safely call list_del - * on it in the removal handler, even if it isn't in a file list. */ - INIT_LIST_HEAD(&blob->head_file); - blob->length = length; - blob->dev = dev; - - if (data) - memcpy(blob->data, data, length); - - ret = drm_mode_object_get_reg(dev, &blob->base, DRM_MODE_OBJECT_BLOB, - true, drm_property_free_blob); - if (ret) { - kfree(blob); - return ERR_PTR(-EINVAL); - } - - mutex_lock(&dev->mode_config.blob_lock); - list_add_tail(&blob->head_global, - &dev->mode_config.property_blob_list); - mutex_unlock(&dev->mode_config.blob_lock); - - return blob; -} -EXPORT_SYMBOL(drm_property_create_blob); - -/** - * drm_property_unreference_blob - Unreference a blob property - * - * Drop a reference on a blob property. May free the object. - * - * @blob: Pointer to blob property - */ -void drm_property_unreference_blob(struct drm_property_blob *blob) -{ - if (!blob) - return; - - drm_mode_object_unreference(&blob->base); -} -EXPORT_SYMBOL(drm_property_unreference_blob); - -/** - * drm_property_destroy_user_blobs - destroy all blobs created by this client - * @dev: DRM device - * @file_priv: destroy all blobs owned by this file handle - */ -void drm_property_destroy_user_blobs(struct drm_device *dev, - struct drm_file *file_priv) -{ - struct drm_property_blob *blob, *bt; - - /* - * When the file gets released that means no one else can access the - * blob list any more, so no need to grab dev->blob_lock. - */ - list_for_each_entry_safe(blob, bt, &file_priv->blobs, head_file) { - list_del_init(&blob->head_file); - drm_property_unreference_blob(blob); - } -} - -/** - * drm_property_reference_blob - Take a reference on an existing property - * - * Take a new reference on an existing blob property. - * - * @blob: Pointer to blob property - */ -struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob) -{ - drm_mode_object_reference(&blob->base); - return blob; -} -EXPORT_SYMBOL(drm_property_reference_blob); - -/** - * drm_property_lookup_blob - look up a blob property and take a reference - * @dev: drm device - * @id: id of the blob property - * - * If successful, this takes an additional reference to the blob property. - * callers need to make sure to eventually unreference the returned property - * again, using @drm_property_unreference_blob. - */ -struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, - uint32_t id) -{ - struct drm_mode_object *obj; - struct drm_property_blob *blob = NULL; - - obj = _object_find(dev, id, DRM_MODE_OBJECT_BLOB); - if (obj) - blob = obj_to_blob(obj); - return blob; -} -EXPORT_SYMBOL(drm_property_lookup_blob); - -/** - * drm_property_replace_global_blob - atomically replace existing blob property - * @dev: drm device - * @replace: location of blob property pointer to be replaced - * @length: length of data for new blob, or 0 for no data - * @data: content for new blob, or NULL for no data - * @obj_holds_id: optional object for property holding blob ID - * @prop_holds_id: optional property holding blob ID - * @return 0 on success or error on failure - * - * This function will atomically replace a global property in the blob list, - * optionally updating a property which holds the ID of that property. It is - * guaranteed to be atomic: no caller will be allowed to see intermediate - * results, and either the entire operation will succeed and clean up the - * previous property, or it will fail and the state will be unchanged. - * - * If length is 0 or data is NULL, no new blob will be created, and the holding - * property, if specified, will be set to 0. - * - * Access to the replace pointer is assumed to be protected by the caller, e.g. - * by holding the relevant modesetting object lock for its parent. - * - * For example, a drm_connector has a 'PATH' property, which contains the ID - * of a blob property with the value of the MST path information. Calling this - * function with replace pointing to the connector's path_blob_ptr, length and - * data set for the new path information, obj_holds_id set to the connector's - * base object, and prop_holds_id set to the path property name, will perform - * a completely atomic update. The access to path_blob_ptr is protected by the - * caller holding a lock on the connector. - */ -static int drm_property_replace_global_blob(struct drm_device *dev, - struct drm_property_blob **replace, - size_t length, - const void *data, - struct drm_mode_object *obj_holds_id, - struct drm_property *prop_holds_id) -{ - struct drm_property_blob *new_blob = NULL; - struct drm_property_blob *old_blob = NULL; - int ret; - - WARN_ON(replace == NULL); - - old_blob = *replace; - - if (length && data) { - new_blob = drm_property_create_blob(dev, length, data); - if (IS_ERR(new_blob)) - return PTR_ERR(new_blob); - } - - /* This does not need to be synchronised with blob_lock, as the - * get_properties ioctl locks all modesetting objects, and - * obj_holds_id must be locked before calling here, so we cannot - * have its value out of sync with the list membership modified - * below under blob_lock. */ - if (obj_holds_id) { - ret = drm_object_property_set_value(obj_holds_id, - prop_holds_id, - new_blob ? - new_blob->base.id : 0); - if (ret != 0) - goto err_created; - } - - drm_property_unreference_blob(old_blob); - *replace = new_blob; - - return 0; - -err_created: - drm_property_unreference_blob(new_blob); - return ret; -} - -/** - * drm_mode_getblob_ioctl - get the contents of a blob property value - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function retrieves the contents of a blob property. The value stored in - * an object's blob property is just a normal modeset object id. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_getblob_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_get_blob *out_resp = data; - struct drm_property_blob *blob; - int ret = 0; - void __user *blob_ptr; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - blob = drm_property_lookup_blob(dev, out_resp->blob_id); - if (!blob) - return -ENOENT; - - if (out_resp->length == blob->length) { - blob_ptr = (void __user *)(unsigned long)out_resp->data; - if (copy_to_user(blob_ptr, blob->data, blob->length)) { - ret = -EFAULT; - goto unref; - } - } - out_resp->length = blob->length; -unref: - drm_property_unreference_blob(blob); - - return ret; -} - -/** - * drm_mode_createblob_ioctl - create a new blob property - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function creates a new blob property with user-defined values. In order - * to give us sensible validation and checking when creating, rather than at - * every potential use, we also require a type to be provided upfront. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_createblob_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_create_blob *out_resp = data; - struct drm_property_blob *blob; - void __user *blob_ptr; - int ret = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - blob = drm_property_create_blob(dev, out_resp->length, NULL); - if (IS_ERR(blob)) - return PTR_ERR(blob); - - blob_ptr = (void __user *)(unsigned long)out_resp->data; - if (copy_from_user(blob->data, blob_ptr, out_resp->length)) { - ret = -EFAULT; - goto out_blob; - } - - /* Dropping the lock between create_blob and our access here is safe - * as only the same file_priv can remove the blob; at this point, it is - * not associated with any file_priv. */ - mutex_lock(&dev->mode_config.blob_lock); - out_resp->blob_id = blob->base.id; - list_add_tail(&blob->head_file, &file_priv->blobs); - mutex_unlock(&dev->mode_config.blob_lock); - - return 0; - -out_blob: - drm_property_unreference_blob(blob); - return ret; -} - -/** - * drm_mode_destroyblob_ioctl - destroy a user blob property - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * Destroy an existing user-defined blob property. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_destroyblob_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_destroy_blob *out_resp = data; - struct drm_property_blob *blob = NULL, *bt; - bool found = false; - int ret = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - blob = drm_property_lookup_blob(dev, out_resp->blob_id); - if (!blob) - return -ENOENT; - - mutex_lock(&dev->mode_config.blob_lock); - /* Ensure the property was actually created by this user. */ - list_for_each_entry(bt, &file_priv->blobs, head_file) { - if (bt == blob) { - found = true; - break; - } - } - - if (!found) { - ret = -EPERM; - goto err; - } - - /* We must drop head_file here, because we may not be the last - * reference on the blob. */ - list_del_init(&blob->head_file); - mutex_unlock(&dev->mode_config.blob_lock); - - /* One reference from lookup, and one from the filp. */ - drm_property_unreference_blob(blob); - drm_property_unreference_blob(blob); - - return 0; - -err: - mutex_unlock(&dev->mode_config.blob_lock); - drm_property_unreference_blob(blob); - - return ret; -} - -/** - * drm_mode_connector_set_path_property - set tile property on connector - * @connector: connector to set property on. - * @path: path to use for property; must not be NULL. - * - * This creates a property to expose to userspace to specify a - * connector path. This is mainly used for DisplayPort MST where - * connectors have a topology and we want to allow userspace to give - * them more meaningful names. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_connector_set_path_property(struct drm_connector *connector, - const char *path) -{ - struct drm_device *dev = connector->dev; - int ret; - - ret = drm_property_replace_global_blob(dev, - &connector->path_blob_ptr, - strlen(path) + 1, - path, - &connector->base, - dev->mode_config.path_property); - return ret; -} -EXPORT_SYMBOL(drm_mode_connector_set_path_property); - -/** - * drm_mode_connector_set_tile_property - set tile property on connector - * @connector: connector to set property on. - * - * This looks up the tile information for a connector, and creates a - * property for userspace to parse if it exists. The property is of - * the form of 8 integers using ':' as a separator. - * - * Returns: - * Zero on success, errno on failure. - */ -int drm_mode_connector_set_tile_property(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - char tile[256]; - int ret; - - if (!connector->has_tile) { - ret = drm_property_replace_global_blob(dev, - &connector->tile_blob_ptr, - 0, - NULL, - &connector->base, - dev->mode_config.tile_property); - return ret; - } - - snprintf(tile, 256, "%d:%d:%d:%d:%d:%d:%d:%d", - connector->tile_group->id, connector->tile_is_single_monitor, - connector->num_h_tile, connector->num_v_tile, - connector->tile_h_loc, connector->tile_v_loc, - connector->tile_h_size, connector->tile_v_size); - - ret = drm_property_replace_global_blob(dev, - &connector->tile_blob_ptr, - strlen(tile) + 1, - tile, - &connector->base, - dev->mode_config.tile_property); - return ret; -} -EXPORT_SYMBOL(drm_mode_connector_set_tile_property); - -/** - * drm_mode_connector_update_edid_property - update the edid property of a connector - * @connector: drm connector - * @edid: new value of the edid property - * - * This function creates a new blob modeset object and assigns its id to the - * connector's edid property. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_connector_update_edid_property(struct drm_connector *connector, - const struct edid *edid) -{ - struct drm_device *dev = connector->dev; - size_t size = 0; - int ret; - - /* ignore requests to set edid when overridden */ - if (connector->override_edid) - return 0; - - if (edid) - size = EDID_LENGTH * (1 + edid->extensions); - - ret = drm_property_replace_global_blob(dev, - &connector->edid_blob_ptr, - size, - edid, - &connector->base, - dev->mode_config.edid_property); - return ret; -} -EXPORT_SYMBOL(drm_mode_connector_update_edid_property); - -/* Some properties could refer to dynamic refcnt'd objects, or things that - * need special locking to handle lifetime issues (ie. to ensure the prop - * value doesn't become invalid part way through the property update due to - * race). The value returned by reference via 'obj' should be passed back - * to drm_property_change_valid_put() after the property is set (and the - * object to which the property is attached has a chance to take it's own - * reference). - */ -bool drm_property_change_valid_get(struct drm_property *property, - uint64_t value, struct drm_mode_object **ref) -{ - int i; - - if (property->flags & DRM_MODE_PROP_IMMUTABLE) - return false; - - *ref = NULL; - - if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) { - if (value < property->values[0] || value > property->values[1]) - return false; - return true; - } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) { - int64_t svalue = U642I64(value); - - if (svalue < U642I64(property->values[0]) || - svalue > U642I64(property->values[1])) - return false; - return true; - } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { - uint64_t valid_mask = 0; - - for (i = 0; i < property->num_values; i++) - valid_mask |= (1ULL << property->values[i]); - return !(value & ~valid_mask); - } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { - struct drm_property_blob *blob; - - if (value == 0) - return true; - - blob = drm_property_lookup_blob(property->dev, value); - if (blob) { - *ref = &blob->base; - return true; - } else { - return false; - } - } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { - /* a zero value for an object property translates to null: */ - if (value == 0) - return true; - - *ref = _object_find(property->dev, value, property->values[0]); - return *ref != NULL; - } - - for (i = 0; i < property->num_values; i++) - if (property->values[i] == value) - return true; - return false; -} - -void drm_property_change_valid_put(struct drm_property *property, - struct drm_mode_object *ref) -{ - if (!ref) - return; - - if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { - drm_mode_object_unreference(ref); - } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) - drm_property_unreference_blob(obj_to_blob(ref)); -} - -/** - * drm_mode_connector_property_set_ioctl - set the current value of a connector property - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function sets the current value for a connectors's property. It also - * calls into a driver's ->set_property callback to update the hardware state - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_connector_property_set_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_connector_set_property *conn_set_prop = data; - struct drm_mode_obj_set_property obj_set_prop = { - .value = conn_set_prop->value, - .prop_id = conn_set_prop->prop_id, - .obj_id = conn_set_prop->connector_id, - .obj_type = DRM_MODE_OBJECT_CONNECTOR - }; - - /* It does all the locking and checking we need */ - return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); -} - -static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, - struct drm_property *property, - uint64_t value) -{ - int ret = -EINVAL; - struct drm_connector *connector = obj_to_connector(obj); - - /* Do DPMS ourselves */ - if (property == connector->dev->mode_config.dpms_property) { - ret = (*connector->funcs->dpms)(connector, (int)value); - } else if (connector->funcs->set_property) - ret = connector->funcs->set_property(connector, property, value); - - /* store the property value if successful */ - if (!ret) - drm_object_property_set_value(&connector->base, property, value); - return ret; -} - -static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, - struct drm_property *property, - uint64_t value) +int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t value) { int ret = -EINVAL; struct drm_crtc *crtc = obj_to_crtc(obj); @@ -5004,546 +904,6 @@ static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, } /** - * drm_mode_plane_set_obj_prop - set the value of a property - * @plane: drm plane object to set property value for - * @property: property to set - * @value: value the property should be set to - * - * This functions sets a given property on a given plane object. This function - * calls the driver's ->set_property callback and changes the software state of - * the property if the callback succeeds. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_mode_plane_set_obj_prop(struct drm_plane *plane, - struct drm_property *property, - uint64_t value) -{ - int ret = -EINVAL; - struct drm_mode_object *obj = &plane->base; - - if (plane->funcs->set_property) - ret = plane->funcs->set_property(plane, property, value); - if (!ret) - drm_object_property_set_value(obj, property, value); - - return ret; -} -EXPORT_SYMBOL(drm_mode_plane_set_obj_prop); - -/** - * drm_mode_obj_get_properties_ioctl - get the current value of a object's property - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function retrieves the current value for an object's property. Compared - * to the connector specific ioctl this one is extended to also work on crtc and - * plane objects. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_obj_get_properties *arg = data; - struct drm_mode_object *obj; - int ret = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - drm_modeset_lock_all(dev); - - obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); - if (!obj) { - ret = -ENOENT; - goto out; - } - if (!obj->properties) { - ret = -EINVAL; - goto out_unref; - } - - ret = get_properties(obj, file_priv->atomic, - (uint32_t __user *)(unsigned long)(arg->props_ptr), - (uint64_t __user *)(unsigned long)(arg->prop_values_ptr), - &arg->count_props); - -out_unref: - drm_mode_object_unreference(obj); -out: - drm_modeset_unlock_all(dev); - return ret; -} - -/** - * drm_mode_obj_set_property_ioctl - set the current value of an object's property - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This function sets the current value for an object's property. It also calls - * into a driver's ->set_property callback to update the hardware state. - * Compared to the connector specific ioctl this one is extended to also work on - * crtc and plane objects. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_mode_obj_set_property *arg = data; - struct drm_mode_object *arg_obj; - struct drm_mode_object *prop_obj; - struct drm_property *property; - int i, ret = -EINVAL; - struct drm_mode_object *ref; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - drm_modeset_lock_all(dev); - - arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); - if (!arg_obj) { - ret = -ENOENT; - goto out; - } - if (!arg_obj->properties) - goto out_unref; - - for (i = 0; i < arg_obj->properties->count; i++) - if (arg_obj->properties->properties[i]->base.id == arg->prop_id) - break; - - if (i == arg_obj->properties->count) - goto out_unref; - - prop_obj = drm_mode_object_find(dev, arg->prop_id, - DRM_MODE_OBJECT_PROPERTY); - if (!prop_obj) { - ret = -ENOENT; - goto out_unref; - } - property = obj_to_property(prop_obj); - - if (!drm_property_change_valid_get(property, arg->value, &ref)) - goto out_unref; - - switch (arg_obj->type) { - case DRM_MODE_OBJECT_CONNECTOR: - ret = drm_mode_connector_set_obj_prop(arg_obj, property, - arg->value); - break; - case DRM_MODE_OBJECT_CRTC: - ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); - break; - case DRM_MODE_OBJECT_PLANE: - ret = drm_mode_plane_set_obj_prop(obj_to_plane(arg_obj), - property, arg->value); - break; - } - - drm_property_change_valid_put(property, ref); - -out_unref: - drm_mode_object_unreference(arg_obj); -out: - drm_modeset_unlock_all(dev); - return ret; -} - -/** - * drm_mode_connector_attach_encoder - attach a connector to an encoder - * @connector: connector to attach - * @encoder: encoder to attach @connector to - * - * This function links up a connector to an encoder. Note that the routing - * restrictions between encoders and crtcs are exposed to userspace through the - * possible_clones and possible_crtcs bitmasks. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_connector_attach_encoder(struct drm_connector *connector, - struct drm_encoder *encoder) -{ - int i; - - /* - * In the past, drivers have attempted to model the static association - * of connector to encoder in simple connector/encoder devices using a - * direct assignment of connector->encoder = encoder. This connection - * is a logical one and the responsibility of the core, so drivers are - * expected not to mess with this. - * - * Note that the error return should've been enough here, but a large - * majority of drivers ignores the return value, so add in a big WARN - * to get people's attention. - */ - if (WARN_ON(connector->encoder)) - return -EINVAL; - - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) { - connector->encoder_ids[i] = encoder->base.id; - return 0; - } - } - return -ENOMEM; -} -EXPORT_SYMBOL(drm_mode_connector_attach_encoder); - -/** - * drm_mode_crtc_set_gamma_size - set the gamma table size - * @crtc: CRTC to set the gamma table size for - * @gamma_size: size of the gamma table - * - * Drivers which support gamma tables should set this to the supported gamma - * table size when initializing the CRTC. Currently the drm core only supports a - * fixed gamma table size. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, - int gamma_size) -{ - uint16_t *r_base, *g_base, *b_base; - int i; - - crtc->gamma_size = gamma_size; - - crtc->gamma_store = kcalloc(gamma_size, sizeof(uint16_t) * 3, - GFP_KERNEL); - if (!crtc->gamma_store) { - crtc->gamma_size = 0; - return -ENOMEM; - } - - r_base = crtc->gamma_store; - g_base = r_base + gamma_size; - b_base = g_base + gamma_size; - for (i = 0; i < gamma_size; i++) { - r_base[i] = i << 8; - g_base[i] = i << 8; - b_base[i] = i << 8; - } - - - return 0; -} -EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); - -/** - * drm_mode_gamma_set_ioctl - set the gamma table - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * Set the gamma table of a CRTC to the one passed in by the user. Userspace can - * inquire the required gamma table size through drm_mode_gamma_get_ioctl. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_gamma_set_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_crtc_lut *crtc_lut = data; - struct drm_crtc *crtc; - void *r_base, *g_base, *b_base; - int size; - int ret = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - drm_modeset_lock_all(dev); - crtc = drm_crtc_find(dev, crtc_lut->crtc_id); - if (!crtc) { - ret = -ENOENT; - goto out; - } - - if (crtc->funcs->gamma_set == NULL) { - ret = -ENOSYS; - goto out; - } - - /* memcpy into gamma store */ - if (crtc_lut->gamma_size != crtc->gamma_size) { - ret = -EINVAL; - goto out; - } - - size = crtc_lut->gamma_size * (sizeof(uint16_t)); - r_base = crtc->gamma_store; - if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) { - ret = -EFAULT; - goto out; - } - - g_base = r_base + size; - if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) { - ret = -EFAULT; - goto out; - } - - b_base = g_base + size; - if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) { - ret = -EFAULT; - goto out; - } - - ret = crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size); - -out: - drm_modeset_unlock_all(dev); - return ret; - -} - -/** - * drm_mode_gamma_get_ioctl - get the gamma table - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * Copy the current gamma table into the storage provided. This also provides - * the gamma table size the driver expects, which can be used to size the - * allocated storage. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_gamma_get_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_crtc_lut *crtc_lut = data; - struct drm_crtc *crtc; - void *r_base, *g_base, *b_base; - int size; - int ret = 0; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - drm_modeset_lock_all(dev); - crtc = drm_crtc_find(dev, crtc_lut->crtc_id); - if (!crtc) { - ret = -ENOENT; - goto out; - } - - /* memcpy into gamma store */ - if (crtc_lut->gamma_size != crtc->gamma_size) { - ret = -EINVAL; - goto out; - } - - size = crtc_lut->gamma_size * (sizeof(uint16_t)); - r_base = crtc->gamma_store; - if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) { - ret = -EFAULT; - goto out; - } - - g_base = r_base + size; - if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) { - ret = -EFAULT; - goto out; - } - - b_base = g_base + size; - if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) { - ret = -EFAULT; - goto out; - } -out: - drm_modeset_unlock_all(dev); - return ret; -} - -/** - * drm_mode_page_flip_ioctl - schedule an asynchronous fb update - * @dev: DRM device - * @data: ioctl data - * @file_priv: DRM file info - * - * This schedules an asynchronous update on a given CRTC, called page flip. - * Optionally a drm event is generated to signal the completion of the event. - * Generic drivers cannot assume that a pageflip with changed framebuffer - * properties (including driver specific metadata like tiling layout) will work, - * but some drivers support e.g. pixel format changes through the pageflip - * ioctl. - * - * Called by the user via ioctl. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_mode_page_flip_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) -{ - struct drm_mode_crtc_page_flip_target *page_flip = data; - struct drm_crtc *crtc; - struct drm_framebuffer *fb = NULL; - struct drm_pending_vblank_event *e = NULL; - u32 target_vblank = page_flip->sequence; - int ret = -EINVAL; - - if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS) - return -EINVAL; - - if (page_flip->sequence != 0 && !(page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET)) - return -EINVAL; - - /* Only one of the DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags - * can be specified - */ - if ((page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) == DRM_MODE_PAGE_FLIP_TARGET) - return -EINVAL; - - if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip) - return -EINVAL; - - crtc = drm_crtc_find(dev, page_flip->crtc_id); - if (!crtc) - return -ENOENT; - - if (crtc->funcs->page_flip_target) { - u32 current_vblank; - int r; - - r = drm_crtc_vblank_get(crtc); - if (r) - return r; - - current_vblank = drm_crtc_vblank_count(crtc); - - switch (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) { - case DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE: - if ((int)(target_vblank - current_vblank) > 1) { - DRM_DEBUG("Invalid absolute flip target %u, " - "must be <= %u\n", target_vblank, - current_vblank + 1); - drm_crtc_vblank_put(crtc); - return -EINVAL; - } - break; - case DRM_MODE_PAGE_FLIP_TARGET_RELATIVE: - if (target_vblank != 0 && target_vblank != 1) { - DRM_DEBUG("Invalid relative flip target %u, " - "must be 0 or 1\n", target_vblank); - drm_crtc_vblank_put(crtc); - return -EINVAL; - } - target_vblank += current_vblank; - break; - default: - target_vblank = current_vblank + - !(page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC); - break; - } - } else if (crtc->funcs->page_flip == NULL || - (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET)) { - return -EINVAL; - } - - drm_modeset_lock_crtc(crtc, crtc->primary); - if (crtc->primary->fb == NULL) { - /* The framebuffer is currently unbound, presumably - * due to a hotplug event, that userspace has not - * yet discovered. - */ - ret = -EBUSY; - goto out; - } - - fb = drm_framebuffer_lookup(dev, page_flip->fb_id); - if (!fb) { - ret = -ENOENT; - goto out; - } - - if (crtc->state) { - const struct drm_plane_state *state = crtc->primary->state; - - ret = check_src_coords(state->src_x, state->src_y, - state->src_w, state->src_h, fb); - } else { - ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb); - } - if (ret) - goto out; - - if (crtc->primary->fb->pixel_format != fb->pixel_format) { - DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); - ret = -EINVAL; - goto out; - } - - if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { - e = kzalloc(sizeof *e, GFP_KERNEL); - if (!e) { - ret = -ENOMEM; - goto out; - } - e->event.base.type = DRM_EVENT_FLIP_COMPLETE; - e->event.base.length = sizeof(e->event); - e->event.user_data = page_flip->user_data; - ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base); - if (ret) { - kfree(e); - goto out; - } - } - - crtc->primary->old_fb = crtc->primary->fb; - if (crtc->funcs->page_flip_target) - ret = crtc->funcs->page_flip_target(crtc, fb, e, - page_flip->flags, - target_vblank); - else - ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags); - if (ret) { - if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) - drm_event_cancel_free(dev, &e->base); - /* Keep the old fb, don't unref it. */ - crtc->primary->old_fb = NULL; - } else { - crtc->primary->fb = fb; - /* Unref only the old framebuffer. */ - fb = NULL; - } - -out: - if (ret) - drm_crtc_vblank_put(crtc); - if (fb) - drm_framebuffer_unreference(fb); - if (crtc->primary->old_fb) - drm_framebuffer_unreference(crtc->primary->old_fb); - crtc->primary->old_fb = NULL; - drm_modeset_unlock_crtc(crtc); - - return ret; -} - -/** * drm_mode_config_reset - call ->reset callbacks * @dev: drm device * @@ -5688,37 +1048,6 @@ int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, } /** - * drm_rotation_simplify() - Try to simplify the rotation - * @rotation: Rotation to be simplified - * @supported_rotations: Supported rotations - * - * Attempt to simplify the rotation to a form that is supported. - * Eg. if the hardware supports everything except DRM_REFLECT_X - * one could call this function like this: - * - * drm_rotation_simplify(rotation, BIT(DRM_ROTATE_0) | - * BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_180) | - * BIT(DRM_ROTATE_270) | BIT(DRM_REFLECT_Y)); - * - * to eliminate the DRM_ROTATE_X flag. Depending on what kind of - * transforms the hardware supports, this function may not - * be able to produce a supported transform, so the caller should - * check the result afterwards. - */ -unsigned int drm_rotation_simplify(unsigned int rotation, - unsigned int supported_rotations) -{ - if (rotation & ~supported_rotations) { - rotation ^= BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y); - rotation = (rotation & DRM_REFLECT_MASK) | - BIT((ffs(rotation & DRM_ROTATE_MASK) + 1) % 4); - } - - return rotation; -} -EXPORT_SYMBOL(drm_rotation_simplify); - -/** * drm_mode_config_init - initialize DRM mode_configuration structure * @dev: DRM device * @@ -5834,24 +1163,6 @@ void drm_mode_config_cleanup(struct drm_device *dev) } EXPORT_SYMBOL(drm_mode_config_cleanup); -struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev, - unsigned int supported_rotations) -{ - static const struct drm_prop_enum_list props[] = { - { DRM_ROTATE_0, "rotate-0" }, - { DRM_ROTATE_90, "rotate-90" }, - { DRM_ROTATE_180, "rotate-180" }, - { DRM_ROTATE_270, "rotate-270" }, - { DRM_REFLECT_X, "reflect-x" }, - { DRM_REFLECT_Y, "reflect-y" }, - }; - - return drm_property_create_bitmask(dev, 0, "rotation", - props, ARRAY_SIZE(props), - supported_rotations); -} -EXPORT_SYMBOL(drm_mode_create_rotation_property); - /** * DOC: Tile group * @@ -5950,48 +1261,3 @@ struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, return tg; } EXPORT_SYMBOL(drm_mode_create_tile_group); - -/** - * drm_crtc_enable_color_mgmt - enable color management properties - * @crtc: DRM CRTC - * @degamma_lut_size: the size of the degamma lut (before CSC) - * @has_ctm: whether to attach ctm_property for CSC matrix - * @gamma_lut_size: the size of the gamma lut (after CSC) - * - * This function lets the driver enable the color correction - * properties on a CRTC. This includes 3 degamma, csc and gamma - * properties that userspace can set and 2 size properties to inform - * the userspace of the lut sizes. Each of the properties are - * optional. The gamma and degamma properties are only attached if - * their size is not 0 and ctm_property is only attached if has_ctm is - * true. - */ -void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc, - uint degamma_lut_size, - bool has_ctm, - uint gamma_lut_size) -{ - struct drm_device *dev = crtc->dev; - struct drm_mode_config *config = &dev->mode_config; - - if (degamma_lut_size) { - drm_object_attach_property(&crtc->base, - config->degamma_lut_property, 0); - drm_object_attach_property(&crtc->base, - config->degamma_lut_size_property, - degamma_lut_size); - } - - if (has_ctm) - drm_object_attach_property(&crtc->base, - config->ctm_property, 0); - - if (gamma_lut_size) { - drm_object_attach_property(&crtc->base, - config->gamma_lut_property, 0); - drm_object_attach_property(&crtc->base, - config->gamma_lut_size_property, - gamma_lut_size); - } -} -EXPORT_SYMBOL(drm_crtc_enable_color_mgmt); diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 604d3ef..5d2cb13 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -75,35 +75,6 @@ */ /** - * drm_helper_move_panel_connectors_to_head() - move panels to the front in the - * connector list - * @dev: drm device to operate on - * - * Some userspace presumes that the first connected connector is the main - * display, where it's supposed to display e.g. the login screen. For - * laptops, this should be the main panel. Use this function to sort all - * (eDP/LVDS) panels to the front of the connector list, instead of - * painstakingly trying to initialize them in the right order. - */ -void drm_helper_move_panel_connectors_to_head(struct drm_device *dev) -{ - struct drm_connector *connector, *tmp; - struct list_head panel_list; - - INIT_LIST_HEAD(&panel_list); - - list_for_each_entry_safe(connector, tmp, - &dev->mode_config.connector_list, head) { - if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS || - connector->connector_type == DRM_MODE_CONNECTOR_eDP) - list_move_tail(&connector->head, &panel_list); - } - - list_splice(&panel_list, &dev->mode_config.connector_list); -} -EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head); - -/** * drm_helper_encoder_in_use - check if a given encoder is in use * @encoder: encoder to check * @@ -913,33 +884,6 @@ int drm_helper_connector_dpms(struct drm_connector *connector, int mode) EXPORT_SYMBOL(drm_helper_connector_dpms); /** - * drm_helper_mode_fill_fb_struct - fill out framebuffer metadata - * @fb: drm_framebuffer object to fill out - * @mode_cmd: metadata from the userspace fb creation request - * - * This helper can be used in a drivers fb_create callback to pre-fill the fb's - * metadata fields. - */ -void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, - const struct drm_mode_fb_cmd2 *mode_cmd) -{ - int i; - - fb->width = mode_cmd->width; - fb->height = mode_cmd->height; - for (i = 0; i < 4; i++) { - fb->pitches[i] = mode_cmd->pitches[i]; - fb->offsets[i] = mode_cmd->offsets[i]; - fb->modifier[i] = mode_cmd->modifier[i]; - } - drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth, - &fb->bits_per_pixel); - fb->pixel_format = mode_cmd->pixel_format; - fb->flags = mode_cmd->flags; -} -EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); - -/** * drm_helper_resume_force_mode - force-restore mode setting configuration * @dev: drm_device which should be restored * diff --git a/drivers/gpu/drm/drm_crtc_helper_internal.h b/drivers/gpu/drm/drm_crtc_helper_internal.h new file mode 100644 index 0000000..28295e5 --- /dev/null +++ b/drivers/gpu/drm/drm_crtc_helper_internal.h @@ -0,0 +1,65 @@ +/* + * Copyright © 2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * This header file contains mode setting related functions and definitions + * which are only used within the drm kms helper module as internal + * implementation details and are not exported to drivers. + */ + +#include <drm/drm_dp_helper.h> + +/* drm_fb_helper.c */ +#ifdef CONFIG_DRM_FBDEV_EMULATION +int drm_fb_helper_modinit(void); +#else +static inline int drm_fb_helper_modinit(void) +{ + return 0; +} +#endif + +/* drm_dp_aux_dev.c */ +#ifdef CONFIG_DRM_DP_AUX_CHARDEV +int drm_dp_aux_dev_init(void); +void drm_dp_aux_dev_exit(void); +int drm_dp_aux_register_devnode(struct drm_dp_aux *aux); +void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux); +#else +static inline int drm_dp_aux_dev_init(void) +{ + return 0; +} + +static inline void drm_dp_aux_dev_exit(void) +{ +} + +static inline int drm_dp_aux_register_devnode(struct drm_dp_aux *aux) +{ + return 0; +} + +static inline void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux) +{ +} +#endif diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index 0c34e6d..c48ba02 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -33,28 +33,15 @@ /* drm_crtc.c */ -void drm_connector_ida_init(void); -void drm_connector_ida_destroy(void); -int drm_mode_object_get(struct drm_device *dev, - struct drm_mode_object *obj, uint32_t obj_type); -void drm_mode_object_unregister(struct drm_device *dev, - struct drm_mode_object *object); -bool drm_property_change_valid_get(struct drm_property *property, - uint64_t value, - struct drm_mode_object **ref); -void drm_property_change_valid_put(struct drm_property *property, - struct drm_mode_object *ref); - -int drm_plane_check_pixel_format(const struct drm_plane *plane, - u32 format); +int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t value); int drm_crtc_check_viewport(const struct drm_crtc *crtc, int x, int y, const struct drm_display_mode *mode, const struct drm_framebuffer *fb); void drm_fb_release(struct drm_file *file_priv); -void drm_property_destroy_user_blobs(struct drm_device *dev, - struct drm_file *file_priv); /* dumb buffer support IOCTLs */ int drm_mode_create_dumb_ioctl(struct drm_device *dev, @@ -64,42 +51,32 @@ int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -/* framebuffer IOCTLs */ -extern int drm_mode_addfb(struct drm_device *dev, - void *data, struct drm_file *file_priv); -extern int drm_mode_addfb2(struct drm_device *dev, - void *data, struct drm_file *file_priv); -int drm_mode_rmfb(struct drm_device *dev, - void *data, struct drm_file *file_priv); -int drm_mode_getfb(struct drm_device *dev, - void *data, struct drm_file *file_priv); -int drm_mode_dirtyfb_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv); - /* IOCTLs */ -int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); - int drm_mode_getresources(struct drm_device *dev, void *data, struct drm_file *file_priv); -int drm_mode_getplane_res(struct drm_device *dev, void *data, - struct drm_file *file_priv); int drm_mode_getcrtc(struct drm_device *dev, void *data, struct drm_file *file_priv); -int drm_mode_getconnector(struct drm_device *dev, - void *data, struct drm_file *file_priv); int drm_mode_setcrtc(struct drm_device *dev, void *data, struct drm_file *file_priv); -int drm_mode_getplane(struct drm_device *dev, - void *data, struct drm_file *file_priv); -int drm_mode_setplane(struct drm_device *dev, - void *data, struct drm_file *file_priv); -int drm_mode_cursor_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv); -int drm_mode_cursor2_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv); + +/* drm_color_mgmt.c */ + +/* IOCTLs */ +int drm_mode_gamma_get_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_gamma_set_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); + +/* drm_property.c */ +void drm_property_destroy_user_blobs(struct drm_device *dev, + struct drm_file *file_priv); +bool drm_property_change_valid_get(struct drm_property *property, + uint64_t value, + struct drm_mode_object **ref); +void drm_property_change_valid_put(struct drm_property *property, + struct drm_mode_object *ref); + +/* IOCTL */ int drm_mode_getproperty_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_mode_getblob_ioctl(struct drm_device *dev, @@ -108,17 +85,80 @@ int drm_mode_createblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_mode_destroyblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -int drm_mode_connector_property_set_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv); + +/* drm_mode_object.c */ +int drm_mode_object_get_reg(struct drm_device *dev, + struct drm_mode_object *obj, + uint32_t obj_type, + bool register_obj, + void (*obj_free_cb)(struct kref *kref)); +void drm_mode_object_register(struct drm_device *dev, + struct drm_mode_object *obj); +int drm_mode_object_get(struct drm_device *dev, + struct drm_mode_object *obj, uint32_t obj_type); +struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev, + uint32_t id, uint32_t type); +void drm_mode_object_unregister(struct drm_device *dev, + struct drm_mode_object *object); +int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic, + uint32_t __user *prop_ptr, + uint64_t __user *prop_values, + uint32_t *arg_count_props); +struct drm_property *drm_mode_obj_find_prop_id(struct drm_mode_object *obj, + uint32_t prop_id); + +/* IOCTL */ + +int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +/* drm_encoder.c */ +int drm_encoder_register_all(struct drm_device *dev); +void drm_encoder_unregister_all(struct drm_device *dev); + +/* IOCTL */ int drm_mode_getencoder(struct drm_device *dev, void *data, struct drm_file *file_priv); -int drm_mode_gamma_get_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv); -int drm_mode_gamma_set_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv); -int drm_mode_page_flip_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv); +/* drm_connector.c */ +void drm_connector_ida_init(void); +void drm_connector_ida_destroy(void); +void drm_connector_unregister_all(struct drm_device *dev); +int drm_connector_register_all(struct drm_device *dev); +int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t value); +int drm_connector_create_standard_properties(struct drm_device *dev); + +/* IOCTL */ +int drm_mode_connector_property_set_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_getconnector(struct drm_device *dev, + void *data, struct drm_file *file_priv); + +/* drm_framebuffer.c */ +struct drm_framebuffer * +drm_internal_framebuffer_create(struct drm_device *dev, + const struct drm_mode_fb_cmd2 *r, + struct drm_file *file_priv); +void drm_framebuffer_free(struct kref *kref); +int drm_framebuffer_check_src_coords(uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h, + const struct drm_framebuffer *fb); + +/* IOCTL */ +int drm_mode_addfb(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_addfb2(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_rmfb(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_getfb(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_dirtyfb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); /* drm_atomic.c */ int drm_atomic_get_property(struct drm_mode_object *obj, @@ -129,6 +169,23 @@ int drm_mode_atomic_ioctl(struct drm_device *dev, int drm_modeset_register_all(struct drm_device *dev); void drm_modeset_unregister_all(struct drm_device *dev); -/* drm_blend.c */ -int drm_atomic_helper_normalize_zpos(struct drm_device *dev, - struct drm_atomic_state *state); + +/* drm_plane.c */ +int drm_plane_register_all(struct drm_device *dev); +void drm_plane_unregister_all(struct drm_device *dev); +int drm_plane_check_pixel_format(const struct drm_plane *plane, + u32 format); + +/* IOCTL */ +int drm_mode_getplane_res(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_mode_getplane(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_setplane(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_cursor_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_cursor2_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); +int drm_mode_page_flip_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); diff --git a/drivers/gpu/drm/drm_dma.c b/drivers/gpu/drm/drm_dma.c index ea48180..3f83e2c 100644 --- a/drivers/gpu/drm/drm_dma.c +++ b/drivers/gpu/drm/drm_dma.c @@ -50,9 +50,8 @@ int drm_legacy_dma_setup(struct drm_device *dev) int i; if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA) || - drm_core_check_feature(dev, DRIVER_MODESET)) { + !drm_core_check_feature(dev, DRIVER_LEGACY)) return 0; - } dev->buf_use = 0; atomic_set(&dev->buf_alloc, 0); @@ -81,9 +80,8 @@ void drm_legacy_dma_takedown(struct drm_device *dev) int i, j; if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA) || - drm_core_check_feature(dev, DRIVER_MODESET)) { + !drm_core_check_feature(dev, DRIVER_LEGACY)) return; - } if (!dma) return; diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c b/drivers/gpu/drm/drm_dp_aux_dev.c index 734f86a..ec1ed94 100644 --- a/drivers/gpu/drm/drm_dp_aux_dev.c +++ b/drivers/gpu/drm/drm_dp_aux_dev.c @@ -36,6 +36,8 @@ #include <drm/drm_crtc.h> #include <drm/drmP.h> +#include "drm_crtc_helper_internal.h" + struct drm_dp_aux_dev { unsigned index; struct drm_dp_aux *aux; @@ -283,12 +285,7 @@ static int auxdev_wait_atomic_t(atomic_t *p) schedule(); return 0; } -/** - * drm_dp_aux_unregister_devnode() - unregister a devnode for this aux channel - * @aux: DisplayPort AUX channel - * - * Returns 0 on success or a negative error code on failure. - */ + void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux) { struct drm_dp_aux_dev *aux_dev; @@ -314,14 +311,7 @@ void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux) DRM_DEBUG("drm_dp_aux_dev: aux [%s] unregistering\n", aux->name); kref_put(&aux_dev->refcount, release_drm_dp_aux_dev); } -EXPORT_SYMBOL(drm_dp_aux_unregister_devnode); -/** - * drm_dp_aux_register_devnode() - register a devnode for this aux channel - * @aux: DisplayPort AUX channel - * - * Returns 0 on success or a negative error code on failure. - */ int drm_dp_aux_register_devnode(struct drm_dp_aux *aux) { struct drm_dp_aux_dev *aux_dev; @@ -347,7 +337,6 @@ error: drm_dp_aux_unregister_devnode(aux); return res; } -EXPORT_SYMBOL(drm_dp_aux_register_devnode); int drm_dp_aux_dev_init(void) { @@ -369,11 +358,9 @@ out: class_destroy(drm_dp_aux_dev_class); return res; } -EXPORT_SYMBOL(drm_dp_aux_dev_init); void drm_dp_aux_dev_exit(void) { unregister_chrdev(drm_dev_major, "aux"); class_destroy(drm_dp_aux_dev_class); } -EXPORT_SYMBOL(drm_dp_aux_dev_exit); diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index eae5ef9..ac3924a 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -27,10 +27,12 @@ #include <linux/errno.h> #include <linux/sched.h> #include <linux/i2c.h> +#include <linux/seq_file.h> #include <drm/drm_dp_helper.h> -#include <drm/drm_dp_aux_dev.h> #include <drm/drmP.h> +#include "drm_crtc_helper_internal.h" + /** * DOC: dp helpers * @@ -223,7 +225,7 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, err = ret; } - DRM_DEBUG_KMS("too many retries, giving up\n"); + DRM_DEBUG_KMS("Too many retries, giving up. First error: %d\n", err); ret = err; unlock: @@ -438,6 +440,179 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) } EXPORT_SYMBOL(drm_dp_link_configure); +/** + * drm_dp_downstream_max_clock() - extract branch device max + * pixel rate for legacy VGA + * converter or max TMDS clock + * rate for others + * @dpcd: DisplayPort configuration data + * @port_cap: port capabilities + * + * Returns max clock in kHz on success or 0 if max clock not defined + */ +int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4]) +{ + int type = port_cap[0] & DP_DS_PORT_TYPE_MASK; + bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] & + DP_DETAILED_CAP_INFO_AVAILABLE; + + if (!detailed_cap_info) + return 0; + + switch (type) { + case DP_DS_PORT_TYPE_VGA: + return port_cap[1] * 8 * 1000; + case DP_DS_PORT_TYPE_DVI: + case DP_DS_PORT_TYPE_HDMI: + case DP_DS_PORT_TYPE_DP_DUALMODE: + return port_cap[1] * 2500; + default: + return 0; + } +} +EXPORT_SYMBOL(drm_dp_downstream_max_clock); + +/** + * drm_dp_downstream_max_bpc() - extract branch device max + * bits per component + * @dpcd: DisplayPort configuration data + * @port_cap: port capabilities + * + * Returns max bpc on success or 0 if max bpc not defined + */ +int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4]) +{ + int type = port_cap[0] & DP_DS_PORT_TYPE_MASK; + bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] & + DP_DETAILED_CAP_INFO_AVAILABLE; + int bpc; + + if (!detailed_cap_info) + return 0; + + switch (type) { + case DP_DS_PORT_TYPE_VGA: + case DP_DS_PORT_TYPE_DVI: + case DP_DS_PORT_TYPE_HDMI: + case DP_DS_PORT_TYPE_DP_DUALMODE: + bpc = port_cap[2] & DP_DS_MAX_BPC_MASK; + + switch (bpc) { + case DP_DS_8BPC: + return 8; + case DP_DS_10BPC: + return 10; + case DP_DS_12BPC: + return 12; + case DP_DS_16BPC: + return 16; + } + default: + return 0; + } +} +EXPORT_SYMBOL(drm_dp_downstream_max_bpc); + +/** + * drm_dp_downstream_id() - identify branch device + * @aux: DisplayPort AUX channel + * @id: DisplayPort branch device id + * + * Returns branch device id on success or NULL on failure + */ +int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6]) +{ + return drm_dp_dpcd_read(aux, DP_BRANCH_ID, id, 6); +} +EXPORT_SYMBOL(drm_dp_downstream_id); + +/** + * drm_dp_downstream_debug() - debug DP branch devices + * @m: pointer for debugfs file + * @dpcd: DisplayPort configuration data + * @port_cap: port capabilities + * @aux: DisplayPort AUX channel + * + */ +void drm_dp_downstream_debug(struct seq_file *m, + const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4], struct drm_dp_aux *aux) +{ + bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] & + DP_DETAILED_CAP_INFO_AVAILABLE; + int clk; + int bpc; + char id[6]; + int len; + uint8_t rev[2]; + int type = port_cap[0] & DP_DS_PORT_TYPE_MASK; + bool branch_device = dpcd[DP_DOWNSTREAMPORT_PRESENT] & + DP_DWN_STRM_PORT_PRESENT; + + seq_printf(m, "\tDP branch device present: %s\n", + branch_device ? "yes" : "no"); + + if (!branch_device) + return; + + switch (type) { + case DP_DS_PORT_TYPE_DP: + seq_puts(m, "\t\tType: DisplayPort\n"); + break; + case DP_DS_PORT_TYPE_VGA: + seq_puts(m, "\t\tType: VGA\n"); + break; + case DP_DS_PORT_TYPE_DVI: + seq_puts(m, "\t\tType: DVI\n"); + break; + case DP_DS_PORT_TYPE_HDMI: + seq_puts(m, "\t\tType: HDMI\n"); + break; + case DP_DS_PORT_TYPE_NON_EDID: + seq_puts(m, "\t\tType: others without EDID support\n"); + break; + case DP_DS_PORT_TYPE_DP_DUALMODE: + seq_puts(m, "\t\tType: DP++\n"); + break; + case DP_DS_PORT_TYPE_WIRELESS: + seq_puts(m, "\t\tType: Wireless\n"); + break; + default: + seq_puts(m, "\t\tType: N/A\n"); + } + + drm_dp_downstream_id(aux, id); + seq_printf(m, "\t\tID: %s\n", id); + + len = drm_dp_dpcd_read(aux, DP_BRANCH_HW_REV, &rev[0], 1); + if (len > 0) + seq_printf(m, "\t\tHW: %d.%d\n", + (rev[0] & 0xf0) >> 4, rev[0] & 0xf); + + len = drm_dp_dpcd_read(aux, DP_BRANCH_SW_REV, &rev, 2); + if (len > 0) + seq_printf(m, "\t\tSW: %d.%d\n", rev[0], rev[1]); + + if (detailed_cap_info) { + clk = drm_dp_downstream_max_clock(dpcd, port_cap); + + if (clk > 0) { + if (type == DP_DS_PORT_TYPE_VGA) + seq_printf(m, "\t\tMax dot clock: %d kHz\n", clk); + else + seq_printf(m, "\t\tMax TMDS clock: %d kHz\n", clk); + } + + bpc = drm_dp_downstream_max_bpc(dpcd, port_cap); + + if (bpc > 0) + seq_printf(m, "\t\tMax bpc: %d\n", bpc); + } +} +EXPORT_SYMBOL(drm_dp_downstream_debug); + /* * I2C-over-AUX implementation */ @@ -574,7 +749,17 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) if (ret == -EBUSY) continue; - DRM_DEBUG_KMS("transaction failed: %d\n", ret); + /* + * While timeouts can be errors, they're usually normal + * behavior (for instance, when a driver tries to + * communicate with a non-existant DisplayPort device). + * Avoid spamming the kernel log with timeout errors. + */ + if (ret == -ETIMEDOUT) + DRM_DEBUG_KMS_RATELIMITED("transaction timed out\n"); + else + DRM_DEBUG_KMS("transaction failed: %d\n", ret); + return ret; } diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index be27ed3..80c7f25 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -33,7 +33,6 @@ #include <linux/mount.h> #include <linux/slab.h> #include <drm/drmP.h> -#include <drm/drm_core.h> #include "drm_crtc_internal.h" #include "drm_legacy.h" #include "drm_internal.h" @@ -46,8 +45,8 @@ unsigned int drm_debug = 0; EXPORT_SYMBOL(drm_debug); -MODULE_AUTHOR(CORE_AUTHOR); -MODULE_DESCRIPTION(CORE_DESC); +MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon Smirl"); +MODULE_DESCRIPTION("DRM shared core routines"); MODULE_LICENSE("GPL and additional rights"); MODULE_PARM_DESC(debug, "Enable debug output, where each bit enables a debug category.\n" "\t\tBit 0 (0x01) will enable CORE messages (drm core code)\n" @@ -63,37 +62,51 @@ static struct idr drm_minors_idr; static struct dentry *drm_debugfs_root; -void drm_err(const char *format, ...) +#define DRM_PRINTK_FMT "[" DRM_NAME ":%s]%s %pV" + +void drm_dev_printk(const struct device *dev, const char *level, + unsigned int category, const char *function_name, + const char *prefix, const char *format, ...) { struct va_format vaf; va_list args; - va_start(args, format); + if (category != DRM_UT_NONE && !(drm_debug & category)) + return; + va_start(args, format); vaf.fmt = format; vaf.va = &args; - printk(KERN_ERR "[" DRM_NAME ":%ps] *ERROR* %pV", - __builtin_return_address(0), &vaf); + if (dev) + dev_printk(level, dev, DRM_PRINTK_FMT, function_name, prefix, + &vaf); + else + printk("%s" DRM_PRINTK_FMT, level, function_name, prefix, &vaf); va_end(args); } -EXPORT_SYMBOL(drm_err); +EXPORT_SYMBOL(drm_dev_printk); -void drm_ut_debug_printk(const char *function_name, const char *format, ...) +void drm_printk(const char *level, unsigned int category, + const char *function_name, const char *prefix, + const char *format, ...) { struct va_format vaf; va_list args; + if (category != DRM_UT_NONE && !(drm_debug & category)) + return; + va_start(args, format); vaf.fmt = format; vaf.va = &args; - printk(KERN_DEBUG "[" DRM_NAME ":%s] %pV", function_name, &vaf); + printk("%s" DRM_PRINTK_FMT, level, function_name, prefix, &vaf); va_end(args); } -EXPORT_SYMBOL(drm_ut_debug_printk); +EXPORT_SYMBOL(drm_printk); /* * DRM Minors @@ -112,7 +125,7 @@ static struct drm_minor **drm_minor_get_slot(struct drm_device *dev, unsigned int type) { switch (type) { - case DRM_MINOR_LEGACY: + case DRM_MINOR_PRIMARY: return &dev->primary; case DRM_MINOR_RENDER: return &dev->render; @@ -325,6 +338,9 @@ void drm_minor_release(struct drm_minor *minor) static int drm_dev_set_unique(struct drm_device *dev, const char *name) { + if (!name) + return -EINVAL; + kfree(dev->unique); dev->unique = kstrdup(name, GFP_KERNEL); @@ -512,7 +528,7 @@ int drm_dev_init(struct drm_device *dev, goto err_minors; } - ret = drm_minor_alloc(dev, DRM_MINOR_LEGACY); + ret = drm_minor_alloc(dev, DRM_MINOR_PRIMARY); if (ret) goto err_minors; @@ -545,7 +561,7 @@ err_ctxbitmap: drm_legacy_ctxbitmap_cleanup(dev); drm_ht_remove(&dev->map_hash); err_minors: - drm_minor_free(dev, DRM_MINOR_LEGACY); + drm_minor_free(dev, DRM_MINOR_PRIMARY); drm_minor_free(dev, DRM_MINOR_RENDER); drm_minor_free(dev, DRM_MINOR_CONTROL); drm_fs_inode_free(dev->anon_inode); @@ -575,7 +591,7 @@ EXPORT_SYMBOL(drm_dev_init); * own struct should look at using drm_dev_init() instead. * * RETURNS: - * Pointer to new DRM device, or NULL if out of memory. + * Pointer to new DRM device, or ERR_PTR on failure. */ struct drm_device *drm_dev_alloc(struct drm_driver *driver, struct device *parent) @@ -585,12 +601,12 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) - return NULL; + return ERR_PTR(-ENOMEM); ret = drm_dev_init(dev, driver, parent); if (ret) { kfree(dev); - return NULL; + return ERR_PTR(ret); } return dev; @@ -608,7 +624,7 @@ static void drm_dev_release(struct kref *ref) drm_ht_remove(&dev->map_hash); drm_fs_inode_free(dev->anon_inode); - drm_minor_free(dev, DRM_MINOR_LEGACY); + drm_minor_free(dev, DRM_MINOR_PRIMARY); drm_minor_free(dev, DRM_MINOR_RENDER); drm_minor_free(dev, DRM_MINOR_CONTROL); @@ -684,7 +700,7 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) if (ret) goto err_minors; - ret = drm_minor_register(dev, DRM_MINOR_LEGACY); + ret = drm_minor_register(dev, DRM_MINOR_PRIMARY); if (ret) goto err_minors; @@ -701,7 +717,7 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) goto out_unlock; err_minors: - drm_minor_unregister(dev, DRM_MINOR_LEGACY); + drm_minor_unregister(dev, DRM_MINOR_PRIMARY); drm_minor_unregister(dev, DRM_MINOR_RENDER); drm_minor_unregister(dev, DRM_MINOR_CONTROL); out_unlock: @@ -741,7 +757,7 @@ void drm_dev_unregister(struct drm_device *dev) list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) drm_legacy_rmmap(dev, r_list->map); - drm_minor_unregister(dev, DRM_MINOR_LEGACY); + drm_minor_unregister(dev, DRM_MINOR_PRIMARY); drm_minor_unregister(dev, DRM_MINOR_RENDER); drm_minor_unregister(dev, DRM_MINOR_CONTROL); } @@ -807,53 +823,48 @@ static const struct file_operations drm_stub_fops = { .llseek = noop_llseek, }; +static void drm_core_exit(void) +{ + unregister_chrdev(DRM_MAJOR, "drm"); + debugfs_remove(drm_debugfs_root); + drm_sysfs_destroy(); + idr_destroy(&drm_minors_idr); + drm_connector_ida_destroy(); + drm_global_release(); +} + static int __init drm_core_init(void) { - int ret = -ENOMEM; + int ret; drm_global_init(); drm_connector_ida_init(); idr_init(&drm_minors_idr); - if (register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops)) - goto err_p1; - ret = drm_sysfs_init(); if (ret < 0) { - printk(KERN_ERR "DRM: Error creating drm class.\n"); - goto err_p2; + DRM_ERROR("Cannot create DRM class: %d\n", ret); + goto error; } drm_debugfs_root = debugfs_create_dir("dri", NULL); if (!drm_debugfs_root) { - DRM_ERROR("Cannot create /sys/kernel/debug/dri\n"); - ret = -1; - goto err_p3; + ret = -ENOMEM; + DRM_ERROR("Cannot create debugfs-root: %d\n", ret); + goto error; } - DRM_INFO("Initialized %s %d.%d.%d %s\n", - CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE); + ret = register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops); + if (ret < 0) + goto error; + + DRM_INFO("Initialized\n"); return 0; -err_p3: - drm_sysfs_destroy(); -err_p2: - unregister_chrdev(DRM_MAJOR, "drm"); - idr_destroy(&drm_minors_idr); -err_p1: +error: + drm_core_exit(); return ret; } -static void __exit drm_core_exit(void) -{ - debugfs_remove(drm_debugfs_root); - drm_sysfs_destroy(); - - unregister_chrdev(DRM_MAJOR, "drm"); - - drm_connector_ida_destroy(); - idr_destroy(&drm_minors_idr); -} - module_init(drm_core_init); module_exit(drm_core_exit); diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 7df26d4..5054132 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -74,6 +74,8 @@ #define EDID_QUIRK_FORCE_8BPC (1 << 8) /* Force 12bpc */ #define EDID_QUIRK_FORCE_12BPC (1 << 9) +/* Force 6bpc */ +#define EDID_QUIRK_FORCE_6BPC (1 << 10) struct detailed_mode_closure { struct drm_connector *connector; @@ -100,6 +102,9 @@ static struct edid_quirk { /* Unknown Acer */ { "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, + /* AEO model 0 reports 8 bpc, but is a 6 bpc panel */ + { "AEO", 0, EDID_QUIRK_FORCE_6BPC }, + /* Belinea 10 15 55 */ { "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 }, { "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 }, @@ -986,7 +991,7 @@ static const struct drm_display_mode edid_cea_modes[] = { .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 64 - 1920x1080@100Hz */ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448, - 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, + 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, }; @@ -3716,14 +3721,7 @@ bool drm_rgb_quant_range_selectable(struct edid *edid) } EXPORT_SYMBOL(drm_rgb_quant_range_selectable); -/** - * drm_assign_hdmi_deep_color_info - detect whether monitor supports - * hdmi deep color modes and update drm_display_info if so. - * @edid: monitor EDID information - * @info: Updated with maximum supported deep color bpc and color format - * if deep color supported. - * @connector: DRM connector, used only for debug output - * +/* * Parse the CEA extension according to CEA-861-B. * Return true if HDMI deep color supported, false if not or unknown. */ @@ -3817,16 +3815,6 @@ static bool drm_assign_hdmi_deep_color_info(struct edid *edid, return false; } -/** - * drm_add_display_info - pull display info out if present - * @edid: EDID data - * @info: display info (attached to connector) - * @connector: connector whose edid is used to build display info - * - * Grab any available display info and stuff it into the drm_display_info - * structure that's part of the connector. Useful for tracking bpp and - * color spaces. - */ static void drm_add_display_info(struct edid *edid, struct drm_display_info *info, struct drm_connector *connector) @@ -3862,6 +3850,20 @@ static void drm_add_display_info(struct edid *edid, /* HDMI deep color modes supported? Assign to info, if so */ drm_assign_hdmi_deep_color_info(edid, info, connector); + /* + * Digital sink with "DFP 1.x compliant TMDS" according to EDID 1.3? + * + * For such displays, the DFP spec 1.0, section 3.10 "EDID support" + * tells us to assume 8 bpc color depth if the EDID doesn't have + * extensions which tell otherwise. + */ + if ((info->bpc == 0) && (edid->revision < 4) && + (edid->input & DRM_EDID_DIGITAL_TYPE_DVI)) { + info->bpc = 8; + DRM_DEBUG("%s: Assigning DFP sink color depth as %d bpc.\n", + connector->name, info->bpc); + } + /* Only defined for 1.4 with digital displays */ if (edid->revision < 4) return; @@ -4033,7 +4035,9 @@ static int add_displayid_detailed_modes(struct drm_connector *connector, * @connector: connector we're probing * @edid: EDID data * - * Add the specified modes to the connector's mode list. + * Add the specified modes to the connector's mode list. Also fills out the + * &drm_display_info structure in @connector with any information which can be + * derived from the edid. * * Return: The number of modes added or 0 if we couldn't find any. */ @@ -4082,6 +4086,9 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) drm_add_display_info(edid, &connector->display_info, connector); + if (quirks & EDID_QUIRK_FORCE_6BPC) + connector->display_info.bpc = 6; + if (quirks & EDID_QUIRK_FORCE_8BPC) connector->display_info.bpc = 8; diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c new file mode 100644 index 0000000..5c06771 --- /dev/null +++ b/drivers/gpu/drm/drm_encoder.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <linux/export.h> +#include <drm/drmP.h> +#include <drm/drm_encoder.h> + +#include "drm_crtc_internal.h" + +/** + * DOC: overview + * + * Encoders represent the connecting element between the CRTC (as the overall + * pixel pipeline, represented by struct &drm_crtc) and the connectors (as the + * generic sink entity, represented by struct &drm_connector). An encoder takes + * pixel data from a CRTC and converts it to a format suitable for any attached + * connector. Encoders are objects exposed to userspace, originally to allow + * userspace to infer cloning and connector/CRTC restrictions. Unfortunately + * almost all drivers get this wrong, making the uabi pretty much useless. On + * top of that the exposed restrictions are too simple for today's hardware, and + * the recommended way to infer restrictions is by using the + * DRM_MODE_ATOMIC_TEST_ONLY flag for the atomic IOCTL. + * + * Otherwise encoders aren't used in the uapi at all (any modeset request from + * userspace directly connects a connector with a CRTC), drivers are therefore + * free to use them however they wish. Modeset helper libraries make strong use + * of encoders to facilitate code sharing. But for more complex settings it is + * usually better to move shared code into a separate &drm_bridge. Compared to + * encoders, bridges also have the benefit of being purely an internal + * abstraction since they are not exposed to userspace at all. + * + * Encoders are initialized with drm_encoder_init() and cleaned up using + * drm_encoder_cleanup(). + */ +static const struct drm_prop_enum_list drm_encoder_enum_list[] = { + { DRM_MODE_ENCODER_NONE, "None" }, + { DRM_MODE_ENCODER_DAC, "DAC" }, + { DRM_MODE_ENCODER_TMDS, "TMDS" }, + { DRM_MODE_ENCODER_LVDS, "LVDS" }, + { DRM_MODE_ENCODER_TVDAC, "TV" }, + { DRM_MODE_ENCODER_VIRTUAL, "Virtual" }, + { DRM_MODE_ENCODER_DSI, "DSI" }, + { DRM_MODE_ENCODER_DPMST, "DP MST" }, + { DRM_MODE_ENCODER_DPI, "DPI" }, +}; + +int drm_encoder_register_all(struct drm_device *dev) +{ + struct drm_encoder *encoder; + int ret = 0; + + drm_for_each_encoder(encoder, dev) { + if (encoder->funcs->late_register) + ret = encoder->funcs->late_register(encoder); + if (ret) + return ret; + } + + return 0; +} + +void drm_encoder_unregister_all(struct drm_device *dev) +{ + struct drm_encoder *encoder; + + drm_for_each_encoder(encoder, dev) { + if (encoder->funcs->early_unregister) + encoder->funcs->early_unregister(encoder); + } +} + +/** + * drm_encoder_init - Init a preallocated encoder + * @dev: drm device + * @encoder: the encoder to init + * @funcs: callbacks for this encoder + * @encoder_type: user visible type of the encoder + * @name: printf style format string for the encoder name, or NULL for default name + * + * Initialises a preallocated encoder. Encoder should be subclassed as part of + * driver encoder objects. At driver unload time drm_encoder_cleanup() should be + * called from the driver's destroy hook in &drm_encoder_funcs. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_encoder_init(struct drm_device *dev, + struct drm_encoder *encoder, + const struct drm_encoder_funcs *funcs, + int encoder_type, const char *name, ...) +{ + int ret; + + drm_modeset_lock_all(dev); + + ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); + if (ret) + goto out_unlock; + + encoder->dev = dev; + encoder->encoder_type = encoder_type; + encoder->funcs = funcs; + if (name) { + va_list ap; + + va_start(ap, name); + encoder->name = kvasprintf(GFP_KERNEL, name, ap); + va_end(ap); + } else { + encoder->name = kasprintf(GFP_KERNEL, "%s-%d", + drm_encoder_enum_list[encoder_type].name, + encoder->base.id); + } + if (!encoder->name) { + ret = -ENOMEM; + goto out_put; + } + + list_add_tail(&encoder->head, &dev->mode_config.encoder_list); + encoder->index = dev->mode_config.num_encoder++; + +out_put: + if (ret) + drm_mode_object_unregister(dev, &encoder->base); + +out_unlock: + drm_modeset_unlock_all(dev); + + return ret; +} +EXPORT_SYMBOL(drm_encoder_init); + +/** + * drm_encoder_cleanup - cleans up an initialised encoder + * @encoder: encoder to cleanup + * + * Cleans up the encoder but doesn't free the object. + */ +void drm_encoder_cleanup(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + + /* Note that the encoder_list is considered to be static; should we + * remove the drm_encoder at runtime we would have to decrement all + * the indices on the drm_encoder after us in the encoder_list. + */ + + drm_modeset_lock_all(dev); + drm_mode_object_unregister(dev, &encoder->base); + kfree(encoder->name); + list_del(&encoder->head); + dev->mode_config.num_encoder--; + drm_modeset_unlock_all(dev); + + memset(encoder, 0, sizeof(*encoder)); +} +EXPORT_SYMBOL(drm_encoder_cleanup); + +static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder) +{ + struct drm_connector *connector; + struct drm_device *dev = encoder->dev; + bool uses_atomic = false; + + /* For atomic drivers only state objects are synchronously updated and + * protected by modeset locks, so check those first. */ + drm_for_each_connector(connector, dev) { + if (!connector->state) + continue; + + uses_atomic = true; + + if (connector->state->best_encoder != encoder) + continue; + + return connector->state->crtc; + } + + /* Don't return stale data (e.g. pending async disable). */ + if (uses_atomic) + return NULL; + + return encoder->crtc; +} + +int drm_mode_getencoder(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_mode_get_encoder *enc_resp = data; + struct drm_encoder *encoder; + struct drm_crtc *crtc; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + encoder = drm_encoder_find(dev, enc_resp->encoder_id); + if (!encoder) + return -ENOENT; + + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + crtc = drm_encoder_get_crtc(encoder); + if (crtc) + enc_resp->crtc_id = crtc->base.id; + else + enc_resp->crtc_id = 0; + drm_modeset_unlock(&dev->mode_config.connection_mutex); + + enc_resp->encoder_type = encoder->encoder_type; + enc_resp->encoder_id = encoder->base.id; + enc_resp->possible_crtcs = encoder->possible_crtcs; + enc_resp->possible_clones = encoder->possible_clones; + + return 0; +} diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index ce54e98..03414bd 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -29,10 +29,10 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/console.h> #include <linux/kernel.h> #include <linux/sysrq.h> #include <linux/slab.h> -#include <linux/fb.h> #include <linux/module.h> #include <drm/drmP.h> #include <drm/drm_crtc.h> @@ -41,6 +41,8 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include "drm_crtc_helper_internal.h" + static bool drm_fbdev_emulation = true; module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600); MODULE_PARM_DESC(fbdev_emulation, @@ -335,7 +337,7 @@ retry: goto fail; } - plane_state->rotation = BIT(DRM_ROTATE_0); + plane_state->rotation = DRM_ROTATE_0; plane->old_fb = plane->fb; plane_mask |= 1 << drm_plane_index(plane); @@ -395,7 +397,7 @@ static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) if (dev->mode_config.rotation_property) { drm_mode_plane_set_obj_prop(plane, dev->mode_config.rotation_property, - BIT(DRM_ROTATE_0)); + DRM_ROTATE_0); } } @@ -464,7 +466,7 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) /* Sometimes user space wants everything disabled, so don't steal the * display if there's a master. */ - if (lockless_dereference(dev->master)) + if (READ_ONCE(dev->master)) return false; drm_for_each_crtc(crtc, dev) { @@ -618,6 +620,16 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) kfree(helper->crtc_info); } +static void drm_fb_helper_resume_worker(struct work_struct *work) +{ + struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper, + resume_work); + + console_lock(); + fb_set_suspend(helper->fbdev, 0); + console_unlock(); +} + static void drm_fb_helper_dirty_work(struct work_struct *work) { struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper, @@ -649,6 +661,7 @@ void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, { INIT_LIST_HEAD(&helper->kernel_fb_list); spin_lock_init(&helper->dirty_lock); + INIT_WORK(&helper->resume_work, drm_fb_helper_resume_worker); INIT_WORK(&helper->dirty_work, drm_fb_helper_dirty_work); helper->dirty_clip.x1 = helper->dirty_clip.y1 = ~0; helper->funcs = funcs; @@ -1024,17 +1037,65 @@ EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit); /** * drm_fb_helper_set_suspend - wrapper around fb_set_suspend * @fb_helper: driver-allocated fbdev helper - * @state: desired state, zero to resume, non-zero to suspend + * @suspend: whether to suspend or resume * - * A wrapper around fb_set_suspend implemented by fbdev core + * A wrapper around fb_set_suspend implemented by fbdev core. + * Use drm_fb_helper_set_suspend_unlocked() if you don't need to take + * the lock yourself */ -void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, int state) +void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend) { if (fb_helper && fb_helper->fbdev) - fb_set_suspend(fb_helper->fbdev, state); + fb_set_suspend(fb_helper->fbdev, suspend); } EXPORT_SYMBOL(drm_fb_helper_set_suspend); +/** + * drm_fb_helper_set_suspend_unlocked - wrapper around fb_set_suspend that also + * takes the console lock + * @fb_helper: driver-allocated fbdev helper + * @suspend: whether to suspend or resume + * + * A wrapper around fb_set_suspend() that takes the console lock. If the lock + * isn't available on resume, a worker is tasked with waiting for the lock + * to become available. The console lock can be pretty contented on resume + * due to all the printk activity. + * + * This function can be called multiple times with the same state since + * &fb_info->state is checked to see if fbdev is running or not before locking. + * + * Use drm_fb_helper_set_suspend() if you need to take the lock yourself. + */ +void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper, + bool suspend) +{ + if (!fb_helper || !fb_helper->fbdev) + return; + + /* make sure there's no pending/ongoing resume */ + flush_work(&fb_helper->resume_work); + + if (suspend) { + if (fb_helper->fbdev->state != FBINFO_STATE_RUNNING) + return; + + console_lock(); + + } else { + if (fb_helper->fbdev->state == FBINFO_STATE_RUNNING) + return; + + if (!console_trylock()) { + schedule_work(&fb_helper->resume_work); + return; + } + } + + fb_set_suspend(fb_helper->fbdev, suspend); + console_unlock(); +} +EXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked); + static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, u16 blue, u16 regno, struct fb_info *info) { @@ -2194,7 +2255,7 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config); * @fb_helper: the drm_fb_helper * * Scan the connectors attached to the fb_helper and try to put together a - * setup after *notification of a change in output configuration. + * setup after notification of a change in output configuration. * * Called at runtime, takes the mode config locks to be able to check/change the * modeset configuration. Must be run from process context (which usually means diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 323c238..e84faec 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -92,7 +92,7 @@ static int drm_setup(struct drm_device * dev) int ret; if (dev->driver->firstopen && - !drm_core_check_feature(dev, DRIVER_MODESET)) { + drm_core_check_feature(dev, DRIVER_LEGACY)) { ret = dev->driver->firstopen(dev); if (ret != 0) return ret; @@ -199,7 +199,6 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) filp->private_data = priv; priv->filp = filp; - priv->uid = current_euid(); priv->pid = get_pid(task_pid(current)); priv->minor = minor; @@ -346,7 +345,7 @@ void drm_lastclose(struct drm_device * dev) dev->driver->lastclose(dev); DRM_DEBUG("driver lastclose completed\n"); - if (!drm_core_check_feature(dev, DRIVER_MODESET)) + if (drm_core_check_feature(dev, DRIVER_LEGACY)) drm_legacy_dev_reinit(dev); } @@ -389,7 +388,7 @@ int drm_release(struct inode *inode, struct file *filp) (long)old_encode_dev(file_priv->minor->kdev->devt), dev->open_count); - if (!drm_core_check_feature(dev, DRIVER_MODESET)) + if (drm_core_check_feature(dev, DRIVER_LEGACY)) drm_legacy_lock_release(dev, filp); if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index 0645c85..29c56b4 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -36,19 +36,60 @@ static char printable_char(int c) } /** + * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description + * @bpp: bits per pixels + * @depth: bit depth per pixel + * + * Computes a drm fourcc pixel format code for the given @bpp/@depth values. + * Useful in fbdev emulation code, since that deals in those values. + */ +uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) +{ + uint32_t fmt; + + switch (bpp) { + case 8: + fmt = DRM_FORMAT_C8; + break; + case 16: + if (depth == 15) + fmt = DRM_FORMAT_XRGB1555; + else + fmt = DRM_FORMAT_RGB565; + break; + case 24: + fmt = DRM_FORMAT_RGB888; + break; + case 32: + if (depth == 24) + fmt = DRM_FORMAT_XRGB8888; + else if (depth == 30) + fmt = DRM_FORMAT_XRGB2101010; + else + fmt = DRM_FORMAT_ARGB8888; + break; + default: + DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n"); + fmt = DRM_FORMAT_XRGB8888; + break; + } + + return fmt; +} +EXPORT_SYMBOL(drm_mode_legacy_fb_format); + +/** * drm_get_format_name - return a string for drm fourcc format * @format: format to compute name of * - * Note that the buffer used by this function is globally shared and owned by - * the function itself. - * - * FIXME: This isn't really multithreading safe. + * Note that the buffer returned by this function is owned by the caller + * and will need to be freed using kfree(). */ -const char *drm_get_format_name(uint32_t format) +char *drm_get_format_name(uint32_t format) { - static char buf[32]; + char *buf = kmalloc(32, GFP_KERNEL); - snprintf(buf, sizeof(buf), + snprintf(buf, 32, "%c%c%c%c %s-endian (0x%08x)", printable_char(format & 0xff), printable_char((format >> 8) & 0xff), @@ -73,6 +114,8 @@ EXPORT_SYMBOL(drm_get_format_name); void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, int *bpp) { + char *format_name; + switch (format) { case DRM_FORMAT_C8: case DRM_FORMAT_RGB332: @@ -127,8 +170,9 @@ void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, *bpp = 32; break; default: - DRM_DEBUG_KMS("unsupported pixel format %s\n", - drm_get_format_name(format)); + format_name = drm_get_format_name(format); + DRM_DEBUG_KMS("unsupported pixel format %s\n", format_name); + kfree(format_name); *depth = 0; *bpp = 0; break; diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c new file mode 100644 index 0000000..398efd6 --- /dev/null +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -0,0 +1,857 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <linux/export.h> +#include <drm/drmP.h> +#include <drm/drm_auth.h> +#include <drm/drm_framebuffer.h> + +#include "drm_crtc_internal.h" + +/** + * DOC: overview + * + * Frame buffers are abstract memory objects that provide a source of pixels to + * scanout to a CRTC. Applications explicitly request the creation of frame + * buffers through the DRM_IOCTL_MODE_ADDFB(2) ioctls and receive an opaque + * handle that can be passed to the KMS CRTC control, plane configuration and + * page flip functions. + * + * Frame buffers rely on the underlying memory manager for allocating backing + * storage. When creating a frame buffer applications pass a memory handle + * (or a list of memory handles for multi-planar formats) through the + * struct &drm_mode_fb_cmd2 argument. For drivers using GEM as their userspace + * buffer management interface this would be a GEM handle. Drivers are however + * free to use their own backing storage object handles, e.g. vmwgfx directly + * exposes special TTM handles to userspace and so expects TTM handles in the + * create ioctl and not GEM handles. + * + * Framebuffers are tracked with struct &drm_framebuffer. They are published + * using drm_framebuffer_init() - after calling that function userspace can use + * and access the framebuffer object. The helper function + * drm_helper_mode_fill_fb_struct() can be used to pre-fill the required + * metadata fields. + * + * The lifetime of a drm framebuffer is controlled with a reference count, + * drivers can grab additional references with drm_framebuffer_reference() and + * drop them again with drm_framebuffer_unreference(). For driver-private + * framebuffers for which the last reference is never dropped (e.g. for the + * fbdev framebuffer when the struct struct &drm_framebuffer is embedded into + * the fbdev helper struct) drivers can manually clean up a framebuffer at + * module unload time with drm_framebuffer_unregister_private(). But doing this + * is not recommended, and it's better to have a normal free-standing struct + * &drm_framebuffer. + */ + +int drm_framebuffer_check_src_coords(uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h, + const struct drm_framebuffer *fb) +{ + unsigned int fb_width, fb_height; + + fb_width = fb->width << 16; + fb_height = fb->height << 16; + + /* Make sure source coordinates are inside the fb. */ + if (src_w > fb_width || + src_x > fb_width - src_w || + src_h > fb_height || + src_y > fb_height - src_h) { + DRM_DEBUG_KMS("Invalid source coordinates " + "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", + src_w >> 16, ((src_w & 0xffff) * 15625) >> 10, + src_h >> 16, ((src_h & 0xffff) * 15625) >> 10, + src_x >> 16, ((src_x & 0xffff) * 15625) >> 10, + src_y >> 16, ((src_y & 0xffff) * 15625) >> 10); + return -ENOSPC; + } + + return 0; +} + +/** + * drm_mode_addfb - add an FB to the graphics configuration + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Add a new FB to the specified CRTC, given a user request. This is the + * original addfb ioctl which only supported RGB formats. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_addfb(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_fb_cmd *or = data; + struct drm_mode_fb_cmd2 r = {}; + int ret; + + /* convert to new format and call new ioctl */ + r.fb_id = or->fb_id; + r.width = or->width; + r.height = or->height; + r.pitches[0] = or->pitch; + r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); + r.handles[0] = or->handle; + + ret = drm_mode_addfb2(dev, &r, file_priv); + if (ret) + return ret; + + or->fb_id = r.fb_id; + + return 0; +} + +static int format_check(const struct drm_mode_fb_cmd2 *r) +{ + uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN; + char *format_name; + + switch (format) { + case DRM_FORMAT_C8: + case DRM_FORMAT_RGB332: + case DRM_FORMAT_BGR233: + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_BGRX4444: + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_BGRX5551: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_BGRA5551: + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + case DRM_FORMAT_RGB888: + case DRM_FORMAT_BGR888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_RGBX1010102: + case DRM_FORMAT_BGRX1010102: + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_BGRA1010102: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_AYUV: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + case DRM_FORMAT_NV24: + case DRM_FORMAT_NV42: + case DRM_FORMAT_YUV410: + case DRM_FORMAT_YVU410: + case DRM_FORMAT_YUV411: + case DRM_FORMAT_YVU411: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YVU422: + case DRM_FORMAT_YUV444: + case DRM_FORMAT_YVU444: + return 0; + default: + format_name = drm_get_format_name(r->pixel_format); + DRM_DEBUG_KMS("invalid pixel format %s\n", format_name); + kfree(format_name); + return -EINVAL; + } +} + +static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) +{ + int ret, hsub, vsub, num_planes, i; + + ret = format_check(r); + if (ret) { + char *format_name = drm_get_format_name(r->pixel_format); + DRM_DEBUG_KMS("bad framebuffer format %s\n", format_name); + kfree(format_name); + return ret; + } + + hsub = drm_format_horz_chroma_subsampling(r->pixel_format); + vsub = drm_format_vert_chroma_subsampling(r->pixel_format); + num_planes = drm_format_num_planes(r->pixel_format); + + if (r->width == 0 || r->width % hsub) { + DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width); + return -EINVAL; + } + + if (r->height == 0 || r->height % vsub) { + DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height); + return -EINVAL; + } + + for (i = 0; i < num_planes; i++) { + unsigned int width = r->width / (i != 0 ? hsub : 1); + unsigned int height = r->height / (i != 0 ? vsub : 1); + unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i); + + if (!r->handles[i]) { + DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i); + return -EINVAL; + } + + if ((uint64_t) width * cpp > UINT_MAX) + return -ERANGE; + + if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) + return -ERANGE; + + if (r->pitches[i] < width * cpp) { + DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); + return -EINVAL; + } + + if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) { + DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n", + r->modifier[i], i); + return -EINVAL; + } + + /* modifier specific checks: */ + switch (r->modifier[i]) { + case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE: + /* NOTE: the pitch restriction may be lifted later if it turns + * out that no hw has this restriction: + */ + if (r->pixel_format != DRM_FORMAT_NV12 || + width % 128 || height % 32 || + r->pitches[i] % 128) { + DRM_DEBUG_KMS("bad modifier data for plane %d\n", i); + return -EINVAL; + } + break; + + default: + break; + } + } + + for (i = num_planes; i < 4; i++) { + if (r->modifier[i]) { + DRM_DEBUG_KMS("non-zero modifier for unused plane %d\n", i); + return -EINVAL; + } + + /* Pre-FB_MODIFIERS userspace didn't clear the structs properly. */ + if (!(r->flags & DRM_MODE_FB_MODIFIERS)) + continue; + + if (r->handles[i]) { + DRM_DEBUG_KMS("buffer object handle for unused plane %d\n", i); + return -EINVAL; + } + + if (r->pitches[i]) { + DRM_DEBUG_KMS("non-zero pitch for unused plane %d\n", i); + return -EINVAL; + } + + if (r->offsets[i]) { + DRM_DEBUG_KMS("non-zero offset for unused plane %d\n", i); + return -EINVAL; + } + } + + return 0; +} + +struct drm_framebuffer * +drm_internal_framebuffer_create(struct drm_device *dev, + const struct drm_mode_fb_cmd2 *r, + struct drm_file *file_priv) +{ + struct drm_mode_config *config = &dev->mode_config; + struct drm_framebuffer *fb; + int ret; + + if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) { + DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags); + return ERR_PTR(-EINVAL); + } + + if ((config->min_width > r->width) || (r->width > config->max_width)) { + DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n", + r->width, config->min_width, config->max_width); + return ERR_PTR(-EINVAL); + } + if ((config->min_height > r->height) || (r->height > config->max_height)) { + DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n", + r->height, config->min_height, config->max_height); + return ERR_PTR(-EINVAL); + } + + if (r->flags & DRM_MODE_FB_MODIFIERS && + !dev->mode_config.allow_fb_modifiers) { + DRM_DEBUG_KMS("driver does not support fb modifiers\n"); + return ERR_PTR(-EINVAL); + } + + ret = framebuffer_check(r); + if (ret) + return ERR_PTR(ret); + + fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); + if (IS_ERR(fb)) { + DRM_DEBUG_KMS("could not create framebuffer\n"); + return fb; + } + + return fb; +} + +/** + * drm_mode_addfb2 - add an FB to the graphics configuration + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Add a new FB to the specified CRTC, given a user request with format. This is + * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers + * and uses fourcc codes as pixel format specifiers. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_addfb2(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_fb_cmd2 *r = data; + struct drm_framebuffer *fb; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + fb = drm_internal_framebuffer_create(dev, r, file_priv); + if (IS_ERR(fb)) + return PTR_ERR(fb); + + DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); + r->fb_id = fb->base.id; + + /* Transfer ownership to the filp for reaping on close */ + mutex_lock(&file_priv->fbs_lock); + list_add(&fb->filp_head, &file_priv->fbs); + mutex_unlock(&file_priv->fbs_lock); + + return 0; +} + +struct drm_mode_rmfb_work { + struct work_struct work; + struct list_head fbs; +}; + +static void drm_mode_rmfb_work_fn(struct work_struct *w) +{ + struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work); + + while (!list_empty(&arg->fbs)) { + struct drm_framebuffer *fb = + list_first_entry(&arg->fbs, typeof(*fb), filp_head); + + list_del_init(&fb->filp_head); + drm_framebuffer_remove(fb); + } +} + +/** + * drm_mode_rmfb - remove an FB from the configuration + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Remove the FB specified by the user. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_rmfb(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_framebuffer *fb = NULL; + struct drm_framebuffer *fbl = NULL; + uint32_t *id = data; + int found = 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + fb = drm_framebuffer_lookup(dev, *id); + if (!fb) + return -ENOENT; + + mutex_lock(&file_priv->fbs_lock); + list_for_each_entry(fbl, &file_priv->fbs, filp_head) + if (fb == fbl) + found = 1; + if (!found) { + mutex_unlock(&file_priv->fbs_lock); + goto fail_unref; + } + + list_del_init(&fb->filp_head); + mutex_unlock(&file_priv->fbs_lock); + + /* drop the reference we picked up in framebuffer lookup */ + drm_framebuffer_unreference(fb); + + /* + * we now own the reference that was stored in the fbs list + * + * drm_framebuffer_remove may fail with -EINTR on pending signals, + * so run this in a separate stack as there's no way to correctly + * handle this after the fb is already removed from the lookup table. + */ + if (drm_framebuffer_read_refcount(fb) > 1) { + struct drm_mode_rmfb_work arg; + + INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn); + INIT_LIST_HEAD(&arg.fbs); + list_add_tail(&fb->filp_head, &arg.fbs); + + schedule_work(&arg.work); + flush_work(&arg.work); + destroy_work_on_stack(&arg.work); + } else + drm_framebuffer_unreference(fb); + + return 0; + +fail_unref: + drm_framebuffer_unreference(fb); + return -ENOENT; +} + +/** + * drm_mode_getfb - get FB info + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Lookup the FB given its ID and return info about it. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_getfb(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_fb_cmd *r = data; + struct drm_framebuffer *fb; + int ret; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + fb = drm_framebuffer_lookup(dev, r->fb_id); + if (!fb) + return -ENOENT; + + r->height = fb->height; + r->width = fb->width; + r->depth = fb->depth; + r->bpp = fb->bits_per_pixel; + r->pitch = fb->pitches[0]; + if (fb->funcs->create_handle) { + if (drm_is_current_master(file_priv) || capable(CAP_SYS_ADMIN) || + drm_is_control_client(file_priv)) { + ret = fb->funcs->create_handle(fb, file_priv, + &r->handle); + } else { + /* GET_FB() is an unprivileged ioctl so we must not + * return a buffer-handle to non-master processes! For + * backwards-compatibility reasons, we cannot make + * GET_FB() privileged, so just return an invalid handle + * for non-masters. */ + r->handle = 0; + ret = 0; + } + } else { + ret = -ENODEV; + } + + drm_framebuffer_unreference(fb); + + return ret; +} + +/** + * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Lookup the FB and flush out the damaged area supplied by userspace as a clip + * rectangle list. Generic userspace which does frontbuffer rendering must call + * this ioctl to flush out the changes on manual-update display outputs, e.g. + * usb display-link, mipi manual update panels or edp panel self refresh modes. + * + * Modesetting drivers which always update the frontbuffer do not need to + * implement the corresponding ->dirty framebuffer callback. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_dirtyfb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_clip_rect __user *clips_ptr; + struct drm_clip_rect *clips = NULL; + struct drm_mode_fb_dirty_cmd *r = data; + struct drm_framebuffer *fb; + unsigned flags; + int num_clips; + int ret; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + fb = drm_framebuffer_lookup(dev, r->fb_id); + if (!fb) + return -ENOENT; + + num_clips = r->num_clips; + clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; + + if (!num_clips != !clips_ptr) { + ret = -EINVAL; + goto out_err1; + } + + flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags; + + /* If userspace annotates copy, clips must come in pairs */ + if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) { + ret = -EINVAL; + goto out_err1; + } + + if (num_clips && clips_ptr) { + if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) { + ret = -EINVAL; + goto out_err1; + } + clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL); + if (!clips) { + ret = -ENOMEM; + goto out_err1; + } + + ret = copy_from_user(clips, clips_ptr, + num_clips * sizeof(*clips)); + if (ret) { + ret = -EFAULT; + goto out_err2; + } + } + + if (fb->funcs->dirty) { + ret = fb->funcs->dirty(fb, file_priv, flags, r->color, + clips, num_clips); + } else { + ret = -ENOSYS; + } + +out_err2: + kfree(clips); +out_err1: + drm_framebuffer_unreference(fb); + + return ret; +} + +/** + * drm_fb_release - remove and free the FBs on this file + * @priv: drm file for the ioctl + * + * Destroy all the FBs associated with @filp. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +void drm_fb_release(struct drm_file *priv) +{ + struct drm_framebuffer *fb, *tfb; + struct drm_mode_rmfb_work arg; + + INIT_LIST_HEAD(&arg.fbs); + + /* + * When the file gets released that means no one else can access the fb + * list any more, so no need to grab fpriv->fbs_lock. And we need to + * avoid upsetting lockdep since the universal cursor code adds a + * framebuffer while holding mutex locks. + * + * Note that a real deadlock between fpriv->fbs_lock and the modeset + * locks is impossible here since no one else but this function can get + * at it any more. + */ + list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { + if (drm_framebuffer_read_refcount(fb) > 1) { + list_move_tail(&fb->filp_head, &arg.fbs); + } else { + list_del_init(&fb->filp_head); + + /* This drops the fpriv->fbs reference. */ + drm_framebuffer_unreference(fb); + } + } + + if (!list_empty(&arg.fbs)) { + INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn); + + schedule_work(&arg.work); + flush_work(&arg.work); + destroy_work_on_stack(&arg.work); + } +} + +void drm_framebuffer_free(struct kref *kref) +{ + struct drm_framebuffer *fb = + container_of(kref, struct drm_framebuffer, base.refcount); + struct drm_device *dev = fb->dev; + + /* + * The lookup idr holds a weak reference, which has not necessarily been + * removed at this point. Check for that. + */ + drm_mode_object_unregister(dev, &fb->base); + + fb->funcs->destroy(fb); +} + +/** + * drm_framebuffer_init - initialize a framebuffer + * @dev: DRM device + * @fb: framebuffer to be initialized + * @funcs: ... with these functions + * + * Allocates an ID for the framebuffer's parent mode object, sets its mode + * functions & device file and adds it to the master fd list. + * + * IMPORTANT: + * This functions publishes the fb and makes it available for concurrent access + * by other users. Which means by this point the fb _must_ be fully set up - + * since all the fb attributes are invariant over its lifetime, no further + * locking but only correct reference counting is required. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, + const struct drm_framebuffer_funcs *funcs) +{ + int ret; + + INIT_LIST_HEAD(&fb->filp_head); + fb->dev = dev; + fb->funcs = funcs; + + ret = drm_mode_object_get_reg(dev, &fb->base, DRM_MODE_OBJECT_FB, + false, drm_framebuffer_free); + if (ret) + goto out; + + mutex_lock(&dev->mode_config.fb_lock); + dev->mode_config.num_fb++; + list_add(&fb->head, &dev->mode_config.fb_list); + mutex_unlock(&dev->mode_config.fb_lock); + + drm_mode_object_register(dev, &fb->base); +out: + return ret; +} +EXPORT_SYMBOL(drm_framebuffer_init); + +/** + * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference + * @dev: drm device + * @id: id of the fb object + * + * If successful, this grabs an additional reference to the framebuffer - + * callers need to make sure to eventually unreference the returned framebuffer + * again, using @drm_framebuffer_unreference. + */ +struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, + uint32_t id) +{ + struct drm_mode_object *obj; + struct drm_framebuffer *fb = NULL; + + obj = __drm_mode_object_find(dev, id, DRM_MODE_OBJECT_FB); + if (obj) + fb = obj_to_fb(obj); + return fb; +} +EXPORT_SYMBOL(drm_framebuffer_lookup); + +/** + * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr + * @fb: fb to unregister + * + * Drivers need to call this when cleaning up driver-private framebuffers, e.g. + * those used for fbdev. Note that the caller must hold a reference of it's own, + * i.e. the object may not be destroyed through this call (since it'll lead to a + * locking inversion). + */ +void drm_framebuffer_unregister_private(struct drm_framebuffer *fb) +{ + struct drm_device *dev; + + if (!fb) + return; + + dev = fb->dev; + + /* Mark fb as reaped and drop idr ref. */ + drm_mode_object_unregister(dev, &fb->base); +} +EXPORT_SYMBOL(drm_framebuffer_unregister_private); + +/** + * drm_framebuffer_cleanup - remove a framebuffer object + * @fb: framebuffer to remove + * + * Cleanup framebuffer. This function is intended to be used from the drivers + * ->destroy callback. It can also be used to clean up driver private + * framebuffers embedded into a larger structure. + * + * Note that this function does not remove the fb from active usuage - if it is + * still used anywhere, hilarity can ensue since userspace could call getfb on + * the id and get back -EINVAL. Obviously no concern at driver unload time. + * + * Also, the framebuffer will not be removed from the lookup idr - for + * user-created framebuffers this will happen in in the rmfb ioctl. For + * driver-private objects (e.g. for fbdev) drivers need to explicitly call + * drm_framebuffer_unregister_private. + */ +void drm_framebuffer_cleanup(struct drm_framebuffer *fb) +{ + struct drm_device *dev = fb->dev; + + mutex_lock(&dev->mode_config.fb_lock); + list_del(&fb->head); + dev->mode_config.num_fb--; + mutex_unlock(&dev->mode_config.fb_lock); +} +EXPORT_SYMBOL(drm_framebuffer_cleanup); + +/** + * drm_framebuffer_remove - remove and unreference a framebuffer object + * @fb: framebuffer to remove + * + * Scans all the CRTCs and planes in @dev's mode_config. If they're + * using @fb, removes it, setting it to NULL. Then drops the reference to the + * passed-in framebuffer. Might take the modeset locks. + * + * Note that this function optimizes the cleanup away if the caller holds the + * last reference to the framebuffer. It is also guaranteed to not take the + * modeset locks in this case. + */ +void drm_framebuffer_remove(struct drm_framebuffer *fb) +{ + struct drm_device *dev; + struct drm_crtc *crtc; + struct drm_plane *plane; + + if (!fb) + return; + + dev = fb->dev; + + WARN_ON(!list_empty(&fb->filp_head)); + + /* + * drm ABI mandates that we remove any deleted framebuffers from active + * useage. But since most sane clients only remove framebuffers they no + * longer need, try to optimize this away. + * + * Since we're holding a reference ourselves, observing a refcount of 1 + * means that we're the last holder and can skip it. Also, the refcount + * can never increase from 1 again, so we don't need any barriers or + * locks. + * + * Note that userspace could try to race with use and instate a new + * usage _after_ we've cleared all current ones. End result will be an + * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot + * in this manner. + */ + if (drm_framebuffer_read_refcount(fb) > 1) { + drm_modeset_lock_all(dev); + /* remove from any CRTC */ + drm_for_each_crtc(crtc, dev) { + if (crtc->primary->fb == fb) { + /* should turn off the crtc */ + if (drm_crtc_force_disable(crtc)) + DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); + } + } + + drm_for_each_plane(plane, dev) { + if (plane->fb == fb) + drm_plane_force_disable(plane); + } + drm_modeset_unlock_all(dev); + } + + drm_framebuffer_unreference(fb); +} +EXPORT_SYMBOL(drm_framebuffer_remove); diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 9134ae1..465bacd 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -257,7 +257,7 @@ drm_gem_object_release_handle(int id, void *ptr, void *data) if (drm_core_check_feature(dev, DRIVER_PRIME)) drm_gem_remove_prime_handles(obj, file_priv); - drm_vma_node_revoke(&obj->vma_node, file_priv->filp); + drm_vma_node_revoke(&obj->vma_node, file_priv); if (dev->driver->gem_close_object) dev->driver->gem_close_object(obj, file_priv); @@ -372,7 +372,7 @@ drm_gem_handle_create_tail(struct drm_file *file_priv, handle = ret; - ret = drm_vma_node_allow(&obj->vma_node, file_priv->filp); + ret = drm_vma_node_allow(&obj->vma_node, file_priv); if (ret) goto err_remove; @@ -386,7 +386,7 @@ drm_gem_handle_create_tail(struct drm_file *file_priv, return 0; err_revoke: - drm_vma_node_revoke(&obj->vma_node, file_priv->filp); + drm_vma_node_revoke(&obj->vma_node, file_priv); err_remove: spin_lock(&file_priv->table_lock); idr_remove(&file_priv->object_idr, handle); @@ -991,7 +991,7 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) if (!obj) return -EINVAL; - if (!drm_vma_node_is_allowed(node, filp)) { + if (!drm_vma_node_is_allowed(node, priv)) { drm_gem_object_unreference_unlocked(obj); return -EACCES; } diff --git a/drivers/gpu/drm/drm_hashtab.c b/drivers/gpu/drm/drm_hashtab.c index 7b30b30..dae18e5 100644 --- a/drivers/gpu/drm/drm_hashtab.c +++ b/drivers/gpu/drm/drm_hashtab.c @@ -142,7 +142,7 @@ int drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *it unsigned long add) { int ret; - unsigned long mask = (1 << bits) - 1; + unsigned long mask = (1UL << bits) - 1; unsigned long first, unshifted_key; unshifted_key = hash_long(seed, bits); diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c index 9ae353f..1df2d33 100644 --- a/drivers/gpu/drm/drm_info.c +++ b/drivers/gpu/drm/drm_info.c @@ -80,6 +80,7 @@ int drm_clients_info(struct seq_file *m, void *data) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; struct drm_file *priv; + kuid_t uid; seq_printf(m, "%20s %5s %3s master a %5s %10s\n", @@ -98,13 +99,14 @@ int drm_clients_info(struct seq_file *m, void *data) rcu_read_lock(); /* locks pid_task()->comm */ task = pid_task(priv->pid, PIDTYPE_PID); + uid = task ? __task_cred(task)->euid : GLOBAL_ROOT_UID; seq_printf(m, "%20s %5d %3d %c %c %5d %10u\n", task ? task->comm : "<unknown>", pid_vnr(priv->pid), priv->minor->index, drm_is_current_master(priv) ? 'y' : 'n', priv->authenticated ? 'y' : 'n', - from_kuid_munged(seq_user_ns(m), priv->uid), + from_kuid_munged(seq_user_ns(m), uid), priv->magic); rcu_read_unlock(); } diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index b86dc9b..e66af28 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -21,6 +21,9 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +#define DRM_IF_MAJOR 1 +#define DRM_IF_MINOR 4 + /* drm_irq.c */ extern unsigned int drm_timestamp_monotonic; diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c index 57676f8..867ab8c 100644 --- a/drivers/gpu/drm/drm_ioc32.c +++ b/drivers/gpu/drm/drm_ioc32.c @@ -32,7 +32,6 @@ #include <linux/export.h> #include <drm/drmP.h> -#include <drm/drm_core.h> #define DRM_IOCTL_VERSION32 DRM_IOWR(0x00, drm_version32_t) #define DRM_IOCTL_GET_UNIQUE32 DRM_IOWR(0x01, drm_unique32_t) @@ -346,6 +345,7 @@ static int compat_drm_getstats(struct file *file, unsigned int cmd, struct drm_stats __user *stats; int i, err; + memset(&s32, 0, sizeof(drm_stats32_t)); stats = compat_alloc_user_space(sizeof(*stats)); if (!stats) return -EFAULT; @@ -1015,6 +1015,7 @@ static int compat_drm_wait_vblank(struct file *file, unsigned int cmd, return 0; } +#if defined(CONFIG_X86) || defined(CONFIG_IA64) typedef struct drm_mode_fb_cmd232 { u32 fb_id; u32 width; @@ -1071,6 +1072,7 @@ static int compat_drm_mode_addfb2(struct file *file, unsigned int cmd, return 0; } +#endif static drm_ioctl_compat_t *drm_compat_ioctls[] = { [DRM_IOCTL_NR(DRM_IOCTL_VERSION32)] = compat_drm_version, @@ -1104,7 +1106,9 @@ static drm_ioctl_compat_t *drm_compat_ioctls[] = { [DRM_IOCTL_NR(DRM_IOCTL_UPDATE_DRAW32)] = compat_drm_update_draw, #endif [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK32)] = compat_drm_wait_vblank, +#if defined(CONFIG_X86) || defined(CONFIG_IA64) [DRM_IOCTL_NR(DRM_IOCTL_MODE_ADDFB232)] = compat_drm_mode_addfb2, +#endif }; /** diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 0099d2a..0ad2c47 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -29,7 +29,6 @@ */ #include <drm/drmP.h> -#include <drm/drm_core.h> #include <drm/drm_auth.h> #include "drm_legacy.h" #include "drm_internal.h" @@ -189,9 +188,8 @@ static int drm_getclient(struct drm_device *dev, void *data, */ if (client->idx == 0) { client->auth = file_priv->authenticated; - client->pid = pid_vnr(file_priv->pid); - client->uid = from_kuid_munged(current_user_ns(), - file_priv->uid); + client->pid = task_pid_vnr(current); + client->uid = overflowuid; client->magic = 0; client->iocs = 0; @@ -722,9 +720,9 @@ long drm_ioctl(struct file *filp, if (ksize > in_size) memset(kdata + in_size, 0, ksize - in_size); - /* Enforce sane locking for kms driver ioctls. Core ioctls are + /* Enforce sane locking for modern driver ioctls. Core ioctls are * too messy still. */ - if ((drm_core_check_feature(dev, DRIVER_MODESET) && is_driver_ioctl) || + if ((!drm_core_check_feature(dev, DRIVER_LEGACY) && is_driver_ioctl) || (ioctl->flags & DRM_UNLOCKED)) retcode = func(dev, kdata, file_priv); else { diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 77f357b..404a1ce 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -482,7 +482,7 @@ int drm_irq_install(struct drm_device *dev, int irq) return ret; } - if (!drm_core_check_feature(dev, DRIVER_MODESET)) + if (drm_core_check_feature(dev, DRIVER_LEGACY)) vga_client_register(dev->pdev, (void *)dev, drm_irq_vgaarb_nokms, NULL); /* After installing handler */ @@ -491,7 +491,7 @@ int drm_irq_install(struct drm_device *dev, int irq) if (ret < 0) { dev->irq_enabled = false; - if (!drm_core_check_feature(dev, DRIVER_MODESET)) + if (drm_core_check_feature(dev, DRIVER_LEGACY)) vga_client_register(dev->pdev, NULL, NULL, NULL); free_irq(irq, dev); } else { @@ -557,7 +557,7 @@ int drm_irq_uninstall(struct drm_device *dev) DRM_DEBUG("irq=%d\n", dev->irq); - if (!drm_core_check_feature(dev, DRIVER_MODESET)) + if (drm_core_check_feature(dev, DRIVER_LEGACY)) vga_client_register(dev->pdev, NULL, NULL, NULL); if (dev->driver->irq_uninstall) @@ -592,7 +592,7 @@ int drm_control(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) return 0; - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) return 0; /* UMS was only ever supported on pci devices. */ if (WARN_ON(!dev->pdev)) @@ -713,10 +713,10 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants); * Negative value on error, failure or if not supported in current * video mode: * - * -EINVAL - Invalid CRTC. - * -EAGAIN - Temporary unavailable, e.g., called before initial modeset. - * -ENOTSUPP - Function not supported in current display mode. - * -EIO - Failed, e.g., due to failed scanout position query. + * -EINVAL Invalid CRTC. + * -EAGAIN Temporary unavailable, e.g., called before initial modeset. + * -ENOTSUPP Function not supported in current display mode. + * -EIO Failed, e.g., due to failed scanout position query. * * Returns or'ed positive status flags on success: * @@ -1295,7 +1295,7 @@ void drm_vblank_off(struct drm_device *dev, unsigned int pipe) if (e->pipe != pipe) continue; DRM_DEBUG("Sending premature vblank event on disable: " - "wanted %d, current %d\n", + "wanted %u, current %u\n", e->event.sequence, seq); list_del(&e->base.link); drm_vblank_put(dev, pipe); @@ -1519,7 +1519,7 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, return 0; /* KMS drivers handle this internally */ - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) return 0; pipe = modeset->crtc; @@ -1585,7 +1585,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, seq = drm_vblank_count_and_time(dev, pipe, &now); - DRM_DEBUG("event on vblank count %d, current %d, crtc %u\n", + DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n", vblwait->request.sequence, seq, pipe); trace_drm_vblank_event_queued(current->pid, pipe, @@ -1693,7 +1693,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, return drm_queue_vblank_event(dev, pipe, vblwait, file_priv); } - DRM_DEBUG("waiting on vblank count %d, crtc %u\n", + DRM_DEBUG("waiting on vblank count %u, crtc %u\n", vblwait->request.sequence, pipe); DRM_WAIT_ON(ret, vblank->queue, 3 * HZ, (((drm_vblank_count(dev, pipe) - @@ -1708,7 +1708,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, vblwait->reply.tval_sec = now.tv_sec; vblwait->reply.tval_usec = now.tv_usec; - DRM_DEBUG("returning %d to client\n", + DRM_DEBUG("returning %u to client\n", vblwait->reply.sequence); } else { DRM_DEBUG("vblank wait interrupted by signal\n"); @@ -1735,7 +1735,7 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe) if ((seq - e->event.sequence) > (1<<23)) continue; - DRM_DEBUG("vblank event on %d, current %d\n", + DRM_DEBUG("vblank event on %u, current %u\n", e->event.sequence, seq); list_del(&e->base.link); @@ -1826,6 +1826,7 @@ EXPORT_SYMBOL(drm_crtc_handle_vblank); */ u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe) { + WARN_ON_ONCE(dev->max_vblank_count != 0); return 0; } EXPORT_SYMBOL(drm_vblank_no_hw_counter); diff --git a/drivers/gpu/drm/drm_kms_helper_common.c b/drivers/gpu/drm/drm_kms_helper_common.c index 3187c4b..45db36c 100644 --- a/drivers/gpu/drm/drm_kms_helper_common.c +++ b/drivers/gpu/drm/drm_kms_helper_common.c @@ -27,7 +27,8 @@ #include <drm/drmP.h> #include <drm/drm_fb_helper.h> -#include <drm/drm_dp_aux_dev.h> + +#include "drm_crtc_helper_internal.h" MODULE_AUTHOR("David Airlie, Jesse Barnes"); MODULE_DESCRIPTION("DRM KMS helper"); diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c index 48ac0eb..c901f3c 100644 --- a/drivers/gpu/drm/drm_lock.c +++ b/drivers/gpu/drm/drm_lock.c @@ -163,7 +163,7 @@ int drm_legacy_lock(struct drm_device *dev, void *data, struct drm_master *master = file_priv->master; int ret = 0; - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; ++file_priv->lock_count; @@ -252,7 +252,7 @@ int drm_legacy_unlock(struct drm_device *dev, void *data, struct drm_file *file_ struct drm_lock *lock = data; struct drm_master *master = file_priv->master; - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; if (lock->context == DRM_KERNEL_CONTEXT) { diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index af0d471..1160a57 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -999,6 +999,27 @@ int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi, EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_on); /** + * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image + * data used by the interface + * @dsi: DSI peripheral device + * @format: pixel format + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format) +{ + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format, + sizeof(format)); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_set_pixel_format); + +/** * mipi_dsi_dcs_set_tear_scanline() - set the scanline to use as trigger for * the Tearing Effect output signal of the display module * @dsi: DSI peripheral device @@ -1021,25 +1042,53 @@ int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline) EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline); /** - * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image - * data used by the interface + * mipi_dsi_dcs_set_display_brightness() - sets the brightness value of the + * display * @dsi: DSI peripheral device - * @format: pixel format + * @brightness: brightness value * * Return: 0 on success or a negative error code on failure. */ -int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format) +int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi, + u16 brightness) { + u8 payload[2] = { brightness & 0xff, brightness >> 8 }; ssize_t err; - err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format, - sizeof(format)); + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, + payload, sizeof(payload)); if (err < 0) return err; return 0; } -EXPORT_SYMBOL(mipi_dsi_dcs_set_pixel_format); +EXPORT_SYMBOL(mipi_dsi_dcs_set_display_brightness); + +/** + * mipi_dsi_dcs_get_display_brightness() - gets the current brightness value + * of the display + * @dsi: DSI peripheral device + * @brightness: brightness value + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi, + u16 *brightness) +{ + ssize_t err; + + err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_DISPLAY_BRIGHTNESS, + brightness, sizeof(*brightness)); + if (err <= 0) { + if (err == 0) + err = -ENODATA; + + return err; + } + + return 0; +} +EXPORT_SYMBOL(mipi_dsi_dcs_get_display_brightness); static int mipi_dsi_drv_probe(struct device *dev) { diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index cb39f45..11d44a1 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -46,6 +46,7 @@ #include <linux/slab.h> #include <linux/seq_file.h> #include <linux/export.h> +#include <linux/interval_tree_generic.h> /** * DOC: Overview @@ -103,6 +104,72 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ u64 end, enum drm_mm_search_flags flags); +#define START(node) ((node)->start) +#define LAST(node) ((node)->start + (node)->size - 1) + +INTERVAL_TREE_DEFINE(struct drm_mm_node, rb, + u64, __subtree_last, + START, LAST, static inline, drm_mm_interval_tree) + +struct drm_mm_node * +drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last) +{ + return drm_mm_interval_tree_iter_first(&mm->interval_tree, + start, last); +} +EXPORT_SYMBOL(drm_mm_interval_first); + +struct drm_mm_node * +drm_mm_interval_next(struct drm_mm_node *node, u64 start, u64 last) +{ + return drm_mm_interval_tree_iter_next(node, start, last); +} +EXPORT_SYMBOL(drm_mm_interval_next); + +static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node, + struct drm_mm_node *node) +{ + struct drm_mm *mm = hole_node->mm; + struct rb_node **link, *rb; + struct drm_mm_node *parent; + + node->__subtree_last = LAST(node); + + if (hole_node->allocated) { + rb = &hole_node->rb; + while (rb) { + parent = rb_entry(rb, struct drm_mm_node, rb); + if (parent->__subtree_last >= node->__subtree_last) + break; + + parent->__subtree_last = node->__subtree_last; + rb = rb_parent(rb); + } + + rb = &hole_node->rb; + link = &hole_node->rb.rb_right; + } else { + rb = NULL; + link = &mm->interval_tree.rb_node; + } + + while (*link) { + rb = *link; + parent = rb_entry(rb, struct drm_mm_node, rb); + if (parent->__subtree_last < node->__subtree_last) + parent->__subtree_last = node->__subtree_last; + if (node->start < parent->start) + link = &parent->rb.rb_left; + else + link = &parent->rb.rb_right; + } + + rb_link_node(&node->rb, rb, link); + rb_insert_augmented(&node->rb, + &mm->interval_tree, + &drm_mm_interval_tree_augment); +} + static void drm_mm_insert_helper(struct drm_mm_node *hole_node, struct drm_mm_node *node, u64 size, unsigned alignment, @@ -150,9 +217,10 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, node->color = color; node->allocated = 1; - INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list); + drm_mm_interval_tree_add_node(hole_node, node); + BUG_ON(node->start + node->size > adj_end); node->hole_follows = 0; @@ -178,41 +246,54 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, */ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) { + u64 end = node->start + node->size; struct drm_mm_node *hole; - u64 end; - u64 hole_start; - u64 hole_end; + u64 hole_start, hole_end; - BUG_ON(node == NULL); + if (WARN_ON(node->size == 0)) + return -EINVAL; end = node->start + node->size; /* Find the relevant hole to add our node to */ - drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { - if (hole_start > node->start || hole_end < end) - continue; + hole = drm_mm_interval_tree_iter_first(&mm->interval_tree, + node->start, ~(u64)0); + if (hole) { + if (hole->start < end) + return -ENOSPC; + } else { + hole = list_entry(&mm->head_node.node_list, + typeof(*hole), node_list); + } - node->mm = mm; - node->allocated = 1; + hole = list_last_entry(&hole->node_list, typeof(*hole), node_list); + if (!hole->hole_follows) + return -ENOSPC; - INIT_LIST_HEAD(&node->hole_stack); - list_add(&node->node_list, &hole->node_list); + hole_start = __drm_mm_hole_node_start(hole); + hole_end = __drm_mm_hole_node_end(hole); + if (hole_start > node->start || hole_end < end) + return -ENOSPC; - if (node->start == hole_start) { - hole->hole_follows = 0; - list_del_init(&hole->hole_stack); - } + node->mm = mm; + node->allocated = 1; - node->hole_follows = 0; - if (end != hole_end) { - list_add(&node->hole_stack, &mm->hole_stack); - node->hole_follows = 1; - } + list_add(&node->node_list, &hole->node_list); - return 0; + drm_mm_interval_tree_add_node(hole, node); + + if (node->start == hole_start) { + hole->hole_follows = 0; + list_del(&hole->hole_stack); } - return -ENOSPC; + node->hole_follows = 0; + if (end != hole_end) { + list_add(&node->hole_stack, &mm->hole_stack); + node->hole_follows = 1; + } + + return 0; } EXPORT_SYMBOL(drm_mm_reserve_node); @@ -239,6 +320,9 @@ int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, { struct drm_mm_node *hole_node; + if (WARN_ON(size == 0)) + return -EINVAL; + hole_node = drm_mm_search_free_generic(mm, size, alignment, color, sflags); if (!hole_node) @@ -299,9 +383,10 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, node->color = color; node->allocated = 1; - INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list); + drm_mm_interval_tree_add_node(hole_node, node); + BUG_ON(node->start < start); BUG_ON(node->start < adj_start); BUG_ON(node->start + node->size > adj_end); @@ -340,6 +425,9 @@ int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *n { struct drm_mm_node *hole_node; + if (WARN_ON(size == 0)) + return -EINVAL; + hole_node = drm_mm_search_free_in_range_generic(mm, size, alignment, color, start, end, sflags); @@ -390,6 +478,7 @@ void drm_mm_remove_node(struct drm_mm_node *node) } else list_move(&prev_node->hole_stack, &mm->hole_stack); + drm_mm_interval_tree_remove(node, &mm->interval_tree); list_del(&node->node_list); node->allocated = 0; } @@ -516,11 +605,13 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) { list_replace(&old->node_list, &new->node_list); list_replace(&old->hole_stack, &new->hole_stack); + rb_replace_node(&old->rb, &new->rb, &old->mm->interval_tree); new->hole_follows = old->hole_follows; new->mm = old->mm; new->start = old->start; new->size = old->size; new->color = old->color; + new->__subtree_last = old->__subtree_last; old->allocated = 0; new->allocated = 1; @@ -748,7 +839,6 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size) /* Clever trick to avoid a special case in the free hole tracking. */ INIT_LIST_HEAD(&mm->head_node.node_list); - INIT_LIST_HEAD(&mm->head_node.hole_stack); mm->head_node.hole_follows = 1; mm->head_node.scanned_block = 0; mm->head_node.scanned_prev_free = 0; @@ -758,6 +848,8 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size) mm->head_node.size = start - mm->head_node.start; list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack); + mm->interval_tree = RB_ROOT; + mm->color_adjust = NULL; } EXPORT_SYMBOL(drm_mm_init); diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c new file mode 100644 index 0000000..9f17085 --- /dev/null +++ b/drivers/gpu/drm/drm_mode_object.c @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <linux/export.h> +#include <drm/drmP.h> +#include <drm/drm_mode_object.h> + +#include "drm_crtc_internal.h" + +/* + * Internal function to assign a slot in the object idr and optionally + * register the object into the idr. + */ +int drm_mode_object_get_reg(struct drm_device *dev, + struct drm_mode_object *obj, + uint32_t obj_type, + bool register_obj, + void (*obj_free_cb)(struct kref *kref)) +{ + int ret; + + mutex_lock(&dev->mode_config.idr_mutex); + ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL); + if (ret >= 0) { + /* + * Set up the object linking under the protection of the idr + * lock so that other users can't see inconsistent state. + */ + obj->id = ret; + obj->type = obj_type; + if (obj_free_cb) { + obj->free_cb = obj_free_cb; + kref_init(&obj->refcount); + } + } + mutex_unlock(&dev->mode_config.idr_mutex); + + return ret < 0 ? ret : 0; +} + +/** + * drm_mode_object_get - allocate a new modeset identifier + * @dev: DRM device + * @obj: object pointer, used to generate unique ID + * @obj_type: object type + * + * Create a unique identifier based on @ptr in @dev's identifier space. Used + * for tracking modes, CRTCs and connectors. Note that despite the _get postfix + * modeset identifiers are _not_ reference counted. Hence don't use this for + * reference counted modeset objects like framebuffers. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_mode_object_get(struct drm_device *dev, + struct drm_mode_object *obj, uint32_t obj_type) +{ + return drm_mode_object_get_reg(dev, obj, obj_type, true, NULL); +} + +void drm_mode_object_register(struct drm_device *dev, + struct drm_mode_object *obj) +{ + mutex_lock(&dev->mode_config.idr_mutex); + idr_replace(&dev->mode_config.crtc_idr, obj, obj->id); + mutex_unlock(&dev->mode_config.idr_mutex); +} + +/** + * drm_mode_object_unregister - free a modeset identifer + * @dev: DRM device + * @object: object to free + * + * Free @id from @dev's unique identifier pool. + * This function can be called multiple times, and guards against + * multiple removals. + * These modeset identifiers are _not_ reference counted. Hence don't use this + * for reference counted modeset objects like framebuffers. + */ +void drm_mode_object_unregister(struct drm_device *dev, + struct drm_mode_object *object) +{ + mutex_lock(&dev->mode_config.idr_mutex); + if (object->id) { + idr_remove(&dev->mode_config.crtc_idr, object->id); + object->id = 0; + } + mutex_unlock(&dev->mode_config.idr_mutex); +} + +struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev, + uint32_t id, uint32_t type) +{ + struct drm_mode_object *obj = NULL; + + mutex_lock(&dev->mode_config.idr_mutex); + obj = idr_find(&dev->mode_config.crtc_idr, id); + if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type) + obj = NULL; + if (obj && obj->id != id) + obj = NULL; + + if (obj && obj->free_cb) { + if (!kref_get_unless_zero(&obj->refcount)) + obj = NULL; + } + mutex_unlock(&dev->mode_config.idr_mutex); + + return obj; +} + +/** + * drm_mode_object_find - look up a drm object with static lifetime + * @dev: drm device + * @id: id of the mode object + * @type: type of the mode object + * + * This function is used to look up a modeset object. It will acquire a + * reference for reference counted objects. This reference must be dropped again + * by callind drm_mode_object_unreference(). + */ +struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, + uint32_t id, uint32_t type) +{ + struct drm_mode_object *obj = NULL; + + obj = __drm_mode_object_find(dev, id, type); + return obj; +} +EXPORT_SYMBOL(drm_mode_object_find); + +/** + * drm_mode_object_unreference - decr the object refcnt + * @obj: mode_object + * + * This function decrements the object's refcount if it is a refcounted modeset + * object. It is a no-op on any other object. This is used to drop references + * acquired with drm_mode_object_reference(). + */ +void drm_mode_object_unreference(struct drm_mode_object *obj) +{ + if (obj->free_cb) { + DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount)); + kref_put(&obj->refcount, obj->free_cb); + } +} +EXPORT_SYMBOL(drm_mode_object_unreference); + +/** + * drm_mode_object_reference - incr the object refcnt + * @obj: mode_object + * + * This function increments the object's refcount if it is a refcounted modeset + * object. It is a no-op on any other object. References should be dropped again + * by calling drm_mode_object_unreference(). + */ +void drm_mode_object_reference(struct drm_mode_object *obj) +{ + if (obj->free_cb) { + DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount)); + kref_get(&obj->refcount); + } +} +EXPORT_SYMBOL(drm_mode_object_reference); + +/** + * drm_object_attach_property - attach a property to a modeset object + * @obj: drm modeset object + * @property: property to attach + * @init_val: initial value of the property + * + * This attaches the given property to the modeset object with the given initial + * value. Currently this function cannot fail since the properties are stored in + * a statically sized array. + */ +void drm_object_attach_property(struct drm_mode_object *obj, + struct drm_property *property, + uint64_t init_val) +{ + int count = obj->properties->count; + + if (count == DRM_OBJECT_MAX_PROPERTY) { + WARN(1, "Failed to attach object property (type: 0x%x). Please " + "increase DRM_OBJECT_MAX_PROPERTY by 1 for each time " + "you see this message on the same object type.\n", + obj->type); + return; + } + + obj->properties->properties[count] = property; + obj->properties->values[count] = init_val; + obj->properties->count++; +} +EXPORT_SYMBOL(drm_object_attach_property); + +/** + * drm_object_property_set_value - set the value of a property + * @obj: drm mode object to set property value for + * @property: property to set + * @val: value the property should be set to + * + * This function sets a given property on a given object. This function only + * changes the software state of the property, it does not call into the + * driver's ->set_property callback. + * + * Note that atomic drivers should not have any need to call this, the core will + * ensure consistency of values reported back to userspace through the + * appropriate ->atomic_get_property callback. Only legacy drivers should call + * this function to update the tracked value (after clamping and other + * restrictions have been applied). + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_object_property_set_value(struct drm_mode_object *obj, + struct drm_property *property, uint64_t val) +{ + int i; + + for (i = 0; i < obj->properties->count; i++) { + if (obj->properties->properties[i] == property) { + obj->properties->values[i] = val; + return 0; + } + } + + return -EINVAL; +} +EXPORT_SYMBOL(drm_object_property_set_value); + +/** + * drm_object_property_get_value - retrieve the value of a property + * @obj: drm mode object to get property value from + * @property: property to retrieve + * @val: storage for the property value + * + * This function retrieves the softare state of the given property for the given + * property. Since there is no driver callback to retrieve the current property + * value this might be out of sync with the hardware, depending upon the driver + * and property. + * + * Atomic drivers should never call this function directly, the core will read + * out property values through the various ->atomic_get_property callbacks. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_object_property_get_value(struct drm_mode_object *obj, + struct drm_property *property, uint64_t *val) +{ + int i; + + /* read-only properties bypass atomic mechanism and still store + * their value in obj->properties->values[].. mostly to avoid + * having to deal w/ EDID and similar props in atomic paths: + */ + if (drm_core_check_feature(property->dev, DRIVER_ATOMIC) && + !(property->flags & DRM_MODE_PROP_IMMUTABLE)) + return drm_atomic_get_property(obj, property, val); + + for (i = 0; i < obj->properties->count; i++) { + if (obj->properties->properties[i] == property) { + *val = obj->properties->values[i]; + return 0; + } + + } + + return -EINVAL; +} +EXPORT_SYMBOL(drm_object_property_get_value); + +/* helper for getconnector and getproperties ioctls */ +int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic, + uint32_t __user *prop_ptr, + uint64_t __user *prop_values, + uint32_t *arg_count_props) +{ + int i, ret, count; + + for (i = 0, count = 0; i < obj->properties->count; i++) { + struct drm_property *prop = obj->properties->properties[i]; + uint64_t val; + + if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic) + continue; + + if (*arg_count_props > count) { + ret = drm_object_property_get_value(obj, prop, &val); + if (ret) + return ret; + + if (put_user(prop->base.id, prop_ptr + count)) + return -EFAULT; + + if (put_user(val, prop_values + count)) + return -EFAULT; + } + + count++; + } + *arg_count_props = count; + + return 0; +} + +/** + * drm_mode_obj_get_properties_ioctl - get the current value of a object's property + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function retrieves the current value for an object's property. Compared + * to the connector specific ioctl this one is extended to also work on crtc and + * plane objects. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_mode_obj_get_properties *arg = data; + struct drm_mode_object *obj; + int ret = 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + drm_modeset_lock_all(dev); + + obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); + if (!obj) { + ret = -ENOENT; + goto out; + } + if (!obj->properties) { + ret = -EINVAL; + goto out_unref; + } + + ret = drm_mode_object_get_properties(obj, file_priv->atomic, + (uint32_t __user *)(unsigned long)(arg->props_ptr), + (uint64_t __user *)(unsigned long)(arg->prop_values_ptr), + &arg->count_props); + +out_unref: + drm_mode_object_unreference(obj); +out: + drm_modeset_unlock_all(dev); + return ret; +} + +struct drm_property *drm_mode_obj_find_prop_id(struct drm_mode_object *obj, + uint32_t prop_id) +{ + int i; + + for (i = 0; i < obj->properties->count; i++) + if (obj->properties->properties[i]->base.id == prop_id) + return obj->properties->properties[i]; + + return NULL; +} + +int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_mode_obj_set_property *arg = data; + struct drm_mode_object *arg_obj; + struct drm_property *property; + int ret = -EINVAL; + struct drm_mode_object *ref; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + drm_modeset_lock_all(dev); + + arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); + if (!arg_obj) { + ret = -ENOENT; + goto out; + } + + if (!arg_obj->properties) + goto out_unref; + + property = drm_mode_obj_find_prop_id(arg_obj, arg->prop_id); + if (!property) + goto out_unref; + + if (!drm_property_change_valid_get(property, arg->value, &ref)) + goto out_unref; + + switch (arg_obj->type) { + case DRM_MODE_OBJECT_CONNECTOR: + ret = drm_mode_connector_set_obj_prop(arg_obj, property, + arg->value); + break; + case DRM_MODE_OBJECT_CRTC: + ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); + break; + case DRM_MODE_OBJECT_PLANE: + ret = drm_mode_plane_set_obj_prop(obj_to_plane(arg_obj), + property, arg->value); + break; + } + + drm_property_change_valid_put(property, ref); + +out_unref: + drm_mode_object_unreference(arg_obj); +out: + drm_modeset_unlock_all(dev); + return ret; +} diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index fc5040a..53f07ac 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -657,11 +657,36 @@ void drm_display_mode_to_videomode(const struct drm_display_mode *dmode, } EXPORT_SYMBOL_GPL(drm_display_mode_to_videomode); +/** + * drm_bus_flags_from_videomode - extract information about pixelclk and + * DE polarity from videomode and store it in a separate variable + * @vm: videomode structure to use + * @bus_flags: information about pixelclk and DE polarity will be stored here + * + * Sets DRM_BUS_FLAG_DE_(LOW|HIGH) and DRM_BUS_FLAG_PIXDATA_(POS|NEG)EDGE + * in @bus_flags according to DISPLAY_FLAGS found in @vm + */ +void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags) +{ + *bus_flags = 0; + if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) + *bus_flags |= DRM_BUS_FLAG_PIXDATA_POSEDGE; + if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) + *bus_flags |= DRM_BUS_FLAG_PIXDATA_NEGEDGE; + + if (vm->flags & DISPLAY_FLAGS_DE_LOW) + *bus_flags |= DRM_BUS_FLAG_DE_LOW; + if (vm->flags & DISPLAY_FLAGS_DE_HIGH) + *bus_flags |= DRM_BUS_FLAG_DE_HIGH; +} +EXPORT_SYMBOL_GPL(drm_bus_flags_from_videomode); + #ifdef CONFIG_OF /** * of_get_drm_display_mode - get a drm_display_mode from devicetree * @np: device_node with the timing specification * @dmode: will be set to the return value + * @bus_flags: information about pixelclk and DE polarity * @index: index into the list of display timings in devicetree * * This function is expensive and should only be used, if only one mode is to be @@ -672,7 +697,8 @@ EXPORT_SYMBOL_GPL(drm_display_mode_to_videomode); * 0 on success, a negative errno code when no of videomode node was found. */ int of_get_drm_display_mode(struct device_node *np, - struct drm_display_mode *dmode, int index) + struct drm_display_mode *dmode, u32 *bus_flags, + int index) { struct videomode vm; int ret; @@ -682,6 +708,8 @@ int of_get_drm_display_mode(struct device_node *np, return ret; drm_display_mode_from_videomode(&vm, dmode); + if (bus_flags) + drm_bus_flags_from_videomode(&vm, bus_flags); pr_debug("%s: got %dx%d display mode from %s\n", of_node_full_name(np), vm.hactive, vm.vactive, np->name); diff --git a/drivers/gpu/drm/drm_modeset_helper.c b/drivers/gpu/drm/drm_modeset_helper.c new file mode 100644 index 0000000..1d45738 --- /dev/null +++ b/drivers/gpu/drm/drm_modeset_helper.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <drm/drm_modeset_helper.h> +#include <drm/drm_plane_helper.h> + +/** + * DOC: aux kms helpers + * + * This helper library contains various one-off functions which don't really fit + * anywhere else in the DRM modeset helper library. + */ + +/** + * drm_helper_move_panel_connectors_to_head() - move panels to the front in the + * connector list + * @dev: drm device to operate on + * + * Some userspace presumes that the first connected connector is the main + * display, where it's supposed to display e.g. the login screen. For + * laptops, this should be the main panel. Use this function to sort all + * (eDP/LVDS) panels to the front of the connector list, instead of + * painstakingly trying to initialize them in the right order. + */ +void drm_helper_move_panel_connectors_to_head(struct drm_device *dev) +{ + struct drm_connector *connector, *tmp; + struct list_head panel_list; + + INIT_LIST_HEAD(&panel_list); + + list_for_each_entry_safe(connector, tmp, + &dev->mode_config.connector_list, head) { + if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS || + connector->connector_type == DRM_MODE_CONNECTOR_eDP) + list_move_tail(&connector->head, &panel_list); + } + + list_splice(&panel_list, &dev->mode_config.connector_list); +} +EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head); + +/** + * drm_helper_mode_fill_fb_struct - fill out framebuffer metadata + * @fb: drm_framebuffer object to fill out + * @mode_cmd: metadata from the userspace fb creation request + * + * This helper can be used in a drivers fb_create callback to pre-fill the fb's + * metadata fields. + */ +void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + int i; + + fb->width = mode_cmd->width; + fb->height = mode_cmd->height; + for (i = 0; i < 4; i++) { + fb->pitches[i] = mode_cmd->pitches[i]; + fb->offsets[i] = mode_cmd->offsets[i]; + fb->modifier[i] = mode_cmd->modifier[i]; + } + drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth, + &fb->bits_per_pixel); + fb->pixel_format = mode_cmd->pixel_format; + fb->flags = mode_cmd->flags; +} +EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); + +/* + * This is the minimal list of formats that seem to be safe for modeset use + * with all current DRM drivers. Most hardware can actually support more + * formats than this and drivers may specify a more accurate list when + * creating the primary plane. However drivers that still call + * drm_plane_init() will use this minimal format list as the default. + */ +static const uint32_t safe_modeset_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, +}; + +static struct drm_plane *create_primary_plane(struct drm_device *dev) +{ + struct drm_plane *primary; + int ret; + + primary = kzalloc(sizeof(*primary), GFP_KERNEL); + if (primary == NULL) { + DRM_DEBUG_KMS("Failed to allocate primary plane\n"); + return NULL; + } + + /* + * Remove the format_default field from drm_plane when dropping + * this helper. + */ + primary->format_default = true; + + /* possible_crtc's will be filled in later by crtc_init */ + ret = drm_universal_plane_init(dev, primary, 0, + &drm_primary_helper_funcs, + safe_modeset_formats, + ARRAY_SIZE(safe_modeset_formats), + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + kfree(primary); + primary = NULL; + } + + return primary; +} + +/** + * drm_crtc_init - Legacy CRTC initialization function + * @dev: DRM device + * @crtc: CRTC object to init + * @funcs: callbacks for the new CRTC + * + * Initialize a CRTC object with a default helper-provided primary plane and no + * cursor plane. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, + const struct drm_crtc_funcs *funcs) +{ + struct drm_plane *primary; + + primary = create_primary_plane(dev); + return drm_crtc_init_with_planes(dev, crtc, primary, NULL, funcs, + NULL); +} +EXPORT_SYMBOL(drm_crtc_init); diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index b2f8f10..3ceea9c 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -175,7 +175,7 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, { struct drm_irq_busid *p = data; - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; /* UMS was only ever support on PCI devices. */ @@ -236,8 +236,8 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, DRM_DEBUG("\n"); dev = drm_dev_alloc(driver, &pdev->dev); - if (!dev) - return -ENOMEM; + if (IS_ERR(dev)) + return PTR_ERR(dev); ret = pci_enable_device(pdev); if (ret) @@ -263,7 +263,7 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, /* No locking needed since shadow-attach is single-threaded since it may * only be called from the per-driver module init hook. */ - if (!drm_core_check_feature(dev, DRIVER_MODESET)) + if (drm_core_check_feature(dev, DRIVER_LEGACY)) list_add_tail(&dev->legacy_dev_list, &driver->legacy_dev_list); return 0; @@ -299,7 +299,7 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) DRM_DEBUG("\n"); - if (driver->driver_features & DRIVER_MODESET) + if (!(driver->driver_features & DRIVER_LEGACY)) return pci_register_driver(pdriver); /* If not using KMS, fall back to stealth mode manual scanning. */ @@ -421,7 +421,7 @@ void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver) struct drm_device *dev, *tmp; DRM_DEBUG("\n"); - if (driver->driver_features & DRIVER_MODESET) { + if (!(driver->driver_features & DRIVER_LEGACY)) { pci_unregister_driver(pdriver); } else { list_for_each_entry_safe(dev, tmp, &driver->legacy_dev_list, diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c new file mode 100644 index 0000000..cd0d475 --- /dev/null +++ b/drivers/gpu/drm/drm_plane.c @@ -0,0 +1,907 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <drm/drmP.h> +#include <drm/drm_plane.h> + +#include "drm_crtc_internal.h" + +/** + * DOC: overview + * + * A plane represents an image source that can be blended with or overlayed on + * top of a CRTC during the scanout process. Planes take their input data from a + * &drm_framebuffer object. The plane itself specifies the cropping and scaling + * of that image, and where it is placed on the visible are of a display + * pipeline, represented by &drm_crtc. A plane can also have additional + * properties that specify how the pixels are positioned and blended, like + * rotation or Z-position. All these properties are stored in &drm_plane_state. + * + * To create a plane, a KMS drivers allocates and zeroes an instances of + * struct &drm_plane (possibly as part of a larger structure) and registers it + * with a call to drm_universal_plane_init(). + * + * Cursor and overlay planes are optional. All drivers should provide one + * primary plane per CRTC to avoid surprising userspace too much. See enum + * &drm_plane_type for a more in-depth discussion of these special uapi-relevant + * plane types. Special planes are associated with their CRTC by calling + * drm_crtc_init_with_planes(). + * + * The type of a plane is exposed in the immutable "type" enumeration property, + * which has one of the following values: "Overlay", "Primary", "Cursor". + */ + +static unsigned int drm_num_planes(struct drm_device *dev) +{ + unsigned int num = 0; + struct drm_plane *tmp; + + drm_for_each_plane(tmp, dev) { + num++; + } + + return num; +} + +/** + * drm_universal_plane_init - Initialize a new universal plane object + * @dev: DRM device + * @plane: plane object to init + * @possible_crtcs: bitmask of possible CRTCs + * @funcs: callbacks for the new plane + * @formats: array of supported formats (DRM_FORMAT\_\*) + * @format_count: number of elements in @formats + * @type: type of plane (overlay, primary, cursor) + * @name: printf style format string for the plane name, or NULL for default name + * + * Initializes a plane object of type @type. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, + unsigned long possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, unsigned int format_count, + enum drm_plane_type type, + const char *name, ...) +{ + struct drm_mode_config *config = &dev->mode_config; + int ret; + + ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); + if (ret) + return ret; + + drm_modeset_lock_init(&plane->mutex); + + plane->base.properties = &plane->properties; + plane->dev = dev; + plane->funcs = funcs; + plane->format_types = kmalloc_array(format_count, sizeof(uint32_t), + GFP_KERNEL); + if (!plane->format_types) { + DRM_DEBUG_KMS("out of memory when allocating plane\n"); + drm_mode_object_unregister(dev, &plane->base); + return -ENOMEM; + } + + if (name) { + va_list ap; + + va_start(ap, name); + plane->name = kvasprintf(GFP_KERNEL, name, ap); + va_end(ap); + } else { + plane->name = kasprintf(GFP_KERNEL, "plane-%d", + drm_num_planes(dev)); + } + if (!plane->name) { + kfree(plane->format_types); + drm_mode_object_unregister(dev, &plane->base); + return -ENOMEM; + } + + memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); + plane->format_count = format_count; + plane->possible_crtcs = possible_crtcs; + plane->type = type; + + list_add_tail(&plane->head, &config->plane_list); + plane->index = config->num_total_plane++; + if (plane->type == DRM_PLANE_TYPE_OVERLAY) + config->num_overlay_plane++; + + drm_object_attach_property(&plane->base, + config->plane_type_property, + plane->type); + + if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { + drm_object_attach_property(&plane->base, config->prop_fb_id, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_id, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_x, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_y, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_w, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_h, 0); + drm_object_attach_property(&plane->base, config->prop_src_x, 0); + drm_object_attach_property(&plane->base, config->prop_src_y, 0); + drm_object_attach_property(&plane->base, config->prop_src_w, 0); + drm_object_attach_property(&plane->base, config->prop_src_h, 0); + } + + return 0; +} +EXPORT_SYMBOL(drm_universal_plane_init); + +int drm_plane_register_all(struct drm_device *dev) +{ + struct drm_plane *plane; + int ret = 0; + + drm_for_each_plane(plane, dev) { + if (plane->funcs->late_register) + ret = plane->funcs->late_register(plane); + if (ret) + return ret; + } + + return 0; +} + +void drm_plane_unregister_all(struct drm_device *dev) +{ + struct drm_plane *plane; + + drm_for_each_plane(plane, dev) { + if (plane->funcs->early_unregister) + plane->funcs->early_unregister(plane); + } +} + +/** + * drm_plane_init - Initialize a legacy plane + * @dev: DRM device + * @plane: plane object to init + * @possible_crtcs: bitmask of possible CRTCs + * @funcs: callbacks for the new plane + * @formats: array of supported formats (DRM_FORMAT\_\*) + * @format_count: number of elements in @formats + * @is_primary: plane type (primary vs overlay) + * + * Legacy API to initialize a DRM plane. + * + * New drivers should call drm_universal_plane_init() instead. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, + unsigned long possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, unsigned int format_count, + bool is_primary) +{ + enum drm_plane_type type; + + type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; + return drm_universal_plane_init(dev, plane, possible_crtcs, funcs, + formats, format_count, type, NULL); +} +EXPORT_SYMBOL(drm_plane_init); + +/** + * drm_plane_cleanup - Clean up the core plane usage + * @plane: plane to cleanup + * + * This function cleans up @plane and removes it from the DRM mode setting + * core. Note that the function does *not* free the plane structure itself, + * this is the responsibility of the caller. + */ +void drm_plane_cleanup(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + + drm_modeset_lock_all(dev); + kfree(plane->format_types); + drm_mode_object_unregister(dev, &plane->base); + + BUG_ON(list_empty(&plane->head)); + + /* Note that the plane_list is considered to be static; should we + * remove the drm_plane at runtime we would have to decrement all + * the indices on the drm_plane after us in the plane_list. + */ + + list_del(&plane->head); + dev->mode_config.num_total_plane--; + if (plane->type == DRM_PLANE_TYPE_OVERLAY) + dev->mode_config.num_overlay_plane--; + drm_modeset_unlock_all(dev); + + WARN_ON(plane->state && !plane->funcs->atomic_destroy_state); + if (plane->state && plane->funcs->atomic_destroy_state) + plane->funcs->atomic_destroy_state(plane, plane->state); + + kfree(plane->name); + + memset(plane, 0, sizeof(*plane)); +} +EXPORT_SYMBOL(drm_plane_cleanup); + +/** + * drm_plane_from_index - find the registered plane at an index + * @dev: DRM device + * @idx: index of registered plane to find for + * + * Given a plane index, return the registered plane from DRM device's + * list of planes with matching index. + */ +struct drm_plane * +drm_plane_from_index(struct drm_device *dev, int idx) +{ + struct drm_plane *plane; + + drm_for_each_plane(plane, dev) + if (idx == plane->index) + return plane; + + return NULL; +} +EXPORT_SYMBOL(drm_plane_from_index); + +/** + * drm_plane_force_disable - Forcibly disable a plane + * @plane: plane to disable + * + * Forces the plane to be disabled. + * + * Used when the plane's current framebuffer is destroyed, + * and when restoring fbdev mode. + */ +void drm_plane_force_disable(struct drm_plane *plane) +{ + int ret; + + if (!plane->fb) + return; + + plane->old_fb = plane->fb; + ret = plane->funcs->disable_plane(plane); + if (ret) { + DRM_ERROR("failed to disable plane with busy fb\n"); + plane->old_fb = NULL; + return; + } + /* disconnect the plane from the fb and crtc: */ + drm_framebuffer_unreference(plane->old_fb); + plane->old_fb = NULL; + plane->fb = NULL; + plane->crtc = NULL; +} +EXPORT_SYMBOL(drm_plane_force_disable); + +/** + * drm_mode_plane_set_obj_prop - set the value of a property + * @plane: drm plane object to set property value for + * @property: property to set + * @value: value the property should be set to + * + * This functions sets a given property on a given plane object. This function + * calls the driver's ->set_property callback and changes the software state of + * the property if the callback succeeds. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_mode_plane_set_obj_prop(struct drm_plane *plane, + struct drm_property *property, + uint64_t value) +{ + int ret = -EINVAL; + struct drm_mode_object *obj = &plane->base; + + if (plane->funcs->set_property) + ret = plane->funcs->set_property(plane, property, value); + if (!ret) + drm_object_property_set_value(obj, property, value); + + return ret; +} +EXPORT_SYMBOL(drm_mode_plane_set_obj_prop); + +int drm_mode_getplane_res(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_mode_get_plane_res *plane_resp = data; + struct drm_mode_config *config; + struct drm_plane *plane; + uint32_t __user *plane_ptr; + int copied = 0; + unsigned num_planes; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + config = &dev->mode_config; + + if (file_priv->universal_planes) + num_planes = config->num_total_plane; + else + num_planes = config->num_overlay_plane; + + /* + * This ioctl is called twice, once to determine how much space is + * needed, and the 2nd time to fill it. + */ + if (num_planes && + (plane_resp->count_planes >= num_planes)) { + plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; + + /* Plane lists are invariant, no locking needed. */ + drm_for_each_plane(plane, dev) { + /* + * Unless userspace set the 'universal planes' + * capability bit, only advertise overlays. + */ + if (plane->type != DRM_PLANE_TYPE_OVERLAY && + !file_priv->universal_planes) + continue; + + if (put_user(plane->base.id, plane_ptr + copied)) + return -EFAULT; + copied++; + } + } + plane_resp->count_planes = num_planes; + + return 0; +} + +int drm_mode_getplane(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_mode_get_plane *plane_resp = data; + struct drm_plane *plane; + uint32_t __user *format_ptr; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + plane = drm_plane_find(dev, plane_resp->plane_id); + if (!plane) + return -ENOENT; + + drm_modeset_lock(&plane->mutex, NULL); + if (plane->crtc) + plane_resp->crtc_id = plane->crtc->base.id; + else + plane_resp->crtc_id = 0; + + if (plane->fb) + plane_resp->fb_id = plane->fb->base.id; + else + plane_resp->fb_id = 0; + drm_modeset_unlock(&plane->mutex); + + plane_resp->plane_id = plane->base.id; + plane_resp->possible_crtcs = plane->possible_crtcs; + plane_resp->gamma_size = 0; + + /* + * This ioctl is called twice, once to determine how much space is + * needed, and the 2nd time to fill it. + */ + if (plane->format_count && + (plane_resp->count_format_types >= plane->format_count)) { + format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr; + if (copy_to_user(format_ptr, + plane->format_types, + sizeof(uint32_t) * plane->format_count)) { + return -EFAULT; + } + } + plane_resp->count_format_types = plane->format_count; + + return 0; +} + +int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format) +{ + unsigned int i; + + for (i = 0; i < plane->format_count; i++) { + if (format == plane->format_types[i]) + return 0; + } + + return -EINVAL; +} + +/* + * setplane_internal - setplane handler for internal callers + * + * Note that we assume an extra reference has already been taken on fb. If the + * update fails, this reference will be dropped before return; if it succeeds, + * the previous framebuffer (if any) will be unreferenced instead. + * + * src_{x,y,w,h} are provided in 16.16 fixed point format + */ +static int __setplane_internal(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int32_t crtc_x, int32_t crtc_y, + uint32_t crtc_w, uint32_t crtc_h, + /* src_{x,y,w,h} values are 16.16 fixed point */ + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + int ret = 0; + + /* No fb means shut it down */ + if (!fb) { + plane->old_fb = plane->fb; + ret = plane->funcs->disable_plane(plane); + if (!ret) { + plane->crtc = NULL; + plane->fb = NULL; + } else { + plane->old_fb = NULL; + } + goto out; + } + + /* Check whether this plane is usable on this CRTC */ + if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) { + DRM_DEBUG_KMS("Invalid crtc for plane\n"); + ret = -EINVAL; + goto out; + } + + /* Check whether this plane supports the fb pixel format. */ + ret = drm_plane_check_pixel_format(plane, fb->pixel_format); + if (ret) { + char *format_name = drm_get_format_name(fb->pixel_format); + DRM_DEBUG_KMS("Invalid pixel format %s\n", format_name); + kfree(format_name); + goto out; + } + + /* Give drivers some help against integer overflows */ + if (crtc_w > INT_MAX || + crtc_x > INT_MAX - (int32_t) crtc_w || + crtc_h > INT_MAX || + crtc_y > INT_MAX - (int32_t) crtc_h) { + DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", + crtc_w, crtc_h, crtc_x, crtc_y); + ret = -ERANGE; + goto out; + } + + ret = drm_framebuffer_check_src_coords(src_x, src_y, src_w, src_h, fb); + if (ret) + goto out; + + plane->old_fb = plane->fb; + ret = plane->funcs->update_plane(plane, crtc, fb, + crtc_x, crtc_y, crtc_w, crtc_h, + src_x, src_y, src_w, src_h); + if (!ret) { + plane->crtc = crtc; + plane->fb = fb; + fb = NULL; + } else { + plane->old_fb = NULL; + } + +out: + if (fb) + drm_framebuffer_unreference(fb); + if (plane->old_fb) + drm_framebuffer_unreference(plane->old_fb); + plane->old_fb = NULL; + + return ret; +} + +static int setplane_internal(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int32_t crtc_x, int32_t crtc_y, + uint32_t crtc_w, uint32_t crtc_h, + /* src_{x,y,w,h} values are 16.16 fixed point */ + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + int ret; + + drm_modeset_lock_all(plane->dev); + ret = __setplane_internal(plane, crtc, fb, + crtc_x, crtc_y, crtc_w, crtc_h, + src_x, src_y, src_w, src_h); + drm_modeset_unlock_all(plane->dev); + + return ret; +} + +int drm_mode_setplane(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_mode_set_plane *plane_req = data; + struct drm_plane *plane; + struct drm_crtc *crtc = NULL; + struct drm_framebuffer *fb = NULL; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + /* + * First, find the plane, crtc, and fb objects. If not available, + * we don't bother to call the driver. + */ + plane = drm_plane_find(dev, plane_req->plane_id); + if (!plane) { + DRM_DEBUG_KMS("Unknown plane ID %d\n", + plane_req->plane_id); + return -ENOENT; + } + + if (plane_req->fb_id) { + fb = drm_framebuffer_lookup(dev, plane_req->fb_id); + if (!fb) { + DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", + plane_req->fb_id); + return -ENOENT; + } + + crtc = drm_crtc_find(dev, plane_req->crtc_id); + if (!crtc) { + DRM_DEBUG_KMS("Unknown crtc ID %d\n", + plane_req->crtc_id); + return -ENOENT; + } + } + + /* + * setplane_internal will take care of deref'ing either the old or new + * framebuffer depending on success. + */ + return setplane_internal(plane, crtc, fb, + plane_req->crtc_x, plane_req->crtc_y, + plane_req->crtc_w, plane_req->crtc_h, + plane_req->src_x, plane_req->src_y, + plane_req->src_w, plane_req->src_h); +} + +static int drm_mode_cursor_universal(struct drm_crtc *crtc, + struct drm_mode_cursor2 *req, + struct drm_file *file_priv) +{ + struct drm_device *dev = crtc->dev; + struct drm_framebuffer *fb = NULL; + struct drm_mode_fb_cmd2 fbreq = { + .width = req->width, + .height = req->height, + .pixel_format = DRM_FORMAT_ARGB8888, + .pitches = { req->width * 4 }, + .handles = { req->handle }, + }; + int32_t crtc_x, crtc_y; + uint32_t crtc_w = 0, crtc_h = 0; + uint32_t src_w = 0, src_h = 0; + int ret = 0; + + BUG_ON(!crtc->cursor); + WARN_ON(crtc->cursor->crtc != crtc && crtc->cursor->crtc != NULL); + + /* + * Obtain fb we'll be using (either new or existing) and take an extra + * reference to it if fb != null. setplane will take care of dropping + * the reference if the plane update fails. + */ + if (req->flags & DRM_MODE_CURSOR_BO) { + if (req->handle) { + fb = drm_internal_framebuffer_create(dev, &fbreq, file_priv); + if (IS_ERR(fb)) { + DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n"); + return PTR_ERR(fb); + } + fb->hot_x = req->hot_x; + fb->hot_y = req->hot_y; + } else { + fb = NULL; + } + } else { + fb = crtc->cursor->fb; + if (fb) + drm_framebuffer_reference(fb); + } + + if (req->flags & DRM_MODE_CURSOR_MOVE) { + crtc_x = req->x; + crtc_y = req->y; + } else { + crtc_x = crtc->cursor_x; + crtc_y = crtc->cursor_y; + } + + if (fb) { + crtc_w = fb->width; + crtc_h = fb->height; + src_w = fb->width << 16; + src_h = fb->height << 16; + } + + /* + * setplane_internal will take care of deref'ing either the old or new + * framebuffer depending on success. + */ + ret = __setplane_internal(crtc->cursor, crtc, fb, + crtc_x, crtc_y, crtc_w, crtc_h, + 0, 0, src_w, src_h); + + /* Update successful; save new cursor position, if necessary */ + if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) { + crtc->cursor_x = req->x; + crtc->cursor_y = req->y; + } + + return ret; +} + +static int drm_mode_cursor_common(struct drm_device *dev, + struct drm_mode_cursor2 *req, + struct drm_file *file_priv) +{ + struct drm_crtc *crtc; + int ret = 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) + return -EINVAL; + + crtc = drm_crtc_find(dev, req->crtc_id); + if (!crtc) { + DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); + return -ENOENT; + } + + /* + * If this crtc has a universal cursor plane, call that plane's update + * handler rather than using legacy cursor handlers. + */ + drm_modeset_lock_crtc(crtc, crtc->cursor); + if (crtc->cursor) { + ret = drm_mode_cursor_universal(crtc, req, file_priv); + goto out; + } + + if (req->flags & DRM_MODE_CURSOR_BO) { + if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { + ret = -ENXIO; + goto out; + } + /* Turns off the cursor if handle is 0 */ + if (crtc->funcs->cursor_set2) + ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle, + req->width, req->height, req->hot_x, req->hot_y); + else + ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, + req->width, req->height); + } + + if (req->flags & DRM_MODE_CURSOR_MOVE) { + if (crtc->funcs->cursor_move) { + ret = crtc->funcs->cursor_move(crtc, req->x, req->y); + } else { + ret = -EFAULT; + goto out; + } + } +out: + drm_modeset_unlock_crtc(crtc); + + return ret; + +} + + +int drm_mode_cursor_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_cursor *req = data; + struct drm_mode_cursor2 new_req; + + memcpy(&new_req, req, sizeof(struct drm_mode_cursor)); + new_req.hot_x = new_req.hot_y = 0; + + return drm_mode_cursor_common(dev, &new_req, file_priv); +} + +/* + * Set the cursor configuration based on user request. This implements the 2nd + * version of the cursor ioctl, which allows userspace to additionally specify + * the hotspot of the pointer. + */ +int drm_mode_cursor2_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_cursor2 *req = data; + + return drm_mode_cursor_common(dev, req, file_priv); +} + +int drm_mode_page_flip_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_crtc_page_flip_target *page_flip = data; + struct drm_crtc *crtc; + struct drm_framebuffer *fb = NULL; + struct drm_pending_vblank_event *e = NULL; + u32 target_vblank = page_flip->sequence; + int ret = -EINVAL; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS) + return -EINVAL; + + if (page_flip->sequence != 0 && !(page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET)) + return -EINVAL; + + /* Only one of the DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags + * can be specified + */ + if ((page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) == DRM_MODE_PAGE_FLIP_TARGET) + return -EINVAL; + + if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip) + return -EINVAL; + + crtc = drm_crtc_find(dev, page_flip->crtc_id); + if (!crtc) + return -ENOENT; + + drm_modeset_lock_crtc(crtc, crtc->primary); + if (crtc->primary->fb == NULL) { + /* The framebuffer is currently unbound, presumably + * due to a hotplug event, that userspace has not + * yet discovered. + */ + ret = -EBUSY; + goto out; + } + + if (crtc->funcs->page_flip == NULL) + goto out; + + fb = drm_framebuffer_lookup(dev, page_flip->fb_id); + if (!fb) { + ret = -ENOENT; + goto out; + } + + if (crtc->state) { + const struct drm_plane_state *state = crtc->primary->state; + + ret = drm_framebuffer_check_src_coords(state->src_x, + state->src_y, + state->src_w, + state->src_h, + fb); + } else { + ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb); + } + if (ret) + goto out; + + if (crtc->primary->fb->pixel_format != fb->pixel_format) { + DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); + ret = -EINVAL; + goto out; + } + + if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { + e = kzalloc(sizeof *e, GFP_KERNEL); + if (!e) { + ret = -ENOMEM; + goto out; + } + e->event.base.type = DRM_EVENT_FLIP_COMPLETE; + e->event.base.length = sizeof(e->event); + e->event.user_data = page_flip->user_data; + ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base); + if (ret) { + kfree(e); + goto out; + } + } + + crtc->primary->old_fb = crtc->primary->fb; + if (crtc->funcs->page_flip_target) { + u32 current_vblank; + int r; + + r = drm_crtc_vblank_get(crtc); + if (r) + return r; + + current_vblank = drm_crtc_vblank_count(crtc); + + switch (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) { + case DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE: + if ((int)(target_vblank - current_vblank) > 1) { + DRM_DEBUG("Invalid absolute flip target %u, " + "must be <= %u\n", target_vblank, + current_vblank + 1); + drm_crtc_vblank_put(crtc); + return -EINVAL; + } + break; + case DRM_MODE_PAGE_FLIP_TARGET_RELATIVE: + if (target_vblank != 0 && target_vblank != 1) { + DRM_DEBUG("Invalid relative flip target %u, " + "must be 0 or 1\n", target_vblank); + drm_crtc_vblank_put(crtc); + return -EINVAL; + } + target_vblank += current_vblank; + break; + default: + target_vblank = current_vblank + + !(page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC); + break; + } + } else if (crtc->funcs->page_flip == NULL || + (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET)) { + return -EINVAL; + } + + if (crtc->funcs->page_flip_target) + ret = crtc->funcs->page_flip_target(crtc, fb, e, + page_flip->flags, + target_vblank); + else + ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags); + if (ret) { + if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) + drm_event_cancel_free(dev, &e->base); + /* Keep the old fb, don't unref it. */ + crtc->primary->old_fb = NULL; + } else { + crtc->primary->fb = fb; + /* Unref only the old framebuffer. */ + fb = NULL; + } + +out: + if (ret && crtc->funcs->page_flip_target) + drm_crtc_vblank_put(crtc); + if (crtc->primary->old_fb) + drm_framebuffer_unreference(crtc->primary->old_fb); + crtc->primary->old_fb = NULL; + drm_modeset_unlock_crtc(crtc); + + return ret; +} diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 16c4a7b..7899fc1 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -64,18 +64,6 @@ */ /* - * This is the minimal list of formats that seem to be safe for modeset use - * with all current DRM drivers. Most hardware can actually support more - * formats than this and drivers may specify a more accurate list when - * creating the primary plane. However drivers that still call - * drm_plane_init() will use this minimal format list as the default. - */ -static const uint32_t safe_modeset_formats[] = { - DRM_FORMAT_XRGB8888, - DRM_FORMAT_ARGB8888, -}; - -/* * Returns the connectors currently associated with a CRTC. This function * should be called twice: once with a NULL connector list to retrieve * the list size, and once with the properly allocated list to be filled in. @@ -108,14 +96,9 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, } /** - * drm_plane_helper_check_update() - Check plane update for validity - * @plane: plane object to update - * @crtc: owning CRTC of owning plane - * @fb: framebuffer to flip onto plane - * @src: source coordinates in 16.16 fixed point - * @dest: integer destination coordinates + * drm_plane_helper_check_state() - Check plane state for validity + * @state: plane state to check * @clip: integer clipping coordinates - * @rotation: plane rotation * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point * @can_position: is it legal to position the plane such that it @@ -123,10 +106,9 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, * only be false for primary planes. * @can_update_disabled: can the plane be updated while the crtc * is disabled? - * @visible: output parameter indicating whether plane is still visible after - * clipping * - * Checks that a desired plane update is valid. Drivers that provide + * Checks that a desired plane update is valid, and updates various + * bits of derived state (clipped coordinates etc.). Drivers that provide * their own plane handling rather than helper-provided implementations may * still wish to call this function to avoid duplication of error checking * code. @@ -134,29 +116,38 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, * RETURNS: * Zero if update appears valid, error code on failure */ -int drm_plane_helper_check_update(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_rect *src, - struct drm_rect *dest, - const struct drm_rect *clip, - unsigned int rotation, - int min_scale, - int max_scale, - bool can_position, - bool can_update_disabled, - bool *visible) +int drm_plane_helper_check_state(struct drm_plane_state *state, + const struct drm_rect *clip, + int min_scale, + int max_scale, + bool can_position, + bool can_update_disabled) { + struct drm_crtc *crtc = state->crtc; + struct drm_framebuffer *fb = state->fb; + struct drm_rect *src = &state->src; + struct drm_rect *dst = &state->dst; + unsigned int rotation = state->rotation; int hscale, vscale; + src->x1 = state->src_x; + src->y1 = state->src_y; + src->x2 = state->src_x + state->src_w; + src->y2 = state->src_y + state->src_h; + + dst->x1 = state->crtc_x; + dst->y1 = state->crtc_y; + dst->x2 = state->crtc_x + state->crtc_w; + dst->y2 = state->crtc_y + state->crtc_h; + if (!fb) { - *visible = false; + state->visible = false; return 0; } /* crtc should only be NULL when disabling (i.e., !fb) */ if (WARN_ON(!crtc)) { - *visible = false; + state->visible = false; return 0; } @@ -168,20 +159,20 @@ int drm_plane_helper_check_update(struct drm_plane *plane, drm_rect_rotate(src, fb->width << 16, fb->height << 16, rotation); /* Check scaling */ - hscale = drm_rect_calc_hscale(src, dest, min_scale, max_scale); - vscale = drm_rect_calc_vscale(src, dest, min_scale, max_scale); + hscale = drm_rect_calc_hscale(src, dst, min_scale, max_scale); + vscale = drm_rect_calc_vscale(src, dst, min_scale, max_scale); if (hscale < 0 || vscale < 0) { DRM_DEBUG_KMS("Invalid scaling of plane\n"); - drm_rect_debug_print("src: ", src, true); - drm_rect_debug_print("dst: ", dest, false); + drm_rect_debug_print("src: ", &state->src, true); + drm_rect_debug_print("dst: ", &state->dst, false); return -ERANGE; } - *visible = drm_rect_clip_scaled(src, dest, clip, hscale, vscale); + state->visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale); drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation); - if (!*visible) + if (!state->visible) /* * Plane isn't visible; some drivers can handle this * so we just return success here. Drivers that can't @@ -191,15 +182,87 @@ int drm_plane_helper_check_update(struct drm_plane *plane, */ return 0; - if (!can_position && !drm_rect_equals(dest, clip)) { + if (!can_position && !drm_rect_equals(dst, clip)) { DRM_DEBUG_KMS("Plane must cover entire CRTC\n"); - drm_rect_debug_print("dst: ", dest, false); + drm_rect_debug_print("dst: ", dst, false); drm_rect_debug_print("clip: ", clip, false); return -EINVAL; } return 0; } +EXPORT_SYMBOL(drm_plane_helper_check_state); + +/** + * drm_plane_helper_check_update() - Check plane update for validity + * @plane: plane object to update + * @crtc: owning CRTC of owning plane + * @fb: framebuffer to flip onto plane + * @src: source coordinates in 16.16 fixed point + * @dst: integer destination coordinates + * @clip: integer clipping coordinates + * @rotation: plane rotation + * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point + * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point + * @can_position: is it legal to position the plane such that it + * doesn't cover the entire crtc? This will generally + * only be false for primary planes. + * @can_update_disabled: can the plane be updated while the crtc + * is disabled? + * @visible: output parameter indicating whether plane is still visible after + * clipping + * + * Checks that a desired plane update is valid. Drivers that provide + * their own plane handling rather than helper-provided implementations may + * still wish to call this function to avoid duplication of error checking + * code. + * + * RETURNS: + * Zero if update appears valid, error code on failure + */ +int drm_plane_helper_check_update(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_rect *src, + struct drm_rect *dst, + const struct drm_rect *clip, + unsigned int rotation, + int min_scale, + int max_scale, + bool can_position, + bool can_update_disabled, + bool *visible) +{ + struct drm_plane_state state = { + .plane = plane, + .crtc = crtc, + .fb = fb, + .src_x = src->x1, + .src_y = src->y1, + .src_w = drm_rect_width(src), + .src_h = drm_rect_height(src), + .crtc_x = dst->x1, + .crtc_y = dst->y1, + .crtc_w = drm_rect_width(dst), + .crtc_h = drm_rect_height(dst), + .rotation = rotation, + .visible = *visible, + }; + int ret; + + ret = drm_plane_helper_check_state(&state, clip, + min_scale, max_scale, + can_position, + can_update_disabled); + if (ret) + return ret; + + *src = state.src; + *dst = state.dst; + *visible = state.visible; + + return 0; +} EXPORT_SYMBOL(drm_plane_helper_check_update); /** @@ -274,7 +337,7 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip, - BIT(DRM_ROTATE_0), + DRM_ROTATE_0, DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, false, false, &visible); @@ -363,60 +426,6 @@ const struct drm_plane_funcs drm_primary_helper_funcs = { }; EXPORT_SYMBOL(drm_primary_helper_funcs); -static struct drm_plane *create_primary_plane(struct drm_device *dev) -{ - struct drm_plane *primary; - int ret; - - primary = kzalloc(sizeof(*primary), GFP_KERNEL); - if (primary == NULL) { - DRM_DEBUG_KMS("Failed to allocate primary plane\n"); - return NULL; - } - - /* - * Remove the format_default field from drm_plane when dropping - * this helper. - */ - primary->format_default = true; - - /* possible_crtc's will be filled in later by crtc_init */ - ret = drm_universal_plane_init(dev, primary, 0, - &drm_primary_helper_funcs, - safe_modeset_formats, - ARRAY_SIZE(safe_modeset_formats), - DRM_PLANE_TYPE_PRIMARY, NULL); - if (ret) { - kfree(primary); - primary = NULL; - } - - return primary; -} - -/** - * drm_crtc_init - Legacy CRTC initialization function - * @dev: DRM device - * @crtc: CRTC object to init - * @funcs: callbacks for the new CRTC - * - * Initialize a CRTC object with a default helper-provided primary plane and no - * cursor plane. - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, - const struct drm_crtc_funcs *funcs) -{ - struct drm_plane *primary; - - primary = create_primary_plane(dev); - return drm_crtc_init_with_planes(dev, crtc, primary, NULL, funcs, - NULL); -} -EXPORT_SYMBOL(drm_crtc_init); - int drm_plane_helper_commit(struct drm_plane *plane, struct drm_plane_state *plane_state, struct drm_framebuffer *old_fb) diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c index 2c819ef..0262698 100644 --- a/drivers/gpu/drm/drm_platform.c +++ b/drivers/gpu/drm/drm_platform.c @@ -48,8 +48,8 @@ static int drm_get_platform_dev(struct platform_device *platdev, DRM_DEBUG("\n"); dev = drm_dev_alloc(driver, &platdev->dev); - if (!dev) - return -ENOMEM; + if (IS_ERR(dev)) + return PTR_ERR(dev); dev->platformdev = platdev; diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index a0df377..f6b64d7 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -129,6 +129,7 @@ void drm_kms_helper_poll_enable_locked(struct drm_device *dev) { bool poll = false; struct drm_connector *connector; + unsigned long delay = DRM_OUTPUT_POLL_PERIOD; WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); @@ -141,8 +142,13 @@ void drm_kms_helper_poll_enable_locked(struct drm_device *dev) poll = true; } + if (dev->mode_config.delayed_event) { + poll = true; + delay = 0; + } + if (poll) - schedule_delayed_work(&dev->mode_config.output_poll_work, DRM_OUTPUT_POLL_PERIOD); + schedule_delayed_work(&dev->mode_config.output_poll_work, delay); } EXPORT_SYMBOL(drm_kms_helper_poll_enable_locked); diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c new file mode 100644 index 0000000..a4d81cf --- /dev/null +++ b/drivers/gpu/drm/drm_property.c @@ -0,0 +1,912 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <linux/export.h> +#include <drm/drmP.h> +#include <drm/drm_property.h> + +#include "drm_crtc_internal.h" + +/** + * DOC: overview + * + * Properties as represented by &drm_property are used to extend the modeset + * interface exposed to userspace. For the atomic modeset IOCTL properties are + * even the only way to transport metadata about the desired new modeset + * configuration from userspace to the kernel. Properties have a well-defined + * value range, which is enforced by the drm core. See the documentation of the + * flags member of struct &drm_property for an overview of the different + * property types and ranges. + * + * Properties don't store the current value directly, but need to be + * instatiated by attaching them to a &drm_mode_object with + * drm_object_attach_property(). + * + * Property values are only 64bit. To support bigger piles of data (like gamma + * tables, color correction matrizes or large structures) a property can instead + * point at a &drm_property_blob with that additional data + * + * Properties are defined by their symbolic name, userspace must keep a + * per-object mapping from those names to the property ID used in the atomic + * IOCTL and in the get/set property IOCTL. + */ + +static bool drm_property_type_valid(struct drm_property *property) +{ + if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) + return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE); + return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE); +} + +/** + * drm_property_create - create a new property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @num_values: number of pre-defined values + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy(), which is done automatically when calling + * drm_mode_config_cleanup(). + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ +struct drm_property *drm_property_create(struct drm_device *dev, int flags, + const char *name, int num_values) +{ + struct drm_property *property = NULL; + int ret; + + property = kzalloc(sizeof(struct drm_property), GFP_KERNEL); + if (!property) + return NULL; + + property->dev = dev; + + if (num_values) { + property->values = kcalloc(num_values, sizeof(uint64_t), + GFP_KERNEL); + if (!property->values) + goto fail; + } + + ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); + if (ret) + goto fail; + + property->flags = flags; + property->num_values = num_values; + INIT_LIST_HEAD(&property->enum_list); + + if (name) { + strncpy(property->name, name, DRM_PROP_NAME_LEN); + property->name[DRM_PROP_NAME_LEN-1] = '\0'; + } + + list_add_tail(&property->head, &dev->mode_config.property_list); + + WARN_ON(!drm_property_type_valid(property)); + + return property; +fail: + kfree(property->values); + kfree(property); + return NULL; +} +EXPORT_SYMBOL(drm_property_create); + +/** + * drm_property_create_enum - create a new enumeration property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @props: enumeration lists with property values + * @num_values: number of pre-defined values + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy(), which is done automatically when calling + * drm_mode_config_cleanup(). + * + * Userspace is only allowed to set one of the predefined values for enumeration + * properties. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ +struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, + const char *name, + const struct drm_prop_enum_list *props, + int num_values) +{ + struct drm_property *property; + int i, ret; + + flags |= DRM_MODE_PROP_ENUM; + + property = drm_property_create(dev, flags, name, num_values); + if (!property) + return NULL; + + for (i = 0; i < num_values; i++) { + ret = drm_property_add_enum(property, i, + props[i].type, + props[i].name); + if (ret) { + drm_property_destroy(dev, property); + return NULL; + } + } + + return property; +} +EXPORT_SYMBOL(drm_property_create_enum); + +/** + * drm_property_create_bitmask - create a new bitmask property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @props: enumeration lists with property bitflags + * @num_props: size of the @props array + * @supported_bits: bitmask of all supported enumeration values + * + * This creates a new bitmask drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy(), which is done automatically when calling + * drm_mode_config_cleanup(). + * + * Compared to plain enumeration properties userspace is allowed to set any + * or'ed together combination of the predefined property bitflag values + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ +struct drm_property *drm_property_create_bitmask(struct drm_device *dev, + int flags, const char *name, + const struct drm_prop_enum_list *props, + int num_props, + uint64_t supported_bits) +{ + struct drm_property *property; + int i, ret, index = 0; + int num_values = hweight64(supported_bits); + + flags |= DRM_MODE_PROP_BITMASK; + + property = drm_property_create(dev, flags, name, num_values); + if (!property) + return NULL; + for (i = 0; i < num_props; i++) { + if (!(supported_bits & (1ULL << props[i].type))) + continue; + + if (WARN_ON(index >= num_values)) { + drm_property_destroy(dev, property); + return NULL; + } + + ret = drm_property_add_enum(property, index++, + props[i].type, + props[i].name); + if (ret) { + drm_property_destroy(dev, property); + return NULL; + } + } + + return property; +} +EXPORT_SYMBOL(drm_property_create_bitmask); + +static struct drm_property *property_create_range(struct drm_device *dev, + int flags, const char *name, + uint64_t min, uint64_t max) +{ + struct drm_property *property; + + property = drm_property_create(dev, flags, name, 2); + if (!property) + return NULL; + + property->values[0] = min; + property->values[1] = max; + + return property; +} + +/** + * drm_property_create_range - create a new unsigned ranged property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @min: minimum value of the property + * @max: maximum value of the property + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy(), which is done automatically when calling + * drm_mode_config_cleanup(). + * + * Userspace is allowed to set any unsigned integer value in the (min, max) + * range inclusive. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ +struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, + const char *name, + uint64_t min, uint64_t max) +{ + return property_create_range(dev, DRM_MODE_PROP_RANGE | flags, + name, min, max); +} +EXPORT_SYMBOL(drm_property_create_range); + +/** + * drm_property_create_signed_range - create a new signed ranged property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @min: minimum value of the property + * @max: maximum value of the property + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy(), which is done automatically when calling + * drm_mode_config_cleanup(). + * + * Userspace is allowed to set any signed integer value in the (min, max) + * range inclusive. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ +struct drm_property *drm_property_create_signed_range(struct drm_device *dev, + int flags, const char *name, + int64_t min, int64_t max) +{ + return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags, + name, I642U64(min), I642U64(max)); +} +EXPORT_SYMBOL(drm_property_create_signed_range); + +/** + * drm_property_create_object - create a new object property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @type: object type from DRM_MODE_OBJECT_* defines + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy(), which is done automatically when calling + * drm_mode_config_cleanup(). + * + * Userspace is only allowed to set this to any property value of the given + * @type. Only useful for atomic properties, which is enforced. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ +struct drm_property *drm_property_create_object(struct drm_device *dev, + int flags, const char *name, + uint32_t type) +{ + struct drm_property *property; + + flags |= DRM_MODE_PROP_OBJECT; + + if (WARN_ON(!(flags & DRM_MODE_PROP_ATOMIC))) + return NULL; + + property = drm_property_create(dev, flags, name, 1); + if (!property) + return NULL; + + property->values[0] = type; + + return property; +} +EXPORT_SYMBOL(drm_property_create_object); + +/** + * drm_property_create_bool - create a new boolean property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy(), which is done automatically when calling + * drm_mode_config_cleanup(). + * + * This is implemented as a ranged property with only {0, 1} as valid values. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ +struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags, + const char *name) +{ + return drm_property_create_range(dev, flags, name, 0, 1); +} +EXPORT_SYMBOL(drm_property_create_bool); + +/** + * drm_property_add_enum - add a possible value to an enumeration property + * @property: enumeration property to change + * @index: index of the new enumeration + * @value: value of the new enumeration + * @name: symbolic name of the new enumeration + * + * This functions adds enumerations to a property. + * + * It's use is deprecated, drivers should use one of the more specific helpers + * to directly create the property with all enumerations already attached. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_property_add_enum(struct drm_property *property, int index, + uint64_t value, const char *name) +{ + struct drm_property_enum *prop_enum; + + if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) || + drm_property_type_is(property, DRM_MODE_PROP_BITMASK))) + return -EINVAL; + + /* + * Bitmask enum properties have the additional constraint of values + * from 0 to 63 + */ + if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) && + (value > 63)) + return -EINVAL; + + if (!list_empty(&property->enum_list)) { + list_for_each_entry(prop_enum, &property->enum_list, head) { + if (prop_enum->value == value) { + strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); + prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; + return 0; + } + } + } + + prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL); + if (!prop_enum) + return -ENOMEM; + + strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); + prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; + prop_enum->value = value; + + property->values[index] = value; + list_add_tail(&prop_enum->head, &property->enum_list); + return 0; +} +EXPORT_SYMBOL(drm_property_add_enum); + +/** + * drm_property_destroy - destroy a drm property + * @dev: drm device + * @property: property to destry + * + * This function frees a property including any attached resources like + * enumeration values. + */ +void drm_property_destroy(struct drm_device *dev, struct drm_property *property) +{ + struct drm_property_enum *prop_enum, *pt; + + list_for_each_entry_safe(prop_enum, pt, &property->enum_list, head) { + list_del(&prop_enum->head); + kfree(prop_enum); + } + + if (property->num_values) + kfree(property->values); + drm_mode_object_unregister(dev, &property->base); + list_del(&property->head); + kfree(property); +} +EXPORT_SYMBOL(drm_property_destroy); + +int drm_mode_getproperty_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_get_property *out_resp = data; + struct drm_property *property; + int enum_count = 0; + int value_count = 0; + int ret = 0, i; + int copied; + struct drm_property_enum *prop_enum; + struct drm_mode_property_enum __user *enum_ptr; + uint64_t __user *values_ptr; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + drm_modeset_lock_all(dev); + property = drm_property_find(dev, out_resp->prop_id); + if (!property) { + ret = -ENOENT; + goto done; + } + + if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || + drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { + list_for_each_entry(prop_enum, &property->enum_list, head) + enum_count++; + } + + value_count = property->num_values; + + strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); + out_resp->name[DRM_PROP_NAME_LEN-1] = 0; + out_resp->flags = property->flags; + + if ((out_resp->count_values >= value_count) && value_count) { + values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr; + for (i = 0; i < value_count; i++) { + if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { + ret = -EFAULT; + goto done; + } + } + } + out_resp->count_values = value_count; + + if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || + drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { + if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { + copied = 0; + enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; + list_for_each_entry(prop_enum, &property->enum_list, head) { + + if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { + ret = -EFAULT; + goto done; + } + + if (copy_to_user(&enum_ptr[copied].name, + &prop_enum->name, DRM_PROP_NAME_LEN)) { + ret = -EFAULT; + goto done; + } + copied++; + } + } + out_resp->count_enum_blobs = enum_count; + } + + /* + * NOTE: The idea seems to have been to use this to read all the blob + * property values. But nothing ever added them to the corresponding + * list, userspace always used the special-purpose get_blob ioctl to + * read the value for a blob property. It also doesn't make a lot of + * sense to return values here when everything else is just metadata for + * the property itself. + */ + if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) + out_resp->count_enum_blobs = 0; +done: + drm_modeset_unlock_all(dev); + return ret; +} + +static void drm_property_free_blob(struct kref *kref) +{ + struct drm_property_blob *blob = + container_of(kref, struct drm_property_blob, base.refcount); + + mutex_lock(&blob->dev->mode_config.blob_lock); + list_del(&blob->head_global); + mutex_unlock(&blob->dev->mode_config.blob_lock); + + drm_mode_object_unregister(blob->dev, &blob->base); + + kfree(blob); +} + +/** + * drm_property_create_blob - Create new blob property + * @dev: DRM device to create property for + * @length: Length to allocate for blob data + * @data: If specified, copies data into blob + * + * Creates a new blob property for a specified DRM device, optionally + * copying data. Note that blob properties are meant to be invariant, hence the + * data must be filled out before the blob is used as the value of any property. + * + * Returns: + * New blob property with a single reference on success, or an ERR_PTR + * value on failure. + */ +struct drm_property_blob * +drm_property_create_blob(struct drm_device *dev, size_t length, + const void *data) +{ + struct drm_property_blob *blob; + int ret; + + if (!length || length > ULONG_MAX - sizeof(struct drm_property_blob)) + return ERR_PTR(-EINVAL); + + blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); + if (!blob) + return ERR_PTR(-ENOMEM); + + /* This must be explicitly initialised, so we can safely call list_del + * on it in the removal handler, even if it isn't in a file list. */ + INIT_LIST_HEAD(&blob->head_file); + blob->length = length; + blob->dev = dev; + + if (data) + memcpy(blob->data, data, length); + + ret = drm_mode_object_get_reg(dev, &blob->base, DRM_MODE_OBJECT_BLOB, + true, drm_property_free_blob); + if (ret) { + kfree(blob); + return ERR_PTR(-EINVAL); + } + + mutex_lock(&dev->mode_config.blob_lock); + list_add_tail(&blob->head_global, + &dev->mode_config.property_blob_list); + mutex_unlock(&dev->mode_config.blob_lock); + + return blob; +} +EXPORT_SYMBOL(drm_property_create_blob); + +/** + * drm_property_unreference_blob - Unreference a blob property + * @blob: Pointer to blob property + * + * Drop a reference on a blob property. May free the object. + */ +void drm_property_unreference_blob(struct drm_property_blob *blob) +{ + if (!blob) + return; + + drm_mode_object_unreference(&blob->base); +} +EXPORT_SYMBOL(drm_property_unreference_blob); + +void drm_property_destroy_user_blobs(struct drm_device *dev, + struct drm_file *file_priv) +{ + struct drm_property_blob *blob, *bt; + + /* + * When the file gets released that means no one else can access the + * blob list any more, so no need to grab dev->blob_lock. + */ + list_for_each_entry_safe(blob, bt, &file_priv->blobs, head_file) { + list_del_init(&blob->head_file); + drm_property_unreference_blob(blob); + } +} + +/** + * drm_property_reference_blob - Take a reference on an existing property + * @blob: Pointer to blob property + * + * Take a new reference on an existing blob property. Returns @blob, which + * allows this to be used as a shorthand in assignments. + */ +struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob) +{ + drm_mode_object_reference(&blob->base); + return blob; +} +EXPORT_SYMBOL(drm_property_reference_blob); + +/** + * drm_property_lookup_blob - look up a blob property and take a reference + * @dev: drm device + * @id: id of the blob property + * + * If successful, this takes an additional reference to the blob property. + * callers need to make sure to eventually unreference the returned property + * again, using @drm_property_unreference_blob. + * + * Return: + * NULL on failure, pointer to the blob on success. + */ +struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, + uint32_t id) +{ + struct drm_mode_object *obj; + struct drm_property_blob *blob = NULL; + + obj = __drm_mode_object_find(dev, id, DRM_MODE_OBJECT_BLOB); + if (obj) + blob = obj_to_blob(obj); + return blob; +} +EXPORT_SYMBOL(drm_property_lookup_blob); + +/** + * drm_property_replace_global_blob - replace existing blob property + * @dev: drm device + * @replace: location of blob property pointer to be replaced + * @length: length of data for new blob, or 0 for no data + * @data: content for new blob, or NULL for no data + * @obj_holds_id: optional object for property holding blob ID + * @prop_holds_id: optional property holding blob ID + * @return 0 on success or error on failure + * + * This function will replace a global property in the blob list, optionally + * updating a property which holds the ID of that property. + * + * If length is 0 or data is NULL, no new blob will be created, and the holding + * property, if specified, will be set to 0. + * + * Access to the replace pointer is assumed to be protected by the caller, e.g. + * by holding the relevant modesetting object lock for its parent. + * + * For example, a drm_connector has a 'PATH' property, which contains the ID + * of a blob property with the value of the MST path information. Calling this + * function with replace pointing to the connector's path_blob_ptr, length and + * data set for the new path information, obj_holds_id set to the connector's + * base object, and prop_holds_id set to the path property name, will perform + * a completely atomic update. The access to path_blob_ptr is protected by the + * caller holding a lock on the connector. + */ +int drm_property_replace_global_blob(struct drm_device *dev, + struct drm_property_blob **replace, + size_t length, + const void *data, + struct drm_mode_object *obj_holds_id, + struct drm_property *prop_holds_id) +{ + struct drm_property_blob *new_blob = NULL; + struct drm_property_blob *old_blob = NULL; + int ret; + + WARN_ON(replace == NULL); + + old_blob = *replace; + + if (length && data) { + new_blob = drm_property_create_blob(dev, length, data); + if (IS_ERR(new_blob)) + return PTR_ERR(new_blob); + } + + if (obj_holds_id) { + ret = drm_object_property_set_value(obj_holds_id, + prop_holds_id, + new_blob ? + new_blob->base.id : 0); + if (ret != 0) + goto err_created; + } + + drm_property_unreference_blob(old_blob); + *replace = new_blob; + + return 0; + +err_created: + drm_property_unreference_blob(new_blob); + return ret; +} +EXPORT_SYMBOL(drm_property_replace_global_blob); + +int drm_mode_getblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_get_blob *out_resp = data; + struct drm_property_blob *blob; + int ret = 0; + void __user *blob_ptr; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + blob = drm_property_lookup_blob(dev, out_resp->blob_id); + if (!blob) + return -ENOENT; + + if (out_resp->length == blob->length) { + blob_ptr = (void __user *)(unsigned long)out_resp->data; + if (copy_to_user(blob_ptr, blob->data, blob->length)) { + ret = -EFAULT; + goto unref; + } + } + out_resp->length = blob->length; +unref: + drm_property_unreference_blob(blob); + + return ret; +} + +int drm_mode_createblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_create_blob *out_resp = data; + struct drm_property_blob *blob; + void __user *blob_ptr; + int ret = 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + blob = drm_property_create_blob(dev, out_resp->length, NULL); + if (IS_ERR(blob)) + return PTR_ERR(blob); + + blob_ptr = (void __user *)(unsigned long)out_resp->data; + if (copy_from_user(blob->data, blob_ptr, out_resp->length)) { + ret = -EFAULT; + goto out_blob; + } + + /* Dropping the lock between create_blob and our access here is safe + * as only the same file_priv can remove the blob; at this point, it is + * not associated with any file_priv. */ + mutex_lock(&dev->mode_config.blob_lock); + out_resp->blob_id = blob->base.id; + list_add_tail(&blob->head_file, &file_priv->blobs); + mutex_unlock(&dev->mode_config.blob_lock); + + return 0; + +out_blob: + drm_property_unreference_blob(blob); + return ret; +} + +int drm_mode_destroyblob_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_destroy_blob *out_resp = data; + struct drm_property_blob *blob = NULL, *bt; + bool found = false; + int ret = 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + blob = drm_property_lookup_blob(dev, out_resp->blob_id); + if (!blob) + return -ENOENT; + + mutex_lock(&dev->mode_config.blob_lock); + /* Ensure the property was actually created by this user. */ + list_for_each_entry(bt, &file_priv->blobs, head_file) { + if (bt == blob) { + found = true; + break; + } + } + + if (!found) { + ret = -EPERM; + goto err; + } + + /* We must drop head_file here, because we may not be the last + * reference on the blob. */ + list_del_init(&blob->head_file); + mutex_unlock(&dev->mode_config.blob_lock); + + /* One reference from lookup, and one from the filp. */ + drm_property_unreference_blob(blob); + drm_property_unreference_blob(blob); + + return 0; + +err: + mutex_unlock(&dev->mode_config.blob_lock); + drm_property_unreference_blob(blob); + + return ret; +} + +/* Some properties could refer to dynamic refcnt'd objects, or things that + * need special locking to handle lifetime issues (ie. to ensure the prop + * value doesn't become invalid part way through the property update due to + * race). The value returned by reference via 'obj' should be passed back + * to drm_property_change_valid_put() after the property is set (and the + * object to which the property is attached has a chance to take it's own + * reference). + */ +bool drm_property_change_valid_get(struct drm_property *property, + uint64_t value, struct drm_mode_object **ref) +{ + int i; + + if (property->flags & DRM_MODE_PROP_IMMUTABLE) + return false; + + *ref = NULL; + + if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) { + if (value < property->values[0] || value > property->values[1]) + return false; + return true; + } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) { + int64_t svalue = U642I64(value); + + if (svalue < U642I64(property->values[0]) || + svalue > U642I64(property->values[1])) + return false; + return true; + } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { + uint64_t valid_mask = 0; + + for (i = 0; i < property->num_values; i++) + valid_mask |= (1ULL << property->values[i]); + return !(value & ~valid_mask); + } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { + struct drm_property_blob *blob; + + if (value == 0) + return true; + + blob = drm_property_lookup_blob(property->dev, value); + if (blob) { + *ref = &blob->base; + return true; + } else { + return false; + } + } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { + /* a zero value for an object property translates to null: */ + if (value == 0) + return true; + + *ref = __drm_mode_object_find(property->dev, value, + property->values[0]); + return *ref != NULL; + } + + for (i = 0; i < property->num_values; i++) + if (property->values[i] == value) + return true; + return false; +} + +void drm_property_change_valid_put(struct drm_property *property, + struct drm_mode_object *ref) +{ + if (!ref) + return; + + if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { + drm_mode_object_unreference(ref); + } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) + drm_property_unreference_blob(obj_to_blob(ref)); +} diff --git a/drivers/gpu/drm/drm_rect.c b/drivers/gpu/drm/drm_rect.c index a8e2c86..73e53a8 100644 --- a/drivers/gpu/drm/drm_rect.c +++ b/drivers/gpu/drm/drm_rect.c @@ -100,7 +100,7 @@ static int drm_calc_scale(int src, int dst) { int scale = 0; - if (src < 0 || dst < 0) + if (WARN_ON(src < 0 || dst < 0)) return -EINVAL; if (dst == 0) @@ -317,38 +317,38 @@ void drm_rect_rotate(struct drm_rect *r, { struct drm_rect tmp; - if (rotation & (BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y))) { + if (rotation & (DRM_REFLECT_X | DRM_REFLECT_Y)) { tmp = *r; - if (rotation & BIT(DRM_REFLECT_X)) { + if (rotation & DRM_REFLECT_X) { r->x1 = width - tmp.x2; r->x2 = width - tmp.x1; } - if (rotation & BIT(DRM_REFLECT_Y)) { + if (rotation & DRM_REFLECT_Y) { r->y1 = height - tmp.y2; r->y2 = height - tmp.y1; } } switch (rotation & DRM_ROTATE_MASK) { - case BIT(DRM_ROTATE_0): + case DRM_ROTATE_0: break; - case BIT(DRM_ROTATE_90): + case DRM_ROTATE_90: tmp = *r; r->x1 = tmp.y1; r->x2 = tmp.y2; r->y1 = width - tmp.x2; r->y2 = width - tmp.x1; break; - case BIT(DRM_ROTATE_180): + case DRM_ROTATE_180: tmp = *r; r->x1 = width - tmp.x2; r->x2 = width - tmp.x1; r->y1 = height - tmp.y2; r->y2 = height - tmp.y1; break; - case BIT(DRM_ROTATE_270): + case DRM_ROTATE_270: tmp = *r; r->x1 = height - tmp.y2; r->x2 = height - tmp.y1; @@ -392,23 +392,23 @@ void drm_rect_rotate_inv(struct drm_rect *r, struct drm_rect tmp; switch (rotation & DRM_ROTATE_MASK) { - case BIT(DRM_ROTATE_0): + case DRM_ROTATE_0: break; - case BIT(DRM_ROTATE_90): + case DRM_ROTATE_90: tmp = *r; r->x1 = width - tmp.y2; r->x2 = width - tmp.y1; r->y1 = tmp.x1; r->y2 = tmp.x2; break; - case BIT(DRM_ROTATE_180): + case DRM_ROTATE_180: tmp = *r; r->x1 = width - tmp.x2; r->x2 = width - tmp.x1; r->y1 = height - tmp.y2; r->y2 = height - tmp.y1; break; - case BIT(DRM_ROTATE_270): + case DRM_ROTATE_270: tmp = *r; r->x1 = tmp.y1; r->x2 = tmp.y2; @@ -419,15 +419,15 @@ void drm_rect_rotate_inv(struct drm_rect *r, break; } - if (rotation & (BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y))) { + if (rotation & (DRM_REFLECT_X | DRM_REFLECT_Y)) { tmp = *r; - if (rotation & BIT(DRM_REFLECT_X)) { + if (rotation & DRM_REFLECT_X) { r->x1 = width - tmp.x2; r->x2 = width - tmp.x1; } - if (rotation & BIT(DRM_REFLECT_Y)) { + if (rotation & DRM_REFLECT_Y) { r->y1 = height - tmp.y2; r->y2 = height - tmp.y1; } diff --git a/drivers/gpu/drm/drm_scatter.c b/drivers/gpu/drm/drm_scatter.c index bf70431..275bca4 100644 --- a/drivers/gpu/drm/drm_scatter.c +++ b/drivers/gpu/drm/drm_scatter.c @@ -68,7 +68,7 @@ static void drm_sg_cleanup(struct drm_sg_mem * entry) void drm_legacy_sg_cleanup(struct drm_device *dev) { if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg && - !drm_core_check_feature(dev, DRIVER_MODESET)) { + drm_core_check_feature(dev, DRIVER_LEGACY)) { drm_sg_cleanup(dev->sg); dev->sg = NULL; } @@ -88,7 +88,7 @@ int drm_legacy_sg_alloc(struct drm_device *dev, void *data, DRM_DEBUG("\n"); - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; if (!drm_core_check_feature(dev, DRIVER_SG)) @@ -201,7 +201,7 @@ int drm_legacy_sg_free(struct drm_device *dev, void *data, struct drm_scatter_gather *request = data; struct drm_sg_mem *entry; - if (drm_core_check_feature(dev, DRIVER_MODESET)) + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) return -EINVAL; if (!drm_core_check_feature(dev, DRIVER_SG)) diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index 0db36d2..7b6d26e 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -34,6 +34,12 @@ static const struct drm_encoder_funcs drm_simple_kms_encoder_funcs = { .destroy = drm_encoder_cleanup, }; +static int drm_simple_kms_crtc_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + return drm_atomic_add_affected_planes(state->state, crtc); +} + static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc) { struct drm_simple_display_pipe *pipe; @@ -57,6 +63,7 @@ static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc) } static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = { + .atomic_check = drm_simple_kms_crtc_check, .disable = drm_simple_kms_crtc_disable, .enable = drm_simple_kms_crtc_enable, }; @@ -73,22 +80,9 @@ static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = { static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *plane_state) { - struct drm_rect src = { - .x1 = plane_state->src_x, - .y1 = plane_state->src_y, - .x2 = plane_state->src_x + plane_state->src_w, - .y2 = plane_state->src_y + plane_state->src_h, - }; - struct drm_rect dest = { - .x1 = plane_state->crtc_x, - .y1 = plane_state->crtc_y, - .x2 = plane_state->crtc_x + plane_state->crtc_w, - .y2 = plane_state->crtc_y + plane_state->crtc_h, - }; struct drm_rect clip = { 0 }; struct drm_simple_display_pipe *pipe; struct drm_crtc_state *crtc_state; - bool visible; int ret; pipe = container_of(plane, struct drm_simple_display_pipe, plane); @@ -102,17 +96,15 @@ static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - ret = drm_plane_helper_check_update(plane, &pipe->crtc, - plane_state->fb, - &src, &dest, &clip, - plane_state->rotation, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - false, true, &visible); + + ret = drm_plane_helper_check_state(plane_state, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true); if (ret) return ret; - if (!visible) + if (!plane_state->visible) return -EINVAL; if (!pipe->funcs || !pipe->funcs->check) @@ -148,16 +140,61 @@ static const struct drm_plane_funcs drm_simple_kms_plane_funcs = { }; /** + * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe + * @pipe: simple display pipe object + * @bridge: bridge to attach + * + * Makes it possible to still use the drm_simple_display_pipe helpers when + * a DRM bridge has to be used. + * + * Note that you probably want to initialize the pipe by passing a NULL + * connector to drm_simple_display_pipe_init(). + * + * Returns: + * Zero on success, negative error code on failure. + */ +int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe, + struct drm_bridge *bridge) +{ + bridge->encoder = &pipe->encoder; + pipe->encoder.bridge = bridge; + return drm_bridge_attach(pipe->encoder.dev, bridge); +} +EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge); + +/** + * drm_simple_display_pipe_detach_bridge - Detach the bridge from the display pipe + * @pipe: simple display pipe object + * + * Detaches the drm bridge previously attached with + * drm_simple_display_pipe_attach_bridge() + */ +void drm_simple_display_pipe_detach_bridge(struct drm_simple_display_pipe *pipe) +{ + if (WARN_ON(!pipe->encoder.bridge)) + return; + + drm_bridge_detach(pipe->encoder.bridge); + pipe->encoder.bridge = NULL; +} +EXPORT_SYMBOL(drm_simple_display_pipe_detach_bridge); + +/** * drm_simple_display_pipe_init - Initialize a simple display pipeline * @dev: DRM device * @pipe: simple display pipe object to initialize * @funcs: callbacks for the display pipe (optional) - * @formats: array of supported formats (%DRM_FORMAT_*) + * @formats: array of supported formats (DRM_FORMAT\_\*) * @format_count: number of elements in @formats - * @connector: connector to attach and register + * @connector: connector to attach and register (optional) * * Sets up a display pipeline which consist of a really simple - * plane-crtc-encoder pipe coupled with the provided connector. + * plane-crtc-encoder pipe. + * + * If a connector is supplied, the pipe will be coupled with the provided + * connector. You may supply a NULL connector when using drm bridges, that + * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()). + * * Teardown of a simple display pipe is all handled automatically by the drm * core through calling drm_mode_config_cleanup(). Drivers afterwards need to * release the memory for the structure themselves. @@ -196,7 +233,7 @@ int drm_simple_display_pipe_init(struct drm_device *dev, encoder->possible_crtcs = 1 << drm_crtc_index(crtc); ret = drm_encoder_init(dev, encoder, &drm_simple_kms_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL); - if (ret) + if (ret || !connector) return ret; return drm_mode_connector_attach_encoder(connector, encoder); diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 32dd821..9a37196 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -19,7 +19,6 @@ #include <linux/export.h> #include <drm/drm_sysfs.h> -#include <drm/drm_core.h> #include <drm/drmP.h> #include "drm_internal.h" @@ -37,12 +36,7 @@ static char *drm_devnode(struct device *dev, umode_t *mode) return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev)); } -static CLASS_ATTR_STRING(version, S_IRUGO, - CORE_NAME " " - __stringify(CORE_MAJOR) "." - __stringify(CORE_MINOR) "." - __stringify(CORE_PATCHLEVEL) " " - CORE_DATE); +static CLASS_ATTR_STRING(version, S_IRUGO, "drm 1.1.0 20060810"); /** * drm_sysfs_init - initialize sysfs helpers diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c index f306c88..20cc33d 100644 --- a/drivers/gpu/drm/drm_vma_manager.c +++ b/drivers/gpu/drm/drm_vma_manager.c @@ -25,7 +25,6 @@ #include <drm/drmP.h> #include <drm/drm_mm.h> #include <drm/drm_vma_manager.h> -#include <linux/fs.h> #include <linux/mm.h> #include <linux/module.h> #include <linux/rbtree.h> @@ -86,7 +85,6 @@ void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr, unsigned long page_offset, unsigned long size) { rwlock_init(&mgr->vm_lock); - mgr->vm_addr_space_rb = RB_ROOT; drm_mm_init(&mgr->vm_addr_space_mm, page_offset, size); } EXPORT_SYMBOL(drm_vma_offset_manager_init); @@ -145,16 +143,16 @@ struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_m unsigned long start, unsigned long pages) { - struct drm_vma_offset_node *node, *best; + struct drm_mm_node *node, *best; struct rb_node *iter; unsigned long offset; - iter = mgr->vm_addr_space_rb.rb_node; + iter = mgr->vm_addr_space_mm.interval_tree.rb_node; best = NULL; while (likely(iter)) { - node = rb_entry(iter, struct drm_vma_offset_node, vm_rb); - offset = node->vm_node.start; + node = rb_entry(iter, struct drm_mm_node, rb); + offset = node->start; if (start >= offset) { iter = iter->rb_right; best = node; @@ -167,38 +165,17 @@ struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_m /* verify that the node spans the requested area */ if (best) { - offset = best->vm_node.start + best->vm_node.size; + offset = best->start + best->size; if (offset < start + pages) best = NULL; } - return best; -} -EXPORT_SYMBOL(drm_vma_offset_lookup_locked); - -/* internal helper to link @node into the rb-tree */ -static void _drm_vma_offset_add_rb(struct drm_vma_offset_manager *mgr, - struct drm_vma_offset_node *node) -{ - struct rb_node **iter = &mgr->vm_addr_space_rb.rb_node; - struct rb_node *parent = NULL; - struct drm_vma_offset_node *iter_node; - - while (likely(*iter)) { - parent = *iter; - iter_node = rb_entry(*iter, struct drm_vma_offset_node, vm_rb); + if (!best) + return NULL; - if (node->vm_node.start < iter_node->vm_node.start) - iter = &(*iter)->rb_left; - else if (node->vm_node.start > iter_node->vm_node.start) - iter = &(*iter)->rb_right; - else - BUG(); - } - - rb_link_node(&node->vm_rb, parent, iter); - rb_insert_color(&node->vm_rb, &mgr->vm_addr_space_rb); + return container_of(best, struct drm_vma_offset_node, vm_node); } +EXPORT_SYMBOL(drm_vma_offset_lookup_locked); /** * drm_vma_offset_add() - Add offset node to manager @@ -240,8 +217,6 @@ int drm_vma_offset_add(struct drm_vma_offset_manager *mgr, if (ret) goto out_unlock; - _drm_vma_offset_add_rb(mgr, node); - out_unlock: write_unlock(&mgr->vm_lock); return ret; @@ -265,7 +240,6 @@ void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr, write_lock(&mgr->vm_lock); if (drm_mm_node_allocated(&node->vm_node)) { - rb_erase(&node->vm_rb, &mgr->vm_addr_space_rb); drm_mm_remove_node(&node->vm_node); memset(&node->vm_node, 0, sizeof(node->vm_node)); } @@ -277,9 +251,9 @@ EXPORT_SYMBOL(drm_vma_offset_remove); /** * drm_vma_node_allow - Add open-file to list of allowed users * @node: Node to modify - * @filp: Open file to add + * @tag: Tag of file to remove * - * Add @filp to the list of allowed open-files for this node. If @filp is + * Add @tag to the list of allowed open-files for this node. If @tag is * already on this list, the ref-count is incremented. * * The list of allowed-users is preserved across drm_vma_offset_add() and @@ -294,7 +268,7 @@ EXPORT_SYMBOL(drm_vma_offset_remove); * RETURNS: * 0 on success, negative error code on internal failure (out-of-mem) */ -int drm_vma_node_allow(struct drm_vma_offset_node *node, struct file *filp) +int drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *tag) { struct rb_node **iter; struct rb_node *parent = NULL; @@ -315,10 +289,10 @@ int drm_vma_node_allow(struct drm_vma_offset_node *node, struct file *filp) parent = *iter; entry = rb_entry(*iter, struct drm_vma_offset_file, vm_rb); - if (filp == entry->vm_filp) { + if (tag == entry->vm_tag) { entry->vm_count++; goto unlock; - } else if (filp > entry->vm_filp) { + } else if (tag > entry->vm_tag) { iter = &(*iter)->rb_right; } else { iter = &(*iter)->rb_left; @@ -330,7 +304,7 @@ int drm_vma_node_allow(struct drm_vma_offset_node *node, struct file *filp) goto unlock; } - new->vm_filp = filp; + new->vm_tag = tag; new->vm_count = 1; rb_link_node(&new->vm_rb, parent, iter); rb_insert_color(&new->vm_rb, &node->vm_files); @@ -346,17 +320,18 @@ EXPORT_SYMBOL(drm_vma_node_allow); /** * drm_vma_node_revoke - Remove open-file from list of allowed users * @node: Node to modify - * @filp: Open file to remove + * @tag: Tag of file to remove * - * Decrement the ref-count of @filp in the list of allowed open-files on @node. - * If the ref-count drops to zero, remove @filp from the list. You must call - * this once for every drm_vma_node_allow() on @filp. + * Decrement the ref-count of @tag in the list of allowed open-files on @node. + * If the ref-count drops to zero, remove @tag from the list. You must call + * this once for every drm_vma_node_allow() on @tag. * * This is locked against concurrent access internally. * - * If @filp is not on the list, nothing is done. + * If @tag is not on the list, nothing is done. */ -void drm_vma_node_revoke(struct drm_vma_offset_node *node, struct file *filp) +void drm_vma_node_revoke(struct drm_vma_offset_node *node, + struct drm_file *tag) { struct drm_vma_offset_file *entry; struct rb_node *iter; @@ -366,13 +341,13 @@ void drm_vma_node_revoke(struct drm_vma_offset_node *node, struct file *filp) iter = node->vm_files.rb_node; while (likely(iter)) { entry = rb_entry(iter, struct drm_vma_offset_file, vm_rb); - if (filp == entry->vm_filp) { + if (tag == entry->vm_tag) { if (!--entry->vm_count) { rb_erase(&entry->vm_rb, &node->vm_files); kfree(entry); } break; - } else if (filp > entry->vm_filp) { + } else if (tag > entry->vm_tag) { iter = iter->rb_right; } else { iter = iter->rb_left; @@ -386,9 +361,9 @@ EXPORT_SYMBOL(drm_vma_node_revoke); /** * drm_vma_node_is_allowed - Check whether an open-file is granted access * @node: Node to check - * @filp: Open-file to check for + * @tag: Tag of file to remove * - * Search the list in @node whether @filp is currently on the list of allowed + * Search the list in @node whether @tag is currently on the list of allowed * open-files (see drm_vma_node_allow()). * * This is locked against concurrent access internally. @@ -397,7 +372,7 @@ EXPORT_SYMBOL(drm_vma_node_revoke); * true iff @filp is on the list */ bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node, - struct file *filp) + struct drm_file *tag) { struct drm_vma_offset_file *entry; struct rb_node *iter; @@ -407,9 +382,9 @@ bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node, iter = node->vm_files.rb_node; while (likely(iter)) { entry = rb_entry(iter, struct drm_vma_offset_file, vm_rb); - if (filp == entry->vm_filp) + if (tag == entry->vm_tag) break; - else if (filp > entry->vm_filp) + else if (tag > entry->vm_tag) iter = iter->rb_right; else iter = iter->rb_left; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c index d8d5564..cb86c7e 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c @@ -21,6 +21,7 @@ #include "common.xml.h" #include "state.xml.h" +#include "state_hi.xml.h" #include "state_3d.xml.h" #include "cmdstream.xml.h" @@ -117,11 +118,6 @@ static void etnaviv_cmd_select_pipe(struct etnaviv_gpu *gpu, VIVS_GL_PIPE_SELECT_PIPE(pipe)); } -static u32 gpu_va(struct etnaviv_gpu *gpu, struct etnaviv_cmdbuf *buf) -{ - return buf->paddr - gpu->memory_base; -} - static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu, struct etnaviv_cmdbuf *buf, u32 off, u32 len) { @@ -129,7 +125,7 @@ static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu, u32 *ptr = buf->vaddr + off; dev_info(gpu->dev, "virt %p phys 0x%08x free 0x%08x\n", - ptr, gpu_va(gpu, buf) + off, size - len * 4 - off); + ptr, etnaviv_iommu_get_cmdbuf_va(gpu, buf) + off, size - len * 4 - off); print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4, ptr, len * 4, 0); @@ -162,7 +158,7 @@ static u32 etnaviv_buffer_reserve(struct etnaviv_gpu *gpu, if (buffer->user_size + cmd_dwords * sizeof(u64) > buffer->size) buffer->user_size = 0; - return gpu_va(gpu, buffer) + buffer->user_size; + return etnaviv_iommu_get_cmdbuf_va(gpu, buffer) + buffer->user_size; } u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu) @@ -173,7 +169,41 @@ u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu) buffer->user_size = 0; CMD_WAIT(buffer); - CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + buffer->user_size - 4); + CMD_LINK(buffer, 2, etnaviv_iommu_get_cmdbuf_va(gpu, buffer) + + buffer->user_size - 4); + + return buffer->user_size / 8; +} + +u16 etnaviv_buffer_config_mmuv2(struct etnaviv_gpu *gpu, u32 mtlb_addr, u32 safe_addr) +{ + struct etnaviv_cmdbuf *buffer = gpu->buffer; + + buffer->user_size = 0; + + if (gpu->identity.features & chipFeatures_PIPE_3D) { + CMD_LOAD_STATE(buffer, VIVS_GL_PIPE_SELECT, + VIVS_GL_PIPE_SELECT_PIPE(ETNA_PIPE_3D)); + CMD_LOAD_STATE(buffer, VIVS_MMUv2_CONFIGURATION, + mtlb_addr | VIVS_MMUv2_CONFIGURATION_MODE_MODE4_K); + CMD_LOAD_STATE(buffer, VIVS_MMUv2_SAFE_ADDRESS, safe_addr); + CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); + CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); + } + + if (gpu->identity.features & chipFeatures_PIPE_2D) { + CMD_LOAD_STATE(buffer, VIVS_GL_PIPE_SELECT, + VIVS_GL_PIPE_SELECT_PIPE(ETNA_PIPE_2D)); + CMD_LOAD_STATE(buffer, VIVS_MMUv2_CONFIGURATION, + mtlb_addr | VIVS_MMUv2_CONFIGURATION_MODE_MODE4_K); + CMD_LOAD_STATE(buffer, VIVS_MMUv2_SAFE_ADDRESS, safe_addr); + CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); + CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); + } + + CMD_END(buffer); + + buffer->user_size = ALIGN(buffer->user_size, 8); return buffer->user_size / 8; } @@ -231,7 +261,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, if (drm_debug & DRM_UT_DRIVER) etnaviv_buffer_dump(gpu, buffer, 0, 0x50); - link_target = gpu_va(gpu, cmdbuf); + link_target = etnaviv_iommu_get_cmdbuf_va(gpu, cmdbuf); link_dwords = cmdbuf->size / 8; /* @@ -246,8 +276,12 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, extra_dwords = 1; /* flush command */ - if (gpu->mmu->need_flush) - extra_dwords += 1; + if (gpu->mmu->need_flush) { + if (gpu->mmu->version == ETNAVIV_IOMMU_V1) + extra_dwords += 1; + else + extra_dwords += 3; + } /* pipe switch commands */ if (gpu->switch_context) @@ -257,12 +291,23 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, if (gpu->mmu->need_flush) { /* Add the MMU flush */ - CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_MMU, - VIVS_GL_FLUSH_MMU_FLUSH_FEMMU | - VIVS_GL_FLUSH_MMU_FLUSH_UNK1 | - VIVS_GL_FLUSH_MMU_FLUSH_UNK2 | - VIVS_GL_FLUSH_MMU_FLUSH_PEMMU | - VIVS_GL_FLUSH_MMU_FLUSH_UNK4); + if (gpu->mmu->version == ETNAVIV_IOMMU_V1) { + CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_MMU, + VIVS_GL_FLUSH_MMU_FLUSH_FEMMU | + VIVS_GL_FLUSH_MMU_FLUSH_UNK1 | + VIVS_GL_FLUSH_MMU_FLUSH_UNK2 | + VIVS_GL_FLUSH_MMU_FLUSH_PEMMU | + VIVS_GL_FLUSH_MMU_FLUSH_UNK4); + } else { + CMD_LOAD_STATE(buffer, VIVS_MMUv2_CONFIGURATION, + VIVS_MMUv2_CONFIGURATION_MODE_MASK | + VIVS_MMUv2_CONFIGURATION_ADDRESS_MASK | + VIVS_MMUv2_CONFIGURATION_FLUSH_FLUSH); + CMD_SEM(buffer, SYNC_RECIPIENT_FE, + SYNC_RECIPIENT_PE); + CMD_STALL(buffer, SYNC_RECIPIENT_FE, + SYNC_RECIPIENT_PE); + } gpu->mmu->need_flush = false; } @@ -301,7 +346,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, if (drm_debug & DRM_UT_DRIVER) pr_info("stream link to 0x%08x @ 0x%08x %p\n", - return_target, gpu_va(gpu, cmdbuf), cmdbuf->vaddr); + return_target, etnaviv_iommu_get_cmdbuf_va(gpu, cmdbuf), cmdbuf->vaddr); if (drm_debug & DRM_UT_DRIVER) { print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4, diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index ffd1b32..aa68766 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -488,8 +488,7 @@ static const struct file_operations fops = { }; static struct drm_driver etnaviv_drm_driver = { - .driver_features = DRIVER_HAVE_IRQ | - DRIVER_GEM | + .driver_features = DRIVER_GEM | DRIVER_PRIME | DRIVER_RENDER, .open = etnaviv_open, @@ -530,10 +529,8 @@ static int etnaviv_bind(struct device *dev) int ret; drm = drm_dev_alloc(&etnaviv_drm_driver, dev); - if (!drm) - return -ENOMEM; - - drm->platformdev = to_platform_device(dev); + if (IS_ERR(drm)) + return PTR_ERR(drm); priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index 115c5bc..65e0576 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -96,6 +96,7 @@ struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev, int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file, uintptr_t ptr, u32 size, u32 flags, u32 *handle); u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu); +u16 etnaviv_buffer_config_mmuv2(struct etnaviv_gpu *gpu, u32 mtlb_addr, u32 safe_addr); void etnaviv_buffer_end(struct etnaviv_gpu *gpu); void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct etnaviv_cmdbuf *cmdbuf); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_dump.c b/drivers/gpu/drm/etnaviv/etnaviv_dump.c index 4a29eea..2bef501 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_dump.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_dump.c @@ -175,11 +175,13 @@ void etnaviv_core_dump(struct etnaviv_gpu *gpu) etnaviv_core_dump_registers(&iter, gpu); etnaviv_core_dump_mmu(&iter, gpu, mmu_size); etnaviv_core_dump_mem(&iter, ETDUMP_BUF_RING, gpu->buffer->vaddr, - gpu->buffer->size, gpu->buffer->paddr); + gpu->buffer->size, + etnaviv_iommu_get_cmdbuf_va(gpu, gpu->buffer)); list_for_each_entry(cmd, &gpu->active_cmd_list, node) etnaviv_core_dump_mem(&iter, ETDUMP_BUF_CMD, cmd->vaddr, - cmd->size, cmd->paddr); + cmd->size, + etnaviv_iommu_get_cmdbuf_va(gpu, cmd)); /* Reserve space for the bomap */ if (n_bomap_pages) { diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 87ef341..b1254f8 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -22,8 +22,6 @@ #include "etnaviv_gpu.h" #include "etnaviv_gem.h" #include "etnaviv_mmu.h" -#include "etnaviv_iommu.h" -#include "etnaviv_iommu_v2.h" #include "common.xml.h" #include "state.xml.h" #include "state_hi.xml.h" @@ -329,6 +327,18 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu) gpu->identity.revision = 0x1051; } } + + /* + * NXP likes to call the GPU on the i.MX6QP GC2000+, but in + * reality it's just a re-branded GC3000. We can identify this + * core by the upper half of the revision register being all 1. + * Fix model/rev here, so all other places can refer to this + * core by its real identity. + */ + if (etnaviv_is_model_rev(gpu, GC2000, 0xffff5450)) { + gpu->identity.model = chipModel_GC3000; + gpu->identity.revision &= 0xffff; + } } dev_info(gpu->dev, "model: GC%x, revision: %x\n", @@ -528,6 +538,14 @@ static void etnaviv_gpu_enable_mlcg(struct etnaviv_gpu *gpu) gpu_write(gpu, VIVS_PM_MODULE_CONTROLS, pmc); } +void etnaviv_gpu_start_fe(struct etnaviv_gpu *gpu, u32 address, u16 prefetch) +{ + gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS, address); + gpu_write(gpu, VIVS_FE_COMMAND_CONTROL, + VIVS_FE_COMMAND_CONTROL_ENABLE | + VIVS_FE_COMMAND_CONTROL_PREFETCH(prefetch)); +} + static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu) { u16 prefetch; @@ -568,33 +586,20 @@ static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu) gpu_write(gpu, VIVS_MC_BUS_CONFIG, bus_config); } - /* set base addresses */ - gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, gpu->memory_base); - gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, gpu->memory_base); - gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_TX, gpu->memory_base); - gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, gpu->memory_base); - gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, gpu->memory_base); - - /* setup the MMU page table pointers */ - etnaviv_iommu_domain_restore(gpu, gpu->mmu->domain); + /* setup the MMU */ + etnaviv_iommu_restore(gpu); /* Start command processor */ prefetch = etnaviv_buffer_init(gpu); gpu_write(gpu, VIVS_HI_INTR_ENBL, ~0U); - gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS, - gpu->buffer->paddr - gpu->memory_base); - gpu_write(gpu, VIVS_FE_COMMAND_CONTROL, - VIVS_FE_COMMAND_CONTROL_ENABLE | - VIVS_FE_COMMAND_CONTROL_PREFETCH(prefetch)); + etnaviv_gpu_start_fe(gpu, etnaviv_iommu_get_cmdbuf_va(gpu, gpu->buffer), + prefetch); } int etnaviv_gpu_init(struct etnaviv_gpu *gpu) { int ret, i; - struct iommu_domain *iommu; - enum etnaviv_iommu_version version; - bool mmuv2; ret = pm_runtime_get_sync(gpu->dev); if (ret < 0) { @@ -642,32 +647,10 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) goto fail; } - /* Setup IOMMU.. eventually we will (I think) do this once per context - * and have separate page tables per context. For now, to keep things - * simple and to get something working, just use a single address space: - */ - mmuv2 = gpu->identity.minor_features1 & chipMinorFeatures1_MMU_VERSION; - dev_dbg(gpu->dev, "mmuv2: %d\n", mmuv2); - - if (!mmuv2) { - iommu = etnaviv_iommu_domain_alloc(gpu); - version = ETNAVIV_IOMMU_V1; - } else { - iommu = etnaviv_iommu_v2_domain_alloc(gpu); - version = ETNAVIV_IOMMU_V2; - } - - if (!iommu) { - dev_err(gpu->dev, "Failed to allocate GPU IOMMU domain\n"); - ret = -ENOMEM; - goto fail; - } - - gpu->mmu = etnaviv_iommu_new(gpu, iommu, version); - if (!gpu->mmu) { + gpu->mmu = etnaviv_iommu_new(gpu); + if (IS_ERR(gpu->mmu)) { dev_err(gpu->dev, "Failed to instantiate GPU IOMMU\n"); - iommu_domain_free(iommu); - ret = -ENOMEM; + ret = PTR_ERR(gpu->mmu); goto fail; } @@ -678,7 +661,9 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) dev_err(gpu->dev, "could not create command buffer\n"); goto destroy_iommu; } - if (gpu->buffer->paddr - gpu->memory_base > 0x80000000) { + + if (gpu->mmu->version == ETNAVIV_IOMMU_V1 && + gpu->buffer->paddr - gpu->memory_base > 0x80000000) { ret = -EINVAL; dev_err(gpu->dev, "command buffer outside valid memory window\n"); @@ -868,45 +853,6 @@ int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m) #endif /* - * Power Management: - */ -static int enable_clk(struct etnaviv_gpu *gpu) -{ - if (gpu->clk_core) - clk_prepare_enable(gpu->clk_core); - if (gpu->clk_shader) - clk_prepare_enable(gpu->clk_shader); - - return 0; -} - -static int disable_clk(struct etnaviv_gpu *gpu) -{ - if (gpu->clk_core) - clk_disable_unprepare(gpu->clk_core); - if (gpu->clk_shader) - clk_disable_unprepare(gpu->clk_shader); - - return 0; -} - -static int enable_axi(struct etnaviv_gpu *gpu) -{ - if (gpu->clk_bus) - clk_prepare_enable(gpu->clk_bus); - - return 0; -} - -static int disable_axi(struct etnaviv_gpu *gpu) -{ - if (gpu->clk_bus) - clk_disable_unprepare(gpu->clk_bus); - - return 0; -} - -/* * Hangcheck detection for locked gpu: */ static void recover_worker(struct work_struct *work) @@ -945,7 +891,7 @@ static void recover_worker(struct work_struct *work) gpu->completed_fence = gpu->active_fence; etnaviv_gpu_hw_init(gpu); - gpu->switch_context = true; + gpu->lastctx = NULL; gpu->exec_state = -1; mutex_unlock(&gpu->lock); @@ -1178,6 +1124,9 @@ struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu, u32 size, if (!cmdbuf) return NULL; + if (gpu->mmu->version == ETNAVIV_IOMMU_V2) + size = ALIGN(size, SZ_4K); + cmdbuf->vaddr = dma_alloc_wc(gpu->dev, size, &cmdbuf->paddr, GFP_KERNEL); if (!cmdbuf->vaddr) { @@ -1193,6 +1142,7 @@ struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu, u32 size, void etnaviv_gpu_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf) { + etnaviv_iommu_put_cmdbuf_va(cmdbuf->gpu, cmdbuf); dma_free_wc(cmdbuf->gpu->dev, cmdbuf->size, cmdbuf->vaddr, cmdbuf->paddr); kfree(cmdbuf); @@ -1333,8 +1283,6 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, if (ret < 0) return ret; - mutex_lock(&gpu->lock); - /* * TODO * @@ -1348,16 +1296,18 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, if (unlikely(event == ~0U)) { DRM_ERROR("no free event\n"); ret = -EBUSY; - goto out_unlock; + goto out_pm_put; } fence = etnaviv_gpu_fence_alloc(gpu); if (!fence) { event_free(gpu, event); ret = -ENOMEM; - goto out_unlock; + goto out_pm_put; } + mutex_lock(&gpu->lock); + gpu->event[event].fence = fence; submit->fence = fence->seqno; gpu->active_fence = submit->fence; @@ -1395,9 +1345,9 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, hangcheck_timer_reset(gpu); ret = 0; -out_unlock: mutex_unlock(&gpu->lock); +out_pm_put: etnaviv_gpu_pm_put(gpu); return ret; @@ -1425,6 +1375,21 @@ static irqreturn_t irq_handler(int irq, void *data) intr &= ~VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR; } + if (intr & VIVS_HI_INTR_ACKNOWLEDGE_MMU_EXCEPTION) { + int i; + + dev_err_ratelimited(gpu->dev, + "MMU fault status 0x%08x\n", + gpu_read(gpu, VIVS_MMUv2_STATUS)); + for (i = 0; i < 4; i++) { + dev_err_ratelimited(gpu->dev, + "MMU %d fault addr 0x%08x\n", + i, gpu_read(gpu, + VIVS_MMUv2_EXCEPTION_ADDR(i))); + } + intr &= ~VIVS_HI_INTR_ACKNOWLEDGE_MMU_EXCEPTION; + } + while ((event = ffs(intr)) != 0) { struct fence *fence; @@ -1466,39 +1431,72 @@ static int etnaviv_gpu_clk_enable(struct etnaviv_gpu *gpu) { int ret; - ret = enable_clk(gpu); - if (ret) - return ret; + if (gpu->clk_bus) { + ret = clk_prepare_enable(gpu->clk_bus); + if (ret) + return ret; + } - ret = enable_axi(gpu); - if (ret) { - disable_clk(gpu); - return ret; + if (gpu->clk_core) { + ret = clk_prepare_enable(gpu->clk_core); + if (ret) + goto disable_clk_bus; + } + + if (gpu->clk_shader) { + ret = clk_prepare_enable(gpu->clk_shader); + if (ret) + goto disable_clk_core; } return 0; + +disable_clk_core: + if (gpu->clk_core) + clk_disable_unprepare(gpu->clk_core); +disable_clk_bus: + if (gpu->clk_bus) + clk_disable_unprepare(gpu->clk_bus); + + return ret; } static int etnaviv_gpu_clk_disable(struct etnaviv_gpu *gpu) { - int ret; + if (gpu->clk_shader) + clk_disable_unprepare(gpu->clk_shader); + if (gpu->clk_core) + clk_disable_unprepare(gpu->clk_core); + if (gpu->clk_bus) + clk_disable_unprepare(gpu->clk_bus); - ret = disable_axi(gpu); - if (ret) - return ret; + return 0; +} - ret = disable_clk(gpu); - if (ret) - return ret; +int etnaviv_gpu_wait_idle(struct etnaviv_gpu *gpu, unsigned int timeout_ms) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); - return 0; + do { + u32 idle = gpu_read(gpu, VIVS_HI_IDLE_STATE); + + if ((idle & gpu->idle_mask) == gpu->idle_mask) + return 0; + + if (time_is_before_jiffies(timeout)) { + dev_warn(gpu->dev, + "timed out waiting for idle: idle=0x%x\n", + idle); + return -ETIMEDOUT; + } + + udelay(5); + } while (1); } static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu) { if (gpu->buffer) { - unsigned long timeout; - /* Replace the last WAIT with END */ etnaviv_buffer_end(gpu); @@ -1507,22 +1505,7 @@ static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu) * happen quickly (as the WAIT is only 200 cycles). If * we fail, just warn and continue. */ - timeout = jiffies + msecs_to_jiffies(100); - do { - u32 idle = gpu_read(gpu, VIVS_HI_IDLE_STATE); - - if ((idle & gpu->idle_mask) == gpu->idle_mask) - break; - - if (time_is_before_jiffies(timeout)) { - dev_warn(gpu->dev, - "timed out waiting for idle: idle=0x%x\n", - idle); - break; - } - - udelay(5); - } while (1); + etnaviv_gpu_wait_idle(gpu, 100); } return etnaviv_gpu_clk_disable(gpu); @@ -1634,7 +1617,7 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct etnaviv_gpu *gpu; - int err = 0; + int err; gpu = devm_kzalloc(dev, sizeof(*gpu), GFP_KERNEL); if (!gpu) @@ -1651,16 +1634,15 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) /* Get Interrupt: */ gpu->irq = platform_get_irq(pdev, 0); if (gpu->irq < 0) { - err = gpu->irq; - dev_err(dev, "failed to get irq: %d\n", err); - goto fail; + dev_err(dev, "failed to get irq: %d\n", gpu->irq); + return gpu->irq; } err = devm_request_irq(&pdev->dev, gpu->irq, irq_handler, 0, dev_name(gpu->dev), gpu); if (err) { dev_err(dev, "failed to request IRQ%u: %d\n", gpu->irq, err); - goto fail; + return err; } /* Get Clocks: */ @@ -1694,13 +1676,10 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) err = component_add(&pdev->dev, &gpu_ops); if (err < 0) { dev_err(&pdev->dev, "failed to register component: %d\n", err); - goto fail; + return err; } return 0; - -fail: - return err; } static int etnaviv_gpu_platform_remove(struct platform_device *pdev) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h index a69cdd5..73c278d 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h @@ -160,6 +160,8 @@ struct etnaviv_cmdbuf { dma_addr_t paddr; u32 size; u32 user_size; + /* vram node used if the cmdbuf is mapped through the MMUv2 */ + struct drm_mm_node vram_node; /* fence after which this buffer is to be disposed */ struct fence *fence; /* target exec state */ @@ -214,6 +216,8 @@ struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu, void etnaviv_gpu_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf); int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu); void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu); +int etnaviv_gpu_wait_idle(struct etnaviv_gpu *gpu, unsigned int timeout_ms); +void etnaviv_gpu_start_fe(struct etnaviv_gpu *gpu, u32 address, u16 prefetch); extern struct platform_driver etnaviv_gpu_driver; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu.c index 16353ee..81f1583 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_iommu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu.c @@ -196,12 +196,19 @@ static struct etnaviv_iommu_ops etnaviv_iommu_ops = { .dump = etnaviv_iommuv1_dump, }; -void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu, - struct iommu_domain *domain) +void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu) { - struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain); + struct etnaviv_iommu_domain *etnaviv_domain = + to_etnaviv_domain(gpu->mmu->domain); u32 pgtable; + /* set base addresses */ + gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, gpu->memory_base); + gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, gpu->memory_base); + gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_TX, gpu->memory_base); + gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, gpu->memory_base); + gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, gpu->memory_base); + /* set page table address in MC */ pgtable = (u32)etnaviv_domain->pgtable.paddr; @@ -212,7 +219,7 @@ void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu, gpu_write(gpu, VIVS_MC_MMU_RA_PAGE_TABLE, pgtable); } -struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu) +struct iommu_domain *etnaviv_iommuv1_domain_alloc(struct etnaviv_gpu *gpu) { struct etnaviv_iommu_domain *etnaviv_domain; int ret; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu.h b/drivers/gpu/drm/etnaviv/etnaviv_iommu.h index cf45503..8b51e7c 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_iommu.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu.h @@ -17,12 +17,12 @@ #ifndef __ETNAVIV_IOMMU_H__ #define __ETNAVIV_IOMMU_H__ -#include <linux/iommu.h> struct etnaviv_gpu; -struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu); -void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu, - struct iommu_domain *domain); -struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu); +struct iommu_domain *etnaviv_iommuv1_domain_alloc(struct etnaviv_gpu *gpu); +void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu); + +struct iommu_domain *etnaviv_iommuv2_domain_alloc(struct etnaviv_gpu *gpu); +void etnaviv_iommuv2_restore(struct etnaviv_gpu *gpu); #endif /* __ETNAVIV_IOMMU_H__ */ diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c index fbb4aed..7e9c4d2 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Christian Gmeiner <christian.gmeiner@gmail.com> + * Copyright (C) 2016 Etnaviv Project * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by @@ -22,12 +22,267 @@ #include <linux/bitops.h> #include "etnaviv_gpu.h" +#include "etnaviv_mmu.h" #include "etnaviv_iommu.h" +#include "state.xml.h" #include "state_hi.xml.h" +#define MMUv2_PTE_PRESENT BIT(0) +#define MMUv2_PTE_EXCEPTION BIT(1) +#define MMUv2_PTE_WRITEABLE BIT(2) -struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu) +#define MMUv2_MTLB_MASK 0xffc00000 +#define MMUv2_MTLB_SHIFT 22 +#define MMUv2_STLB_MASK 0x003ff000 +#define MMUv2_STLB_SHIFT 12 + +#define MMUv2_MAX_STLB_ENTRIES 1024 + +struct etnaviv_iommuv2_domain { + struct iommu_domain domain; + struct device *dev; + void *bad_page_cpu; + dma_addr_t bad_page_dma; + /* M(aster) TLB aka first level pagetable */ + u32 *mtlb_cpu; + dma_addr_t mtlb_dma; + /* S(lave) TLB aka second level pagetable */ + u32 *stlb_cpu[1024]; + dma_addr_t stlb_dma[1024]; +}; + +static struct etnaviv_iommuv2_domain *to_etnaviv_domain(struct iommu_domain *domain) +{ + return container_of(domain, struct etnaviv_iommuv2_domain, domain); +} + +static int etnaviv_iommuv2_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, size_t size, int prot) +{ + struct etnaviv_iommuv2_domain *etnaviv_domain = + to_etnaviv_domain(domain); + int mtlb_entry, stlb_entry; + u32 entry = (u32)paddr | MMUv2_PTE_PRESENT; + + if (size != SZ_4K) + return -EINVAL; + + if (prot & IOMMU_WRITE) + entry |= MMUv2_PTE_WRITEABLE; + + mtlb_entry = (iova & MMUv2_MTLB_MASK) >> MMUv2_MTLB_SHIFT; + stlb_entry = (iova & MMUv2_STLB_MASK) >> MMUv2_STLB_SHIFT; + + etnaviv_domain->stlb_cpu[mtlb_entry][stlb_entry] = entry; + + return 0; +} + +static size_t etnaviv_iommuv2_unmap(struct iommu_domain *domain, + unsigned long iova, size_t size) +{ + struct etnaviv_iommuv2_domain *etnaviv_domain = + to_etnaviv_domain(domain); + int mtlb_entry, stlb_entry; + + if (size != SZ_4K) + return -EINVAL; + + mtlb_entry = (iova & MMUv2_MTLB_MASK) >> MMUv2_MTLB_SHIFT; + stlb_entry = (iova & MMUv2_STLB_MASK) >> MMUv2_STLB_SHIFT; + + etnaviv_domain->stlb_cpu[mtlb_entry][stlb_entry] = MMUv2_PTE_EXCEPTION; + + return SZ_4K; +} + +static phys_addr_t etnaviv_iommuv2_iova_to_phys(struct iommu_domain *domain, + dma_addr_t iova) +{ + struct etnaviv_iommuv2_domain *etnaviv_domain = + to_etnaviv_domain(domain); + int mtlb_entry, stlb_entry; + + mtlb_entry = (iova & MMUv2_MTLB_MASK) >> MMUv2_MTLB_SHIFT; + stlb_entry = (iova & MMUv2_STLB_MASK) >> MMUv2_STLB_SHIFT; + + return etnaviv_domain->stlb_cpu[mtlb_entry][stlb_entry] & ~(SZ_4K - 1); +} + +static int etnaviv_iommuv2_init(struct etnaviv_iommuv2_domain *etnaviv_domain) +{ + u32 *p; + int ret, i, j; + + /* allocate scratch page */ + etnaviv_domain->bad_page_cpu = dma_alloc_coherent(etnaviv_domain->dev, + SZ_4K, + &etnaviv_domain->bad_page_dma, + GFP_KERNEL); + if (!etnaviv_domain->bad_page_cpu) { + ret = -ENOMEM; + goto fail_mem; + } + p = etnaviv_domain->bad_page_cpu; + for (i = 0; i < SZ_4K / 4; i++) + *p++ = 0xdead55aa; + + etnaviv_domain->mtlb_cpu = dma_alloc_coherent(etnaviv_domain->dev, + SZ_4K, + &etnaviv_domain->mtlb_dma, + GFP_KERNEL); + if (!etnaviv_domain->mtlb_cpu) { + ret = -ENOMEM; + goto fail_mem; + } + + /* pre-populate STLB pages (may want to switch to on-demand later) */ + for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) { + etnaviv_domain->stlb_cpu[i] = + dma_alloc_coherent(etnaviv_domain->dev, + SZ_4K, + &etnaviv_domain->stlb_dma[i], + GFP_KERNEL); + if (!etnaviv_domain->stlb_cpu[i]) { + ret = -ENOMEM; + goto fail_mem; + } + p = etnaviv_domain->stlb_cpu[i]; + for (j = 0; j < SZ_4K / 4; j++) + *p++ = MMUv2_PTE_EXCEPTION; + + etnaviv_domain->mtlb_cpu[i] = etnaviv_domain->stlb_dma[i] | + MMUv2_PTE_PRESENT; + } + + return 0; + +fail_mem: + if (etnaviv_domain->bad_page_cpu) + dma_free_coherent(etnaviv_domain->dev, SZ_4K, + etnaviv_domain->bad_page_cpu, + etnaviv_domain->bad_page_dma); + + if (etnaviv_domain->mtlb_cpu) + dma_free_coherent(etnaviv_domain->dev, SZ_4K, + etnaviv_domain->mtlb_cpu, + etnaviv_domain->mtlb_dma); + + for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) { + if (etnaviv_domain->stlb_cpu[i]) + dma_free_coherent(etnaviv_domain->dev, SZ_4K, + etnaviv_domain->stlb_cpu[i], + etnaviv_domain->stlb_dma[i]); + } + + return ret; +} + +static void etnaviv_iommuv2_domain_free(struct iommu_domain *domain) +{ + struct etnaviv_iommuv2_domain *etnaviv_domain = + to_etnaviv_domain(domain); + int i; + + dma_free_coherent(etnaviv_domain->dev, SZ_4K, + etnaviv_domain->bad_page_cpu, + etnaviv_domain->bad_page_dma); + + dma_free_coherent(etnaviv_domain->dev, SZ_4K, + etnaviv_domain->mtlb_cpu, + etnaviv_domain->mtlb_dma); + + for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) { + if (etnaviv_domain->stlb_cpu[i]) + dma_free_coherent(etnaviv_domain->dev, SZ_4K, + etnaviv_domain->stlb_cpu[i], + etnaviv_domain->stlb_dma[i]); + } + + vfree(etnaviv_domain); +} + +static size_t etnaviv_iommuv2_dump_size(struct iommu_domain *domain) +{ + struct etnaviv_iommuv2_domain *etnaviv_domain = + to_etnaviv_domain(domain); + size_t dump_size = SZ_4K; + int i; + + for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) + if (etnaviv_domain->mtlb_cpu[i] & MMUv2_PTE_PRESENT) + dump_size += SZ_4K; + + return dump_size; +} + +static void etnaviv_iommuv2_dump(struct iommu_domain *domain, void *buf) { - /* TODO */ + struct etnaviv_iommuv2_domain *etnaviv_domain = + to_etnaviv_domain(domain); + int i; + + memcpy(buf, etnaviv_domain->mtlb_cpu, SZ_4K); + buf += SZ_4K; + for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++, buf += SZ_4K) + if (etnaviv_domain->mtlb_cpu[i] & MMUv2_PTE_PRESENT) + memcpy(buf, etnaviv_domain->stlb_cpu[i], SZ_4K); +} + +static struct etnaviv_iommu_ops etnaviv_iommu_ops = { + .ops = { + .domain_free = etnaviv_iommuv2_domain_free, + .map = etnaviv_iommuv2_map, + .unmap = etnaviv_iommuv2_unmap, + .iova_to_phys = etnaviv_iommuv2_iova_to_phys, + .pgsize_bitmap = SZ_4K, + }, + .dump_size = etnaviv_iommuv2_dump_size, + .dump = etnaviv_iommuv2_dump, +}; + +void etnaviv_iommuv2_restore(struct etnaviv_gpu *gpu) +{ + struct etnaviv_iommuv2_domain *etnaviv_domain = + to_etnaviv_domain(gpu->mmu->domain); + u16 prefetch; + + /* If the MMU is already enabled the state is still there. */ + if (gpu_read(gpu, VIVS_MMUv2_CONTROL) & VIVS_MMUv2_CONTROL_ENABLE) + return; + + prefetch = etnaviv_buffer_config_mmuv2(gpu, + (u32)etnaviv_domain->mtlb_dma, + (u32)etnaviv_domain->bad_page_dma); + etnaviv_gpu_start_fe(gpu, gpu->buffer->paddr, prefetch); + etnaviv_gpu_wait_idle(gpu, 100); + + gpu_write(gpu, VIVS_MMUv2_CONTROL, VIVS_MMUv2_CONTROL_ENABLE); +} +struct iommu_domain *etnaviv_iommuv2_domain_alloc(struct etnaviv_gpu *gpu) +{ + struct etnaviv_iommuv2_domain *etnaviv_domain; + int ret; + + etnaviv_domain = vzalloc(sizeof(*etnaviv_domain)); + if (!etnaviv_domain) + return NULL; + + etnaviv_domain->dev = gpu->dev; + + etnaviv_domain->domain.type = __IOMMU_DOMAIN_PAGING; + etnaviv_domain->domain.ops = &etnaviv_iommu_ops.ops; + etnaviv_domain->domain.pgsize_bitmap = SZ_4K; + etnaviv_domain->domain.geometry.aperture_start = 0; + etnaviv_domain->domain.geometry.aperture_end = ~0UL & ~(SZ_4K - 1); + + ret = etnaviv_iommuv2_init(etnaviv_domain); + if (ret) + goto out_free; + + return &etnaviv_domain->domain; + +out_free: + vfree(etnaviv_domain); return NULL; } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h deleted file mode 100644 index 603ea41..0000000 --- a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2014 Christian Gmeiner <christian.gmeiner@gmail.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef __ETNAVIV_IOMMU_V2_H__ -#define __ETNAVIV_IOMMU_V2_H__ - -#include <linux/iommu.h> -struct etnaviv_gpu; - -struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu); - -#endif /* __ETNAVIV_IOMMU_V2_H__ */ diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c index 29a723f..d3796ed 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c @@ -14,9 +14,11 @@ * this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "common.xml.h" #include "etnaviv_drv.h" #include "etnaviv_gem.h" #include "etnaviv_gpu.h" +#include "etnaviv_iommu.h" #include "etnaviv_mmu.h" static int etnaviv_fault_handler(struct iommu_domain *iommu, struct device *dev, @@ -101,40 +103,21 @@ static void etnaviv_iommu_remove_mapping(struct etnaviv_iommu *mmu, drm_mm_remove_node(&mapping->vram_node); } -int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu, - struct etnaviv_gem_object *etnaviv_obj, u32 memory_base, - struct etnaviv_vram_mapping *mapping) +static int etnaviv_iommu_find_iova(struct etnaviv_iommu *mmu, + struct drm_mm_node *node, size_t size) { struct etnaviv_vram_mapping *free = NULL; - struct sg_table *sgt = etnaviv_obj->sgt; - struct drm_mm_node *node; int ret; - lockdep_assert_held(&etnaviv_obj->lock); - - mutex_lock(&mmu->lock); - - /* v1 MMU can optimize single entry (contiguous) scatterlists */ - if (sgt->nents == 1 && !(etnaviv_obj->flags & ETNA_BO_FORCE_MMU)) { - u32 iova; - - iova = sg_dma_address(sgt->sgl) - memory_base; - if (iova < 0x80000000 - sg_dma_len(sgt->sgl)) { - mapping->iova = iova; - list_add_tail(&mapping->mmu_node, &mmu->mappings); - mutex_unlock(&mmu->lock); - return 0; - } - } + lockdep_assert_held(&mmu->lock); - node = &mapping->vram_node; while (1) { struct etnaviv_vram_mapping *m, *n; struct list_head list; bool found; ret = drm_mm_insert_node_in_range(&mmu->mm, node, - etnaviv_obj->base.size, 0, mmu->last_iova, ~0UL, + size, 0, mmu->last_iova, ~0UL, DRM_MM_SEARCH_DEFAULT); if (ret != -ENOSPC) @@ -151,7 +134,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu, } /* Try to retire some entries */ - drm_mm_init_scan(&mmu->mm, etnaviv_obj->base.size, 0, 0); + drm_mm_init_scan(&mmu->mm, size, 0, 0); found = 0; INIT_LIST_HEAD(&list); @@ -212,6 +195,38 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu, mmu->need_flush = true; } + return ret; +} + +int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu, + struct etnaviv_gem_object *etnaviv_obj, u32 memory_base, + struct etnaviv_vram_mapping *mapping) +{ + struct sg_table *sgt = etnaviv_obj->sgt; + struct drm_mm_node *node; + int ret; + + lockdep_assert_held(&etnaviv_obj->lock); + + mutex_lock(&mmu->lock); + + /* v1 MMU can optimize single entry (contiguous) scatterlists */ + if (mmu->version == ETNAVIV_IOMMU_V1 && + sgt->nents == 1 && !(etnaviv_obj->flags & ETNA_BO_FORCE_MMU)) { + u32 iova; + + iova = sg_dma_address(sgt->sgl) - memory_base; + if (iova < 0x80000000 - sg_dma_len(sgt->sgl)) { + mapping->iova = iova; + list_add_tail(&mapping->mmu_node, &mmu->mappings); + mutex_unlock(&mmu->lock); + return 0; + } + } + + node = &mapping->vram_node; + + ret = etnaviv_iommu_find_iova(mmu, node, etnaviv_obj->base.size); if (ret < 0) { mutex_unlock(&mmu->lock); return ret; @@ -256,30 +271,102 @@ void etnaviv_iommu_destroy(struct etnaviv_iommu *mmu) kfree(mmu); } -struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu, - struct iommu_domain *domain, enum etnaviv_iommu_version version) +struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu) { + enum etnaviv_iommu_version version; struct etnaviv_iommu *mmu; mmu = kzalloc(sizeof(*mmu), GFP_KERNEL); if (!mmu) return ERR_PTR(-ENOMEM); - mmu->domain = domain; + if (!(gpu->identity.minor_features1 & chipMinorFeatures1_MMU_VERSION)) { + mmu->domain = etnaviv_iommuv1_domain_alloc(gpu); + version = ETNAVIV_IOMMU_V1; + } else { + mmu->domain = etnaviv_iommuv2_domain_alloc(gpu); + version = ETNAVIV_IOMMU_V2; + } + + if (!mmu->domain) { + dev_err(gpu->dev, "Failed to allocate GPU IOMMU domain\n"); + kfree(mmu); + return ERR_PTR(-ENOMEM); + } + mmu->gpu = gpu; mmu->version = version; mutex_init(&mmu->lock); INIT_LIST_HEAD(&mmu->mappings); - drm_mm_init(&mmu->mm, domain->geometry.aperture_start, - domain->geometry.aperture_end - - domain->geometry.aperture_start + 1); + drm_mm_init(&mmu->mm, mmu->domain->geometry.aperture_start, + mmu->domain->geometry.aperture_end - + mmu->domain->geometry.aperture_start + 1); - iommu_set_fault_handler(domain, etnaviv_fault_handler, gpu->dev); + iommu_set_fault_handler(mmu->domain, etnaviv_fault_handler, gpu->dev); return mmu; } +void etnaviv_iommu_restore(struct etnaviv_gpu *gpu) +{ + if (gpu->mmu->version == ETNAVIV_IOMMU_V1) + etnaviv_iommuv1_restore(gpu); + else + etnaviv_iommuv2_restore(gpu); +} + +u32 etnaviv_iommu_get_cmdbuf_va(struct etnaviv_gpu *gpu, + struct etnaviv_cmdbuf *buf) +{ + struct etnaviv_iommu *mmu = gpu->mmu; + + if (mmu->version == ETNAVIV_IOMMU_V1) { + return buf->paddr - gpu->memory_base; + } else { + int ret; + + if (buf->vram_node.allocated) + return (u32)buf->vram_node.start; + + mutex_lock(&mmu->lock); + ret = etnaviv_iommu_find_iova(mmu, &buf->vram_node, buf->size); + if (ret < 0) { + mutex_unlock(&mmu->lock); + return 0; + } + ret = iommu_map(mmu->domain, buf->vram_node.start, buf->paddr, + buf->size, IOMMU_READ); + if (ret < 0) { + drm_mm_remove_node(&buf->vram_node); + mutex_unlock(&mmu->lock); + return 0; + } + /* + * At least on GC3000 the FE MMU doesn't properly flush old TLB + * entries. Make sure to space the command buffers out in a way + * that the FE MMU prefetch won't load invalid entries. + */ + mmu->last_iova = buf->vram_node.start + buf->size + SZ_64K; + gpu->mmu->need_flush = true; + mutex_unlock(&mmu->lock); + + return (u32)buf->vram_node.start; + } +} + +void etnaviv_iommu_put_cmdbuf_va(struct etnaviv_gpu *gpu, + struct etnaviv_cmdbuf *buf) +{ + struct etnaviv_iommu *mmu = gpu->mmu; + + if (mmu->version == ETNAVIV_IOMMU_V2 && buf->vram_node.allocated) { + mutex_lock(&mmu->lock); + iommu_unmap(mmu->domain, buf->vram_node.start, buf->size); + drm_mm_remove_node(&buf->vram_node); + mutex_unlock(&mmu->lock); + } +} size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu) { struct etnaviv_iommu_ops *ops; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.h b/drivers/gpu/drm/etnaviv/etnaviv_mmu.h index fff215a..e787e49 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.h @@ -62,10 +62,15 @@ void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu, struct etnaviv_vram_mapping *mapping); void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu); +u32 etnaviv_iommu_get_cmdbuf_va(struct etnaviv_gpu *gpu, + struct etnaviv_cmdbuf *buf); +void etnaviv_iommu_put_cmdbuf_va(struct etnaviv_gpu *gpu, + struct etnaviv_cmdbuf *buf); + size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu); void etnaviv_iommu_dump(struct etnaviv_iommu *iommu, void *buf); -struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu, - struct iommu_domain *domain, enum etnaviv_iommu_version version); +struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu); +void etnaviv_iommu_restore(struct etnaviv_gpu *gpu); #endif /* __ETNAVIV_MMU_H__ */ diff --git a/drivers/gpu/drm/etnaviv/state_hi.xml.h b/drivers/gpu/drm/etnaviv/state_hi.xml.h index 807a3d9..43c73e2 100644 --- a/drivers/gpu/drm/etnaviv/state_hi.xml.h +++ b/drivers/gpu/drm/etnaviv/state_hi.xml.h @@ -8,10 +8,10 @@ http://0x04.net/cgit/index.cgi/rules-ng-ng git clone git://0x04.net/rules-ng-ng The rules-ng-ng source files this header was generated from are: -- state_hi.xml ( 24309 bytes, from 2015-12-12 09:02:53) -- common.xml ( 18437 bytes, from 2015-12-12 09:02:53) +- state_hi.xml ( 25620 bytes, from 2016-08-19 22:07:37) +- common.xml ( 20583 bytes, from 2016-06-07 05:22:38) -Copyright (C) 2015 +Copyright (C) 2016 */ @@ -78,9 +78,10 @@ Copyright (C) 2015 #define VIVS_HI_AXI_STATUS_DET_RD_ERR 0x00000200 #define VIVS_HI_INTR_ACKNOWLEDGE 0x00000010 -#define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__MASK 0x7fffffff +#define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__MASK 0x3fffffff #define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__SHIFT 0 #define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC(x) (((x) << VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__SHIFT) & VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__MASK) +#define VIVS_HI_INTR_ACKNOWLEDGE_MMU_EXCEPTION 0x40000000 #define VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR 0x80000000 #define VIVS_HI_INTR_ENBL 0x00000014 diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 877d2ef..486943e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -105,7 +105,7 @@ static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit) atomic_inc(&exynos_crtc->pending_update); } - drm_atomic_helper_commit_planes(dev, state, false); + drm_atomic_helper_commit_planes(dev, state, 0); exynos_atomic_wait_for_commit(state); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index e016640..40ce841 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -55,11 +55,11 @@ static int check_fb_gem_memory_type(struct drm_device *drm_dev, flags = exynos_gem->flags; /* - * without iommu support, not support physically non-continuous memory - * for framebuffer. + * Physically non-contiguous memory type for framebuffer is not + * supported without IOMMU. */ if (IS_NONCONTIG_BUFFER(flags)) { - DRM_ERROR("cannot use this gem memory type for fb.\n"); + DRM_ERROR("Non-contiguous GEM memory is not supported.\n"); return -EINVAL; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c index 0525c56..147ef0d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c @@ -1753,32 +1753,6 @@ static int fimc_clk_ctrl(struct fimc_context *ctx, bool enable) return 0; } -#ifdef CONFIG_PM_SLEEP -static int fimc_suspend(struct device *dev) -{ - struct fimc_context *ctx = get_fimc_context(dev); - - DRM_DEBUG_KMS("id[%d]\n", ctx->id); - - if (pm_runtime_suspended(dev)) - return 0; - - return fimc_clk_ctrl(ctx, false); -} - -static int fimc_resume(struct device *dev) -{ - struct fimc_context *ctx = get_fimc_context(dev); - - DRM_DEBUG_KMS("id[%d]\n", ctx->id); - - if (!pm_runtime_suspended(dev)) - return fimc_clk_ctrl(ctx, true); - - return 0; -} -#endif - static int fimc_runtime_suspend(struct device *dev) { struct fimc_context *ctx = get_fimc_context(dev); @@ -1799,7 +1773,8 @@ static int fimc_runtime_resume(struct device *dev) #endif static const struct dev_pm_ops fimc_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL) }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 4bf00f5..6eca8bb 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -1475,8 +1475,8 @@ static int g2d_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int g2d_suspend(struct device *dev) +#ifdef CONFIG_PM +static int g2d_runtime_suspend(struct device *dev) { struct g2d_data *g2d = dev_get_drvdata(dev); @@ -1490,25 +1490,6 @@ static int g2d_suspend(struct device *dev) flush_work(&g2d->runqueue_work); - return 0; -} - -static int g2d_resume(struct device *dev) -{ - struct g2d_data *g2d = dev_get_drvdata(dev); - - g2d->suspended = false; - g2d_exec_runqueue(g2d); - - return 0; -} -#endif - -#ifdef CONFIG_PM -static int g2d_runtime_suspend(struct device *dev) -{ - struct g2d_data *g2d = dev_get_drvdata(dev); - clk_disable_unprepare(g2d->gate_clk); return 0; @@ -1523,12 +1504,16 @@ static int g2d_runtime_resume(struct device *dev) if (ret < 0) dev_warn(dev, "failed to enable clock.\n"); + g2d->suspended = false; + g2d_exec_runqueue(g2d); + return ret; } #endif static const struct dev_pm_ops g2d_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(g2d_suspend, g2d_resume) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) SET_RUNTIME_PM_OPS(g2d_runtime_suspend, g2d_runtime_resume, NULL) }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c index 5d20da8..52a9d26 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c @@ -1760,34 +1760,7 @@ static int gsc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int gsc_suspend(struct device *dev) -{ - struct gsc_context *ctx = get_gsc_context(dev); - - DRM_DEBUG_KMS("id[%d]\n", ctx->id); - - if (pm_runtime_suspended(dev)) - return 0; - - return gsc_clk_ctrl(ctx, false); -} - -static int gsc_resume(struct device *dev) -{ - struct gsc_context *ctx = get_gsc_context(dev); - - DRM_DEBUG_KMS("id[%d]\n", ctx->id); - - if (!pm_runtime_suspended(dev)) - return gsc_clk_ctrl(ctx, true); - - return 0; -} -#endif - -#ifdef CONFIG_PM -static int gsc_runtime_suspend(struct device *dev) +static int __maybe_unused gsc_runtime_suspend(struct device *dev) { struct gsc_context *ctx = get_gsc_context(dev); @@ -1796,7 +1769,7 @@ static int gsc_runtime_suspend(struct device *dev) return gsc_clk_ctrl(ctx, false); } -static int gsc_runtime_resume(struct device *dev) +static int __maybe_unused gsc_runtime_resume(struct device *dev) { struct gsc_context *ctx = get_gsc_context(dev); @@ -1804,10 +1777,10 @@ static int gsc_runtime_resume(struct device *dev) return gsc_clk_ctrl(ctx, true); } -#endif static const struct dev_pm_ops gsc_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(gsc_suspend, gsc_resume) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) SET_RUNTIME_PM_OPS(gsc_runtime_suspend, gsc_runtime_resume, NULL) }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c index 404367a..6591e40 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c @@ -794,29 +794,6 @@ static int rotator_clk_crtl(struct rot_context *rot, bool enable) return 0; } - -#ifdef CONFIG_PM_SLEEP -static int rotator_suspend(struct device *dev) -{ - struct rot_context *rot = dev_get_drvdata(dev); - - if (pm_runtime_suspended(dev)) - return 0; - - return rotator_clk_crtl(rot, false); -} - -static int rotator_resume(struct device *dev) -{ - struct rot_context *rot = dev_get_drvdata(dev); - - if (!pm_runtime_suspended(dev)) - return rotator_clk_crtl(rot, true); - - return 0; -} -#endif - static int rotator_runtime_suspend(struct device *dev) { struct rot_context *rot = dev_get_drvdata(dev); @@ -833,7 +810,8 @@ static int rotator_runtime_resume(struct device *dev) #endif static const struct dev_pm_ops rotator_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(rotator_suspend, rotator_resume) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume, NULL) }; diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index 7882387..0884c45 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -270,7 +270,7 @@ static int fsl_dcu_drm_pm_resume(struct device *dev) ret = clk_prepare_enable(fsl_dev->pix_clk); if (ret < 0) { dev_err(dev, "failed to enable pix clk\n"); - return ret; + goto disable_dcu_clk; } fsl_dcu_drm_init_planes(fsl_dev->drm); @@ -284,6 +284,10 @@ static int fsl_dcu_drm_pm_resume(struct device *dev) enable_irq(fsl_dev->irq); return 0; + +disable_dcu_clk: + clk_disable_unprepare(fsl_dev->clk); + return ret; } #endif @@ -330,6 +334,7 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev) const char *pix_clk_in_name; const struct of_device_id *id; int ret; + u8 div_ratio_shift = 0; fsl_dev = devm_kzalloc(dev, sizeof(*fsl_dev), GFP_KERNEL); if (!fsl_dev) @@ -382,11 +387,14 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev) pix_clk_in = fsl_dev->clk; } + if (of_property_read_bool(dev->of_node, "big-endian")) + div_ratio_shift = 24; + pix_clk_in_name = __clk_get_name(pix_clk_in); snprintf(pix_clk_name, sizeof(pix_clk_name), "%s_pix", pix_clk_in_name); fsl_dev->pix_clk = clk_register_divider(dev, pix_clk_name, pix_clk_in_name, 0, base + DCU_DIV_RATIO, - 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL); + div_ratio_shift, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL); if (IS_ERR(fsl_dev->pix_clk)) { dev_err(dev, "failed to register pix clk\n"); ret = PTR_ERR(fsl_dev->pix_clk); @@ -402,8 +410,8 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev) fsl_dev->tcon = fsl_tcon_init(dev); drm = drm_dev_alloc(driver, dev); - if (!drm) { - ret = -ENOMEM; + if (IS_ERR(drm)) { + ret = PTR_ERR(drm); goto disable_pix_clk; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c index e50467a..a7e5486 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c @@ -169,25 +169,10 @@ static void fsl_dcu_drm_plane_atomic_update(struct drm_plane *plane, return; } -static void -fsl_dcu_drm_plane_cleanup_fb(struct drm_plane *plane, - const struct drm_plane_state *new_state) -{ -} - -static int -fsl_dcu_drm_plane_prepare_fb(struct drm_plane *plane, - const struct drm_plane_state *new_state) -{ - return 0; -} - static const struct drm_plane_helper_funcs fsl_dcu_drm_plane_helper_funcs = { .atomic_check = fsl_dcu_drm_plane_atomic_check, .atomic_disable = fsl_dcu_drm_plane_atomic_disable, .atomic_update = fsl_dcu_drm_plane_atomic_update, - .cleanup_fb = fsl_dcu_drm_plane_cleanup_fb, - .prepare_fb = fsl_dcu_drm_plane_prepare_fb, }; static void fsl_dcu_drm_plane_destroy(struct drm_plane *plane) diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c index bca09ea..3194e54 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c @@ -57,10 +57,7 @@ static int fsl_tcon_init_regmap(struct device *dev, tcon->regs = devm_regmap_init_mmio(dev, regs, &fsl_tcon_regmap_config); - if (IS_ERR(tcon->regs)) - return PTR_ERR(tcon->regs); - - return 0; + return PTR_ERR_OR_ZERO(tcon->regs); } struct fsl_tcon *fsl_tcon_init(struct device *dev) diff --git a/drivers/gpu/drm/gma500/accel_2d.c b/drivers/gpu/drm/gma500/accel_2d.c index db9f7d0..0d2bb16 100644 --- a/drivers/gpu/drm/gma500/accel_2d.c +++ b/drivers/gpu/drm/gma500/accel_2d.c @@ -28,7 +28,6 @@ #include <linux/tty.h> #include <linux/slab.h> #include <linux/delay.h> -#include <linux/fb.h> #include <linux/init.h> #include <linux/console.h> diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c index 38dc890..ea733ab 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c @@ -415,14 +415,6 @@ static int cdv_intel_lvds_get_modes(struct drm_connector *connector) if (ret) return ret; - /* Didn't get an EDID, so - * Set wide sync ranges so we get all modes - * handed to valid_mode for checking - */ - connector->display_info.min_vfreq = 0; - connector->display_info.max_vfreq = 200; - connector->display_info.min_hfreq = 0; - connector->display_info.max_hfreq = 200; if (mode_dev->panel_fixed_mode != NULL) { struct drm_display_mode *mode = drm_mode_duplicate(dev, mode_dev->panel_fixed_mode); diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 0fcdce0..3a44e70 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -26,7 +26,6 @@ #include <linux/tty.h> #include <linux/slab.h> #include <linux/delay.h> -#include <linux/fb.h> #include <linux/init.h> #include <linux/console.h> diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c index 907cb51..acb3848 100644 --- a/drivers/gpu/drm/gma500/mdfld_dsi_output.c +++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c @@ -335,11 +335,6 @@ static int mdfld_dsi_connector_get_modes(struct drm_connector *connector) struct drm_display_mode *dup_mode = NULL; struct drm_device *dev = connector->dev; - connector->display_info.min_vfreq = 0; - connector->display_info.max_vfreq = 200; - connector->display_info.min_hfreq = 0; - connector->display_info.max_hfreq = 200; - if (fixed_mode) { dev_dbg(dev->dev, "fixed_mode %dx%d\n", fixed_mode->hdisplay, fixed_mode->vdisplay); diff --git a/drivers/gpu/drm/gma500/opregion.c b/drivers/gpu/drm/gma500/opregion.c index ab696ca..eab6d88 100644 --- a/drivers/gpu/drm/gma500/opregion.c +++ b/drivers/gpu/drm/gma500/opregion.c @@ -163,10 +163,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) if (bclp > 255) return ASLE_BACKLIGHT_FAILED; - if (config_enabled(CONFIG_BACKLIGHT_CLASS_DEVICE)) { - int max = bd->props.max_brightness; - gma_backlight_set(dev, bclp * max / 255); - } + gma_backlight_set(dev, bclp * bd->props.max_brightness / 255); asle->cblv = (bclp * 0x64) / 0xff | ASLE_CBLV_VALID; diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c index e55733c..fd7c912 100644 --- a/drivers/gpu/drm/gma500/psb_intel_lvds.c +++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c @@ -530,15 +530,6 @@ static int psb_intel_lvds_get_modes(struct drm_connector *connector) if (ret) return ret; - /* Didn't get an EDID, so - * Set wide sync ranges so we get all modes - * handed to valid_mode for checking - */ - connector->display_info.min_vfreq = 0; - connector->display_info.max_vfreq = 200; - connector->display_info.min_hfreq = 0; - connector->display_info.max_hfreq = 200; - if (mode_dev->panel_fixed_mode != NULL) { struct drm_display_mode *mode = drm_mode_duplicate(dev, mode_dev->panel_fixed_mode); diff --git a/drivers/gpu/drm/gma500/psb_intel_modes.c b/drivers/gpu/drm/gma500/psb_intel_modes.c index 4fca0d6..e536072 100644 --- a/drivers/gpu/drm/gma500/psb_intel_modes.c +++ b/drivers/gpu/drm/gma500/psb_intel_modes.c @@ -18,7 +18,6 @@ */ #include <linux/i2c.h> -#include <linux/fb.h> #include <drm/drmP.h> #include "psb_intel_drv.h" diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c index c3707d4..7e7a4d4 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -608,15 +608,17 @@ static void ade_rdma_set(void __iomem *base, struct drm_framebuffer *fb, u32 ch, u32 y, u32 in_h, u32 fmt) { struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0); + char *format_name; u32 reg_ctrl, reg_addr, reg_size, reg_stride, reg_space, reg_en; u32 stride = fb->pitches[0]; u32 addr = (u32)obj->paddr + y * stride; DRM_DEBUG_DRIVER("rdma%d: (y=%d, height=%d), stride=%d, paddr=0x%x\n", ch + 1, y, in_h, stride, (u32)obj->paddr); + format_name = drm_get_format_name(fb->pixel_format); DRM_DEBUG_DRIVER("addr=0x%x, fb:%dx%d, pixel_format=%d(%s)\n", - addr, fb->width, fb->height, fmt, - drm_get_format_name(fb->pixel_format)); + addr, fb->width, fb->height, fmt, format_name); + kfree(format_name); /* get reg offset */ reg_ctrl = RD_CH_CTRL(ch); @@ -815,19 +817,6 @@ static void ade_disable_channel(struct ade_plane *aplane) ade_compositor_routing_disable(base, ch); } -static int ade_plane_prepare_fb(struct drm_plane *plane, - const struct drm_plane_state *new_state) -{ - /* do nothing */ - return 0; -} - -static void ade_plane_cleanup_fb(struct drm_plane *plane, - const struct drm_plane_state *old_state) -{ - /* do nothing */ -} - static int ade_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) { @@ -895,8 +884,6 @@ static void ade_plane_atomic_disable(struct drm_plane *plane, } static const struct drm_plane_helper_funcs ade_plane_helper_funcs = { - .prepare_fb = ade_plane_prepare_fb, - .cleanup_fb = ade_plane_cleanup_fb, .atomic_check = ade_plane_atomic_check, .atomic_update = ade_plane_atomic_update, .atomic_disable = ade_plane_atomic_disable, diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 1edd9bc..90377a6 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -169,7 +169,7 @@ static int kirin_gem_cma_dumb_create(struct drm_file *file, static struct drm_driver kirin_drm_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | - DRIVER_ATOMIC | DRIVER_HAVE_IRQ, + DRIVER_ATOMIC, .fops = &kirin_drm_fops, .gem_free_object_unlocked = drm_gem_cma_free_object, @@ -207,8 +207,8 @@ static int kirin_drm_bind(struct device *dev) int ret; drm_dev = drm_dev_alloc(driver, dev); - if (!drm_dev) - return -ENOMEM; + if (IS_ERR(drm_dev)) + return PTR_ERR(drm_dev); drm_dev->platformdev = to_platform_device(dev); diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig index 4d341db..a6c92be 100644 --- a/drivers/gpu/drm/i2c/Kconfig +++ b/drivers/gpu/drm/i2c/Kconfig @@ -22,6 +22,7 @@ config DRM_I2C_SIL164 config DRM_I2C_NXP_TDA998X tristate "NXP Semiconductors TDA998X HDMI encoder" default m if DRM_TILCDC + select SND_SOC_HDMI_CODEC if SND_SOC help Support for NXP Semiconductors TDA998X HDMI encoders. diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index f4315bc..9798d40 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -20,6 +20,7 @@ #include <linux/module.h> #include <linux/irq.h> #include <sound/asoundef.h> +#include <sound/hdmi-codec.h> #include <drm/drmP.h> #include <drm/drm_atomic_helper.h> @@ -30,6 +31,11 @@ #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) +struct tda998x_audio_port { + u8 format; /* AFMT_xxx */ + u8 config; /* AP value */ +}; + struct tda998x_priv { struct i2c_client *cec; struct i2c_client *hdmi; @@ -41,7 +47,10 @@ struct tda998x_priv { u8 vip_cntrl_0; u8 vip_cntrl_1; u8 vip_cntrl_2; - struct tda998x_encoder_params params; + struct tda998x_audio_params audio_params; + + struct platform_device *audio_pdev; + struct mutex audio_mutex; wait_queue_head_t wq_edid; volatile int wq_edid_wait; @@ -53,6 +62,8 @@ struct tda998x_priv { struct drm_encoder encoder; struct drm_connector connector; + + struct tda998x_audio_port audio_port[2]; }; #define conn_to_tda998x_priv(x) \ @@ -666,26 +677,16 @@ tda998x_write_if(struct tda998x_priv *priv, u8 bit, u16 addr, reg_set(priv, REG_DIP_IF_FLAGS, bit); } -static void -tda998x_write_aif(struct tda998x_priv *priv, struct tda998x_encoder_params *p) +static int tda998x_write_aif(struct tda998x_priv *priv, + struct hdmi_audio_infoframe *cea) { union hdmi_infoframe frame; - hdmi_audio_infoframe_init(&frame.audio); - - frame.audio.channels = p->audio_frame[1] & 0x07; - frame.audio.channel_allocation = p->audio_frame[4]; - frame.audio.level_shift_value = (p->audio_frame[5] & 0x78) >> 3; - frame.audio.downmix_inhibit = (p->audio_frame[5] & 0x80) >> 7; - - /* - * L-PCM and IEC61937 compressed audio shall always set sample - * frequency to "refer to stream". For others, see the HDMI - * specification. - */ - frame.audio.sample_frequency = (p->audio_frame[2] & 0x1c) >> 2; + frame.audio = *cea; tda998x_write_if(priv, DIP_IF_FLAGS_IF4, REG_IF4_HB0, &frame); + + return 0; } static void @@ -710,20 +711,21 @@ static void tda998x_audio_mute(struct tda998x_priv *priv, bool on) } } -static void +static int tda998x_configure_audio(struct tda998x_priv *priv, - struct drm_display_mode *mode, struct tda998x_encoder_params *p) + struct tda998x_audio_params *params, + unsigned mode_clock) { u8 buf[6], clksel_aip, clksel_fs, cts_n, adiv; u32 n; /* Enable audio ports */ - reg_write(priv, REG_ENA_AP, p->audio_cfg); - reg_write(priv, REG_ENA_ACLK, p->audio_clk_cfg); + reg_write(priv, REG_ENA_AP, params->config); /* Set audio input source */ - switch (p->audio_format) { + switch (params->format) { case AFMT_SPDIF: + reg_write(priv, REG_ENA_ACLK, 0); reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_SPDIF); clksel_aip = AIP_CLKSEL_AIP_SPDIF; clksel_fs = AIP_CLKSEL_FS_FS64SPDIF; @@ -731,15 +733,29 @@ tda998x_configure_audio(struct tda998x_priv *priv, break; case AFMT_I2S: + reg_write(priv, REG_ENA_ACLK, 1); reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_I2S); clksel_aip = AIP_CLKSEL_AIP_I2S; clksel_fs = AIP_CLKSEL_FS_ACLK; - cts_n = CTS_N_M(3) | CTS_N_K(3); + switch (params->sample_width) { + case 16: + cts_n = CTS_N_M(3) | CTS_N_K(1); + break; + case 18: + case 20: + case 24: + cts_n = CTS_N_M(3) | CTS_N_K(2); + break; + default: + case 32: + cts_n = CTS_N_M(3) | CTS_N_K(3); + break; + } break; default: - BUG(); - return; + dev_err(&priv->hdmi->dev, "Unsupported I2S format\n"); + return -EINVAL; } reg_write(priv, REG_AIP_CLKSEL, clksel_aip); @@ -755,11 +771,11 @@ tda998x_configure_audio(struct tda998x_priv *priv, * assume 100MHz requires larger divider. */ adiv = AUDIO_DIV_SERCLK_8; - if (mode->clock > 100000) + if (mode_clock > 100000) adiv++; /* AUDIO_DIV_SERCLK_16 */ /* S/PDIF asks for a larger divider */ - if (p->audio_format == AFMT_SPDIF) + if (params->format == AFMT_SPDIF) adiv++; /* AUDIO_DIV_SERCLK_16 or _32 */ reg_write(priv, REG_AUDIO_DIV, adiv); @@ -768,7 +784,7 @@ tda998x_configure_audio(struct tda998x_priv *priv, * This is the approximate value of N, which happens to be * the recommended values for non-coherent clocks. */ - n = 128 * p->audio_sample_rate / 1000; + n = 128 * params->sample_rate / 1000; /* Write the CTS and N values */ buf[0] = 0x44; @@ -786,20 +802,21 @@ tda998x_configure_audio(struct tda998x_priv *priv, reg_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS); reg_clear(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS); - /* Write the channel status */ - buf[0] = IEC958_AES0_CON_NOT_COPYRIGHT; - buf[1] = 0x00; - buf[2] = IEC958_AES3_CON_FS_NOTID; - buf[3] = IEC958_AES4_CON_ORIGFS_NOTID | - IEC958_AES4_CON_MAX_WORDLEN_24; + /* Write the channel status + * The REG_CH_STAT_B-registers skip IEC958 AES2 byte, because + * there is a separate register for each I2S wire. + */ + buf[0] = params->status[0]; + buf[1] = params->status[1]; + buf[2] = params->status[3]; + buf[3] = params->status[4]; reg_write_range(priv, REG_CH_STAT_B(0), buf, 4); tda998x_audio_mute(priv, true); msleep(20); tda998x_audio_mute(priv, false); - /* Write the audio information packet */ - tda998x_write_aif(priv, p); + return tda998x_write_aif(priv, ¶ms->cea); } /* DRM encoder functions */ @@ -820,7 +837,7 @@ static void tda998x_encoder_set_config(struct tda998x_priv *priv, VIP_CNTRL_2_SWAP_F(p->swap_f) | (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0); - priv->params = *p; + priv->audio_params = p->audio_params; } static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode) @@ -1057,9 +1074,13 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder, tda998x_write_avi(priv, adjusted_mode); - if (priv->params.audio_cfg) - tda998x_configure_audio(priv, adjusted_mode, - &priv->params); + if (priv->audio_params.format != AFMT_UNUSED) { + mutex_lock(&priv->audio_mutex); + tda998x_configure_audio(priv, + &priv->audio_params, + adjusted_mode->clock); + mutex_unlock(&priv->audio_mutex); + } } } @@ -1159,6 +1180,8 @@ static int tda998x_connector_get_modes(struct drm_connector *connector) drm_mode_connector_update_edid_property(connector, edid); n = drm_add_edid_modes(connector, edid); priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid); + drm_edid_to_eld(connector, edid); + kfree(edid); return n; @@ -1180,6 +1203,9 @@ static void tda998x_destroy(struct tda998x_priv *priv) cec_write(priv, REG_CEC_RXSHPDINTENA, 0); reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); + if (priv->audio_pdev) + platform_device_unregister(priv->audio_pdev); + if (priv->hdmi->irq) free_irq(priv->hdmi->irq, priv); @@ -1189,8 +1215,189 @@ static void tda998x_destroy(struct tda998x_priv *priv) i2c_unregister_device(priv->cec); } +static int tda998x_audio_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + struct tda998x_priv *priv = dev_get_drvdata(dev); + int i, ret; + struct tda998x_audio_params audio = { + .sample_width = params->sample_width, + .sample_rate = params->sample_rate, + .cea = params->cea, + }; + + if (!priv->encoder.crtc) + return -ENODEV; + + memcpy(audio.status, params->iec.status, + min(sizeof(audio.status), sizeof(params->iec.status))); + + switch (daifmt->fmt) { + case HDMI_I2S: + if (daifmt->bit_clk_inv || daifmt->frame_clk_inv || + daifmt->bit_clk_master || daifmt->frame_clk_master) { + dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__, + daifmt->bit_clk_inv, daifmt->frame_clk_inv, + daifmt->bit_clk_master, + daifmt->frame_clk_master); + return -EINVAL; + } + for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++) + if (priv->audio_port[i].format == AFMT_I2S) + audio.config = priv->audio_port[i].config; + audio.format = AFMT_I2S; + break; + case HDMI_SPDIF: + for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++) + if (priv->audio_port[i].format == AFMT_SPDIF) + audio.config = priv->audio_port[i].config; + audio.format = AFMT_SPDIF; + break; + default: + dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt); + return -EINVAL; + } + + if (audio.config == 0) { + dev_err(dev, "%s: No audio configutation found\n", __func__); + return -EINVAL; + } + + mutex_lock(&priv->audio_mutex); + ret = tda998x_configure_audio(priv, + &audio, + priv->encoder.crtc->hwmode.clock); + + if (ret == 0) + priv->audio_params = audio; + mutex_unlock(&priv->audio_mutex); + + return ret; +} + +static void tda998x_audio_shutdown(struct device *dev, void *data) +{ + struct tda998x_priv *priv = dev_get_drvdata(dev); + + mutex_lock(&priv->audio_mutex); + + reg_write(priv, REG_ENA_AP, 0); + + priv->audio_params.format = AFMT_UNUSED; + + mutex_unlock(&priv->audio_mutex); +} + +int tda998x_audio_digital_mute(struct device *dev, void *data, bool enable) +{ + struct tda998x_priv *priv = dev_get_drvdata(dev); + + mutex_lock(&priv->audio_mutex); + + tda998x_audio_mute(priv, enable); + + mutex_unlock(&priv->audio_mutex); + return 0; +} + +static int tda998x_audio_get_eld(struct device *dev, void *data, + uint8_t *buf, size_t len) +{ + struct tda998x_priv *priv = dev_get_drvdata(dev); + struct drm_mode_config *config = &priv->encoder.dev->mode_config; + struct drm_connector *connector; + int ret = -ENODEV; + + mutex_lock(&config->mutex); + list_for_each_entry(connector, &config->connector_list, head) { + if (&priv->encoder == connector->encoder) { + memcpy(buf, connector->eld, + min(sizeof(connector->eld), len)); + ret = 0; + } + } + mutex_unlock(&config->mutex); + + return ret; +} + +static const struct hdmi_codec_ops audio_codec_ops = { + .hw_params = tda998x_audio_hw_params, + .audio_shutdown = tda998x_audio_shutdown, + .digital_mute = tda998x_audio_digital_mute, + .get_eld = tda998x_audio_get_eld, +}; + +static int tda998x_audio_codec_init(struct tda998x_priv *priv, + struct device *dev) +{ + struct hdmi_codec_pdata codec_data = { + .ops = &audio_codec_ops, + .max_i2s_channels = 2, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++) { + if (priv->audio_port[i].format == AFMT_I2S && + priv->audio_port[i].config != 0) + codec_data.i2s = 1; + if (priv->audio_port[i].format == AFMT_SPDIF && + priv->audio_port[i].config != 0) + codec_data.spdif = 1; + } + + priv->audio_pdev = platform_device_register_data( + dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO, + &codec_data, sizeof(codec_data)); + + return PTR_ERR_OR_ZERO(priv->audio_pdev); +} + /* I2C driver functions */ +static int tda998x_get_audio_ports(struct tda998x_priv *priv, + struct device_node *np) +{ + const u32 *port_data; + u32 size; + int i; + + port_data = of_get_property(np, "audio-ports", &size); + if (!port_data) + return 0; + + size /= sizeof(u32); + if (size > 2 * ARRAY_SIZE(priv->audio_port) || size % 2 != 0) { + dev_err(&priv->hdmi->dev, + "Bad number of elements in audio-ports dt-property\n"); + return -EINVAL; + } + + size /= 2; + + for (i = 0; i < size; i++) { + u8 afmt = be32_to_cpup(&port_data[2*i]); + u8 ena_ap = be32_to_cpup(&port_data[2*i+1]); + + if (afmt != AFMT_SPDIF && afmt != AFMT_I2S) { + dev_err(&priv->hdmi->dev, + "Bad audio format %u\n", afmt); + return -EINVAL; + } + + priv->audio_port[i].format = afmt; + priv->audio_port[i].config = ena_ap; + } + + if (priv->audio_port[0].format == priv->audio_port[1].format) { + dev_err(&priv->hdmi->dev, + "There can only be on I2S port and one SPDIF port\n"); + return -EINVAL; + } + return 0; +} + static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) { struct device_node *np = client->dev.of_node; @@ -1304,7 +1511,7 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) if (!np) return 0; /* non-DT */ - /* get the optional video properties */ + /* get the device tree parameters */ ret = of_property_read_u32(np, "video-ports", &video); if (ret == 0) { priv->vip_cntrl_0 = video >> 16; @@ -1312,8 +1519,16 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) priv->vip_cntrl_2 = video; } - return 0; + mutex_init(&priv->audio_mutex); /* Protect access from audio thread */ + ret = tda998x_get_audio_ports(priv, np); + if (ret) + goto fail; + + if (priv->audio_port[0].format != AFMT_UNUSED) + tda998x_audio_codec_init(priv, &client->dev); + + return 0; fail: /* if encoder_init fails, the encoder slave is never registered, * so cleanup here: diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c index 44f4a13..0be55dc 100644 --- a/drivers/gpu/drm/i810/i810_drv.c +++ b/drivers/gpu/drm/i810/i810_drv.c @@ -56,9 +56,7 @@ static const struct file_operations i810_driver_fops = { }; static struct drm_driver driver = { - .driver_features = - DRIVER_USE_AGP | - DRIVER_HAVE_DMA, + .driver_features = DRIVER_USE_AGP | DRIVER_HAVE_DMA | DRIVER_LEGACY, .dev_priv_size = sizeof(drm_i810_buf_priv_t), .load = i810_driver_load, .lastclose = i810_driver_lastclose, diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 684fc1c..a998c2b 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -3,15 +3,20 @@ # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. subdir-ccflags-$(CONFIG_DRM_I915_WERROR) := -Werror +subdir-ccflags-y += \ + $(call as-instr,movntdqa (%eax)$(comma)%xmm0,-DCONFIG_AS_MOVNTDQA) # Please keep these build lists sorted! # core driver code i915-y := i915_drv.o \ i915_irq.o \ + i915_memcpy.o \ + i915_mm.o \ i915_params.o \ i915_pci.o \ i915_suspend.o \ + i915_sw_fence.o \ i915_sysfs.o \ intel_csr.o \ intel_device_info.o \ @@ -25,7 +30,6 @@ i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o i915-y += i915_cmd_parser.o \ i915_gem_batch_pool.o \ i915_gem_context.o \ - i915_gem_debug.o \ i915_gem_dmabuf.o \ i915_gem_evict.o \ i915_gem_execbuffer.o \ @@ -33,6 +37,7 @@ i915-y += i915_cmd_parser.o \ i915_gem_gtt.o \ i915_gem.o \ i915_gem_render_state.o \ + i915_gem_request.o \ i915_gem_shrinker.o \ i915_gem_stolen.o \ i915_gem_tiling.o \ @@ -40,6 +45,7 @@ i915-y += i915_cmd_parser.o \ i915_gpu_error.o \ i915_trace_points.o \ intel_breadcrumbs.o \ + intel_engine_cs.o \ intel_lrc.o \ intel_mocs.o \ intel_ringbuffer.o \ @@ -109,6 +115,6 @@ i915-y += intel_gvt.o include $(src)/gvt/Makefile endif -obj-$(CONFIG_DRM_I915) += i915.o +obj-$(CONFIG_DRM_I915) += i915.o CFLAGS_i915_trace_points.o := -I$(src) diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c index b0fd6a7..70980f8 100644 --- a/drivers/gpu/drm/i915/i915_cmd_parser.c +++ b/drivers/gpu/drm/i915/i915_cmd_parser.c @@ -62,23 +62,23 @@ * The parser always rejects such commands. * * The majority of the problematic commands fall in the MI_* range, with only a - * few specific commands on each ring (e.g. PIPE_CONTROL and MI_FLUSH_DW). + * few specific commands on each engine (e.g. PIPE_CONTROL and MI_FLUSH_DW). * * Implementation: - * Each ring maintains tables of commands and registers which the parser uses in - * scanning batch buffers submitted to that ring. + * Each engine maintains tables of commands and registers which the parser + * uses in scanning batch buffers submitted to that engine. * * Since the set of commands that the parser must check for is significantly * smaller than the number of commands supported, the parser tables contain only * those commands required by the parser. This generally works because command * opcode ranges have standard command length encodings. So for commands that * the parser does not need to check, it can easily skip them. This is - * implemented via a per-ring length decoding vfunc. + * implemented via a per-engine length decoding vfunc. * * Unfortunately, there are a number of commands that do not follow the standard * length encoding for their opcode range, primarily amongst the MI_* commands. * To handle this, the parser provides a way to define explicit "skip" entries - * in the per-ring command tables. + * in the per-engine command tables. * * Other command table entries map fairly directly to high level categories * mentioned above: rejected, master-only, register whitelist. The parser @@ -86,24 +86,25 @@ * general bitmasking mechanism. */ -#define STD_MI_OPCODE_MASK 0xFF800000 -#define STD_3D_OPCODE_MASK 0xFFFF0000 -#define STD_2D_OPCODE_MASK 0xFFC00000 -#define STD_MFX_OPCODE_MASK 0xFFFF0000 +#define STD_MI_OPCODE_SHIFT (32 - 9) +#define STD_3D_OPCODE_SHIFT (32 - 16) +#define STD_2D_OPCODE_SHIFT (32 - 10) +#define STD_MFX_OPCODE_SHIFT (32 - 16) +#define MIN_OPCODE_SHIFT 16 #define CMD(op, opm, f, lm, fl, ...) \ { \ .flags = (fl) | ((f) ? CMD_DESC_FIXED : 0), \ - .cmd = { (op), (opm) }, \ + .cmd = { (op), ~0u << (opm) }, \ .length = { (lm) }, \ __VA_ARGS__ \ } /* Convenience macros to compress the tables */ -#define SMI STD_MI_OPCODE_MASK -#define S3D STD_3D_OPCODE_MASK -#define S2D STD_2D_OPCODE_MASK -#define SMFX STD_MFX_OPCODE_MASK +#define SMI STD_MI_OPCODE_SHIFT +#define S3D STD_3D_OPCODE_SHIFT +#define S2D STD_2D_OPCODE_SHIFT +#define SMFX STD_MFX_OPCODE_SHIFT #define F true #define S CMD_DESC_SKIP #define R CMD_DESC_REJECT @@ -350,6 +351,9 @@ static const struct drm_i915_cmd_descriptor hsw_blt_cmds[] = { CMD( MI_LOAD_SCAN_LINES_EXCL, SMI, !F, 0x3F, R ), }; +static const struct drm_i915_cmd_descriptor noop_desc = + CMD(MI_NOOP, SMI, F, 1, S); + #undef CMD #undef SMI #undef S3D @@ -458,6 +462,7 @@ static const struct drm_i915_reg_descriptor gen7_render_regs[] = { REG32(GEN7_GPGPU_DISPATCHDIMX), REG32(GEN7_GPGPU_DISPATCHDIMY), REG32(GEN7_GPGPU_DISPATCHDIMZ), + REG64_IDX(RING_TIMESTAMP, BSD_RING_BASE), REG64_IDX(GEN7_SO_NUM_PRIMS_WRITTEN, 0), REG64_IDX(GEN7_SO_NUM_PRIMS_WRITTEN, 1), REG64_IDX(GEN7_SO_NUM_PRIMS_WRITTEN, 2), @@ -473,6 +478,7 @@ static const struct drm_i915_reg_descriptor gen7_render_regs[] = { REG32(GEN7_L3SQCREG1), REG32(GEN7_L3CNTLREG2), REG32(GEN7_L3CNTLREG3), + REG64_IDX(RING_TIMESTAMP, BLT_RING_BASE), }; static const struct drm_i915_reg_descriptor hsw_render_regs[] = { @@ -502,7 +508,10 @@ static const struct drm_i915_reg_descriptor hsw_render_regs[] = { }; static const struct drm_i915_reg_descriptor gen7_blt_regs[] = { + REG64_IDX(RING_TIMESTAMP, RENDER_RING_BASE), + REG64_IDX(RING_TIMESTAMP, BSD_RING_BASE), REG32(BCS_SWCTRL), + REG64_IDX(RING_TIMESTAMP, BLT_RING_BASE), }; static const struct drm_i915_reg_descriptor ivb_master_regs[] = { @@ -603,7 +612,7 @@ static u32 gen7_blt_get_cmd_length_mask(u32 cmd_header) return 0; } -static bool validate_cmds_sorted(struct intel_engine_cs *engine, +static bool validate_cmds_sorted(const struct intel_engine_cs *engine, const struct drm_i915_cmd_table *cmd_tables, int cmd_table_count) { @@ -624,8 +633,10 @@ static bool validate_cmds_sorted(struct intel_engine_cs *engine, u32 curr = desc->cmd.value & desc->cmd.mask; if (curr < previous) { - DRM_ERROR("CMD: table not sorted ring=%d table=%d entry=%d cmd=0x%08X prev=0x%08X\n", - engine->id, i, j, curr, previous); + DRM_ERROR("CMD: %s [%d] command table not sorted: " + "table=%d entry=%d cmd=0x%08X prev=0x%08X\n", + engine->name, engine->id, + i, j, curr, previous); ret = false; } @@ -636,7 +647,7 @@ static bool validate_cmds_sorted(struct intel_engine_cs *engine, return ret; } -static bool check_sorted(int ring_id, +static bool check_sorted(const struct intel_engine_cs *engine, const struct drm_i915_reg_descriptor *reg_table, int reg_count) { @@ -648,8 +659,10 @@ static bool check_sorted(int ring_id, u32 curr = i915_mmio_reg_offset(reg_table[i].addr); if (curr < previous) { - DRM_ERROR("CMD: table not sorted ring=%d entry=%d reg=0x%08X prev=0x%08X\n", - ring_id, i, curr, previous); + DRM_ERROR("CMD: %s [%d] register table not sorted: " + "entry=%d reg=0x%08X prev=0x%08X\n", + engine->name, engine->id, + i, curr, previous); ret = false; } @@ -666,7 +679,7 @@ static bool validate_regs_sorted(struct intel_engine_cs *engine) for (i = 0; i < engine->reg_table_count; i++) { table = &engine->reg_tables[i]; - if (!check_sorted(engine->id, table->regs, table->num_regs)) + if (!check_sorted(engine, table->regs, table->num_regs)) return false; } @@ -687,12 +700,26 @@ struct cmd_node { * non-opcode bits being set. But if we don't include those bits, some 3D * commands may hash to the same bucket due to not including opcode bits that * make the command unique. For now, we will risk hashing to the same bucket. - * - * If we attempt to generate a perfect hash, we should be able to look at bits - * 31:29 of a command from a batch buffer and use the full mask for that - * client. The existing INSTR_CLIENT_MASK/SHIFT defines can be used for this. */ -#define CMD_HASH_MASK STD_MI_OPCODE_MASK +static inline u32 cmd_header_key(u32 x) +{ + u32 shift; + + switch (x >> INSTR_CLIENT_SHIFT) { + default: + case INSTR_MI_CLIENT: + shift = STD_MI_OPCODE_SHIFT; + break; + case INSTR_RC_CLIENT: + shift = STD_3D_OPCODE_SHIFT; + break; + case INSTR_BC_CLIENT: + shift = STD_2D_OPCODE_SHIFT; + break; + } + + return x >> shift; +} static int init_hash_table(struct intel_engine_cs *engine, const struct drm_i915_cmd_table *cmd_tables, @@ -716,7 +743,7 @@ static int init_hash_table(struct intel_engine_cs *engine, desc_node->desc = desc; hash_add(engine->cmd_hash, &desc_node->node, - desc->cmd.value & CMD_HASH_MASK); + cmd_header_key(desc->cmd.value)); } } @@ -736,23 +763,21 @@ static void fini_hash_table(struct intel_engine_cs *engine) } /** - * i915_cmd_parser_init_ring() - set cmd parser related fields for a ringbuffer + * intel_engine_init_cmd_parser() - set cmd parser related fields for an engine * @engine: the engine to initialize * * Optionally initializes fields related to batch buffer command parsing in the * struct intel_engine_cs based on whether the platform requires software * command parsing. - * - * Return: non-zero if initialization fails */ -int i915_cmd_parser_init_ring(struct intel_engine_cs *engine) +void intel_engine_init_cmd_parser(struct intel_engine_cs *engine) { const struct drm_i915_cmd_table *cmd_tables; int cmd_table_count; int ret; if (!IS_GEN7(engine->i915)) - return 0; + return; switch (engine->id) { case RCS: @@ -806,36 +831,38 @@ int i915_cmd_parser_init_ring(struct intel_engine_cs *engine) engine->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask; break; default: - DRM_ERROR("CMD: cmd_parser_init with unknown ring: %d\n", - engine->id); - BUG(); + MISSING_CASE(engine->id); + return; } - BUG_ON(!validate_cmds_sorted(engine, cmd_tables, cmd_table_count)); - BUG_ON(!validate_regs_sorted(engine)); - - WARN_ON(!hash_empty(engine->cmd_hash)); + if (!validate_cmds_sorted(engine, cmd_tables, cmd_table_count)) { + DRM_ERROR("%s: command descriptions are not sorted\n", + engine->name); + return; + } + if (!validate_regs_sorted(engine)) { + DRM_ERROR("%s: registers are not sorted\n", engine->name); + return; + } ret = init_hash_table(engine, cmd_tables, cmd_table_count); if (ret) { - DRM_ERROR("CMD: cmd_parser_init failed!\n"); + DRM_ERROR("%s: initialised failed!\n", engine->name); fini_hash_table(engine); - return ret; + return; } engine->needs_cmd_parser = true; - - return 0; } /** - * i915_cmd_parser_fini_ring() - clean up cmd parser related fields + * intel_engine_cleanup_cmd_parser() - clean up cmd parser related fields * @engine: the engine to clean up * * Releases any resources related to command parsing that may have been - * initialized for the specified ring. + * initialized for the specified engine. */ -void i915_cmd_parser_fini_ring(struct intel_engine_cs *engine) +void intel_engine_cleanup_cmd_parser(struct intel_engine_cs *engine) { if (!engine->needs_cmd_parser) return; @@ -850,12 +877,9 @@ find_cmd_in_table(struct intel_engine_cs *engine, struct cmd_node *desc_node; hash_for_each_possible(engine->cmd_hash, desc_node, node, - cmd_header & CMD_HASH_MASK) { + cmd_header_key(cmd_header)) { const struct drm_i915_cmd_descriptor *desc = desc_node->desc; - u32 masked_cmd = desc->cmd.mask & cmd_header; - u32 masked_value = desc->cmd.value & desc->cmd.mask; - - if (masked_cmd == masked_value) + if (((cmd_header ^ desc->cmd.value) & desc->cmd.mask) == 0) return desc; } @@ -866,18 +890,21 @@ find_cmd_in_table(struct intel_engine_cs *engine, * Returns a pointer to a descriptor for the command specified by cmd_header. * * The caller must supply space for a default descriptor via the default_desc - * parameter. If no descriptor for the specified command exists in the ring's + * parameter. If no descriptor for the specified command exists in the engine's * command parser tables, this function fills in default_desc based on the - * ring's default length encoding and returns default_desc. + * engine's default length encoding and returns default_desc. */ static const struct drm_i915_cmd_descriptor* find_cmd(struct intel_engine_cs *engine, u32 cmd_header, + const struct drm_i915_cmd_descriptor *desc, struct drm_i915_cmd_descriptor *default_desc) { - const struct drm_i915_cmd_descriptor *desc; u32 mask; + if (((cmd_header ^ desc->cmd.value) & desc->cmd.mask) == 0) + return desc; + desc = find_cmd_in_table(engine, cmd_header); if (desc) return desc; @@ -886,152 +913,140 @@ find_cmd(struct intel_engine_cs *engine, if (!mask) return NULL; - BUG_ON(!default_desc); - default_desc->flags = CMD_DESC_SKIP; + default_desc->cmd.value = cmd_header; + default_desc->cmd.mask = ~0u << MIN_OPCODE_SHIFT; default_desc->length.mask = mask; - + default_desc->flags = CMD_DESC_SKIP; return default_desc; } static const struct drm_i915_reg_descriptor * -find_reg(const struct drm_i915_reg_descriptor *table, - int count, u32 addr) +__find_reg(const struct drm_i915_reg_descriptor *table, int count, u32 addr) { - int i; - - for (i = 0; i < count; i++) { - if (i915_mmio_reg_offset(table[i].addr) == addr) - return &table[i]; + int start = 0, end = count; + while (start < end) { + int mid = start + (end - start) / 2; + int ret = addr - i915_mmio_reg_offset(table[mid].addr); + if (ret < 0) + end = mid; + else if (ret > 0) + start = mid + 1; + else + return &table[mid]; } - return NULL; } static const struct drm_i915_reg_descriptor * -find_reg_in_tables(const struct drm_i915_reg_table *tables, - int count, bool is_master, u32 addr) +find_reg(const struct intel_engine_cs *engine, bool is_master, u32 addr) { - int i; - const struct drm_i915_reg_table *table; - const struct drm_i915_reg_descriptor *reg; + const struct drm_i915_reg_table *table = engine->reg_tables; + int count = engine->reg_table_count; - for (i = 0; i < count; i++) { - table = &tables[i]; + do { if (!table->master || is_master) { - reg = find_reg(table->regs, table->num_regs, - addr); + const struct drm_i915_reg_descriptor *reg; + + reg = __find_reg(table->regs, table->num_regs, addr); if (reg != NULL) return reg; } - } + } while (table++, --count); return NULL; } -static u32 *vmap_batch(struct drm_i915_gem_object *obj, - unsigned start, unsigned len) -{ - int i; - void *addr = NULL; - struct sg_page_iter sg_iter; - int first_page = start >> PAGE_SHIFT; - int last_page = (len + start + 4095) >> PAGE_SHIFT; - int npages = last_page - first_page; - struct page **pages; - - pages = drm_malloc_ab(npages, sizeof(*pages)); - if (pages == NULL) { - DRM_DEBUG_DRIVER("Failed to get space for pages\n"); - goto finish; - } - - i = 0; - for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, first_page) { - pages[i++] = sg_page_iter_page(&sg_iter); - if (i == npages) - break; - } - - addr = vmap(pages, i, 0, PAGE_KERNEL); - if (addr == NULL) { - DRM_DEBUG_DRIVER("Failed to vmap pages\n"); - goto finish; - } - -finish: - if (pages) - drm_free_large(pages); - return (u32*)addr; -} - -/* Returns a vmap'd pointer to dest_obj, which the caller must unmap */ -static u32 *copy_batch(struct drm_i915_gem_object *dest_obj, +/* Returns a vmap'd pointer to dst_obj, which the caller must unmap */ +static u32 *copy_batch(struct drm_i915_gem_object *dst_obj, struct drm_i915_gem_object *src_obj, u32 batch_start_offset, - u32 batch_len) + u32 batch_len, + bool *needs_clflush_after) { - int needs_clflush = 0; - void *src_base, *src; - void *dst = NULL; + unsigned int src_needs_clflush; + unsigned int dst_needs_clflush; + void *dst, *src; int ret; - if (batch_len > dest_obj->base.size || - batch_len + batch_start_offset > src_obj->base.size) - return ERR_PTR(-E2BIG); - - if (WARN_ON(dest_obj->pages_pin_count == 0)) - return ERR_PTR(-ENODEV); - - ret = i915_gem_obj_prepare_shmem_read(src_obj, &needs_clflush); - if (ret) { - DRM_DEBUG_DRIVER("CMD: failed to prepare shadow batch\n"); + ret = i915_gem_obj_prepare_shmem_read(src_obj, &src_needs_clflush); + if (ret) return ERR_PTR(ret); - } - src_base = vmap_batch(src_obj, batch_start_offset, batch_len); - if (!src_base) { - DRM_DEBUG_DRIVER("CMD: Failed to vmap batch\n"); - ret = -ENOMEM; + ret = i915_gem_obj_prepare_shmem_write(dst_obj, &dst_needs_clflush); + if (ret) { + dst = ERR_PTR(ret); goto unpin_src; } - ret = i915_gem_object_set_to_cpu_domain(dest_obj, true); - if (ret) { - DRM_DEBUG_DRIVER("CMD: Failed to set shadow batch to CPU\n"); - goto unmap_src; + dst = i915_gem_object_pin_map(dst_obj, I915_MAP_WB); + if (IS_ERR(dst)) + goto unpin_dst; + + src = ERR_PTR(-ENODEV); + if (src_needs_clflush && + i915_memcpy_from_wc((void *)(uintptr_t)batch_start_offset, NULL, 0)) { + src = i915_gem_object_pin_map(src_obj, I915_MAP_WC); + if (!IS_ERR(src)) { + i915_memcpy_from_wc(dst, + src + batch_start_offset, + ALIGN(batch_len, 16)); + i915_gem_object_unpin_map(src_obj); + } } - - dst = vmap_batch(dest_obj, 0, batch_len); - if (!dst) { - DRM_DEBUG_DRIVER("CMD: Failed to vmap shadow batch\n"); - ret = -ENOMEM; - goto unmap_src; + if (IS_ERR(src)) { + void *ptr; + int offset, n; + + offset = offset_in_page(batch_start_offset); + + /* We can avoid clflushing partial cachelines before the write + * if we only every write full cache-lines. Since we know that + * both the source and destination are in multiples of + * PAGE_SIZE, we can simply round up to the next cacheline. + * We don't care about copying too much here as we only + * validate up to the end of the batch. + */ + if (dst_needs_clflush & CLFLUSH_BEFORE) + batch_len = roundup(batch_len, + boot_cpu_data.x86_clflush_size); + + ptr = dst; + for (n = batch_start_offset >> PAGE_SHIFT; batch_len; n++) { + int len = min_t(int, batch_len, PAGE_SIZE - offset); + + src = kmap_atomic(i915_gem_object_get_page(src_obj, n)); + if (src_needs_clflush) + drm_clflush_virt_range(src + offset, len); + memcpy(ptr, src + offset, len); + kunmap_atomic(src); + + ptr += len; + batch_len -= len; + offset = 0; + } } - src = src_base + offset_in_page(batch_start_offset); - if (needs_clflush) - drm_clflush_virt_range(src, batch_len); - - memcpy(dst, src, batch_len); + /* dst_obj is returned with vmap pinned */ + *needs_clflush_after = dst_needs_clflush & CLFLUSH_AFTER; -unmap_src: - vunmap(src_base); +unpin_dst: + i915_gem_obj_finish_shmem_access(dst_obj); unpin_src: - i915_gem_object_unpin_pages(src_obj); - - return ret ? ERR_PTR(ret) : dst; + i915_gem_obj_finish_shmem_access(src_obj); + return dst; } /** - * i915_needs_cmd_parser() - should a given ring use software command parsing? + * intel_engine_needs_cmd_parser() - should a given engine use software + * command parsing? * @engine: the engine in question * * Only certain platforms require software batch buffer command parsing, and * only when enabled via module parameter. * - * Return: true if the ring requires software command parsing + * Return: true if the engine requires software command parsing */ -bool i915_needs_cmd_parser(struct intel_engine_cs *engine) +bool intel_engine_needs_cmd_parser(struct intel_engine_cs *engine) { if (!engine->needs_cmd_parser) return false; @@ -1048,6 +1063,9 @@ static bool check_cmd(const struct intel_engine_cs *engine, const bool is_master, bool *oacontrol_set) { + if (desc->flags & CMD_DESC_SKIP) + return true; + if (desc->flags & CMD_DESC_REJECT) { DRM_DEBUG_DRIVER("CMD: Rejected command: 0x%08X\n", *cmd); return false; @@ -1072,14 +1090,11 @@ static bool check_cmd(const struct intel_engine_cs *engine, offset += step) { const u32 reg_addr = cmd[offset] & desc->reg.mask; const struct drm_i915_reg_descriptor *reg = - find_reg_in_tables(engine->reg_tables, - engine->reg_table_count, - is_master, - reg_addr); + find_reg(engine, is_master, reg_addr); if (!reg) { - DRM_DEBUG_DRIVER("CMD: Rejected register 0x%08X in command: 0x%08X (ring=%d)\n", - reg_addr, *cmd, engine->id); + DRM_DEBUG_DRIVER("CMD: Rejected register 0x%08X in command: 0x%08X (exec_id=%d)\n", + reg_addr, *cmd, engine->exec_id); return false; } @@ -1159,11 +1174,11 @@ static bool check_cmd(const struct intel_engine_cs *engine, desc->bits[i].mask; if (dword != desc->bits[i].expected) { - DRM_DEBUG_DRIVER("CMD: Rejected command 0x%08X for bitmask 0x%08X (exp=0x%08X act=0x%08X) (ring=%d)\n", + DRM_DEBUG_DRIVER("CMD: Rejected command 0x%08X for bitmask 0x%08X (exp=0x%08X act=0x%08X) (exec_id=%d)\n", *cmd, desc->bits[i].mask, desc->bits[i].expected, - dword, engine->id); + dword, engine->exec_id); return false; } } @@ -1189,23 +1204,26 @@ static bool check_cmd(const struct intel_engine_cs *engine, * Return: non-zero if the parser finds violations or otherwise fails; -EACCES * if the batch appears legal but should use hardware parsing */ -int i915_parse_cmds(struct intel_engine_cs *engine, - struct drm_i915_gem_object *batch_obj, - struct drm_i915_gem_object *shadow_batch_obj, - u32 batch_start_offset, - u32 batch_len, - bool is_master) +int intel_engine_cmd_parser(struct intel_engine_cs *engine, + struct drm_i915_gem_object *batch_obj, + struct drm_i915_gem_object *shadow_batch_obj, + u32 batch_start_offset, + u32 batch_len, + bool is_master) { - u32 *cmd, *batch_base, *batch_end; - struct drm_i915_cmd_descriptor default_desc = { 0 }; + u32 *cmd, *batch_end; + struct drm_i915_cmd_descriptor default_desc = noop_desc; + const struct drm_i915_cmd_descriptor *desc = &default_desc; bool oacontrol_set = false; /* OACONTROL tracking. See check_cmd() */ + bool needs_clflush_after = false; int ret = 0; - batch_base = copy_batch(shadow_batch_obj, batch_obj, - batch_start_offset, batch_len); - if (IS_ERR(batch_base)) { + cmd = copy_batch(shadow_batch_obj, batch_obj, + batch_start_offset, batch_len, + &needs_clflush_after); + if (IS_ERR(cmd)) { DRM_DEBUG_DRIVER("CMD: Failed to copy batch\n"); - return PTR_ERR(batch_base); + return PTR_ERR(cmd); } /* @@ -1213,17 +1231,14 @@ int i915_parse_cmds(struct intel_engine_cs *engine, * large or larger and copy_batch() will write MI_NOPs to the extra * space. Parsing should be faster in some cases this way. */ - batch_end = batch_base + (batch_len / sizeof(*batch_end)); - - cmd = batch_base; + batch_end = cmd + (batch_len / sizeof(*batch_end)); while (cmd < batch_end) { - const struct drm_i915_cmd_descriptor *desc; u32 length; if (*cmd == MI_BATCH_BUFFER_END) break; - desc = find_cmd(engine, *cmd, &default_desc); + desc = find_cmd(engine, *cmd, desc, &default_desc); if (!desc) { DRM_DEBUG_DRIVER("CMD: Unrecognized command: 0x%08X\n", *cmd); @@ -1274,7 +1289,9 @@ int i915_parse_cmds(struct intel_engine_cs *engine, ret = -EINVAL; } - vunmap(batch_base); + if (ret == 0 && needs_clflush_after) + drm_clflush_virt_range(shadow_batch_obj->mapping, batch_len); + i915_gem_object_unpin_map(shadow_batch_obj); return ret; } @@ -1295,7 +1312,7 @@ int i915_cmd_parser_get_version(struct drm_i915_private *dev_priv) /* If the command parser is not enabled, report 0 - unsupported */ for_each_engine(engine, dev_priv) { - if (i915_needs_cmd_parser(engine)) { + if (intel_engine_needs_cmd_parser(engine)) { active = true; break; } diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 844fea7..27b0e34 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -40,11 +40,10 @@ #include <drm/i915_drm.h> #include "i915_drv.h" -enum { - ACTIVE_LIST, - INACTIVE_LIST, - PINNED_LIST, -}; +static inline struct drm_i915_private *node_to_i915(struct drm_info_node *node) +{ + return to_i915(node->minor->dev); +} /* As the drm_debugfs_init() routines are called before dev->dev_private is * allocated we need to hook into the minor for release. */ @@ -63,7 +62,7 @@ drm_add_fake_info_node(struct drm_minor *minor, node->minor = minor; node->dent = ent; - node->info_ent = (void *) key; + node->info_ent = (void *)key; mutex_lock(&minor->debugfs_lock); list_add(&node->list, &minor->debugfs_list); @@ -74,12 +73,11 @@ drm_add_fake_info_node(struct drm_minor *minor, static int i915_capabilities(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - const struct intel_device_info *info = INTEL_INFO(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + const struct intel_device_info *info = INTEL_INFO(dev_priv); - seq_printf(m, "gen: %d\n", info->gen); - seq_printf(m, "pch: %d\n", INTEL_PCH_TYPE(dev)); + seq_printf(m, "gen: %d\n", INTEL_GEN(dev_priv)); + seq_printf(m, "pch: %d\n", INTEL_PCH_TYPE(dev_priv)); #define PRINT_FLAG(x) seq_printf(m, #x ": %s\n", yesno(info->x)) #define SEP_SEMICOLON ; DEV_INFO_FOR_EACH_FLAG(PRINT_FLAG, SEP_SEMICOLON); @@ -91,7 +89,7 @@ static int i915_capabilities(struct seq_file *m, void *data) static char get_active_flag(struct drm_i915_gem_object *obj) { - return obj->active ? '*' : ' '; + return i915_gem_object_is_active(obj) ? '*' : ' '; } static char get_pin_flag(struct drm_i915_gem_object *obj) @@ -101,7 +99,7 @@ static char get_pin_flag(struct drm_i915_gem_object *obj) static char get_tiling_flag(struct drm_i915_gem_object *obj) { - switch (obj->tiling_mode) { + switch (i915_gem_object_get_tiling(obj)) { default: case I915_TILING_NONE: return ' '; case I915_TILING_X: return 'X'; @@ -111,7 +109,7 @@ static char get_tiling_flag(struct drm_i915_gem_object *obj) static char get_global_flag(struct drm_i915_gem_object *obj) { - return i915_gem_obj_to_ggtt(obj) ? 'g' : ' '; + return i915_gem_object_to_ggtt(obj, NULL) ? 'g' : ' '; } static char get_pin_mapped_flag(struct drm_i915_gem_object *obj) @@ -125,7 +123,7 @@ static u64 i915_gem_obj_total_ggtt_size(struct drm_i915_gem_object *obj) struct i915_vma *vma; list_for_each_entry(vma, &obj->vma_list, obj_link) { - if (vma->is_ggtt && drm_mm_node_allocated(&vma->node)) + if (i915_vma_is_ggtt(vma) && drm_mm_node_allocated(&vma->node)) size += vma->node.size; } @@ -138,6 +136,7 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) struct drm_i915_private *dev_priv = to_i915(obj->base.dev); struct intel_engine_cs *engine; struct i915_vma *vma; + unsigned int frontbuffer_bits; int pin_count = 0; enum intel_engine_id id; @@ -155,30 +154,36 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) obj->base.write_domain); for_each_engine_id(engine, dev_priv, id) seq_printf(m, "%x ", - i915_gem_request_get_seqno(obj->last_read_req[id])); - seq_printf(m, "] %x %x%s%s%s", - i915_gem_request_get_seqno(obj->last_write_req), - i915_gem_request_get_seqno(obj->last_fenced_req), - i915_cache_level_str(to_i915(obj->base.dev), obj->cache_level), + i915_gem_active_get_seqno(&obj->last_read[id], + &obj->base.dev->struct_mutex)); + seq_printf(m, "] %x %s%s%s", + i915_gem_active_get_seqno(&obj->last_write, + &obj->base.dev->struct_mutex), + i915_cache_level_str(dev_priv, obj->cache_level), obj->dirty ? " dirty" : "", obj->madv == I915_MADV_DONTNEED ? " purgeable" : ""); if (obj->base.name) seq_printf(m, " (name: %d)", obj->base.name); list_for_each_entry(vma, &obj->vma_list, obj_link) { - if (vma->pin_count > 0) + if (i915_vma_is_pinned(vma)) pin_count++; } seq_printf(m, " (pinned x %d)", pin_count); if (obj->pin_display) seq_printf(m, " (display)"); - if (obj->fence_reg != I915_FENCE_REG_NONE) - seq_printf(m, " (fence: %d)", obj->fence_reg); list_for_each_entry(vma, &obj->vma_list, obj_link) { + if (!drm_mm_node_allocated(&vma->node)) + continue; + seq_printf(m, " (%sgtt offset: %08llx, size: %08llx", - vma->is_ggtt ? "g" : "pp", + i915_vma_is_ggtt(vma) ? "g" : "pp", vma->node.start, vma->node.size); - if (vma->is_ggtt) + if (i915_vma_is_ggtt(vma)) seq_printf(m, ", type: %u", vma->ggtt_view.type); + if (vma->fence) + seq_printf(m, " , fence: %d%s", + vma->fence->id, + i915_gem_active_isset(&vma->last_fence) ? "*" : ""); seq_puts(m, ")"); } if (obj->stolen) @@ -192,58 +197,15 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) *t = '\0'; seq_printf(m, " (%s mappable)", s); } - if (obj->last_write_req != NULL) - seq_printf(m, " (%s)", - i915_gem_request_get_engine(obj->last_write_req)->name); - if (obj->frontbuffer_bits) - seq_printf(m, " (frontbuffer: 0x%03x)", obj->frontbuffer_bits); -} - -static int i915_gem_object_list_info(struct seq_file *m, void *data) -{ - struct drm_info_node *node = m->private; - uintptr_t list = (uintptr_t) node->info_ent->data; - struct list_head *head; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct i915_ggtt *ggtt = &dev_priv->ggtt; - struct i915_vma *vma; - u64 total_obj_size, total_gtt_size; - int count, ret; - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; + engine = i915_gem_active_get_engine(&obj->last_write, + &dev_priv->drm.struct_mutex); + if (engine) + seq_printf(m, " (%s)", engine->name); - /* FIXME: the user of this interface might want more than just GGTT */ - switch (list) { - case ACTIVE_LIST: - seq_puts(m, "Active:\n"); - head = &ggtt->base.active_list; - break; - case INACTIVE_LIST: - seq_puts(m, "Inactive:\n"); - head = &ggtt->base.inactive_list; - break; - default: - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - } - - total_obj_size = total_gtt_size = count = 0; - list_for_each_entry(vma, head, vm_link) { - seq_printf(m, " "); - describe_obj(m, vma->obj); - seq_printf(m, "\n"); - total_obj_size += vma->obj->base.size; - total_gtt_size += vma->node.size; - count++; - } - mutex_unlock(&dev->struct_mutex); - - seq_printf(m, "Total %d objects, %llu bytes, %llu GTT size\n", - count, total_obj_size, total_gtt_size); - return 0; + frontbuffer_bits = atomic_read(&obj->frontbuffer_bits); + if (frontbuffer_bits) + seq_printf(m, " (frontbuffer: 0x%03x)", frontbuffer_bits); } static int obj_rank_by_stolen(void *priv, @@ -263,9 +225,8 @@ static int obj_rank_by_stolen(void *priv, static int i915_gem_stolen_list_info(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct drm_i915_gem_object *obj; u64 total_obj_size, total_gtt_size; LIST_HEAD(stolen); @@ -311,17 +272,6 @@ static int i915_gem_stolen_list_info(struct seq_file *m, void *data) return 0; } -#define count_objects(list, member) do { \ - list_for_each_entry(obj, list, member) { \ - size += i915_gem_obj_total_ggtt_size(obj); \ - ++count; \ - if (obj->map_and_fenceable) { \ - mappable_size += i915_gem_obj_ggtt_size(obj); \ - ++mappable_count; \ - } \ - } \ -} while (0) - struct file_stats { struct drm_i915_file_private *file_priv; unsigned long count; @@ -338,46 +288,29 @@ static int per_file_stats(int id, void *ptr, void *data) stats->count++; stats->total += obj->base.size; - + if (!obj->bind_count) + stats->unbound += obj->base.size; if (obj->base.name || obj->base.dma_buf) stats->shared += obj->base.size; - if (USES_FULL_PPGTT(obj->base.dev)) { - list_for_each_entry(vma, &obj->vma_list, obj_link) { - struct i915_hw_ppgtt *ppgtt; - - if (!drm_mm_node_allocated(&vma->node)) - continue; + list_for_each_entry(vma, &obj->vma_list, obj_link) { + if (!drm_mm_node_allocated(&vma->node)) + continue; - if (vma->is_ggtt) { - stats->global += obj->base.size; - continue; - } + if (i915_vma_is_ggtt(vma)) { + stats->global += vma->node.size; + } else { + struct i915_hw_ppgtt *ppgtt = i915_vm_to_ppgtt(vma->vm); - ppgtt = container_of(vma->vm, struct i915_hw_ppgtt, base); - if (ppgtt->file_priv != stats->file_priv) + if (ppgtt->base.file != stats->file_priv) continue; - - if (obj->active) /* XXX per-vma statistic */ - stats->active += obj->base.size; - else - stats->inactive += obj->base.size; - - return 0; - } - } else { - if (i915_gem_obj_ggtt_bound(obj)) { - stats->global += obj->base.size; - if (obj->active) - stats->active += obj->base.size; - else - stats->inactive += obj->base.size; - return 0; } - } - if (!list_empty(&obj->global_list)) - stats->unbound += obj->base.size; + if (i915_vma_is_active(vma)) + stats->active += vma->node.size; + else + stats->inactive += vma->node.size; + } return 0; } @@ -424,9 +357,9 @@ static int per_file_ctx_stats(int id, void *ptr, void *data) for (n = 0; n < ARRAY_SIZE(ctx->engine); n++) { if (ctx->engine[n].state) - per_file_stats(0, ctx->engine[n].state, data); - if (ctx->engine[n].ringbuf) - per_file_stats(0, ctx->engine[n].ringbuf->obj, data); + per_file_stats(0, ctx->engine[n].state->obj, data); + if (ctx->engine[n].ring) + per_file_stats(0, ctx->engine[n].ring->vma->obj, data); } return 0; @@ -435,48 +368,34 @@ static int per_file_ctx_stats(int id, void *ptr, void *data) static void print_context_stats(struct seq_file *m, struct drm_i915_private *dev_priv) { + struct drm_device *dev = &dev_priv->drm; struct file_stats stats; struct drm_file *file; memset(&stats, 0, sizeof(stats)); - mutex_lock(&dev_priv->drm.struct_mutex); + mutex_lock(&dev->struct_mutex); if (dev_priv->kernel_context) per_file_ctx_stats(0, dev_priv->kernel_context, &stats); - list_for_each_entry(file, &dev_priv->drm.filelist, lhead) { + list_for_each_entry(file, &dev->filelist, lhead) { struct drm_i915_file_private *fpriv = file->driver_priv; idr_for_each(&fpriv->context_idr, per_file_ctx_stats, &stats); } - mutex_unlock(&dev_priv->drm.struct_mutex); + mutex_unlock(&dev->struct_mutex); print_file_stats(m, "[k]contexts", stats); } -#define count_vmas(list, member) do { \ - list_for_each_entry(vma, list, member) { \ - size += i915_gem_obj_total_ggtt_size(vma->obj); \ - ++count; \ - if (vma->obj->map_and_fenceable) { \ - mappable_size += i915_gem_obj_ggtt_size(vma->obj); \ - ++mappable_count; \ - } \ - } \ -} while (0) - -static int i915_gem_object_info(struct seq_file *m, void* data) +static int i915_gem_object_info(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct i915_ggtt *ggtt = &dev_priv->ggtt; - u32 count, mappable_count, purgeable_count; - u64 size, mappable_size, purgeable_size; - unsigned long pin_mapped_count = 0, pin_mapped_purgeable_count = 0; - u64 pin_mapped_size = 0, pin_mapped_purgeable_size = 0; + u32 count, mapped_count, purgeable_count, dpy_count; + u64 size, mapped_size, purgeable_size, dpy_size; struct drm_i915_gem_object *obj; struct drm_file *file; - struct i915_vma *vma; int ret; ret = mutex_lock_interruptible(&dev->struct_mutex); @@ -487,70 +406,53 @@ static int i915_gem_object_info(struct seq_file *m, void* data) dev_priv->mm.object_count, dev_priv->mm.object_memory); - size = count = mappable_size = mappable_count = 0; - count_objects(&dev_priv->mm.bound_list, global_list); - seq_printf(m, "%u [%u] objects, %llu [%llu] bytes in gtt\n", - count, mappable_count, size, mappable_size); - - size = count = mappable_size = mappable_count = 0; - count_vmas(&ggtt->base.active_list, vm_link); - seq_printf(m, " %u [%u] active objects, %llu [%llu] bytes\n", - count, mappable_count, size, mappable_size); + size = count = 0; + mapped_size = mapped_count = 0; + purgeable_size = purgeable_count = 0; + list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list) { + size += obj->base.size; + ++count; - size = count = mappable_size = mappable_count = 0; - count_vmas(&ggtt->base.inactive_list, vm_link); - seq_printf(m, " %u [%u] inactive objects, %llu [%llu] bytes\n", - count, mappable_count, size, mappable_size); + if (obj->madv == I915_MADV_DONTNEED) { + purgeable_size += obj->base.size; + ++purgeable_count; + } - size = count = purgeable_size = purgeable_count = 0; - list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list) { - size += obj->base.size, ++count; - if (obj->madv == I915_MADV_DONTNEED) - purgeable_size += obj->base.size, ++purgeable_count; if (obj->mapping) { - pin_mapped_count++; - pin_mapped_size += obj->base.size; - if (obj->pages_pin_count == 0) { - pin_mapped_purgeable_count++; - pin_mapped_purgeable_size += obj->base.size; - } + mapped_count++; + mapped_size += obj->base.size; } } seq_printf(m, "%u unbound objects, %llu bytes\n", count, size); - size = count = mappable_size = mappable_count = 0; + size = count = dpy_size = dpy_count = 0; list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) { - if (obj->fault_mappable) { - size += i915_gem_obj_ggtt_size(obj); - ++count; - } + size += obj->base.size; + ++count; + if (obj->pin_display) { - mappable_size += i915_gem_obj_ggtt_size(obj); - ++mappable_count; + dpy_size += obj->base.size; + ++dpy_count; } + if (obj->madv == I915_MADV_DONTNEED) { purgeable_size += obj->base.size; ++purgeable_count; } + if (obj->mapping) { - pin_mapped_count++; - pin_mapped_size += obj->base.size; - if (obj->pages_pin_count == 0) { - pin_mapped_purgeable_count++; - pin_mapped_purgeable_size += obj->base.size; - } + mapped_count++; + mapped_size += obj->base.size; } } + seq_printf(m, "%u bound objects, %llu bytes\n", + count, size); seq_printf(m, "%u purgeable objects, %llu bytes\n", purgeable_count, purgeable_size); - seq_printf(m, "%u pinned mappable objects, %llu bytes\n", - mappable_count, mappable_size); - seq_printf(m, "%u fault mappable objects, %llu bytes\n", - count, size); - seq_printf(m, - "%lu [%lu] pin mapped objects, %llu [%llu] bytes [purgeable]\n", - pin_mapped_count, pin_mapped_purgeable_count, - pin_mapped_size, pin_mapped_purgeable_size); + seq_printf(m, "%u mapped objects, %llu bytes\n", + mapped_count, mapped_size); + seq_printf(m, "%u display objects (pinned), %llu bytes\n", + dpy_count, dpy_size); seq_printf(m, "%llu [%llu] gtt total\n", ggtt->base.total, ggtt->mappable_end - ggtt->base.start); @@ -563,6 +465,8 @@ static int i915_gem_object_info(struct seq_file *m, void* data) print_context_stats(m, dev_priv); list_for_each_entry_reverse(file, &dev->filelist, lhead) { struct file_stats stats; + struct drm_i915_file_private *file_priv = file->driver_priv; + struct drm_i915_gem_request *request; struct task_struct *task; memset(&stats, 0, sizeof(stats)); @@ -576,10 +480,17 @@ static int i915_gem_object_info(struct seq_file *m, void* data) * still alive (e.g. get_pid(current) => fork() => exit()). * Therefore, we need to protect this ->comm access using RCU. */ + mutex_lock(&dev->struct_mutex); + request = list_first_entry_or_null(&file_priv->mm.request_list, + struct drm_i915_gem_request, + client_list); rcu_read_lock(); - task = pid_task(file->pid, PIDTYPE_PID); + task = pid_task(request && request->ctx->pid ? + request->ctx->pid : file->pid, + PIDTYPE_PID); print_file_stats(m, task ? task->comm : "<unknown>", stats); rcu_read_unlock(); + mutex_unlock(&dev->struct_mutex); } mutex_unlock(&dev->filelist_mutex); @@ -589,9 +500,9 @@ static int i915_gem_object_info(struct seq_file *m, void* data) static int i915_gem_gtt_info(struct seq_file *m, void *data) { struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - uintptr_t list = (uintptr_t) node->info_ent->data; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(node); + struct drm_device *dev = &dev_priv->drm; + bool show_pin_display_only = !!node->info_ent->data; struct drm_i915_gem_object *obj; u64 total_obj_size, total_gtt_size; int count, ret; @@ -602,7 +513,7 @@ static int i915_gem_gtt_info(struct seq_file *m, void *data) total_obj_size = total_gtt_size = count = 0; list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) { - if (list == PINNED_LIST && !i915_gem_obj_is_pinned(obj)) + if (show_pin_display_only && !obj->pin_display) continue; seq_puts(m, " "); @@ -623,9 +534,8 @@ static int i915_gem_gtt_info(struct seq_file *m, void *data) static int i915_gem_pageflip_info(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct intel_crtc *crtc; int ret; @@ -672,7 +582,7 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data) intel_crtc_get_vblank_counter(crtc)); seq_printf(m, "%d prepares\n", atomic_read(&work->pending)); - if (INTEL_INFO(dev)->gen >= 4) + if (INTEL_GEN(dev_priv) >= 4) addr = I915_HI_DISPBASE(I915_READ(DSPSURF(crtc->plane))); else addr = I915_READ(DSPADDR(crtc->plane)); @@ -693,9 +603,8 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data) static int i915_gem_batch_pool_info(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct drm_i915_gem_object *obj; struct intel_engine_cs *engine; int total = 0; @@ -738,9 +647,8 @@ static int i915_gem_batch_pool_info(struct seq_file *m, void *data) static int i915_gem_request_info(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct intel_engine_cs *engine; struct drm_i915_gem_request *req; int ret, any; @@ -754,21 +662,20 @@ static int i915_gem_request_info(struct seq_file *m, void *data) int count; count = 0; - list_for_each_entry(req, &engine->request_list, list) + list_for_each_entry(req, &engine->request_list, link) count++; if (count == 0) continue; seq_printf(m, "%s requests: %d\n", engine->name, count); - list_for_each_entry(req, &engine->request_list, list) { + list_for_each_entry(req, &engine->request_list, link) { + struct pid *pid = req->ctx->pid; struct task_struct *task; rcu_read_lock(); - task = NULL; - if (req->pid) - task = pid_task(req->pid, PIDTYPE_PID); + task = pid ? pid_task(pid, PIDTYPE_PID) : NULL; seq_printf(m, " %x @ %d: %s [%d]\n", - req->seqno, + req->fence.seqno, (int) (jiffies - req->emitted_jiffies), task ? task->comm : "<unknown>", task ? task->pid : -1); @@ -793,8 +700,6 @@ static void i915_ring_seqno_info(struct seq_file *m, seq_printf(m, "Current sequence (%s): %x\n", engine->name, intel_engine_get_seqno(engine)); - seq_printf(m, "Current user interrupts (%s): %lx\n", - engine->name, READ_ONCE(engine->breadcrumbs.irq_wakeups)); spin_lock(&b->lock); for (rb = rb_first(&b->waiters); rb; rb = rb_next(rb)) { @@ -808,41 +713,25 @@ static void i915_ring_seqno_info(struct seq_file *m, static int i915_gem_seqno_info(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); struct intel_engine_cs *engine; - int ret; - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; - intel_runtime_pm_get(dev_priv); for_each_engine(engine, dev_priv) i915_ring_seqno_info(m, engine); - intel_runtime_pm_put(dev_priv); - mutex_unlock(&dev->struct_mutex); - return 0; } static int i915_interrupt_info(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); struct intel_engine_cs *engine; - int ret, i, pipe; + int i, pipe; - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; intel_runtime_pm_get(dev_priv); - if (IS_CHERRYVIEW(dev)) { + if (IS_CHERRYVIEW(dev_priv)) { seq_printf(m, "Master Interrupt Control:\t%08x\n", I915_READ(GEN8_MASTER_IRQ)); @@ -881,7 +770,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data) I915_READ(GEN8_PCU_IIR)); seq_printf(m, "PCU interrupt enable:\t%08x\n", I915_READ(GEN8_PCU_IER)); - } else if (INTEL_INFO(dev)->gen >= 8) { + } else if (INTEL_GEN(dev_priv) >= 8) { seq_printf(m, "Master Interrupt Control:\t%08x\n", I915_READ(GEN8_MASTER_IRQ)); @@ -937,7 +826,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data) I915_READ(GEN8_PCU_IIR)); seq_printf(m, "PCU interrupt enable:\t%08x\n", I915_READ(GEN8_PCU_IER)); - } else if (IS_VALLEYVIEW(dev)) { + } else if (IS_VALLEYVIEW(dev_priv)) { seq_printf(m, "Display IER:\t%08x\n", I915_READ(VLV_IER)); seq_printf(m, "Display IIR:\t%08x\n", @@ -975,7 +864,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data) seq_printf(m, "DPINVGTT:\t%08x\n", I915_READ(DPINVGTT)); - } else if (!HAS_PCH_SPLIT(dev)) { + } else if (!HAS_PCH_SPLIT(dev_priv)) { seq_printf(m, "Interrupt enable: %08x\n", I915_READ(IER)); seq_printf(m, "Interrupt identity: %08x\n", @@ -1007,7 +896,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data) I915_READ(GTIMR)); } for_each_engine(engine, dev_priv) { - if (INTEL_INFO(dev)->gen >= 6) { + if (INTEL_GEN(dev_priv) >= 6) { seq_printf(m, "Graphics Interrupt mask (%s): %08x\n", engine->name, I915_READ_IMR(engine)); @@ -1015,16 +904,14 @@ static int i915_interrupt_info(struct seq_file *m, void *data) i915_ring_seqno_info(m, engine); } intel_runtime_pm_put(dev_priv); - mutex_unlock(&dev->struct_mutex); return 0; } static int i915_gem_fence_regs_info(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; int i, ret; ret = mutex_lock_interruptible(&dev->struct_mutex); @@ -1033,14 +920,14 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data) seq_printf(m, "Total fences = %d\n", dev_priv->num_fence_regs); for (i = 0; i < dev_priv->num_fence_regs; i++) { - struct drm_i915_gem_object *obj = dev_priv->fence_regs[i].obj; + struct i915_vma *vma = dev_priv->fence_regs[i].vma; seq_printf(m, "Fence %d, pin count = %d, object = ", i, dev_priv->fence_regs[i].pin_count); - if (obj == NULL) + if (!vma) seq_puts(m, "unused"); else - describe_obj(m, obj); + describe_obj(m, vma->obj); seq_putc(m, '\n'); } @@ -1051,8 +938,7 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data) static int i915_hws_info(struct seq_file *m, void *data) { struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(node); struct intel_engine_cs *engine; const u32 *hws; int i; @@ -1077,33 +963,25 @@ i915_error_state_write(struct file *filp, loff_t *ppos) { struct i915_error_state_file_priv *error_priv = filp->private_data; - struct drm_device *dev = error_priv->dev; - int ret; DRM_DEBUG_DRIVER("Resetting error state\n"); - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; - - i915_destroy_error_state(dev); - mutex_unlock(&dev->struct_mutex); + i915_destroy_error_state(error_priv->dev); return cnt; } static int i915_error_state_open(struct inode *inode, struct file *file) { - struct drm_device *dev = inode->i_private; + struct drm_i915_private *dev_priv = inode->i_private; struct i915_error_state_file_priv *error_priv; error_priv = kzalloc(sizeof(*error_priv), GFP_KERNEL); if (!error_priv) return -ENOMEM; - error_priv->dev = dev; + error_priv->dev = &dev_priv->drm; - i915_error_state_get(dev, error_priv); + i915_error_state_get(&dev_priv->drm, error_priv); file->private_data = error_priv; @@ -1129,7 +1007,8 @@ static ssize_t i915_error_state_read(struct file *file, char __user *userbuf, ssize_t ret_count = 0; int ret; - ret = i915_error_state_buf_init(&error_str, to_i915(error_priv->dev), count, *pos); + ret = i915_error_state_buf_init(&error_str, + to_i915(error_priv->dev), count, *pos); if (ret) return ret; @@ -1162,16 +1041,15 @@ static const struct file_operations i915_error_state_fops = { static int i915_next_seqno_get(void *data, u64 *val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = data; int ret; - ret = mutex_lock_interruptible(&dev->struct_mutex); + ret = mutex_lock_interruptible(&dev_priv->drm.struct_mutex); if (ret) return ret; *val = dev_priv->next_seqno; - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&dev_priv->drm.struct_mutex); return 0; } @@ -1179,7 +1057,8 @@ i915_next_seqno_get(void *data, u64 *val) static int i915_next_seqno_set(void *data, u64 val) { - struct drm_device *dev = data; + struct drm_i915_private *dev_priv = data; + struct drm_device *dev = &dev_priv->drm; int ret; ret = mutex_lock_interruptible(&dev->struct_mutex); @@ -1198,16 +1077,13 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_next_seqno_fops, static int i915_frequency_info(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; int ret = 0; intel_runtime_pm_get(dev_priv); - flush_delayed_work(&dev_priv->rps.delayed_resume_work); - - if (IS_GEN5(dev)) { + if (IS_GEN5(dev_priv)) { u16 rgvswctl = I915_READ16(MEMSWCTL); u16 rgvstat = I915_READ16(MEMSTAT_ILK); @@ -1217,7 +1093,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused) MEMSTAT_VID_SHIFT); seq_printf(m, "Current P-state: %d\n", (rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT); - } else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) { + } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { u32 freq_sts; mutex_lock(&dev_priv->rps.hw_lock); @@ -1244,7 +1120,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused) "efficient (RPe) frequency: %d MHz\n", intel_gpu_freq(dev_priv, dev_priv->rps.efficient_freq)); mutex_unlock(&dev_priv->rps.hw_lock); - } else if (INTEL_INFO(dev)->gen >= 6) { + } else if (INTEL_GEN(dev_priv) >= 6) { u32 rp_state_limits; u32 gt_perf_status; u32 rp_state_cap; @@ -1256,7 +1132,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused) int max_freq; rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS); - if (IS_BROXTON(dev)) { + if (IS_BROXTON(dev_priv)) { rp_state_cap = I915_READ(BXT_RP_STATE_CAP); gt_perf_status = I915_READ(BXT_GT_PERF_STATUS); } else { @@ -1272,11 +1148,11 @@ static int i915_frequency_info(struct seq_file *m, void *unused) intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); reqf = I915_READ(GEN6_RPNSWREQ); - if (IS_GEN9(dev)) + if (IS_GEN9(dev_priv)) reqf >>= 23; else { reqf &= ~GEN6_TURBO_DISABLE; - if (IS_HASWELL(dev) || IS_BROADWELL(dev)) + if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) reqf >>= 24; else reqf >>= 25; @@ -1294,9 +1170,9 @@ static int i915_frequency_info(struct seq_file *m, void *unused) rpdownei = I915_READ(GEN6_RP_CUR_DOWN_EI) & GEN6_CURIAVG_MASK; rpcurdown = I915_READ(GEN6_RP_CUR_DOWN) & GEN6_CURBSYTAVG_MASK; rpprevdown = I915_READ(GEN6_RP_PREV_DOWN) & GEN6_CURBSYTAVG_MASK; - if (IS_GEN9(dev)) + if (IS_GEN9(dev_priv)) cagf = (rpstat & GEN9_CAGF_MASK) >> GEN9_CAGF_SHIFT; - else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) + else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) cagf = (rpstat & HSW_CAGF_MASK) >> HSW_CAGF_SHIFT; else cagf = (rpstat & GEN6_CAGF_MASK) >> GEN6_CAGF_SHIFT; @@ -1305,7 +1181,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused) intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); mutex_unlock(&dev->struct_mutex); - if (IS_GEN6(dev) || IS_GEN7(dev)) { + if (IS_GEN6(dev_priv) || IS_GEN7(dev_priv)) { pm_ier = I915_READ(GEN6_PMIER); pm_imr = I915_READ(GEN6_PMIMR); pm_isr = I915_READ(GEN6_PMISR); @@ -1323,7 +1199,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused) seq_printf(m, "pm_intr_keep: 0x%08x\n", dev_priv->rps.pm_intr_keep); seq_printf(m, "GT_PERF_STATUS: 0x%08x\n", gt_perf_status); seq_printf(m, "Render p-state ratio: %d\n", - (gt_perf_status & (IS_GEN9(dev) ? 0x1ff00 : 0xff00)) >> 8); + (gt_perf_status & (IS_GEN9(dev_priv) ? 0x1ff00 : 0xff00)) >> 8); seq_printf(m, "Render p-state VID: %d\n", gt_perf_status & 0xff); seq_printf(m, "Render p-state limit: %d\n", @@ -1352,22 +1228,22 @@ static int i915_frequency_info(struct seq_file *m, void *unused) seq_printf(m, "Down threshold: %d%%\n", dev_priv->rps.down_threshold); - max_freq = (IS_BROXTON(dev) ? rp_state_cap >> 0 : + max_freq = (IS_BROXTON(dev_priv) ? rp_state_cap >> 0 : rp_state_cap >> 16) & 0xff; - max_freq *= (IS_SKYLAKE(dev) || IS_KABYLAKE(dev) ? + max_freq *= (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) ? GEN9_FREQ_SCALER : 1); seq_printf(m, "Lowest (RPN) frequency: %dMHz\n", intel_gpu_freq(dev_priv, max_freq)); max_freq = (rp_state_cap & 0xff00) >> 8; - max_freq *= (IS_SKYLAKE(dev) || IS_KABYLAKE(dev) ? + max_freq *= (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) ? GEN9_FREQ_SCALER : 1); seq_printf(m, "Nominal (RP1) frequency: %dMHz\n", intel_gpu_freq(dev_priv, max_freq)); - max_freq = (IS_BROXTON(dev) ? rp_state_cap >> 16 : + max_freq = (IS_BROXTON(dev_priv) ? rp_state_cap >> 16 : rp_state_cap >> 0) & 0xff; - max_freq *= (IS_SKYLAKE(dev) || IS_KABYLAKE(dev) ? + max_freq *= (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) ? GEN9_FREQ_SCALER : 1); seq_printf(m, "Max non-overclocked (RP0) frequency: %dMHz\n", intel_gpu_freq(dev_priv, max_freq)); @@ -1381,6 +1257,8 @@ static int i915_frequency_info(struct seq_file *m, void *unused) intel_gpu_freq(dev_priv, dev_priv->rps.idle_freq)); seq_printf(m, "Min freq: %d MHz\n", intel_gpu_freq(dev_priv, dev_priv->rps.min_freq)); + seq_printf(m, "Boost freq: %d MHz\n", + intel_gpu_freq(dev_priv, dev_priv->rps.boost_freq)); seq_printf(m, "Max freq: %d MHz\n", intel_gpu_freq(dev_priv, dev_priv->rps.max_freq)); seq_printf(m, @@ -1401,9 +1279,7 @@ out: static int i915_hangcheck_info(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); struct intel_engine_cs *engine; u64 acthd[I915_NUM_ENGINES]; u32 seqno[I915_NUM_ENGINES]; @@ -1411,6 +1287,15 @@ static int i915_hangcheck_info(struct seq_file *m, void *unused) enum intel_engine_id id; int j; + if (test_bit(I915_WEDGED, &dev_priv->gpu_error.flags)) + seq_printf(m, "Wedged\n"); + if (test_bit(I915_RESET_IN_PROGRESS, &dev_priv->gpu_error.flags)) + seq_printf(m, "Reset in progress\n"); + if (waitqueue_active(&dev_priv->gpu_error.wait_queue)) + seq_printf(m, "Waiter holding struct mutex\n"); + if (waitqueue_active(&dev_priv->gpu_error.reset_queue)) + seq_printf(m, "struct_mutex blocked for reset\n"); + if (!i915.enable_hangcheck) { seq_printf(m, "Hangcheck disabled\n"); return 0; @@ -1419,7 +1304,7 @@ static int i915_hangcheck_info(struct seq_file *m, void *unused) intel_runtime_pm_get(dev_priv); for_each_engine_id(engine, dev_priv, id) { - acthd[id] = intel_ring_get_active_head(engine); + acthd[id] = intel_engine_get_active_head(engine); seqno[id] = intel_engine_get_seqno(engine); } @@ -1440,11 +1325,10 @@ static int i915_hangcheck_info(struct seq_file *m, void *unused) engine->hangcheck.seqno, seqno[id], engine->last_submitted_seqno); - seq_printf(m, "\twaiters? %d\n", - intel_engine_has_waiter(engine)); - seq_printf(m, "\tuser interrupts = %lx [current %lx]\n", - engine->hangcheck.user_interrupts, - READ_ONCE(engine->breadcrumbs.irq_wakeups)); + seq_printf(m, "\twaiters? %s, fake irq active? %s\n", + yesno(intel_engine_has_waiter(engine)), + yesno(test_bit(engine->id, + &dev_priv->gpu_error.missed_irq_rings))); seq_printf(m, "\tACTHD = 0x%08llx [current 0x%08llx]\n", (long long)engine->hangcheck.acthd, (long long)acthd[id]); @@ -1472,9 +1356,8 @@ static int i915_hangcheck_info(struct seq_file *m, void *unused) static int ironlake_drpc_info(struct seq_file *m) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; u32 rgvmodectl, rstdbyctl; u16 crstandvid; int ret; @@ -1540,9 +1423,7 @@ static int ironlake_drpc_info(struct seq_file *m) static int i915_forcewake_domains(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); struct intel_uncore_forcewake_domain *fw_domain; spin_lock_irq(&dev_priv->uncore.lock); @@ -1558,9 +1439,7 @@ static int i915_forcewake_domains(struct seq_file *m, void *data) static int vlv_drpc_info(struct seq_file *m) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); u32 rpmodectl1, rcctl1, pw_status; intel_runtime_pm_get(dev_priv); @@ -1598,10 +1477,10 @@ static int vlv_drpc_info(struct seq_file *m) static int gen6_drpc_info(struct seq_file *m) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; u32 rpmodectl1, gt_core_status, rcctl1, rc6vids = 0; + u32 gen9_powergate_enable = 0, gen9_powergate_status = 0; unsigned forcewake_count; int count = 0, ret; @@ -1629,6 +1508,10 @@ static int gen6_drpc_info(struct seq_file *m) rpmodectl1 = I915_READ(GEN6_RP_CONTROL); rcctl1 = I915_READ(GEN6_RC_CONTROL); + if (INTEL_GEN(dev_priv) >= 9) { + gen9_powergate_enable = I915_READ(GEN9_PG_ENABLE); + gen9_powergate_status = I915_READ(GEN9_PWRGT_DOMAIN_STATUS); + } mutex_unlock(&dev->struct_mutex); mutex_lock(&dev_priv->rps.hw_lock); sandybridge_pcode_read(dev_priv, GEN6_PCODE_READ_RC6VIDS, &rc6vids); @@ -1647,6 +1530,12 @@ static int gen6_drpc_info(struct seq_file *m) yesno(rcctl1 & GEN6_RC_CTL_RC1e_ENABLE)); seq_printf(m, "RC6 Enabled: %s\n", yesno(rcctl1 & GEN6_RC_CTL_RC6_ENABLE)); + if (INTEL_GEN(dev_priv) >= 9) { + seq_printf(m, "Render Well Gating Enabled: %s\n", + yesno(gen9_powergate_enable & GEN9_RENDER_PG_ENABLE)); + seq_printf(m, "Media Well Gating Enabled: %s\n", + yesno(gen9_powergate_enable & GEN9_MEDIA_PG_ENABLE)); + } seq_printf(m, "Deep RC6 Enabled: %s\n", yesno(rcctl1 & GEN6_RC_CTL_RC6p_ENABLE)); seq_printf(m, "Deepest RC6 Enabled: %s\n", @@ -1675,6 +1564,14 @@ static int gen6_drpc_info(struct seq_file *m) seq_printf(m, "Core Power Down: %s\n", yesno(gt_core_status & GEN6_CORE_CPD_STATE_MASK)); + if (INTEL_GEN(dev_priv) >= 9) { + seq_printf(m, "Render Power Well: %s\n", + (gen9_powergate_status & + GEN9_PWRGT_RENDER_STATUS_MASK) ? "Up" : "Down"); + seq_printf(m, "Media Power Well: %s\n", + (gen9_powergate_status & + GEN9_PWRGT_MEDIA_STATUS_MASK) ? "Up" : "Down"); + } /* Not exactly sure what this is */ seq_printf(m, "RC6 \"Locked to RPn\" residency since boot: %u\n", @@ -1692,17 +1589,16 @@ static int gen6_drpc_info(struct seq_file *m) GEN6_DECODE_RC6_VID(((rc6vids >> 8) & 0xff))); seq_printf(m, "RC6++ voltage: %dmV\n", GEN6_DECODE_RC6_VID(((rc6vids >> 16) & 0xff))); - return 0; + return i915_forcewake_domains(m, NULL); } static int i915_drpc_info(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = node_to_i915(m->private); - if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) return vlv_drpc_info(m); - else if (INTEL_INFO(dev)->gen >= 6) + else if (INTEL_GEN(dev_priv) >= 6) return gen6_drpc_info(m); else return ironlake_drpc_info(m); @@ -1710,9 +1606,7 @@ static int i915_drpc_info(struct seq_file *m, void *unused) static int i915_frontbuffer_tracking(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); seq_printf(m, "FB tracking busy bits: 0x%08x\n", dev_priv->fb_tracking.busy_bits); @@ -1725,11 +1619,9 @@ static int i915_frontbuffer_tracking(struct seq_file *m, void *unused) static int i915_fbc_status(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); - if (!HAS_FBC(dev)) { + if (!HAS_FBC(dev_priv)) { seq_puts(m, "FBC unsupported on this chipset\n"); return 0; } @@ -1743,7 +1635,7 @@ static int i915_fbc_status(struct seq_file *m, void *unused) seq_printf(m, "FBC disabled: %s\n", dev_priv->fbc.no_fbc_reason); - if (INTEL_INFO(dev_priv)->gen >= 7) + if (INTEL_GEN(dev_priv) >= 7) seq_printf(m, "Compressing: %s\n", yesno(I915_READ(FBC_STATUS2) & FBC_COMPRESSION_MASK)); @@ -1756,10 +1648,9 @@ static int i915_fbc_status(struct seq_file *m, void *unused) static int i915_fbc_fc_get(void *data, u64 *val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = data; - if (INTEL_INFO(dev)->gen < 7 || !HAS_FBC(dev)) + if (INTEL_GEN(dev_priv) < 7 || !HAS_FBC(dev_priv)) return -ENODEV; *val = dev_priv->fbc.false_color; @@ -1769,11 +1660,10 @@ static int i915_fbc_fc_get(void *data, u64 *val) static int i915_fbc_fc_set(void *data, u64 val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = data; u32 reg; - if (INTEL_INFO(dev)->gen < 7 || !HAS_FBC(dev)) + if (INTEL_GEN(dev_priv) < 7 || !HAS_FBC(dev_priv)) return -ENODEV; mutex_lock(&dev_priv->fbc.lock); @@ -1795,11 +1685,9 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_fbc_fc_fops, static int i915_ips_status(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); - if (!HAS_IPS(dev)) { + if (!HAS_IPS(dev_priv)) { seq_puts(m, "not supported\n"); return 0; } @@ -1809,7 +1697,7 @@ static int i915_ips_status(struct seq_file *m, void *unused) seq_printf(m, "Enabled by kernel parameter: %s\n", yesno(i915.enable_ips)); - if (INTEL_INFO(dev)->gen >= 8) { + if (INTEL_GEN(dev_priv) >= 8) { seq_puts(m, "Currently: unknown\n"); } else { if (I915_READ(IPS_CTL) & IPS_ENABLE) @@ -1825,23 +1713,21 @@ static int i915_ips_status(struct seq_file *m, void *unused) static int i915_sr_status(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); bool sr_enabled = false; intel_runtime_pm_get(dev_priv); - if (HAS_PCH_SPLIT(dev)) + if (HAS_PCH_SPLIT(dev_priv)) sr_enabled = I915_READ(WM1_LP_ILK) & WM1_LP_SR_EN; - else if (IS_CRESTLINE(dev) || IS_G4X(dev) || - IS_I945G(dev) || IS_I945GM(dev)) + else if (IS_CRESTLINE(dev_priv) || IS_G4X(dev_priv) || + IS_I945G(dev_priv) || IS_I945GM(dev_priv)) sr_enabled = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN; - else if (IS_I915GM(dev)) + else if (IS_I915GM(dev_priv)) sr_enabled = I915_READ(INSTPM) & INSTPM_SELF_EN; - else if (IS_PINEVIEW(dev)) + else if (IS_PINEVIEW(dev_priv)) sr_enabled = I915_READ(DSPFW3) & PINEVIEW_SELF_REFRESH_EN; - else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) + else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) sr_enabled = I915_READ(FW_BLC_SELF_VLV) & FW_CSPWRDWNEN; intel_runtime_pm_put(dev_priv); @@ -1854,13 +1740,12 @@ static int i915_sr_status(struct seq_file *m, void *unused) static int i915_emon_status(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; unsigned long temp, chipset, gfx; int ret; - if (!IS_GEN5(dev)) + if (!IS_GEN5(dev_priv)) return -ENODEV; ret = mutex_lock_interruptible(&dev->struct_mutex); @@ -1882,27 +1767,23 @@ static int i915_emon_status(struct seq_file *m, void *unused) static int i915_ring_freq_table(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); int ret = 0; int gpu_freq, ia_freq; unsigned int max_gpu_freq, min_gpu_freq; - if (!HAS_CORE_RING_FREQ(dev)) { + if (!HAS_LLC(dev_priv)) { seq_puts(m, "unsupported on this chipset\n"); return 0; } intel_runtime_pm_get(dev_priv); - flush_delayed_work(&dev_priv->rps.delayed_resume_work); - ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock); if (ret) goto out; - if (IS_SKYLAKE(dev) || IS_KABYLAKE(dev)) { + if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { /* Convert GT frequency to 50 HZ units */ min_gpu_freq = dev_priv->rps.min_freq_softlimit / GEN9_FREQ_SCALER; @@ -1922,7 +1803,7 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused) &ia_freq); seq_printf(m, "%d\t\t%d\t\t\t\t%d\n", intel_gpu_freq(dev_priv, (gpu_freq * - (IS_SKYLAKE(dev) || IS_KABYLAKE(dev) ? + (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) ? GEN9_FREQ_SCALER : 1))), ((ia_freq >> 0) & 0xff) * 100, ((ia_freq >> 8) & 0xff) * 100); @@ -1937,9 +1818,8 @@ out: static int i915_opregion(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct intel_opregion *opregion = &dev_priv->opregion; int ret; @@ -1958,10 +1838,7 @@ out: static int i915_vbt(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_opregion *opregion = &dev_priv->opregion; + struct intel_opregion *opregion = &node_to_i915(m->private)->opregion; if (opregion->vbt) seq_write(m, opregion->vbt, opregion->vbt_size); @@ -1971,8 +1848,8 @@ static int i915_vbt(struct seq_file *m, void *unused) static int i915_gem_framebuffer_info(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct intel_framebuffer *fbdev_fb = NULL; struct drm_framebuffer *drm_fb; int ret; @@ -1982,8 +1859,8 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) return ret; #ifdef CONFIG_DRM_FBDEV_EMULATION - if (to_i915(dev)->fbdev) { - fbdev_fb = to_intel_framebuffer(to_i915(dev)->fbdev->helper.fb); + if (dev_priv->fbdev) { + fbdev_fb = to_intel_framebuffer(dev_priv->fbdev->helper.fb); seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, modifier 0x%llx, refcount %d, obj ", fbdev_fb->base.width, @@ -2019,19 +1896,17 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) return 0; } -static void describe_ctx_ringbuf(struct seq_file *m, - struct intel_ringbuffer *ringbuf) +static void describe_ctx_ring(struct seq_file *m, struct intel_ring *ring) { seq_printf(m, " (ringbuffer, space: %d, head: %u, tail: %u, last head: %d)", - ringbuf->space, ringbuf->head, ringbuf->tail, - ringbuf->last_retired_head); + ring->space, ring->head, ring->tail, + ring->last_retired_head); } static int i915_context_status(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct intel_engine_cs *engine; struct i915_gem_context *ctx; int ret; @@ -2042,18 +1917,17 @@ static int i915_context_status(struct seq_file *m, void *unused) list_for_each_entry(ctx, &dev_priv->context_list, link) { seq_printf(m, "HW context %u ", ctx->hw_id); - if (IS_ERR(ctx->file_priv)) { - seq_puts(m, "(deleted) "); - } else if (ctx->file_priv) { - struct pid *pid = ctx->file_priv->file->pid; + if (ctx->pid) { struct task_struct *task; - task = get_pid_task(pid, PIDTYPE_PID); + task = get_pid_task(ctx->pid, PIDTYPE_PID); if (task) { seq_printf(m, "(%s [%d]) ", task->comm, task->pid); put_task_struct(task); } + } else if (IS_ERR(ctx->file_priv)) { + seq_puts(m, "(deleted) "); } else { seq_puts(m, "(kernel) "); } @@ -2067,9 +1941,9 @@ static int i915_context_status(struct seq_file *m, void *unused) seq_printf(m, "%s: ", engine->name); seq_putc(m, ce->initialised ? 'I' : 'i'); if (ce->state) - describe_obj(m, ce->state); - if (ce->ringbuf) - describe_ctx_ringbuf(m, ce->ringbuf); + describe_obj(m, ce->state->obj); + if (ce->ring) + describe_ctx_ring(m, ce->ring); seq_putc(m, '\n'); } @@ -2085,36 +1959,34 @@ static void i915_dump_lrc_obj(struct seq_file *m, struct i915_gem_context *ctx, struct intel_engine_cs *engine) { - struct drm_i915_gem_object *ctx_obj = ctx->engine[engine->id].state; + struct i915_vma *vma = ctx->engine[engine->id].state; struct page *page; - uint32_t *reg_state; int j; - unsigned long ggtt_offset = 0; seq_printf(m, "CONTEXT: %s %u\n", engine->name, ctx->hw_id); - if (ctx_obj == NULL) { - seq_puts(m, "\tNot allocated\n"); + if (!vma) { + seq_puts(m, "\tFake context\n"); return; } - if (!i915_gem_obj_ggtt_bound(ctx_obj)) - seq_puts(m, "\tNot bound in GGTT\n"); - else - ggtt_offset = i915_gem_obj_ggtt_offset(ctx_obj); + if (vma->flags & I915_VMA_GLOBAL_BIND) + seq_printf(m, "\tBound in GGTT at 0x%08x\n", + i915_ggtt_offset(vma)); - if (i915_gem_object_get_pages(ctx_obj)) { - seq_puts(m, "\tFailed to get pages for context object\n"); + if (i915_gem_object_get_pages(vma->obj)) { + seq_puts(m, "\tFailed to get pages for context object\n\n"); return; } - page = i915_gem_object_get_page(ctx_obj, LRC_STATE_PN); - if (!WARN_ON(page == NULL)) { - reg_state = kmap_atomic(page); + page = i915_gem_object_get_page(vma->obj, LRC_STATE_PN); + if (page) { + u32 *reg_state = kmap_atomic(page); for (j = 0; j < 0x600 / sizeof(u32) / 4; j += 4) { - seq_printf(m, "\t[0x%08lx] 0x%08x 0x%08x 0x%08x 0x%08x\n", - ggtt_offset + 4096 + (j * 4), + seq_printf(m, + "\t[0x%04x] 0x%08x 0x%08x 0x%08x 0x%08x\n", + j * 4, reg_state[j], reg_state[j + 1], reg_state[j + 2], reg_state[j + 3]); } @@ -2126,9 +1998,8 @@ static void i915_dump_lrc_obj(struct seq_file *m, static int i915_dump_lrc(struct seq_file *m, void *unused) { - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct intel_engine_cs *engine; struct i915_gem_context *ctx; int ret; @@ -2153,9 +2024,8 @@ static int i915_dump_lrc(struct seq_file *m, void *unused) static int i915_execlists(struct seq_file *m, void *data) { - struct drm_info_node *node = (struct drm_info_node *)m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct intel_engine_cs *engine; u32 status_pointer; u8 read_pointer; @@ -2190,7 +2060,7 @@ static int i915_execlists(struct seq_file *m, void *data) status_pointer = I915_READ(RING_CONTEXT_STATUS_PTR(engine)); seq_printf(m, "\tStatus pointer: 0x%08X\n", status_pointer); - read_pointer = engine->next_context_status_buffer; + read_pointer = GEN8_CSB_READ_PTR(status_pointer); write_pointer = GEN8_CSB_WRITE_PTR(status_pointer); if (read_pointer > write_pointer) write_pointer += GEN8_CSB_ENTRIES; @@ -2256,9 +2126,8 @@ static const char *swizzle_string(unsigned swizzle) static int i915_swizzle_info(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; int ret; ret = mutex_lock_interruptible(&dev->struct_mutex); @@ -2271,7 +2140,7 @@ static int i915_swizzle_info(struct seq_file *m, void *data) seq_printf(m, "bit6 swizzle for Y-tiling = %s\n", swizzle_string(dev_priv->mm.bit_6_swizzle_y)); - if (IS_GEN3(dev) || IS_GEN4(dev)) { + if (IS_GEN3(dev_priv) || IS_GEN4(dev_priv)) { seq_printf(m, "DDC = 0x%08x\n", I915_READ(DCC)); seq_printf(m, "DDC2 = 0x%08x\n", @@ -2280,7 +2149,7 @@ static int i915_swizzle_info(struct seq_file *m, void *data) I915_READ16(C0DRB3)); seq_printf(m, "C1DRB3 = 0x%04x\n", I915_READ16(C1DRB3)); - } else if (INTEL_INFO(dev)->gen >= 6) { + } else if (INTEL_GEN(dev_priv) >= 6) { seq_printf(m, "MAD_DIMM_C0 = 0x%08x\n", I915_READ(MAD_DIMM_C0)); seq_printf(m, "MAD_DIMM_C1 = 0x%08x\n", @@ -2289,7 +2158,7 @@ static int i915_swizzle_info(struct seq_file *m, void *data) I915_READ(MAD_DIMM_C2)); seq_printf(m, "TILECTL = 0x%08x\n", I915_READ(TILECTL)); - if (INTEL_INFO(dev)->gen >= 8) + if (INTEL_GEN(dev_priv) >= 8) seq_printf(m, "GAMTARBMODE = 0x%08x\n", I915_READ(GAMTARBMODE)); else @@ -2329,9 +2198,9 @@ static int per_file_ctx(int id, void *ptr, void *data) return 0; } -static void gen8_ppgtt_info(struct seq_file *m, struct drm_device *dev) +static void gen8_ppgtt_info(struct seq_file *m, + struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_engine_cs *engine; struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; int i; @@ -2350,9 +2219,9 @@ static void gen8_ppgtt_info(struct seq_file *m, struct drm_device *dev) } } -static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev) +static void gen6_ppgtt_info(struct seq_file *m, + struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_engine_cs *engine; if (IS_GEN6(dev_priv)) @@ -2384,22 +2253,23 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev) static int i915_ppgtt_info(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct drm_file *file; + int ret; - int ret = mutex_lock_interruptible(&dev->struct_mutex); + mutex_lock(&dev->filelist_mutex); + ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) - return ret; + goto out_unlock; + intel_runtime_pm_get(dev_priv); - if (INTEL_INFO(dev)->gen >= 8) - gen8_ppgtt_info(m, dev); - else if (INTEL_INFO(dev)->gen >= 6) - gen6_ppgtt_info(m, dev); + if (INTEL_GEN(dev_priv) >= 8) + gen8_ppgtt_info(m, dev_priv); + else if (INTEL_GEN(dev_priv) >= 6) + gen6_ppgtt_info(m, dev_priv); - mutex_lock(&dev->filelist_mutex); list_for_each_entry_reverse(file, &dev->filelist, lhead) { struct drm_i915_file_private *file_priv = file->driver_priv; struct task_struct *task; @@ -2407,19 +2277,19 @@ static int i915_ppgtt_info(struct seq_file *m, void *data) task = get_pid_task(file->pid, PIDTYPE_PID); if (!task) { ret = -ESRCH; - goto out_unlock; + goto out_rpm; } seq_printf(m, "\nproc: %s\n", task->comm); put_task_struct(task); idr_for_each(&file_priv->context_idr, per_file_ctx, (void *)(unsigned long)m); } -out_unlock: - mutex_unlock(&dev->filelist_mutex); +out_rpm: intel_runtime_pm_put(dev_priv); mutex_unlock(&dev->struct_mutex); - +out_unlock: + mutex_unlock(&dev->filelist_mutex); return ret; } @@ -2434,23 +2304,41 @@ static int count_irq_waiters(struct drm_i915_private *i915) return count; } +static const char *rps_power_to_str(unsigned int power) +{ + static const char * const strings[] = { + [LOW_POWER] = "low power", + [BETWEEN] = "mixed", + [HIGH_POWER] = "high power", + }; + + if (power >= ARRAY_SIZE(strings) || !strings[power]) + return "unknown"; + + return strings[power]; +} + static int i915_rps_boost_info(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct drm_file *file; seq_printf(m, "RPS enabled? %d\n", dev_priv->rps.enabled); seq_printf(m, "GPU busy? %s [%x]\n", yesno(dev_priv->gt.awake), dev_priv->gt.active_engines); seq_printf(m, "CPU waiting? %d\n", count_irq_waiters(dev_priv)); - seq_printf(m, "Frequency requested %d; min hard:%d, soft:%d; max soft:%d, hard:%d\n", - intel_gpu_freq(dev_priv, dev_priv->rps.cur_freq), + seq_printf(m, "Frequency requested %d\n", + intel_gpu_freq(dev_priv, dev_priv->rps.cur_freq)); + seq_printf(m, " min hard:%d, soft:%d; max soft:%d, hard:%d\n", intel_gpu_freq(dev_priv, dev_priv->rps.min_freq), intel_gpu_freq(dev_priv, dev_priv->rps.min_freq_softlimit), intel_gpu_freq(dev_priv, dev_priv->rps.max_freq_softlimit), intel_gpu_freq(dev_priv, dev_priv->rps.max_freq)); + seq_printf(m, " idle:%d, efficient:%d, boost:%d\n", + intel_gpu_freq(dev_priv, dev_priv->rps.idle_freq), + intel_gpu_freq(dev_priv, dev_priv->rps.efficient_freq), + intel_gpu_freq(dev_priv, dev_priv->rps.boost_freq)); mutex_lock(&dev->filelist_mutex); spin_lock(&dev_priv->rps.client_lock); @@ -2467,27 +2355,44 @@ static int i915_rps_boost_info(struct seq_file *m, void *data) list_empty(&file_priv->rps.link) ? "" : ", active"); rcu_read_unlock(); } - seq_printf(m, "Semaphore boosts: %d%s\n", - dev_priv->rps.semaphores.boosts, - list_empty(&dev_priv->rps.semaphores.link) ? "" : ", active"); - seq_printf(m, "MMIO flip boosts: %d%s\n", - dev_priv->rps.mmioflips.boosts, - list_empty(&dev_priv->rps.mmioflips.link) ? "" : ", active"); - seq_printf(m, "Kernel boosts: %d\n", dev_priv->rps.boosts); + seq_printf(m, "Kernel (anonymous) boosts: %d\n", dev_priv->rps.boosts); spin_unlock(&dev_priv->rps.client_lock); mutex_unlock(&dev->filelist_mutex); + if (INTEL_GEN(dev_priv) >= 6 && + dev_priv->rps.enabled && + dev_priv->gt.active_engines) { + u32 rpup, rpupei; + u32 rpdown, rpdownei; + + intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); + rpup = I915_READ_FW(GEN6_RP_CUR_UP) & GEN6_RP_EI_MASK; + rpupei = I915_READ_FW(GEN6_RP_CUR_UP_EI) & GEN6_RP_EI_MASK; + rpdown = I915_READ_FW(GEN6_RP_CUR_DOWN) & GEN6_RP_EI_MASK; + rpdownei = I915_READ_FW(GEN6_RP_CUR_DOWN_EI) & GEN6_RP_EI_MASK; + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); + + seq_printf(m, "\nRPS Autotuning (current \"%s\" window):\n", + rps_power_to_str(dev_priv->rps.power)); + seq_printf(m, " Avg. up: %d%% [above threshold? %d%%]\n", + 100 * rpup / rpupei, + dev_priv->rps.up_threshold); + seq_printf(m, " Avg. down: %d%% [below threshold? %d%%]\n", + 100 * rpdown / rpdownei, + dev_priv->rps.down_threshold); + } else { + seq_puts(m, "\nRPS Autotuning inactive\n"); + } + return 0; } static int i915_llc(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); const bool edram = INTEL_GEN(dev_priv) > 8; - seq_printf(m, "LLC: %s\n", yesno(HAS_LLC(dev))); + seq_printf(m, "LLC: %s\n", yesno(HAS_LLC(dev_priv))); seq_printf(m, "%s: %lluMB\n", edram ? "eDRAM" : "eLLC", intel_uncore_edram_size(dev_priv)/1024/1024); @@ -2496,8 +2401,7 @@ static int i915_llc(struct seq_file *m, void *data) static int i915_guc_load_status_info(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_i915_private *dev_priv = to_i915(node->minor->dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; u32 tmp, i; @@ -2543,6 +2447,7 @@ static void i915_guc_client_info(struct seq_file *m, struct i915_guc_client *client) { struct intel_engine_cs *engine; + enum intel_engine_id id; uint64_t tot = 0; seq_printf(m, "\tPriority %d, GuC ctx index: %u, PD offset 0x%x\n", @@ -2553,27 +2458,26 @@ static void i915_guc_client_info(struct seq_file *m, client->wq_size, client->wq_offset, client->wq_tail); seq_printf(m, "\tWork queue full: %u\n", client->no_wq_space); - seq_printf(m, "\tFailed to queue: %u\n", client->q_fail); seq_printf(m, "\tFailed doorbell: %u\n", client->b_fail); seq_printf(m, "\tLast submission result: %d\n", client->retcode); - for_each_engine(engine, dev_priv) { + for_each_engine_id(engine, dev_priv, id) { + u64 submissions = client->submissions[id]; + tot += submissions; seq_printf(m, "\tSubmissions: %llu %s\n", - client->submissions[engine->id], - engine->name); - tot += client->submissions[engine->id]; + submissions, engine->name); } seq_printf(m, "\tTotal: %llu\n", tot); } static int i915_guc_info(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct intel_guc guc; struct i915_guc_client client = {}; struct intel_engine_cs *engine; + enum intel_engine_id id; u64 total = 0; if (!HAS_GUC_SCHED(dev_priv)) @@ -2600,11 +2504,11 @@ static int i915_guc_info(struct seq_file *m, void *data) seq_printf(m, "GuC last action error code: %d\n", guc.action_err); seq_printf(m, "\nGuC submissions:\n"); - for_each_engine(engine, dev_priv) { + for_each_engine_id(engine, dev_priv, id) { + u64 submissions = guc.submissions[id]; + total += submissions; seq_printf(m, "\t%-24s: %10llu, last seqno 0x%08x\n", - engine->name, guc.submissions[engine->id], - guc.last_seqno[engine->id]); - total += guc.submissions[engine->id]; + engine->name, submissions, guc.last_seqno[id]); } seq_printf(m, "\t%s: %llu\n", "Total", total); @@ -2618,18 +2522,16 @@ static int i915_guc_info(struct seq_file *m, void *data) static int i915_guc_log_dump(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct drm_i915_gem_object *log_obj = dev_priv->guc.log_obj; - u32 *log; + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_i915_gem_object *obj; int i = 0, pg; - if (!log_obj) + if (!dev_priv->guc.log_vma) return 0; - for (pg = 0; pg < log_obj->base.size / PAGE_SIZE; pg++) { - log = kmap_atomic(i915_gem_object_get_page(log_obj, pg)); + obj = dev_priv->guc.log_vma->obj; + for (pg = 0; pg < obj->base.size / PAGE_SIZE; pg++) { + u32 *log = kmap_atomic(i915_gem_object_get_page(obj, pg)); for (i = 0; i < PAGE_SIZE / sizeof(u32); i += 4) seq_printf(m, "0x%08x 0x%08x 0x%08x 0x%08x\n", @@ -2646,15 +2548,13 @@ static int i915_guc_log_dump(struct seq_file *m, void *data) static int i915_edp_psr_status(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); u32 psrperf = 0; u32 stat[3]; enum pipe pipe; bool enabled = false; - if (!HAS_PSR(dev)) { + if (!HAS_PSR(dev_priv)) { seq_puts(m, "PSR not supported\n"); return 0; } @@ -2671,7 +2571,7 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) seq_printf(m, "Re-enable work scheduled: %s\n", yesno(work_busy(&dev_priv->psr.work.work))); - if (HAS_DDI(dev)) + if (HAS_DDI(dev_priv)) enabled = I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE; else { for_each_pipe(dev_priv, pipe) { @@ -2688,7 +2588,7 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) seq_printf(m, "HW Enabled & Active bit: %s", yesno(enabled)); - if (!HAS_DDI(dev)) + if (!HAS_DDI(dev_priv)) for_each_pipe(dev_priv, pipe) { if ((stat[pipe] == VLV_EDP_PSR_ACTIVE_NORFB_UP) || (stat[pipe] == VLV_EDP_PSR_ACTIVE_SF_UPDATE)) @@ -2700,7 +2600,7 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) * VLV/CHV PSR has no kind of performance counter * SKL+ Perf counter is reset to 0 everytime DC state is entered */ - if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { + if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { psrperf = I915_READ(EDP_PSR_PERF_CNT) & EDP_PSR_PERF_CNT_MASK; @@ -2714,8 +2614,8 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) static int i915_sink_crc(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct intel_connector *connector; struct intel_dp *intel_dp = NULL; int ret; @@ -2754,13 +2654,11 @@ out: static int i915_energy_uJ(struct seq_file *m, void *data) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); u64 power; u32 units; - if (INTEL_INFO(dev)->gen < 6) + if (INTEL_GEN(dev_priv) < 6) return -ENODEV; intel_runtime_pm_get(dev_priv); @@ -2780,9 +2678,8 @@ static int i915_energy_uJ(struct seq_file *m, void *data) static int i915_runtime_pm_status(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct pci_dev *pdev = dev_priv->drm.pdev; if (!HAS_RUNTIME_PM(dev_priv)) seq_puts(m, "Runtime power management not supported\n"); @@ -2792,22 +2689,20 @@ static int i915_runtime_pm_status(struct seq_file *m, void *unused) yesno(!intel_irqs_enabled(dev_priv))); #ifdef CONFIG_PM seq_printf(m, "Usage count: %d\n", - atomic_read(&dev->dev->power.usage_count)); + atomic_read(&dev_priv->drm.dev->power.usage_count)); #else seq_printf(m, "Device Power Management (CONFIG_PM) disabled\n"); #endif seq_printf(m, "PCI device power state: %s [%d]\n", - pci_power_name(dev_priv->drm.pdev->current_state), - dev_priv->drm.pdev->current_state); + pci_power_name(pdev->current_state), + pdev->current_state); return 0; } static int i915_power_domain_info(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); struct i915_power_domains *power_domains = &dev_priv->power_domains; int i; @@ -2840,12 +2735,10 @@ static int i915_power_domain_info(struct seq_file *m, void *unused) static int i915_dmc_info(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); struct intel_csr *csr; - if (!HAS_CSR(dev)) { + if (!HAS_CSR(dev_priv)) { seq_puts(m, "not supported\n"); return 0; } @@ -2863,12 +2756,12 @@ static int i915_dmc_info(struct seq_file *m, void *unused) seq_printf(m, "version: %d.%d\n", CSR_VERSION_MAJOR(csr->version), CSR_VERSION_MINOR(csr->version)); - if (IS_SKYLAKE(dev) && csr->version >= CSR_VERSION(1, 6)) { + if (IS_SKYLAKE(dev_priv) && csr->version >= CSR_VERSION(1, 6)) { seq_printf(m, "DC3 -> DC5 count: %d\n", I915_READ(SKL_CSR_DC3_DC5_COUNT)); seq_printf(m, "DC5 -> DC6 count: %d\n", I915_READ(SKL_CSR_DC5_DC6_COUNT)); - } else if (IS_BROXTON(dev) && csr->version >= CSR_VERSION(1, 4)) { + } else if (IS_BROXTON(dev_priv) && csr->version >= CSR_VERSION(1, 4)) { seq_printf(m, "DC3 -> DC5 count: %d\n", I915_READ(BXT_CSR_DC3_DC5_COUNT)); } @@ -2905,8 +2798,8 @@ static void intel_encoder_info(struct seq_file *m, struct intel_crtc *intel_crtc, struct intel_encoder *intel_encoder) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct drm_crtc *crtc = &intel_crtc->base; struct intel_connector *intel_connector; struct drm_encoder *encoder; @@ -2932,8 +2825,8 @@ static void intel_encoder_info(struct seq_file *m, static void intel_crtc_info(struct seq_file *m, struct intel_crtc *intel_crtc) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct drm_crtc *crtc = &intel_crtc->base; struct intel_encoder *intel_encoder; struct drm_plane_state *plane_state = crtc->primary->state; @@ -2967,6 +2860,9 @@ static void intel_dp_info(struct seq_file *m, seq_printf(m, "\taudio support: %s\n", yesno(intel_dp->has_audio)); if (intel_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP) intel_panel_info(m, &intel_connector->panel); + + drm_dp_downstream_debug(m, intel_dp->dpcd, intel_dp->downstream_ports, + &intel_dp->aux); } static void intel_hdmi_info(struct seq_file *m, @@ -3031,12 +2927,11 @@ static void intel_connector_info(struct seq_file *m, intel_seq_print_mode(m, 2, mode); } -static bool cursor_active(struct drm_device *dev, int pipe) +static bool cursor_active(struct drm_i915_private *dev_priv, int pipe) { - struct drm_i915_private *dev_priv = to_i915(dev); u32 state; - if (IS_845G(dev) || IS_I865G(dev)) + if (IS_845G(dev_priv) || IS_I865G(dev_priv)) state = I915_READ(CURCNTR(PIPE_A)) & CURSOR_ENABLE; else state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE; @@ -3044,9 +2939,9 @@ static bool cursor_active(struct drm_device *dev, int pipe) return state; } -static bool cursor_position(struct drm_device *dev, int pipe, int *x, int *y) +static bool cursor_position(struct drm_i915_private *dev_priv, + int pipe, int *x, int *y) { - struct drm_i915_private *dev_priv = to_i915(dev); u32 pos; pos = I915_READ(CURPOS(pipe)); @@ -3059,7 +2954,7 @@ static bool cursor_position(struct drm_device *dev, int pipe, int *x, int *y) if (pos & (CURSOR_POS_SIGN << CURSOR_Y_SHIFT)) *y = -*y; - return cursor_active(dev, pipe); + return cursor_active(dev_priv, pipe); } static const char *plane_type(enum drm_plane_type type) @@ -3089,12 +2984,12 @@ static const char *plane_rotation(unsigned int rotation) */ snprintf(buf, sizeof(buf), "%s%s%s%s%s%s(0x%08x)", - (rotation & BIT(DRM_ROTATE_0)) ? "0 " : "", - (rotation & BIT(DRM_ROTATE_90)) ? "90 " : "", - (rotation & BIT(DRM_ROTATE_180)) ? "180 " : "", - (rotation & BIT(DRM_ROTATE_270)) ? "270 " : "", - (rotation & BIT(DRM_REFLECT_X)) ? "FLIPX " : "", - (rotation & BIT(DRM_REFLECT_Y)) ? "FLIPY " : "", + (rotation & DRM_ROTATE_0) ? "0 " : "", + (rotation & DRM_ROTATE_90) ? "90 " : "", + (rotation & DRM_ROTATE_180) ? "180 " : "", + (rotation & DRM_ROTATE_270) ? "270 " : "", + (rotation & DRM_REFLECT_X) ? "FLIPX " : "", + (rotation & DRM_REFLECT_Y) ? "FLIPY " : "", rotation); return buf; @@ -3102,13 +2997,14 @@ static const char *plane_rotation(unsigned int rotation) static void intel_plane_info(struct seq_file *m, struct intel_crtc *intel_crtc) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct intel_plane *intel_plane; for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { struct drm_plane_state *state; struct drm_plane *plane = &intel_plane->base; + char *format_name; if (!plane->state) { seq_puts(m, "plane->state is NULL!\n"); @@ -3117,6 +3013,12 @@ static void intel_plane_info(struct seq_file *m, struct intel_crtc *intel_crtc) state = plane->state; + if (state->fb) { + format_name = drm_get_format_name(state->fb->pixel_format); + } else { + format_name = kstrdup("N/A", GFP_KERNEL); + } + seq_printf(m, "\t--Plane id %d: type=%s, crtc_pos=%4dx%4d, crtc_size=%4dx%4d, src_pos=%d.%04ux%d.%04u, src_size=%d.%04ux%d.%04u, format=%s, rotation=%s\n", plane->base.id, plane_type(intel_plane->base.type), @@ -3130,8 +3032,10 @@ static void intel_plane_info(struct seq_file *m, struct intel_crtc *intel_crtc) ((state->src_w & 0xffff) * 15625) >> 10, (state->src_h >> 16), ((state->src_h & 0xffff) * 15625) >> 10, - state->fb ? drm_get_format_name(state->fb->pixel_format) : "N/A", + format_name, plane_rotation(state->rotation)); + + kfree(format_name); } } @@ -3165,9 +3069,8 @@ static void intel_scaler_info(struct seq_file *m, struct intel_crtc *intel_crtc) static int i915_display_info(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct intel_crtc *crtc; struct drm_connector *connector; @@ -3191,7 +3094,7 @@ static int i915_display_info(struct seq_file *m, void *unused) if (pipe_config->base.active) { intel_crtc_info(m, crtc); - active = cursor_position(dev, crtc->pipe, &x, &y); + active = cursor_position(dev_priv, crtc->pipe, &x, &y); seq_printf(m, "\tcursor visible? %s, position (%d, %d), size %dx%d, addr 0x%08x, active? %s\n", yesno(crtc->cursor_base), x, y, crtc->base.cursor->state->crtc_w, @@ -3220,15 +3123,14 @@ static int i915_display_info(struct seq_file *m, void *unused) static int i915_semaphore_status(struct seq_file *m, void *unused) { - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct intel_engine_cs *engine; - int num_rings = hweight32(INTEL_INFO(dev)->ring_mask); + int num_rings = INTEL_INFO(dev_priv)->num_rings; enum intel_engine_id id; int j, ret; - if (!i915_semaphore_is_enabled(dev_priv)) { + if (!i915.semaphores) { seq_puts(m, "Semaphores are disabled\n"); return 0; } @@ -3238,11 +3140,11 @@ static int i915_semaphore_status(struct seq_file *m, void *unused) return ret; intel_runtime_pm_get(dev_priv); - if (IS_BROADWELL(dev)) { + if (IS_BROADWELL(dev_priv)) { struct page *page; uint64_t *seqno; - page = i915_gem_object_get_page(dev_priv->semaphore_obj, 0); + page = i915_gem_object_get_page(dev_priv->semaphore->obj, 0); seqno = (uint64_t *)kmap_atomic(page); for_each_engine_id(engine, dev_priv, id) { @@ -3293,9 +3195,8 @@ static int i915_semaphore_status(struct seq_file *m, void *unused) static int i915_shared_dplls_info(struct seq_file *m, void *unused) { - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; int i; drm_modeset_lock_all(dev); @@ -3323,9 +3224,8 @@ static int i915_wa_registers(struct seq_file *m, void *unused) int i; int ret; struct intel_engine_cs *engine; - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct i915_workarounds *workarounds = &dev_priv->workarounds; enum intel_engine_id id; @@ -3361,15 +3261,14 @@ static int i915_wa_registers(struct seq_file *m, void *unused) static int i915_ddb_info(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct skl_ddb_allocation *ddb; struct skl_ddb_entry *entry; enum pipe pipe; int plane; - if (INTEL_INFO(dev)->gen < 9) + if (INTEL_GEN(dev_priv) < 9) return 0; drm_modeset_lock_all(dev); @@ -3399,7 +3298,8 @@ static int i915_ddb_info(struct seq_file *m, void *unused) } static void drrs_status_per_crtc(struct seq_file *m, - struct drm_device *dev, struct intel_crtc *intel_crtc) + struct drm_device *dev, + struct intel_crtc *intel_crtc) { struct drm_i915_private *dev_priv = to_i915(dev); struct i915_drrs *drrs = &dev_priv->drrs; @@ -3468,8 +3368,8 @@ static void drrs_status_per_crtc(struct seq_file *m, static int i915_drrs_status(struct seq_file *m, void *unused) { - struct drm_info_node *node = m->private; - struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct intel_crtc *intel_crtc; int active_crtc_cnt = 0; @@ -3492,14 +3392,14 @@ static int i915_drrs_status(struct seq_file *m, void *unused) struct pipe_crc_info { const char *name; - struct drm_device *dev; + struct drm_i915_private *dev_priv; enum pipe pipe; }; static int i915_dp_mst_info(struct seq_file *m, void *unused) { - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct drm_device *dev = &dev_priv->drm; struct intel_encoder *intel_encoder; struct intel_digital_port *intel_dig_port; struct drm_connector *connector; @@ -3528,10 +3428,10 @@ static int i915_dp_mst_info(struct seq_file *m, void *unused) static int i915_pipe_crc_open(struct inode *inode, struct file *filep) { struct pipe_crc_info *info = inode->i_private; - struct drm_i915_private *dev_priv = to_i915(info->dev); + struct drm_i915_private *dev_priv = info->dev_priv; struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe]; - if (info->pipe >= INTEL_INFO(info->dev)->num_pipes) + if (info->pipe >= INTEL_INFO(dev_priv)->num_pipes) return -ENODEV; spin_lock_irq(&pipe_crc->lock); @@ -3552,7 +3452,7 @@ static int i915_pipe_crc_open(struct inode *inode, struct file *filep) static int i915_pipe_crc_release(struct inode *inode, struct file *filep) { struct pipe_crc_info *info = inode->i_private; - struct drm_i915_private *dev_priv = to_i915(info->dev); + struct drm_i915_private *dev_priv = info->dev_priv; struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe]; spin_lock_irq(&pipe_crc->lock); @@ -3579,8 +3479,7 @@ i915_pipe_crc_read(struct file *filep, char __user *user_buf, size_t count, loff_t *pos) { struct pipe_crc_info *info = filep->private_data; - struct drm_device *dev = info->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = info->dev_priv; struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe]; char buf[PIPE_CRC_BUFFER_LEN]; int n_entries; @@ -3621,7 +3520,6 @@ i915_pipe_crc_read(struct file *filep, char __user *user_buf, size_t count, while (n_entries > 0) { struct intel_pipe_crc_entry *entry = &pipe_crc->entries[pipe_crc->tail]; - int ret; if (CIRC_CNT(pipe_crc->head, pipe_crc->tail, INTEL_PIPE_CRC_ENTRIES_NR) < 1) @@ -3638,8 +3536,7 @@ i915_pipe_crc_read(struct file *filep, char __user *user_buf, size_t count, spin_unlock_irq(&pipe_crc->lock); - ret = copy_to_user(user_buf, buf, PIPE_CRC_LINE_LEN); - if (ret == PIPE_CRC_LINE_LEN) + if (copy_to_user(user_buf, buf, PIPE_CRC_LINE_LEN)) return -EFAULT; user_buf += PIPE_CRC_LINE_LEN; @@ -3678,11 +3575,11 @@ static struct pipe_crc_info i915_pipe_crc_data[I915_MAX_PIPES] = { static int i915_pipe_crc_create(struct dentry *root, struct drm_minor *minor, enum pipe pipe) { - struct drm_device *dev = minor->dev; + struct drm_i915_private *dev_priv = to_i915(minor->dev); struct dentry *ent; struct pipe_crc_info *info = &i915_pipe_crc_data[pipe]; - info->dev = dev; + info->dev_priv = dev_priv; ent = debugfs_create_file(info->name, S_IRUGO, root, info, &i915_pipe_crc_fops); if (!ent) @@ -3712,8 +3609,7 @@ static const char *pipe_crc_source_name(enum intel_pipe_crc_source source) static int display_crc_ctl_show(struct seq_file *m, void *data) { - struct drm_device *dev = m->private; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = m->private; int i; for (i = 0; i < I915_MAX_PIPES; i++) @@ -3725,9 +3621,7 @@ static int display_crc_ctl_show(struct seq_file *m, void *data) static int display_crc_ctl_open(struct inode *inode, struct file *file) { - struct drm_device *dev = inode->i_private; - - return single_open(file, display_crc_ctl_show, dev); + return single_open(file, display_crc_ctl_show, inode->i_private); } static int i8xx_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source, @@ -3750,9 +3644,11 @@ static int i8xx_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source, return 0; } -static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe, +static int i9xx_pipe_crc_auto_source(struct drm_i915_private *dev_priv, + enum pipe pipe, enum intel_pipe_crc_source *source) { + struct drm_device *dev = &dev_priv->drm; struct intel_encoder *encoder; struct intel_crtc *crtc; struct intel_digital_port *dig_port; @@ -3802,16 +3698,15 @@ static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe, return ret; } -static int vlv_pipe_crc_ctl_reg(struct drm_device *dev, +static int vlv_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv, enum pipe pipe, enum intel_pipe_crc_source *source, uint32_t *val) { - struct drm_i915_private *dev_priv = to_i915(dev); bool need_stable_symbols = false; if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) { - int ret = i9xx_pipe_crc_auto_source(dev, pipe, source); + int ret = i9xx_pipe_crc_auto_source(dev_priv, pipe, source); if (ret) return ret; } @@ -3829,7 +3724,7 @@ static int vlv_pipe_crc_ctl_reg(struct drm_device *dev, need_stable_symbols = true; break; case INTEL_PIPE_CRC_SOURCE_DP_D: - if (!IS_CHERRYVIEW(dev)) + if (!IS_CHERRYVIEW(dev_priv)) return -EINVAL; *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_D_VLV; need_stable_symbols = true; @@ -3873,16 +3768,15 @@ static int vlv_pipe_crc_ctl_reg(struct drm_device *dev, return 0; } -static int i9xx_pipe_crc_ctl_reg(struct drm_device *dev, +static int i9xx_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv, enum pipe pipe, enum intel_pipe_crc_source *source, uint32_t *val) { - struct drm_i915_private *dev_priv = to_i915(dev); bool need_stable_symbols = false; if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) { - int ret = i9xx_pipe_crc_auto_source(dev, pipe, source); + int ret = i9xx_pipe_crc_auto_source(dev_priv, pipe, source); if (ret) return ret; } @@ -3892,24 +3786,24 @@ static int i9xx_pipe_crc_ctl_reg(struct drm_device *dev, *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_I9XX; break; case INTEL_PIPE_CRC_SOURCE_TV: - if (!SUPPORTS_TV(dev)) + if (!SUPPORTS_TV(dev_priv)) return -EINVAL; *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_TV_PRE; break; case INTEL_PIPE_CRC_SOURCE_DP_B: - if (!IS_G4X(dev)) + if (!IS_G4X(dev_priv)) return -EINVAL; *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_B_G4X; need_stable_symbols = true; break; case INTEL_PIPE_CRC_SOURCE_DP_C: - if (!IS_G4X(dev)) + if (!IS_G4X(dev_priv)) return -EINVAL; *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_C_G4X; need_stable_symbols = true; break; case INTEL_PIPE_CRC_SOURCE_DP_D: - if (!IS_G4X(dev)) + if (!IS_G4X(dev_priv)) return -EINVAL; *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_D_G4X; need_stable_symbols = true; @@ -3933,7 +3827,7 @@ static int i9xx_pipe_crc_ctl_reg(struct drm_device *dev, if (need_stable_symbols) { uint32_t tmp = I915_READ(PORT_DFT2_G4X); - WARN_ON(!IS_G4X(dev)); + WARN_ON(!IS_G4X(dev_priv)); I915_WRITE(PORT_DFT_I9XX, I915_READ(PORT_DFT_I9XX) | DC_BALANCE_RESET); @@ -3949,10 +3843,9 @@ static int i9xx_pipe_crc_ctl_reg(struct drm_device *dev, return 0; } -static void vlv_undo_pipe_scramble_reset(struct drm_device *dev, +static void vlv_undo_pipe_scramble_reset(struct drm_i915_private *dev_priv, enum pipe pipe) { - struct drm_i915_private *dev_priv = to_i915(dev); uint32_t tmp = I915_READ(PORT_DFT2_G4X); switch (pipe) { @@ -3974,10 +3867,9 @@ static void vlv_undo_pipe_scramble_reset(struct drm_device *dev, } -static void g4x_undo_pipe_scramble_reset(struct drm_device *dev, +static void g4x_undo_pipe_scramble_reset(struct drm_i915_private *dev_priv, enum pipe pipe) { - struct drm_i915_private *dev_priv = to_i915(dev); uint32_t tmp = I915_READ(PORT_DFT2_G4X); if (pipe == PIPE_A) @@ -4018,9 +3910,10 @@ static int ilk_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source, return 0; } -static void hsw_trans_edp_pipe_A_crc_wa(struct drm_device *dev, bool enable) +static void hsw_trans_edp_pipe_A_crc_wa(struct drm_i915_private *dev_priv, + bool enable) { - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_device *dev = &dev_priv->drm; struct intel_crtc *crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_A]); struct intel_crtc_state *pipe_config; @@ -4054,7 +3947,7 @@ out: drm_atomic_state_free(state); } -static int ivb_pipe_crc_ctl_reg(struct drm_device *dev, +static int ivb_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv, enum pipe pipe, enum intel_pipe_crc_source *source, uint32_t *val) @@ -4070,8 +3963,8 @@ static int ivb_pipe_crc_ctl_reg(struct drm_device *dev, *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_SPRITE_IVB; break; case INTEL_PIPE_CRC_SOURCE_PF: - if (IS_HASWELL(dev) && pipe == PIPE_A) - hsw_trans_edp_pipe_A_crc_wa(dev, true); + if (IS_HASWELL(dev_priv) && pipe == PIPE_A) + hsw_trans_edp_pipe_A_crc_wa(dev_priv, true); *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PF_IVB; break; @@ -4085,13 +3978,14 @@ static int ivb_pipe_crc_ctl_reg(struct drm_device *dev, return 0; } -static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe, +static int pipe_crc_set_source(struct drm_i915_private *dev_priv, + enum pipe pipe, enum intel_pipe_crc_source source) { - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_device *dev = &dev_priv->drm; struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe]; - struct intel_crtc *crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev, - pipe)); + struct intel_crtc *crtc = + to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe)); enum intel_display_power_domain power_domain; u32 val = 0; /* shut up gcc */ int ret; @@ -4109,16 +4003,16 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe, return -EIO; } - if (IS_GEN2(dev)) + if (IS_GEN2(dev_priv)) ret = i8xx_pipe_crc_ctl_reg(&source, &val); - else if (INTEL_INFO(dev)->gen < 5) - ret = i9xx_pipe_crc_ctl_reg(dev, pipe, &source, &val); - else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) - ret = vlv_pipe_crc_ctl_reg(dev, pipe, &source, &val); - else if (IS_GEN5(dev) || IS_GEN6(dev)) + else if (INTEL_GEN(dev_priv) < 5) + ret = i9xx_pipe_crc_ctl_reg(dev_priv, pipe, &source, &val); + else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) + ret = vlv_pipe_crc_ctl_reg(dev_priv, pipe, &source, &val); + else if (IS_GEN5(dev_priv) || IS_GEN6(dev_priv)) ret = ilk_pipe_crc_ctl_reg(&source, &val); else - ret = ivb_pipe_crc_ctl_reg(dev, pipe, &source, &val); + ret = ivb_pipe_crc_ctl_reg(dev_priv, pipe, &source, &val); if (ret != 0) goto out; @@ -4182,12 +4076,12 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe, kfree(entries); - if (IS_G4X(dev)) - g4x_undo_pipe_scramble_reset(dev, pipe); - else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) - vlv_undo_pipe_scramble_reset(dev, pipe); - else if (IS_HASWELL(dev) && pipe == PIPE_A) - hsw_trans_edp_pipe_A_crc_wa(dev, false); + if (IS_G4X(dev_priv)) + g4x_undo_pipe_scramble_reset(dev_priv, pipe); + else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) + vlv_undo_pipe_scramble_reset(dev_priv, pipe); + else if (IS_HASWELL(dev_priv) && pipe == PIPE_A) + hsw_trans_edp_pipe_A_crc_wa(dev_priv, false); hsw_enable_ips(crtc); } @@ -4291,7 +4185,8 @@ display_crc_ctl_parse_source(const char *buf, enum intel_pipe_crc_source *s) return -EINVAL; } -static int display_crc_ctl_parse(struct drm_device *dev, char *buf, size_t len) +static int display_crc_ctl_parse(struct drm_i915_private *dev_priv, + char *buf, size_t len) { #define N_WORDS 3 int n_words; @@ -4322,14 +4217,14 @@ static int display_crc_ctl_parse(struct drm_device *dev, char *buf, size_t len) return -EINVAL; } - return pipe_crc_set_source(dev, pipe, source); + return pipe_crc_set_source(dev_priv, pipe, source); } static ssize_t display_crc_ctl_write(struct file *file, const char __user *ubuf, size_t len, loff_t *offp) { struct seq_file *m = file->private_data; - struct drm_device *dev = m->private; + struct drm_i915_private *dev_priv = m->private; char *tmpbuf; int ret; @@ -4352,7 +4247,7 @@ static ssize_t display_crc_ctl_write(struct file *file, const char __user *ubuf, } tmpbuf[len] = '\0'; - ret = display_crc_ctl_parse(dev, tmpbuf, len); + ret = display_crc_ctl_parse(dev_priv, tmpbuf, len); out: kfree(tmpbuf); @@ -4373,8 +4268,8 @@ static const struct file_operations i915_display_crc_ctl_fops = { }; static ssize_t i915_displayport_test_active_write(struct file *file, - const char __user *ubuf, - size_t len, loff_t *offp) + const char __user *ubuf, + size_t len, loff_t *offp) { char *input_buffer; int status = 0; @@ -4404,7 +4299,6 @@ static ssize_t i915_displayport_test_active_write(struct file *file, DRM_DEBUG_DRIVER("Copied %d bytes from user\n", (unsigned int)len); list_for_each_entry(connector, connector_list, head) { - if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) continue; @@ -4442,7 +4336,6 @@ static int i915_displayport_test_active_show(struct seq_file *m, void *data) struct intel_dp *intel_dp; list_for_each_entry(connector, connector_list, head) { - if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) continue; @@ -4462,11 +4355,12 @@ static int i915_displayport_test_active_show(struct seq_file *m, void *data) } static int i915_displayport_test_active_open(struct inode *inode, - struct file *file) + struct file *file) { - struct drm_device *dev = inode->i_private; + struct drm_i915_private *dev_priv = inode->i_private; - return single_open(file, i915_displayport_test_active_show, dev); + return single_open(file, i915_displayport_test_active_show, + &dev_priv->drm); } static const struct file_operations i915_displayport_test_active_fops = { @@ -4486,7 +4380,6 @@ static int i915_displayport_test_data_show(struct seq_file *m, void *data) struct intel_dp *intel_dp; list_for_each_entry(connector, connector_list, head) { - if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) continue; @@ -4502,11 +4395,12 @@ static int i915_displayport_test_data_show(struct seq_file *m, void *data) return 0; } static int i915_displayport_test_data_open(struct inode *inode, - struct file *file) + struct file *file) { - struct drm_device *dev = inode->i_private; + struct drm_i915_private *dev_priv = inode->i_private; - return single_open(file, i915_displayport_test_data_show, dev); + return single_open(file, i915_displayport_test_data_show, + &dev_priv->drm); } static const struct file_operations i915_displayport_test_data_fops = { @@ -4525,7 +4419,6 @@ static int i915_displayport_test_type_show(struct seq_file *m, void *data) struct intel_dp *intel_dp; list_for_each_entry(connector, connector_list, head) { - if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) continue; @@ -4544,9 +4437,10 @@ static int i915_displayport_test_type_show(struct seq_file *m, void *data) static int i915_displayport_test_type_open(struct inode *inode, struct file *file) { - struct drm_device *dev = inode->i_private; + struct drm_i915_private *dev_priv = inode->i_private; - return single_open(file, i915_displayport_test_type_show, dev); + return single_open(file, i915_displayport_test_type_show, + &dev_priv->drm); } static const struct file_operations i915_displayport_test_type_fops = { @@ -4559,13 +4453,14 @@ static const struct file_operations i915_displayport_test_type_fops = { static void wm_latency_show(struct seq_file *m, const uint16_t wm[8]) { - struct drm_device *dev = m->private; + struct drm_i915_private *dev_priv = m->private; + struct drm_device *dev = &dev_priv->drm; int level; int num_levels; - if (IS_CHERRYVIEW(dev)) + if (IS_CHERRYVIEW(dev_priv)) num_levels = 3; - else if (IS_VALLEYVIEW(dev)) + else if (IS_VALLEYVIEW(dev_priv)) num_levels = 1; else num_levels = ilk_wm_max_level(dev) + 1; @@ -4579,8 +4474,8 @@ static void wm_latency_show(struct seq_file *m, const uint16_t wm[8]) * - WM1+ latency values in 0.5us units * - latencies are in us on gen9/vlv/chv */ - if (INTEL_INFO(dev)->gen >= 9 || IS_VALLEYVIEW(dev) || - IS_CHERRYVIEW(dev)) + if (INTEL_GEN(dev_priv) >= 9 || IS_VALLEYVIEW(dev_priv) || + IS_CHERRYVIEW(dev_priv)) latency *= 10; else if (level > 0) latency *= 5; @@ -4594,14 +4489,13 @@ static void wm_latency_show(struct seq_file *m, const uint16_t wm[8]) static int pri_wm_latency_show(struct seq_file *m, void *data) { - struct drm_device *dev = m->private; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = m->private; const uint16_t *latencies; - if (INTEL_INFO(dev)->gen >= 9) + if (INTEL_GEN(dev_priv) >= 9) latencies = dev_priv->wm.skl_latency; else - latencies = to_i915(dev)->wm.pri_latency; + latencies = dev_priv->wm.pri_latency; wm_latency_show(m, latencies); @@ -4610,14 +4504,13 @@ static int pri_wm_latency_show(struct seq_file *m, void *data) static int spr_wm_latency_show(struct seq_file *m, void *data) { - struct drm_device *dev = m->private; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = m->private; const uint16_t *latencies; - if (INTEL_INFO(dev)->gen >= 9) + if (INTEL_GEN(dev_priv) >= 9) latencies = dev_priv->wm.skl_latency; else - latencies = to_i915(dev)->wm.spr_latency; + latencies = dev_priv->wm.spr_latency; wm_latency_show(m, latencies); @@ -4626,14 +4519,13 @@ static int spr_wm_latency_show(struct seq_file *m, void *data) static int cur_wm_latency_show(struct seq_file *m, void *data) { - struct drm_device *dev = m->private; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = m->private; const uint16_t *latencies; - if (INTEL_INFO(dev)->gen >= 9) + if (INTEL_GEN(dev_priv) >= 9) latencies = dev_priv->wm.skl_latency; else - latencies = to_i915(dev)->wm.cur_latency; + latencies = dev_priv->wm.cur_latency; wm_latency_show(m, latencies); @@ -4642,48 +4534,49 @@ static int cur_wm_latency_show(struct seq_file *m, void *data) static int pri_wm_latency_open(struct inode *inode, struct file *file) { - struct drm_device *dev = inode->i_private; + struct drm_i915_private *dev_priv = inode->i_private; - if (INTEL_INFO(dev)->gen < 5) + if (INTEL_GEN(dev_priv) < 5) return -ENODEV; - return single_open(file, pri_wm_latency_show, dev); + return single_open(file, pri_wm_latency_show, dev_priv); } static int spr_wm_latency_open(struct inode *inode, struct file *file) { - struct drm_device *dev = inode->i_private; + struct drm_i915_private *dev_priv = inode->i_private; - if (HAS_GMCH_DISPLAY(dev)) + if (HAS_GMCH_DISPLAY(dev_priv)) return -ENODEV; - return single_open(file, spr_wm_latency_show, dev); + return single_open(file, spr_wm_latency_show, dev_priv); } static int cur_wm_latency_open(struct inode *inode, struct file *file) { - struct drm_device *dev = inode->i_private; + struct drm_i915_private *dev_priv = inode->i_private; - if (HAS_GMCH_DISPLAY(dev)) + if (HAS_GMCH_DISPLAY(dev_priv)) return -ENODEV; - return single_open(file, cur_wm_latency_show, dev); + return single_open(file, cur_wm_latency_show, dev_priv); } static ssize_t wm_latency_write(struct file *file, const char __user *ubuf, size_t len, loff_t *offp, uint16_t wm[8]) { struct seq_file *m = file->private_data; - struct drm_device *dev = m->private; + struct drm_i915_private *dev_priv = m->private; + struct drm_device *dev = &dev_priv->drm; uint16_t new[8] = { 0 }; int num_levels; int level; int ret; char tmp[32]; - if (IS_CHERRYVIEW(dev)) + if (IS_CHERRYVIEW(dev_priv)) num_levels = 3; - else if (IS_VALLEYVIEW(dev)) + else if (IS_VALLEYVIEW(dev_priv)) num_levels = 1; else num_levels = ilk_wm_max_level(dev) + 1; @@ -4717,14 +4610,13 @@ static ssize_t pri_wm_latency_write(struct file *file, const char __user *ubuf, size_t len, loff_t *offp) { struct seq_file *m = file->private_data; - struct drm_device *dev = m->private; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = m->private; uint16_t *latencies; - if (INTEL_INFO(dev)->gen >= 9) + if (INTEL_GEN(dev_priv) >= 9) latencies = dev_priv->wm.skl_latency; else - latencies = to_i915(dev)->wm.pri_latency; + latencies = dev_priv->wm.pri_latency; return wm_latency_write(file, ubuf, len, offp, latencies); } @@ -4733,14 +4625,13 @@ static ssize_t spr_wm_latency_write(struct file *file, const char __user *ubuf, size_t len, loff_t *offp) { struct seq_file *m = file->private_data; - struct drm_device *dev = m->private; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = m->private; uint16_t *latencies; - if (INTEL_INFO(dev)->gen >= 9) + if (INTEL_GEN(dev_priv) >= 9) latencies = dev_priv->wm.skl_latency; else - latencies = to_i915(dev)->wm.spr_latency; + latencies = dev_priv->wm.spr_latency; return wm_latency_write(file, ubuf, len, offp, latencies); } @@ -4749,14 +4640,13 @@ static ssize_t cur_wm_latency_write(struct file *file, const char __user *ubuf, size_t len, loff_t *offp) { struct seq_file *m = file->private_data; - struct drm_device *dev = m->private; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = m->private; uint16_t *latencies; - if (INTEL_INFO(dev)->gen >= 9) + if (INTEL_GEN(dev_priv) >= 9) latencies = dev_priv->wm.skl_latency; else - latencies = to_i915(dev)->wm.cur_latency; + latencies = dev_priv->wm.cur_latency; return wm_latency_write(file, ubuf, len, offp, latencies); } @@ -4791,8 +4681,7 @@ static const struct file_operations i915_cur_wm_latency_fops = { static int i915_wedged_get(void *data, u64 *val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = data; *val = i915_terminally_wedged(&dev_priv->gpu_error); @@ -4802,8 +4691,7 @@ i915_wedged_get(void *data, u64 *val) static int i915_wedged_set(void *data, u64 val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = data; /* * There is no safeguard against this debugfs entry colliding @@ -4833,8 +4721,7 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_wedged_fops, static int i915_ring_missed_irq_get(void *data, u64 *val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = data; *val = dev_priv->gpu_error.missed_irq_rings; return 0; @@ -4843,8 +4730,8 @@ i915_ring_missed_irq_get(void *data, u64 *val) static int i915_ring_missed_irq_set(void *data, u64 val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = data; + struct drm_device *dev = &dev_priv->drm; int ret; /* Lock against concurrent debugfs callers */ @@ -4864,8 +4751,7 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_ring_missed_irq_fops, static int i915_ring_test_irq_get(void *data, u64 *val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = data; *val = dev_priv->gpu_error.test_irq_rings; @@ -4875,8 +4761,7 @@ i915_ring_test_irq_get(void *data, u64 *val) static int i915_ring_test_irq_set(void *data, u64 val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = data; val &= INTEL_INFO(dev_priv)->ring_mask; DRM_DEBUG_DRIVER("Masking interrupts on rings 0x%08llx\n", val); @@ -4908,8 +4793,8 @@ i915_drop_caches_get(void *data, u64 *val) static int i915_drop_caches_set(void *data, u64 val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = data; + struct drm_device *dev = &dev_priv->drm; int ret; DRM_DEBUG("Dropping caches: 0x%08llx\n", val); @@ -4921,7 +4806,9 @@ i915_drop_caches_set(void *data, u64 val) return ret; if (val & DROP_ACTIVE) { - ret = i915_gem_wait_for_idle(dev_priv); + ret = i915_gem_wait_for_idle(dev_priv, + I915_WAIT_INTERRUPTIBLE | + I915_WAIT_LOCKED); if (ret) goto unlock; } @@ -4948,38 +4835,25 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_drop_caches_fops, static int i915_max_freq_get(void *data, u64 *val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); - int ret; + struct drm_i915_private *dev_priv = data; - if (INTEL_INFO(dev)->gen < 6) + if (INTEL_GEN(dev_priv) < 6) return -ENODEV; - flush_delayed_work(&dev_priv->rps.delayed_resume_work); - - ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock); - if (ret) - return ret; - *val = intel_gpu_freq(dev_priv, dev_priv->rps.max_freq_softlimit); - mutex_unlock(&dev_priv->rps.hw_lock); - return 0; } static int i915_max_freq_set(void *data, u64 val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = data; u32 hw_max, hw_min; int ret; - if (INTEL_INFO(dev)->gen < 6) + if (INTEL_GEN(dev_priv) < 6) return -ENODEV; - flush_delayed_work(&dev_priv->rps.delayed_resume_work); - DRM_DEBUG_DRIVER("Manually setting max freq to %llu\n", val); ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock); @@ -5015,38 +4889,25 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_max_freq_fops, static int i915_min_freq_get(void *data, u64 *val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); - int ret; + struct drm_i915_private *dev_priv = data; - if (INTEL_INFO(dev)->gen < 6) + if (INTEL_GEN(dev_priv) < 6) return -ENODEV; - flush_delayed_work(&dev_priv->rps.delayed_resume_work); - - ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock); - if (ret) - return ret; - *val = intel_gpu_freq(dev_priv, dev_priv->rps.min_freq_softlimit); - mutex_unlock(&dev_priv->rps.hw_lock); - return 0; } static int i915_min_freq_set(void *data, u64 val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = data; u32 hw_max, hw_min; int ret; - if (INTEL_INFO(dev)->gen < 6) + if (INTEL_GEN(dev_priv) < 6) return -ENODEV; - flush_delayed_work(&dev_priv->rps.delayed_resume_work); - DRM_DEBUG_DRIVER("Manually setting min freq to %llu\n", val); ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock); @@ -5061,7 +4922,8 @@ i915_min_freq_set(void *data, u64 val) hw_max = dev_priv->rps.max_freq; hw_min = dev_priv->rps.min_freq; - if (val < hw_min || val > hw_max || val > dev_priv->rps.max_freq_softlimit) { + if (val < hw_min || + val > hw_max || val > dev_priv->rps.max_freq_softlimit) { mutex_unlock(&dev_priv->rps.hw_lock); return -EINVAL; } @@ -5082,12 +4944,12 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_min_freq_fops, static int i915_cache_sharing_get(void *data, u64 *val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = data; + struct drm_device *dev = &dev_priv->drm; u32 snpcr; int ret; - if (!(IS_GEN6(dev) || IS_GEN7(dev))) + if (!(IS_GEN6(dev_priv) || IS_GEN7(dev_priv))) return -ENODEV; ret = mutex_lock_interruptible(&dev->struct_mutex); @@ -5098,7 +4960,7 @@ i915_cache_sharing_get(void *data, u64 *val) snpcr = I915_READ(GEN6_MBCUNIT_SNPCR); intel_runtime_pm_put(dev_priv); - mutex_unlock(&dev_priv->drm.struct_mutex); + mutex_unlock(&dev->struct_mutex); *val = (snpcr & GEN6_MBC_SNPCR_MASK) >> GEN6_MBC_SNPCR_SHIFT; @@ -5108,11 +4970,10 @@ i915_cache_sharing_get(void *data, u64 *val) static int i915_cache_sharing_set(void *data, u64 val) { - struct drm_device *dev = data; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = data; u32 snpcr; - if (!(IS_GEN6(dev) || IS_GEN7(dev))) + if (!(IS_GEN6(dev_priv) || IS_GEN7(dev_priv))) return -ENODEV; if (val > 3) @@ -5135,18 +4996,9 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_cache_sharing_fops, i915_cache_sharing_get, i915_cache_sharing_set, "%llu\n"); -struct sseu_dev_status { - unsigned int slice_total; - unsigned int subslice_total; - unsigned int subslice_per_slice; - unsigned int eu_total; - unsigned int eu_per_subslice; -}; - -static void cherryview_sseu_device_status(struct drm_device *dev, - struct sseu_dev_status *stat) +static void cherryview_sseu_device_status(struct drm_i915_private *dev_priv, + struct sseu_dev_info *sseu) { - struct drm_i915_private *dev_priv = to_i915(dev); int ss_max = 2; int ss; u32 sig1[ss_max], sig2[ss_max]; @@ -5163,28 +5015,27 @@ static void cherryview_sseu_device_status(struct drm_device *dev, /* skip disabled subslice */ continue; - stat->slice_total = 1; - stat->subslice_per_slice++; + sseu->slice_mask = BIT(0); + sseu->subslice_mask |= BIT(ss); eu_cnt = ((sig1[ss] & CHV_EU08_PG_ENABLE) ? 0 : 2) + ((sig1[ss] & CHV_EU19_PG_ENABLE) ? 0 : 2) + ((sig1[ss] & CHV_EU210_PG_ENABLE) ? 0 : 2) + ((sig2[ss] & CHV_EU311_PG_ENABLE) ? 0 : 2); - stat->eu_total += eu_cnt; - stat->eu_per_subslice = max(stat->eu_per_subslice, eu_cnt); + sseu->eu_total += eu_cnt; + sseu->eu_per_subslice = max_t(unsigned int, + sseu->eu_per_subslice, eu_cnt); } - stat->subslice_total = stat->subslice_per_slice; } -static void gen9_sseu_device_status(struct drm_device *dev, - struct sseu_dev_status *stat) +static void gen9_sseu_device_status(struct drm_i915_private *dev_priv, + struct sseu_dev_info *sseu) { - struct drm_i915_private *dev_priv = to_i915(dev); int s_max = 3, ss_max = 4; int s, ss; u32 s_reg[s_max], eu_reg[2*s_max], eu_mask[2]; /* BXT has a single slice and at most 3 subslices. */ - if (IS_BROXTON(dev)) { + if (IS_BROXTON(dev_priv)) { s_max = 1; ss_max = 3; } @@ -5205,126 +5056,134 @@ static void gen9_sseu_device_status(struct drm_device *dev, GEN9_PGCTL_SSB_EU311_ACK; for (s = 0; s < s_max; s++) { - unsigned int ss_cnt = 0; - if ((s_reg[s] & GEN9_PGCTL_SLICE_ACK) == 0) /* skip disabled slice */ continue; - stat->slice_total++; + sseu->slice_mask |= BIT(s); - if (IS_SKYLAKE(dev) || IS_KABYLAKE(dev)) - ss_cnt = INTEL_INFO(dev)->subslice_per_slice; + if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) + sseu->subslice_mask = + INTEL_INFO(dev_priv)->sseu.subslice_mask; for (ss = 0; ss < ss_max; ss++) { unsigned int eu_cnt; - if (IS_BROXTON(dev) && - !(s_reg[s] & (GEN9_PGCTL_SS_ACK(ss)))) - /* skip disabled subslice */ - continue; + if (IS_BROXTON(dev_priv)) { + if (!(s_reg[s] & (GEN9_PGCTL_SS_ACK(ss)))) + /* skip disabled subslice */ + continue; - if (IS_BROXTON(dev)) - ss_cnt++; + sseu->subslice_mask |= BIT(ss); + } eu_cnt = 2 * hweight32(eu_reg[2*s + ss/2] & eu_mask[ss%2]); - stat->eu_total += eu_cnt; - stat->eu_per_subslice = max(stat->eu_per_subslice, - eu_cnt); + sseu->eu_total += eu_cnt; + sseu->eu_per_subslice = max_t(unsigned int, + sseu->eu_per_subslice, + eu_cnt); } - - stat->subslice_total += ss_cnt; - stat->subslice_per_slice = max(stat->subslice_per_slice, - ss_cnt); } } -static void broadwell_sseu_device_status(struct drm_device *dev, - struct sseu_dev_status *stat) +static void broadwell_sseu_device_status(struct drm_i915_private *dev_priv, + struct sseu_dev_info *sseu) { - struct drm_i915_private *dev_priv = to_i915(dev); - int s; u32 slice_info = I915_READ(GEN8_GT_SLICE_INFO); + int s; - stat->slice_total = hweight32(slice_info & GEN8_LSLICESTAT_MASK); + sseu->slice_mask = slice_info & GEN8_LSLICESTAT_MASK; - if (stat->slice_total) { - stat->subslice_per_slice = INTEL_INFO(dev)->subslice_per_slice; - stat->subslice_total = stat->slice_total * - stat->subslice_per_slice; - stat->eu_per_subslice = INTEL_INFO(dev)->eu_per_subslice; - stat->eu_total = stat->eu_per_subslice * stat->subslice_total; + if (sseu->slice_mask) { + sseu->subslice_mask = INTEL_INFO(dev_priv)->sseu.subslice_mask; + sseu->eu_per_subslice = + INTEL_INFO(dev_priv)->sseu.eu_per_subslice; + sseu->eu_total = sseu->eu_per_subslice * + sseu_subslice_total(sseu); /* subtract fused off EU(s) from enabled slice(s) */ - for (s = 0; s < stat->slice_total; s++) { - u8 subslice_7eu = INTEL_INFO(dev)->subslice_7eu[s]; + for (s = 0; s < fls(sseu->slice_mask); s++) { + u8 subslice_7eu = + INTEL_INFO(dev_priv)->sseu.subslice_7eu[s]; - stat->eu_total -= hweight8(subslice_7eu); + sseu->eu_total -= hweight8(subslice_7eu); } } } -static int i915_sseu_status(struct seq_file *m, void *unused) +static void i915_print_sseu_info(struct seq_file *m, bool is_available_info, + const struct sseu_dev_info *sseu) { - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - struct sseu_dev_status stat; + struct drm_i915_private *dev_priv = node_to_i915(m->private); + const char *type = is_available_info ? "Available" : "Enabled"; - if (INTEL_INFO(dev)->gen < 8) - return -ENODEV; + seq_printf(m, " %s Slice Mask: %04x\n", type, + sseu->slice_mask); + seq_printf(m, " %s Slice Total: %u\n", type, + hweight8(sseu->slice_mask)); + seq_printf(m, " %s Subslice Total: %u\n", type, + sseu_subslice_total(sseu)); + seq_printf(m, " %s Subslice Mask: %04x\n", type, + sseu->subslice_mask); + seq_printf(m, " %s Subslice Per Slice: %u\n", type, + hweight8(sseu->subslice_mask)); + seq_printf(m, " %s EU Total: %u\n", type, + sseu->eu_total); + seq_printf(m, " %s EU Per Subslice: %u\n", type, + sseu->eu_per_subslice); + + if (!is_available_info) + return; + + seq_printf(m, " Has Pooled EU: %s\n", yesno(HAS_POOLED_EU(dev_priv))); + if (HAS_POOLED_EU(dev_priv)) + seq_printf(m, " Min EU in pool: %u\n", sseu->min_eu_in_pool); - seq_puts(m, "SSEU Device Info\n"); - seq_printf(m, " Available Slice Total: %u\n", - INTEL_INFO(dev)->slice_total); - seq_printf(m, " Available Subslice Total: %u\n", - INTEL_INFO(dev)->subslice_total); - seq_printf(m, " Available Subslice Per Slice: %u\n", - INTEL_INFO(dev)->subslice_per_slice); - seq_printf(m, " Available EU Total: %u\n", - INTEL_INFO(dev)->eu_total); - seq_printf(m, " Available EU Per Subslice: %u\n", - INTEL_INFO(dev)->eu_per_subslice); - seq_printf(m, " Has Pooled EU: %s\n", yesno(HAS_POOLED_EU(dev))); - if (HAS_POOLED_EU(dev)) - seq_printf(m, " Min EU in pool: %u\n", - INTEL_INFO(dev)->min_eu_in_pool); seq_printf(m, " Has Slice Power Gating: %s\n", - yesno(INTEL_INFO(dev)->has_slice_pg)); + yesno(sseu->has_slice_pg)); seq_printf(m, " Has Subslice Power Gating: %s\n", - yesno(INTEL_INFO(dev)->has_subslice_pg)); + yesno(sseu->has_subslice_pg)); seq_printf(m, " Has EU Power Gating: %s\n", - yesno(INTEL_INFO(dev)->has_eu_pg)); + yesno(sseu->has_eu_pg)); +} + +static int i915_sseu_status(struct seq_file *m, void *unused) +{ + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct sseu_dev_info sseu; + + if (INTEL_GEN(dev_priv) < 8) + return -ENODEV; + + seq_puts(m, "SSEU Device Info\n"); + i915_print_sseu_info(m, true, &INTEL_INFO(dev_priv)->sseu); seq_puts(m, "SSEU Device Status\n"); - memset(&stat, 0, sizeof(stat)); - if (IS_CHERRYVIEW(dev)) { - cherryview_sseu_device_status(dev, &stat); - } else if (IS_BROADWELL(dev)) { - broadwell_sseu_device_status(dev, &stat); - } else if (INTEL_INFO(dev)->gen >= 9) { - gen9_sseu_device_status(dev, &stat); - } - seq_printf(m, " Enabled Slice Total: %u\n", - stat.slice_total); - seq_printf(m, " Enabled Subslice Total: %u\n", - stat.subslice_total); - seq_printf(m, " Enabled Subslice Per Slice: %u\n", - stat.subslice_per_slice); - seq_printf(m, " Enabled EU Total: %u\n", - stat.eu_total); - seq_printf(m, " Enabled EU Per Subslice: %u\n", - stat.eu_per_subslice); + memset(&sseu, 0, sizeof(sseu)); + + intel_runtime_pm_get(dev_priv); + + if (IS_CHERRYVIEW(dev_priv)) { + cherryview_sseu_device_status(dev_priv, &sseu); + } else if (IS_BROADWELL(dev_priv)) { + broadwell_sseu_device_status(dev_priv, &sseu); + } else if (INTEL_GEN(dev_priv) >= 9) { + gen9_sseu_device_status(dev_priv, &sseu); + } + + intel_runtime_pm_put(dev_priv); + + i915_print_sseu_info(m, false, &sseu); return 0; } static int i915_forcewake_open(struct inode *inode, struct file *file) { - struct drm_device *dev = inode->i_private; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = inode->i_private; - if (INTEL_INFO(dev)->gen < 6) + if (INTEL_GEN(dev_priv) < 6) return 0; intel_runtime_pm_get(dev_priv); @@ -5335,10 +5194,9 @@ static int i915_forcewake_open(struct inode *inode, struct file *file) static int i915_forcewake_release(struct inode *inode, struct file *file) { - struct drm_device *dev = inode->i_private; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = inode->i_private; - if (INTEL_INFO(dev)->gen < 6) + if (INTEL_GEN(dev_priv) < 6) return 0; intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); @@ -5355,12 +5213,11 @@ static const struct file_operations i915_forcewake_fops = { static int i915_forcewake_create(struct dentry *root, struct drm_minor *minor) { - struct drm_device *dev = minor->dev; struct dentry *ent; ent = debugfs_create_file("i915_forcewake_user", S_IRUSR, - root, dev, + root, to_i915(minor->dev), &i915_forcewake_fops); if (!ent) return -ENOMEM; @@ -5373,12 +5230,11 @@ static int i915_debugfs_create(struct dentry *root, const char *name, const struct file_operations *fops) { - struct drm_device *dev = minor->dev; struct dentry *ent; ent = debugfs_create_file(name, S_IRUGO | S_IWUSR, - root, dev, + root, to_i915(minor->dev), fops); if (!ent) return -ENOMEM; @@ -5390,9 +5246,7 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_capabilities", i915_capabilities, 0}, {"i915_gem_objects", i915_gem_object_info, 0}, {"i915_gem_gtt", i915_gem_gtt_info, 0}, - {"i915_gem_pinned", i915_gem_gtt_info, 0, (void *) PINNED_LIST}, - {"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST}, - {"i915_gem_inactive", i915_gem_object_list_info, 0, (void *) INACTIVE_LIST}, + {"i915_gem_pin_display", i915_gem_gtt_info, 0, (void *)1}, {"i915_gem_stolen", i915_gem_stolen_list_info }, {"i915_gem_pageflip", i915_gem_pageflip_info, 0}, {"i915_gem_request", i915_gem_request_info, 0}, @@ -5467,9 +5321,8 @@ static const struct i915_debugfs_files { {"i915_dp_test_active", &i915_displayport_test_active_fops} }; -void intel_display_crc_init(struct drm_device *dev) +void intel_display_crc_init(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); enum pipe pipe; for_each_pipe(dev_priv, pipe) { @@ -5517,7 +5370,7 @@ void i915_debugfs_unregister(struct drm_i915_private *dev_priv) drm_debugfs_remove_files(i915_debugfs_list, I915_DEBUGFS_ENTRIES, minor); - drm_debugfs_remove_files((struct drm_info_list *) &i915_forcewake_fops, + drm_debugfs_remove_files((struct drm_info_list *)&i915_forcewake_fops, 1, minor); for (i = 0; i < ARRAY_SIZE(i915_pipe_crc_data); i++) { @@ -5529,7 +5382,7 @@ void i915_debugfs_unregister(struct drm_i915_private *dev_priv) for (i = 0; i < ARRAY_SIZE(i915_debugfs_files); i++) { struct drm_info_list *info_list = - (struct drm_info_list *) i915_debugfs_files[i].fops; + (struct drm_info_list *)i915_debugfs_files[i].fops; drm_debugfs_remove_files(info_list, 1, minor); } @@ -5609,6 +5462,40 @@ static const struct file_operations i915_dpcd_fops = { .release = single_release, }; +static int i915_panel_show(struct seq_file *m, void *data) +{ + struct drm_connector *connector = m->private; + struct intel_dp *intel_dp = + enc_to_intel_dp(&intel_attached_encoder(connector)->base); + + if (connector->status != connector_status_connected) + return -ENODEV; + + seq_printf(m, "Panel power up delay: %d\n", + intel_dp->panel_power_up_delay); + seq_printf(m, "Panel power down delay: %d\n", + intel_dp->panel_power_down_delay); + seq_printf(m, "Backlight on delay: %d\n", + intel_dp->backlight_on_delay); + seq_printf(m, "Backlight off delay: %d\n", + intel_dp->backlight_off_delay); + + return 0; +} + +static int i915_panel_open(struct inode *inode, struct file *file) +{ + return single_open(file, i915_panel_show, inode->i_private); +} + +static const struct file_operations i915_panel_fops = { + .owner = THIS_MODULE, + .open = i915_panel_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /** * i915_debugfs_connector_add - add i915 specific connector debugfs files * @connector: pointer to a registered drm_connector @@ -5628,8 +5515,12 @@ int i915_debugfs_connector_add(struct drm_connector *connector) if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort || connector->connector_type == DRM_MODE_CONNECTOR_eDP) - debugfs_create_file("i915_dpcd", S_IRUGO, root, connector, - &i915_dpcd_fops); + debugfs_create_file("i915_dpcd", S_IRUGO, root, + connector, &i915_dpcd_fops); + + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) + debugfs_create_file("i915_panel_timings", S_IRUGO, root, + connector, &i915_panel_fops); return 0; } diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 95ddd56..7f4e8ad 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -77,7 +77,7 @@ __i915_printk(struct drm_i915_private *dev_priv, const char *level, const char *fmt, ...) { static bool shown_bug_once; - struct device *dev = dev_priv->drm.dev; + struct device *kdev = dev_priv->drm.dev; bool is_error = level[1] <= KERN_ERR[1]; bool is_debug = level[1] == KERN_DEBUG[1]; struct va_format vaf; @@ -91,11 +91,11 @@ __i915_printk(struct drm_i915_private *dev_priv, const char *level, vaf.fmt = fmt; vaf.va = &args; - dev_printk(level, dev, "[" DRM_NAME ":%ps] %pV", + dev_printk(level, kdev, "[" DRM_NAME ":%ps] %pV", __builtin_return_address(0), &vaf); if (is_error && !shown_bug_once) { - dev_notice(dev, "%s", FDO_BUG_MSG); + dev_notice(kdev, "%s", FDO_BUG_MSG); shown_bug_once = true; } @@ -228,31 +228,11 @@ static void intel_detect_pch(struct drm_device *dev) pci_dev_put(pch); } -bool i915_semaphore_is_enabled(struct drm_i915_private *dev_priv) -{ - if (INTEL_GEN(dev_priv) < 6) - return false; - - if (i915.semaphores >= 0) - return i915.semaphores; - - /* TODO: make semaphores and Execlists play nicely together */ - if (i915.enable_execlists) - return false; - -#ifdef CONFIG_INTEL_IOMMU - /* Enable semaphores on SNB when IO remapping is off */ - if (IS_GEN6(dev_priv) && intel_iommu_gfx_mapped) - return false; -#endif - - return true; -} - static int i915_getparam(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; drm_i915_getparam_t *param = data; int value; @@ -263,13 +243,10 @@ static int i915_getparam(struct drm_device *dev, void *data, /* Reject all old ums/dri params. */ return -ENODEV; case I915_PARAM_CHIPSET_ID: - value = dev->pdev->device; + value = pdev->device; break; case I915_PARAM_REVISION: - value = dev->pdev->revision; - break; - case I915_PARAM_HAS_GEM: - value = 1; + value = pdev->revision; break; case I915_PARAM_NUM_FENCES_AVAIL: value = dev_priv->num_fence_regs; @@ -277,13 +254,6 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_OVERLAY: value = dev_priv->overlay ? 1 : 0; break; - case I915_PARAM_HAS_PAGEFLIPPING: - value = 1; - break; - case I915_PARAM_HAS_EXECBUF2: - /* depends on GEM */ - value = 1; - break; case I915_PARAM_HAS_BSD: value = intel_engine_initialized(&dev_priv->engine[VCS]); break; @@ -296,67 +266,34 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_BSD2: value = intel_engine_initialized(&dev_priv->engine[VCS2]); break; - case I915_PARAM_HAS_RELAXED_FENCING: - value = 1; - break; - case I915_PARAM_HAS_COHERENT_RINGS: - value = 1; - break; case I915_PARAM_HAS_EXEC_CONSTANTS: - value = INTEL_INFO(dev)->gen >= 4; - break; - case I915_PARAM_HAS_RELAXED_DELTA: - value = 1; - break; - case I915_PARAM_HAS_GEN7_SOL_RESET: - value = 1; + value = INTEL_GEN(dev_priv) >= 4; break; case I915_PARAM_HAS_LLC: - value = HAS_LLC(dev); + value = HAS_LLC(dev_priv); break; case I915_PARAM_HAS_WT: - value = HAS_WT(dev); + value = HAS_WT(dev_priv); break; case I915_PARAM_HAS_ALIASING_PPGTT: - value = USES_PPGTT(dev); - break; - case I915_PARAM_HAS_WAIT_TIMEOUT: - value = 1; + value = USES_PPGTT(dev_priv); break; case I915_PARAM_HAS_SEMAPHORES: - value = i915_semaphore_is_enabled(dev_priv); - break; - case I915_PARAM_HAS_PRIME_VMAP_FLUSH: - value = 1; + value = i915.semaphores; break; case I915_PARAM_HAS_SECURE_BATCHES: value = capable(CAP_SYS_ADMIN); break; - case I915_PARAM_HAS_PINNED_BATCHES: - value = 1; - break; - case I915_PARAM_HAS_EXEC_NO_RELOC: - value = 1; - break; - case I915_PARAM_HAS_EXEC_HANDLE_LUT: - value = 1; - break; case I915_PARAM_CMD_PARSER_VERSION: value = i915_cmd_parser_get_version(dev_priv); break; - case I915_PARAM_HAS_COHERENT_PHYS_GTT: - value = 1; - break; - case I915_PARAM_MMAP_VERSION: - value = 1; - break; case I915_PARAM_SUBSLICE_TOTAL: - value = INTEL_INFO(dev)->subslice_total; + value = sseu_subslice_total(&INTEL_INFO(dev_priv)->sseu); if (!value) return -ENODEV; break; case I915_PARAM_EU_TOTAL: - value = INTEL_INFO(dev)->eu_total; + value = INTEL_INFO(dev_priv)->sseu.eu_total; if (!value) return -ENODEV; break; @@ -364,16 +301,43 @@ static int i915_getparam(struct drm_device *dev, void *data, value = i915.enable_hangcheck && intel_has_gpu_reset(dev_priv); break; case I915_PARAM_HAS_RESOURCE_STREAMER: - value = HAS_RESOURCE_STREAMER(dev); - break; - case I915_PARAM_HAS_EXEC_SOFTPIN: - value = 1; + value = HAS_RESOURCE_STREAMER(dev_priv); break; case I915_PARAM_HAS_POOLED_EU: - value = HAS_POOLED_EU(dev); + value = HAS_POOLED_EU(dev_priv); break; case I915_PARAM_MIN_EU_IN_POOL: - value = INTEL_INFO(dev)->min_eu_in_pool; + value = INTEL_INFO(dev_priv)->sseu.min_eu_in_pool; + break; + case I915_PARAM_MMAP_GTT_VERSION: + /* Though we've started our numbering from 1, and so class all + * earlier versions as 0, in effect their value is undefined as + * the ioctl will report EINVAL for the unknown param! + */ + value = i915_gem_mmap_gtt_version(); + break; + case I915_PARAM_MMAP_VERSION: + /* Remember to bump this if the version changes! */ + case I915_PARAM_HAS_GEM: + case I915_PARAM_HAS_PAGEFLIPPING: + case I915_PARAM_HAS_EXECBUF2: /* depends on GEM */ + case I915_PARAM_HAS_RELAXED_FENCING: + case I915_PARAM_HAS_COHERENT_RINGS: + case I915_PARAM_HAS_RELAXED_DELTA: + case I915_PARAM_HAS_GEN7_SOL_RESET: + case I915_PARAM_HAS_WAIT_TIMEOUT: + case I915_PARAM_HAS_PRIME_VMAP_FLUSH: + case I915_PARAM_HAS_PINNED_BATCHES: + case I915_PARAM_HAS_EXEC_NO_RELOC: + case I915_PARAM_HAS_EXEC_HANDLE_LUT: + case I915_PARAM_HAS_COHERENT_PHYS_GTT: + case I915_PARAM_HAS_EXEC_SOFTPIN: + /* For the time being all of these are always true; + * if some supported hardware does not have one of these + * features this value needs to be provided from + * INTEL_INFO(), a feature macro, or similar. + */ + value = 1; break; default: DRM_DEBUG("Unknown parameter %d\n", param->param); @@ -537,7 +501,7 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_ pr_info("switched on\n"); dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; /* i915 resume handler doesn't set to D0 */ - pci_set_power_state(dev->pdev, PCI_D0); + pci_set_power_state(pdev, PCI_D0); i915_resume_switcheroo(dev); dev->switch_power_state = DRM_SWITCH_POWER_ON; } else { @@ -595,7 +559,6 @@ static void i915_gem_fini(struct drm_device *dev) } mutex_lock(&dev->struct_mutex); - i915_gem_reset(dev); i915_gem_cleanup_engines(dev); i915_gem_context_fini(dev); mutex_unlock(&dev->struct_mutex); @@ -606,6 +569,7 @@ static void i915_gem_fini(struct drm_device *dev) static int i915_load_modeset_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; int ret; if (i915_inject_load_failure()) @@ -622,13 +586,13 @@ static int i915_load_modeset_init(struct drm_device *dev) * then we do not take part in VGA arbitration and the * vga_client_register() fails with -ENODEV. */ - ret = vga_client_register(dev->pdev, dev, NULL, i915_vga_set_decode); + ret = vga_client_register(pdev, dev, NULL, i915_vga_set_decode); if (ret && ret != -ENODEV) goto out; intel_register_dsm_handler(); - ret = vga_switcheroo_register_client(dev->pdev, &i915_switcheroo_ops, false); + ret = vga_switcheroo_register_client(pdev, &i915_switcheroo_ops, false); if (ret) goto cleanup_vga_client; @@ -680,9 +644,9 @@ cleanup_irq: cleanup_csr: intel_csr_ucode_fini(dev_priv); intel_power_domains_fini(dev_priv); - vga_switcheroo_unregister_client(dev->pdev); + vga_switcheroo_unregister_client(pdev); cleanup_vga_client: - vga_client_register(dev->pdev, NULL, NULL, NULL); + vga_client_register(pdev, NULL, NULL, NULL); out: return ret; } @@ -706,7 +670,7 @@ static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; - ret = remove_conflicting_framebuffers(ap, "inteldrmfb", primary); + ret = drm_fb_helper_remove_conflicting_framebuffers(ap, "inteldrmfb", primary); kfree(ap); @@ -848,6 +812,8 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv, mutex_init(&dev_priv->wm.wm_mutex); mutex_init(&dev_priv->pps_mutex); + i915_memcpy_init_early(dev_priv); + ret = i915_workqueues_init(dev_priv); if (ret < 0) return ret; @@ -868,7 +834,7 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv, intel_init_audio_hooks(dev_priv); i915_gem_load_init(&dev_priv->drm); - intel_display_crc_init(&dev_priv->drm); + intel_display_crc_init(dev_priv); intel_device_info_dump(dev_priv); @@ -900,6 +866,7 @@ static void i915_driver_cleanup_early(struct drm_i915_private *dev_priv) static int i915_mmio_setup(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; int mmio_bar; int mmio_size; @@ -916,7 +883,7 @@ static int i915_mmio_setup(struct drm_device *dev) mmio_size = 512 * 1024; else mmio_size = 2 * 1024 * 1024; - dev_priv->regs = pci_iomap(dev->pdev, mmio_bar, mmio_size); + dev_priv->regs = pci_iomap(pdev, mmio_bar, mmio_size); if (dev_priv->regs == NULL) { DRM_ERROR("failed to map registers\n"); @@ -932,9 +899,10 @@ static int i915_mmio_setup(struct drm_device *dev) static void i915_mmio_cleanup(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; intel_teardown_mchbar(dev); - pci_iounmap(dev->pdev, dev_priv->regs); + pci_iounmap(pdev, dev_priv->regs); } /** @@ -999,6 +967,9 @@ static void intel_sanitize_options(struct drm_i915_private *dev_priv) i915.enable_ppgtt = intel_sanitize_enable_ppgtt(dev_priv, i915.enable_ppgtt); DRM_DEBUG_DRIVER("ppgtt mode: %i\n", i915.enable_ppgtt); + + i915.semaphores = intel_sanitize_semaphores(dev_priv, i915.semaphores); + DRM_DEBUG_DRIVER("use GPU sempahores? %s\n", yesno(i915.semaphores)); } /** @@ -1010,9 +981,8 @@ static void intel_sanitize_options(struct drm_i915_private *dev_priv) */ static int i915_driver_init_hw(struct drm_i915_private *dev_priv) { + struct pci_dev *pdev = dev_priv->drm.pdev; struct drm_device *dev = &dev_priv->drm; - struct i915_ggtt *ggtt = &dev_priv->ggtt; - uint32_t aperture_size; int ret; if (i915_inject_load_failure()) @@ -1022,16 +992,10 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv) intel_sanitize_options(dev_priv); - ret = i915_ggtt_init_hw(dev); + ret = i915_ggtt_probe_hw(dev_priv); if (ret) return ret; - ret = i915_ggtt_enable_hw(dev); - if (ret) { - DRM_ERROR("failed to enable GGTT\n"); - goto out_ggtt; - } - /* WARNING: Apparently we must kick fbdev drivers before vgacon, * otherwise the vga fbdev driver falls over. */ ret = i915_kick_out_firmware_fb(dev_priv); @@ -1046,11 +1010,21 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv) goto out_ggtt; } - pci_set_master(dev->pdev); + ret = i915_ggtt_init_hw(dev_priv); + if (ret) + return ret; + + ret = i915_ggtt_enable_hw(dev_priv); + if (ret) { + DRM_ERROR("failed to enable GGTT\n"); + goto out_ggtt; + } + + pci_set_master(pdev); /* overlay on gen2 is broken and can't address above 1G */ if (IS_GEN2(dev)) { - ret = dma_set_coherent_mask(&dev->pdev->dev, DMA_BIT_MASK(30)); + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(30)); if (ret) { DRM_ERROR("failed to set DMA mask\n"); @@ -1058,7 +1032,6 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv) } } - /* 965GM sometimes incorrectly writes to hardware status page (HWS) * using 32bit addressing, overwriting memory if HWS is located * above 4GB. @@ -1068,7 +1041,7 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv) * which also needs to be handled carefully. */ if (IS_BROADWATER(dev) || IS_CRESTLINE(dev)) { - ret = dma_set_coherent_mask(&dev->pdev->dev, DMA_BIT_MASK(32)); + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); if (ret) { DRM_ERROR("failed to set DMA mask\n"); @@ -1077,19 +1050,6 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv) } } - aperture_size = ggtt->mappable_end; - - ggtt->mappable = - io_mapping_create_wc(ggtt->mappable_base, - aperture_size); - if (!ggtt->mappable) { - ret = -EIO; - goto out_ggtt; - } - - ggtt->mtrr = arch_phys_wc_add(ggtt->mappable_base, - aperture_size); - pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); @@ -1111,14 +1071,14 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv) * stuck interrupts on some machines. */ if (!IS_I945G(dev) && !IS_I945GM(dev)) { - if (pci_enable_msi(dev->pdev) < 0) + if (pci_enable_msi(pdev) < 0) DRM_DEBUG_DRIVER("can't enable MSI"); } return 0; out_ggtt: - i915_ggtt_cleanup_hw(dev); + i915_ggtt_cleanup_hw(dev_priv); return ret; } @@ -1129,16 +1089,13 @@ out_ggtt: */ static void i915_driver_cleanup_hw(struct drm_i915_private *dev_priv) { - struct drm_device *dev = &dev_priv->drm; - struct i915_ggtt *ggtt = &dev_priv->ggtt; + struct pci_dev *pdev = dev_priv->drm.pdev; - if (dev->pdev->msi_enabled) - pci_disable_msi(dev->pdev); + if (pdev->msi_enabled) + pci_disable_msi(pdev); pm_qos_remove_request(&dev_priv->pm_qos); - arch_phys_wc_del(ggtt->mtrr); - io_mapping_free(ggtt->mappable); - i915_ggtt_cleanup_hw(dev); + i915_ggtt_cleanup_hw(dev_priv); } /** @@ -1164,7 +1121,7 @@ static void i915_driver_register(struct drm_i915_private *dev_priv) /* Reveal our presence to userspace */ if (drm_dev_register(dev, 0) == 0) { i915_debugfs_register(dev_priv); - i915_setup_sysfs(dev); + i915_setup_sysfs(dev_priv); } else DRM_ERROR("Failed to register driver for userspace access!\n"); @@ -1201,7 +1158,7 @@ static void i915_driver_unregister(struct drm_i915_private *dev_priv) acpi_video_unregister(); intel_opregion_unregister(dev_priv); - i915_teardown_sysfs(&dev_priv->drm); + i915_teardown_sysfs(dev_priv); i915_debugfs_unregister(dev_priv); drm_dev_unregister(&dev_priv->drm); @@ -1281,6 +1238,11 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent) intel_runtime_pm_enable(dev_priv); + /* Everything is in place, we can now relax! */ + DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n", + driver.name, driver.major, driver.minor, driver.patchlevel, + driver.date, pci_name(pdev), dev_priv->drm.primary->index); + intel_runtime_pm_put(dev_priv); return 0; @@ -1305,6 +1267,7 @@ out_free_priv: void i915_driver_unload(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; intel_fbdev_fini(dev); @@ -1333,8 +1296,8 @@ void i915_driver_unload(struct drm_device *dev) kfree(dev_priv->vbt.lfp_lvds_vbt_mode); dev_priv->vbt.lfp_lvds_vbt_mode = NULL; - vga_switcheroo_unregister_client(dev->pdev); - vga_client_register(dev->pdev, NULL, NULL, NULL); + vga_switcheroo_unregister_client(pdev); + vga_client_register(pdev, NULL, NULL, NULL); intel_csr_ucode_fini(dev_priv); @@ -1343,7 +1306,7 @@ void i915_driver_unload(struct drm_device *dev) i915_destroy_error_state(dev); /* Flush any outstanding unpin_work. */ - flush_workqueue(dev_priv->wq); + drain_workqueue(dev_priv->wq); intel_guc_fini(dev); i915_gem_fini(dev); @@ -1431,6 +1394,7 @@ static bool suspend_to_idle(struct drm_i915_private *dev_priv) static int i915_drm_suspend(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; pci_power_t opregion_target_state; int error; @@ -1447,19 +1411,17 @@ static int i915_drm_suspend(struct drm_device *dev) drm_kms_helper_poll_disable(dev); - pci_save_state(dev->pdev); + pci_save_state(pdev); error = i915_gem_suspend(dev); if (error) { - dev_err(&dev->pdev->dev, + dev_err(&pdev->dev, "GEM idle failed, resume might fail\n"); goto out; } intel_guc_suspend(dev); - intel_suspend_gt_powersave(dev_priv); - intel_display_suspend(dev); intel_dp_mst_suspend(dev); @@ -1495,9 +1457,10 @@ out: return error; } -static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation) +static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation) { - struct drm_i915_private *dev_priv = to_i915(drm_dev); + struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; bool fw_csr; int ret; @@ -1531,7 +1494,7 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation) goto out; } - pci_disable_device(drm_dev->pdev); + pci_disable_device(pdev); /* * During hibernation on some platforms the BIOS may try to access * the device even though it's already in D3 and hang the machine. So @@ -1545,7 +1508,7 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation) * Acer Aspire 1830T */ if (!(hibernation && INTEL_INFO(dev_priv)->gen < 6)) - pci_set_power_state(drm_dev->pdev, PCI_D3hot); + pci_set_power_state(pdev, PCI_D3hot); dev_priv->suspended_to_idle = suspend_to_idle(dev_priv); @@ -1585,18 +1548,18 @@ static int i915_drm_resume(struct drm_device *dev) int ret; disable_rpm_wakeref_asserts(dev_priv); + intel_sanitize_gt_powersave(dev_priv); - ret = i915_ggtt_enable_hw(dev); + ret = i915_ggtt_enable_hw(dev_priv); if (ret) DRM_ERROR("failed to re-enable GGTT\n"); intel_csr_ucode_resume(dev_priv); - mutex_lock(&dev->struct_mutex); - i915_gem_restore_gtt_mappings(dev); - mutex_unlock(&dev->struct_mutex); + i915_gem_resume(dev); i915_restore_state(dev); + intel_pps_unlock_regs_wa(dev_priv); intel_opregion_setup(dev_priv); intel_init_pch_refclk(dev); @@ -1615,7 +1578,7 @@ static int i915_drm_resume(struct drm_device *dev) mutex_lock(&dev->struct_mutex); if (i915_gem_init_hw(dev)) { DRM_ERROR("failed to re-initialize GPU, declaring wedged!\n"); - atomic_or(I915_WEDGED, &dev_priv->gpu_error.reset_counter); + i915_gem_set_wedged(dev_priv); } mutex_unlock(&dev->struct_mutex); @@ -1652,6 +1615,7 @@ static int i915_drm_resume(struct drm_device *dev) intel_opregion_notify_adapter(dev_priv, PCI_D0); + intel_autoenable_gt_powersave(dev_priv); drm_kms_helper_poll_enable(dev); enable_rpm_wakeref_asserts(dev_priv); @@ -1662,6 +1626,7 @@ static int i915_drm_resume(struct drm_device *dev) static int i915_drm_resume_early(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; int ret; /* @@ -1684,7 +1649,7 @@ static int i915_drm_resume_early(struct drm_device *dev) * the device powered we can also remove the following set power state * call. */ - ret = pci_set_power_state(dev->pdev, PCI_D0); + ret = pci_set_power_state(pdev, PCI_D0); if (ret) { DRM_ERROR("failed to set PCI D0 power state (%d)\n", ret); goto out; @@ -1703,12 +1668,12 @@ static int i915_drm_resume_early(struct drm_device *dev) * depend on the device enable refcount we can't anyway depend on them * disabling/enabling the device. */ - if (pci_enable_device(dev->pdev)) { + if (pci_enable_device(pdev)) { ret = -EIO; goto out; } - pci_set_master(dev->pdev); + pci_set_master(pdev); disable_rpm_wakeref_asserts(dev_priv); @@ -1760,8 +1725,10 @@ int i915_resume_switcheroo(struct drm_device *dev) * i915_reset - reset chip after a hang * @dev: drm device to reset * - * Reset the chip. Useful if a hang is detected. Returns zero on successful - * reset or otherwise an error code. + * Reset the chip. Useful if a hang is detected. Marks the device as wedged + * on failure. + * + * Caller must hold the struct_mutex. * * Procedure is fairly simple: * - reset the chip using the reset reg @@ -1771,31 +1738,22 @@ int i915_resume_switcheroo(struct drm_device *dev) * - re-init interrupt state * - re-init display */ -int i915_reset(struct drm_i915_private *dev_priv) +void i915_reset(struct drm_i915_private *dev_priv) { struct drm_device *dev = &dev_priv->drm; struct i915_gpu_error *error = &dev_priv->gpu_error; - unsigned reset_counter; int ret; - intel_reset_gt_powersave(dev_priv); + lockdep_assert_held(&dev->struct_mutex); - mutex_lock(&dev->struct_mutex); + if (!test_and_clear_bit(I915_RESET_IN_PROGRESS, &error->flags)) + return; /* Clear any previous failed attempts at recovery. Time to try again. */ - atomic_andnot(I915_WEDGED, &error->reset_counter); - - /* Clear the reset-in-progress flag and increment the reset epoch. */ - reset_counter = atomic_inc_return(&error->reset_counter); - if (WARN_ON(__i915_reset_in_progress(reset_counter))) { - ret = -EIO; - goto error; - } + __clear_bit(I915_WEDGED, &error->flags); + error->reset_count++; pr_notice("drm/i915: Resetting chip after gpu hang\n"); - - i915_gem_reset(dev); - ret = intel_gpu_reset(dev_priv, ALL_ENGINES); if (ret) { if (ret != -ENODEV) @@ -1805,6 +1763,7 @@ int i915_reset(struct drm_i915_private *dev_priv) goto error; } + i915_gem_reset(dev_priv); intel_overlay_reset(dev_priv); /* Ok, now get things going again... */ @@ -1827,44 +1786,43 @@ int i915_reset(struct drm_i915_private *dev_priv) goto error; } - mutex_unlock(&dev->struct_mutex); - /* * rps/rc6 re-init is necessary to restore state lost after the * reset and the re-install of gt irqs. Skip for ironlake per * previous concerns that it doesn't respond well to some forms * of re-init after reset. */ - if (INTEL_INFO(dev)->gen > 5) - intel_enable_gt_powersave(dev_priv); + intel_sanitize_gt_powersave(dev_priv); + intel_autoenable_gt_powersave(dev_priv); - return 0; +wakeup: + wake_up_bit(&error->flags, I915_RESET_IN_PROGRESS); + return; error: - atomic_or(I915_WEDGED, &error->reset_counter); - mutex_unlock(&dev->struct_mutex); - return ret; + i915_gem_set_wedged(dev_priv); + goto wakeup; } -static int i915_pm_suspend(struct device *dev) +static int i915_pm_suspend(struct device *kdev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct pci_dev *pdev = to_pci_dev(kdev); + struct drm_device *dev = pci_get_drvdata(pdev); - if (!drm_dev) { - dev_err(dev, "DRM not initialized, aborting suspend.\n"); + if (!dev) { + dev_err(kdev, "DRM not initialized, aborting suspend.\n"); return -ENODEV; } - if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - return i915_drm_suspend(drm_dev); + return i915_drm_suspend(dev); } -static int i915_pm_suspend_late(struct device *dev) +static int i915_pm_suspend_late(struct device *kdev) { - struct drm_device *drm_dev = &dev_to_i915(dev)->drm; + struct drm_device *dev = &kdev_to_i915(kdev)->drm; /* * We have a suspend ordering issue with the snd-hda driver also @@ -1875,57 +1833,57 @@ static int i915_pm_suspend_late(struct device *dev) * FIXME: This should be solved with a special hdmi sink device or * similar so that power domains can be employed. */ - if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - return i915_drm_suspend_late(drm_dev, false); + return i915_drm_suspend_late(dev, false); } -static int i915_pm_poweroff_late(struct device *dev) +static int i915_pm_poweroff_late(struct device *kdev) { - struct drm_device *drm_dev = &dev_to_i915(dev)->drm; + struct drm_device *dev = &kdev_to_i915(kdev)->drm; - if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - return i915_drm_suspend_late(drm_dev, true); + return i915_drm_suspend_late(dev, true); } -static int i915_pm_resume_early(struct device *dev) +static int i915_pm_resume_early(struct device *kdev) { - struct drm_device *drm_dev = &dev_to_i915(dev)->drm; + struct drm_device *dev = &kdev_to_i915(kdev)->drm; - if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - return i915_drm_resume_early(drm_dev); + return i915_drm_resume_early(dev); } -static int i915_pm_resume(struct device *dev) +static int i915_pm_resume(struct device *kdev) { - struct drm_device *drm_dev = &dev_to_i915(dev)->drm; + struct drm_device *dev = &kdev_to_i915(kdev)->drm; - if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - return i915_drm_resume(drm_dev); + return i915_drm_resume(dev); } /* freeze: before creating the hibernation_image */ -static int i915_pm_freeze(struct device *dev) +static int i915_pm_freeze(struct device *kdev) { - return i915_pm_suspend(dev); + return i915_pm_suspend(kdev); } -static int i915_pm_freeze_late(struct device *dev) +static int i915_pm_freeze_late(struct device *kdev) { int ret; - ret = i915_pm_suspend_late(dev); + ret = i915_pm_suspend_late(kdev); if (ret) return ret; - ret = i915_gem_freeze_late(dev_to_i915(dev)); + ret = i915_gem_freeze_late(kdev_to_i915(kdev)); if (ret) return ret; @@ -1933,25 +1891,25 @@ static int i915_pm_freeze_late(struct device *dev) } /* thaw: called after creating the hibernation image, but before turning off. */ -static int i915_pm_thaw_early(struct device *dev) +static int i915_pm_thaw_early(struct device *kdev) { - return i915_pm_resume_early(dev); + return i915_pm_resume_early(kdev); } -static int i915_pm_thaw(struct device *dev) +static int i915_pm_thaw(struct device *kdev) { - return i915_pm_resume(dev); + return i915_pm_resume(kdev); } /* restore: called after loading the hibernation image. */ -static int i915_pm_restore_early(struct device *dev) +static int i915_pm_restore_early(struct device *kdev) { - return i915_pm_resume_early(dev); + return i915_pm_resume_early(kdev); } -static int i915_pm_restore(struct device *dev) +static int i915_pm_restore(struct device *kdev) { - return i915_pm_resume(dev); + return i915_pm_resume(kdev); } /* @@ -2313,9 +2271,9 @@ static int vlv_resume_prepare(struct drm_i915_private *dev_priv, return ret; } -static int intel_runtime_suspend(struct device *device) +static int intel_runtime_suspend(struct device *kdev) { - struct pci_dev *pdev = to_pci_dev(device); + struct pci_dev *pdev = to_pci_dev(kdev); struct drm_device *dev = pci_get_drvdata(pdev); struct drm_i915_private *dev_priv = to_i915(dev); int ret; @@ -2341,7 +2299,7 @@ static int intel_runtime_suspend(struct device *device) * Bump the expiration timestamp, otherwise the suspend won't * be rescheduled. */ - pm_runtime_mark_last_busy(device); + pm_runtime_mark_last_busy(kdev); return -EAGAIN; } @@ -2420,9 +2378,9 @@ static int intel_runtime_suspend(struct device *device) return 0; } -static int intel_runtime_resume(struct device *device) +static int intel_runtime_resume(struct device *kdev) { - struct pci_dev *pdev = to_pci_dev(device); + struct pci_dev *pdev = to_pci_dev(kdev); struct drm_device *dev = pci_get_drvdata(pdev); struct drm_i915_private *dev_priv = to_i915(dev); int ret = 0; @@ -2462,7 +2420,6 @@ static int intel_runtime_resume(struct device *device) * we can do is to hope that things will still work (and disable RPM). */ i915_gem_init_swizzling(dev); - gen6_update_ring_freq(dev_priv); intel_runtime_pm_enable_interrupts(dev_priv); @@ -2618,6 +2575,7 @@ static struct drm_driver driver = { .postclose = i915_driver_postclose, .set_busid = drm_pci_set_busid, + .gem_close_object = i915_gem_close_object, .gem_free_object = i915_gem_free_object, .gem_vm_ops = &i915_gem_vm_ops, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 21f9390..4dd307e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -61,6 +61,7 @@ #include "i915_gem.h" #include "i915_gem_gtt.h" #include "i915_gem_render_state.h" +#include "i915_gem_request.h" #include "intel_gvt.h" @@ -69,7 +70,7 @@ #define DRIVER_NAME "i915" #define DRIVER_DESC "Intel Graphics" -#define DRIVER_DATE "20160711" +#define DRIVER_DATE "20160919" #undef WARN_ON /* Many gcc seem to no see through this and fall over :( */ @@ -401,7 +402,7 @@ struct drm_i915_file_private { unsigned boosts; } rps; - unsigned int bsd_ring; + unsigned int bsd_engine; }; /* Used by dp and fdi links */ @@ -431,8 +432,6 @@ void intel_link_compute_m_n(int bpp, int nlanes, #define DRIVER_MINOR 6 #define DRIVER_PATCHLEVEL 0 -#define WATCH_LISTS 0 - struct opregion_header; struct opregion_acpi; struct opregion_swsci; @@ -456,15 +455,21 @@ struct intel_opregion { struct intel_overlay; struct intel_overlay_error_state; -#define I915_FENCE_REG_NONE -1 -#define I915_MAX_NUM_FENCES 32 -/* 32 fences + sign bit for FENCE_REG_NONE */ -#define I915_MAX_NUM_FENCE_BITS 6 - struct drm_i915_fence_reg { - struct list_head lru_list; - struct drm_i915_gem_object *obj; + struct list_head link; + struct drm_i915_private *i915; + struct i915_vma *vma; int pin_count; + int id; + /** + * Whether the tiling parameters for the currently + * associated fence register have changed. Note that + * for the purposes of tracking tiling changes we also + * treat the unfenced register, the register slot that + * the object occupies whilst it executes a fenced + * command (such as BLT on gen2/3), as a "fence". + */ + bool dirty; }; struct sdvo_device_mapping { @@ -476,130 +481,6 @@ struct sdvo_device_mapping { u8 ddc_pin; }; -struct intel_display_error_state; - -struct drm_i915_error_state { - struct kref ref; - struct timeval time; - - char error_msg[128]; - bool simulated; - int iommu; - u32 reset_count; - u32 suspend_count; - - /* Generic register state */ - u32 eir; - u32 pgtbl_er; - u32 ier; - u32 gtier[4]; - u32 ccid; - u32 derrmr; - u32 forcewake; - u32 error; /* gen6+ */ - u32 err_int; /* gen7 */ - u32 fault_data0; /* gen8, gen9 */ - u32 fault_data1; /* gen8, gen9 */ - u32 done_reg; - u32 gac_eco; - u32 gam_ecochk; - u32 gab_ctl; - u32 gfx_mode; - u32 extra_instdone[I915_NUM_INSTDONE_REG]; - u64 fence[I915_MAX_NUM_FENCES]; - struct intel_overlay_error_state *overlay; - struct intel_display_error_state *display; - struct drm_i915_error_object *semaphore_obj; - - struct drm_i915_error_ring { - bool valid; - /* Software tracked state */ - bool waiting; - int num_waiters; - int hangcheck_score; - enum intel_ring_hangcheck_action hangcheck_action; - int num_requests; - - /* our own tracking of ring head and tail */ - u32 cpu_ring_head; - u32 cpu_ring_tail; - - u32 last_seqno; - u32 semaphore_seqno[I915_NUM_ENGINES - 1]; - - /* Register state */ - u32 start; - u32 tail; - u32 head; - u32 ctl; - u32 hws; - u32 ipeir; - u32 ipehr; - u32 instdone; - u32 bbstate; - u32 instpm; - u32 instps; - u32 seqno; - u64 bbaddr; - u64 acthd; - u32 fault_reg; - u64 faddr; - u32 rc_psmi; /* sleep state */ - u32 semaphore_mboxes[I915_NUM_ENGINES - 1]; - - struct drm_i915_error_object { - int page_count; - u64 gtt_offset; - u32 *pages[0]; - } *ringbuffer, *batchbuffer, *wa_batchbuffer, *ctx, *hws_page; - - struct drm_i915_error_object *wa_ctx; - - struct drm_i915_error_request { - long jiffies; - u32 seqno; - u32 tail; - } *requests; - - struct drm_i915_error_waiter { - char comm[TASK_COMM_LEN]; - pid_t pid; - u32 seqno; - } *waiters; - - struct { - u32 gfx_mode; - union { - u64 pdp[4]; - u32 pp_dir_base; - }; - } vm_info; - - pid_t pid; - char comm[TASK_COMM_LEN]; - } ring[I915_NUM_ENGINES]; - - struct drm_i915_error_buffer { - u32 size; - u32 name; - u32 rseqno[I915_NUM_ENGINES], wseqno; - u64 gtt_offset; - u32 read_domains; - u32 write_domain; - s32 fence_reg:I915_MAX_NUM_FENCE_BITS; - s32 pinned:2; - u32 tiling:2; - u32 dirty:1; - u32 purgeable:1; - u32 userptr:1; - s32 ring:4; - u32 cache_level:3; - } **active_bo, **pinned_bo; - - u32 *active_bo_count, *pinned_bo_count; - u32 vm_count; -}; - struct intel_connector; struct intel_encoder; struct intel_crtc_state; @@ -629,8 +510,12 @@ struct drm_i915_display_funcs { struct intel_initial_plane_config *); int (*crtc_compute_clock)(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state); - void (*crtc_enable)(struct drm_crtc *crtc); - void (*crtc_disable)(struct drm_crtc *crtc); + void (*crtc_enable)(struct intel_crtc_state *pipe_config, + struct drm_atomic_state *old_state); + void (*crtc_disable)(struct intel_crtc_state *old_crtc_state, + struct drm_atomic_state *old_state); + void (*update_crtcs)(struct drm_atomic_state *state, + unsigned int *crtc_vblank_mask); void (*audio_codec_enable)(struct drm_connector *connector, struct intel_encoder *encoder, const struct drm_display_mode *adjusted_mode); @@ -694,8 +579,6 @@ struct intel_uncore_funcs { uint16_t val, bool trace); void (*mmio_writel)(struct drm_i915_private *dev_priv, i915_reg_t r, uint32_t val, bool trace); - void (*mmio_writeq)(struct drm_i915_private *dev_priv, i915_reg_t r, - uint64_t val, bool trace); }; struct intel_uncore { @@ -756,7 +639,7 @@ struct intel_csr { func(is_i915g) sep \ func(is_i945gm) sep \ func(is_g33) sep \ - func(need_gfx_hws) sep \ + func(hws_needs_physical) sep \ func(is_g4x) sep \ func(is_pineview) sep \ func(is_broadwater) sep \ @@ -771,6 +654,19 @@ struct intel_csr { func(is_kabylake) sep \ func(is_preliminary) sep \ func(has_fbc) sep \ + func(has_psr) sep \ + func(has_runtime_pm) sep \ + func(has_csr) sep \ + func(has_resource_streamer) sep \ + func(has_rc6) sep \ + func(has_rc6p) sep \ + func(has_dp_mst) sep \ + func(has_gmbus_irq) sep \ + func(has_hw_contexts) sep \ + func(has_logical_ring_contexts) sep \ + func(has_l3_dpf) sep \ + func(has_gmch_display) sep \ + func(has_guc) sep \ func(has_pipe_cxsr) sep \ func(has_hotplug) sep \ func(cursor_needs_physical) sep \ @@ -786,6 +682,24 @@ struct intel_csr { #define DEFINE_FLAG(name) u8 name:1 #define SEP_SEMICOLON ; +struct sseu_dev_info { + u8 slice_mask; + u8 subslice_mask; + u8 eu_total; + u8 eu_per_subslice; + u8 min_eu_in_pool; + /* For each slice, which subslice(s) has(have) 7 EUs (bitfield)? */ + u8 subslice_7eu[3]; + u8 has_slice_pg:1; + u8 has_subslice_pg:1; + u8 has_eu_pg:1; +}; + +static inline unsigned int sseu_subslice_total(const struct sseu_dev_info *sseu) +{ + return hweight8(sseu->slice_mask) * hweight8(sseu->subslice_mask); +} + struct intel_device_info { u32 display_mmio_offset; u16 device_id; @@ -794,7 +708,9 @@ struct intel_device_info { u8 gen; u16 gen_mask; u8 ring_mask; /* Rings supported by the HW */ + u8 num_rings; DEV_INFO_FOR_EACH_FLAG(DEFINE_FLAG, SEP_SEMICOLON); + u16 ddb_size; /* in blocks */ /* Register offsets for the various display pipes and transcoders */ int pipe_offsets[I915_MAX_TRANSCODERS]; int trans_offsets[I915_MAX_TRANSCODERS]; @@ -802,17 +718,7 @@ struct intel_device_info { int cursor_offsets[I915_MAX_PIPES]; /* Slice/subslice/EU info */ - u8 slice_total; - u8 subslice_total; - u8 subslice_per_slice; - u8 eu_total; - u8 eu_per_subslice; - u8 min_eu_in_pool; - /* For each slice, which subslice(s) has(have) 7 EUs (bitfield)? */ - u8 subslice_7eu[3]; - u8 has_slice_pg:1; - u8 has_subslice_pg:1; - u8 has_eu_pg:1; + struct sseu_dev_info sseu; struct color_luts { u16 degamma_lut_size; @@ -823,6 +729,134 @@ struct intel_device_info { #undef DEFINE_FLAG #undef SEP_SEMICOLON +struct intel_display_error_state; + +struct drm_i915_error_state { + struct kref ref; + struct timeval time; + + char error_msg[128]; + bool simulated; + int iommu; + u32 reset_count; + u32 suspend_count; + struct intel_device_info device_info; + + /* Generic register state */ + u32 eir; + u32 pgtbl_er; + u32 ier; + u32 gtier[4]; + u32 ccid; + u32 derrmr; + u32 forcewake; + u32 error; /* gen6+ */ + u32 err_int; /* gen7 */ + u32 fault_data0; /* gen8, gen9 */ + u32 fault_data1; /* gen8, gen9 */ + u32 done_reg; + u32 gac_eco; + u32 gam_ecochk; + u32 gab_ctl; + u32 gfx_mode; + u32 extra_instdone[I915_NUM_INSTDONE_REG]; + u64 fence[I915_MAX_NUM_FENCES]; + struct intel_overlay_error_state *overlay; + struct intel_display_error_state *display; + struct drm_i915_error_object *semaphore; + + struct drm_i915_error_engine { + int engine_id; + /* Software tracked state */ + bool waiting; + int num_waiters; + int hangcheck_score; + enum intel_engine_hangcheck_action hangcheck_action; + struct i915_address_space *vm; + int num_requests; + + /* our own tracking of ring head and tail */ + u32 cpu_ring_head; + u32 cpu_ring_tail; + + u32 last_seqno; + u32 semaphore_seqno[I915_NUM_ENGINES - 1]; + + /* Register state */ + u32 start; + u32 tail; + u32 head; + u32 ctl; + u32 mode; + u32 hws; + u32 ipeir; + u32 ipehr; + u32 instdone; + u32 bbstate; + u32 instpm; + u32 instps; + u32 seqno; + u64 bbaddr; + u64 acthd; + u32 fault_reg; + u64 faddr; + u32 rc_psmi; /* sleep state */ + u32 semaphore_mboxes[I915_NUM_ENGINES - 1]; + + struct drm_i915_error_object { + int page_count; + u64 gtt_offset; + u64 gtt_size; + u32 *pages[0]; + } *ringbuffer, *batchbuffer, *wa_batchbuffer, *ctx, *hws_page; + + struct drm_i915_error_object *wa_ctx; + + struct drm_i915_error_request { + long jiffies; + pid_t pid; + u32 seqno; + u32 head; + u32 tail; + } *requests; + + struct drm_i915_error_waiter { + char comm[TASK_COMM_LEN]; + pid_t pid; + u32 seqno; + } *waiters; + + struct { + u32 gfx_mode; + union { + u64 pdp[4]; + u32 pp_dir_base; + }; + } vm_info; + + pid_t pid; + char comm[TASK_COMM_LEN]; + } engine[I915_NUM_ENGINES]; + + struct drm_i915_error_buffer { + u32 size; + u32 name; + u32 rseqno[I915_NUM_ENGINES], wseqno; + u64 gtt_offset; + u32 read_domains; + u32 write_domain; + s32 fence_reg:I915_MAX_NUM_FENCE_BITS; + u32 tiling:2; + u32 dirty:1; + u32 purgeable:1; + u32 userptr:1; + s32 engine:4; + u32 cache_level:3; + } *active_bo[I915_NUM_ENGINES], *pinned_bo; + u32 active_bo_count[I915_NUM_ENGINES], pinned_bo_count; + struct i915_address_space *active_vm[I915_NUM_ENGINES]; +}; + enum i915_cache_level { I915_CACHE_NONE = 0, I915_CACHE_LLC, /* also used for snoopable memory on non-LLC */ @@ -879,22 +913,23 @@ struct i915_gem_context { struct drm_i915_private *i915; struct drm_i915_file_private *file_priv; struct i915_hw_ppgtt *ppgtt; + struct pid *pid; struct i915_ctx_hang_stats hang_stats; - /* Unique identifier for this context, used by the hw for tracking */ unsigned long flags; #define CONTEXT_NO_ZEROMAP BIT(0) #define CONTEXT_NO_ERROR_CAPTURE BIT(1) - unsigned hw_id; + + /* Unique identifier for this context, used by the hw for tracking */ + unsigned int hw_id; u32 user_handle; u32 ggtt_alignment; struct intel_context { - struct drm_i915_gem_object *state; - struct intel_ringbuffer *ringbuf; - struct i915_vma *lrc_vma; + struct i915_vma *state; + struct intel_ring *ring; uint32_t *lrc_reg_state; u64 lrc_desc; int pin_count; @@ -908,6 +943,7 @@ struct i915_gem_context { struct list_head link; u8 remap_slice; + bool closed:1; }; enum fb_op_origin { @@ -1061,13 +1097,6 @@ struct intel_gmbus { struct i915_suspend_saved_registers { u32 saveDSPARB; - u32 saveLVDS; - u32 savePP_ON_DELAYS; - u32 savePP_OFF_DELAYS; - u32 savePP_ON; - u32 savePP_OFF; - u32 savePP_CONTROL; - u32 savePP_DIVISOR; u32 saveFBC_CONTROL; u32 saveCACHE_MODE_0; u32 saveMI_ARB_STATE; @@ -1156,6 +1185,7 @@ struct intel_gen6_power_mgmt { bool interrupts_enabled; u32 pm_iir; + /* PM interrupt bits that should never be masked */ u32 pm_intr_keep; /* Frequencies are stored in potentially platform dependent multiples. @@ -1173,6 +1203,7 @@ struct intel_gen6_power_mgmt { u8 max_freq_softlimit; /* Max frequency permitted by the driver */ u8 max_freq; /* Maximum frequency, RP0 if not overclocking */ u8 min_freq; /* AKA RPn. Minimum frequency */ + u8 boost_freq; /* Frequency to request when wait boosting */ u8 idle_freq; /* Frequency to request when we are idle */ u8 efficient_freq; /* AKA RPe. Pre-determined balanced frequency */ u8 rp1_freq; /* "less than" RP0 power/freqency */ @@ -1190,11 +1221,9 @@ struct intel_gen6_power_mgmt { bool client_boost; bool enabled; - struct delayed_work delayed_resume_work; + struct delayed_work autoenable_work; unsigned boosts; - struct intel_rps_client semaphores, mmioflips; - /* manual wa residency calculations */ struct intel_rps_ei up_ei, down_ei; @@ -1319,7 +1348,6 @@ struct i915_gem_mm { struct notifier_block oom_notifier; struct notifier_block vmap_notifier; struct shrinker shrinker; - bool shrinker_no_lock_stealing; /** LRU list of objects with fence regs on them. */ struct list_head fence_list; @@ -1331,7 +1359,7 @@ struct i915_gem_mm { bool interruptible; /* the indicator for dispatch video commands on two BSD rings */ - unsigned int bsd_ring_dispatch_index; + atomic_t bsd_engine_dispatch_index; /** Bit 6 swizzling required for X tiling */ uint32_t bit_6_swizzle_x; @@ -1379,9 +1407,10 @@ struct i915_gpu_error { * State variable controlling the reset flow and count * * This is a counter which gets incremented when reset is triggered, - * and again when reset has been handled. So odd values (lowest bit set) - * means that reset is in progress and even values that - * (reset_counter >> 1):th reset was successfully completed. + * + * Before the reset commences, the I915_RESET_IN_PROGRESS bit is set + * meaning that any waiters holding onto the struct_mutex should + * relinquish the lock immediately in order for the reset to start. * * If reset is not completed succesfully, the I915_WEDGE bit is * set meaning that hardware is terminally sour and there is no @@ -1396,10 +1425,11 @@ struct i915_gpu_error { * naturally enforces the correct ordering between the bail-out of the * waiter and the gpu reset work code. */ - atomic_t reset_counter; + unsigned long reset_count; -#define I915_RESET_IN_PROGRESS_FLAG 1 -#define I915_WEDGED (1 << 31) + unsigned long flags; +#define I915_RESET_IN_PROGRESS 0 +#define I915_WEDGED (BITS_PER_LONG - 1) /** * Waitqueue to signal when a hang is detected. Used to for waiters @@ -1670,7 +1700,7 @@ struct intel_pipe_crc { }; struct i915_frontbuffer_tracking { - struct mutex lock; + spinlock_t lock; /* * Tracking bits for delayed frontbuffer flushing du to gpu activity or @@ -1705,18 +1735,6 @@ struct i915_virtual_gpu { bool active; }; -struct i915_execbuffer_params { - struct drm_device *dev; - struct drm_file *file; - uint32_t dispatch_flags; - uint32_t args_batch_start_offset; - uint64_t batch_obj_vm_offset; - struct intel_engine_cs *engine; - struct drm_i915_gem_object *batch_obj; - struct i915_gem_context *ctx; - struct drm_i915_gem_request *request; -}; - /* used in computing the new watermarks state */ struct intel_wm_config { unsigned int num_pipes_active; @@ -1763,13 +1781,15 @@ struct drm_i915_private { uint32_t psr_mmio_base; + uint32_t pps_mmio_base; + wait_queue_head_t gmbus_wait_queue; struct pci_dev *bridge_dev; struct i915_gem_context *kernel_context; struct intel_engine_cs engine[I915_NUM_ENGINES]; - struct drm_i915_gem_object *semaphore_obj; - uint32_t last_seqno, next_seqno; + struct i915_vma *semaphore; + u32 next_seqno; struct drm_dma_handle *status_page_dmah; struct resource mch_res; @@ -1854,6 +1874,7 @@ struct drm_i915_private { enum modeset_restore modeset_restore; struct mutex modeset_restore_lock; struct drm_atomic_state *modeset_restore_state; + struct drm_modeset_acquire_ctx reset_ctx; struct list_head vm_list; /* Global list of all address spaces */ struct i915_ggtt ggtt; /* VM representing the global address space */ @@ -1962,6 +1983,13 @@ struct drm_i915_private { struct i915_suspend_saved_registers regfile; struct vlv_s0ix_state vlv_s0ix_state; + enum { + I915_SKL_SAGV_UNKNOWN = 0, + I915_SKL_SAGV_DISABLED, + I915_SKL_SAGV_ENABLED, + I915_SKL_SAGV_NOT_CONTROLLED + } skl_sagv_status; + struct { /* * Raw watermark latency values: @@ -2016,12 +2044,8 @@ struct drm_i915_private { /* Abstract the submission mechanism (legacy ringbuffer or execlists) away */ struct { - int (*execbuf_submit)(struct i915_execbuffer_params *params, - struct drm_i915_gem_execbuffer2 *args, - struct list_head *vmas); - int (*init_engines)(struct drm_device *dev); + void (*resume)(struct drm_i915_private *); void (*cleanup_engine)(struct intel_engine_cs *engine); - void (*stop_engine)(struct intel_engine_cs *engine); /** * Is the GPU currently considered idle, or busy executing @@ -2068,9 +2092,9 @@ static inline struct drm_i915_private *to_i915(const struct drm_device *dev) return container_of(dev, struct drm_i915_private, drm); } -static inline struct drm_i915_private *dev_to_i915(struct device *dev) +static inline struct drm_i915_private *kdev_to_i915(struct device *kdev) { - return to_i915(dev_get_drvdata(dev)); + return to_i915(dev_get_drvdata(kdev)); } static inline struct drm_i915_private *guc_to_i915(struct intel_guc *guc) @@ -2093,13 +2117,16 @@ static inline struct drm_i915_private *guc_to_i915(struct intel_guc *guc) for_each_if (((id__) = (engine__)->id, \ intel_engine_initialized(engine__))) +#define __mask_next_bit(mask) ({ \ + int __idx = ffs(mask) - 1; \ + mask &= ~BIT(__idx); \ + __idx; \ +}) + /* Iterator over subset of engines selected by mask */ -#define for_each_engine_masked(engine__, dev_priv__, mask__) \ - for ((engine__) = &(dev_priv__)->engine[0]; \ - (engine__) < &(dev_priv__)->engine[I915_NUM_ENGINES]; \ - (engine__)++) \ - for_each_if (((mask__) & intel_engine_flag(engine__)) && \ - intel_engine_initialized(engine__)) +#define for_each_engine_masked(engine__, dev_priv__, mask__, tmp__) \ + for (tmp__ = mask__ & INTEL_INFO(dev_priv__)->ring_mask; \ + tmp__ ? (engine__ = &(dev_priv__)->engine[__mask_next_bit(tmp__)]), 1 : 0; ) enum hdmi_force_audio { HDMI_AUDIO_OFF_DVI = -2, /* no aux data for HDMI-DVI converter */ @@ -2144,8 +2171,6 @@ struct drm_i915_gem_object_ops { */ #define INTEL_MAX_SPRITE_BITS_PER_PIPE 5 #define INTEL_FRONTBUFFER_BITS_PER_PIPE 8 -#define INTEL_FRONTBUFFER_BITS \ - (INTEL_FRONTBUFFER_BITS_PER_PIPE * I915_MAX_PIPES) #define INTEL_FRONTBUFFER_PRIMARY(pipe) \ (1 << (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))) #define INTEL_FRONTBUFFER_CURSOR(pipe) \ @@ -2169,18 +2194,21 @@ struct drm_i915_gem_object { struct drm_mm_node *stolen; struct list_head global_list; - struct list_head engine_list[I915_NUM_ENGINES]; /** Used in execbuf to temporarily hold a ref */ struct list_head obj_exec_link; struct list_head batch_pool_link; + unsigned long flags; /** * This is set if the object is on the active lists (has pending * rendering and so a non-zero seqno), and is not set if it i s on * inactive (ready to be unbound) list. */ - unsigned int active:I915_NUM_ENGINES; +#define I915_BO_ACTIVE_SHIFT 0 +#define I915_BO_ACTIVE_MASK ((1 << I915_NUM_ENGINES) - 1) +#define __I915_BO_ACTIVE(bo) \ + ((READ_ONCE((bo)->flags) >> I915_BO_ACTIVE_SHIFT) & I915_BO_ACTIVE_MASK) /** * This is set if the object has been written to since last bound @@ -2189,37 +2217,11 @@ struct drm_i915_gem_object { unsigned int dirty:1; /** - * Fence register bits (if any) for this object. Will be set - * as needed when mapped into the GTT. - * Protected by dev->struct_mutex. - */ - signed int fence_reg:I915_MAX_NUM_FENCE_BITS; - - /** * Advice: are the backing pages purgeable? */ unsigned int madv:2; /** - * Current tiling mode for the object. - */ - unsigned int tiling_mode:2; - /** - * Whether the tiling parameters for the currently associated fence - * register have changed. Note that for the purposes of tracking - * tiling changes we also treat the unfenced register, the register - * slot that the object occupies whilst it executes a fenced - * command (such as BLT on gen2/3), as a "fence". - */ - unsigned int fence_dirty:1; - - /** - * Is the object at the current location in the gtt mappable and - * fenceable? Used to avoid costly recalculations. - */ - unsigned int map_and_fenceable:1; - - /** * Whether the current gtt mapping needs to be mappable (and isn't just * mappable by accident). Track pin and fault separate for a more * accurate mappable working set. @@ -2234,9 +2236,17 @@ struct drm_i915_gem_object { unsigned int cache_level:3; unsigned int cache_dirty:1; - unsigned int frontbuffer_bits:INTEL_FRONTBUFFER_BITS; + atomic_t frontbuffer_bits; + unsigned int frontbuffer_ggtt_origin; /* write once */ + + /** Current tiling stride for the object, if it's tiled. */ + unsigned int tiling_and_stride; +#define FENCE_MINIMUM_STRIDE 128 /* See i915_tiling_ok() */ +#define TILING_MASK (FENCE_MINIMUM_STRIDE-1) +#define STRIDE_MASK (~TILING_MASK) - unsigned int has_wc_mmap; + /** Count of VMA actually bound by this object */ + unsigned int bind_count; unsigned int pin_display; struct sg_table *pages; @@ -2256,14 +2266,9 @@ struct drm_i915_gem_object { * requests on one ring where the write request is older than the * read request. This allows for the CPU to read from an active * buffer by only waiting for the write to complete. - * */ - struct drm_i915_gem_request *last_read_req[I915_NUM_ENGINES]; - struct drm_i915_gem_request *last_write_req; - /** Breadcrumb of last fenced GPU access to the buffer. */ - struct drm_i915_gem_request *last_fenced_req; - - /** Current tiling stride for the object, if it's tiled. */ - uint32_t stride; + */ + struct i915_gem_active last_read[I915_NUM_ENGINES]; + struct i915_gem_active last_write; /** References from framebuffers, locks out tiling changes. */ unsigned long framebuffer_references; @@ -2287,7 +2292,56 @@ struct drm_i915_gem_object { } userptr; }; }; -#define to_intel_bo(x) container_of(x, struct drm_i915_gem_object, base) + +static inline struct drm_i915_gem_object * +to_intel_bo(struct drm_gem_object *gem) +{ + /* Assert that to_intel_bo(NULL) == NULL */ + BUILD_BUG_ON(offsetof(struct drm_i915_gem_object, base)); + + return container_of(gem, struct drm_i915_gem_object, base); +} + +static inline struct drm_i915_gem_object * +i915_gem_object_lookup(struct drm_file *file, u32 handle) +{ + return to_intel_bo(drm_gem_object_lookup(file, handle)); +} + +__deprecated +extern struct drm_gem_object * +drm_gem_object_lookup(struct drm_file *file, u32 handle); + +__attribute__((nonnull)) +static inline struct drm_i915_gem_object * +i915_gem_object_get(struct drm_i915_gem_object *obj) +{ + drm_gem_object_reference(&obj->base); + return obj; +} + +__deprecated +extern void drm_gem_object_reference(struct drm_gem_object *); + +__attribute__((nonnull)) +static inline void +i915_gem_object_put(struct drm_i915_gem_object *obj) +{ + drm_gem_object_unreference(&obj->base); +} + +__deprecated +extern void drm_gem_object_unreference(struct drm_gem_object *); + +__attribute__((nonnull)) +static inline void +i915_gem_object_put_unlocked(struct drm_i915_gem_object *obj) +{ + drm_gem_object_unreference_unlocked(&obj->base); +} + +__deprecated +extern void drm_gem_object_unreference_unlocked(struct drm_gem_object *); static inline bool i915_gem_object_has_struct_page(const struct drm_i915_gem_object *obj) @@ -2295,6 +2349,67 @@ i915_gem_object_has_struct_page(const struct drm_i915_gem_object *obj) return obj->ops->flags & I915_GEM_OBJECT_HAS_STRUCT_PAGE; } +static inline unsigned long +i915_gem_object_get_active(const struct drm_i915_gem_object *obj) +{ + return (obj->flags >> I915_BO_ACTIVE_SHIFT) & I915_BO_ACTIVE_MASK; +} + +static inline bool +i915_gem_object_is_active(const struct drm_i915_gem_object *obj) +{ + return i915_gem_object_get_active(obj); +} + +static inline void +i915_gem_object_set_active(struct drm_i915_gem_object *obj, int engine) +{ + obj->flags |= BIT(engine + I915_BO_ACTIVE_SHIFT); +} + +static inline void +i915_gem_object_clear_active(struct drm_i915_gem_object *obj, int engine) +{ + obj->flags &= ~BIT(engine + I915_BO_ACTIVE_SHIFT); +} + +static inline bool +i915_gem_object_has_active_engine(const struct drm_i915_gem_object *obj, + int engine) +{ + return obj->flags & BIT(engine + I915_BO_ACTIVE_SHIFT); +} + +static inline unsigned int +i915_gem_object_get_tiling(struct drm_i915_gem_object *obj) +{ + return obj->tiling_and_stride & TILING_MASK; +} + +static inline bool +i915_gem_object_is_tiled(struct drm_i915_gem_object *obj) +{ + return i915_gem_object_get_tiling(obj) != I915_TILING_NONE; +} + +static inline unsigned int +i915_gem_object_get_stride(struct drm_i915_gem_object *obj) +{ + return obj->tiling_and_stride & STRIDE_MASK; +} + +static inline struct i915_vma *i915_vma_get(struct i915_vma *vma) +{ + i915_gem_object_get(vma->obj); + return vma; +} + +static inline void i915_vma_put(struct i915_vma *vma) +{ + lockdep_assert_held(&vma->vm->dev->struct_mutex); + i915_gem_object_put(vma->obj); +} + /* * Optimised SGL iterator for GEM objects */ @@ -2365,171 +2480,6 @@ static inline struct scatterlist *__sg_next(struct scatterlist *sg) (((__iter).curr += PAGE_SIZE) < (__iter).max) || \ ((__iter) = __sgt_iter(__sg_next((__iter).sgp), false), 0)) -/** - * Request queue structure. - * - * The request queue allows us to note sequence numbers that have been emitted - * and may be associated with active buffers to be retired. - * - * By keeping this list, we can avoid having to do questionable sequence - * number comparisons on buffer last_read|write_seqno. It also allows an - * emission time to be associated with the request for tracking how far ahead - * of the GPU the submission is. - * - * The requests are reference counted, so upon creation they should have an - * initial reference taken using kref_init - */ -struct drm_i915_gem_request { - struct kref ref; - - /** On Which ring this request was generated */ - struct drm_i915_private *i915; - struct intel_engine_cs *engine; - struct intel_signal_node signaling; - - /** GEM sequence number associated with the previous request, - * when the HWS breadcrumb is equal to this the GPU is processing - * this request. - */ - u32 previous_seqno; - - /** GEM sequence number associated with this request, - * when the HWS breadcrumb is equal or greater than this the GPU - * has finished processing this request. - */ - u32 seqno; - - /** Position in the ringbuffer of the start of the request */ - u32 head; - - /** - * Position in the ringbuffer of the start of the postfix. - * This is required to calculate the maximum available ringbuffer - * space without overwriting the postfix. - */ - u32 postfix; - - /** Position in the ringbuffer of the end of the whole request */ - u32 tail; - - /** Preallocate space in the ringbuffer for the emitting the request */ - u32 reserved_space; - - /** - * Context and ring buffer related to this request - * Contexts are refcounted, so when this request is associated with a - * context, we must increment the context's refcount, to guarantee that - * it persists while any request is linked to it. Requests themselves - * are also refcounted, so the request will only be freed when the last - * reference to it is dismissed, and the code in - * i915_gem_request_free() will then decrement the refcount on the - * context. - */ - struct i915_gem_context *ctx; - struct intel_ringbuffer *ringbuf; - - /** - * Context related to the previous request. - * As the contexts are accessed by the hardware until the switch is - * completed to a new context, the hardware may still be writing - * to the context object after the breadcrumb is visible. We must - * not unpin/unbind/prune that object whilst still active and so - * we keep the previous context pinned until the following (this) - * request is retired. - */ - struct i915_gem_context *previous_context; - - /** Batch buffer related to this request if any (used for - error state dump only) */ - struct drm_i915_gem_object *batch_obj; - - /** Time at which this request was emitted, in jiffies. */ - unsigned long emitted_jiffies; - - /** global list entry for this request */ - struct list_head list; - - struct drm_i915_file_private *file_priv; - /** file_priv list entry for this request */ - struct list_head client_list; - - /** process identifier submitting this request */ - struct pid *pid; - - /** - * The ELSP only accepts two elements at a time, so we queue - * context/tail pairs on a given queue (ring->execlist_queue) until the - * hardware is available. The queue serves a double purpose: we also use - * it to keep track of the up to 2 contexts currently in the hardware - * (usually one in execution and the other queued up by the GPU): We - * only remove elements from the head of the queue when the hardware - * informs us that an element has been completed. - * - * All accesses to the queue are mediated by a spinlock - * (ring->execlist_lock). - */ - - /** Execlist link in the submission queue.*/ - struct list_head execlist_link; - - /** Execlists no. of times this request has been sent to the ELSP */ - int elsp_submitted; - - /** Execlists context hardware id. */ - unsigned ctx_hw_id; -}; - -struct drm_i915_gem_request * __must_check -i915_gem_request_alloc(struct intel_engine_cs *engine, - struct i915_gem_context *ctx); -void i915_gem_request_free(struct kref *req_ref); -int i915_gem_request_add_to_client(struct drm_i915_gem_request *req, - struct drm_file *file); - -static inline uint32_t -i915_gem_request_get_seqno(struct drm_i915_gem_request *req) -{ - return req ? req->seqno : 0; -} - -static inline struct intel_engine_cs * -i915_gem_request_get_engine(struct drm_i915_gem_request *req) -{ - return req ? req->engine : NULL; -} - -static inline struct drm_i915_gem_request * -i915_gem_request_reference(struct drm_i915_gem_request *req) -{ - if (req) - kref_get(&req->ref); - return req; -} - -static inline void -i915_gem_request_unreference(struct drm_i915_gem_request *req) -{ - kref_put(&req->ref, i915_gem_request_free); -} - -static inline void i915_gem_request_assign(struct drm_i915_gem_request **pdst, - struct drm_i915_gem_request *src) -{ - if (src) - i915_gem_request_reference(src); - - if (*pdst) - i915_gem_request_unreference(*pdst); - - *pdst = src; -} - -/* - * XXX: i915_gem_request_completed should be here but currently needs the - * definition of i915_seqno_passed() which is below. It will be moved in - * a later patch when the call to i915_seqno_passed() is obsoleted... - */ - /* * A command that requires special handling by the command parser. */ @@ -2617,8 +2567,9 @@ struct drm_i915_cmd_descriptor { /* * A table of commands requiring special handling by the command parser. * - * Each ring has an array of tables. Each table consists of an array of command - * descriptors, which must be sorted with command opcodes in ascending order. + * Each engine has an array of tables. Each table consists of an array of + * command descriptors, which must be sorted with command opcodes in + * ascending order. */ struct drm_i915_cmd_table { const struct drm_i915_cmd_descriptor *table; @@ -2636,7 +2587,7 @@ struct drm_i915_cmd_table { BUILD_BUG(); \ __p; \ }) -#define INTEL_INFO(p) (&__I915__(p)->info) +#define INTEL_INFO(p) (&__I915__(p)->info) #define INTEL_GEN(p) (INTEL_INFO(p)->gen) #define INTEL_DEVID(p) (INTEL_INFO(p)->device_id) @@ -2803,10 +2754,10 @@ struct drm_i915_cmd_table { #define HAS_EDRAM(dev) (!!(__I915__(dev)->edram_cap & EDRAM_ENABLED)) #define HAS_WT(dev) ((IS_HASWELL(dev) || IS_BROADWELL(dev)) && \ HAS_EDRAM(dev)) -#define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws) +#define HWS_NEEDS_PHYSICAL(dev) (INTEL_INFO(dev)->hws_needs_physical) -#define HAS_HW_CONTEXTS(dev) (INTEL_INFO(dev)->gen >= 6) -#define HAS_LOGICAL_RING_CONTEXTS(dev) (INTEL_INFO(dev)->gen >= 8) +#define HAS_HW_CONTEXTS(dev) (INTEL_INFO(dev)->has_hw_contexts) +#define HAS_LOGICAL_RING_CONTEXTS(dev) (INTEL_INFO(dev)->has_logical_ring_contexts) #define USES_PPGTT(dev) (i915.enable_ppgtt) #define USES_FULL_PPGTT(dev) (i915.enable_ppgtt >= 2) #define USES_FULL_48BIT_PPGTT(dev) (i915.enable_ppgtt == 3) @@ -2830,7 +2781,7 @@ struct drm_i915_cmd_table { * interrupt source and so prevents the other device from working properly. */ #define HAS_AUX_IRQ(dev) (INTEL_INFO(dev)->gen >= 5) -#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->gen >= 5) +#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->has_gmbus_irq) /* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte * rows, which changed the alignment requirements and fence programming. @@ -2846,38 +2797,27 @@ struct drm_i915_cmd_table { #define HAS_IPS(dev) (IS_HSW_ULT(dev) || IS_BROADWELL(dev)) -#define HAS_DP_MST(dev) (IS_HASWELL(dev) || IS_BROADWELL(dev) || \ - INTEL_INFO(dev)->gen >= 9) +#define HAS_DP_MST(dev) (INTEL_INFO(dev)->has_dp_mst) #define HAS_DDI(dev) (INTEL_INFO(dev)->has_ddi) #define HAS_FPGA_DBG_UNCLAIMED(dev) (INTEL_INFO(dev)->has_fpga_dbg) -#define HAS_PSR(dev) (IS_HASWELL(dev) || IS_BROADWELL(dev) || \ - IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev) || \ - IS_SKYLAKE(dev) || IS_KABYLAKE(dev)) -#define HAS_RUNTIME_PM(dev) (IS_GEN6(dev) || IS_HASWELL(dev) || \ - IS_BROADWELL(dev) || IS_VALLEYVIEW(dev) || \ - IS_CHERRYVIEW(dev) || IS_SKYLAKE(dev) || \ - IS_KABYLAKE(dev) || IS_BROXTON(dev)) -#define HAS_RC6(dev) (INTEL_INFO(dev)->gen >= 6) -#define HAS_RC6p(dev) (IS_GEN6(dev) || IS_IVYBRIDGE(dev)) - -#define HAS_CSR(dev) (IS_GEN9(dev)) +#define HAS_PSR(dev) (INTEL_INFO(dev)->has_psr) +#define HAS_RUNTIME_PM(dev) (INTEL_INFO(dev)->has_runtime_pm) +#define HAS_RC6(dev) (INTEL_INFO(dev)->has_rc6) +#define HAS_RC6p(dev) (INTEL_INFO(dev)->has_rc6p) + +#define HAS_CSR(dev) (INTEL_INFO(dev)->has_csr) /* * For now, anything with a GuC requires uCode loading, and then supports * command submission once loaded. But these are logically independent * properties, so we have separate macros to test them. */ -#define HAS_GUC(dev) (IS_GEN9(dev)) +#define HAS_GUC(dev) (INTEL_INFO(dev)->has_guc) #define HAS_GUC_UCODE(dev) (HAS_GUC(dev)) #define HAS_GUC_SCHED(dev) (HAS_GUC(dev)) -#define HAS_RESOURCE_STREAMER(dev) (IS_HASWELL(dev) || \ - INTEL_INFO(dev)->gen >= 8) - -#define HAS_CORE_RING_FREQ(dev) (INTEL_INFO(dev)->gen >= 6 && \ - !IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev) && \ - !IS_BROXTON(dev)) +#define HAS_RESOURCE_STREAMER(dev) (INTEL_INFO(dev)->has_resource_streamer) #define HAS_POOLED_EU(dev) (INTEL_INFO(dev)->has_pooled_eu) @@ -2905,11 +2845,10 @@ struct drm_i915_cmd_table { #define HAS_PCH_NOP(dev) (INTEL_PCH_TYPE(dev) == PCH_NOP) #define HAS_PCH_SPLIT(dev) (INTEL_PCH_TYPE(dev) != PCH_NONE) -#define HAS_GMCH_DISPLAY(dev) (INTEL_INFO(dev)->gen < 5 || \ - IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) +#define HAS_GMCH_DISPLAY(dev) (INTEL_INFO(dev)->has_gmch_display) /* DPF == dynamic parity feature */ -#define HAS_L3_DPF(dev) (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) +#define HAS_L3_DPF(dev) (INTEL_INFO(dev)->has_l3_dpf) #define NUM_L3_SLICES(dev) (IS_HSW_GT3(dev) ? 2 : HAS_L3_DPF(dev)) #define GT_FREQUENCY_MULTIPLIER 50 @@ -2930,7 +2869,9 @@ extern int i915_suspend_switcheroo(struct drm_device *dev, pm_message_t state); extern int i915_resume_switcheroo(struct drm_device *dev); int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv, - int enable_ppgtt); + int enable_ppgtt); + +bool intel_sanitize_semaphores(struct drm_i915_private *dev_priv, int value); /* i915_drv.c */ void __printf(3, 4) @@ -2946,7 +2887,7 @@ extern long i915_compat_ioctl(struct file *filp, unsigned int cmd, #endif extern int intel_gpu_reset(struct drm_i915_private *dev_priv, u32 engine_mask); extern bool intel_has_gpu_reset(struct drm_i915_private *dev_priv); -extern int i915_reset(struct drm_i915_private *dev_priv); +extern void i915_reset(struct drm_i915_private *dev_priv); extern int intel_guc_reset(struct drm_i915_private *dev_priv); extern void intel_engine_init_hangcheck(struct intel_engine_cs *engine); extern unsigned long i915_chipset_val(struct drm_i915_private *dev_priv); @@ -3107,11 +3048,6 @@ int i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -void i915_gem_execbuffer_move_to_active(struct list_head *vmas, - struct drm_i915_gem_request *req); -int i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params, - struct drm_i915_gem_execbuffer2 *args, - struct list_head *vmas); int i915_gem_execbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_execbuffer2(struct drm_device *dev, void *data, @@ -3150,47 +3086,28 @@ struct drm_i915_gem_object *i915_gem_object_create(struct drm_device *dev, size_t size); struct drm_i915_gem_object *i915_gem_object_create_from_data( struct drm_device *dev, const void *data, size_t size); +void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file); void i915_gem_free_object(struct drm_gem_object *obj); -void i915_gem_vma_destroy(struct i915_vma *vma); - -/* Flags used by pin/bind&friends. */ -#define PIN_MAPPABLE (1<<0) -#define PIN_NONBLOCK (1<<1) -#define PIN_GLOBAL (1<<2) -#define PIN_OFFSET_BIAS (1<<3) -#define PIN_USER (1<<4) -#define PIN_UPDATE (1<<5) -#define PIN_ZONE_4G (1<<6) -#define PIN_HIGH (1<<7) -#define PIN_OFFSET_FIXED (1<<8) -#define PIN_OFFSET_MASK (~4095) -int __must_check -i915_gem_object_pin(struct drm_i915_gem_object *obj, - struct i915_address_space *vm, - uint32_t alignment, - uint64_t flags); -int __must_check + +struct i915_vma * __must_check i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj, const struct i915_ggtt_view *view, - uint32_t alignment, - uint64_t flags); + u64 size, + u64 alignment, + u64 flags); int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level, u32 flags); void __i915_vma_set_map_and_fenceable(struct i915_vma *vma); int __must_check i915_vma_unbind(struct i915_vma *vma); -/* - * BEWARE: Do not use the function below unless you can _absolutely_ - * _guarantee_ VMA in question is _not in use_ anywhere. - */ -int __must_check __i915_vma_unbind_no_wait(struct i915_vma *vma); +void i915_vma_close(struct i915_vma *vma); +void i915_vma_destroy(struct i915_vma *vma); + +int i915_gem_object_unbind(struct drm_i915_gem_object *obj); int i915_gem_object_put_pages(struct drm_i915_gem_object *obj); void i915_gem_release_all_mmaps(struct drm_i915_private *dev_priv); void i915_gem_release_mmap(struct drm_i915_gem_object *obj); -int i915_gem_obj_prepare_shmem_read(struct drm_i915_gem_object *obj, - int *needs_clflush); - int __must_check i915_gem_object_get_pages(struct drm_i915_gem_object *obj); static inline int __sg_page_count(struct scatterlist *sg) @@ -3250,13 +3167,20 @@ static inline void i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj) obj->pages_pin_count--; } +enum i915_map_type { + I915_MAP_WB = 0, + I915_MAP_WC, +}; + /** * i915_gem_object_pin_map - return a contiguous mapping of the entire object * @obj - the object to map into kernel address space + * @type - the type of mapping, used to select pgprot_t * * Calls i915_gem_object_pin_pages() to prevent reaping of the object's * pages and then returns a contiguous mapping of the backing storage into - * the kernel address space. + * the kernel address space. Based on the @type of mapping, the PTE will be + * set to either WriteBack or WriteCombine (via pgprot_t). * * The caller must hold the struct_mutex, and is responsible for calling * i915_gem_object_unpin_map() when the mapping is no longer required. @@ -3264,7 +3188,8 @@ static inline void i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj) * Returns the pointer through which to access the mapped object, or an * ERR_PTR() on error. */ -void *__must_check i915_gem_object_pin_map(struct drm_i915_gem_object *obj); +void *__must_check i915_gem_object_pin_map(struct drm_i915_gem_object *obj, + enum i915_map_type type); /** * i915_gem_object_unpin_map - releases an earlier mapping @@ -3283,122 +3208,73 @@ static inline void i915_gem_object_unpin_map(struct drm_i915_gem_object *obj) i915_gem_object_unpin_pages(obj); } +int i915_gem_obj_prepare_shmem_read(struct drm_i915_gem_object *obj, + unsigned int *needs_clflush); +int i915_gem_obj_prepare_shmem_write(struct drm_i915_gem_object *obj, + unsigned int *needs_clflush); +#define CLFLUSH_BEFORE 0x1 +#define CLFLUSH_AFTER 0x2 +#define CLFLUSH_FLAGS (CLFLUSH_BEFORE | CLFLUSH_AFTER) + +static inline void +i915_gem_obj_finish_shmem_access(struct drm_i915_gem_object *obj) +{ + i915_gem_object_unpin_pages(obj); +} + int __must_check i915_mutex_lock_interruptible(struct drm_device *dev); -int i915_gem_object_sync(struct drm_i915_gem_object *obj, - struct intel_engine_cs *to, - struct drm_i915_gem_request **to_req); void i915_vma_move_to_active(struct i915_vma *vma, - struct drm_i915_gem_request *req); + struct drm_i915_gem_request *req, + unsigned int flags); int i915_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args); int i915_gem_mmap_gtt(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle, uint64_t *offset); +int i915_gem_mmap_gtt_version(void); void i915_gem_track_fb(struct drm_i915_gem_object *old, struct drm_i915_gem_object *new, unsigned frontbuffer_bits); -/** - * Returns true if seq1 is later than seq2. - */ -static inline bool -i915_seqno_passed(uint32_t seq1, uint32_t seq2) -{ - return (int32_t)(seq1 - seq2) >= 0; -} - -static inline bool i915_gem_request_started(const struct drm_i915_gem_request *req) -{ - return i915_seqno_passed(intel_engine_get_seqno(req->engine), - req->previous_seqno); -} - -static inline bool i915_gem_request_completed(const struct drm_i915_gem_request *req) -{ - return i915_seqno_passed(intel_engine_get_seqno(req->engine), - req->seqno); -} - -bool __i915_spin_request(const struct drm_i915_gem_request *request, - int state, unsigned long timeout_us); -static inline bool i915_spin_request(const struct drm_i915_gem_request *request, - int state, unsigned long timeout_us) -{ - return (i915_gem_request_started(request) && - __i915_spin_request(request, state, timeout_us)); -} - -int __must_check i915_gem_get_seqno(struct drm_i915_private *dev_priv, u32 *seqno); int __must_check i915_gem_set_seqno(struct drm_device *dev, u32 seqno); struct drm_i915_gem_request * i915_gem_find_active_request(struct intel_engine_cs *engine); void i915_gem_retire_requests(struct drm_i915_private *dev_priv); -void i915_gem_retire_requests_ring(struct intel_engine_cs *engine); - -static inline u32 i915_reset_counter(struct i915_gpu_error *error) -{ - return atomic_read(&error->reset_counter); -} - -static inline bool __i915_reset_in_progress(u32 reset) -{ - return unlikely(reset & I915_RESET_IN_PROGRESS_FLAG); -} - -static inline bool __i915_reset_in_progress_or_wedged(u32 reset) -{ - return unlikely(reset & (I915_RESET_IN_PROGRESS_FLAG | I915_WEDGED)); -} - -static inline bool __i915_terminally_wedged(u32 reset) -{ - return unlikely(reset & I915_WEDGED); -} static inline bool i915_reset_in_progress(struct i915_gpu_error *error) { - return __i915_reset_in_progress(i915_reset_counter(error)); + return unlikely(test_bit(I915_RESET_IN_PROGRESS, &error->flags)); } -static inline bool i915_reset_in_progress_or_wedged(struct i915_gpu_error *error) +static inline bool i915_terminally_wedged(struct i915_gpu_error *error) { - return __i915_reset_in_progress_or_wedged(i915_reset_counter(error)); + return unlikely(test_bit(I915_WEDGED, &error->flags)); } -static inline bool i915_terminally_wedged(struct i915_gpu_error *error) +static inline bool i915_reset_in_progress_or_wedged(struct i915_gpu_error *error) { - return __i915_terminally_wedged(i915_reset_counter(error)); + return i915_reset_in_progress(error) | i915_terminally_wedged(error); } static inline u32 i915_reset_count(struct i915_gpu_error *error) { - return ((i915_reset_counter(error) & ~I915_WEDGED) + 1) / 2; + return READ_ONCE(error->reset_count); } -void i915_gem_reset(struct drm_device *dev); +void i915_gem_reset(struct drm_i915_private *dev_priv); +void i915_gem_set_wedged(struct drm_i915_private *dev_priv); bool i915_gem_clflush_object(struct drm_i915_gem_object *obj, bool force); int __must_check i915_gem_init(struct drm_device *dev); -int i915_gem_init_engines(struct drm_device *dev); int __must_check i915_gem_init_hw(struct drm_device *dev); void i915_gem_init_swizzling(struct drm_device *dev); void i915_gem_cleanup_engines(struct drm_device *dev); -int __must_check i915_gem_wait_for_idle(struct drm_i915_private *dev_priv); +int __must_check i915_gem_wait_for_idle(struct drm_i915_private *dev_priv, + unsigned int flags); int __must_check i915_gem_suspend(struct drm_device *dev); -void __i915_add_request(struct drm_i915_gem_request *req, - struct drm_i915_gem_object *batch_obj, - bool flush_caches); -#define i915_add_request(req) \ - __i915_add_request(req, NULL, true) -#define i915_add_request_no_flush(req) \ - __i915_add_request(req, NULL, false) -int __i915_wait_request(struct drm_i915_gem_request *req, - bool interruptible, - s64 *timeout, - struct intel_rps_client *rps); -int __must_check i915_wait_request(struct drm_i915_gem_request *req); +void i915_gem_resume(struct drm_device *dev); int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); int __must_check i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj, @@ -3408,22 +3284,20 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write); int __must_check i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write); -int __must_check +struct i915_vma * __must_check i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, u32 alignment, const struct i915_ggtt_view *view); -void i915_gem_object_unpin_from_display_plane(struct drm_i915_gem_object *obj, - const struct i915_ggtt_view *view); +void i915_gem_object_unpin_from_display_plane(struct i915_vma *vma); int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align); int i915_gem_open(struct drm_device *dev, struct drm_file *file); void i915_gem_release(struct drm_device *dev, struct drm_file *file); -uint32_t -i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode); -uint32_t -i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size, - int tiling_mode, bool fenced); +u64 i915_gem_get_ggtt_size(struct drm_i915_private *dev_priv, u64 size, + int tiling_mode); +u64 i915_gem_get_ggtt_alignment(struct drm_i915_private *dev_priv, u64 size, + int tiling_mode, bool fenced); int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, enum i915_cache_level cache_level); @@ -3434,86 +3308,82 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, struct dma_buf *i915_gem_prime_export(struct drm_device *dev, struct drm_gem_object *gem_obj, int flags); -u64 i915_gem_obj_ggtt_offset_view(struct drm_i915_gem_object *o, - const struct i915_ggtt_view *view); -u64 i915_gem_obj_offset(struct drm_i915_gem_object *o, - struct i915_address_space *vm); -static inline u64 -i915_gem_obj_ggtt_offset(struct drm_i915_gem_object *o) -{ - return i915_gem_obj_ggtt_offset_view(o, &i915_ggtt_view_normal); -} - -bool i915_gem_obj_bound_any(struct drm_i915_gem_object *o); -bool i915_gem_obj_ggtt_bound_view(struct drm_i915_gem_object *o, - const struct i915_ggtt_view *view); -bool i915_gem_obj_bound(struct drm_i915_gem_object *o, - struct i915_address_space *vm); - struct i915_vma * i915_gem_obj_to_vma(struct drm_i915_gem_object *obj, - struct i915_address_space *vm); -struct i915_vma * -i915_gem_obj_to_ggtt_view(struct drm_i915_gem_object *obj, - const struct i915_ggtt_view *view); + struct i915_address_space *vm, + const struct i915_ggtt_view *view); struct i915_vma * i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj, - struct i915_address_space *vm); -struct i915_vma * -i915_gem_obj_lookup_or_create_ggtt_vma(struct drm_i915_gem_object *obj, - const struct i915_ggtt_view *view); - -static inline struct i915_vma * -i915_gem_obj_to_ggtt(struct drm_i915_gem_object *obj) -{ - return i915_gem_obj_to_ggtt_view(obj, &i915_ggtt_view_normal); -} -bool i915_gem_obj_is_pinned(struct drm_i915_gem_object *obj); + struct i915_address_space *vm, + const struct i915_ggtt_view *view); -/* Some GGTT VM helpers */ static inline struct i915_hw_ppgtt * i915_vm_to_ppgtt(struct i915_address_space *vm) { return container_of(vm, struct i915_hw_ppgtt, base); } +static inline struct i915_vma * +i915_gem_object_to_ggtt(struct drm_i915_gem_object *obj, + const struct i915_ggtt_view *view) +{ + return i915_gem_obj_to_vma(obj, &to_i915(obj->base.dev)->ggtt.base, view); +} -static inline bool i915_gem_obj_ggtt_bound(struct drm_i915_gem_object *obj) +static inline unsigned long +i915_gem_object_ggtt_offset(struct drm_i915_gem_object *o, + const struct i915_ggtt_view *view) { - return i915_gem_obj_ggtt_bound_view(obj, &i915_ggtt_view_normal); + return i915_ggtt_offset(i915_gem_object_to_ggtt(o, view)); } -unsigned long -i915_gem_obj_ggtt_size(struct drm_i915_gem_object *obj); +/* i915_gem_fence.c */ +int __must_check i915_vma_get_fence(struct i915_vma *vma); +int __must_check i915_vma_put_fence(struct i915_vma *vma); -static inline int __must_check -i915_gem_obj_ggtt_pin(struct drm_i915_gem_object *obj, - uint32_t alignment, - unsigned flags) +/** + * i915_vma_pin_fence - pin fencing state + * @vma: vma to pin fencing for + * + * This pins the fencing state (whether tiled or untiled) to make sure the + * vma (and its object) is ready to be used as a scanout target. Fencing + * status must be synchronize first by calling i915_vma_get_fence(): + * + * The resulting fence pin reference must be released again with + * i915_vma_unpin_fence(). + * + * Returns: + * + * True if the vma has a fence, false otherwise. + */ +static inline bool +i915_vma_pin_fence(struct i915_vma *vma) { - struct drm_i915_private *dev_priv = to_i915(obj->base.dev); - struct i915_ggtt *ggtt = &dev_priv->ggtt; - - return i915_gem_object_pin(obj, &ggtt->base, - alignment, flags | PIN_GLOBAL); + if (vma->fence) { + vma->fence->pin_count++; + return true; + } else + return false; } -void i915_gem_object_ggtt_unpin_view(struct drm_i915_gem_object *obj, - const struct i915_ggtt_view *view); +/** + * i915_vma_unpin_fence - unpin fencing state + * @vma: vma to unpin fencing for + * + * This releases the fence pin reference acquired through + * i915_vma_pin_fence. It will handle both objects with and without an + * attached fence correctly, callers do not need to distinguish this. + */ static inline void -i915_gem_object_ggtt_unpin(struct drm_i915_gem_object *obj) +i915_vma_unpin_fence(struct i915_vma *vma) { - i915_gem_object_ggtt_unpin_view(obj, &i915_ggtt_view_normal); + if (vma->fence) { + GEM_BUG_ON(vma->fence->pin_count <= 0); + vma->fence->pin_count--; + } } -/* i915_gem_fence.c */ -int __must_check i915_gem_object_get_fence(struct drm_i915_gem_object *obj); -int __must_check i915_gem_object_put_fence(struct drm_i915_gem_object *obj); - -bool i915_gem_object_pin_fence(struct drm_i915_gem_object *obj); -void i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj); - void i915_gem_restore_fences(struct drm_device *dev); void i915_gem_detect_bit_6_swizzle(struct drm_device *dev); @@ -3524,10 +3394,10 @@ void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj); int __must_check i915_gem_context_init(struct drm_device *dev); void i915_gem_context_lost(struct drm_i915_private *dev_priv); void i915_gem_context_fini(struct drm_device *dev); -void i915_gem_context_reset(struct drm_device *dev); int i915_gem_context_open(struct drm_device *dev, struct drm_file *file); void i915_gem_context_close(struct drm_device *dev, struct drm_file *file); int i915_switch_context(struct drm_i915_gem_request *req); +int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv); void i915_gem_context_free(struct kref *ctx_ref); struct drm_i915_gem_object * i915_gem_alloc_context_obj(struct drm_device *dev, size_t size); @@ -3548,12 +3418,14 @@ i915_gem_context_lookup(struct drm_i915_file_private *file_priv, u32 id) return ctx; } -static inline void i915_gem_context_reference(struct i915_gem_context *ctx) +static inline struct i915_gem_context * +i915_gem_context_get(struct i915_gem_context *ctx) { kref_get(&ctx->ref); + return ctx; } -static inline void i915_gem_context_unreference(struct i915_gem_context *ctx) +static inline void i915_gem_context_put(struct i915_gem_context *ctx) { lockdep_assert_held(&ctx->i915->drm.struct_mutex); kref_put(&ctx->ref, i915_gem_context_free); @@ -3576,13 +3448,10 @@ int i915_gem_context_reset_stats_ioctl(struct drm_device *dev, void *data, struct drm_file *file); /* i915_gem_evict.c */ -int __must_check i915_gem_evict_something(struct drm_device *dev, - struct i915_address_space *vm, - int min_size, - unsigned alignment, +int __must_check i915_gem_evict_something(struct i915_address_space *vm, + u64 min_size, u64 alignment, unsigned cache_level, - unsigned long start, - unsigned long end, + u64 start, u64 end, unsigned flags); int __must_check i915_gem_evict_for_vma(struct i915_vma *target); int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle); @@ -3590,6 +3459,7 @@ int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle); /* belongs in i915_gem_gtt.h */ static inline void i915_gem_chipset_flush(struct drm_i915_private *dev_priv) { + wmb(); if (INTEL_GEN(dev_priv) < 6) intel_gtt_chipset_flush(); } @@ -3634,28 +3504,21 @@ static inline bool i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_objec struct drm_i915_private *dev_priv = to_i915(obj->base.dev); return dev_priv->mm.bit_6_swizzle_x == I915_BIT_6_SWIZZLE_9_10_17 && - obj->tiling_mode != I915_TILING_NONE; + i915_gem_object_is_tiled(obj); } -/* i915_gem_debug.c */ -#if WATCH_LISTS -int i915_verify_lists(struct drm_device *dev); -#else -#define i915_verify_lists(dev) 0 -#endif - /* i915_debugfs.c */ #ifdef CONFIG_DEBUG_FS int i915_debugfs_register(struct drm_i915_private *dev_priv); void i915_debugfs_unregister(struct drm_i915_private *dev_priv); int i915_debugfs_connector_add(struct drm_connector *connector); -void intel_display_crc_init(struct drm_device *dev); +void intel_display_crc_init(struct drm_i915_private *dev_priv); #else static inline int i915_debugfs_register(struct drm_i915_private *dev_priv) {return 0;} static inline void i915_debugfs_unregister(struct drm_i915_private *dev_priv) {} static inline int i915_debugfs_connector_add(struct drm_connector *connector) { return 0; } -static inline void intel_display_crc_init(struct drm_device *dev) {} +static inline void intel_display_crc_init(struct drm_i915_private *dev_priv) {} #endif /* i915_gpu_error.c */ @@ -3684,23 +3547,23 @@ const char *i915_cache_level_str(struct drm_i915_private *i915, int type); /* i915_cmd_parser.c */ int i915_cmd_parser_get_version(struct drm_i915_private *dev_priv); -int i915_cmd_parser_init_ring(struct intel_engine_cs *engine); -void i915_cmd_parser_fini_ring(struct intel_engine_cs *engine); -bool i915_needs_cmd_parser(struct intel_engine_cs *engine); -int i915_parse_cmds(struct intel_engine_cs *engine, - struct drm_i915_gem_object *batch_obj, - struct drm_i915_gem_object *shadow_batch_obj, - u32 batch_start_offset, - u32 batch_len, - bool is_master); +void intel_engine_init_cmd_parser(struct intel_engine_cs *engine); +void intel_engine_cleanup_cmd_parser(struct intel_engine_cs *engine); +bool intel_engine_needs_cmd_parser(struct intel_engine_cs *engine); +int intel_engine_cmd_parser(struct intel_engine_cs *engine, + struct drm_i915_gem_object *batch_obj, + struct drm_i915_gem_object *shadow_batch_obj, + u32 batch_start_offset, + u32 batch_len, + bool is_master); /* i915_suspend.c */ extern int i915_save_state(struct drm_device *dev); extern int i915_restore_state(struct drm_device *dev); /* i915_sysfs.c */ -void i915_setup_sysfs(struct drm_device *dev_priv); -void i915_teardown_sysfs(struct drm_device *dev_priv); +void i915_setup_sysfs(struct drm_i915_private *dev_priv); +void i915_teardown_sysfs(struct drm_i915_private *dev_priv); /* intel_i2c.c */ extern int intel_setup_gmbus(struct drm_device *dev); @@ -3800,7 +3663,6 @@ extern void intel_set_rps(struct drm_i915_private *dev_priv, u8 val); extern void intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable); -extern bool i915_semaphore_is_enabled(struct drm_i915_private *dev_priv); int i915_reg_read_ioctl(struct drm_device *dev, void *data, struct drm_file *file); @@ -3878,9 +3740,16 @@ int intel_freq_opcode(struct drm_i915_private *dev_priv, int val); * will be implemented using 2 32-bit writes in an arbitrary order with * an arbitrary delay between them. This can cause the hardware to * act upon the intermediate value, possibly leading to corruption and - * machine death. You have been warned. + * machine death. For this reason we do not support I915_WRITE64, or + * dev_priv->uncore.funcs.mmio_writeq. + * + * When reading a 64-bit value as two 32-bit values, the delay may cause + * the two reads to mismatch, e.g. a timestamp overflowing. Also note that + * occasionally a 64-bit register does not actualy support a full readq + * and must be read using two 32-bit reads. + * + * You have been warned. */ -#define I915_WRITE64(reg, val) dev_priv->uncore.funcs.mmio_writeq(dev_priv, (reg), (val), true) #define I915_READ64(reg) dev_priv->uncore.funcs.mmio_readq(dev_priv, (reg), true) #define I915_READ64_2x32(lower_reg, upper_reg) ({ \ @@ -3923,7 +3792,7 @@ __raw_write(64, q) #undef __raw_write /* These are untraced mmio-accessors that are only valid to be used inside - * criticial sections inside IRQ handlers where forcewake is explicitly + * critical sections inside IRQ handlers where forcewake is explicitly * controlled. * Think twice, and think again, before using these. * Note: Should only be used between intel_uncore_forcewake_irqlock() and @@ -3995,7 +3864,9 @@ wait_remaining_ms_from_jiffies(unsigned long timestamp_jiffies, int to_wait_ms) schedule_timeout_uninterruptible(remaining_jiffies); } } -static inline bool __i915_request_irq_complete(struct drm_i915_gem_request *req) + +static inline bool +__i915_request_irq_complete(struct drm_i915_gem_request *req) { struct intel_engine_cs *engine = req->engine; @@ -4017,7 +3888,7 @@ static inline bool __i915_request_irq_complete(struct drm_i915_gem_request *req) * is woken. */ if (engine->irq_seqno_barrier && - READ_ONCE(engine->breadcrumbs.irq_seqno_bh) == current && + rcu_access_pointer(engine->breadcrumbs.irq_seqno_bh) == current && cmpxchg_relaxed(&engine->breadcrumbs.irq_posted, 1, 0)) { struct task_struct *tsk; @@ -4042,7 +3913,7 @@ static inline bool __i915_request_irq_complete(struct drm_i915_gem_request *req) * irq_posted == false but we are still running). */ rcu_read_lock(); - tsk = READ_ONCE(engine->breadcrumbs.irq_seqno_bh); + tsk = rcu_dereference(engine->breadcrumbs.irq_seqno_bh); if (tsk && tsk != current) /* Note that if the bottom-half is changed as we * are sending the wake-up, the new bottom-half will @@ -4057,18 +3928,35 @@ static inline bool __i915_request_irq_complete(struct drm_i915_gem_request *req) return true; } - /* We need to check whether any gpu reset happened in between - * the request being submitted and now. If a reset has occurred, - * the seqno will have been advance past ours and our request - * is complete. If we are in the process of handling a reset, - * the request is effectively complete as the rendering will - * be discarded, but we need to return in order to drop the - * struct_mutex. - */ - if (i915_reset_in_progress(&req->i915->gpu_error)) - return true; - return false; } +void i915_memcpy_init_early(struct drm_i915_private *dev_priv); +bool i915_memcpy_from_wc(void *dst, const void *src, unsigned long len); + +/* i915_mm.c */ +int remap_io_mapping(struct vm_area_struct *vma, + unsigned long addr, unsigned long pfn, unsigned long size, + struct io_mapping *iomap); + +#define ptr_mask_bits(ptr) ({ \ + unsigned long __v = (unsigned long)(ptr); \ + (typeof(ptr))(__v & PAGE_MASK); \ +}) + +#define ptr_unpack_bits(ptr, bits) ({ \ + unsigned long __v = (unsigned long)(ptr); \ + (bits) = __v & ~PAGE_MASK; \ + (typeof(ptr))(__v & PAGE_MASK); \ +}) + +#define ptr_pack_bits(ptr, bits) \ + ((typeof(ptr))((unsigned long)(ptr) | (bits))) + +#define fetch_and_zero(ptr) ({ \ + typeof(*ptr) __T = *(ptr); \ + *(ptr) = (typeof(*ptr))0; \ + __T; \ +}) + #endif diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 1168150..2c81067 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -29,10 +29,13 @@ #include <drm/drm_vma_manager.h> #include <drm/i915_drm.h> #include "i915_drv.h" +#include "i915_gem_dmabuf.h" #include "i915_vgpu.h" #include "i915_trace.h" #include "intel_drv.h" +#include "intel_frontbuffer.h" #include "intel_mocs.h" +#include <linux/reservation.h> #include <linux/shmem_fs.h> #include <linux/slab.h> #include <linux/swap.h> @@ -41,10 +44,6 @@ static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj); static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj); -static void -i915_gem_object_retire__write(struct drm_i915_gem_object *obj); -static void -i915_gem_object_retire__read(struct drm_i915_gem_object *obj, int ring); static bool cpu_cache_is_coherent(struct drm_device *dev, enum i915_cache_level level) @@ -139,7 +138,6 @@ int i915_mutex_lock_interruptible(struct drm_device *dev) if (ret) return ret; - WARN_ON(i915_verify_lists(dev)); return 0; } @@ -156,10 +154,10 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, pinned = 0; mutex_lock(&dev->struct_mutex); list_for_each_entry(vma, &ggtt->base.active_list, vm_link) - if (vma->pin_count) + if (i915_vma_is_pinned(vma)) pinned += vma->node.size; list_for_each_entry(vma, &ggtt->base.inactive_list, vm_link) - if (vma->pin_count) + if (i915_vma_is_pinned(vma)) pinned += vma->node.size; mutex_unlock(&dev->struct_mutex); @@ -281,23 +279,129 @@ static const struct drm_i915_gem_object_ops i915_gem_phys_ops = { .release = i915_gem_object_release_phys, }; -static int -drop_pages(struct drm_i915_gem_object *obj) +int i915_gem_object_unbind(struct drm_i915_gem_object *obj) { - struct i915_vma *vma, *next; + struct i915_vma *vma; + LIST_HEAD(still_in_list); int ret; - drm_gem_object_reference(&obj->base); - list_for_each_entry_safe(vma, next, &obj->vma_list, obj_link) - if (i915_vma_unbind(vma)) - break; + lockdep_assert_held(&obj->base.dev->struct_mutex); - ret = i915_gem_object_put_pages(obj); - drm_gem_object_unreference(&obj->base); + /* Closed vma are removed from the obj->vma_list - but they may + * still have an active binding on the object. To remove those we + * must wait for all rendering to complete to the object (as unbinding + * must anyway), and retire the requests. + */ + ret = i915_gem_object_wait_rendering(obj, false); + if (ret) + return ret; + + i915_gem_retire_requests(to_i915(obj->base.dev)); + + while ((vma = list_first_entry_or_null(&obj->vma_list, + struct i915_vma, + obj_link))) { + list_move_tail(&vma->obj_link, &still_in_list); + ret = i915_vma_unbind(vma); + if (ret) + break; + } + list_splice(&still_in_list, &obj->vma_list); return ret; } +/** + * Ensures that all rendering to the object has completed and the object is + * safe to unbind from the GTT or access from the CPU. + * @obj: i915 gem object + * @readonly: waiting for just read access or read-write access + */ +int +i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj, + bool readonly) +{ + struct reservation_object *resv; + struct i915_gem_active *active; + unsigned long active_mask; + int idx; + + lockdep_assert_held(&obj->base.dev->struct_mutex); + + if (!readonly) { + active = obj->last_read; + active_mask = i915_gem_object_get_active(obj); + } else { + active_mask = 1; + active = &obj->last_write; + } + + for_each_active(active_mask, idx) { + int ret; + + ret = i915_gem_active_wait(&active[idx], + &obj->base.dev->struct_mutex); + if (ret) + return ret; + } + + resv = i915_gem_object_get_dmabuf_resv(obj); + if (resv) { + long err; + + err = reservation_object_wait_timeout_rcu(resv, !readonly, true, + MAX_SCHEDULE_TIMEOUT); + if (err < 0) + return err; + } + + return 0; +} + +/* A nonblocking variant of the above wait. Must be called prior to + * acquiring the mutex for the object, as the object state may change + * during this call. A reference must be held by the caller for the object. + */ +static __must_check int +__unsafe_wait_rendering(struct drm_i915_gem_object *obj, + struct intel_rps_client *rps, + bool readonly) +{ + struct i915_gem_active *active; + unsigned long active_mask; + int idx; + + active_mask = __I915_BO_ACTIVE(obj); + if (!active_mask) + return 0; + + if (!readonly) { + active = obj->last_read; + } else { + active_mask = 1; + active = &obj->last_write; + } + + for_each_active(active_mask, idx) { + int ret; + + ret = i915_gem_active_wait_unlocked(&active[idx], + I915_WAIT_INTERRUPTIBLE, + NULL, rps); + if (ret) + return ret; + } + + return 0; +} + +static struct intel_rps_client *to_rps_client(struct drm_file *file) +{ + struct drm_i915_file_private *fpriv = file->driver_priv; + + return &fpriv->rps; +} + int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align) @@ -318,7 +422,11 @@ i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, if (obj->base.filp == NULL) return -EINVAL; - ret = drop_pages(obj); + ret = i915_gem_object_unbind(obj); + if (ret) + return ret; + + ret = i915_gem_object_put_pages(obj); if (ret) return ret; @@ -408,7 +516,7 @@ i915_gem_create(struct drm_file *file, ret = drm_gem_handle_create(file, &obj->base, &handle); /* drop reference from allocate - handle holds it now */ - drm_gem_object_unreference_unlocked(&obj->base); + i915_gem_object_put_unlocked(obj); if (ret) return ret; @@ -502,33 +610,106 @@ __copy_from_user_swizzled(char *gpu_vaddr, int gpu_offset, * flush the object from the CPU cache. */ int i915_gem_obj_prepare_shmem_read(struct drm_i915_gem_object *obj, - int *needs_clflush) + unsigned int *needs_clflush) { int ret; *needs_clflush = 0; - if (WARN_ON(!i915_gem_object_has_struct_page(obj))) - return -EINVAL; + if (!i915_gem_object_has_struct_page(obj)) + return -ENODEV; + + ret = i915_gem_object_wait_rendering(obj, true); + if (ret) + return ret; + + ret = i915_gem_object_get_pages(obj); + if (ret) + return ret; - if (!(obj->base.read_domains & I915_GEM_DOMAIN_CPU)) { - /* If we're not in the cpu read domain, set ourself into the gtt - * read domain and manually flush cachelines (if required). This - * optimizes for the case when the gpu will dirty the data - * anyway again before the next pread happens. */ + i915_gem_object_pin_pages(obj); + + i915_gem_object_flush_gtt_write_domain(obj); + + /* If we're not in the cpu read domain, set ourself into the gtt + * read domain and manually flush cachelines (if required). This + * optimizes for the case when the gpu will dirty the data + * anyway again before the next pread happens. + */ + if (!(obj->base.read_domains & I915_GEM_DOMAIN_CPU)) *needs_clflush = !cpu_cache_is_coherent(obj->base.dev, obj->cache_level); - ret = i915_gem_object_wait_rendering(obj, true); + + if (*needs_clflush && !static_cpu_has(X86_FEATURE_CLFLUSH)) { + ret = i915_gem_object_set_to_cpu_domain(obj, false); if (ret) - return ret; + goto err_unpin; + + *needs_clflush = 0; } + /* return with the pages pinned */ + return 0; + +err_unpin: + i915_gem_object_unpin_pages(obj); + return ret; +} + +int i915_gem_obj_prepare_shmem_write(struct drm_i915_gem_object *obj, + unsigned int *needs_clflush) +{ + int ret; + + *needs_clflush = 0; + if (!i915_gem_object_has_struct_page(obj)) + return -ENODEV; + + ret = i915_gem_object_wait_rendering(obj, false); + if (ret) + return ret; + ret = i915_gem_object_get_pages(obj); if (ret) return ret; i915_gem_object_pin_pages(obj); + i915_gem_object_flush_gtt_write_domain(obj); + + /* If we're not in the cpu write domain, set ourself into the + * gtt write domain and manually flush cachelines (as required). + * This optimizes for the case when the gpu will use the data + * right away and we therefore have to clflush anyway. + */ + if (obj->base.write_domain != I915_GEM_DOMAIN_CPU) + *needs_clflush |= cpu_write_needs_clflush(obj) << 1; + + /* Same trick applies to invalidate partially written cachelines read + * before writing. + */ + if (!(obj->base.read_domains & I915_GEM_DOMAIN_CPU)) + *needs_clflush |= !cpu_cache_is_coherent(obj->base.dev, + obj->cache_level); + + if (*needs_clflush && !static_cpu_has(X86_FEATURE_CLFLUSH)) { + ret = i915_gem_object_set_to_cpu_domain(obj, true); + if (ret) + goto err_unpin; + + *needs_clflush = 0; + } + + if ((*needs_clflush & CLFLUSH_AFTER) == 0) + obj->cache_dirty = true; + + intel_fb_obj_invalidate(obj, ORIGIN_CPU); + obj->dirty = 1; + /* return with the pages pinned */ + return 0; + +err_unpin: + i915_gem_object_unpin_pages(obj); return ret; } @@ -638,14 +819,24 @@ i915_gem_gtt_pread(struct drm_device *dev, { struct drm_i915_private *dev_priv = to_i915(dev); struct i915_ggtt *ggtt = &dev_priv->ggtt; + struct i915_vma *vma; struct drm_mm_node node; char __user *user_data; uint64_t remain; uint64_t offset; int ret; - ret = i915_gem_obj_ggtt_pin(obj, 0, PIN_MAPPABLE); - if (ret) { + vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, PIN_MAPPABLE); + if (!IS_ERR(vma)) { + node.start = i915_ggtt_offset(vma); + node.allocated = false; + ret = i915_vma_put_fence(vma); + if (ret) { + i915_vma_unpin(vma); + vma = ERR_PTR(ret); + } + } + if (IS_ERR(vma)) { ret = insert_mappable_node(dev_priv, &node, PAGE_SIZE); if (ret) goto out; @@ -657,12 +848,6 @@ i915_gem_gtt_pread(struct drm_device *dev, } i915_gem_object_pin_pages(obj); - } else { - node.start = i915_gem_obj_ggtt_offset(obj); - node.allocated = false; - ret = i915_gem_object_put_fence(obj); - if (ret) - goto out_unpin; } ret = i915_gem_object_set_to_gtt_domain(obj, false); @@ -707,7 +892,7 @@ i915_gem_gtt_pread(struct drm_device *dev, * and write to user memory which may result into page * faults, and so we cannot perform this under struct_mutex. */ - if (slow_user_access(ggtt->mappable, page_base, + if (slow_user_access(&ggtt->mappable, page_base, page_offset, user_data, page_length, false)) { ret = -EFAULT; @@ -739,7 +924,7 @@ out_unpin: i915_gem_object_unpin_pages(obj); remove_mappable_node(&node); } else { - i915_gem_object_ggtt_unpin(obj); + i915_vma_unpin(vma); } out: return ret; @@ -760,19 +945,14 @@ i915_gem_shmem_pread(struct drm_device *dev, int needs_clflush = 0; struct sg_page_iter sg_iter; - if (!i915_gem_object_has_struct_page(obj)) - return -ENODEV; - - user_data = u64_to_user_ptr(args->data_ptr); - remain = args->size; - - obj_do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj); - ret = i915_gem_obj_prepare_shmem_read(obj, &needs_clflush); if (ret) return ret; + obj_do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj); + user_data = u64_to_user_ptr(args->data_ptr); offset = args->offset; + remain = args->size; for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, offset >> PAGE_SHIFT) { @@ -828,7 +1008,7 @@ next_page: } out: - i915_gem_object_unpin_pages(obj); + i915_gem_obj_finish_shmem_access(obj); return ret; } @@ -857,36 +1037,44 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, args->size)) return -EFAULT; - ret = i915_mutex_lock_interruptible(dev); - if (ret) - return ret; - - obj = to_intel_bo(drm_gem_object_lookup(file, args->handle)); - if (&obj->base == NULL) { - ret = -ENOENT; - goto unlock; - } + obj = i915_gem_object_lookup(file, args->handle); + if (!obj) + return -ENOENT; /* Bounds check source. */ if (args->offset > obj->base.size || args->size > obj->base.size - args->offset) { ret = -EINVAL; - goto out; + goto err; } trace_i915_gem_object_pread(obj, args->offset, args->size); + ret = __unsafe_wait_rendering(obj, to_rps_client(file), true); + if (ret) + goto err; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + goto err; + ret = i915_gem_shmem_pread(dev, obj, args, file); /* pread for non shmem backed objects */ - if (ret == -EFAULT || ret == -ENODEV) + if (ret == -EFAULT || ret == -ENODEV) { + intel_runtime_pm_get(to_i915(dev)); ret = i915_gem_gtt_pread(dev, obj, args->size, args->offset, args->data_ptr); + intel_runtime_pm_put(to_i915(dev)); + } -out: - drm_gem_object_unreference(&obj->base); -unlock: + i915_gem_object_put(obj); mutex_unlock(&dev->struct_mutex); + + return ret; + +err: + i915_gem_object_put_unlocked(obj); return ret; } @@ -916,7 +1104,7 @@ fast_user_write(struct io_mapping *mapping, /** * This is the fast pwrite path, where we copy the data directly from the * user into the GTT, uncached. - * @dev: drm device pointer + * @i915: i915 device private data * @obj: i915 gem object * @args: pwrite arguments structure * @file: drm file pointer @@ -929,17 +1117,28 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_private *i915, { struct i915_ggtt *ggtt = &i915->ggtt; struct drm_device *dev = obj->base.dev; + struct i915_vma *vma; struct drm_mm_node node; uint64_t remain, offset; char __user *user_data; int ret; bool hit_slow_path = false; - if (obj->tiling_mode != I915_TILING_NONE) + if (i915_gem_object_is_tiled(obj)) return -EFAULT; - ret = i915_gem_obj_ggtt_pin(obj, 0, PIN_MAPPABLE | PIN_NONBLOCK); - if (ret) { + vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, + PIN_MAPPABLE | PIN_NONBLOCK); + if (!IS_ERR(vma)) { + node.start = i915_ggtt_offset(vma); + node.allocated = false; + ret = i915_vma_put_fence(vma); + if (ret) { + i915_vma_unpin(vma); + vma = ERR_PTR(ret); + } + } + if (IS_ERR(vma)) { ret = insert_mappable_node(i915, &node, PAGE_SIZE); if (ret) goto out; @@ -951,19 +1150,13 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_private *i915, } i915_gem_object_pin_pages(obj); - } else { - node.start = i915_gem_obj_ggtt_offset(obj); - node.allocated = false; - ret = i915_gem_object_put_fence(obj); - if (ret) - goto out_unpin; } ret = i915_gem_object_set_to_gtt_domain(obj, true); if (ret) goto out_unpin; - intel_fb_obj_invalidate(obj, ORIGIN_GTT); + intel_fb_obj_invalidate(obj, ORIGIN_CPU); obj->dirty = true; user_data = u64_to_user_ptr(args->data_ptr); @@ -995,11 +1188,11 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_private *i915, * If the object is non-shmem backed, we retry again with the * path that handles page fault. */ - if (fast_user_write(ggtt->mappable, page_base, + if (fast_user_write(&ggtt->mappable, page_base, page_offset, user_data, page_length)) { hit_slow_path = true; mutex_unlock(&dev->struct_mutex); - if (slow_user_access(ggtt->mappable, + if (slow_user_access(&ggtt->mappable, page_base, page_offset, user_data, page_length, true)) { @@ -1030,7 +1223,7 @@ out_flush: } } - intel_fb_obj_flush(obj, false, ORIGIN_GTT); + intel_fb_obj_flush(obj, false, ORIGIN_CPU); out_unpin: if (node.allocated) { wmb(); @@ -1040,7 +1233,7 @@ out_unpin: i915_gem_object_unpin_pages(obj); remove_mappable_node(&node); } else { - i915_gem_object_ggtt_unpin(obj); + i915_vma_unpin(vma); } out: return ret; @@ -1123,41 +1316,17 @@ i915_gem_shmem_pwrite(struct drm_device *dev, int shmem_page_offset, page_length, ret = 0; int obj_do_bit17_swizzling, page_do_bit17_swizzling; int hit_slowpath = 0; - int needs_clflush_after = 0; - int needs_clflush_before = 0; + unsigned int needs_clflush; struct sg_page_iter sg_iter; - user_data = u64_to_user_ptr(args->data_ptr); - remain = args->size; - - obj_do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj); - - if (obj->base.write_domain != I915_GEM_DOMAIN_CPU) { - /* If we're not in the cpu write domain, set ourself into the gtt - * write domain and manually flush cachelines (if required). This - * optimizes for the case when the gpu will use the data - * right away and we therefore have to clflush anyway. */ - needs_clflush_after = cpu_write_needs_clflush(obj); - ret = i915_gem_object_wait_rendering(obj, false); - if (ret) - return ret; - } - /* Same trick applies to invalidate partially written cachelines read - * before writing. */ - if ((obj->base.read_domains & I915_GEM_DOMAIN_CPU) == 0) - needs_clflush_before = - !cpu_cache_is_coherent(dev, obj->cache_level); - - ret = i915_gem_object_get_pages(obj); + ret = i915_gem_obj_prepare_shmem_write(obj, &needs_clflush); if (ret) return ret; - intel_fb_obj_invalidate(obj, ORIGIN_CPU); - - i915_gem_object_pin_pages(obj); - + obj_do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj); + user_data = u64_to_user_ptr(args->data_ptr); offset = args->offset; - obj->dirty = 1; + remain = args->size; for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, offset >> PAGE_SHIFT) { @@ -1181,7 +1350,7 @@ i915_gem_shmem_pwrite(struct drm_device *dev, /* If we don't overwrite a cacheline completely we need to be * careful to have up-to-date data by first clflushing. Don't * overcomplicate things and flush the entire patch. */ - partial_cacheline_write = needs_clflush_before && + partial_cacheline_write = needs_clflush & CLFLUSH_BEFORE && ((shmem_page_offset | page_length) & (boot_cpu_data.x86_clflush_size - 1)); @@ -1191,7 +1360,7 @@ i915_gem_shmem_pwrite(struct drm_device *dev, ret = shmem_pwrite_fast(page, shmem_page_offset, page_length, user_data, page_do_bit17_swizzling, partial_cacheline_write, - needs_clflush_after); + needs_clflush & CLFLUSH_AFTER); if (ret == 0) goto next_page; @@ -1200,7 +1369,7 @@ i915_gem_shmem_pwrite(struct drm_device *dev, ret = shmem_pwrite_slow(page, shmem_page_offset, page_length, user_data, page_do_bit17_swizzling, partial_cacheline_write, - needs_clflush_after); + needs_clflush & CLFLUSH_AFTER); mutex_lock(&dev->struct_mutex); @@ -1214,7 +1383,7 @@ next_page: } out: - i915_gem_object_unpin_pages(obj); + i915_gem_obj_finish_shmem_access(obj); if (hit_slowpath) { /* @@ -1222,17 +1391,15 @@ out: * cachelines in-line while writing and the object moved * out of the cpu write domain while we've dropped the lock. */ - if (!needs_clflush_after && + if (!(needs_clflush & CLFLUSH_AFTER) && obj->base.write_domain != I915_GEM_DOMAIN_CPU) { if (i915_gem_clflush_object(obj, obj->pin_display)) - needs_clflush_after = true; + needs_clflush |= CLFLUSH_AFTER; } } - if (needs_clflush_after) + if (needs_clflush & CLFLUSH_AFTER) i915_gem_chipset_flush(to_i915(dev)); - else - obj->cache_dirty = true; intel_fb_obj_flush(obj, false, ORIGIN_CPU); return ret; @@ -1270,27 +1437,29 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, return -EFAULT; } - intel_runtime_pm_get(dev_priv); - - ret = i915_mutex_lock_interruptible(dev); - if (ret) - goto put_rpm; - - obj = to_intel_bo(drm_gem_object_lookup(file, args->handle)); - if (&obj->base == NULL) { - ret = -ENOENT; - goto unlock; - } + obj = i915_gem_object_lookup(file, args->handle); + if (!obj) + return -ENOENT; /* Bounds check destination. */ if (args->offset > obj->base.size || args->size > obj->base.size - args->offset) { ret = -EINVAL; - goto out; + goto err; } trace_i915_gem_object_pwrite(obj, args->offset, args->size); + ret = __unsafe_wait_rendering(obj, to_rps_client(file), false); + if (ret) + goto err; + + intel_runtime_pm_get(dev_priv); + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + goto err_rpm; + ret = -EFAULT; /* We can only do the GTT pwrite on untiled buffers, as otherwise * it would end up going through the fenced access, and we'll get @@ -1306,508 +1475,31 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, * textures). Fallback to the shmem path in that case. */ } - if (ret == -EFAULT) { + if (ret == -EFAULT || ret == -ENOSPC) { if (obj->phys_handle) ret = i915_gem_phys_pwrite(obj, args, file); - else if (i915_gem_object_has_struct_page(obj)) - ret = i915_gem_shmem_pwrite(dev, obj, args, file); else - ret = -ENODEV; + ret = i915_gem_shmem_pwrite(dev, obj, args, file); } -out: - drm_gem_object_unreference(&obj->base); -unlock: + i915_gem_object_put(obj); mutex_unlock(&dev->struct_mutex); -put_rpm: intel_runtime_pm_put(dev_priv); return ret; -} - -static int -i915_gem_check_wedge(unsigned reset_counter, bool interruptible) -{ - if (__i915_terminally_wedged(reset_counter)) - return -EIO; - - if (__i915_reset_in_progress(reset_counter)) { - /* Non-interruptible callers can't handle -EAGAIN, hence return - * -EIO unconditionally for these. */ - if (!interruptible) - return -EIO; - - return -EAGAIN; - } - - return 0; -} - -static unsigned long local_clock_us(unsigned *cpu) -{ - unsigned long t; - - /* Cheaply and approximately convert from nanoseconds to microseconds. - * The result and subsequent calculations are also defined in the same - * approximate microseconds units. The principal source of timing - * error here is from the simple truncation. - * - * Note that local_clock() is only defined wrt to the current CPU; - * the comparisons are no longer valid if we switch CPUs. Instead of - * blocking preemption for the entire busywait, we can detect the CPU - * switch and use that as indicator of system load and a reason to - * stop busywaiting, see busywait_stop(). - */ - *cpu = get_cpu(); - t = local_clock() >> 10; - put_cpu(); - - return t; -} - -static bool busywait_stop(unsigned long timeout, unsigned cpu) -{ - unsigned this_cpu; - - if (time_after(local_clock_us(&this_cpu), timeout)) - return true; - - return this_cpu != cpu; -} - -bool __i915_spin_request(const struct drm_i915_gem_request *req, - int state, unsigned long timeout_us) -{ - unsigned cpu; - - /* When waiting for high frequency requests, e.g. during synchronous - * rendering split between the CPU and GPU, the finite amount of time - * required to set up the irq and wait upon it limits the response - * rate. By busywaiting on the request completion for a short while we - * can service the high frequency waits as quick as possible. However, - * if it is a slow request, we want to sleep as quickly as possible. - * The tradeoff between waiting and sleeping is roughly the time it - * takes to sleep on a request, on the order of a microsecond. - */ - - timeout_us += local_clock_us(&cpu); - do { - if (i915_gem_request_completed(req)) - return true; - - if (signal_pending_state(state, current)) - break; - - if (busywait_stop(timeout_us, cpu)) - break; - - cpu_relax_lowlatency(); - } while (!need_resched()); - - return false; -} - -/** - * __i915_wait_request - wait until execution of request has finished - * @req: duh! - * @interruptible: do an interruptible wait (normally yes) - * @timeout: in - how long to wait (NULL forever); out - how much time remaining - * @rps: RPS client - * - * Note: It is of utmost importance that the passed in seqno and reset_counter - * values have been read by the caller in an smp safe manner. Where read-side - * locks are involved, it is sufficient to read the reset_counter before - * unlocking the lock that protects the seqno. For lockless tricks, the - * reset_counter _must_ be read before, and an appropriate smp_rmb must be - * inserted. - * - * Returns 0 if the request was found within the alloted time. Else returns the - * errno with remaining time filled in timeout argument. - */ -int __i915_wait_request(struct drm_i915_gem_request *req, - bool interruptible, - s64 *timeout, - struct intel_rps_client *rps) -{ - int state = interruptible ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE; - DEFINE_WAIT(reset); - struct intel_wait wait; - unsigned long timeout_remain; - s64 before = 0; /* Only to silence a compiler warning. */ - int ret = 0; - - might_sleep(); - - if (list_empty(&req->list)) - return 0; - - if (i915_gem_request_completed(req)) - return 0; - - timeout_remain = MAX_SCHEDULE_TIMEOUT; - if (timeout) { - if (WARN_ON(*timeout < 0)) - return -EINVAL; - - if (*timeout == 0) - return -ETIME; - - timeout_remain = nsecs_to_jiffies_timeout(*timeout); - - /* - * Record current time in case interrupted by signal, or wedged. - */ - before = ktime_get_raw_ns(); - } - - trace_i915_gem_request_wait_begin(req); - - /* This client is about to stall waiting for the GPU. In many cases - * this is undesirable and limits the throughput of the system, as - * many clients cannot continue processing user input/output whilst - * blocked. RPS autotuning may take tens of milliseconds to respond - * to the GPU load and thus incurs additional latency for the client. - * We can circumvent that by promoting the GPU frequency to maximum - * before we wait. This makes the GPU throttle up much more quickly - * (good for benchmarks and user experience, e.g. window animations), - * but at a cost of spending more power processing the workload - * (bad for battery). Not all clients even want their results - * immediately and for them we should just let the GPU select its own - * frequency to maximise efficiency. To prevent a single client from - * forcing the clocks too high for the whole system, we only allow - * each client to waitboost once in a busy period. - */ - if (INTEL_INFO(req->i915)->gen >= 6) - gen6_rps_boost(req->i915, rps, req->emitted_jiffies); - - /* Optimistic spin for the next ~jiffie before touching IRQs */ - if (i915_spin_request(req, state, 5)) - goto complete; - - set_current_state(state); - add_wait_queue(&req->i915->gpu_error.wait_queue, &reset); - - intel_wait_init(&wait, req->seqno); - if (intel_engine_add_wait(req->engine, &wait)) - /* In order to check that we haven't missed the interrupt - * as we enabled it, we need to kick ourselves to do a - * coherent check on the seqno before we sleep. - */ - goto wakeup; - - for (;;) { - if (signal_pending_state(state, current)) { - ret = -ERESTARTSYS; - break; - } - - timeout_remain = io_schedule_timeout(timeout_remain); - if (timeout_remain == 0) { - ret = -ETIME; - break; - } - - if (intel_wait_complete(&wait)) - break; - - set_current_state(state); - -wakeup: - /* Carefully check if the request is complete, giving time - * for the seqno to be visible following the interrupt. - * We also have to check in case we are kicked by the GPU - * reset in order to drop the struct_mutex. - */ - if (__i915_request_irq_complete(req)) - break; - - /* Only spin if we know the GPU is processing this request */ - if (i915_spin_request(req, state, 2)) - break; - } - remove_wait_queue(&req->i915->gpu_error.wait_queue, &reset); - - intel_engine_remove_wait(req->engine, &wait); - __set_current_state(TASK_RUNNING); -complete: - trace_i915_gem_request_wait_end(req); - - if (timeout) { - s64 tres = *timeout - (ktime_get_raw_ns() - before); - - *timeout = tres < 0 ? 0 : tres; - - /* - * Apparently ktime isn't accurate enough and occasionally has a - * bit of mismatch in the jiffies<->nsecs<->ktime loop. So patch - * things up to make the test happy. We allow up to 1 jiffy. - * - * This is a regrssion from the timespec->ktime conversion. - */ - if (ret == -ETIME && *timeout < jiffies_to_usecs(1)*1000) - *timeout = 0; - } - - if (rps && req->seqno == req->engine->last_submitted_seqno) { - /* The GPU is now idle and this client has stalled. - * Since no other client has submitted a request in the - * meantime, assume that this client is the only one - * supplying work to the GPU but is unable to keep that - * work supplied because it is waiting. Since the GPU is - * then never kept fully busy, RPS autoclocking will - * keep the clocks relatively low, causing further delays. - * Compensate by giving the synchronous client credit for - * a waitboost next time. - */ - spin_lock(&req->i915->rps.client_lock); - list_del_init(&rps->link); - spin_unlock(&req->i915->rps.client_lock); - } - - return ret; -} - -int i915_gem_request_add_to_client(struct drm_i915_gem_request *req, - struct drm_file *file) -{ - struct drm_i915_file_private *file_priv; - - WARN_ON(!req || !file || req->file_priv); - - if (!req || !file) - return -EINVAL; - - if (req->file_priv) - return -EINVAL; - - file_priv = file->driver_priv; - - spin_lock(&file_priv->mm.lock); - req->file_priv = file_priv; - list_add_tail(&req->client_list, &file_priv->mm.request_list); - spin_unlock(&file_priv->mm.lock); - - req->pid = get_pid(task_pid(current)); - - return 0; -} - -static inline void -i915_gem_request_remove_from_client(struct drm_i915_gem_request *request) -{ - struct drm_i915_file_private *file_priv = request->file_priv; - - if (!file_priv) - return; - - spin_lock(&file_priv->mm.lock); - list_del(&request->client_list); - request->file_priv = NULL; - spin_unlock(&file_priv->mm.lock); - - put_pid(request->pid); - request->pid = NULL; -} - -static void i915_gem_request_retire(struct drm_i915_gem_request *request) -{ - trace_i915_gem_request_retire(request); - - /* We know the GPU must have read the request to have - * sent us the seqno + interrupt, so use the position - * of tail of the request to update the last known position - * of the GPU head. - * - * Note this requires that we are always called in request - * completion order. - */ - request->ringbuf->last_retired_head = request->postfix; - - list_del_init(&request->list); - i915_gem_request_remove_from_client(request); - - if (request->previous_context) { - if (i915.enable_execlists) - intel_lr_context_unpin(request->previous_context, - request->engine); - } - - i915_gem_context_unreference(request->ctx); - i915_gem_request_unreference(request); -} - -static void -__i915_gem_request_retire__upto(struct drm_i915_gem_request *req) -{ - struct intel_engine_cs *engine = req->engine; - struct drm_i915_gem_request *tmp; - - lockdep_assert_held(&engine->i915->drm.struct_mutex); - - if (list_empty(&req->list)) - return; - - do { - tmp = list_first_entry(&engine->request_list, - typeof(*tmp), list); - - i915_gem_request_retire(tmp); - } while (tmp != req); - - WARN_ON(i915_verify_lists(engine->dev)); -} - -/** - * Waits for a request to be signaled, and cleans up the - * request and object lists appropriately for that event. - * @req: request to wait on - */ -int -i915_wait_request(struct drm_i915_gem_request *req) -{ - struct drm_i915_private *dev_priv = req->i915; - bool interruptible; - int ret; - - interruptible = dev_priv->mm.interruptible; - - BUG_ON(!mutex_is_locked(&dev_priv->drm.struct_mutex)); - - ret = __i915_wait_request(req, interruptible, NULL, NULL); - if (ret) - return ret; - - /* If the GPU hung, we want to keep the requests to find the guilty. */ - if (!i915_reset_in_progress(&dev_priv->gpu_error)) - __i915_gem_request_retire__upto(req); - - return 0; -} - -/** - * Ensures that all rendering to the object has completed and the object is - * safe to unbind from the GTT or access from the CPU. - * @obj: i915 gem object - * @readonly: waiting for read access or write - */ -int -i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj, - bool readonly) -{ - int ret, i; - - if (!obj->active) - return 0; - - if (readonly) { - if (obj->last_write_req != NULL) { - ret = i915_wait_request(obj->last_write_req); - if (ret) - return ret; - - i = obj->last_write_req->engine->id; - if (obj->last_read_req[i] == obj->last_write_req) - i915_gem_object_retire__read(obj, i); - else - i915_gem_object_retire__write(obj); - } - } else { - for (i = 0; i < I915_NUM_ENGINES; i++) { - if (obj->last_read_req[i] == NULL) - continue; - - ret = i915_wait_request(obj->last_read_req[i]); - if (ret) - return ret; - - i915_gem_object_retire__read(obj, i); - } - GEM_BUG_ON(obj->active); - } - - return 0; -} - -static void -i915_gem_object_retire_request(struct drm_i915_gem_object *obj, - struct drm_i915_gem_request *req) -{ - int ring = req->engine->id; - - if (obj->last_read_req[ring] == req) - i915_gem_object_retire__read(obj, ring); - else if (obj->last_write_req == req) - i915_gem_object_retire__write(obj); - - if (!i915_reset_in_progress(&req->i915->gpu_error)) - __i915_gem_request_retire__upto(req); -} - -/* A nonblocking variant of the above wait. This is a highly dangerous routine - * as the object state may change during this call. - */ -static __must_check int -i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, - struct intel_rps_client *rps, - bool readonly) -{ - struct drm_device *dev = obj->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct drm_i915_gem_request *requests[I915_NUM_ENGINES]; - int ret, i, n = 0; - - BUG_ON(!mutex_is_locked(&dev->struct_mutex)); - BUG_ON(!dev_priv->mm.interruptible); - - if (!obj->active) - return 0; - - if (readonly) { - struct drm_i915_gem_request *req; - - req = obj->last_write_req; - if (req == NULL) - return 0; - - requests[n++] = i915_gem_request_reference(req); - } else { - for (i = 0; i < I915_NUM_ENGINES; i++) { - struct drm_i915_gem_request *req; - - req = obj->last_read_req[i]; - if (req == NULL) - continue; - - requests[n++] = i915_gem_request_reference(req); - } - } - - mutex_unlock(&dev->struct_mutex); - ret = 0; - for (i = 0; ret == 0 && i < n; i++) - ret = __i915_wait_request(requests[i], true, NULL, rps); - mutex_lock(&dev->struct_mutex); - - for (i = 0; i < n; i++) { - if (ret == 0) - i915_gem_object_retire_request(obj, requests[i]); - i915_gem_request_unreference(requests[i]); - } +err_rpm: + intel_runtime_pm_put(dev_priv); +err: + i915_gem_object_put_unlocked(obj); return ret; } -static struct intel_rps_client *to_rps_client(struct drm_file *file) -{ - struct drm_i915_file_private *fpriv = file->driver_priv; - return &fpriv->rps; -} - -static enum fb_op_origin +static inline enum fb_op_origin write_origin(struct drm_i915_gem_object *obj, unsigned domain) { - return domain == I915_GEM_DOMAIN_GTT && !obj->has_wc_mmap ? - ORIGIN_GTT : ORIGIN_CPU; + return (domain == I915_GEM_DOMAIN_GTT ? + obj->frontbuffer_ggtt_origin : ORIGIN_CPU); } /** @@ -1828,10 +1520,7 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, int ret; /* Only handle setting domains to types used by the CPU. */ - if (write_domain & I915_GEM_GPU_DOMAINS) - return -EINVAL; - - if (read_domains & I915_GEM_GPU_DOMAINS) + if ((write_domain | read_domains) & I915_GEM_GPU_DOMAINS) return -EINVAL; /* Having something in the write domain implies it's in the read @@ -1840,25 +1529,21 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, if (write_domain != 0 && read_domains != write_domain) return -EINVAL; - ret = i915_mutex_lock_interruptible(dev); - if (ret) - return ret; - - obj = to_intel_bo(drm_gem_object_lookup(file, args->handle)); - if (&obj->base == NULL) { - ret = -ENOENT; - goto unlock; - } + obj = i915_gem_object_lookup(file, args->handle); + if (!obj) + return -ENOENT; /* Try to flush the object off the GPU without holding the lock. * We will repeat the flush holding the lock in the normal manner * to catch cases where we are gazumped. */ - ret = i915_gem_object_wait_rendering__nonblocking(obj, - to_rps_client(file), - !write_domain); + ret = __unsafe_wait_rendering(obj, to_rps_client(file), !write_domain); if (ret) - goto unref; + goto err; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + goto err; if (read_domains & I915_GEM_DOMAIN_GTT) ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0); @@ -1868,11 +1553,13 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, if (write_domain != 0) intel_fb_obj_invalidate(obj, write_origin(obj, write_domain)); -unref: - drm_gem_object_unreference(&obj->base); -unlock: + i915_gem_object_put(obj); mutex_unlock(&dev->struct_mutex); return ret; + +err: + i915_gem_object_put_unlocked(obj); + return ret; } /** @@ -1887,26 +1574,23 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, { struct drm_i915_gem_sw_finish *args = data; struct drm_i915_gem_object *obj; - int ret = 0; + int err = 0; - ret = i915_mutex_lock_interruptible(dev); - if (ret) - return ret; - - obj = to_intel_bo(drm_gem_object_lookup(file, args->handle)); - if (&obj->base == NULL) { - ret = -ENOENT; - goto unlock; - } + obj = i915_gem_object_lookup(file, args->handle); + if (!obj) + return -ENOENT; /* Pinned buffers may be scanout, so flush the cache */ - if (obj->pin_display) - i915_gem_object_flush_cpu_write_domain(obj); + if (READ_ONCE(obj->pin_display)) { + err = i915_mutex_lock_interruptible(dev); + if (!err) { + i915_gem_object_flush_cpu_write_domain(obj); + mutex_unlock(&dev->struct_mutex); + } + } - drm_gem_object_unreference(&obj->base); -unlock: - mutex_unlock(&dev->struct_mutex); - return ret; + i915_gem_object_put_unlocked(obj); + return err; } /** @@ -1934,7 +1618,7 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { struct drm_i915_gem_mmap *args = data; - struct drm_gem_object *obj; + struct drm_i915_gem_object *obj; unsigned long addr; if (args->flags & ~(I915_MMAP_WC)) @@ -1943,19 +1627,19 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, if (args->flags & I915_MMAP_WC && !boot_cpu_has(X86_FEATURE_PAT)) return -ENODEV; - obj = drm_gem_object_lookup(file, args->handle); - if (obj == NULL) + obj = i915_gem_object_lookup(file, args->handle); + if (!obj) return -ENOENT; /* prime objects have no backing filp to GEM mmap * pages from. */ - if (!obj->filp) { - drm_gem_object_unreference_unlocked(obj); + if (!obj->base.filp) { + i915_gem_object_put_unlocked(obj); return -EINVAL; } - addr = vm_mmap(obj->filp, 0, args->size, + addr = vm_mmap(obj->base.filp, 0, args->size, PROT_READ | PROT_WRITE, MAP_SHARED, args->offset); if (args->flags & I915_MMAP_WC) { @@ -1963,7 +1647,7 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, struct vm_area_struct *vma; if (down_write_killable(&mm->mmap_sem)) { - drm_gem_object_unreference_unlocked(obj); + i915_gem_object_put_unlocked(obj); return -EINTR; } vma = find_vma(mm, addr); @@ -1975,9 +1659,9 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, up_write(&mm->mmap_sem); /* This may race, but that's ok, it only gets set */ - WRITE_ONCE(to_intel_bo(obj)->has_wc_mmap, true); + WRITE_ONCE(obj->frontbuffer_ggtt_origin, ORIGIN_CPU); } - drm_gem_object_unreference_unlocked(obj); + i915_gem_object_put_unlocked(obj); if (IS_ERR((void *)addr)) return addr; @@ -1986,9 +1670,69 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, return 0; } +static unsigned int tile_row_pages(struct drm_i915_gem_object *obj) +{ + u64 size; + + size = i915_gem_object_get_stride(obj); + size *= i915_gem_object_get_tiling(obj) == I915_TILING_Y ? 32 : 8; + + return size >> PAGE_SHIFT; +} + +/** + * i915_gem_mmap_gtt_version - report the current feature set for GTT mmaps + * + * A history of the GTT mmap interface: + * + * 0 - Everything had to fit into the GTT. Both parties of a memcpy had to + * aligned and suitable for fencing, and still fit into the available + * mappable space left by the pinned display objects. A classic problem + * we called the page-fault-of-doom where we would ping-pong between + * two objects that could not fit inside the GTT and so the memcpy + * would page one object in at the expense of the other between every + * single byte. + * + * 1 - Objects can be any size, and have any compatible fencing (X Y, or none + * as set via i915_gem_set_tiling() [DRM_I915_GEM_SET_TILING]). If the + * object is too large for the available space (or simply too large + * for the mappable aperture!), a view is created instead and faulted + * into userspace. (This view is aligned and sized appropriately for + * fenced access.) + * + * Restrictions: + * + * * snoopable objects cannot be accessed via the GTT. It can cause machine + * hangs on some architectures, corruption on others. An attempt to service + * a GTT page fault from a snoopable object will generate a SIGBUS. + * + * * the object must be able to fit into RAM (physical memory, though no + * limited to the mappable aperture). + * + * + * Caveats: + * + * * a new GTT page fault will synchronize rendering from the GPU and flush + * all data to system memory. Subsequent access will not be synchronized. + * + * * all mappings are revoked on runtime device suspend. + * + * * there are only 8, 16 or 32 fence registers to share between all users + * (older machines require fence register for display and blitter access + * as well). Contention of the fence registers will cause the previous users + * to be unmapped and any new access will generate new page faults. + * + * * running out of memory while servicing a fault may generate a SIGBUS, + * rather than the expected SIGSEGV. + */ +int i915_gem_mmap_gtt_version(void) +{ + return 1; +} + /** * i915_gem_fault - fault a page into the GTT - * @vma: VMA in question + * @area: CPU VMA in question * @vmf: fault info * * The fault handler is set up by drm_gem_mmap() when a object is GTT mapped @@ -2001,122 +1745,120 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, * from the GTT and/or fence registers to make room. So performance may * suffer if the GTT working set is large or there are few fence registers * left. + * + * The current feature set supported by i915_gem_fault() and thus GTT mmaps + * is exposed via I915_PARAM_MMAP_GTT_VERSION (see i915_gem_mmap_gtt_version). */ -int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +int i915_gem_fault(struct vm_area_struct *area, struct vm_fault *vmf) { - struct drm_i915_gem_object *obj = to_intel_bo(vma->vm_private_data); +#define MIN_CHUNK_PAGES ((1 << 20) >> PAGE_SHIFT) /* 1 MiB */ + struct drm_i915_gem_object *obj = to_intel_bo(area->vm_private_data); struct drm_device *dev = obj->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); struct i915_ggtt *ggtt = &dev_priv->ggtt; - struct i915_ggtt_view view = i915_ggtt_view_normal; - pgoff_t page_offset; - unsigned long pfn; - int ret = 0; bool write = !!(vmf->flags & FAULT_FLAG_WRITE); - - intel_runtime_pm_get(dev_priv); + struct i915_vma *vma; + pgoff_t page_offset; + unsigned int flags; + int ret; /* We don't use vmf->pgoff since that has the fake offset */ - page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >> + page_offset = ((unsigned long)vmf->virtual_address - area->vm_start) >> PAGE_SHIFT; - ret = i915_mutex_lock_interruptible(dev); - if (ret) - goto out; - trace_i915_gem_object_fault(obj, page_offset, true, write); /* Try to flush the object off the GPU first without holding the lock. - * Upon reacquiring the lock, we will perform our sanity checks and then + * Upon acquiring the lock, we will perform our sanity checks and then * repeat the flush holding the lock in the normal manner to catch cases * where we are gazumped. */ - ret = i915_gem_object_wait_rendering__nonblocking(obj, NULL, !write); + ret = __unsafe_wait_rendering(obj, NULL, !write); if (ret) - goto unlock; + goto err; + + intel_runtime_pm_get(dev_priv); + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + goto err_rpm; /* Access to snoopable pages through the GTT is incoherent. */ if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(dev)) { ret = -EFAULT; - goto unlock; + goto err_unlock; } - /* Use a partial view if the object is bigger than the aperture. */ - if (obj->base.size >= ggtt->mappable_end && - obj->tiling_mode == I915_TILING_NONE) { - static const unsigned int chunk_size = 256; // 1 MiB + /* If the object is smaller than a couple of partial vma, it is + * not worth only creating a single partial vma - we may as well + * clear enough space for the full object. + */ + flags = PIN_MAPPABLE; + if (obj->base.size > 2 * MIN_CHUNK_PAGES << PAGE_SHIFT) + flags |= PIN_NONBLOCK | PIN_NONFAULT; + + /* Now pin it into the GTT as needed */ + vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, flags); + if (IS_ERR(vma)) { + struct i915_ggtt_view view; + unsigned int chunk_size; + + /* Use a partial view if it is bigger than available space */ + chunk_size = MIN_CHUNK_PAGES; + if (i915_gem_object_is_tiled(obj)) + chunk_size = max(chunk_size, tile_row_pages(obj)); memset(&view, 0, sizeof(view)); view.type = I915_GGTT_VIEW_PARTIAL; view.params.partial.offset = rounddown(page_offset, chunk_size); view.params.partial.size = - min_t(unsigned int, - chunk_size, - (vma->vm_end - vma->vm_start)/PAGE_SIZE - + min_t(unsigned int, chunk_size, + (area->vm_end - area->vm_start) / PAGE_SIZE - view.params.partial.offset); - } - /* Now pin it into the GTT if needed */ - ret = i915_gem_object_ggtt_pin(obj, &view, 0, PIN_MAPPABLE); - if (ret) - goto unlock; + /* If the partial covers the entire object, just create a + * normal VMA. + */ + if (chunk_size >= obj->base.size >> PAGE_SHIFT) + view.type = I915_GGTT_VIEW_NORMAL; + + /* Userspace is now writing through an untracked VMA, abandon + * all hope that the hardware is able to track future writes. + */ + obj->frontbuffer_ggtt_origin = ORIGIN_CPU; + + vma = i915_gem_object_ggtt_pin(obj, &view, 0, 0, PIN_MAPPABLE); + } + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); + goto err_unlock; + } ret = i915_gem_object_set_to_gtt_domain(obj, write); if (ret) - goto unpin; + goto err_unpin; - ret = i915_gem_object_get_fence(obj); + ret = i915_vma_get_fence(vma); if (ret) - goto unpin; + goto err_unpin; /* Finally, remap it using the new GTT offset */ - pfn = ggtt->mappable_base + - i915_gem_obj_ggtt_offset_view(obj, &view); - pfn >>= PAGE_SHIFT; - - if (unlikely(view.type == I915_GGTT_VIEW_PARTIAL)) { - /* Overriding existing pages in partial view does not cause - * us any trouble as TLBs are still valid because the fault - * is due to userspace losing part of the mapping or never - * having accessed it before (at this partials' range). - */ - unsigned long base = vma->vm_start + - (view.params.partial.offset << PAGE_SHIFT); - unsigned int i; - - for (i = 0; i < view.params.partial.size; i++) { - ret = vm_insert_pfn(vma, base + i * PAGE_SIZE, pfn + i); - if (ret) - break; - } - - obj->fault_mappable = true; - } else { - if (!obj->fault_mappable) { - unsigned long size = min_t(unsigned long, - vma->vm_end - vma->vm_start, - obj->base.size); - int i; - - for (i = 0; i < size >> PAGE_SHIFT; i++) { - ret = vm_insert_pfn(vma, - (unsigned long)vma->vm_start + i * PAGE_SIZE, - pfn + i); - if (ret) - break; - } + ret = remap_io_mapping(area, + area->vm_start + (vma->ggtt_view.params.partial.offset << PAGE_SHIFT), + (ggtt->mappable_base + vma->node.start) >> PAGE_SHIFT, + min_t(u64, vma->size, area->vm_end - area->vm_start), + &ggtt->mappable); + if (ret) + goto err_unpin; - obj->fault_mappable = true; - } else - ret = vm_insert_pfn(vma, - (unsigned long)vmf->virtual_address, - pfn + page_offset); - } -unpin: - i915_gem_object_ggtt_unpin_view(obj, &view); -unlock: + obj->fault_mappable = true; +err_unpin: + __i915_vma_unpin(vma); +err_unlock: mutex_unlock(&dev->struct_mutex); -out: +err_rpm: + intel_runtime_pm_put(dev_priv); +err: switch (ret) { case -EIO: /* @@ -2157,8 +1899,6 @@ out: ret = VM_FAULT_SIGBUS; break; } - - intel_runtime_pm_put(dev_priv); return ret; } @@ -2212,46 +1952,58 @@ i915_gem_release_all_mmaps(struct drm_i915_private *dev_priv) i915_gem_release_mmap(obj); } -uint32_t -i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode) +/** + * i915_gem_get_ggtt_size - return required global GTT size for an object + * @dev_priv: i915 device + * @size: object size + * @tiling_mode: tiling mode + * + * Return the required global GTT size for an object, taking into account + * potential fence register mapping. + */ +u64 i915_gem_get_ggtt_size(struct drm_i915_private *dev_priv, + u64 size, int tiling_mode) { - uint32_t gtt_size; + u64 ggtt_size; + + GEM_BUG_ON(size == 0); - if (INTEL_INFO(dev)->gen >= 4 || + if (INTEL_GEN(dev_priv) >= 4 || tiling_mode == I915_TILING_NONE) return size; /* Previous chips need a power-of-two fence region when tiling */ - if (IS_GEN3(dev)) - gtt_size = 1024*1024; + if (IS_GEN3(dev_priv)) + ggtt_size = 1024*1024; else - gtt_size = 512*1024; + ggtt_size = 512*1024; - while (gtt_size < size) - gtt_size <<= 1; + while (ggtt_size < size) + ggtt_size <<= 1; - return gtt_size; + return ggtt_size; } /** - * i915_gem_get_gtt_alignment - return required GTT alignment for an object - * @dev: drm device + * i915_gem_get_ggtt_alignment - return required global GTT alignment + * @dev_priv: i915 device * @size: object size * @tiling_mode: tiling mode - * @fenced: is fenced alignemned required or not + * @fenced: is fenced alignment required or not * - * Return the required GTT alignment for an object, taking into account + * Return the required global GTT alignment for an object, taking into account * potential fence register mapping. */ -uint32_t -i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size, - int tiling_mode, bool fenced) +u64 i915_gem_get_ggtt_alignment(struct drm_i915_private *dev_priv, u64 size, + int tiling_mode, bool fenced) { + GEM_BUG_ON(size == 0); + /* * Minimum alignment is 4k (GTT page size), but might be greater * if a fence register is needed for the object. */ - if (INTEL_INFO(dev)->gen >= 4 || (!fenced && IS_G33(dev)) || + if (INTEL_GEN(dev_priv) >= 4 || (!fenced && IS_G33(dev_priv)) || tiling_mode == I915_TILING_NONE) return 4096; @@ -2259,42 +2011,34 @@ i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size, * Previous chips need to be aligned to the size of the smallest * fence register that can contain the object. */ - return i915_gem_get_gtt_size(dev, size, tiling_mode); + return i915_gem_get_ggtt_size(dev_priv, size, tiling_mode); } static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj) { struct drm_i915_private *dev_priv = to_i915(obj->base.dev); - int ret; - - dev_priv->mm.shrinker_no_lock_stealing = true; + int err; - ret = drm_gem_create_mmap_offset(&obj->base); - if (ret != -ENOSPC) - goto out; + err = drm_gem_create_mmap_offset(&obj->base); + if (!err) + return 0; - /* Badly fragmented mmap space? The only way we can recover - * space is by destroying unwanted objects. We can't randomly release - * mmap_offsets as userspace expects them to be persistent for the - * lifetime of the objects. The closest we can is to release the - * offsets on purgeable objects by truncating it and marking it purged, - * which prevents userspace from ever using that object again. + /* We can idle the GPU locklessly to flush stale objects, but in order + * to claim that space for ourselves, we need to take the big + * struct_mutex to free the requests+objects and allocate our slot. */ - i915_gem_shrink(dev_priv, - obj->base.size >> PAGE_SHIFT, - I915_SHRINK_BOUND | - I915_SHRINK_UNBOUND | - I915_SHRINK_PURGEABLE); - ret = drm_gem_create_mmap_offset(&obj->base); - if (ret != -ENOSPC) - goto out; + err = i915_gem_wait_for_idle(dev_priv, I915_WAIT_INTERRUPTIBLE); + if (err) + return err; - i915_gem_shrink_all(dev_priv); - ret = drm_gem_create_mmap_offset(&obj->base); -out: - dev_priv->mm.shrinker_no_lock_stealing = false; + err = i915_mutex_lock_interruptible(&dev_priv->drm); + if (!err) { + i915_gem_retire_requests(dev_priv); + err = drm_gem_create_mmap_offset(&obj->base); + mutex_unlock(&dev_priv->drm.struct_mutex); + } - return ret; + return err; } static void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj) @@ -2311,32 +2055,15 @@ i915_gem_mmap_gtt(struct drm_file *file, struct drm_i915_gem_object *obj; int ret; - ret = i915_mutex_lock_interruptible(dev); - if (ret) - return ret; - - obj = to_intel_bo(drm_gem_object_lookup(file, handle)); - if (&obj->base == NULL) { - ret = -ENOENT; - goto unlock; - } - - if (obj->madv != I915_MADV_WILLNEED) { - DRM_DEBUG("Attempting to mmap a purgeable buffer\n"); - ret = -EFAULT; - goto out; - } + obj = i915_gem_object_lookup(file, handle); + if (!obj) + return -ENOENT; ret = i915_gem_object_create_mmap_offset(obj); - if (ret) - goto out; - - *offset = drm_vma_node_offset_addr(&obj->base.vma_node); + if (ret == 0) + *offset = drm_vma_node_offset_addr(&obj->base.vma_node); -out: - drm_gem_object_unreference(&obj->base); -unlock: - mutex_unlock(&dev->struct_mutex); + i915_gem_object_put_unlocked(obj); return ret; } @@ -2454,7 +2181,7 @@ i915_gem_object_put_pages(struct drm_i915_gem_object *obj) if (obj->pages_pin_count) return -EBUSY; - BUG_ON(i915_gem_obj_bound_any(obj)); + GEM_BUG_ON(obj->bind_count); /* ->put_pages might need to allocate memory for the bit17 swizzle * array, hence protect them from being reaped by removing them from gtt @@ -2462,10 +2189,14 @@ i915_gem_object_put_pages(struct drm_i915_gem_object *obj) list_del(&obj->global_list); if (obj->mapping) { - if (is_vmalloc_addr(obj->mapping)) - vunmap(obj->mapping); + void *ptr; + + ptr = ptr_mask_bits(obj->mapping); + if (is_vmalloc_addr(ptr)) + vunmap(ptr); else - kunmap(kmap_to_page(obj->mapping)); + kunmap(kmap_to_page(ptr)); + obj->mapping = NULL; } @@ -2574,7 +2305,7 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) if (i915_gem_object_needs_bit17_swizzle(obj)) i915_gem_object_do_bit_17_swizzle(obj); - if (obj->tiling_mode != I915_TILING_NONE && + if (i915_gem_object_is_tiled(obj) && dev_priv->quirks & QUIRK_PIN_SWIZZLED_PAGES) i915_gem_object_pin_pages(obj); @@ -2638,7 +2369,8 @@ i915_gem_object_get_pages(struct drm_i915_gem_object *obj) } /* The 'mapping' part of i915_gem_object_pin_map() below */ -static void *i915_gem_object_map(const struct drm_i915_gem_object *obj) +static void *i915_gem_object_map(const struct drm_i915_gem_object *obj, + enum i915_map_type type) { unsigned long n_pages = obj->base.size >> PAGE_SHIFT; struct sg_table *sgt = obj->pages; @@ -2647,10 +2379,11 @@ static void *i915_gem_object_map(const struct drm_i915_gem_object *obj) struct page *stack_pages[32]; struct page **pages = stack_pages; unsigned long i = 0; + pgprot_t pgprot; void *addr; /* A single page can always be kmapped */ - if (n_pages == 1) + if (n_pages == 1 && type == I915_MAP_WB) return kmap(sg_page(sgt->sgl)); if (n_pages > ARRAY_SIZE(stack_pages)) { @@ -2666,7 +2399,15 @@ static void *i915_gem_object_map(const struct drm_i915_gem_object *obj) /* Check that we have the expected number of pages */ GEM_BUG_ON(i != n_pages); - addr = vmap(pages, n_pages, 0, PAGE_KERNEL); + switch (type) { + case I915_MAP_WB: + pgprot = PAGE_KERNEL; + break; + case I915_MAP_WC: + pgprot = pgprot_writecombine(PAGE_KERNEL_IO); + break; + } + addr = vmap(pages, n_pages, 0, pgprot); if (pages != stack_pages) drm_free_large(pages); @@ -2675,276 +2416,89 @@ static void *i915_gem_object_map(const struct drm_i915_gem_object *obj) } /* get, pin, and map the pages of the object into kernel space */ -void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj) +void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj, + enum i915_map_type type) { + enum i915_map_type has_type; + bool pinned; + void *ptr; int ret; lockdep_assert_held(&obj->base.dev->struct_mutex); + GEM_BUG_ON(!i915_gem_object_has_struct_page(obj)); ret = i915_gem_object_get_pages(obj); if (ret) return ERR_PTR(ret); i915_gem_object_pin_pages(obj); + pinned = obj->pages_pin_count > 1; - if (!obj->mapping) { - obj->mapping = i915_gem_object_map(obj); - if (!obj->mapping) { - i915_gem_object_unpin_pages(obj); - return ERR_PTR(-ENOMEM); + ptr = ptr_unpack_bits(obj->mapping, has_type); + if (ptr && has_type != type) { + if (pinned) { + ret = -EBUSY; + goto err; } - } - return obj->mapping; -} + if (is_vmalloc_addr(ptr)) + vunmap(ptr); + else + kunmap(kmap_to_page(ptr)); -void i915_vma_move_to_active(struct i915_vma *vma, - struct drm_i915_gem_request *req) -{ - struct drm_i915_gem_object *obj = vma->obj; - struct intel_engine_cs *engine; + ptr = obj->mapping = NULL; + } - engine = i915_gem_request_get_engine(req); + if (!ptr) { + ptr = i915_gem_object_map(obj, type); + if (!ptr) { + ret = -ENOMEM; + goto err; + } - /* Add a reference if we're newly entering the active list. */ - if (obj->active == 0) - drm_gem_object_reference(&obj->base); - obj->active |= intel_engine_flag(engine); + obj->mapping = ptr_pack_bits(ptr, type); + } - list_move_tail(&obj->engine_list[engine->id], &engine->active_list); - i915_gem_request_assign(&obj->last_read_req[engine->id], req); + return ptr; - list_move_tail(&vma->vm_link, &vma->vm->active_list); +err: + i915_gem_object_unpin_pages(obj); + return ERR_PTR(ret); } static void -i915_gem_object_retire__write(struct drm_i915_gem_object *obj) +i915_gem_object_retire__write(struct i915_gem_active *active, + struct drm_i915_gem_request *request) { - GEM_BUG_ON(obj->last_write_req == NULL); - GEM_BUG_ON(!(obj->active & intel_engine_flag(obj->last_write_req->engine))); + struct drm_i915_gem_object *obj = + container_of(active, struct drm_i915_gem_object, last_write); - i915_gem_request_assign(&obj->last_write_req, NULL); intel_fb_obj_flush(obj, true, ORIGIN_CS); } static void -i915_gem_object_retire__read(struct drm_i915_gem_object *obj, int ring) +i915_gem_object_retire__read(struct i915_gem_active *active, + struct drm_i915_gem_request *request) { - struct i915_vma *vma; + int idx = request->engine->id; + struct drm_i915_gem_object *obj = + container_of(active, struct drm_i915_gem_object, last_read[idx]); - GEM_BUG_ON(obj->last_read_req[ring] == NULL); - GEM_BUG_ON(!(obj->active & (1 << ring))); + GEM_BUG_ON(!i915_gem_object_has_active_engine(obj, idx)); - list_del_init(&obj->engine_list[ring]); - i915_gem_request_assign(&obj->last_read_req[ring], NULL); - - if (obj->last_write_req && obj->last_write_req->engine->id == ring) - i915_gem_object_retire__write(obj); - - obj->active &= ~(1 << ring); - if (obj->active) + i915_gem_object_clear_active(obj, idx); + if (i915_gem_object_is_active(obj)) return; /* Bump our place on the bound list to keep it roughly in LRU order * so that we don't steal from recently used but inactive objects * (unless we are forced to ofc!) */ - list_move_tail(&obj->global_list, - &to_i915(obj->base.dev)->mm.bound_list); - - list_for_each_entry(vma, &obj->vma_list, obj_link) { - if (!list_empty(&vma->vm_link)) - list_move_tail(&vma->vm_link, &vma->vm->inactive_list); - } - - i915_gem_request_assign(&obj->last_fenced_req, NULL); - drm_gem_object_unreference(&obj->base); -} - -static int -i915_gem_init_seqno(struct drm_i915_private *dev_priv, u32 seqno) -{ - struct intel_engine_cs *engine; - int ret; - - /* Carefully retire all requests without writing to the rings */ - for_each_engine(engine, dev_priv) { - ret = intel_engine_idle(engine); - if (ret) - return ret; - } - i915_gem_retire_requests(dev_priv); - - /* If the seqno wraps around, we need to clear the breadcrumb rbtree */ - if (!i915_seqno_passed(seqno, dev_priv->next_seqno)) { - while (intel_kick_waiters(dev_priv) || - intel_kick_signalers(dev_priv)) - yield(); - } - - /* Finally reset hw state */ - for_each_engine(engine, dev_priv) - intel_ring_init_seqno(engine, seqno); - - return 0; -} - -int i915_gem_set_seqno(struct drm_device *dev, u32 seqno) -{ - struct drm_i915_private *dev_priv = to_i915(dev); - int ret; - - if (seqno == 0) - return -EINVAL; - - /* HWS page needs to be set less than what we - * will inject to ring - */ - ret = i915_gem_init_seqno(dev_priv, seqno - 1); - if (ret) - return ret; - - /* Carefully set the last_seqno value so that wrap - * detection still works - */ - dev_priv->next_seqno = seqno; - dev_priv->last_seqno = seqno - 1; - if (dev_priv->last_seqno == 0) - dev_priv->last_seqno--; - - return 0; -} - -int -i915_gem_get_seqno(struct drm_i915_private *dev_priv, u32 *seqno) -{ - /* reserve 0 for non-seqno */ - if (dev_priv->next_seqno == 0) { - int ret = i915_gem_init_seqno(dev_priv, 0); - if (ret) - return ret; - - dev_priv->next_seqno = 1; - } - - *seqno = dev_priv->last_seqno = dev_priv->next_seqno++; - return 0; -} - -static void i915_gem_mark_busy(const struct intel_engine_cs *engine) -{ - struct drm_i915_private *dev_priv = engine->i915; - - dev_priv->gt.active_engines |= intel_engine_flag(engine); - if (dev_priv->gt.awake) - return; - - intel_runtime_pm_get_noresume(dev_priv); - dev_priv->gt.awake = true; - - i915_update_gfx_val(dev_priv); - if (INTEL_GEN(dev_priv) >= 6) - gen6_rps_busy(dev_priv); + if (obj->bind_count) + list_move_tail(&obj->global_list, + &request->i915->mm.bound_list); - queue_delayed_work(dev_priv->wq, - &dev_priv->gt.retire_work, - round_jiffies_up_relative(HZ)); -} - -/* - * NB: This function is not allowed to fail. Doing so would mean the the - * request is not being tracked for completion but the work itself is - * going to happen on the hardware. This would be a Bad Thing(tm). - */ -void __i915_add_request(struct drm_i915_gem_request *request, - struct drm_i915_gem_object *obj, - bool flush_caches) -{ - struct intel_engine_cs *engine; - struct intel_ringbuffer *ringbuf; - u32 request_start; - u32 reserved_tail; - int ret; - - if (WARN_ON(request == NULL)) - return; - - engine = request->engine; - ringbuf = request->ringbuf; - - /* - * To ensure that this call will not fail, space for its emissions - * should already have been reserved in the ring buffer. Let the ring - * know that it is time to use that space up. - */ - request_start = intel_ring_get_tail(ringbuf); - reserved_tail = request->reserved_space; - request->reserved_space = 0; - - /* - * Emit any outstanding flushes - execbuf can fail to emit the flush - * after having emitted the batchbuffer command. Hence we need to fix - * things up similar to emitting the lazy request. The difference here - * is that the flush _must_ happen before the next request, no matter - * what. - */ - if (flush_caches) { - if (i915.enable_execlists) - ret = logical_ring_flush_all_caches(request); - else - ret = intel_ring_flush_all_caches(request); - /* Not allowed to fail! */ - WARN(ret, "*_ring_flush_all_caches failed: %d!\n", ret); - } - - trace_i915_gem_request_add(request); - - request->head = request_start; - - /* Whilst this request exists, batch_obj will be on the - * active_list, and so will hold the active reference. Only when this - * request is retired will the the batch_obj be moved onto the - * inactive_list and lose its active reference. Hence we do not need - * to explicitly hold another reference here. - */ - request->batch_obj = obj; - - /* Seal the request and mark it as pending execution. Note that - * we may inspect this state, without holding any locks, during - * hangcheck. Hence we apply the barrier to ensure that we do not - * see a more recent value in the hws than we are tracking. - */ - request->emitted_jiffies = jiffies; - request->previous_seqno = engine->last_submitted_seqno; - smp_store_mb(engine->last_submitted_seqno, request->seqno); - list_add_tail(&request->list, &engine->request_list); - - /* Record the position of the start of the request so that - * should we detect the updated seqno part-way through the - * GPU processing the request, we never over-estimate the - * position of the head. - */ - request->postfix = intel_ring_get_tail(ringbuf); - - if (i915.enable_execlists) - ret = engine->emit_request(request); - else { - ret = engine->add_request(request); - - request->tail = intel_ring_get_tail(ringbuf); - } - /* Not allowed to fail! */ - WARN(ret, "emit|add_request failed: %d!\n", ret); - /* Sanity check that the reserved size was large enough. */ - ret = intel_ring_get_tail(ringbuf) - request_start; - if (ret < 0) - ret += ringbuf->size; - WARN_ONCE(ret > reserved_tail, - "Not enough space reserved (%d bytes) " - "for adding the request (%d bytes)\n", - reserved_tail, ret); - - i915_gem_mark_busy(engine); + i915_gem_object_put(obj); } static bool i915_context_is_banned(const struct i915_gem_context *ctx) @@ -2978,101 +2532,6 @@ static void i915_set_reset_status(struct i915_gem_context *ctx, } } -void i915_gem_request_free(struct kref *req_ref) -{ - struct drm_i915_gem_request *req = container_of(req_ref, - typeof(*req), ref); - kmem_cache_free(req->i915->requests, req); -} - -static inline int -__i915_gem_request_alloc(struct intel_engine_cs *engine, - struct i915_gem_context *ctx, - struct drm_i915_gem_request **req_out) -{ - struct drm_i915_private *dev_priv = engine->i915; - unsigned reset_counter = i915_reset_counter(&dev_priv->gpu_error); - struct drm_i915_gem_request *req; - int ret; - - if (!req_out) - return -EINVAL; - - *req_out = NULL; - - /* ABI: Before userspace accesses the GPU (e.g. execbuffer), report - * EIO if the GPU is already wedged, or EAGAIN to drop the struct_mutex - * and restart. - */ - ret = i915_gem_check_wedge(reset_counter, dev_priv->mm.interruptible); - if (ret) - return ret; - - req = kmem_cache_zalloc(dev_priv->requests, GFP_KERNEL); - if (req == NULL) - return -ENOMEM; - - ret = i915_gem_get_seqno(engine->i915, &req->seqno); - if (ret) - goto err; - - kref_init(&req->ref); - req->i915 = dev_priv; - req->engine = engine; - req->ctx = ctx; - i915_gem_context_reference(req->ctx); - - /* - * Reserve space in the ring buffer for all the commands required to - * eventually emit this request. This is to guarantee that the - * i915_add_request() call can't fail. Note that the reserve may need - * to be redone if the request is not actually submitted straight - * away, e.g. because a GPU scheduler has deferred it. - */ - req->reserved_space = MIN_SPACE_FOR_ADD_REQUEST; - - if (i915.enable_execlists) - ret = intel_logical_ring_alloc_request_extras(req); - else - ret = intel_ring_alloc_request_extras(req); - if (ret) - goto err_ctx; - - *req_out = req; - return 0; - -err_ctx: - i915_gem_context_unreference(ctx); -err: - kmem_cache_free(dev_priv->requests, req); - return ret; -} - -/** - * i915_gem_request_alloc - allocate a request structure - * - * @engine: engine that we wish to issue the request on. - * @ctx: context that the request will be associated with. - * This can be NULL if the request is not directly related to - * any specific user context, in which case this function will - * choose an appropriate context to use. - * - * Returns a pointer to the allocated request if successful, - * or an error code if not. - */ -struct drm_i915_gem_request * -i915_gem_request_alloc(struct intel_engine_cs *engine, - struct i915_gem_context *ctx) -{ - struct drm_i915_gem_request *req; - int err; - - if (ctx == NULL) - ctx = engine->i915->kernel_context; - err = __i915_gem_request_alloc(engine, ctx, &req); - return err ? ERR_PTR(err) : req; -} - struct drm_i915_gem_request * i915_gem_find_active_request(struct intel_engine_cs *engine) { @@ -3086,182 +2545,139 @@ i915_gem_find_active_request(struct intel_engine_cs *engine) * extra delay for a recent interrupt is pointless. Hence, we do * not need an engine->irq_seqno_barrier() before the seqno reads. */ - list_for_each_entry(request, &engine->request_list, list) { + list_for_each_entry(request, &engine->request_list, link) { if (i915_gem_request_completed(request)) continue; + if (!i915_sw_fence_done(&request->submit)) + break; + return request; } return NULL; } -static void i915_gem_reset_engine_status(struct intel_engine_cs *engine) +static void reset_request(struct drm_i915_gem_request *request) +{ + void *vaddr = request->ring->vaddr; + u32 head; + + /* As this request likely depends on state from the lost + * context, clear out all the user operations leaving the + * breadcrumb at the end (so we get the fence notifications). + */ + head = request->head; + if (request->postfix < head) { + memset(vaddr + head, 0, request->ring->size - head); + head = 0; + } + memset(vaddr + head, 0, request->postfix - head); +} + +static void i915_gem_reset_engine(struct intel_engine_cs *engine) { struct drm_i915_gem_request *request; + struct i915_gem_context *incomplete_ctx; bool ring_hung; + /* Ensure irq handler finishes, and not run again. */ + tasklet_kill(&engine->irq_tasklet); + if (engine->irq_seqno_barrier) + engine->irq_seqno_barrier(engine); + request = i915_gem_find_active_request(engine); - if (request == NULL) + if (!request) return; ring_hung = engine->hangcheck.score >= HANGCHECK_SCORE_RING_HUNG; - i915_set_reset_status(request->ctx, ring_hung); - list_for_each_entry_continue(request, &engine->request_list, list) - i915_set_reset_status(request->ctx, false); -} - -static void i915_gem_reset_engine_cleanup(struct intel_engine_cs *engine) -{ - struct intel_ringbuffer *buffer; - - while (!list_empty(&engine->active_list)) { - struct drm_i915_gem_object *obj; - - obj = list_first_entry(&engine->active_list, - struct drm_i915_gem_object, - engine_list[engine->id]); - - i915_gem_object_retire__read(obj, engine->id); - } - - /* - * Clear the execlists queue up before freeing the requests, as those - * are the ones that keep the context and ringbuffer backing objects - * pinned in place. - */ + if (!ring_hung) + return; - if (i915.enable_execlists) { - /* Ensure irq handler finishes or is cancelled. */ - tasklet_kill(&engine->irq_tasklet); + DRM_DEBUG_DRIVER("resetting %s to restart from tail of request 0x%x\n", + engine->name, request->fence.seqno); - intel_execlists_cancel_requests(engine); - } + /* Setup the CS to resume from the breadcrumb of the hung request */ + engine->reset_hw(engine, request); - /* - * We must free the requests after all the corresponding objects have - * been moved off active lists. Which is the same order as the normal - * retire_requests function does. This is important if object hold - * implicit references on things like e.g. ppgtt address spaces through - * the request. + /* Users of the default context do not rely on logical state + * preserved between batches. They have to emit full state on + * every batch and so it is safe to execute queued requests following + * the hang. + * + * Other contexts preserve state, now corrupt. We want to skip all + * queued requests that reference the corrupt context. */ - while (!list_empty(&engine->request_list)) { - struct drm_i915_gem_request *request; - - request = list_first_entry(&engine->request_list, - struct drm_i915_gem_request, - list); - - i915_gem_request_retire(request); - } + incomplete_ctx = request->ctx; + if (i915_gem_context_is_default(incomplete_ctx)) + return; - /* Having flushed all requests from all queues, we know that all - * ringbuffers must now be empty. However, since we do not reclaim - * all space when retiring the request (to prevent HEADs colliding - * with rapid ringbuffer wraparound) the amount of available space - * upon reset is less than when we start. Do one more pass over - * all the ringbuffers to reset last_retired_head. - */ - list_for_each_entry(buffer, &engine->buffers, link) { - buffer->last_retired_head = buffer->tail; - intel_ring_update_space(buffer); - } + list_for_each_entry_continue(request, &engine->request_list, link) + if (request->ctx == incomplete_ctx) + reset_request(request); - intel_ring_init_seqno(engine, engine->last_submitted_seqno); + engine->i915->gt.active_engines &= ~intel_engine_flag(engine); } -void i915_gem_reset(struct drm_device *dev) +void i915_gem_reset(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_engine_cs *engine; - /* - * Before we free the objects from the requests, we need to inspect - * them for finding the guilty party. As the requests only borrow - * their reference to the objects, the inspection must be done first. - */ - for_each_engine(engine, dev_priv) - i915_gem_reset_engine_status(engine); + i915_gem_retire_requests(dev_priv); for_each_engine(engine, dev_priv) - i915_gem_reset_engine_cleanup(engine); - - i915_gem_context_reset(dev); + i915_gem_reset_engine(engine); + mod_delayed_work(dev_priv->wq, &dev_priv->gt.idle_work, 0); - i915_gem_restore_fences(dev); + i915_gem_restore_fences(&dev_priv->drm); +} - WARN_ON(i915_verify_lists(dev)); +static void nop_submit_request(struct drm_i915_gem_request *request) +{ } -/** - * This function clears the request list as sequence numbers are passed. - * @engine: engine to retire requests on - */ -void -i915_gem_retire_requests_ring(struct intel_engine_cs *engine) +static void i915_gem_cleanup_engine(struct intel_engine_cs *engine) { - WARN_ON(i915_verify_lists(engine->dev)); + engine->submit_request = nop_submit_request; - /* Retire requests first as we use it above for the early return. - * If we retire requests last, we may use a later seqno and so clear - * the requests lists without clearing the active list, leading to - * confusion. + /* Mark all pending requests as complete so that any concurrent + * (lockless) lookup doesn't try and wait upon the request as we + * reset it. */ - while (!list_empty(&engine->request_list)) { - struct drm_i915_gem_request *request; - - request = list_first_entry(&engine->request_list, - struct drm_i915_gem_request, - list); - - if (!i915_gem_request_completed(request)) - break; - - i915_gem_request_retire(request); - } + intel_engine_init_seqno(engine, engine->last_submitted_seqno); - /* Move any buffers on the active list that are no longer referenced - * by the ringbuffer to the flushing/inactive lists as appropriate, - * before we free the context associated with the requests. + /* + * Clear the execlists queue up before freeing the requests, as those + * are the ones that keep the context and ringbuffer backing objects + * pinned in place. */ - while (!list_empty(&engine->active_list)) { - struct drm_i915_gem_object *obj; - obj = list_first_entry(&engine->active_list, - struct drm_i915_gem_object, - engine_list[engine->id]); - - if (!list_empty(&obj->last_read_req[engine->id]->list)) - break; - - i915_gem_object_retire__read(obj, engine->id); + if (i915.enable_execlists) { + spin_lock(&engine->execlist_lock); + INIT_LIST_HEAD(&engine->execlist_queue); + i915_gem_request_put(engine->execlist_port[0].request); + i915_gem_request_put(engine->execlist_port[1].request); + memset(engine->execlist_port, 0, sizeof(engine->execlist_port)); + spin_unlock(&engine->execlist_lock); } - WARN_ON(i915_verify_lists(engine->dev)); + engine->i915->gt.active_engines &= ~intel_engine_flag(engine); } -void i915_gem_retire_requests(struct drm_i915_private *dev_priv) +void i915_gem_set_wedged(struct drm_i915_private *dev_priv) { struct intel_engine_cs *engine; lockdep_assert_held(&dev_priv->drm.struct_mutex); + set_bit(I915_WEDGED, &dev_priv->gpu_error.flags); - if (dev_priv->gt.active_engines == 0) - return; - - GEM_BUG_ON(!dev_priv->gt.awake); - - for_each_engine(engine, dev_priv) { - i915_gem_retire_requests_ring(engine); - if (list_empty(&engine->request_list)) - dev_priv->gt.active_engines &= ~intel_engine_flag(engine); - } + i915_gem_context_lost(dev_priv); + for_each_engine(engine, dev_priv) + i915_gem_cleanup_engine(engine); + mod_delayed_work(dev_priv->wq, &dev_priv->gt.idle_work, 0); - if (dev_priv->gt.active_engines == 0) - queue_delayed_work(dev_priv->wq, - &dev_priv->gt.idle_work, - msecs_to_jiffies(100)); + i915_gem_retire_requests(dev_priv); } static void @@ -3281,10 +2697,12 @@ i915_gem_retire_work_handler(struct work_struct *work) * We do not need to do this test under locking as in the worst-case * we queue the retire worker once too often. */ - if (READ_ONCE(dev_priv->gt.awake)) + if (READ_ONCE(dev_priv->gt.awake)) { + i915_queue_hangcheck(dev_priv); queue_delayed_work(dev_priv->wq, &dev_priv->gt.retire_work, round_jiffies_up_relative(HZ)); + } } static void @@ -3294,7 +2712,6 @@ i915_gem_idle_work_handler(struct work_struct *work) container_of(work, typeof(*dev_priv), gt.idle_work.work); struct drm_device *dev = &dev_priv->drm; struct intel_engine_cs *engine; - unsigned int stuck_engines; bool rearm_hangcheck; if (!READ_ONCE(dev_priv->gt.awake)) @@ -3324,12 +2741,6 @@ i915_gem_idle_work_handler(struct work_struct *work) dev_priv->gt.awake = false; rearm_hangcheck = false; - stuck_engines = intel_kick_waiters(dev_priv); - if (unlikely(stuck_engines)) { - DRM_DEBUG_DRIVER("kicked stuck waiters...missed irq\n"); - dev_priv->gpu_error.missed_irq_rings |= stuck_engines; - } - if (INTEL_GEN(dev_priv) >= 6) gen6_rps_idle(dev_priv); intel_runtime_pm_put(dev_priv); @@ -3343,32 +2754,17 @@ out_rearm: } } -/** - * Ensures that an object will eventually get non-busy by flushing any required - * write domains, emitting any outstanding lazy request and retiring and - * completed requests. - * @obj: object to flush - */ -static int -i915_gem_object_flush_active(struct drm_i915_gem_object *obj) +void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file) { - int i; - - if (!obj->active) - return 0; - - for (i = 0; i < I915_NUM_ENGINES; i++) { - struct drm_i915_gem_request *req; - - req = obj->last_read_req[i]; - if (req == NULL) - continue; - - if (i915_gem_request_completed(req)) - i915_gem_object_retire__read(obj, i); - } + struct drm_i915_gem_object *obj = to_intel_bo(gem); + struct drm_i915_file_private *fpriv = file->driver_priv; + struct i915_vma *vma, *vn; - return 0; + mutex_lock(&obj->base.dev->struct_mutex); + list_for_each_entry_safe(vma, vn, &obj->vma_list, obj_link) + if (vma->vm->file == fpriv) + i915_vma_close(vma); + mutex_unlock(&obj->base.dev->struct_mutex); } /** @@ -3399,219 +2795,35 @@ int i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { struct drm_i915_gem_wait *args = data; + struct intel_rps_client *rps = to_rps_client(file); struct drm_i915_gem_object *obj; - struct drm_i915_gem_request *req[I915_NUM_ENGINES]; - int i, n = 0; - int ret; + unsigned long active; + int idx, ret = 0; if (args->flags != 0) return -EINVAL; - ret = i915_mutex_lock_interruptible(dev); - if (ret) - return ret; - - obj = to_intel_bo(drm_gem_object_lookup(file, args->bo_handle)); - if (&obj->base == NULL) { - mutex_unlock(&dev->struct_mutex); + obj = i915_gem_object_lookup(file, args->bo_handle); + if (!obj) return -ENOENT; - } - - /* Need to make sure the object gets inactive eventually. */ - ret = i915_gem_object_flush_active(obj); - if (ret) - goto out; - - if (!obj->active) - goto out; - - /* Do this after OLR check to make sure we make forward progress polling - * on this IOCTL with a timeout == 0 (like busy ioctl) - */ - if (args->timeout_ns == 0) { - ret = -ETIME; - goto out; - } - - drm_gem_object_unreference(&obj->base); - - for (i = 0; i < I915_NUM_ENGINES; i++) { - if (obj->last_read_req[i] == NULL) - continue; - - req[n++] = i915_gem_request_reference(obj->last_read_req[i]); - } - - mutex_unlock(&dev->struct_mutex); - - for (i = 0; i < n; i++) { - if (ret == 0) - ret = __i915_wait_request(req[i], true, - args->timeout_ns > 0 ? &args->timeout_ns : NULL, - to_rps_client(file)); - i915_gem_request_unreference(req[i]); - } - return ret; -out: - drm_gem_object_unreference(&obj->base); - mutex_unlock(&dev->struct_mutex); - return ret; -} - -static int -__i915_gem_object_sync(struct drm_i915_gem_object *obj, - struct intel_engine_cs *to, - struct drm_i915_gem_request *from_req, - struct drm_i915_gem_request **to_req) -{ - struct intel_engine_cs *from; - int ret; - - from = i915_gem_request_get_engine(from_req); - if (to == from) - return 0; - - if (i915_gem_request_completed(from_req)) - return 0; - - if (!i915_semaphore_is_enabled(to_i915(obj->base.dev))) { - struct drm_i915_private *i915 = to_i915(obj->base.dev); - ret = __i915_wait_request(from_req, - i915->mm.interruptible, - NULL, - &i915->rps.semaphores); + active = __I915_BO_ACTIVE(obj); + for_each_active(active, idx) { + s64 *timeout = args->timeout_ns >= 0 ? &args->timeout_ns : NULL; + ret = i915_gem_active_wait_unlocked(&obj->last_read[idx], + I915_WAIT_INTERRUPTIBLE, + timeout, rps); if (ret) - return ret; - - i915_gem_object_retire_request(obj, from_req); - } else { - int idx = intel_ring_sync_index(from, to); - u32 seqno = i915_gem_request_get_seqno(from_req); - - WARN_ON(!to_req); - - if (seqno <= from->semaphore.sync_seqno[idx]) - return 0; - - if (*to_req == NULL) { - struct drm_i915_gem_request *req; - - req = i915_gem_request_alloc(to, NULL); - if (IS_ERR(req)) - return PTR_ERR(req); - - *to_req = req; - } - - trace_i915_gem_ring_sync_to(*to_req, from, from_req); - ret = to->semaphore.sync_to(*to_req, from, seqno); - if (ret) - return ret; - - /* We use last_read_req because sync_to() - * might have just caused seqno wrap under - * the radar. - */ - from->semaphore.sync_seqno[idx] = - i915_gem_request_get_seqno(obj->last_read_req[from->id]); - } - - return 0; -} - -/** - * i915_gem_object_sync - sync an object to a ring. - * - * @obj: object which may be in use on another ring. - * @to: ring we wish to use the object on. May be NULL. - * @to_req: request we wish to use the object for. See below. - * This will be allocated and returned if a request is - * required but not passed in. - * - * This code is meant to abstract object synchronization with the GPU. - * Calling with NULL implies synchronizing the object with the CPU - * rather than a particular GPU ring. Conceptually we serialise writes - * between engines inside the GPU. We only allow one engine to write - * into a buffer at any time, but multiple readers. To ensure each has - * a coherent view of memory, we must: - * - * - If there is an outstanding write request to the object, the new - * request must wait for it to complete (either CPU or in hw, requests - * on the same ring will be naturally ordered). - * - * - If we are a write request (pending_write_domain is set), the new - * request must wait for outstanding read requests to complete. - * - * For CPU synchronisation (NULL to) no request is required. For syncing with - * rings to_req must be non-NULL. However, a request does not have to be - * pre-allocated. If *to_req is NULL and sync commands will be emitted then a - * request will be allocated automatically and returned through *to_req. Note - * that it is not guaranteed that commands will be emitted (because the system - * might already be idle). Hence there is no need to create a request that - * might never have any work submitted. Note further that if a request is - * returned in *to_req, it is the responsibility of the caller to submit - * that request (after potentially adding more work to it). - * - * Returns 0 if successful, else propagates up the lower layer error. - */ -int -i915_gem_object_sync(struct drm_i915_gem_object *obj, - struct intel_engine_cs *to, - struct drm_i915_gem_request **to_req) -{ - const bool readonly = obj->base.pending_write_domain == 0; - struct drm_i915_gem_request *req[I915_NUM_ENGINES]; - int ret, i, n; - - if (!obj->active) - return 0; - - if (to == NULL) - return i915_gem_object_wait_rendering(obj, readonly); - - n = 0; - if (readonly) { - if (obj->last_write_req) - req[n++] = obj->last_write_req; - } else { - for (i = 0; i < I915_NUM_ENGINES; i++) - if (obj->last_read_req[i]) - req[n++] = obj->last_read_req[i]; - } - for (i = 0; i < n; i++) { - ret = __i915_gem_object_sync(obj, to, req[i], to_req); - if (ret) - return ret; + break; } - return 0; -} - -static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj) -{ - u32 old_write_domain, old_read_domains; - - /* Force a pagefault for domain tracking on next user access */ - i915_gem_release_mmap(obj); - - if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0) - return; - - old_read_domains = obj->base.read_domains; - old_write_domain = obj->base.write_domain; - - obj->base.read_domains &= ~I915_GEM_DOMAIN_GTT; - obj->base.write_domain &= ~I915_GEM_DOMAIN_GTT; - - trace_i915_gem_object_change_domain(obj, - old_read_domains, - old_write_domain); + i915_gem_object_put_unlocked(obj); + return ret; } static void __i915_vma_iounmap(struct i915_vma *vma) { - GEM_BUG_ON(vma->pin_count); + GEM_BUG_ON(i915_vma_is_pinned(vma)); if (vma->iomap == NULL) return; @@ -3620,65 +2832,83 @@ static void __i915_vma_iounmap(struct i915_vma *vma) vma->iomap = NULL; } -static int __i915_vma_unbind(struct i915_vma *vma, bool wait) +int i915_vma_unbind(struct i915_vma *vma) { struct drm_i915_gem_object *obj = vma->obj; - struct drm_i915_private *dev_priv = to_i915(obj->base.dev); + unsigned long active; int ret; - if (list_empty(&vma->obj_link)) - return 0; - - if (!drm_mm_node_allocated(&vma->node)) { - i915_gem_vma_destroy(vma); - return 0; - } - - if (vma->pin_count) - return -EBUSY; + /* First wait upon any activity as retiring the request may + * have side-effects such as unpinning or even unbinding this vma. + */ + active = i915_vma_get_active(vma); + if (active) { + int idx; + + /* When a closed VMA is retired, it is unbound - eek. + * In order to prevent it from being recursively closed, + * take a pin on the vma so that the second unbind is + * aborted. + */ + __i915_vma_pin(vma); - BUG_ON(obj->pages == NULL); + for_each_active(active, idx) { + ret = i915_gem_active_retire(&vma->last_read[idx], + &vma->vm->dev->struct_mutex); + if (ret) + break; + } - if (wait) { - ret = i915_gem_object_wait_rendering(obj, false); + __i915_vma_unpin(vma); if (ret) return ret; + + GEM_BUG_ON(i915_vma_is_active(vma)); } - if (vma->is_ggtt && vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL) { - i915_gem_object_finish_gtt(obj); + if (i915_vma_is_pinned(vma)) + return -EBUSY; + + if (!drm_mm_node_allocated(&vma->node)) + goto destroy; + + GEM_BUG_ON(obj->bind_count == 0); + GEM_BUG_ON(!obj->pages); + if (i915_vma_is_map_and_fenceable(vma)) { /* release the fence reg _after_ flushing */ - ret = i915_gem_object_put_fence(obj); + ret = i915_vma_put_fence(vma); if (ret) return ret; + /* Force a pagefault for domain tracking on next user access */ + i915_gem_release_mmap(obj); + __i915_vma_iounmap(vma); + vma->flags &= ~I915_VMA_CAN_FENCE; } - trace_i915_vma_unbind(vma); - - vma->vm->unbind_vma(vma); - vma->bound = 0; - - list_del_init(&vma->vm_link); - if (vma->is_ggtt) { - if (vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL) { - obj->map_and_fenceable = false; - } else if (vma->ggtt_view.pages) { - sg_free_table(vma->ggtt_view.pages); - kfree(vma->ggtt_view.pages); - } - vma->ggtt_view.pages = NULL; + if (likely(!vma->vm->closed)) { + trace_i915_vma_unbind(vma); + vma->vm->unbind_vma(vma); } + vma->flags &= ~(I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND); drm_mm_remove_node(&vma->node); - i915_gem_vma_destroy(vma); + list_move_tail(&vma->vm_link, &vma->vm->unbound_list); + + if (vma->pages != obj->pages) { + GEM_BUG_ON(!vma->pages); + sg_free_table(vma->pages); + kfree(vma->pages); + } + vma->pages = NULL; /* Since the unbound list is global, only move to that list if * no more VMAs exist. */ - if (list_empty(&obj->vma_list)) - list_move_tail(&obj->global_list, &dev_priv->mm.unbound_list); + if (--obj->bind_count == 0) + list_move_tail(&obj->global_list, + &to_i915(obj->base.dev)->mm.unbound_list); /* And finally now the object is completely decoupled from this vma, * we can drop its hold on the backing storage and allow it to be @@ -3686,36 +2916,28 @@ static int __i915_vma_unbind(struct i915_vma *vma, bool wait) */ i915_gem_object_unpin_pages(obj); - return 0; -} +destroy: + if (unlikely(i915_vma_is_closed(vma))) + i915_vma_destroy(vma); -int i915_vma_unbind(struct i915_vma *vma) -{ - return __i915_vma_unbind(vma, true); -} - -int __i915_vma_unbind_no_wait(struct i915_vma *vma) -{ - return __i915_vma_unbind(vma, false); + return 0; } -int i915_gem_wait_for_idle(struct drm_i915_private *dev_priv) +int i915_gem_wait_for_idle(struct drm_i915_private *dev_priv, + unsigned int flags) { struct intel_engine_cs *engine; int ret; - lockdep_assert_held(&dev_priv->drm.struct_mutex); - for_each_engine(engine, dev_priv) { if (engine->last_context == NULL) continue; - ret = intel_engine_idle(engine); + ret = intel_engine_idle(engine, flags); if (ret) return ret; } - WARN_ON(i915_verify_lists(dev)); return 0; } @@ -3753,128 +2975,87 @@ static bool i915_gem_valid_gtt_space(struct i915_vma *vma, } /** - * Finds free space in the GTT aperture and binds the object or a view of it - * there. - * @obj: object to bind - * @vm: address space to bind into - * @ggtt_view: global gtt view if applicable - * @alignment: requested alignment + * i915_vma_insert - finds a slot for the vma in its address space + * @vma: the vma + * @size: requested size in bytes (can be larger than the VMA) + * @alignment: required alignment * @flags: mask of PIN_* flags to use + * + * First we try to allocate some free space that meets the requirements for + * the VMA. Failiing that, if the flags permit, it will evict an old VMA, + * preferrably the oldest idle entry to make room for the new VMA. + * + * Returns: + * 0 on success, negative error code otherwise. */ -static struct i915_vma * -i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj, - struct i915_address_space *vm, - const struct i915_ggtt_view *ggtt_view, - unsigned alignment, - uint64_t flags) +static int +i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags) { - struct drm_device *dev = obj->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct i915_ggtt *ggtt = &dev_priv->ggtt; - u32 fence_alignment, unfenced_alignment; - u32 search_flag, alloc_flag; + struct drm_i915_private *dev_priv = to_i915(vma->vm->dev); + struct drm_i915_gem_object *obj = vma->obj; u64 start, end; - u64 size, fence_size; - struct i915_vma *vma; int ret; - if (i915_is_ggtt(vm)) { - u32 view_size; - - if (WARN_ON(!ggtt_view)) - return ERR_PTR(-EINVAL); - - view_size = i915_ggtt_view_size(obj, ggtt_view); - - fence_size = i915_gem_get_gtt_size(dev, - view_size, - obj->tiling_mode); - fence_alignment = i915_gem_get_gtt_alignment(dev, - view_size, - obj->tiling_mode, - true); - unfenced_alignment = i915_gem_get_gtt_alignment(dev, - view_size, - obj->tiling_mode, - false); - size = flags & PIN_MAPPABLE ? fence_size : view_size; - } else { - fence_size = i915_gem_get_gtt_size(dev, - obj->base.size, - obj->tiling_mode); - fence_alignment = i915_gem_get_gtt_alignment(dev, - obj->base.size, - obj->tiling_mode, - true); - unfenced_alignment = - i915_gem_get_gtt_alignment(dev, - obj->base.size, - obj->tiling_mode, - false); - size = flags & PIN_MAPPABLE ? fence_size : obj->base.size; - } + GEM_BUG_ON(vma->flags & (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND)); + GEM_BUG_ON(drm_mm_node_allocated(&vma->node)); + + size = max(size, vma->size); + if (flags & PIN_MAPPABLE) + size = i915_gem_get_ggtt_size(dev_priv, size, + i915_gem_object_get_tiling(obj)); + + alignment = max(max(alignment, vma->display_alignment), + i915_gem_get_ggtt_alignment(dev_priv, size, + i915_gem_object_get_tiling(obj), + flags & PIN_MAPPABLE)); start = flags & PIN_OFFSET_BIAS ? flags & PIN_OFFSET_MASK : 0; - end = vm->total; + + end = vma->vm->total; if (flags & PIN_MAPPABLE) - end = min_t(u64, end, ggtt->mappable_end); + end = min_t(u64, end, dev_priv->ggtt.mappable_end); if (flags & PIN_ZONE_4G) end = min_t(u64, end, (1ULL << 32) - PAGE_SIZE); - if (alignment == 0) - alignment = flags & PIN_MAPPABLE ? fence_alignment : - unfenced_alignment; - if (flags & PIN_MAPPABLE && alignment & (fence_alignment - 1)) { - DRM_DEBUG("Invalid object (view type=%u) alignment requested %u\n", - ggtt_view ? ggtt_view->type : 0, - alignment); - return ERR_PTR(-EINVAL); - } - /* If binding the object/GGTT view requires more space than the entire * aperture has, reject it early before evicting everything in a vain * attempt to find space. */ if (size > end) { - DRM_DEBUG("Attempting to bind an object (view type=%u) larger than the aperture: size=%llu > %s aperture=%llu\n", - ggtt_view ? ggtt_view->type : 0, - size, + DRM_DEBUG("Attempting to bind an object larger than the aperture: request=%llu [object=%zd] > %s aperture=%llu\n", + size, obj->base.size, flags & PIN_MAPPABLE ? "mappable" : "total", end); - return ERR_PTR(-E2BIG); + return -E2BIG; } ret = i915_gem_object_get_pages(obj); if (ret) - return ERR_PTR(ret); + return ret; i915_gem_object_pin_pages(obj); - vma = ggtt_view ? i915_gem_obj_lookup_or_create_ggtt_vma(obj, ggtt_view) : - i915_gem_obj_lookup_or_create_vma(obj, vm); - - if (IS_ERR(vma)) - goto err_unpin; - if (flags & PIN_OFFSET_FIXED) { - uint64_t offset = flags & PIN_OFFSET_MASK; - - if (offset & (alignment - 1) || offset + size > end) { + u64 offset = flags & PIN_OFFSET_MASK; + if (offset & (alignment - 1) || offset > end - size) { ret = -EINVAL; - goto err_free_vma; + goto err_unpin; } + vma->node.start = offset; vma->node.size = size; vma->node.color = obj->cache_level; - ret = drm_mm_reserve_node(&vm->mm, &vma->node); + ret = drm_mm_reserve_node(&vma->vm->mm, &vma->node); if (ret) { ret = i915_gem_evict_for_vma(vma); if (ret == 0) - ret = drm_mm_reserve_node(&vm->mm, &vma->node); + ret = drm_mm_reserve_node(&vma->vm->mm, &vma->node); + if (ret) + goto err_unpin; } - if (ret) - goto err_free_vma; } else { + u32 search_flag, alloc_flag; + if (flags & PIN_HIGH) { search_flag = DRM_MM_SEARCH_BELOW; alloc_flag = DRM_MM_CREATE_TOP; @@ -3883,47 +3064,45 @@ i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj, alloc_flag = DRM_MM_CREATE_DEFAULT; } + /* We only allocate in PAGE_SIZE/GTT_PAGE_SIZE (4096) chunks, + * so we know that we always have a minimum alignment of 4096. + * The drm_mm range manager is optimised to return results + * with zero alignment, so where possible use the optimal + * path. + */ + if (alignment <= 4096) + alignment = 0; + search_free: - ret = drm_mm_insert_node_in_range_generic(&vm->mm, &vma->node, + ret = drm_mm_insert_node_in_range_generic(&vma->vm->mm, + &vma->node, size, alignment, obj->cache_level, start, end, search_flag, alloc_flag); if (ret) { - ret = i915_gem_evict_something(dev, vm, size, alignment, + ret = i915_gem_evict_something(vma->vm, size, alignment, obj->cache_level, start, end, flags); if (ret == 0) goto search_free; - goto err_free_vma; + goto err_unpin; } } - if (WARN_ON(!i915_gem_valid_gtt_space(vma, obj->cache_level))) { - ret = -EINVAL; - goto err_remove_node; - } - - trace_i915_vma_bind(vma, flags); - ret = i915_vma_bind(vma, obj->cache_level, flags); - if (ret) - goto err_remove_node; + GEM_BUG_ON(!i915_gem_valid_gtt_space(vma, obj->cache_level)); list_move_tail(&obj->global_list, &dev_priv->mm.bound_list); - list_add_tail(&vma->vm_link, &vm->inactive_list); + list_move_tail(&vma->vm_link, &vma->vm->inactive_list); + obj->bind_count++; - return vma; + return 0; -err_remove_node: - drm_mm_remove_node(&vma->node); -err_free_vma: - i915_gem_vma_destroy(vma); - vma = ERR_PTR(ret); err_unpin: i915_gem_object_unpin_pages(obj); - return vma; + return ret; } bool @@ -3968,51 +3147,72 @@ i915_gem_clflush_object(struct drm_i915_gem_object *obj, static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj) { - uint32_t old_write_domain; + struct drm_i915_private *dev_priv = to_i915(obj->base.dev); if (obj->base.write_domain != I915_GEM_DOMAIN_GTT) return; /* No actual flushing is required for the GTT write domain. Writes - * to it immediately go to main memory as far as we know, so there's + * to it "immediately" go to main memory as far as we know, so there's * no chipset flush. It also doesn't land in render cache. * * However, we do have to enforce the order so that all writes through * the GTT land before any writes to the device, such as updates to * the GATT itself. + * + * We also have to wait a bit for the writes to land from the GTT. + * An uncached read (i.e. mmio) seems to be ideal for the round-trip + * timing. This issue has only been observed when switching quickly + * between GTT writes and CPU reads from inside the kernel on recent hw, + * and it appears to only affect discrete GTT blocks (i.e. on LLC + * system agents we cannot reproduce this behaviour). */ wmb(); + if (INTEL_GEN(dev_priv) >= 6 && !HAS_LLC(dev_priv)) + POSTING_READ(RING_ACTHD(dev_priv->engine[RCS].mmio_base)); - old_write_domain = obj->base.write_domain; - obj->base.write_domain = 0; - - intel_fb_obj_flush(obj, false, ORIGIN_GTT); + intel_fb_obj_flush(obj, false, write_origin(obj, I915_GEM_DOMAIN_GTT)); + obj->base.write_domain = 0; trace_i915_gem_object_change_domain(obj, obj->base.read_domains, - old_write_domain); + I915_GEM_DOMAIN_GTT); } /** Flushes the CPU write domain for the object if it's dirty. */ static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj) { - uint32_t old_write_domain; - if (obj->base.write_domain != I915_GEM_DOMAIN_CPU) return; if (i915_gem_clflush_object(obj, obj->pin_display)) i915_gem_chipset_flush(to_i915(obj->base.dev)); - old_write_domain = obj->base.write_domain; - obj->base.write_domain = 0; - intel_fb_obj_flush(obj, false, ORIGIN_CPU); + obj->base.write_domain = 0; trace_i915_gem_object_change_domain(obj, obj->base.read_domains, - old_write_domain); + I915_GEM_DOMAIN_CPU); +} + +static void i915_gem_object_bump_inactive_ggtt(struct drm_i915_gem_object *obj) +{ + struct i915_vma *vma; + + list_for_each_entry(vma, &obj->vma_list, obj_link) { + if (!i915_vma_is_ggtt(vma)) + continue; + + if (i915_vma_is_active(vma)) + continue; + + if (!drm_mm_node_allocated(&vma->node)) + continue; + + list_move_tail(&vma->vm_link, &vma->vm->inactive_list); + } } /** @@ -4026,20 +3226,16 @@ i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj) int i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) { - struct drm_device *dev = obj->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct i915_ggtt *ggtt = &dev_priv->ggtt; uint32_t old_write_domain, old_read_domains; - struct i915_vma *vma; int ret; - if (obj->base.write_domain == I915_GEM_DOMAIN_GTT) - return 0; - ret = i915_gem_object_wait_rendering(obj, !write); if (ret) return ret; + if (obj->base.write_domain == I915_GEM_DOMAIN_GTT) + return 0; + /* Flush and acquire obj->pages so that we are coherent through * direct access in memory with previous cached writes through * shmemfs and that our cache domain tracking remains valid. @@ -4080,10 +3276,7 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) old_write_domain); /* And bump the LRU for this access */ - vma = i915_gem_obj_to_ggtt(obj); - if (vma && drm_mm_node_allocated(&vma->node) && !obj->active) - list_move_tail(&vma->vm_link, - &ggtt->base.inactive_list); + i915_gem_object_bump_inactive_ggtt(obj); return 0; } @@ -4106,9 +3299,7 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, enum i915_cache_level cache_level) { - struct drm_device *dev = obj->base.dev; - struct i915_vma *vma, *next; - bool bound = false; + struct i915_vma *vma; int ret = 0; if (obj->cache_level == cache_level) @@ -4119,21 +3310,28 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, * catch the issue of the CS prefetch crossing page boundaries and * reading an invalid PTE on older architectures. */ - list_for_each_entry_safe(vma, next, &obj->vma_list, obj_link) { +restart: + list_for_each_entry(vma, &obj->vma_list, obj_link) { if (!drm_mm_node_allocated(&vma->node)) continue; - if (vma->pin_count) { + if (i915_vma_is_pinned(vma)) { DRM_DEBUG("can not change the cache level of pinned objects\n"); return -EBUSY; } - if (!i915_gem_valid_gtt_space(vma, cache_level)) { - ret = i915_vma_unbind(vma); - if (ret) - return ret; - } else - bound = true; + if (i915_gem_valid_gtt_space(vma, cache_level)) + continue; + + ret = i915_vma_unbind(vma); + if (ret) + return ret; + + /* As unbinding may affect other elements in the + * obj->vma_list (due to side-effects from retiring + * an active vma), play safe and restart the iterator. + */ + goto restart; } /* We can reuse the existing drm_mm nodes but need to change the @@ -4143,7 +3341,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, * rewrite the PTE in the belief that doing so tramples upon less * state and so involves less work. */ - if (bound) { + if (obj->bind_count) { /* Before we change the PTE, the GPU must not be accessing it. * If we wait upon the object, we know that all the bound * VMA are no longer active. @@ -4152,7 +3350,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, if (ret) return ret; - if (!HAS_LLC(dev) && cache_level != I915_CACHE_NONE) { + if (!HAS_LLC(obj->base.dev) && cache_level != I915_CACHE_NONE) { /* Access to snoopable pages through the GTT is * incoherent and on some machines causes a hard * lockup. Relinquish the CPU mmaping to force @@ -4169,9 +3367,11 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, * dropped the fence as all snoopable access is * supposed to be linear. */ - ret = i915_gem_object_put_fence(obj); - if (ret) - return ret; + list_for_each_entry(vma, &obj->vma_list, obj_link) { + ret = i915_vma_put_fence(vma); + if (ret) + return ret; + } } else { /* We either have incoherent backing store and * so no GTT access or the architecture is fully @@ -4215,8 +3415,8 @@ int i915_gem_get_caching_ioctl(struct drm_device *dev, void *data, struct drm_i915_gem_caching *args = data; struct drm_i915_gem_object *obj; - obj = to_intel_bo(drm_gem_object_lookup(file, args->handle)); - if (&obj->base == NULL) + obj = i915_gem_object_lookup(file, args->handle); + if (!obj) return -ENOENT; switch (obj->cache_level) { @@ -4234,7 +3434,7 @@ int i915_gem_get_caching_ioctl(struct drm_device *dev, void *data, break; } - drm_gem_object_unreference_unlocked(&obj->base); + i915_gem_object_put_unlocked(obj); return 0; } @@ -4276,15 +3476,15 @@ int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data, if (ret) goto rpm_put; - obj = to_intel_bo(drm_gem_object_lookup(file, args->handle)); - if (&obj->base == NULL) { + obj = i915_gem_object_lookup(file, args->handle); + if (!obj) { ret = -ENOENT; goto unlock; } ret = i915_gem_object_set_cache_level(obj, level); - drm_gem_object_unreference(&obj->base); + i915_gem_object_put(obj); unlock: mutex_unlock(&dev->struct_mutex); rpm_put: @@ -4298,11 +3498,12 @@ rpm_put: * Can be called from an uninterruptible phase (modesetting) and allows * any flushes to be pipelined (for pageflips). */ -int +struct i915_vma * i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, u32 alignment, const struct i915_ggtt_view *view) { + struct i915_vma *vma; u32 old_read_domains, old_write_domain; int ret; @@ -4322,19 +3523,31 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, */ ret = i915_gem_object_set_cache_level(obj, HAS_WT(obj->base.dev) ? I915_CACHE_WT : I915_CACHE_NONE); - if (ret) + if (ret) { + vma = ERR_PTR(ret); goto err_unpin_display; + } /* As the user may map the buffer once pinned in the display plane * (e.g. libkms for the bootup splash), we have to ensure that we - * always use map_and_fenceable for all scanout buffers. + * always use map_and_fenceable for all scanout buffers. However, + * it may simply be too big to fit into mappable, in which case + * put it anyway and hope that userspace can cope (but always first + * try to preserve the existing ABI). */ - ret = i915_gem_object_ggtt_pin(obj, view, alignment, - view->type == I915_GGTT_VIEW_NORMAL ? - PIN_MAPPABLE : 0); - if (ret) + vma = ERR_PTR(-ENOSPC); + if (view->type == I915_GGTT_VIEW_NORMAL) + vma = i915_gem_object_ggtt_pin(obj, view, 0, alignment, + PIN_MAPPABLE | PIN_NONBLOCK); + if (IS_ERR(vma)) + vma = i915_gem_object_ggtt_pin(obj, view, 0, alignment, 0); + if (IS_ERR(vma)) goto err_unpin_display; + vma->display_alignment = max_t(u64, vma->display_alignment, alignment); + + WARN_ON(obj->pin_display > i915_vma_pin_count(vma)); + i915_gem_object_flush_cpu_write_domain(obj); old_write_domain = obj->base.write_domain; @@ -4350,23 +3563,28 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, old_read_domains, old_write_domain); - return 0; + return vma; err_unpin_display: obj->pin_display--; - return ret; + return vma; } void -i915_gem_object_unpin_from_display_plane(struct drm_i915_gem_object *obj, - const struct i915_ggtt_view *view) +i915_gem_object_unpin_from_display_plane(struct i915_vma *vma) { - if (WARN_ON(obj->pin_display == 0)) + if (WARN_ON(vma->obj->pin_display == 0)) return; - i915_gem_object_ggtt_unpin_view(obj, view); + if (--vma->obj->pin_display == 0) + vma->display_alignment = 0; - obj->pin_display--; + /* Bump the LRU to try and avoid premature eviction whilst flipping */ + if (!i915_vma_is_active(vma)) + list_move_tail(&vma->vm_link, &vma->vm->inactive_list); + + i915_vma_unpin(vma); + WARN_ON(vma->obj->pin_display > i915_vma_pin_count(vma)); } /** @@ -4383,13 +3601,13 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write) uint32_t old_write_domain, old_read_domains; int ret; - if (obj->base.write_domain == I915_GEM_DOMAIN_CPU) - return 0; - ret = i915_gem_object_wait_rendering(obj, !write); if (ret) return ret; + if (obj->base.write_domain == I915_GEM_DOMAIN_CPU) + return 0; + i915_gem_object_flush_gtt_write_domain(obj); old_write_domain = obj->base.write_domain; @@ -4464,28 +3682,31 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) target = request; } if (target) - i915_gem_request_reference(target); + i915_gem_request_get(target); spin_unlock(&file_priv->mm.lock); if (target == NULL) return 0; - ret = __i915_wait_request(target, true, NULL, NULL); - i915_gem_request_unreference(target); + ret = i915_wait_request(target, I915_WAIT_INTERRUPTIBLE, NULL, NULL); + i915_gem_request_put(target); return ret; } static bool -i915_vma_misplaced(struct i915_vma *vma, uint32_t alignment, uint64_t flags) +i915_vma_misplaced(struct i915_vma *vma, u64 size, u64 alignment, u64 flags) { - struct drm_i915_gem_object *obj = vma->obj; + if (!drm_mm_node_allocated(&vma->node)) + return false; + + if (vma->node.size < size) + return true; - if (alignment && - vma->node.start & (alignment - 1)) + if (alignment && vma->node.start & (alignment - 1)) return true; - if (flags & PIN_MAPPABLE && !obj->map_and_fenceable) + if (flags & PIN_MAPPABLE && !i915_vma_is_map_and_fenceable(vma)) return true; if (flags & PIN_OFFSET_BIAS && @@ -4502,135 +3723,208 @@ i915_vma_misplaced(struct i915_vma *vma, uint32_t alignment, uint64_t flags) void __i915_vma_set_map_and_fenceable(struct i915_vma *vma) { struct drm_i915_gem_object *obj = vma->obj; + struct drm_i915_private *dev_priv = to_i915(obj->base.dev); bool mappable, fenceable; u32 fence_size, fence_alignment; - fence_size = i915_gem_get_gtt_size(obj->base.dev, - obj->base.size, - obj->tiling_mode); - fence_alignment = i915_gem_get_gtt_alignment(obj->base.dev, - obj->base.size, - obj->tiling_mode, - true); + fence_size = i915_gem_get_ggtt_size(dev_priv, + vma->size, + i915_gem_object_get_tiling(obj)); + fence_alignment = i915_gem_get_ggtt_alignment(dev_priv, + vma->size, + i915_gem_object_get_tiling(obj), + true); fenceable = (vma->node.size == fence_size && (vma->node.start & (fence_alignment - 1)) == 0); mappable = (vma->node.start + fence_size <= - to_i915(obj->base.dev)->ggtt.mappable_end); + dev_priv->ggtt.mappable_end); - obj->map_and_fenceable = mappable && fenceable; + if (mappable && fenceable) + vma->flags |= I915_VMA_CAN_FENCE; + else + vma->flags &= ~I915_VMA_CAN_FENCE; } -static int -i915_gem_object_do_pin(struct drm_i915_gem_object *obj, - struct i915_address_space *vm, - const struct i915_ggtt_view *ggtt_view, - uint32_t alignment, - uint64_t flags) +int __i915_vma_do_pin(struct i915_vma *vma, + u64 size, u64 alignment, u64 flags) { - struct drm_i915_private *dev_priv = to_i915(obj->base.dev); - struct i915_vma *vma; - unsigned bound; + unsigned int bound = vma->flags; int ret; - if (WARN_ON(vm == &dev_priv->mm.aliasing_ppgtt->base)) - return -ENODEV; + GEM_BUG_ON((flags & (PIN_GLOBAL | PIN_USER)) == 0); + GEM_BUG_ON((flags & PIN_GLOBAL) && !i915_vma_is_ggtt(vma)); - if (WARN_ON(flags & (PIN_GLOBAL | PIN_MAPPABLE) && !i915_is_ggtt(vm))) - return -EINVAL; + if (WARN_ON(bound & I915_VMA_PIN_OVERFLOW)) { + ret = -EBUSY; + goto err; + } - if (WARN_ON((flags & (PIN_MAPPABLE | PIN_GLOBAL)) == PIN_MAPPABLE)) - return -EINVAL; + if ((bound & I915_VMA_BIND_MASK) == 0) { + ret = i915_vma_insert(vma, size, alignment, flags); + if (ret) + goto err; + } - if (WARN_ON(i915_is_ggtt(vm) != !!ggtt_view)) - return -EINVAL; + ret = i915_vma_bind(vma, vma->obj->cache_level, flags); + if (ret) + goto err; - vma = ggtt_view ? i915_gem_obj_to_ggtt_view(obj, ggtt_view) : - i915_gem_obj_to_vma(obj, vm); + if ((bound ^ vma->flags) & I915_VMA_GLOBAL_BIND) + __i915_vma_set_map_and_fenceable(vma); - if (vma) { - if (WARN_ON(vma->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT)) - return -EBUSY; + GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags)); + return 0; - if (i915_vma_misplaced(vma, alignment, flags)) { - WARN(vma->pin_count, - "bo is already pinned in %s with incorrect alignment:" - " offset=%08x %08x, req.alignment=%x, req.map_and_fenceable=%d," - " obj->map_and_fenceable=%d\n", - ggtt_view ? "ggtt" : "ppgtt", - upper_32_bits(vma->node.start), - lower_32_bits(vma->node.start), - alignment, - !!(flags & PIN_MAPPABLE), - obj->map_and_fenceable); - ret = i915_vma_unbind(vma); - if (ret) - return ret; +err: + __i915_vma_unpin(vma); + return ret; +} - vma = NULL; - } - } +struct i915_vma * +i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj, + const struct i915_ggtt_view *view, + u64 size, + u64 alignment, + u64 flags) +{ + struct i915_address_space *vm = &to_i915(obj->base.dev)->ggtt.base; + struct i915_vma *vma; + int ret; - bound = vma ? vma->bound : 0; - if (vma == NULL || !drm_mm_node_allocated(&vma->node)) { - vma = i915_gem_object_bind_to_vm(obj, vm, ggtt_view, alignment, - flags); - if (IS_ERR(vma)) - return PTR_ERR(vma); - } else { - ret = i915_vma_bind(vma, obj->cache_level, flags); + vma = i915_gem_obj_lookup_or_create_vma(obj, vm, view); + if (IS_ERR(vma)) + return vma; + + if (i915_vma_misplaced(vma, size, alignment, flags)) { + if (flags & PIN_NONBLOCK && + (i915_vma_is_pinned(vma) || i915_vma_is_active(vma))) + return ERR_PTR(-ENOSPC); + + WARN(i915_vma_is_pinned(vma), + "bo is already pinned in ggtt with incorrect alignment:" + " offset=%08x, req.alignment=%llx," + " req.map_and_fenceable=%d, vma->map_and_fenceable=%d\n", + i915_ggtt_offset(vma), alignment, + !!(flags & PIN_MAPPABLE), + i915_vma_is_map_and_fenceable(vma)); + ret = i915_vma_unbind(vma); if (ret) - return ret; + return ERR_PTR(ret); } - if (ggtt_view && ggtt_view->type == I915_GGTT_VIEW_NORMAL && - (bound ^ vma->bound) & GLOBAL_BIND) { - __i915_vma_set_map_and_fenceable(vma); - WARN_ON(flags & PIN_MAPPABLE && !obj->map_and_fenceable); - } + ret = i915_vma_pin(vma, size, alignment, flags | PIN_GLOBAL); + if (ret) + return ERR_PTR(ret); - vma->pin_count++; - return 0; + return vma; } -int -i915_gem_object_pin(struct drm_i915_gem_object *obj, - struct i915_address_space *vm, - uint32_t alignment, - uint64_t flags) +static __always_inline unsigned int __busy_read_flag(unsigned int id) { - return i915_gem_object_do_pin(obj, vm, - i915_is_ggtt(vm) ? &i915_ggtt_view_normal : NULL, - alignment, flags); + /* Note that we could alias engines in the execbuf API, but + * that would be very unwise as it prevents userspace from + * fine control over engine selection. Ahem. + * + * This should be something like EXEC_MAX_ENGINE instead of + * I915_NUM_ENGINES. + */ + BUILD_BUG_ON(I915_NUM_ENGINES > 16); + return 0x10000 << id; } -int -i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj, - const struct i915_ggtt_view *view, - uint32_t alignment, - uint64_t flags) +static __always_inline unsigned int __busy_write_id(unsigned int id) { - struct drm_device *dev = obj->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct i915_ggtt *ggtt = &dev_priv->ggtt; + /* The uABI guarantees an active writer is also amongst the read + * engines. This would be true if we accessed the activity tracking + * under the lock, but as we perform the lookup of the object and + * its activity locklessly we can not guarantee that the last_write + * being active implies that we have set the same engine flag from + * last_read - hence we always set both read and write busy for + * last_write. + */ + return id | __busy_read_flag(id); +} - BUG_ON(!view); +static __always_inline unsigned int +__busy_set_if_active(const struct i915_gem_active *active, + unsigned int (*flag)(unsigned int id)) +{ + struct drm_i915_gem_request *request; + + request = rcu_dereference(active->request); + if (!request || i915_gem_request_completed(request)) + return 0; - return i915_gem_object_do_pin(obj, &ggtt->base, view, - alignment, flags | PIN_GLOBAL); + /* This is racy. See __i915_gem_active_get_rcu() for an in detail + * discussion of how to handle the race correctly, but for reporting + * the busy state we err on the side of potentially reporting the + * wrong engine as being busy (but we guarantee that the result + * is at least self-consistent). + * + * As we use SLAB_DESTROY_BY_RCU, the request may be reallocated + * whilst we are inspecting it, even under the RCU read lock as we are. + * This means that there is a small window for the engine and/or the + * seqno to have been overwritten. The seqno will always be in the + * future compared to the intended, and so we know that if that + * seqno is idle (on whatever engine) our request is idle and the + * return 0 above is correct. + * + * The issue is that if the engine is switched, it is just as likely + * to report that it is busy (but since the switch happened, we know + * the request should be idle). So there is a small chance that a busy + * result is actually the wrong engine. + * + * So why don't we care? + * + * For starters, the busy ioctl is a heuristic that is by definition + * racy. Even with perfect serialisation in the driver, the hardware + * state is constantly advancing - the state we report to the user + * is stale. + * + * The critical information for the busy-ioctl is whether the object + * is idle as userspace relies on that to detect whether its next + * access will stall, or if it has missed submitting commands to + * the hardware allowing the GPU to stall. We never generate a + * false-positive for idleness, thus busy-ioctl is reliable at the + * most fundamental level, and we maintain the guarantee that a + * busy object left to itself will eventually become idle (and stay + * idle!). + * + * We allow ourselves the leeway of potentially misreporting the busy + * state because that is an optimisation heuristic that is constantly + * in flux. Being quickly able to detect the busy/idle state is much + * more important than accurate logging of exactly which engines were + * busy. + * + * For accuracy in reporting the engine, we could use + * + * result = 0; + * request = __i915_gem_active_get_rcu(active); + * if (request) { + * if (!i915_gem_request_completed(request)) + * result = flag(request->engine->exec_id); + * i915_gem_request_put(request); + * } + * + * but that still remains susceptible to both hardware and userspace + * races. So we accept making the result of that race slightly worse, + * given the rarity of the race and its low impact on the result. + */ + return flag(READ_ONCE(request->engine->exec_id)); } -void -i915_gem_object_ggtt_unpin_view(struct drm_i915_gem_object *obj, - const struct i915_ggtt_view *view) +static __always_inline unsigned int +busy_check_reader(const struct i915_gem_active *active) { - struct i915_vma *vma = i915_gem_obj_to_ggtt_view(obj, view); - - WARN_ON(vma->pin_count == 0); - WARN_ON(!i915_gem_obj_ggtt_bound_view(obj, view)); + return __busy_set_if_active(active, __busy_read_flag); +} - --vma->pin_count; +static __always_inline unsigned int +busy_check_writer(const struct i915_gem_active *active) +{ + return __busy_set_if_active(active, __busy_write_id); } int @@ -4639,47 +3933,64 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, { struct drm_i915_gem_busy *args = data; struct drm_i915_gem_object *obj; - int ret; + unsigned long active; - ret = i915_mutex_lock_interruptible(dev); - if (ret) - return ret; + obj = i915_gem_object_lookup(file, args->handle); + if (!obj) + return -ENOENT; - obj = to_intel_bo(drm_gem_object_lookup(file, args->handle)); - if (&obj->base == NULL) { - ret = -ENOENT; - goto unlock; - } + args->busy = 0; + active = __I915_BO_ACTIVE(obj); + if (active) { + int idx; - /* Count all active objects as busy, even if they are currently not used - * by the gpu. Users of this interface expect objects to eventually - * become non-busy without any further actions, therefore emit any - * necessary flushes here. - */ - ret = i915_gem_object_flush_active(obj); - if (ret) - goto unref; + /* Yes, the lookups are intentionally racy. + * + * First, we cannot simply rely on __I915_BO_ACTIVE. We have + * to regard the value as stale and as our ABI guarantees + * forward progress, we confirm the status of each active + * request with the hardware. + * + * Even though we guard the pointer lookup by RCU, that only + * guarantees that the pointer and its contents remain + * dereferencable and does *not* mean that the request we + * have is the same as the one being tracked by the object. + * + * Consider that we lookup the request just as it is being + * retired and freed. We take a local copy of the pointer, + * but before we add its engine into the busy set, the other + * thread reallocates it and assigns it to a task on another + * engine with a fresh and incomplete seqno. Guarding against + * that requires careful serialisation and reference counting, + * i.e. using __i915_gem_active_get_request_rcu(). We don't, + * instead we expect that if the result is busy, which engines + * are busy is not completely reliable - we only guarantee + * that the object was busy. + */ + rcu_read_lock(); - args->busy = 0; - if (obj->active) { - int i; + for_each_active(active, idx) + args->busy |= busy_check_reader(&obj->last_read[idx]); - for (i = 0; i < I915_NUM_ENGINES; i++) { - struct drm_i915_gem_request *req; + /* For ABI sanity, we only care that the write engine is in + * the set of read engines. This should be ensured by the + * ordering of setting last_read/last_write in + * i915_vma_move_to_active(), and then in reverse in retire. + * However, for good measure, we always report the last_write + * request as a busy read as well as being a busy write. + * + * We don't care that the set of active read/write engines + * may change during construction of the result, as it is + * equally liable to change before userspace can inspect + * the result. + */ + args->busy |= busy_check_writer(&obj->last_write); - req = obj->last_read_req[i]; - if (req) - args->busy |= 1 << (16 + req->engine->exec_id); - } - if (obj->last_write_req) - args->busy |= obj->last_write_req->engine->exec_id; + rcu_read_unlock(); } -unref: - drm_gem_object_unreference(&obj->base); -unlock: - mutex_unlock(&dev->struct_mutex); - return ret; + i915_gem_object_put_unlocked(obj); + return 0; } int @@ -4710,19 +4021,14 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data, if (ret) return ret; - obj = to_intel_bo(drm_gem_object_lookup(file_priv, args->handle)); - if (&obj->base == NULL) { + obj = i915_gem_object_lookup(file_priv, args->handle); + if (!obj) { ret = -ENOENT; goto unlock; } - if (i915_gem_obj_is_pinned(obj)) { - ret = -EINVAL; - goto out; - } - if (obj->pages && - obj->tiling_mode != I915_TILING_NONE && + i915_gem_object_is_tiled(obj) && dev_priv->quirks & QUIRK_PIN_SWIZZLED_PAGES) { if (obj->madv == I915_MADV_WILLNEED) i915_gem_object_unpin_pages(obj); @@ -4739,8 +4045,7 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data, args->retained = obj->madv != __I915_MADV_PURGED; -out: - drm_gem_object_unreference(&obj->base); + i915_gem_object_put(obj); unlock: mutex_unlock(&dev->struct_mutex); return ret; @@ -4753,14 +4058,17 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj, INIT_LIST_HEAD(&obj->global_list); for (i = 0; i < I915_NUM_ENGINES; i++) - INIT_LIST_HEAD(&obj->engine_list[i]); + init_request_active(&obj->last_read[i], + i915_gem_object_retire__read); + init_request_active(&obj->last_write, + i915_gem_object_retire__write); INIT_LIST_HEAD(&obj->obj_exec_link); INIT_LIST_HEAD(&obj->vma_list); INIT_LIST_HEAD(&obj->batch_pool_link); obj->ops = ops; - obj->fence_reg = I915_FENCE_REG_NONE; + obj->frontbuffer_ggtt_origin = ORIGIN_GTT; obj->madv = I915_MADV_WILLNEED; i915_gem_info_add_obj(to_i915(obj->base.dev), obj->base.size); @@ -4865,33 +4173,31 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) trace_i915_gem_object_destroy(obj); + /* All file-owned VMA should have been released by this point through + * i915_gem_close_object(), or earlier by i915_gem_context_close(). + * However, the object may also be bound into the global GTT (e.g. + * older GPUs without per-process support, or for direct access through + * the GTT either for the user or for scanout). Those VMA still need to + * unbound now. + */ list_for_each_entry_safe(vma, next, &obj->vma_list, obj_link) { - int ret; - - vma->pin_count = 0; - ret = i915_vma_unbind(vma); - if (WARN_ON(ret == -ERESTARTSYS)) { - bool was_interruptible; - - was_interruptible = dev_priv->mm.interruptible; - dev_priv->mm.interruptible = false; - - WARN_ON(i915_vma_unbind(vma)); - - dev_priv->mm.interruptible = was_interruptible; - } + GEM_BUG_ON(!i915_vma_is_ggtt(vma)); + GEM_BUG_ON(i915_vma_is_active(vma)); + vma->flags &= ~I915_VMA_PIN_MASK; + i915_vma_close(vma); } + GEM_BUG_ON(obj->bind_count); /* Stolen objects don't hold a ref, but do hold pin count. Fix that up * before progressing. */ if (obj->stolen) i915_gem_object_unpin_pages(obj); - WARN_ON(obj->frontbuffer_bits); + WARN_ON(atomic_read(&obj->frontbuffer_bits)); if (obj->pages && obj->madv == I915_MADV_WILLNEED && dev_priv->quirks & QUIRK_PIN_SWIZZLED_PAGES && - obj->tiling_mode != I915_TILING_NONE) + i915_gem_object_is_tiled(obj)) i915_gem_object_unpin_pages(obj); if (WARN_ON(obj->pages_pin_count)) @@ -4899,7 +4205,6 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) if (discard_backing_storage(obj)) obj->madv = I915_MADV_DONTNEED; i915_gem_object_put_pages(obj); - i915_gem_object_free_mmap_offset(obj); BUG_ON(obj->pages); @@ -4918,71 +4223,35 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) intel_runtime_pm_put(dev_priv); } -struct i915_vma *i915_gem_obj_to_vma(struct drm_i915_gem_object *obj, - struct i915_address_space *vm) -{ - struct i915_vma *vma; - list_for_each_entry(vma, &obj->vma_list, obj_link) { - if (vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL && - vma->vm == vm) - return vma; - } - return NULL; -} - -struct i915_vma *i915_gem_obj_to_ggtt_view(struct drm_i915_gem_object *obj, - const struct i915_ggtt_view *view) -{ - struct i915_vma *vma; - - GEM_BUG_ON(!view); - - list_for_each_entry(vma, &obj->vma_list, obj_link) - if (vma->is_ggtt && i915_ggtt_view_equal(&vma->ggtt_view, view)) - return vma; - return NULL; -} - -void i915_gem_vma_destroy(struct i915_vma *vma) -{ - WARN_ON(vma->node.allocated); - - /* Keep the vma as a placeholder in the execbuffer reservation lists */ - if (!list_empty(&vma->exec_list)) - return; - - if (!vma->is_ggtt) - i915_ppgtt_put(i915_vm_to_ppgtt(vma->vm)); - - list_del(&vma->obj_link); - - kmem_cache_free(to_i915(vma->obj->base.dev)->vmas, vma); -} - -static void -i915_gem_stop_engines(struct drm_device *dev) +int i915_gem_suspend(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_engine_cs *engine; - - for_each_engine(engine, dev_priv) - dev_priv->gt.stop_engine(engine); -} + int ret; -int -i915_gem_suspend(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = to_i915(dev); - int ret = 0; + intel_suspend_gt_powersave(dev_priv); mutex_lock(&dev->struct_mutex); - ret = i915_gem_wait_for_idle(dev_priv); + + /* We have to flush all the executing contexts to main memory so + * that they can saved in the hibernation image. To ensure the last + * context image is coherent, we have to switch away from it. That + * leaves the dev_priv->kernel_context still active when + * we actually suspend, and its image in memory may not match the GPU + * state. Fortunately, the kernel_context is disposable and we do + * not rely on its state. + */ + ret = i915_gem_switch_to_kernel_context(dev_priv); + if (ret) + goto err; + + ret = i915_gem_wait_for_idle(dev_priv, + I915_WAIT_INTERRUPTIBLE | + I915_WAIT_LOCKED); if (ret) goto err; i915_gem_retire_requests(dev_priv); - i915_gem_stop_engines(dev); i915_gem_context_lost(dev_priv); mutex_unlock(&dev->struct_mutex); @@ -5002,6 +4271,22 @@ err: return ret; } +void i915_gem_resume(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + + mutex_lock(&dev->struct_mutex); + i915_gem_restore_gtt_mappings(dev); + + /* As we didn't flush the kernel context before suspend, we cannot + * guarantee that the context image is complete. So let's just reset + * it and start again. + */ + dev_priv->gt.resume(dev_priv); + + mutex_unlock(&dev->struct_mutex); +} + void i915_gem_init_swizzling(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); @@ -5054,53 +4339,6 @@ static void init_unused_rings(struct drm_device *dev) } } -int i915_gem_init_engines(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = to_i915(dev); - int ret; - - ret = intel_init_render_ring_buffer(dev); - if (ret) - return ret; - - if (HAS_BSD(dev)) { - ret = intel_init_bsd_ring_buffer(dev); - if (ret) - goto cleanup_render_ring; - } - - if (HAS_BLT(dev)) { - ret = intel_init_blt_ring_buffer(dev); - if (ret) - goto cleanup_bsd_ring; - } - - if (HAS_VEBOX(dev)) { - ret = intel_init_vebox_ring_buffer(dev); - if (ret) - goto cleanup_blt_ring; - } - - if (HAS_BSD2(dev)) { - ret = intel_init_bsd2_ring_buffer(dev); - if (ret) - goto cleanup_vebox_ring; - } - - return 0; - -cleanup_vebox_ring: - intel_cleanup_engine(&dev_priv->engine[VECS]); -cleanup_blt_ring: - intel_cleanup_engine(&dev_priv->engine[BCS]); -cleanup_bsd_ring: - intel_cleanup_engine(&dev_priv->engine[VCS]); -cleanup_render_ring: - intel_cleanup_engine(&dev_priv->engine[RCS]); - - return ret; -} - int i915_gem_init_hw(struct drm_device *dev) { @@ -5167,6 +4405,27 @@ out: return ret; } +bool intel_sanitize_semaphores(struct drm_i915_private *dev_priv, int value) +{ + if (INTEL_INFO(dev_priv)->gen < 6) + return false; + + /* TODO: make semaphores and Execlists play nicely together */ + if (i915.enable_execlists) + return false; + + if (value >= 0) + return value; + +#ifdef CONFIG_INTEL_IOMMU + /* Enable semaphores on SNB when IO remapping is off */ + if (INTEL_INFO(dev_priv)->gen == 6 && intel_iommu_gfx_mapped) + return false; +#endif + + return true; +} + int i915_gem_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); @@ -5175,15 +4434,11 @@ int i915_gem_init(struct drm_device *dev) mutex_lock(&dev->struct_mutex); if (!i915.enable_execlists) { - dev_priv->gt.execbuf_submit = i915_gem_ringbuffer_submission; - dev_priv->gt.init_engines = i915_gem_init_engines; - dev_priv->gt.cleanup_engine = intel_cleanup_engine; - dev_priv->gt.stop_engine = intel_stop_engine; + dev_priv->gt.resume = intel_legacy_submission_resume; + dev_priv->gt.cleanup_engine = intel_engine_cleanup; } else { - dev_priv->gt.execbuf_submit = intel_execlists_submission; - dev_priv->gt.init_engines = intel_logical_rings_init; + dev_priv->gt.resume = intel_lr_context_resume; dev_priv->gt.cleanup_engine = intel_logical_ring_cleanup; - dev_priv->gt.stop_engine = intel_logical_ring_stop; } /* This is just a security blanket to placate dragons. @@ -5195,24 +4450,27 @@ int i915_gem_init(struct drm_device *dev) intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); i915_gem_init_userptr(dev_priv); - i915_gem_init_ggtt(dev); + + ret = i915_gem_init_ggtt(dev_priv); + if (ret) + goto out_unlock; ret = i915_gem_context_init(dev); if (ret) goto out_unlock; - ret = dev_priv->gt.init_engines(dev); + ret = intel_engines_init(dev); if (ret) goto out_unlock; ret = i915_gem_init_hw(dev); if (ret == -EIO) { - /* Allow ring initialisation to fail by marking the GPU as + /* Allow engine initialisation to fail by marking the GPU as * wedged. But we only want to do this where the GPU is angry, * for all other failure, such as an allocation failure, bail. */ DRM_ERROR("Failed to initialize GPU, declaring it wedged\n"); - atomic_or(I915_WEDGED, &dev_priv->gpu_error.reset_counter); + i915_gem_set_wedged(dev_priv); ret = 0; } @@ -5236,7 +4494,6 @@ i915_gem_cleanup_engines(struct drm_device *dev) static void init_engine_lists(struct intel_engine_cs *engine) { - INIT_LIST_HEAD(&engine->active_list); INIT_LIST_HEAD(&engine->request_list); } @@ -5244,6 +4501,7 @@ void i915_gem_load_init_fences(struct drm_i915_private *dev_priv) { struct drm_device *dev = &dev_priv->drm; + int i; if (INTEL_INFO(dev_priv)->gen >= 7 && !IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv)) @@ -5259,6 +4517,13 @@ i915_gem_load_init_fences(struct drm_i915_private *dev_priv) I915_READ(vgtif_reg(avail_rs.fence_num)); /* Initialize fence registers to zero */ + for (i = 0; i < dev_priv->num_fence_regs; i++) { + struct drm_i915_fence_reg *fence = &dev_priv->fence_regs[i]; + + fence->i915 = dev_priv; + fence->id = i; + list_add_tail(&fence->link, &dev_priv->mm.fence_list); + } i915_gem_restore_fences(dev); i915_gem_detect_bit_6_swizzle(dev); @@ -5283,18 +4548,17 @@ i915_gem_load_init(struct drm_device *dev) dev_priv->requests = kmem_cache_create("i915_gem_request", sizeof(struct drm_i915_gem_request), 0, - SLAB_HWCACHE_ALIGN, + SLAB_HWCACHE_ALIGN | + SLAB_RECLAIM_ACCOUNT | + SLAB_DESTROY_BY_RCU, NULL); - INIT_LIST_HEAD(&dev_priv->vm_list); INIT_LIST_HEAD(&dev_priv->context_list); INIT_LIST_HEAD(&dev_priv->mm.unbound_list); INIT_LIST_HEAD(&dev_priv->mm.bound_list); INIT_LIST_HEAD(&dev_priv->mm.fence_list); for (i = 0; i < I915_NUM_ENGINES; i++) init_engine_lists(&dev_priv->engine[i]); - for (i = 0; i < I915_MAX_NUM_FENCES; i++) - INIT_LIST_HEAD(&dev_priv->fence_regs[i].lru_list); INIT_DELAYED_WORK(&dev_priv->gt.retire_work, i915_gem_retire_work_handler); INIT_DELAYED_WORK(&dev_priv->gt.idle_work, @@ -5304,13 +4568,13 @@ i915_gem_load_init(struct drm_device *dev) dev_priv->relative_constants_mode = I915_EXEC_CONSTANTS_REL_GENERAL; - INIT_LIST_HEAD(&dev_priv->mm.fence_list); - init_waitqueue_head(&dev_priv->pending_flip_queue); dev_priv->mm.interruptible = true; - mutex_init(&dev_priv->fb_tracking.lock); + atomic_set(&dev_priv->mm.bsd_engine_dispatch_index, 0); + + spin_lock_init(&dev_priv->fb_tracking.lock); } void i915_gem_load_cleanup(struct drm_device *dev) @@ -5320,11 +4584,19 @@ void i915_gem_load_cleanup(struct drm_device *dev) kmem_cache_destroy(dev_priv->requests); kmem_cache_destroy(dev_priv->vmas); kmem_cache_destroy(dev_priv->objects); + + /* And ensure that our DESTROY_BY_RCU slabs are truly destroyed */ + rcu_barrier(); } int i915_gem_freeze_late(struct drm_i915_private *dev_priv) { struct drm_i915_gem_object *obj; + struct list_head *phases[] = { + &dev_priv->mm.unbound_list, + &dev_priv->mm.bound_list, + NULL + }, **p; /* Called just before we write the hibernation image. * @@ -5335,16 +4607,18 @@ int i915_gem_freeze_late(struct drm_i915_private *dev_priv) * * To make sure the hibernation image contains the latest state, * we update that state just before writing out the image. + * + * To try and reduce the hibernation image, we manually shrink + * the objects as well. */ - list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list) { - obj->base.read_domains = I915_GEM_DOMAIN_CPU; - obj->base.write_domain = I915_GEM_DOMAIN_CPU; - } + i915_gem_shrink_all(dev_priv); - list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) { - obj->base.read_domains = I915_GEM_DOMAIN_CPU; - obj->base.write_domain = I915_GEM_DOMAIN_CPU; + for (p = phases; *p; p++) { + list_for_each_entry(obj, *p, global_list) { + obj->base.read_domains = I915_GEM_DOMAIN_CPU; + obj->base.write_domain = I915_GEM_DOMAIN_CPU; + } } return 0; @@ -5353,21 +4627,15 @@ int i915_gem_freeze_late(struct drm_i915_private *dev_priv) void i915_gem_release(struct drm_device *dev, struct drm_file *file) { struct drm_i915_file_private *file_priv = file->driver_priv; + struct drm_i915_gem_request *request; /* Clean up our request list when the client is going away, so that * later retire_requests won't dereference our soon-to-be-gone * file_priv. */ spin_lock(&file_priv->mm.lock); - while (!list_empty(&file_priv->mm.request_list)) { - struct drm_i915_gem_request *request; - - request = list_first_entry(&file_priv->mm.request_list, - struct drm_i915_gem_request, - client_list); - list_del(&request->client_list); + list_for_each_entry(request, &file_priv->mm.request_list, client_list) request->file_priv = NULL; - } spin_unlock(&file_priv->mm.lock); if (!list_empty(&file_priv->rps.link)) { @@ -5396,7 +4664,7 @@ int i915_gem_open(struct drm_device *dev, struct drm_file *file) spin_lock_init(&file_priv->mm.lock); INIT_LIST_HEAD(&file_priv->mm.request_list); - file_priv->bsd_ring = -1; + file_priv->bsd_engine = -1; ret = i915_gem_context_open(dev, file); if (ret) @@ -5418,118 +4686,24 @@ void i915_gem_track_fb(struct drm_i915_gem_object *old, struct drm_i915_gem_object *new, unsigned frontbuffer_bits) { + /* Control of individual bits within the mask are guarded by + * the owning plane->mutex, i.e. we can never see concurrent + * manipulation of individual bits. But since the bitfield as a whole + * is updated using RMW, we need to use atomics in order to update + * the bits. + */ + BUILD_BUG_ON(INTEL_FRONTBUFFER_BITS_PER_PIPE * I915_MAX_PIPES > + sizeof(atomic_t) * BITS_PER_BYTE); + if (old) { - WARN_ON(!mutex_is_locked(&old->base.dev->struct_mutex)); - WARN_ON(!(old->frontbuffer_bits & frontbuffer_bits)); - old->frontbuffer_bits &= ~frontbuffer_bits; + WARN_ON(!(atomic_read(&old->frontbuffer_bits) & frontbuffer_bits)); + atomic_andnot(frontbuffer_bits, &old->frontbuffer_bits); } if (new) { - WARN_ON(!mutex_is_locked(&new->base.dev->struct_mutex)); - WARN_ON(new->frontbuffer_bits & frontbuffer_bits); - new->frontbuffer_bits |= frontbuffer_bits; - } -} - -/* All the new VM stuff */ -u64 i915_gem_obj_offset(struct drm_i915_gem_object *o, - struct i915_address_space *vm) -{ - struct drm_i915_private *dev_priv = to_i915(o->base.dev); - struct i915_vma *vma; - - WARN_ON(vm == &dev_priv->mm.aliasing_ppgtt->base); - - list_for_each_entry(vma, &o->vma_list, obj_link) { - if (vma->is_ggtt && - vma->ggtt_view.type != I915_GGTT_VIEW_NORMAL) - continue; - if (vma->vm == vm) - return vma->node.start; - } - - WARN(1, "%s vma for this object not found.\n", - i915_is_ggtt(vm) ? "global" : "ppgtt"); - return -1; -} - -u64 i915_gem_obj_ggtt_offset_view(struct drm_i915_gem_object *o, - const struct i915_ggtt_view *view) -{ - struct i915_vma *vma; - - list_for_each_entry(vma, &o->vma_list, obj_link) - if (vma->is_ggtt && i915_ggtt_view_equal(&vma->ggtt_view, view)) - return vma->node.start; - - WARN(1, "global vma for this object not found. (view=%u)\n", view->type); - return -1; -} - -bool i915_gem_obj_bound(struct drm_i915_gem_object *o, - struct i915_address_space *vm) -{ - struct i915_vma *vma; - - list_for_each_entry(vma, &o->vma_list, obj_link) { - if (vma->is_ggtt && - vma->ggtt_view.type != I915_GGTT_VIEW_NORMAL) - continue; - if (vma->vm == vm && drm_mm_node_allocated(&vma->node)) - return true; - } - - return false; -} - -bool i915_gem_obj_ggtt_bound_view(struct drm_i915_gem_object *o, - const struct i915_ggtt_view *view) -{ - struct i915_vma *vma; - - list_for_each_entry(vma, &o->vma_list, obj_link) - if (vma->is_ggtt && - i915_ggtt_view_equal(&vma->ggtt_view, view) && - drm_mm_node_allocated(&vma->node)) - return true; - - return false; -} - -bool i915_gem_obj_bound_any(struct drm_i915_gem_object *o) -{ - struct i915_vma *vma; - - list_for_each_entry(vma, &o->vma_list, obj_link) - if (drm_mm_node_allocated(&vma->node)) - return true; - - return false; -} - -unsigned long i915_gem_obj_ggtt_size(struct drm_i915_gem_object *o) -{ - struct i915_vma *vma; - - GEM_BUG_ON(list_empty(&o->vma_list)); - - list_for_each_entry(vma, &o->vma_list, obj_link) { - if (vma->is_ggtt && - vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL) - return vma->node.size; + WARN_ON(atomic_read(&new->frontbuffer_bits) & frontbuffer_bits); + atomic_or(frontbuffer_bits, &new->frontbuffer_bits); } - - return 0; -} - -bool i915_gem_obj_is_pinned(struct drm_i915_gem_object *obj) -{ - struct i915_vma *vma; - list_for_each_entry(vma, &obj->vma_list, obj_link) - if (vma->pin_count > 0) - return true; - - return false; } /* Like i915_gem_object_get_page(), but mark the returned page dirty */ @@ -5584,6 +4758,6 @@ i915_gem_object_create_from_data(struct drm_device *dev, return obj; fail: - drm_gem_object_unreference(&obj->base); + i915_gem_object_put(obj); return ERR_PTR(ret); } diff --git a/drivers/gpu/drm/i915/i915_gem_batch_pool.c b/drivers/gpu/drm/i915/i915_gem_batch_pool.c index 3752d5d..ed98959 100644 --- a/drivers/gpu/drm/i915/i915_gem_batch_pool.c +++ b/drivers/gpu/drm/i915/i915_gem_batch_pool.c @@ -41,15 +41,15 @@ /** * i915_gem_batch_pool_init() - initialize a batch buffer pool - * @dev: the drm device + * @engine: the associated request submission engine * @pool: the batch buffer pool */ -void i915_gem_batch_pool_init(struct drm_device *dev, +void i915_gem_batch_pool_init(struct intel_engine_cs *engine, struct i915_gem_batch_pool *pool) { int n; - pool->dev = dev; + pool->engine = engine; for (n = 0; n < ARRAY_SIZE(pool->cache_list); n++) INIT_LIST_HEAD(&pool->cache_list[n]); @@ -65,18 +65,17 @@ void i915_gem_batch_pool_fini(struct i915_gem_batch_pool *pool) { int n; - WARN_ON(!mutex_is_locked(&pool->dev->struct_mutex)); + lockdep_assert_held(&pool->engine->i915->drm.struct_mutex); for (n = 0; n < ARRAY_SIZE(pool->cache_list); n++) { - while (!list_empty(&pool->cache_list[n])) { - struct drm_i915_gem_object *obj = - list_first_entry(&pool->cache_list[n], - struct drm_i915_gem_object, - batch_pool_link); - - list_del(&obj->batch_pool_link); - drm_gem_object_unreference(&obj->base); - } + struct drm_i915_gem_object *obj, *next; + + list_for_each_entry_safe(obj, next, + &pool->cache_list[n], + batch_pool_link) + i915_gem_object_put(obj); + + INIT_LIST_HEAD(&pool->cache_list[n]); } } @@ -102,7 +101,7 @@ i915_gem_batch_pool_get(struct i915_gem_batch_pool *pool, struct list_head *list; int n; - WARN_ON(!mutex_is_locked(&pool->dev->struct_mutex)); + lockdep_assert_held(&pool->engine->i915->drm.struct_mutex); /* Compute a power-of-two bucket, but throw everything greater than * 16KiB into the same bucket: i.e. the the buckets hold objects of @@ -115,13 +114,14 @@ i915_gem_batch_pool_get(struct i915_gem_batch_pool *pool, list_for_each_entry_safe(tmp, next, list, batch_pool_link) { /* The batches are strictly LRU ordered */ - if (tmp->active) + if (!i915_gem_active_is_idle(&tmp->last_read[pool->engine->id], + &tmp->base.dev->struct_mutex)) break; /* While we're looping, do some clean up */ if (tmp->madv == __I915_MADV_PURGED) { list_del(&tmp->batch_pool_link); - drm_gem_object_unreference(&tmp->base); + i915_gem_object_put(tmp); continue; } @@ -134,7 +134,7 @@ i915_gem_batch_pool_get(struct i915_gem_batch_pool *pool, if (obj == NULL) { int ret; - obj = i915_gem_object_create(pool->dev, size); + obj = i915_gem_object_create(&pool->engine->i915->drm, size); if (IS_ERR(obj)) return obj; diff --git a/drivers/gpu/drm/i915/i915_gem_batch_pool.h b/drivers/gpu/drm/i915/i915_gem_batch_pool.h index 848e907..10d5ac4 100644 --- a/drivers/gpu/drm/i915/i915_gem_batch_pool.h +++ b/drivers/gpu/drm/i915/i915_gem_batch_pool.h @@ -27,13 +27,15 @@ #include "i915_drv.h" +struct intel_engine_cs; + struct i915_gem_batch_pool { - struct drm_device *dev; + struct intel_engine_cs *engine; struct list_head cache_list[4]; }; /* i915_gem_batch_pool.c */ -void i915_gem_batch_pool_init(struct drm_device *dev, +void i915_gem_batch_pool_init(struct intel_engine_cs *engine, struct i915_gem_batch_pool *pool); void i915_gem_batch_pool_fini(struct i915_gem_batch_pool *pool); struct drm_i915_gem_object* diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 3c97f0e..df10f4e9 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -134,21 +134,6 @@ static int get_context_size(struct drm_i915_private *dev_priv) return ret; } -static void i915_gem_context_clean(struct i915_gem_context *ctx) -{ - struct i915_hw_ppgtt *ppgtt = ctx->ppgtt; - struct i915_vma *vma, *next; - - if (!ppgtt) - return; - - list_for_each_entry_safe(vma, next, &ppgtt->base.inactive_list, - vm_link) { - if (WARN_ON(__i915_vma_unbind_no_wait(vma))) - break; - } -} - void i915_gem_context_free(struct kref *ctx_ref) { struct i915_gem_context *ctx = container_of(ctx_ref, typeof(*ctx), ref); @@ -156,13 +141,7 @@ void i915_gem_context_free(struct kref *ctx_ref) lockdep_assert_held(&ctx->i915->drm.struct_mutex); trace_i915_context_free(ctx); - - /* - * This context is going away and we need to remove all VMAs still - * around. This is to handle imported shared objects for which - * destructor did not run when their handles were closed. - */ - i915_gem_context_clean(ctx); + GEM_BUG_ON(!ctx->closed); i915_ppgtt_put(ctx->ppgtt); @@ -173,12 +152,13 @@ void i915_gem_context_free(struct kref *ctx_ref) continue; WARN_ON(ce->pin_count); - if (ce->ringbuf) - intel_ringbuffer_free(ce->ringbuf); + if (ce->ring) + intel_ring_free(ce->ring); - drm_gem_object_unreference(&ce->state->base); + i915_vma_put(ce->state); } + put_pid(ctx->pid); list_del(&ctx->link); ida_simple_remove(&ctx->i915->context_hw_ida, ctx->hw_id); @@ -216,7 +196,7 @@ i915_gem_alloc_context_obj(struct drm_device *dev, size_t size) ret = i915_gem_object_set_cache_level(obj, I915_CACHE_L3_LLC); /* Failure shouldn't ever happen this early */ if (WARN_ON(ret)) { - drm_gem_object_unreference(&obj->base); + i915_gem_object_put(obj); return ERR_PTR(ret); } } @@ -224,6 +204,37 @@ i915_gem_alloc_context_obj(struct drm_device *dev, size_t size) return obj; } +static void i915_ppgtt_close(struct i915_address_space *vm) +{ + struct list_head *phases[] = { + &vm->active_list, + &vm->inactive_list, + &vm->unbound_list, + NULL, + }, **phase; + + GEM_BUG_ON(vm->closed); + vm->closed = true; + + for (phase = phases; *phase; phase++) { + struct i915_vma *vma, *vn; + + list_for_each_entry_safe(vma, vn, *phase, vm_link) + if (!i915_vma_is_closed(vma)) + i915_vma_close(vma); + } +} + +static void context_close(struct i915_gem_context *ctx) +{ + GEM_BUG_ON(ctx->closed); + ctx->closed = true; + if (ctx->ppgtt) + i915_ppgtt_close(&ctx->ppgtt->base); + ctx->file_priv = ERR_PTR(-EBADF); + i915_gem_context_put(ctx); +} + static int assign_hw_id(struct drm_i915_private *dev_priv, unsigned *out) { int ret; @@ -271,13 +282,24 @@ __create_hw_context(struct drm_device *dev, ctx->ggtt_alignment = get_context_alignment(dev_priv); if (dev_priv->hw_context_size) { - struct drm_i915_gem_object *obj = - i915_gem_alloc_context_obj(dev, dev_priv->hw_context_size); + struct drm_i915_gem_object *obj; + struct i915_vma *vma; + + obj = i915_gem_alloc_context_obj(dev, + dev_priv->hw_context_size); if (IS_ERR(obj)) { ret = PTR_ERR(obj); goto err_out; } - ctx->engine[RCS].state = obj; + + vma = i915_vma_create(obj, &dev_priv->ggtt.base, NULL); + if (IS_ERR(vma)) { + i915_gem_object_put(obj); + ret = PTR_ERR(vma); + goto err_out; + } + + ctx->engine[RCS].state = vma; } /* Default context will never have a file_priv */ @@ -290,6 +312,9 @@ __create_hw_context(struct drm_device *dev, ret = DEFAULT_CONTEXT_HANDLE; ctx->file_priv = file_priv; + if (file_priv) + ctx->pid = get_task_pid(current, PIDTYPE_PID); + ctx->user_handle = ret; /* NB: Mark all slices as needing a remap so that when the context first * loads it will restore whatever remap state already exists. If there @@ -305,7 +330,7 @@ __create_hw_context(struct drm_device *dev, return ctx; err_out: - i915_gem_context_unreference(ctx); + context_close(ctx); return ERR_PTR(ret); } @@ -327,13 +352,14 @@ i915_gem_create_context(struct drm_device *dev, return ctx; if (USES_FULL_PPGTT(dev)) { - struct i915_hw_ppgtt *ppgtt = i915_ppgtt_create(dev, file_priv); + struct i915_hw_ppgtt *ppgtt = + i915_ppgtt_create(to_i915(dev), file_priv); if (IS_ERR(ppgtt)) { DRM_DEBUG_DRIVER("PPGTT setup failed (%ld)\n", PTR_ERR(ppgtt)); idr_remove(&file_priv->context_idr, ctx->user_handle); - i915_gem_context_unreference(ctx); + context_close(ctx); return ERR_CAST(ppgtt); } @@ -388,28 +414,12 @@ static void i915_gem_context_unpin(struct i915_gem_context *ctx, struct intel_context *ce = &ctx->engine[engine->id]; if (ce->state) - i915_gem_object_ggtt_unpin(ce->state); + i915_vma_unpin(ce->state); - i915_gem_context_unreference(ctx); + i915_gem_context_put(ctx); } } -void i915_gem_context_reset(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = to_i915(dev); - - lockdep_assert_held(&dev->struct_mutex); - - if (i915.enable_execlists) { - struct i915_gem_context *ctx; - - list_for_each_entry(ctx, &dev_priv->context_list, link) - intel_lr_context_reset(dev_priv, ctx); - } - - i915_gem_context_lost(dev_priv); -} - int i915_gem_context_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); @@ -504,7 +514,7 @@ void i915_gem_context_fini(struct drm_device *dev) lockdep_assert_held(&dev->struct_mutex); - i915_gem_context_unreference(dctx); + context_close(dctx); dev_priv->kernel_context = NULL; ida_destroy(&dev_priv->context_hw_ida); @@ -514,8 +524,7 @@ static int context_idr_cleanup(int id, void *p, void *data) { struct i915_gem_context *ctx = p; - ctx->file_priv = ERR_PTR(-EBADF); - i915_gem_context_unreference(ctx); + context_close(ctx); return 0; } @@ -552,12 +561,13 @@ static inline int mi_set_context(struct drm_i915_gem_request *req, u32 hw_flags) { struct drm_i915_private *dev_priv = req->i915; + struct intel_ring *ring = req->ring; struct intel_engine_cs *engine = req->engine; u32 flags = hw_flags | MI_MM_SPACE_GTT; const int num_rings = /* Use an extended w/a on ivb+ if signalling from other rings */ - i915_semaphore_is_enabled(dev_priv) ? - hweight32(INTEL_INFO(dev_priv)->ring_mask) - 1 : + i915.semaphores ? + INTEL_INFO(dev_priv)->num_rings - 1 : 0; int len, ret; @@ -567,7 +577,7 @@ mi_set_context(struct drm_i915_gem_request *req, u32 hw_flags) * itlb_before_ctx_switch. */ if (IS_GEN6(dev_priv)) { - ret = engine->flush(req, I915_GEM_GPU_DOMAINS, 0); + ret = engine->emit_flush(req, EMIT_INVALIDATE); if (ret) return ret; } @@ -589,64 +599,64 @@ mi_set_context(struct drm_i915_gem_request *req, u32 hw_flags) /* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw,bdw,chv */ if (INTEL_GEN(dev_priv) >= 7) { - intel_ring_emit(engine, MI_ARB_ON_OFF | MI_ARB_DISABLE); + intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_DISABLE); if (num_rings) { struct intel_engine_cs *signaller; - intel_ring_emit(engine, + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(num_rings)); for_each_engine(signaller, dev_priv) { if (signaller == engine) continue; - intel_ring_emit_reg(engine, + intel_ring_emit_reg(ring, RING_PSMI_CTL(signaller->mmio_base)); - intel_ring_emit(engine, + intel_ring_emit(ring, _MASKED_BIT_ENABLE(GEN6_PSMI_SLEEP_MSG_DISABLE)); } } } - intel_ring_emit(engine, MI_NOOP); - intel_ring_emit(engine, MI_SET_CONTEXT); - intel_ring_emit(engine, - i915_gem_obj_ggtt_offset(req->ctx->engine[RCS].state) | - flags); + intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, MI_SET_CONTEXT); + intel_ring_emit(ring, + i915_ggtt_offset(req->ctx->engine[RCS].state) | flags); /* * w/a: MI_SET_CONTEXT must always be followed by MI_NOOP * WaMiSetContext_Hang:snb,ivb,vlv */ - intel_ring_emit(engine, MI_NOOP); + intel_ring_emit(ring, MI_NOOP); if (INTEL_GEN(dev_priv) >= 7) { if (num_rings) { struct intel_engine_cs *signaller; i915_reg_t last_reg = {}; /* keep gcc quiet */ - intel_ring_emit(engine, + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(num_rings)); for_each_engine(signaller, dev_priv) { if (signaller == engine) continue; last_reg = RING_PSMI_CTL(signaller->mmio_base); - intel_ring_emit_reg(engine, last_reg); - intel_ring_emit(engine, + intel_ring_emit_reg(ring, last_reg); + intel_ring_emit(ring, _MASKED_BIT_DISABLE(GEN6_PSMI_SLEEP_MSG_DISABLE)); } /* Insert a delay before the next switch! */ - intel_ring_emit(engine, + intel_ring_emit(ring, MI_STORE_REGISTER_MEM | MI_SRM_LRM_GLOBAL_GTT); - intel_ring_emit_reg(engine, last_reg); - intel_ring_emit(engine, engine->scratch.gtt_offset); - intel_ring_emit(engine, MI_NOOP); + intel_ring_emit_reg(ring, last_reg); + intel_ring_emit(ring, + i915_ggtt_offset(engine->scratch)); + intel_ring_emit(ring, MI_NOOP); } - intel_ring_emit(engine, MI_ARB_ON_OFF | MI_ARB_ENABLE); + intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_ENABLE); } - intel_ring_advance(engine); + intel_ring_advance(ring); return ret; } @@ -654,7 +664,7 @@ mi_set_context(struct drm_i915_gem_request *req, u32 hw_flags) static int remap_l3(struct drm_i915_gem_request *req, int slice) { u32 *remap_info = req->i915->l3_parity.remap_info[slice]; - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; int i, ret; if (!remap_info) @@ -669,13 +679,13 @@ static int remap_l3(struct drm_i915_gem_request *req, int slice) * here because no other code should access these registers other than * at initialization time. */ - intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(GEN7_L3LOG_SIZE/4)); + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(GEN7_L3LOG_SIZE/4)); for (i = 0; i < GEN7_L3LOG_SIZE/4; i++) { - intel_ring_emit_reg(engine, GEN7_L3LOG(slice, i)); - intel_ring_emit(engine, remap_info[i]); + intel_ring_emit_reg(ring, GEN7_L3LOG(slice, i)); + intel_ring_emit(ring, remap_info[i]); } - intel_ring_emit(engine, MI_NOOP); - intel_ring_advance(engine); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); return 0; } @@ -744,6 +754,7 @@ static int do_rcs_switch(struct drm_i915_gem_request *req) struct i915_gem_context *to = req->ctx; struct intel_engine_cs *engine = req->engine; struct i915_hw_ppgtt *ppgtt = to->ppgtt ?: req->i915->mm.aliasing_ppgtt; + struct i915_vma *vma = to->engine[RCS].state; struct i915_gem_context *from; u32 hw_flags; int ret, i; @@ -751,10 +762,15 @@ static int do_rcs_switch(struct drm_i915_gem_request *req) if (skip_rcs_switch(ppgtt, engine, to)) return 0; + /* Clear this page out of any CPU caches for coherent swap-in/out. */ + if (!(vma->flags & I915_VMA_GLOBAL_BIND)) { + ret = i915_gem_object_set_to_gtt_domain(vma->obj, false); + if (ret) + return ret; + } + /* Trying to pin first makes error handling easier. */ - ret = i915_gem_obj_ggtt_pin(to->engine[RCS].state, - to->ggtt_alignment, - 0); + ret = i915_vma_pin(vma, 0, to->ggtt_alignment, PIN_GLOBAL); if (ret) return ret; @@ -767,18 +783,6 @@ static int do_rcs_switch(struct drm_i915_gem_request *req) */ from = engine->last_context; - /* - * Clear this page out of any CPU caches for coherent swap-in/out. Note - * that thanks to write = false in this call and us not setting any gpu - * write domains when putting a context object onto the active list - * (when switching away from it), this won't block. - * - * XXX: We need a real interface to do this instead of trickery. - */ - ret = i915_gem_object_set_to_gtt_domain(to->engine[RCS].state, false); - if (ret) - goto unpin_out; - if (needs_pd_load_pre(ppgtt, engine, to)) { /* Older GENs and non render rings still want the load first, * "PP_DCLV followed by PP_DIR_BASE register through Load @@ -787,7 +791,7 @@ static int do_rcs_switch(struct drm_i915_gem_request *req) trace_switch_mm(engine, to); ret = ppgtt->switch_mm(ppgtt, req); if (ret) - goto unpin_out; + goto err; } if (!to->engine[RCS].initialised || i915_gem_context_is_default(to)) @@ -804,7 +808,7 @@ static int do_rcs_switch(struct drm_i915_gem_request *req) if (to != from || (hw_flags & MI_FORCE_RESTORE)) { ret = mi_set_context(req, hw_flags); if (ret) - goto unpin_out; + goto err; } /* The backing object for the context is done after switching to the @@ -814,8 +818,6 @@ static int do_rcs_switch(struct drm_i915_gem_request *req) * MI_SET_CONTEXT instead of when the next seqno has completed. */ if (from != NULL) { - from->engine[RCS].state->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION; - i915_vma_move_to_active(i915_gem_obj_to_ggtt(from->engine[RCS].state), req); /* As long as MI_SET_CONTEXT is serializing, ie. it flushes the * whole damn pipeline, we don't need to explicitly mark the * object dirty. The only exception is that the context must be @@ -823,14 +825,12 @@ static int do_rcs_switch(struct drm_i915_gem_request *req) * able to defer doing this until we know the object would be * swapped, but there is no way to do that yet. */ - from->engine[RCS].state->dirty = 1; - - /* obj is kept alive until the next request by its active ref */ - i915_gem_object_ggtt_unpin(from->engine[RCS].state); - i915_gem_context_unreference(from); + i915_vma_move_to_active(from->engine[RCS].state, req, 0); + /* state is kept alive until the next request */ + i915_vma_unpin(from->engine[RCS].state); + i915_gem_context_put(from); } - i915_gem_context_reference(to); - engine->last_context = to; + engine->last_context = i915_gem_context_get(to); /* GEN8 does *not* require an explicit reload if the PDPs have been * setup, and we do not wish to move them. @@ -872,8 +872,8 @@ static int do_rcs_switch(struct drm_i915_gem_request *req) return 0; -unpin_out: - i915_gem_object_ggtt_unpin(to->engine[RCS].state); +err: + i915_vma_unpin(vma); return ret; } @@ -894,8 +894,9 @@ int i915_switch_context(struct drm_i915_gem_request *req) { struct intel_engine_cs *engine = req->engine; - WARN_ON(i915.enable_execlists); lockdep_assert_held(&req->i915->drm.struct_mutex); + if (i915.enable_execlists) + return 0; if (!req->ctx->engine[engine->id].state) { struct i915_gem_context *to = req->ctx; @@ -914,10 +915,9 @@ int i915_switch_context(struct drm_i915_gem_request *req) } if (to != engine->last_context) { - i915_gem_context_reference(to); if (engine->last_context) - i915_gem_context_unreference(engine->last_context); - engine->last_context = to; + i915_gem_context_put(engine->last_context); + engine->last_context = i915_gem_context_get(to); } return 0; @@ -926,6 +926,33 @@ int i915_switch_context(struct drm_i915_gem_request *req) return do_rcs_switch(req); } +int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv) +{ + struct intel_engine_cs *engine; + + for_each_engine(engine, dev_priv) { + struct drm_i915_gem_request *req; + int ret; + + if (engine->last_context == NULL) + continue; + + if (engine->last_context == dev_priv->kernel_context) + continue; + + req = i915_gem_request_alloc(engine, dev_priv->kernel_context); + if (IS_ERR(req)) + return PTR_ERR(req); + + ret = i915_switch_context(req); + i915_add_request_no_flush(req); + if (ret) + return ret; + } + + return 0; +} + static bool contexts_enabled(struct drm_device *dev) { return i915.enable_execlists || to_i915(dev)->hw_context_size; @@ -985,7 +1012,7 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, } idr_remove(&file_priv->context_idr, ctx->user_handle); - i915_gem_context_unreference(ctx); + context_close(ctx); mutex_unlock(&dev->struct_mutex); DRM_DEBUG_DRIVER("HW context %d destroyed\n", args->ctx_id); diff --git a/drivers/gpu/drm/i915/i915_gem_debug.c b/drivers/gpu/drm/i915/i915_gem_debug.c deleted file mode 100644 index a565164..0000000 --- a/drivers/gpu/drm/i915/i915_gem_debug.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright © 2008 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - * - * Authors: - * Keith Packard <keithp@keithp.com> - * - */ - -#include <drm/drmP.h> -#include <drm/i915_drm.h> -#include "i915_drv.h" - -#if WATCH_LISTS -int -i915_verify_lists(struct drm_device *dev) -{ - static int warned; - struct drm_i915_private *dev_priv = to_i915(dev); - struct drm_i915_gem_object *obj; - struct intel_engine_cs *engine; - int err = 0; - - if (warned) - return 0; - - for_each_engine(engine, dev_priv) { - list_for_each_entry(obj, &engine->active_list, - engine_list[engine->id]) { - if (obj->base.dev != dev || - !atomic_read(&obj->base.refcount.refcount)) { - DRM_ERROR("%s: freed active obj %p\n", - engine->name, obj); - err++; - break; - } else if (!obj->active || - obj->last_read_req[engine->id] == NULL) { - DRM_ERROR("%s: invalid active obj %p\n", - engine->name, obj); - err++; - } else if (obj->base.write_domain) { - DRM_ERROR("%s: invalid write obj %p (w %x)\n", - engine->name, - obj, obj->base.write_domain); - err++; - } - } - } - - return warned = err; -} -#endif /* WATCH_LIST */ diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index 80bbe43..10265bb 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -23,9 +23,13 @@ * Authors: * Dave Airlie <airlied@redhat.com> */ + +#include <linux/dma-buf.h> +#include <linux/reservation.h> + #include <drm/drmP.h> + #include "i915_drv.h" -#include <linux/dma-buf.h> static struct drm_i915_gem_object *dma_buf_to_obj(struct dma_buf *buf) { @@ -115,7 +119,7 @@ static void *i915_gem_dmabuf_vmap(struct dma_buf *dma_buf) if (ret) return ERR_PTR(ret); - addr = i915_gem_object_pin_map(obj); + addr = i915_gem_object_pin_map(obj, I915_MAP_WB); mutex_unlock(&dev->struct_mutex); return addr; @@ -218,25 +222,73 @@ static const struct dma_buf_ops i915_dmabuf_ops = { .end_cpu_access = i915_gem_end_cpu_access, }; +static void export_fences(struct drm_i915_gem_object *obj, + struct dma_buf *dma_buf) +{ + struct reservation_object *resv = dma_buf->resv; + struct drm_i915_gem_request *req; + unsigned long active; + int idx; + + active = __I915_BO_ACTIVE(obj); + if (!active) + return; + + /* Serialise with execbuf to prevent concurrent fence-loops */ + mutex_lock(&obj->base.dev->struct_mutex); + + /* Mark the object for future fences before racily adding old fences */ + obj->base.dma_buf = dma_buf; + + ww_mutex_lock(&resv->lock, NULL); + + for_each_active(active, idx) { + req = i915_gem_active_get(&obj->last_read[idx], + &obj->base.dev->struct_mutex); + if (!req) + continue; + + if (reservation_object_reserve_shared(resv) == 0) + reservation_object_add_shared_fence(resv, &req->fence); + + i915_gem_request_put(req); + } + + req = i915_gem_active_get(&obj->last_write, + &obj->base.dev->struct_mutex); + if (req) { + reservation_object_add_excl_fence(resv, &req->fence); + i915_gem_request_put(req); + } + + ww_mutex_unlock(&resv->lock); + mutex_unlock(&obj->base.dev->struct_mutex); +} + struct dma_buf *i915_gem_prime_export(struct drm_device *dev, struct drm_gem_object *gem_obj, int flags) { struct drm_i915_gem_object *obj = to_intel_bo(gem_obj); DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct dma_buf *dma_buf; exp_info.ops = &i915_dmabuf_ops; exp_info.size = gem_obj->size; exp_info.flags = flags; exp_info.priv = gem_obj; - if (obj->ops->dmabuf_export) { int ret = obj->ops->dmabuf_export(obj); if (ret) return ERR_PTR(ret); } - return dma_buf_export(&exp_info); + dma_buf = dma_buf_export(&exp_info); + if (IS_ERR(dma_buf)) + return dma_buf; + + export_fences(obj, dma_buf); + return dma_buf; } static int i915_gem_object_get_pages_dmabuf(struct drm_i915_gem_object *obj) @@ -278,8 +330,7 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, * Importing dmabuf exported from out own gem increases * refcount on gem itself instead of f_count of dmabuf. */ - drm_gem_object_reference(&obj->base); - return &obj->base; + return &i915_gem_object_get(obj)->base; } } @@ -300,6 +351,16 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, i915_gem_object_init(obj, &i915_gem_object_dmabuf_ops); obj->base.import_attach = attach; + /* We use GTT as shorthand for a coherent domain, one that is + * neither in the GPU cache nor in the CPU cache, where all + * writes are immediately visible in memory. (That's not strictly + * true, but it's close! There are internal buffers such as the + * write-combined buffer or a delay through the chipset for GTT + * writes that do require us to treat GTT as a separate cache domain.) + */ + obj->base.read_domains = I915_GEM_DOMAIN_GTT; + obj->base.write_domain = 0; + return &obj->base; fail_detach: diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 3c1280e..5b6f81c 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -33,53 +33,37 @@ #include "intel_drv.h" #include "i915_trace.h" -static int switch_to_pinned_context(struct drm_i915_private *dev_priv) +static bool +gpu_is_idle(struct drm_i915_private *dev_priv) { struct intel_engine_cs *engine; - if (i915.enable_execlists) - return 0; - for_each_engine(engine, dev_priv) { - struct drm_i915_gem_request *req; - int ret; - - if (engine->last_context == NULL) - continue; - - if (engine->last_context == dev_priv->kernel_context) - continue; - - req = i915_gem_request_alloc(engine, dev_priv->kernel_context); - if (IS_ERR(req)) - return PTR_ERR(req); - - ret = i915_switch_context(req); - i915_add_request_no_flush(req); - if (ret) - return ret; + if (intel_engine_is_active(engine)) + return false; } - return 0; + return true; } - static bool -mark_free(struct i915_vma *vma, struct list_head *unwind) +mark_free(struct i915_vma *vma, unsigned int flags, struct list_head *unwind) { - if (vma->pin_count) + if (i915_vma_is_pinned(vma)) return false; if (WARN_ON(!list_empty(&vma->exec_list))) return false; + if (flags & PIN_NONFAULT && vma->obj->fault_mappable) + return false; + list_add(&vma->exec_list, unwind); return drm_mm_scan_add_block(&vma->node); } /** * i915_gem_evict_something - Evict vmas to make room for binding a new one - * @dev: drm_device * @vm: address space to evict from * @min_size: size of the desired free space * @alignment: alignment constraint of the desired free space @@ -102,42 +86,37 @@ mark_free(struct i915_vma *vma, struct list_head *unwind) * memory in e.g. the shrinker. */ int -i915_gem_evict_something(struct drm_device *dev, struct i915_address_space *vm, - int min_size, unsigned alignment, unsigned cache_level, - unsigned long start, unsigned long end, +i915_gem_evict_something(struct i915_address_space *vm, + u64 min_size, u64 alignment, + unsigned cache_level, + u64 start, u64 end, unsigned flags) { - struct list_head eviction_list, unwind_list; - struct i915_vma *vma; - int ret = 0; - int pass = 0; + struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct list_head eviction_list; + struct list_head *phases[] = { + &vm->inactive_list, + &vm->active_list, + NULL, + }, **phase; + struct i915_vma *vma, *next; + int ret; - trace_i915_gem_evict(dev, min_size, alignment, flags); + trace_i915_gem_evict(vm, min_size, alignment, flags); /* * The goal is to evict objects and amalgamate space in LRU order. * The oldest idle objects reside on the inactive list, which is in - * retirement order. The next objects to retire are those on the (per - * ring) active list that do not have an outstanding flush. Once the - * hardware reports completion (the seqno is updated after the - * batchbuffer has been finished) the clean buffer objects would - * be retired to the inactive list. Any dirty objects would be added - * to the tail of the flushing list. So after processing the clean - * active objects we need to emit a MI_FLUSH to retire the flushing - * list, hence the retirement order of the flushing list is in - * advance of the dirty objects on the active lists. + * retirement order. The next objects to retire are those in flight, + * on the active list, again in retirement order. * * The retirement sequence is thus: * 1. Inactive objects (already retired) - * 2. Clean active objects - * 3. Flushing list - * 4. Dirty active objects. + * 2. Active objects (will stall on unbinding) * * On each list, the oldest objects lie at the HEAD with the freshest * object on the TAIL. */ - - INIT_LIST_HEAD(&unwind_list); if (start != 0 || end != vm->total) { drm_mm_init_scan_with_range(&vm->mm, min_size, alignment, cache_level, @@ -145,96 +124,86 @@ i915_gem_evict_something(struct drm_device *dev, struct i915_address_space *vm, } else drm_mm_init_scan(&vm->mm, min_size, alignment, cache_level); -search_again: - /* First see if there is a large enough contiguous idle region... */ - list_for_each_entry(vma, &vm->inactive_list, vm_link) { - if (mark_free(vma, &unwind_list)) - goto found; - } - if (flags & PIN_NONBLOCK) - goto none; + phases[1] = NULL; - /* Now merge in the soon-to-be-expired objects... */ - list_for_each_entry(vma, &vm->active_list, vm_link) { - if (mark_free(vma, &unwind_list)) - goto found; - } +search_again: + INIT_LIST_HEAD(&eviction_list); + phase = phases; + do { + list_for_each_entry(vma, *phase, vm_link) + if (mark_free(vma, flags, &eviction_list)) + goto found; + } while (*++phase); -none: /* Nothing found, clean up and bail out! */ - while (!list_empty(&unwind_list)) { - vma = list_first_entry(&unwind_list, - struct i915_vma, - exec_list); + list_for_each_entry_safe(vma, next, &eviction_list, exec_list) { ret = drm_mm_scan_remove_block(&vma->node); BUG_ON(ret); - list_del_init(&vma->exec_list); + INIT_LIST_HEAD(&vma->exec_list); } /* Can we unpin some objects such as idle hw contents, - * or pending flips? + * or pending flips? But since only the GGTT has global entries + * such as scanouts, rinbuffers and contexts, we can skip the + * purge when inspecting per-process local address spaces. */ - if (flags & PIN_NONBLOCK) + if (!i915_is_ggtt(vm) || flags & PIN_NONBLOCK) return -ENOSPC; - /* Only idle the GPU and repeat the search once */ - if (pass++ == 0) { - struct drm_i915_private *dev_priv = to_i915(dev); - - if (i915_is_ggtt(vm)) { - ret = switch_to_pinned_context(dev_priv); - if (ret) - return ret; - } - - ret = i915_gem_wait_for_idle(dev_priv); - if (ret) - return ret; - - i915_gem_retire_requests(dev_priv); - goto search_again; + if (gpu_is_idle(dev_priv)) { + /* If we still have pending pageflip completions, drop + * back to userspace to give our workqueues time to + * acquire our locks and unpin the old scanouts. + */ + return intel_has_pending_fb_unpin(vm->dev) ? -EAGAIN : -ENOSPC; } - /* If we still have pending pageflip completions, drop - * back to userspace to give our workqueues time to - * acquire our locks and unpin the old scanouts. + /* Not everything in the GGTT is tracked via vma (otherwise we + * could evict as required with minimal stalling) so we are forced + * to idle the GPU and explicitly retire outstanding requests in + * the hopes that we can then remove contexts and the like only + * bound by their active reference. */ - return intel_has_pending_fb_unpin(dev) ? -EAGAIN : -ENOSPC; + ret = i915_gem_switch_to_kernel_context(dev_priv); + if (ret) + return ret; + + ret = i915_gem_wait_for_idle(dev_priv, + I915_WAIT_INTERRUPTIBLE | + I915_WAIT_LOCKED); + if (ret) + return ret; + + i915_gem_retire_requests(dev_priv); + goto search_again; found: /* drm_mm doesn't allow any other other operations while - * scanning, therefore store to be evicted objects on a - * temporary list. */ - INIT_LIST_HEAD(&eviction_list); - while (!list_empty(&unwind_list)) { - vma = list_first_entry(&unwind_list, - struct i915_vma, - exec_list); - if (drm_mm_scan_remove_block(&vma->node)) { - list_move(&vma->exec_list, &eviction_list); - drm_gem_object_reference(&vma->obj->base); - continue; - } - list_del_init(&vma->exec_list); + * scanning, therefore store to-be-evicted objects on a + * temporary list and take a reference for all before + * calling unbind (which may remove the active reference + * of any of our objects, thus corrupting the list). + */ + list_for_each_entry_safe(vma, next, &eviction_list, exec_list) { + if (drm_mm_scan_remove_block(&vma->node)) + __i915_vma_pin(vma); + else + list_del_init(&vma->exec_list); } /* Unbinding will emit any required flushes */ while (!list_empty(&eviction_list)) { - struct drm_gem_object *obj; vma = list_first_entry(&eviction_list, struct i915_vma, exec_list); - obj = &vma->obj->base; list_del_init(&vma->exec_list); + __i915_vma_unpin(vma); if (ret == 0) ret = i915_vma_unbind(vma); - - drm_gem_object_unreference(obj); } - return ret; } @@ -256,8 +225,8 @@ i915_gem_evict_for_vma(struct i915_vma *target) vma = container_of(node, typeof(*vma), node); - if (vma->pin_count) { - if (!vma->exec_entry || (vma->pin_count > 1)) + if (i915_vma_is_pinned(vma)) { + if (!vma->exec_entry || i915_vma_pin_count(vma) > 1) /* Object is pinned for some other use */ return -EBUSY; @@ -303,22 +272,23 @@ int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle) struct drm_i915_private *dev_priv = to_i915(vm->dev); if (i915_is_ggtt(vm)) { - ret = switch_to_pinned_context(dev_priv); + ret = i915_gem_switch_to_kernel_context(dev_priv); if (ret) return ret; } - ret = i915_gem_wait_for_idle(dev_priv); + ret = i915_gem_wait_for_idle(dev_priv, + I915_WAIT_INTERRUPTIBLE | + I915_WAIT_LOCKED); if (ret) return ret; i915_gem_retire_requests(dev_priv); - WARN_ON(!list_empty(&vm->active_list)); } list_for_each_entry_safe(vma, next, &vm->inactive_list, vm_link) - if (vma->pin_count == 0) + if (!i915_vma_is_pinned(vma)) WARN_ON(i915_vma_unbind(vma)); return 0; diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 1978633..33c8522 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -26,22 +26,42 @@ * */ +#include <linux/dma_remapping.h> +#include <linux/reservation.h> +#include <linux/uaccess.h> + #include <drm/drmP.h> #include <drm/i915_drm.h> + #include "i915_drv.h" +#include "i915_gem_dmabuf.h" #include "i915_trace.h" #include "intel_drv.h" -#include <linux/dma_remapping.h> -#include <linux/uaccess.h> +#include "intel_frontbuffer.h" + +#define DBG_USE_CPU_RELOC 0 /* -1 force GTT relocs; 1 force CPU relocs */ -#define __EXEC_OBJECT_HAS_PIN (1<<31) -#define __EXEC_OBJECT_HAS_FENCE (1<<30) -#define __EXEC_OBJECT_NEEDS_MAP (1<<29) -#define __EXEC_OBJECT_NEEDS_BIAS (1<<28) +#define __EXEC_OBJECT_HAS_PIN (1<<31) +#define __EXEC_OBJECT_HAS_FENCE (1<<30) +#define __EXEC_OBJECT_NEEDS_MAP (1<<29) +#define __EXEC_OBJECT_NEEDS_BIAS (1<<28) +#define __EXEC_OBJECT_INTERNAL_FLAGS (0xf<<28) /* all of the above */ #define BATCH_OFFSET_BIAS (256*1024) +struct i915_execbuffer_params { + struct drm_device *dev; + struct drm_file *file; + struct i915_vma *batch; + u32 dispatch_flags; + u32 args_batch_start_offset; + struct intel_engine_cs *engine; + struct i915_gem_context *ctx; + struct drm_i915_gem_request *request; +}; + struct eb_vmas { + struct drm_i915_private *i915; struct list_head vmas; int and; union { @@ -51,7 +71,8 @@ struct eb_vmas { }; static struct eb_vmas * -eb_create(struct drm_i915_gem_execbuffer2 *args) +eb_create(struct drm_i915_private *i915, + struct drm_i915_gem_execbuffer2 *args) { struct eb_vmas *eb = NULL; @@ -78,6 +99,7 @@ eb_create(struct drm_i915_gem_execbuffer2 *args) } else eb->and = -args->buffer_count; + eb->i915 = i915; INIT_LIST_HEAD(&eb->vmas); return eb; } @@ -89,6 +111,26 @@ eb_reset(struct eb_vmas *eb) memset(eb->buckets, 0, (eb->and+1)*sizeof(struct hlist_head)); } +static struct i915_vma * +eb_get_batch(struct eb_vmas *eb) +{ + struct i915_vma *vma = list_entry(eb->vmas.prev, typeof(*vma), exec_list); + + /* + * SNA is doing fancy tricks with compressing batch buffers, which leads + * to negative relocation deltas. Usually that works out ok since the + * relocate address is still positive, except when the batch is placed + * very low in the GTT. Ensure this doesn't happen. + * + * Note that actual hangs have only been observed on gen7, but for + * paranoia do it everywhere. + */ + if ((vma->exec_entry->flags & EXEC_OBJECT_PINNED) == 0) + vma->exec_entry->flags |= __EXEC_OBJECT_NEEDS_BIAS; + + return vma; +} + static int eb_lookup_vmas(struct eb_vmas *eb, struct drm_i915_gem_exec_object2 *exec, @@ -122,7 +164,7 @@ eb_lookup_vmas(struct eb_vmas *eb, goto err; } - drm_gem_object_reference(&obj->base); + i915_gem_object_get(obj); list_add_tail(&obj->obj_exec_link, &objects); } spin_unlock(&file->table_lock); @@ -143,8 +185,8 @@ eb_lookup_vmas(struct eb_vmas *eb, * from the (obj, vm) we don't run the risk of creating * duplicated vmas for the same vm. */ - vma = i915_gem_obj_lookup_or_create_vma(obj, vm); - if (IS_ERR(vma)) { + vma = i915_gem_obj_lookup_or_create_vma(obj, vm, NULL); + if (unlikely(IS_ERR(vma))) { DRM_DEBUG("Failed to lookup VMA\n"); ret = PTR_ERR(vma); goto err; @@ -175,7 +217,7 @@ err: struct drm_i915_gem_object, obj_exec_link); list_del_init(&obj->obj_exec_link); - drm_gem_object_unreference(&obj->base); + i915_gem_object_put(obj); } /* * Objects already transfered to the vmas list will be unreferenced by @@ -208,7 +250,6 @@ static void i915_gem_execbuffer_unreserve_vma(struct i915_vma *vma) { struct drm_i915_gem_exec_object2 *entry; - struct drm_i915_gem_object *obj = vma->obj; if (!drm_mm_node_allocated(&vma->node)) return; @@ -216,10 +257,10 @@ i915_gem_execbuffer_unreserve_vma(struct i915_vma *vma) entry = vma->exec_entry; if (entry->flags & __EXEC_OBJECT_HAS_FENCE) - i915_gem_object_unpin_fence(obj); + i915_vma_unpin_fence(vma); if (entry->flags & __EXEC_OBJECT_HAS_PIN) - vma->pin_count--; + __i915_vma_unpin(vma); entry->flags &= ~(__EXEC_OBJECT_HAS_FENCE | __EXEC_OBJECT_HAS_PIN); } @@ -234,13 +275,19 @@ static void eb_destroy(struct eb_vmas *eb) exec_list); list_del_init(&vma->exec_list); i915_gem_execbuffer_unreserve_vma(vma); - drm_gem_object_unreference(&vma->obj->base); + i915_vma_put(vma); } kfree(eb); } static inline int use_cpu_reloc(struct drm_i915_gem_object *obj) { + if (!i915_gem_object_has_struct_page(obj)) + return false; + + if (DBG_USE_CPU_RELOC) + return DBG_USE_CPU_RELOC > 0; + return (HAS_LLC(obj->base.dev) || obj->base.write_domain == I915_GEM_DOMAIN_CPU || obj->cache_level != I915_CACHE_NONE); @@ -265,144 +312,265 @@ static inline uint64_t gen8_noncanonical_addr(uint64_t address) } static inline uint64_t -relocation_target(struct drm_i915_gem_relocation_entry *reloc, +relocation_target(const struct drm_i915_gem_relocation_entry *reloc, uint64_t target_offset) { return gen8_canonical_addr((int)reloc->delta + target_offset); } -static int -relocate_entry_cpu(struct drm_i915_gem_object *obj, - struct drm_i915_gem_relocation_entry *reloc, - uint64_t target_offset) +struct reloc_cache { + struct drm_i915_private *i915; + struct drm_mm_node node; + unsigned long vaddr; + unsigned int page; + bool use_64bit_reloc; +}; + +static void reloc_cache_init(struct reloc_cache *cache, + struct drm_i915_private *i915) { - struct drm_device *dev = obj->base.dev; - uint32_t page_offset = offset_in_page(reloc->offset); - uint64_t delta = relocation_target(reloc, target_offset); - char *vaddr; - int ret; + cache->page = -1; + cache->vaddr = 0; + cache->i915 = i915; + cache->use_64bit_reloc = INTEL_GEN(cache->i915) >= 8; + cache->node.allocated = false; +} - ret = i915_gem_object_set_to_cpu_domain(obj, true); - if (ret) - return ret; +static inline void *unmask_page(unsigned long p) +{ + return (void *)(uintptr_t)(p & PAGE_MASK); +} - vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj, - reloc->offset >> PAGE_SHIFT)); - *(uint32_t *)(vaddr + page_offset) = lower_32_bits(delta); +static inline unsigned int unmask_flags(unsigned long p) +{ + return p & ~PAGE_MASK; +} + +#define KMAP 0x4 /* after CLFLUSH_FLAGS */ + +static void reloc_cache_fini(struct reloc_cache *cache) +{ + void *vaddr; + + if (!cache->vaddr) + return; - if (INTEL_INFO(dev)->gen >= 8) { - page_offset = offset_in_page(page_offset + sizeof(uint32_t)); + vaddr = unmask_page(cache->vaddr); + if (cache->vaddr & KMAP) { + if (cache->vaddr & CLFLUSH_AFTER) + mb(); - if (page_offset == 0) { - kunmap_atomic(vaddr); - vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj, - (reloc->offset + sizeof(uint32_t)) >> PAGE_SHIFT)); + kunmap_atomic(vaddr); + i915_gem_obj_finish_shmem_access((struct drm_i915_gem_object *)cache->node.mm); + } else { + wmb(); + io_mapping_unmap_atomic((void __iomem *)vaddr); + if (cache->node.allocated) { + struct i915_ggtt *ggtt = &cache->i915->ggtt; + + ggtt->base.clear_range(&ggtt->base, + cache->node.start, + cache->node.size, + true); + drm_mm_remove_node(&cache->node); + } else { + i915_vma_unpin((struct i915_vma *)cache->node.mm); } + } +} + +static void *reloc_kmap(struct drm_i915_gem_object *obj, + struct reloc_cache *cache, + int page) +{ + void *vaddr; + + if (cache->vaddr) { + kunmap_atomic(unmask_page(cache->vaddr)); + } else { + unsigned int flushes; + int ret; - *(uint32_t *)(vaddr + page_offset) = upper_32_bits(delta); + ret = i915_gem_obj_prepare_shmem_write(obj, &flushes); + if (ret) + return ERR_PTR(ret); + + BUILD_BUG_ON(KMAP & CLFLUSH_FLAGS); + BUILD_BUG_ON((KMAP | CLFLUSH_FLAGS) & PAGE_MASK); + + cache->vaddr = flushes | KMAP; + cache->node.mm = (void *)obj; + if (flushes) + mb(); } - kunmap_atomic(vaddr); + vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj, page)); + cache->vaddr = unmask_flags(cache->vaddr) | (unsigned long)vaddr; + cache->page = page; - return 0; + return vaddr; } -static int -relocate_entry_gtt(struct drm_i915_gem_object *obj, - struct drm_i915_gem_relocation_entry *reloc, - uint64_t target_offset) +static void *reloc_iomap(struct drm_i915_gem_object *obj, + struct reloc_cache *cache, + int page) { - struct drm_device *dev = obj->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct i915_ggtt *ggtt = &dev_priv->ggtt; - uint64_t delta = relocation_target(reloc, target_offset); - uint64_t offset; - void __iomem *reloc_page; - int ret; + struct i915_ggtt *ggtt = &cache->i915->ggtt; + unsigned long offset; + void *vaddr; - ret = i915_gem_object_set_to_gtt_domain(obj, true); - if (ret) - return ret; + if (cache->node.allocated) { + wmb(); + ggtt->base.insert_page(&ggtt->base, + i915_gem_object_get_dma_address(obj, page), + cache->node.start, I915_CACHE_NONE, 0); + cache->page = page; + return unmask_page(cache->vaddr); + } - ret = i915_gem_object_put_fence(obj); - if (ret) - return ret; + if (cache->vaddr) { + io_mapping_unmap_atomic(unmask_page(cache->vaddr)); + } else { + struct i915_vma *vma; + int ret; - /* Map the page containing the relocation we're going to perform. */ - offset = i915_gem_obj_ggtt_offset(obj); - offset += reloc->offset; - reloc_page = io_mapping_map_atomic_wc(ggtt->mappable, - offset & PAGE_MASK); - iowrite32(lower_32_bits(delta), reloc_page + offset_in_page(offset)); - - if (INTEL_INFO(dev)->gen >= 8) { - offset += sizeof(uint32_t); - - if (offset_in_page(offset) == 0) { - io_mapping_unmap_atomic(reloc_page); - reloc_page = - io_mapping_map_atomic_wc(ggtt->mappable, - offset); + if (use_cpu_reloc(obj)) + return NULL; + + ret = i915_gem_object_set_to_gtt_domain(obj, true); + if (ret) + return ERR_PTR(ret); + + vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, + PIN_MAPPABLE | PIN_NONBLOCK); + if (IS_ERR(vma)) { + memset(&cache->node, 0, sizeof(cache->node)); + ret = drm_mm_insert_node_in_range_generic + (&ggtt->base.mm, &cache->node, + 4096, 0, 0, + 0, ggtt->mappable_end, + DRM_MM_SEARCH_DEFAULT, + DRM_MM_CREATE_DEFAULT); + if (ret) + return ERR_PTR(ret); + } else { + ret = i915_vma_put_fence(vma); + if (ret) { + i915_vma_unpin(vma); + return ERR_PTR(ret); + } + + cache->node.start = vma->node.start; + cache->node.mm = (void *)vma; } + } - iowrite32(upper_32_bits(delta), - reloc_page + offset_in_page(offset)); + offset = cache->node.start; + if (cache->node.allocated) { + ggtt->base.insert_page(&ggtt->base, + i915_gem_object_get_dma_address(obj, page), + offset, I915_CACHE_NONE, 0); + } else { + offset += page << PAGE_SHIFT; } - io_mapping_unmap_atomic(reloc_page); + vaddr = io_mapping_map_atomic_wc(&cache->i915->ggtt.mappable, offset); + cache->page = page; + cache->vaddr = (unsigned long)vaddr; - return 0; + return vaddr; } -static void -clflush_write32(void *addr, uint32_t value) +static void *reloc_vaddr(struct drm_i915_gem_object *obj, + struct reloc_cache *cache, + int page) { - /* This is not a fast path, so KISS. */ - drm_clflush_virt_range(addr, sizeof(uint32_t)); - *(uint32_t *)addr = value; - drm_clflush_virt_range(addr, sizeof(uint32_t)); + void *vaddr; + + if (cache->page == page) { + vaddr = unmask_page(cache->vaddr); + } else { + vaddr = NULL; + if ((cache->vaddr & KMAP) == 0) + vaddr = reloc_iomap(obj, cache, page); + if (!vaddr) + vaddr = reloc_kmap(obj, cache, page); + } + + return vaddr; } -static int -relocate_entry_clflush(struct drm_i915_gem_object *obj, - struct drm_i915_gem_relocation_entry *reloc, - uint64_t target_offset) +static void clflush_write32(u32 *addr, u32 value, unsigned int flushes) { - struct drm_device *dev = obj->base.dev; - uint32_t page_offset = offset_in_page(reloc->offset); - uint64_t delta = relocation_target(reloc, target_offset); - char *vaddr; - int ret; + if (unlikely(flushes & (CLFLUSH_BEFORE | CLFLUSH_AFTER))) { + if (flushes & CLFLUSH_BEFORE) { + clflushopt(addr); + mb(); + } - ret = i915_gem_object_set_to_gtt_domain(obj, true); - if (ret) - return ret; + *addr = value; - vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj, - reloc->offset >> PAGE_SHIFT)); - clflush_write32(vaddr + page_offset, lower_32_bits(delta)); + /* Writes to the same cacheline are serialised by the CPU + * (including clflush). On the write path, we only require + * that it hits memory in an orderly fashion and place + * mb barriers at the start and end of the relocation phase + * to ensure ordering of clflush wrt to the system. + */ + if (flushes & CLFLUSH_AFTER) + clflushopt(addr); + } else + *addr = value; +} - if (INTEL_INFO(dev)->gen >= 8) { - page_offset = offset_in_page(page_offset + sizeof(uint32_t)); +static int +relocate_entry(struct drm_i915_gem_object *obj, + const struct drm_i915_gem_relocation_entry *reloc, + struct reloc_cache *cache, + u64 target_offset) +{ + u64 offset = reloc->offset; + bool wide = cache->use_64bit_reloc; + void *vaddr; + + target_offset = relocation_target(reloc, target_offset); +repeat: + vaddr = reloc_vaddr(obj, cache, offset >> PAGE_SHIFT); + if (IS_ERR(vaddr)) + return PTR_ERR(vaddr); + + clflush_write32(vaddr + offset_in_page(offset), + lower_32_bits(target_offset), + cache->vaddr); + + if (wide) { + offset += sizeof(u32); + target_offset >>= 32; + wide = false; + goto repeat; + } - if (page_offset == 0) { - kunmap_atomic(vaddr); - vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj, - (reloc->offset + sizeof(uint32_t)) >> PAGE_SHIFT)); - } + return 0; +} - clflush_write32(vaddr + page_offset, upper_32_bits(delta)); - } +static bool object_is_idle(struct drm_i915_gem_object *obj) +{ + unsigned long active = i915_gem_object_get_active(obj); + int idx; - kunmap_atomic(vaddr); + for_each_active(active, idx) { + if (!i915_gem_active_is_idle(&obj->last_read[idx], + &obj->base.dev->struct_mutex)) + return false; + } - return 0; + return true; } static int i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, struct eb_vmas *eb, - struct drm_i915_gem_relocation_entry *reloc) + struct drm_i915_gem_relocation_entry *reloc, + struct reloc_cache *cache) { struct drm_device *dev = obj->base.dev; struct drm_gem_object *target_obj; @@ -465,7 +633,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, /* Check that the relocation address is valid... */ if (unlikely(reloc->offset > - obj->base.size - (INTEL_INFO(dev)->gen >= 8 ? 8 : 4))) { + obj->base.size - (cache->use_64bit_reloc ? 8 : 4))) { DRM_DEBUG("Relocation beyond object bounds: " "obj %p target %d offset %d size %d.\n", obj, reloc->target_handle, @@ -482,26 +650,15 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, } /* We can't wait for rendering with pagefaults disabled */ - if (obj->active && pagefault_disabled()) + if (pagefault_disabled() && !object_is_idle(obj)) return -EFAULT; - if (use_cpu_reloc(obj)) - ret = relocate_entry_cpu(obj, reloc, target_offset); - else if (obj->map_and_fenceable) - ret = relocate_entry_gtt(obj, reloc, target_offset); - else if (static_cpu_has(X86_FEATURE_CLFLUSH)) - ret = relocate_entry_clflush(obj, reloc, target_offset); - else { - WARN_ONCE(1, "Impossible case in relocation handling\n"); - ret = -ENODEV; - } - + ret = relocate_entry(obj, reloc, cache, target_offset); if (ret) return ret; /* and update the user's relocation entry */ reloc->presumed_offset = target_offset; - return 0; } @@ -513,9 +670,11 @@ i915_gem_execbuffer_relocate_vma(struct i915_vma *vma, struct drm_i915_gem_relocation_entry stack_reloc[N_RELOC(512)]; struct drm_i915_gem_relocation_entry __user *user_relocs; struct drm_i915_gem_exec_object2 *entry = vma->exec_entry; - int remain, ret; + struct reloc_cache cache; + int remain, ret = 0; user_relocs = u64_to_user_ptr(entry->relocs_ptr); + reloc_cache_init(&cache, eb->i915); remain = entry->relocation_count; while (remain) { @@ -525,19 +684,23 @@ i915_gem_execbuffer_relocate_vma(struct i915_vma *vma, count = ARRAY_SIZE(stack_reloc); remain -= count; - if (__copy_from_user_inatomic(r, user_relocs, count*sizeof(r[0]))) - return -EFAULT; + if (__copy_from_user_inatomic(r, user_relocs, count*sizeof(r[0]))) { + ret = -EFAULT; + goto out; + } do { u64 offset = r->presumed_offset; - ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, r); + ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, r, &cache); if (ret) - return ret; + goto out; if (r->presumed_offset != offset && - __put_user(r->presumed_offset, &user_relocs->presumed_offset)) { - return -EFAULT; + __put_user(r->presumed_offset, + &user_relocs->presumed_offset)) { + ret = -EFAULT; + goto out; } user_relocs++; @@ -545,7 +708,9 @@ i915_gem_execbuffer_relocate_vma(struct i915_vma *vma, } while (--count); } - return 0; +out: + reloc_cache_fini(&cache); + return ret; #undef N_RELOC } @@ -555,15 +720,18 @@ i915_gem_execbuffer_relocate_vma_slow(struct i915_vma *vma, struct drm_i915_gem_relocation_entry *relocs) { const struct drm_i915_gem_exec_object2 *entry = vma->exec_entry; - int i, ret; + struct reloc_cache cache; + int i, ret = 0; + reloc_cache_init(&cache, eb->i915); for (i = 0; i < entry->relocation_count; i++) { - ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, &relocs[i]); + ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, &relocs[i], &cache); if (ret) - return ret; + break; } + reloc_cache_fini(&cache); - return 0; + return ret; } static int @@ -626,23 +794,27 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma, flags |= PIN_HIGH; } - ret = i915_gem_object_pin(obj, vma->vm, entry->alignment, flags); - if ((ret == -ENOSPC || ret == -E2BIG) && + ret = i915_vma_pin(vma, + entry->pad_to_size, + entry->alignment, + flags); + if ((ret == -ENOSPC || ret == -E2BIG) && only_mappable_for_reloc(entry->flags)) - ret = i915_gem_object_pin(obj, vma->vm, - entry->alignment, - flags & ~PIN_MAPPABLE); + ret = i915_vma_pin(vma, + entry->pad_to_size, + entry->alignment, + flags & ~PIN_MAPPABLE); if (ret) return ret; entry->flags |= __EXEC_OBJECT_HAS_PIN; if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) { - ret = i915_gem_object_get_fence(obj); + ret = i915_vma_get_fence(vma); if (ret) return ret; - if (i915_gem_object_pin_fence(obj)) + if (i915_vma_pin_fence(vma)) entry->flags |= __EXEC_OBJECT_HAS_FENCE; } @@ -667,7 +839,7 @@ need_reloc_mappable(struct i915_vma *vma) if (entry->relocation_count == 0) return false; - if (!vma->is_ggtt) + if (!i915_vma_is_ggtt(vma)) return false; /* See also use_cpu_reloc() */ @@ -684,14 +856,17 @@ static bool eb_vma_misplaced(struct i915_vma *vma) { struct drm_i915_gem_exec_object2 *entry = vma->exec_entry; - struct drm_i915_gem_object *obj = vma->obj; - WARN_ON(entry->flags & __EXEC_OBJECT_NEEDS_MAP && !vma->is_ggtt); + WARN_ON(entry->flags & __EXEC_OBJECT_NEEDS_MAP && + !i915_vma_is_ggtt(vma)); if (entry->alignment && vma->node.start & (entry->alignment - 1)) return true; + if (vma->node.size < entry->pad_to_size) + return true; + if (entry->flags & EXEC_OBJECT_PINNED && vma->node.start != entry->offset) return true; @@ -701,7 +876,8 @@ eb_vma_misplaced(struct i915_vma *vma) return true; /* avoid costly ping-pong once a batch bo ended up non-mappable */ - if (entry->flags & __EXEC_OBJECT_NEEDS_MAP && !obj->map_and_fenceable) + if (entry->flags & __EXEC_OBJECT_NEEDS_MAP && + !i915_vma_is_map_and_fenceable(vma)) return !only_mappable_for_reloc(entry->flags); if ((entry->flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS) == 0 && @@ -725,8 +901,6 @@ i915_gem_execbuffer_reserve(struct intel_engine_cs *engine, bool has_fenced_gpu_access = INTEL_GEN(engine->i915) < 4; int retry; - i915_gem_retire_requests_ring(engine); - vm = list_first_entry(vmas, struct i915_vma, exec_list)->vm; INIT_LIST_HEAD(&ordered_vmas); @@ -746,7 +920,7 @@ i915_gem_execbuffer_reserve(struct intel_engine_cs *engine, entry->flags &= ~EXEC_OBJECT_NEEDS_FENCE; need_fence = entry->flags & EXEC_OBJECT_NEEDS_FENCE && - obj->tiling_mode != I915_TILING_NONE; + i915_gem_object_is_tiled(obj); need_mappable = need_fence || need_reloc_mappable(vma); if (entry->flags & EXEC_OBJECT_PINNED) @@ -843,7 +1017,7 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, vma = list_first_entry(&eb->vmas, struct i915_vma, exec_list); list_del_init(&vma->exec_list); i915_gem_execbuffer_unreserve_vma(vma); - drm_gem_object_unreference(&vma->obj->base); + i915_vma_put(vma); } mutex_unlock(&dev->struct_mutex); @@ -937,41 +1111,54 @@ err: return ret; } +static unsigned int eb_other_engines(struct drm_i915_gem_request *req) +{ + unsigned int mask; + + mask = ~intel_engine_flag(req->engine) & I915_BO_ACTIVE_MASK; + mask <<= I915_BO_ACTIVE_SHIFT; + + return mask; +} + static int i915_gem_execbuffer_move_to_gpu(struct drm_i915_gem_request *req, struct list_head *vmas) { - const unsigned other_rings = ~intel_engine_flag(req->engine); + const unsigned int other_rings = eb_other_engines(req); struct i915_vma *vma; - uint32_t flush_domains = 0; - bool flush_chipset = false; int ret; list_for_each_entry(vma, vmas, exec_list) { struct drm_i915_gem_object *obj = vma->obj; + struct reservation_object *resv; - if (obj->active & other_rings) { - ret = i915_gem_object_sync(obj, req->engine, &req); + if (obj->flags & other_rings) { + ret = i915_gem_request_await_object + (req, obj, obj->base.pending_write_domain); if (ret) return ret; } - if (obj->base.write_domain & I915_GEM_DOMAIN_CPU) - flush_chipset |= i915_gem_clflush_object(obj, false); + resv = i915_gem_object_get_dmabuf_resv(obj); + if (resv) { + ret = i915_sw_fence_await_reservation + (&req->submit, resv, &i915_fence_ops, + obj->base.pending_write_domain, 10*HZ, + GFP_KERNEL | __GFP_NOWARN); + if (ret < 0) + return ret; + } - flush_domains |= obj->base.write_domain; + if (obj->base.write_domain & I915_GEM_DOMAIN_CPU) + i915_gem_clflush_object(obj, false); } - if (flush_chipset) - i915_gem_chipset_flush(req->engine->i915); - - if (flush_domains & I915_GEM_DOMAIN_GTT) - wmb(); + /* Unconditionally flush any chipset caches (for streaming writes). */ + i915_gem_chipset_flush(req->engine->i915); - /* Unconditionally invalidate gpu caches and ensure that we do flush - * any residual writes from the previous batch. - */ - return intel_ring_invalidate_all_caches(req); + /* Unconditionally invalidate GPU caches and TLBs. */ + return req->engine->emit_flush(req, EMIT_INVALIDATE); } static bool @@ -1007,6 +1194,9 @@ validate_exec_list(struct drm_device *dev, unsigned invalid_flags; int i; + /* INTERNAL flags must not overlap with external ones */ + BUILD_BUG_ON(__EXEC_OBJECT_INTERNAL_FLAGS & ~__EXEC_OBJECT_UNKNOWN_FLAGS); + invalid_flags = __EXEC_OBJECT_UNKNOWN_FLAGS; if (USES_FULL_PPGTT(dev)) invalid_flags |= EXEC_OBJECT_NEEDS_GTT; @@ -1036,6 +1226,14 @@ validate_exec_list(struct drm_device *dev, if (exec[i].alignment && !is_power_of_2(exec[i].alignment)) return -EINVAL; + /* pad_to_size was once a reserved field, so sanitize it */ + if (exec[i].flags & EXEC_OBJECT_PAD_TO_SIZE) { + if (offset_in_page(exec[i].pad_to_size)) + return -EINVAL; + } else { + exec[i].pad_to_size = 0; + } + /* First check for malicious input causing overflow in * the worst case where we need to allocate the entire * relocation tree as a single array. @@ -1067,12 +1265,9 @@ static struct i915_gem_context * i915_gem_validate_context(struct drm_device *dev, struct drm_file *file, struct intel_engine_cs *engine, const u32 ctx_id) { - struct i915_gem_context *ctx = NULL; + struct i915_gem_context *ctx; struct i915_ctx_hang_stats *hs; - if (engine->id != RCS && ctx_id != DEFAULT_CONTEXT_HANDLE) - return ERR_PTR(-EINVAL); - ctx = i915_gem_context_lookup(file->driver_priv, ctx_id); if (IS_ERR(ctx)) return ctx; @@ -1086,66 +1281,99 @@ i915_gem_validate_context(struct drm_device *dev, struct drm_file *file, return ctx; } -void +void i915_vma_move_to_active(struct i915_vma *vma, + struct drm_i915_gem_request *req, + unsigned int flags) +{ + struct drm_i915_gem_object *obj = vma->obj; + const unsigned int idx = req->engine->id; + + GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); + + obj->dirty = 1; /* be paranoid */ + + /* Add a reference if we're newly entering the active list. + * The order in which we add operations to the retirement queue is + * vital here: mark_active adds to the start of the callback list, + * such that subsequent callbacks are called first. Therefore we + * add the active reference first and queue for it to be dropped + * *last*. + */ + if (!i915_gem_object_is_active(obj)) + i915_gem_object_get(obj); + i915_gem_object_set_active(obj, idx); + i915_gem_active_set(&obj->last_read[idx], req); + + if (flags & EXEC_OBJECT_WRITE) { + i915_gem_active_set(&obj->last_write, req); + + intel_fb_obj_invalidate(obj, ORIGIN_CS); + + /* update for the implicit flush after a batch */ + obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS; + } + + if (flags & EXEC_OBJECT_NEEDS_FENCE) + i915_gem_active_set(&vma->last_fence, req); + + i915_vma_set_active(vma, idx); + i915_gem_active_set(&vma->last_read[idx], req); + list_move_tail(&vma->vm_link, &vma->vm->active_list); +} + +static void eb_export_fence(struct drm_i915_gem_object *obj, + struct drm_i915_gem_request *req, + unsigned int flags) +{ + struct reservation_object *resv; + + resv = i915_gem_object_get_dmabuf_resv(obj); + if (!resv) + return; + + /* Ignore errors from failing to allocate the new fence, we can't + * handle an error right now. Worst case should be missed + * synchronisation leading to rendering corruption. + */ + ww_mutex_lock(&resv->lock, NULL); + if (flags & EXEC_OBJECT_WRITE) + reservation_object_add_excl_fence(resv, &req->fence); + else if (reservation_object_reserve_shared(resv) == 0) + reservation_object_add_shared_fence(resv, &req->fence); + ww_mutex_unlock(&resv->lock); +} + +static void i915_gem_execbuffer_move_to_active(struct list_head *vmas, struct drm_i915_gem_request *req) { - struct intel_engine_cs *engine = i915_gem_request_get_engine(req); struct i915_vma *vma; list_for_each_entry(vma, vmas, exec_list) { - struct drm_i915_gem_exec_object2 *entry = vma->exec_entry; struct drm_i915_gem_object *obj = vma->obj; u32 old_read = obj->base.read_domains; u32 old_write = obj->base.write_domain; - obj->dirty = 1; /* be paranoid */ obj->base.write_domain = obj->base.pending_write_domain; - if (obj->base.write_domain == 0) + if (obj->base.write_domain) + vma->exec_entry->flags |= EXEC_OBJECT_WRITE; + else obj->base.pending_read_domains |= obj->base.read_domains; obj->base.read_domains = obj->base.pending_read_domains; - i915_vma_move_to_active(vma, req); - if (obj->base.write_domain) { - i915_gem_request_assign(&obj->last_write_req, req); - - intel_fb_obj_invalidate(obj, ORIGIN_CS); - - /* update for the implicit flush after a batch */ - obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS; - } - if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) { - i915_gem_request_assign(&obj->last_fenced_req, req); - if (entry->flags & __EXEC_OBJECT_HAS_FENCE) { - struct drm_i915_private *dev_priv = engine->i915; - list_move_tail(&dev_priv->fence_regs[obj->fence_reg].lru_list, - &dev_priv->mm.fence_list); - } - } - + i915_vma_move_to_active(vma, req, vma->exec_entry->flags); + eb_export_fence(obj, req, vma->exec_entry->flags); trace_i915_gem_object_change_domain(obj, old_read, old_write); } } -static void -i915_gem_execbuffer_retire_commands(struct i915_execbuffer_params *params) -{ - /* Unconditionally force add_request to emit a full flush. */ - params->engine->gpu_caches_dirty = true; - - /* Add a breadcrumb for the completion of the batch buffer */ - __i915_add_request(params->request, params->batch_obj, true); -} - static int -i915_reset_gen7_sol_offsets(struct drm_device *dev, - struct drm_i915_gem_request *req) +i915_reset_gen7_sol_offsets(struct drm_i915_gem_request *req) { - struct intel_engine_cs *engine = req->engine; - struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_ring *ring = req->ring; int ret, i; - if (!IS_GEN7(dev) || engine != &dev_priv->engine[RCS]) { + if (!IS_GEN7(req->i915) || req->engine->id != RCS) { DRM_DEBUG("sol reset is gen7/rcs only\n"); return -EINVAL; } @@ -1155,21 +1383,21 @@ i915_reset_gen7_sol_offsets(struct drm_device *dev, return ret; for (i = 0; i < 4; i++) { - intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(1)); - intel_ring_emit_reg(engine, GEN7_SO_WRITE_OFFSET(i)); - intel_ring_emit(engine, 0); + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1)); + intel_ring_emit_reg(ring, GEN7_SO_WRITE_OFFSET(i)); + intel_ring_emit(ring, 0); } - intel_ring_advance(engine); + intel_ring_advance(ring); return 0; } -static struct drm_i915_gem_object* +static struct i915_vma * i915_gem_execbuffer_parse(struct intel_engine_cs *engine, struct drm_i915_gem_exec_object2 *shadow_exec_entry, - struct eb_vmas *eb, struct drm_i915_gem_object *batch_obj, + struct eb_vmas *eb, u32 batch_start_offset, u32 batch_len, bool is_master) @@ -1181,51 +1409,44 @@ i915_gem_execbuffer_parse(struct intel_engine_cs *engine, shadow_batch_obj = i915_gem_batch_pool_get(&engine->batch_pool, PAGE_ALIGN(batch_len)); if (IS_ERR(shadow_batch_obj)) - return shadow_batch_obj; - - ret = i915_parse_cmds(engine, - batch_obj, - shadow_batch_obj, - batch_start_offset, - batch_len, - is_master); - if (ret) - goto err; - - ret = i915_gem_obj_ggtt_pin(shadow_batch_obj, 0, 0); - if (ret) - goto err; + return ERR_CAST(shadow_batch_obj); + + ret = intel_engine_cmd_parser(engine, + batch_obj, + shadow_batch_obj, + batch_start_offset, + batch_len, + is_master); + if (ret) { + if (ret == -EACCES) /* unhandled chained batch */ + vma = NULL; + else + vma = ERR_PTR(ret); + goto out; + } - i915_gem_object_unpin_pages(shadow_batch_obj); + vma = i915_gem_object_ggtt_pin(shadow_batch_obj, NULL, 0, 0, 0); + if (IS_ERR(vma)) + goto out; memset(shadow_exec_entry, 0, sizeof(*shadow_exec_entry)); - vma = i915_gem_obj_to_ggtt(shadow_batch_obj); vma->exec_entry = shadow_exec_entry; vma->exec_entry->flags = __EXEC_OBJECT_HAS_PIN; - drm_gem_object_reference(&shadow_batch_obj->base); + i915_gem_object_get(shadow_batch_obj); list_add_tail(&vma->exec_list, &eb->vmas); - shadow_batch_obj->base.pending_read_domains = I915_GEM_DOMAIN_COMMAND; - - return shadow_batch_obj; - -err: +out: i915_gem_object_unpin_pages(shadow_batch_obj); - if (ret == -EACCES) /* unhandled chained batch */ - return batch_obj; - else - return ERR_PTR(ret); + return vma; } -int -i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params, - struct drm_i915_gem_execbuffer2 *args, - struct list_head *vmas) +static int +execbuf_submit(struct i915_execbuffer_params *params, + struct drm_i915_gem_execbuffer2 *args, + struct list_head *vmas) { - struct drm_device *dev = params->dev; - struct intel_engine_cs *engine = params->engine; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = params->request->i915; u64 exec_start, exec_len; int instp_mode; u32 instp_mask; @@ -1239,34 +1460,31 @@ i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params, if (ret) return ret; - WARN(params->ctx->ppgtt && params->ctx->ppgtt->pd_dirty_rings & (1<<engine->id), - "%s didn't clear reload\n", engine->name); - instp_mode = args->flags & I915_EXEC_CONSTANTS_MASK; instp_mask = I915_EXEC_CONSTANTS_MASK; switch (instp_mode) { case I915_EXEC_CONSTANTS_REL_GENERAL: case I915_EXEC_CONSTANTS_ABSOLUTE: case I915_EXEC_CONSTANTS_REL_SURFACE: - if (instp_mode != 0 && engine != &dev_priv->engine[RCS]) { + if (instp_mode != 0 && params->engine->id != RCS) { DRM_DEBUG("non-0 rel constants mode on non-RCS\n"); return -EINVAL; } if (instp_mode != dev_priv->relative_constants_mode) { - if (INTEL_INFO(dev)->gen < 4) { + if (INTEL_INFO(dev_priv)->gen < 4) { DRM_DEBUG("no rel constants on pre-gen4\n"); return -EINVAL; } - if (INTEL_INFO(dev)->gen > 5 && + if (INTEL_INFO(dev_priv)->gen > 5 && instp_mode == I915_EXEC_CONSTANTS_REL_SURFACE) { DRM_DEBUG("rel surface constants mode invalid on gen5+\n"); return -EINVAL; } /* The HW changed the meaning on this bit on gen6 */ - if (INTEL_INFO(dev)->gen >= 6) + if (INTEL_INFO(dev_priv)->gen >= 6) instp_mask &= ~I915_EXEC_CONSTANTS_REL_SURFACE; } break; @@ -1275,37 +1493,39 @@ i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params, return -EINVAL; } - if (engine == &dev_priv->engine[RCS] && + if (params->engine->id == RCS && instp_mode != dev_priv->relative_constants_mode) { + struct intel_ring *ring = params->request->ring; + ret = intel_ring_begin(params->request, 4); if (ret) return ret; - intel_ring_emit(engine, MI_NOOP); - intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(1)); - intel_ring_emit_reg(engine, INSTPM); - intel_ring_emit(engine, instp_mask << 16 | instp_mode); - intel_ring_advance(engine); + intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1)); + intel_ring_emit_reg(ring, INSTPM); + intel_ring_emit(ring, instp_mask << 16 | instp_mode); + intel_ring_advance(ring); dev_priv->relative_constants_mode = instp_mode; } if (args->flags & I915_EXEC_GEN7_SOL_RESET) { - ret = i915_reset_gen7_sol_offsets(dev, params->request); + ret = i915_reset_gen7_sol_offsets(params->request); if (ret) return ret; } exec_len = args->batch_len; - exec_start = params->batch_obj_vm_offset + + exec_start = params->batch->node.start + params->args_batch_start_offset; if (exec_len == 0) - exec_len = params->batch_obj->base.size; + exec_len = params->batch->size - params->args_batch_start_offset; - ret = engine->dispatch_execbuffer(params->request, - exec_start, exec_len, - params->dispatch_flags); + ret = params->engine->emit_bb_start(params->request, + exec_start, exec_len, + params->dispatch_flags); if (ret) return ret; @@ -1318,43 +1538,20 @@ i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params, /** * Find one BSD ring to dispatch the corresponding BSD command. - * The ring index is returned. + * The engine index is returned. */ static unsigned int -gen8_dispatch_bsd_ring(struct drm_i915_private *dev_priv, struct drm_file *file) +gen8_dispatch_bsd_engine(struct drm_i915_private *dev_priv, + struct drm_file *file) { struct drm_i915_file_private *file_priv = file->driver_priv; /* Check whether the file_priv has already selected one ring. */ - if ((int)file_priv->bsd_ring < 0) { - /* If not, use the ping-pong mechanism to select one. */ - mutex_lock(&dev_priv->drm.struct_mutex); - file_priv->bsd_ring = dev_priv->mm.bsd_ring_dispatch_index; - dev_priv->mm.bsd_ring_dispatch_index ^= 1; - mutex_unlock(&dev_priv->drm.struct_mutex); - } + if ((int)file_priv->bsd_engine < 0) + file_priv->bsd_engine = atomic_fetch_xor(1, + &dev_priv->mm.bsd_engine_dispatch_index); - return file_priv->bsd_ring; -} - -static struct drm_i915_gem_object * -eb_get_batch(struct eb_vmas *eb) -{ - struct i915_vma *vma = list_entry(eb->vmas.prev, typeof(*vma), exec_list); - - /* - * SNA is doing fancy tricks with compressing batch buffers, which leads - * to negative relocation deltas. Usually that works out ok since the - * relocate address is still positive, except when the batch is placed - * very low in the GTT. Ensure this doesn't happen. - * - * Note that actual hangs have only been observed on gen7, but for - * paranoia do it everywhere. - */ - if ((vma->exec_entry->flags & EXEC_OBJECT_PINNED) == 0) - vma->exec_entry->flags |= __EXEC_OBJECT_NEEDS_BIAS; - - return vma->obj; + return file_priv->bsd_engine; } #define I915_USER_RINGS (4) @@ -1367,31 +1564,31 @@ static const enum intel_engine_id user_ring_map[I915_USER_RINGS + 1] = { [I915_EXEC_VEBOX] = VECS }; -static int -eb_select_ring(struct drm_i915_private *dev_priv, - struct drm_file *file, - struct drm_i915_gem_execbuffer2 *args, - struct intel_engine_cs **ring) +static struct intel_engine_cs * +eb_select_engine(struct drm_i915_private *dev_priv, + struct drm_file *file, + struct drm_i915_gem_execbuffer2 *args) { unsigned int user_ring_id = args->flags & I915_EXEC_RING_MASK; + struct intel_engine_cs *engine; if (user_ring_id > I915_USER_RINGS) { DRM_DEBUG("execbuf with unknown ring: %u\n", user_ring_id); - return -EINVAL; + return NULL; } if ((user_ring_id != I915_EXEC_BSD) && ((args->flags & I915_EXEC_BSD_MASK) != 0)) { DRM_DEBUG("execbuf with non bsd ring but with invalid " "bsd dispatch flags: %d\n", (int)(args->flags)); - return -EINVAL; + return NULL; } if (user_ring_id == I915_EXEC_BSD && HAS_BSD2(dev_priv)) { unsigned int bsd_idx = args->flags & I915_EXEC_BSD_MASK; if (bsd_idx == I915_EXEC_BSD_DEFAULT) { - bsd_idx = gen8_dispatch_bsd_ring(dev_priv, file); + bsd_idx = gen8_dispatch_bsd_engine(dev_priv, file); } else if (bsd_idx >= I915_EXEC_BSD_RING1 && bsd_idx <= I915_EXEC_BSD_RING2) { bsd_idx >>= I915_EXEC_BSD_SHIFT; @@ -1399,20 +1596,20 @@ eb_select_ring(struct drm_i915_private *dev_priv, } else { DRM_DEBUG("execbuf with unknown bsd ring: %u\n", bsd_idx); - return -EINVAL; + return NULL; } - *ring = &dev_priv->engine[_VCS(bsd_idx)]; + engine = &dev_priv->engine[_VCS(bsd_idx)]; } else { - *ring = &dev_priv->engine[user_ring_map[user_ring_id]]; + engine = &dev_priv->engine[user_ring_map[user_ring_id]]; } - if (!intel_engine_initialized(*ring)) { + if (!intel_engine_initialized(engine)) { DRM_DEBUG("execbuf with invalid ring: %u\n", user_ring_id); - return -EINVAL; + return NULL; } - return 0; + return engine; } static int @@ -1423,9 +1620,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, { struct drm_i915_private *dev_priv = to_i915(dev); struct i915_ggtt *ggtt = &dev_priv->ggtt; - struct drm_i915_gem_request *req = NULL; struct eb_vmas *eb; - struct drm_i915_gem_object *batch_obj; struct drm_i915_gem_exec_object2 shadow_exec_entry; struct intel_engine_cs *engine; struct i915_gem_context *ctx; @@ -1454,9 +1649,9 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, if (args->flags & I915_EXEC_IS_PINNED) dispatch_flags |= I915_DISPATCH_PINNED; - ret = eb_select_ring(dev_priv, file, args, &engine); - if (ret) - return ret; + engine = eb_select_engine(dev_priv, file, args); + if (!engine) + return -EINVAL; if (args->buffer_count < 1) { DRM_DEBUG("execbuf with %d buffers\n", args->buffer_count); @@ -1496,7 +1691,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, goto pre_mutex_err; } - i915_gem_context_reference(ctx); + i915_gem_context_get(ctx); if (ctx->ppgtt) vm = &ctx->ppgtt->base; @@ -1505,9 +1700,9 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, memset(¶ms_master, 0x00, sizeof(params_master)); - eb = eb_create(args); + eb = eb_create(dev_priv, args); if (eb == NULL) { - i915_gem_context_unreference(ctx); + i915_gem_context_put(ctx); mutex_unlock(&dev->struct_mutex); ret = -ENOMEM; goto pre_mutex_err; @@ -1519,7 +1714,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, goto err; /* take note of the batch buffer before we might reorder the lists */ - batch_obj = eb_get_batch(eb); + params->batch = eb_get_batch(eb); /* Move the objects en-masse into the GTT, evicting if necessary. */ need_relocs = (args->flags & I915_EXEC_NO_RELOC) == 0; @@ -1543,34 +1738,34 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, } /* Set the pending read domains for the batch buffer to COMMAND */ - if (batch_obj->base.pending_write_domain) { + if (params->batch->obj->base.pending_write_domain) { DRM_DEBUG("Attempting to use self-modifying batch buffer\n"); ret = -EINVAL; goto err; } + if (args->batch_start_offset > params->batch->size || + args->batch_len > params->batch->size - args->batch_start_offset) { + DRM_DEBUG("Attempting to use out-of-bounds batch\n"); + ret = -EINVAL; + goto err; + } params->args_batch_start_offset = args->batch_start_offset; - if (i915_needs_cmd_parser(engine) && args->batch_len) { - struct drm_i915_gem_object *parsed_batch_obj; - - parsed_batch_obj = i915_gem_execbuffer_parse(engine, - &shadow_exec_entry, - eb, - batch_obj, - args->batch_start_offset, - args->batch_len, - drm_is_current_master(file)); - if (IS_ERR(parsed_batch_obj)) { - ret = PTR_ERR(parsed_batch_obj); + if (intel_engine_needs_cmd_parser(engine) && args->batch_len) { + struct i915_vma *vma; + + vma = i915_gem_execbuffer_parse(engine, &shadow_exec_entry, + params->batch->obj, + eb, + args->batch_start_offset, + args->batch_len, + drm_is_current_master(file)); + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); goto err; } - /* - * parsed_batch_obj == batch_obj means batch not fully parsed: - * Accept, but don't promote to secure. - */ - - if (parsed_batch_obj != batch_obj) { + if (vma) { /* * Batch parsed and accepted: * @@ -1582,16 +1777,19 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, */ dispatch_flags |= I915_DISPATCH_SECURE; params->args_batch_start_offset = 0; - batch_obj = parsed_batch_obj; + params->batch = vma; } } - batch_obj->base.pending_read_domains |= I915_GEM_DOMAIN_COMMAND; + params->batch->obj->base.pending_read_domains |= I915_GEM_DOMAIN_COMMAND; /* snb/ivb/vlv conflate the "batch in ppgtt" bit with the "non-secure * batch" bit. Hence we need to pin secure batches into the global gtt. * hsw should have this fixed, but bdw mucks it up again. */ if (dispatch_flags & I915_DISPATCH_SECURE) { + struct drm_i915_gem_object *obj = params->batch->obj; + struct i915_vma *vma; + /* * So on first glance it looks freaky that we pin the batch here * outside of the reservation loop. But: @@ -1602,22 +1800,31 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, * fitting due to fragmentation. * So this is actually safe. */ - ret = i915_gem_obj_ggtt_pin(batch_obj, 0, 0); - if (ret) + vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0); + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); goto err; + } - params->batch_obj_vm_offset = i915_gem_obj_ggtt_offset(batch_obj); - } else - params->batch_obj_vm_offset = i915_gem_obj_offset(batch_obj, vm); + params->batch = vma; + } /* Allocate a request for this batch buffer nice and early. */ - req = i915_gem_request_alloc(engine, ctx); - if (IS_ERR(req)) { - ret = PTR_ERR(req); + params->request = i915_gem_request_alloc(engine, ctx); + if (IS_ERR(params->request)) { + ret = PTR_ERR(params->request); goto err_batch_unpin; } - ret = i915_gem_request_add_to_client(req, file); + /* Whilst this request exists, batch_obj will be on the + * active_list, and so will hold the active reference. Only when this + * request is retired will the the batch_obj be moved onto the + * inactive_list and lose its active reference. Hence we do not need + * to explicitly hold another reference here. + */ + params->request->batch = params->batch; + + ret = i915_gem_request_add_to_client(params->request, file); if (ret) goto err_request; @@ -1631,13 +1838,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, params->file = file; params->engine = engine; params->dispatch_flags = dispatch_flags; - params->batch_obj = batch_obj; params->ctx = ctx; - params->request = req; - ret = dev_priv->gt.execbuf_submit(params, args, &eb->vmas); + ret = execbuf_submit(params, args, &eb->vmas); err_request: - i915_gem_execbuffer_retire_commands(params); + __i915_add_request(params->request, ret == 0); err_batch_unpin: /* @@ -1647,11 +1852,10 @@ err_batch_unpin: * active. */ if (dispatch_flags & I915_DISPATCH_SECURE) - i915_gem_object_ggtt_unpin(batch_obj); - + i915_vma_unpin(params->batch); err: /* the request owns the ref now */ - i915_gem_context_unreference(ctx); + i915_gem_context_put(ctx); eb_destroy(eb); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_gem_fence.c b/drivers/gpu/drm/i915/i915_gem_fence.c index 251d7a9..8df1fa7 100644 --- a/drivers/gpu/drm/i915/i915_gem_fence.c +++ b/drivers/gpu/drm/i915/i915_gem_fence.c @@ -55,226 +55,228 @@ * CPU ptes into GTT mmaps (not the GTT ptes themselves) as needed. */ -static void i965_write_fence_reg(struct drm_device *dev, int reg, - struct drm_i915_gem_object *obj) +#define pipelined 0 + +static void i965_write_fence_reg(struct drm_i915_fence_reg *fence, + struct i915_vma *vma) { - struct drm_i915_private *dev_priv = to_i915(dev); i915_reg_t fence_reg_lo, fence_reg_hi; int fence_pitch_shift; + u64 val; - if (INTEL_INFO(dev)->gen >= 6) { - fence_reg_lo = FENCE_REG_GEN6_LO(reg); - fence_reg_hi = FENCE_REG_GEN6_HI(reg); + if (INTEL_INFO(fence->i915)->gen >= 6) { + fence_reg_lo = FENCE_REG_GEN6_LO(fence->id); + fence_reg_hi = FENCE_REG_GEN6_HI(fence->id); fence_pitch_shift = GEN6_FENCE_PITCH_SHIFT; + } else { - fence_reg_lo = FENCE_REG_965_LO(reg); - fence_reg_hi = FENCE_REG_965_HI(reg); + fence_reg_lo = FENCE_REG_965_LO(fence->id); + fence_reg_hi = FENCE_REG_965_HI(fence->id); fence_pitch_shift = I965_FENCE_PITCH_SHIFT; } - /* To w/a incoherency with non-atomic 64-bit register updates, - * we split the 64-bit update into two 32-bit writes. In order - * for a partial fence not to be evaluated between writes, we - * precede the update with write to turn off the fence register, - * and only enable the fence as the last step. - * - * For extra levels of paranoia, we make sure each step lands - * before applying the next step. - */ - I915_WRITE(fence_reg_lo, 0); - POSTING_READ(fence_reg_lo); - - if (obj) { - u32 size = i915_gem_obj_ggtt_size(obj); - uint64_t val; - - /* Adjust fence size to match tiled area */ - if (obj->tiling_mode != I915_TILING_NONE) { - uint32_t row_size = obj->stride * - (obj->tiling_mode == I915_TILING_Y ? 32 : 8); - size = (size / row_size) * row_size; - } - - val = (uint64_t)((i915_gem_obj_ggtt_offset(obj) + size - 4096) & - 0xfffff000) << 32; - val |= i915_gem_obj_ggtt_offset(obj) & 0xfffff000; - val |= (uint64_t)((obj->stride / 128) - 1) << fence_pitch_shift; - if (obj->tiling_mode == I915_TILING_Y) - val |= 1 << I965_FENCE_TILING_Y_SHIFT; + val = 0; + if (vma) { + unsigned int tiling = i915_gem_object_get_tiling(vma->obj); + bool is_y_tiled = tiling == I915_TILING_Y; + unsigned int stride = i915_gem_object_get_stride(vma->obj); + u32 row_size = stride * (is_y_tiled ? 32 : 8); + u32 size = rounddown((u32)vma->node.size, row_size); + + val = ((vma->node.start + size - 4096) & 0xfffff000) << 32; + val |= vma->node.start & 0xfffff000; + val |= (u64)((stride / 128) - 1) << fence_pitch_shift; + if (is_y_tiled) + val |= BIT(I965_FENCE_TILING_Y_SHIFT); val |= I965_FENCE_REG_VALID; + } - I915_WRITE(fence_reg_hi, val >> 32); - POSTING_READ(fence_reg_hi); + if (!pipelined) { + struct drm_i915_private *dev_priv = fence->i915; - I915_WRITE(fence_reg_lo, val); + /* To w/a incoherency with non-atomic 64-bit register updates, + * we split the 64-bit update into two 32-bit writes. In order + * for a partial fence not to be evaluated between writes, we + * precede the update with write to turn off the fence register, + * and only enable the fence as the last step. + * + * For extra levels of paranoia, we make sure each step lands + * before applying the next step. + */ + I915_WRITE(fence_reg_lo, 0); + POSTING_READ(fence_reg_lo); + + I915_WRITE(fence_reg_hi, upper_32_bits(val)); + I915_WRITE(fence_reg_lo, lower_32_bits(val)); POSTING_READ(fence_reg_lo); - } else { - I915_WRITE(fence_reg_hi, 0); - POSTING_READ(fence_reg_hi); } } -static void i915_write_fence_reg(struct drm_device *dev, int reg, - struct drm_i915_gem_object *obj) +static void i915_write_fence_reg(struct drm_i915_fence_reg *fence, + struct i915_vma *vma) { - struct drm_i915_private *dev_priv = to_i915(dev); u32 val; - if (obj) { - u32 size = i915_gem_obj_ggtt_size(obj); + val = 0; + if (vma) { + unsigned int tiling = i915_gem_object_get_tiling(vma->obj); + bool is_y_tiled = tiling == I915_TILING_Y; + unsigned int stride = i915_gem_object_get_stride(vma->obj); int pitch_val; int tile_width; - WARN((i915_gem_obj_ggtt_offset(obj) & ~I915_FENCE_START_MASK) || - (size & -size) != size || - (i915_gem_obj_ggtt_offset(obj) & (size - 1)), - "object 0x%08llx [fenceable? %d] not 1M or pot-size (0x%08x) aligned\n", - i915_gem_obj_ggtt_offset(obj), obj->map_and_fenceable, size); + WARN((vma->node.start & ~I915_FENCE_START_MASK) || + !is_power_of_2(vma->node.size) || + (vma->node.start & (vma->node.size - 1)), + "object 0x%08llx [fenceable? %d] not 1M or pot-size (0x%08llx) aligned\n", + vma->node.start, + i915_vma_is_map_and_fenceable(vma), + vma->node.size); - if (obj->tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev)) + if (is_y_tiled && HAS_128_BYTE_Y_TILING(fence->i915)) tile_width = 128; else tile_width = 512; /* Note: pitch better be a power of two tile widths */ - pitch_val = obj->stride / tile_width; + pitch_val = stride / tile_width; pitch_val = ffs(pitch_val) - 1; - val = i915_gem_obj_ggtt_offset(obj); - if (obj->tiling_mode == I915_TILING_Y) - val |= 1 << I830_FENCE_TILING_Y_SHIFT; - val |= I915_FENCE_SIZE_BITS(size); + val = vma->node.start; + if (is_y_tiled) + val |= BIT(I830_FENCE_TILING_Y_SHIFT); + val |= I915_FENCE_SIZE_BITS(vma->node.size); val |= pitch_val << I830_FENCE_PITCH_SHIFT; val |= I830_FENCE_REG_VALID; - } else - val = 0; + } + + if (!pipelined) { + struct drm_i915_private *dev_priv = fence->i915; + i915_reg_t reg = FENCE_REG(fence->id); - I915_WRITE(FENCE_REG(reg), val); - POSTING_READ(FENCE_REG(reg)); + I915_WRITE(reg, val); + POSTING_READ(reg); + } } -static void i830_write_fence_reg(struct drm_device *dev, int reg, - struct drm_i915_gem_object *obj) +static void i830_write_fence_reg(struct drm_i915_fence_reg *fence, + struct i915_vma *vma) { - struct drm_i915_private *dev_priv = to_i915(dev); - uint32_t val; + u32 val; - if (obj) { - u32 size = i915_gem_obj_ggtt_size(obj); - uint32_t pitch_val; + val = 0; + if (vma) { + unsigned int tiling = i915_gem_object_get_tiling(vma->obj); + bool is_y_tiled = tiling == I915_TILING_Y; + unsigned int stride = i915_gem_object_get_stride(vma->obj); + u32 pitch_val; - WARN((i915_gem_obj_ggtt_offset(obj) & ~I830_FENCE_START_MASK) || - (size & -size) != size || - (i915_gem_obj_ggtt_offset(obj) & (size - 1)), - "object 0x%08llx not 512K or pot-size 0x%08x aligned\n", - i915_gem_obj_ggtt_offset(obj), size); + WARN((vma->node.start & ~I830_FENCE_START_MASK) || + !is_power_of_2(vma->node.size) || + (vma->node.start & (vma->node.size - 1)), + "object 0x%08llx not 512K or pot-size 0x%08llx aligned\n", + vma->node.start, vma->node.size); - pitch_val = obj->stride / 128; + pitch_val = stride / 128; pitch_val = ffs(pitch_val) - 1; - val = i915_gem_obj_ggtt_offset(obj); - if (obj->tiling_mode == I915_TILING_Y) - val |= 1 << I830_FENCE_TILING_Y_SHIFT; - val |= I830_FENCE_SIZE_BITS(size); + val = vma->node.start; + if (is_y_tiled) + val |= BIT(I830_FENCE_TILING_Y_SHIFT); + val |= I830_FENCE_SIZE_BITS(vma->node.size); val |= pitch_val << I830_FENCE_PITCH_SHIFT; val |= I830_FENCE_REG_VALID; - } else - val = 0; + } - I915_WRITE(FENCE_REG(reg), val); - POSTING_READ(FENCE_REG(reg)); -} + if (!pipelined) { + struct drm_i915_private *dev_priv = fence->i915; + i915_reg_t reg = FENCE_REG(fence->id); -inline static bool i915_gem_object_needs_mb(struct drm_i915_gem_object *obj) -{ - return obj && obj->base.read_domains & I915_GEM_DOMAIN_GTT; + I915_WRITE(reg, val); + POSTING_READ(reg); + } } -static void i915_gem_write_fence(struct drm_device *dev, int reg, - struct drm_i915_gem_object *obj) +static void fence_write(struct drm_i915_fence_reg *fence, + struct i915_vma *vma) { - struct drm_i915_private *dev_priv = to_i915(dev); - - /* Ensure that all CPU reads are completed before installing a fence - * and all writes before removing the fence. + /* Previous access through the fence register is marshalled by + * the mb() inside the fault handlers (i915_gem_release_mmaps) + * and explicitly managed for internal users. */ - if (i915_gem_object_needs_mb(dev_priv->fence_regs[reg].obj)) - mb(); - - WARN(obj && (!obj->stride || !obj->tiling_mode), - "bogus fence setup with stride: 0x%x, tiling mode: %i\n", - obj->stride, obj->tiling_mode); - - if (IS_GEN2(dev)) - i830_write_fence_reg(dev, reg, obj); - else if (IS_GEN3(dev)) - i915_write_fence_reg(dev, reg, obj); - else if (INTEL_INFO(dev)->gen >= 4) - i965_write_fence_reg(dev, reg, obj); - - /* And similarly be paranoid that no direct access to this region - * is reordered to before the fence is installed. + + if (IS_GEN2(fence->i915)) + i830_write_fence_reg(fence, vma); + else if (IS_GEN3(fence->i915)) + i915_write_fence_reg(fence, vma); + else + i965_write_fence_reg(fence, vma); + + /* Access through the fenced region afterwards is + * ordered by the posting reads whilst writing the registers. */ - if (i915_gem_object_needs_mb(obj)) - mb(); -} -static inline int fence_number(struct drm_i915_private *dev_priv, - struct drm_i915_fence_reg *fence) -{ - return fence - dev_priv->fence_regs; + fence->dirty = false; } -static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj, - struct drm_i915_fence_reg *fence, - bool enable) +static int fence_update(struct drm_i915_fence_reg *fence, + struct i915_vma *vma) { - struct drm_i915_private *dev_priv = to_i915(obj->base.dev); - int reg = fence_number(dev_priv, fence); - - i915_gem_write_fence(obj->base.dev, reg, enable ? obj : NULL); + int ret; - if (enable) { - obj->fence_reg = reg; - fence->obj = obj; - list_move_tail(&fence->lru_list, &dev_priv->mm.fence_list); - } else { - obj->fence_reg = I915_FENCE_REG_NONE; - fence->obj = NULL; - list_del_init(&fence->lru_list); - } - obj->fence_dirty = false; -} + if (vma) { + if (!i915_vma_is_map_and_fenceable(vma)) + return -EINVAL; -static inline void i915_gem_object_fence_lost(struct drm_i915_gem_object *obj) -{ - if (obj->tiling_mode) - i915_gem_release_mmap(obj); + if (WARN(!i915_gem_object_get_stride(vma->obj) || + !i915_gem_object_get_tiling(vma->obj), + "bogus fence setup with stride: 0x%x, tiling mode: %i\n", + i915_gem_object_get_stride(vma->obj), + i915_gem_object_get_tiling(vma->obj))) + return -EINVAL; - /* As we do not have an associated fence register, we will force - * a tiling change if we ever need to acquire one. - */ - obj->fence_dirty = false; - obj->fence_reg = I915_FENCE_REG_NONE; -} + ret = i915_gem_active_retire(&vma->last_fence, + &vma->obj->base.dev->struct_mutex); + if (ret) + return ret; + } -static int -i915_gem_object_wait_fence(struct drm_i915_gem_object *obj) -{ - if (obj->last_fenced_req) { - int ret = i915_wait_request(obj->last_fenced_req); + if (fence->vma) { + ret = i915_gem_active_retire(&fence->vma->last_fence, + &fence->vma->obj->base.dev->struct_mutex); if (ret) return ret; + } + + if (fence->vma && fence->vma != vma) { + /* Ensure that all userspace CPU access is completed before + * stealing the fence. + */ + i915_gem_release_mmap(fence->vma->obj); + + fence->vma->fence = NULL; + fence->vma = NULL; + + list_move(&fence->link, &fence->i915->mm.fence_list); + } + + fence_write(fence, vma); + + if (vma) { + if (fence->vma != vma) { + vma->fence = fence; + fence->vma = vma; + } - i915_gem_request_assign(&obj->last_fenced_req, NULL); + list_move_tail(&fence->link, &fence->i915->mm.fence_list); } return 0; } /** - * i915_gem_object_put_fence - force-remove fence for an object - * @obj: object to map through a fence reg + * i915_vma_put_fence - force-remove fence for a VMA + * @vma: vma to map linearly (not through a fence reg) * * This function force-removes any fence from the given object, which is useful * if the kernel wants to do untiled GTT access. @@ -284,70 +286,40 @@ i915_gem_object_wait_fence(struct drm_i915_gem_object *obj) * 0 on success, negative error code on failure. */ int -i915_gem_object_put_fence(struct drm_i915_gem_object *obj) +i915_vma_put_fence(struct i915_vma *vma) { - struct drm_i915_private *dev_priv = to_i915(obj->base.dev); - struct drm_i915_fence_reg *fence; - int ret; + struct drm_i915_fence_reg *fence = vma->fence; - ret = i915_gem_object_wait_fence(obj); - if (ret) - return ret; - - if (obj->fence_reg == I915_FENCE_REG_NONE) + if (!fence) return 0; - fence = &dev_priv->fence_regs[obj->fence_reg]; - - if (WARN_ON(fence->pin_count)) + if (fence->pin_count) return -EBUSY; - i915_gem_object_fence_lost(obj); - i915_gem_object_update_fence(obj, fence, false); - - return 0; + return fence_update(fence, NULL); } -static struct drm_i915_fence_reg * -i915_find_fence_reg(struct drm_device *dev) +static struct drm_i915_fence_reg *fence_find(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); - struct drm_i915_fence_reg *reg, *avail; - int i; - - /* First try to find a free reg */ - avail = NULL; - for (i = 0; i < dev_priv->num_fence_regs; i++) { - reg = &dev_priv->fence_regs[i]; - if (!reg->obj) - return reg; - - if (!reg->pin_count) - avail = reg; - } - - if (avail == NULL) - goto deadlock; + struct drm_i915_fence_reg *fence; - /* None available, try to steal one or wait for a user to finish */ - list_for_each_entry(reg, &dev_priv->mm.fence_list, lru_list) { - if (reg->pin_count) + list_for_each_entry(fence, &dev_priv->mm.fence_list, link) { + if (fence->pin_count) continue; - return reg; + return fence; } -deadlock: /* Wait for completion of pending flips which consume fences */ - if (intel_has_pending_fb_unpin(dev)) + if (intel_has_pending_fb_unpin(&dev_priv->drm)) return ERR_PTR(-EAGAIN); return ERR_PTR(-EDEADLK); } /** - * i915_gem_object_get_fence - set up fencing for an object - * @obj: object to map through a fence reg + * i915_vma_get_fence - set up fencing for a vma + * @vma: vma to map through a fence reg * * When mapping objects through the GTT, userspace wants to be able to write * to them without having to worry about swizzling if the object is tiled. @@ -364,103 +336,27 @@ deadlock: * 0 on success, negative error code on failure. */ int -i915_gem_object_get_fence(struct drm_i915_gem_object *obj) +i915_vma_get_fence(struct i915_vma *vma) { - struct drm_device *dev = obj->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - bool enable = obj->tiling_mode != I915_TILING_NONE; - struct drm_i915_fence_reg *reg; - int ret; - - /* Have we updated the tiling parameters upon the object and so - * will need to serialise the write to the associated fence register? - */ - if (obj->fence_dirty) { - ret = i915_gem_object_wait_fence(obj); - if (ret) - return ret; - } + struct drm_i915_fence_reg *fence; + struct i915_vma *set = i915_gem_object_is_tiled(vma->obj) ? vma : NULL; /* Just update our place in the LRU if our fence is getting reused. */ - if (obj->fence_reg != I915_FENCE_REG_NONE) { - reg = &dev_priv->fence_regs[obj->fence_reg]; - if (!obj->fence_dirty) { - list_move_tail(®->lru_list, - &dev_priv->mm.fence_list); + if (vma->fence) { + fence = vma->fence; + if (!fence->dirty) { + list_move_tail(&fence->link, + &fence->i915->mm.fence_list); return 0; } - } else if (enable) { - if (WARN_ON(!obj->map_and_fenceable)) - return -EINVAL; - - reg = i915_find_fence_reg(dev); - if (IS_ERR(reg)) - return PTR_ERR(reg); - - if (reg->obj) { - struct drm_i915_gem_object *old = reg->obj; - - ret = i915_gem_object_wait_fence(old); - if (ret) - return ret; - - i915_gem_object_fence_lost(old); - } + } else if (set) { + fence = fence_find(to_i915(vma->vm->dev)); + if (IS_ERR(fence)) + return PTR_ERR(fence); } else return 0; - i915_gem_object_update_fence(obj, reg, enable); - - return 0; -} - -/** - * i915_gem_object_pin_fence - pin fencing state - * @obj: object to pin fencing for - * - * This pins the fencing state (whether tiled or untiled) to make sure the - * object is ready to be used as a scanout target. Fencing status must be - * synchronize first by calling i915_gem_object_get_fence(): - * - * The resulting fence pin reference must be released again with - * i915_gem_object_unpin_fence(). - * - * Returns: - * - * True if the object has a fence, false otherwise. - */ -bool -i915_gem_object_pin_fence(struct drm_i915_gem_object *obj) -{ - if (obj->fence_reg != I915_FENCE_REG_NONE) { - struct drm_i915_private *dev_priv = to_i915(obj->base.dev); - struct i915_vma *ggtt_vma = i915_gem_obj_to_ggtt(obj); - - WARN_ON(!ggtt_vma || - dev_priv->fence_regs[obj->fence_reg].pin_count > - ggtt_vma->pin_count); - dev_priv->fence_regs[obj->fence_reg].pin_count++; - return true; - } else - return false; -} - -/** - * i915_gem_object_unpin_fence - unpin fencing state - * @obj: object to unpin fencing for - * - * This releases the fence pin reference acquired through - * i915_gem_object_pin_fence. It will handle both objects with and without an - * attached fence correctly, callers do not need to distinguish this. - */ -void -i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj) -{ - if (obj->fence_reg != I915_FENCE_REG_NONE) { - struct drm_i915_private *dev_priv = to_i915(obj->base.dev); - WARN_ON(dev_priv->fence_regs[obj->fence_reg].pin_count <= 0); - dev_priv->fence_regs[obj->fence_reg].pin_count--; - } + return fence_update(fence, set); } /** @@ -477,17 +373,16 @@ void i915_gem_restore_fences(struct drm_device *dev) for (i = 0; i < dev_priv->num_fence_regs; i++) { struct drm_i915_fence_reg *reg = &dev_priv->fence_regs[i]; + struct i915_vma *vma = reg->vma; /* * Commit delayed tiling changes if we have an object still * attached to the fence, otherwise just clear the fence. */ - if (reg->obj) { - i915_gem_object_update_fence(reg->obj, reg, - reg->obj->tiling_mode); - } else { - i915_gem_write_fence(dev, i, NULL); - } + if (vma && !i915_gem_object_is_tiled(vma->obj)) + vma = NULL; + + fence_update(reg, vma); } } diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 10f1e32..0bb4232 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -32,6 +32,8 @@ #include "i915_trace.h" #include "intel_drv.h" +#define I915_GFP_DMA (GFP_KERNEL | __GFP_HIGHMEM) + /** * DOC: Global GTT views * @@ -122,8 +124,11 @@ int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv, has_full_48bit_ppgtt = IS_BROADWELL(dev_priv) || INTEL_GEN(dev_priv) >= 9; - if (intel_vgpu_active(dev_priv)) - has_full_ppgtt = false; /* emulation is too hard */ + if (intel_vgpu_active(dev_priv)) { + /* emulation is too hard */ + has_full_ppgtt = false; + has_full_48bit_ppgtt = false; + } if (!has_aliasing_ppgtt) return 0; @@ -158,7 +163,7 @@ int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv, return 0; } - if (INTEL_GEN(dev_priv) >= 8 && i915.enable_execlists) + if (INTEL_GEN(dev_priv) >= 8 && i915.enable_execlists && has_full_ppgtt) return has_full_48bit_ppgtt ? 3 : 2; else return has_aliasing_ppgtt ? 1 : 0; @@ -170,11 +175,13 @@ static int ppgtt_bind_vma(struct i915_vma *vma, { u32 pte_flags = 0; + vma->pages = vma->obj->pages; + /* Currently applicable only to VLV */ if (vma->obj->gt_ro) pte_flags |= PTE_READ_ONLY; - vma->vm->insert_entries(vma->vm, vma->obj->pages, vma->node.start, + vma->vm->insert_entries(vma->vm, vma->pages, vma->node.start, cache_level, pte_flags); return 0; @@ -184,7 +191,7 @@ static void ppgtt_unbind_vma(struct i915_vma *vma) { vma->vm->clear_range(vma->vm, vma->node.start, - vma->obj->base.size, + vma->size, true); } @@ -324,16 +331,16 @@ static gen6_pte_t iris_pte_encode(dma_addr_t addr, static int __setup_page_dma(struct drm_device *dev, struct i915_page_dma *p, gfp_t flags) { - struct device *device = &dev->pdev->dev; + struct device *kdev = &dev->pdev->dev; p->page = alloc_page(flags); if (!p->page) return -ENOMEM; - p->daddr = dma_map_page(device, + p->daddr = dma_map_page(kdev, p->page, 0, 4096, PCI_DMA_BIDIRECTIONAL); - if (dma_mapping_error(device, p->daddr)) { + if (dma_mapping_error(kdev, p->daddr)) { __free_page(p->page); return -EINVAL; } @@ -343,15 +350,17 @@ static int __setup_page_dma(struct drm_device *dev, static int setup_page_dma(struct drm_device *dev, struct i915_page_dma *p) { - return __setup_page_dma(dev, p, GFP_KERNEL); + return __setup_page_dma(dev, p, I915_GFP_DMA); } static void cleanup_page_dma(struct drm_device *dev, struct i915_page_dma *p) { + struct pci_dev *pdev = dev->pdev; + if (WARN_ON(!p->page)) return; - dma_unmap_page(&dev->pdev->dev, p->daddr, 4096, PCI_DMA_BIDIRECTIONAL); + dma_unmap_page(&pdev->dev, p->daddr, 4096, PCI_DMA_BIDIRECTIONAL); __free_page(p->page); memset(p, 0, sizeof(*p)); } @@ -405,33 +414,18 @@ static void fill_page_dma_32(struct drm_device *dev, struct i915_page_dma *p, fill_page_dma(dev, p, v); } -static struct i915_page_scratch *alloc_scratch_page(struct drm_device *dev) +static int +setup_scratch_page(struct drm_device *dev, + struct i915_page_dma *scratch, + gfp_t gfp) { - struct i915_page_scratch *sp; - int ret; - - sp = kzalloc(sizeof(*sp), GFP_KERNEL); - if (sp == NULL) - return ERR_PTR(-ENOMEM); - - ret = __setup_page_dma(dev, px_base(sp), GFP_DMA32 | __GFP_ZERO); - if (ret) { - kfree(sp); - return ERR_PTR(ret); - } - - set_pages_uc(px_page(sp), 1); - - return sp; + return __setup_page_dma(dev, scratch, gfp | __GFP_ZERO); } -static void free_scratch_page(struct drm_device *dev, - struct i915_page_scratch *sp) +static void cleanup_scratch_page(struct drm_device *dev, + struct i915_page_dma *scratch) { - set_pages_wb(px_page(sp), 1); - - cleanup_px(dev, sp); - kfree(sp); + cleanup_page_dma(dev, scratch); } static struct i915_page_table *alloc_pt(struct drm_device *dev) @@ -477,7 +471,7 @@ static void gen8_initialize_pt(struct i915_address_space *vm, { gen8_pte_t scratch_pte; - scratch_pte = gen8_pte_encode(px_dma(vm->scratch_page), + scratch_pte = gen8_pte_encode(vm->scratch_page.daddr, I915_CACHE_LLC, true); fill_px(vm->dev, pt, scratch_pte); @@ -488,9 +482,9 @@ static void gen6_initialize_pt(struct i915_address_space *vm, { gen6_pte_t scratch_pte; - WARN_ON(px_dma(vm->scratch_page) == 0); + WARN_ON(vm->scratch_page.daddr == 0); - scratch_pte = vm->pte_encode(px_dma(vm->scratch_page), + scratch_pte = vm->pte_encode(vm->scratch_page.daddr, I915_CACHE_LLC, true, 0); fill32_px(vm->dev, pt, scratch_pte); @@ -669,6 +663,7 @@ static int gen8_write_pdp(struct drm_i915_gem_request *req, unsigned entry, dma_addr_t addr) { + struct intel_ring *ring = req->ring; struct intel_engine_cs *engine = req->engine; int ret; @@ -678,13 +673,13 @@ static int gen8_write_pdp(struct drm_i915_gem_request *req, if (ret) return ret; - intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(1)); - intel_ring_emit_reg(engine, GEN8_RING_PDP_UDW(engine, entry)); - intel_ring_emit(engine, upper_32_bits(addr)); - intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(1)); - intel_ring_emit_reg(engine, GEN8_RING_PDP_LDW(engine, entry)); - intel_ring_emit(engine, lower_32_bits(addr)); - intel_ring_advance(engine); + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1)); + intel_ring_emit_reg(ring, GEN8_RING_PDP_UDW(engine, entry)); + intel_ring_emit(ring, upper_32_bits(addr)); + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1)); + intel_ring_emit_reg(ring, GEN8_RING_PDP_LDW(engine, entry)); + intel_ring_emit(ring, lower_32_bits(addr)); + intel_ring_advance(ring); return 0; } @@ -773,7 +768,7 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm, bool use_scratch) { struct i915_hw_ppgtt *ppgtt = i915_vm_to_ppgtt(vm); - gen8_pte_t scratch_pte = gen8_pte_encode(px_dma(vm->scratch_page), + gen8_pte_t scratch_pte = gen8_pte_encode(vm->scratch_page.daddr, I915_CACHE_LLC, use_scratch); if (!USES_FULL_48BIT_PPGTT(vm->dev)) { @@ -879,9 +874,9 @@ static int gen8_init_scratch(struct i915_address_space *vm) struct drm_device *dev = vm->dev; int ret; - vm->scratch_page = alloc_scratch_page(dev); - if (IS_ERR(vm->scratch_page)) - return PTR_ERR(vm->scratch_page); + ret = setup_scratch_page(dev, &vm->scratch_page, I915_GFP_DMA); + if (ret) + return ret; vm->scratch_pt = alloc_pt(dev); if (IS_ERR(vm->scratch_pt)) { @@ -915,7 +910,7 @@ free_pd: free_pt: free_pt(dev, vm->scratch_pt); free_scratch_page: - free_scratch_page(dev, vm->scratch_page); + cleanup_scratch_page(dev, &vm->scratch_page); return ret; } @@ -959,7 +954,7 @@ static void gen8_free_scratch(struct i915_address_space *vm) free_pdp(dev, vm->scratch_pdp); free_pd(dev, vm->scratch_pd); free_pt(dev, vm->scratch_pt); - free_scratch_page(dev, vm->scratch_page); + cleanup_scratch_page(dev, &vm->scratch_page); } static void gen8_ppgtt_cleanup_3lvl(struct drm_device *dev, @@ -1456,7 +1451,7 @@ static void gen8_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m) struct i915_address_space *vm = &ppgtt->base; uint64_t start = ppgtt->base.start; uint64_t length = ppgtt->base.total; - gen8_pte_t scratch_pte = gen8_pte_encode(px_dma(vm->scratch_page), + gen8_pte_t scratch_pte = gen8_pte_encode(vm->scratch_page.daddr, I915_CACHE_LLC, true); if (!USES_FULL_48BIT_PPGTT(vm->dev)) { @@ -1573,7 +1568,7 @@ static void gen6_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m) uint32_t pte, pde; uint32_t start = ppgtt->base.start, length = ppgtt->base.total; - scratch_pte = vm->pte_encode(px_dma(vm->scratch_page), + scratch_pte = vm->pte_encode(vm->scratch_page.daddr, I915_CACHE_LLC, true, 0); gen6_for_each_pde(unused, &ppgtt->pd, start, length, pde) { @@ -1660,11 +1655,12 @@ static uint32_t get_pd_offset(struct i915_hw_ppgtt *ppgtt) static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt, struct drm_i915_gem_request *req) { + struct intel_ring *ring = req->ring; struct intel_engine_cs *engine = req->engine; int ret; /* NB: TLBs must be flushed and invalidated before a switch */ - ret = engine->flush(req, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS); + ret = engine->emit_flush(req, EMIT_INVALIDATE | EMIT_FLUSH); if (ret) return ret; @@ -1672,13 +1668,13 @@ static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt, if (ret) return ret; - intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(2)); - intel_ring_emit_reg(engine, RING_PP_DIR_DCLV(engine)); - intel_ring_emit(engine, PP_DIR_DCLV_2G); - intel_ring_emit_reg(engine, RING_PP_DIR_BASE(engine)); - intel_ring_emit(engine, get_pd_offset(ppgtt)); - intel_ring_emit(engine, MI_NOOP); - intel_ring_advance(engine); + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(2)); + intel_ring_emit_reg(ring, RING_PP_DIR_DCLV(engine)); + intel_ring_emit(ring, PP_DIR_DCLV_2G); + intel_ring_emit_reg(ring, RING_PP_DIR_BASE(engine)); + intel_ring_emit(ring, get_pd_offset(ppgtt)); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); return 0; } @@ -1686,11 +1682,12 @@ static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt, static int gen7_mm_switch(struct i915_hw_ppgtt *ppgtt, struct drm_i915_gem_request *req) { + struct intel_ring *ring = req->ring; struct intel_engine_cs *engine = req->engine; int ret; /* NB: TLBs must be flushed and invalidated before a switch */ - ret = engine->flush(req, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS); + ret = engine->emit_flush(req, EMIT_INVALIDATE | EMIT_FLUSH); if (ret) return ret; @@ -1698,17 +1695,17 @@ static int gen7_mm_switch(struct i915_hw_ppgtt *ppgtt, if (ret) return ret; - intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(2)); - intel_ring_emit_reg(engine, RING_PP_DIR_DCLV(engine)); - intel_ring_emit(engine, PP_DIR_DCLV_2G); - intel_ring_emit_reg(engine, RING_PP_DIR_BASE(engine)); - intel_ring_emit(engine, get_pd_offset(ppgtt)); - intel_ring_emit(engine, MI_NOOP); - intel_ring_advance(engine); + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(2)); + intel_ring_emit_reg(ring, RING_PP_DIR_DCLV(engine)); + intel_ring_emit(ring, PP_DIR_DCLV_2G); + intel_ring_emit_reg(ring, RING_PP_DIR_BASE(engine)); + intel_ring_emit(ring, get_pd_offset(ppgtt)); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); /* XXX: RCS is the only one to auto invalidate the TLBs? */ if (engine->id != RCS) { - ret = engine->flush(req, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS); + ret = engine->emit_flush(req, EMIT_INVALIDATE | EMIT_FLUSH); if (ret) return ret; } @@ -1796,7 +1793,7 @@ static void gen6_ppgtt_clear_range(struct i915_address_space *vm, unsigned first_pte = first_entry % GEN6_PTES; unsigned last_pte, i; - scratch_pte = vm->pte_encode(px_dma(vm->scratch_page), + scratch_pte = vm->pte_encode(vm->scratch_page.daddr, I915_CACHE_LLC, true, 0); while (num_entries) { @@ -1942,14 +1939,15 @@ unwind_out: static int gen6_init_scratch(struct i915_address_space *vm) { struct drm_device *dev = vm->dev; + int ret; - vm->scratch_page = alloc_scratch_page(dev); - if (IS_ERR(vm->scratch_page)) - return PTR_ERR(vm->scratch_page); + ret = setup_scratch_page(dev, &vm->scratch_page, I915_GFP_DMA); + if (ret) + return ret; vm->scratch_pt = alloc_pt(dev); if (IS_ERR(vm->scratch_pt)) { - free_scratch_page(dev, vm->scratch_page); + cleanup_scratch_page(dev, &vm->scratch_page); return PTR_ERR(vm->scratch_pt); } @@ -1963,7 +1961,7 @@ static void gen6_free_scratch(struct i915_address_space *vm) struct drm_device *dev = vm->dev; free_pt(dev, vm->scratch_pt); - free_scratch_page(dev, vm->scratch_page); + cleanup_scratch_page(dev, &vm->scratch_page); } static void gen6_ppgtt_cleanup(struct i915_address_space *vm) @@ -2009,7 +2007,7 @@ alloc: 0, ggtt->base.total, DRM_MM_TOPDOWN); if (ret == -ENOSPC && !retried) { - ret = i915_gem_evict_something(dev, &ggtt->base, + ret = i915_gem_evict_something(&ggtt->base, GEN6_PD_SIZE, GEN6_PD_ALIGN, I915_CACHE_NONE, 0, ggtt->base.total, @@ -2101,11 +2099,12 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) return 0; } -static int __hw_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt) +static int __hw_ppgtt_init(struct i915_hw_ppgtt *ppgtt, + struct drm_i915_private *dev_priv) { - ppgtt->base.dev = dev; + ppgtt->base.dev = &dev_priv->drm; - if (INTEL_INFO(dev)->gen < 8) + if (INTEL_INFO(dev_priv)->gen < 8) return gen6_ppgtt_init(ppgtt); else return gen8_ppgtt_init(ppgtt); @@ -2115,9 +2114,9 @@ static void i915_address_space_init(struct i915_address_space *vm, struct drm_i915_private *dev_priv) { drm_mm_init(&vm->mm, vm->start, vm->total); - vm->dev = &dev_priv->drm; INIT_LIST_HEAD(&vm->active_list); INIT_LIST_HEAD(&vm->inactive_list); + INIT_LIST_HEAD(&vm->unbound_list); list_add_tail(&vm->global_link, &dev_priv->vm_list); } @@ -2140,15 +2139,17 @@ static void gtt_write_workarounds(struct drm_device *dev) I915_WRITE(GEN8_L3_LRA_1_GPGPU, GEN9_L3_LRA_1_GPGPU_DEFAULT_VALUE_BXT); } -static int i915_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt) +static int i915_ppgtt_init(struct i915_hw_ppgtt *ppgtt, + struct drm_i915_private *dev_priv, + struct drm_i915_file_private *file_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); - int ret = 0; + int ret; - ret = __hw_ppgtt_init(dev, ppgtt); + ret = __hw_ppgtt_init(ppgtt, dev_priv); if (ret == 0) { kref_init(&ppgtt->ref); i915_address_space_init(&ppgtt->base, dev_priv); + ppgtt->base.file = file_priv; } return ret; @@ -2180,7 +2181,8 @@ int i915_ppgtt_init_hw(struct drm_device *dev) } struct i915_hw_ppgtt * -i915_ppgtt_create(struct drm_device *dev, struct drm_i915_file_private *fpriv) +i915_ppgtt_create(struct drm_i915_private *dev_priv, + struct drm_i915_file_private *fpriv) { struct i915_hw_ppgtt *ppgtt; int ret; @@ -2189,14 +2191,12 @@ i915_ppgtt_create(struct drm_device *dev, struct drm_i915_file_private *fpriv) if (!ppgtt) return ERR_PTR(-ENOMEM); - ret = i915_ppgtt_init(dev, ppgtt); + ret = i915_ppgtt_init(ppgtt, dev_priv, fpriv); if (ret) { kfree(ppgtt); return ERR_PTR(ret); } - ppgtt->file_priv = fpriv; - trace_i915_ppgtt_create(&ppgtt->base); return ppgtt; @@ -2209,9 +2209,10 @@ void i915_ppgtt_release(struct kref *kref) trace_i915_ppgtt_release(&ppgtt->base); - /* vmas should already be unbound */ + /* vmas should already be unbound and destroyed */ WARN_ON(!list_empty(&ppgtt->base.active_list)); WARN_ON(!list_empty(&ppgtt->base.inactive_list)); + WARN_ON(!list_empty(&ppgtt->base.unbound_list)); list_del(&ppgtt->base.global_link); drm_mm_takedown(&ppgtt->base.mm); @@ -2220,47 +2221,21 @@ void i915_ppgtt_release(struct kref *kref) kfree(ppgtt); } -extern int intel_iommu_gfx_mapped; /* Certain Gen5 chipsets require require idling the GPU before * unmapping anything from the GTT when VT-d is enabled. */ -static bool needs_idle_maps(struct drm_device *dev) +static bool needs_idle_maps(struct drm_i915_private *dev_priv) { #ifdef CONFIG_INTEL_IOMMU /* Query intel_iommu to see if we need the workaround. Presumably that * was loaded first. */ - if (IS_GEN5(dev) && IS_MOBILE(dev) && intel_iommu_gfx_mapped) + if (IS_GEN5(dev_priv) && IS_MOBILE(dev_priv) && intel_iommu_gfx_mapped) return true; #endif return false; } -static bool do_idling(struct drm_i915_private *dev_priv) -{ - struct i915_ggtt *ggtt = &dev_priv->ggtt; - bool ret = dev_priv->mm.interruptible; - - if (unlikely(ggtt->do_idle_maps)) { - dev_priv->mm.interruptible = false; - if (i915_gem_wait_for_idle(dev_priv)) { - DRM_ERROR("Failed to wait for idle; VT'd may hang.\n"); - /* Wait a bit, in hopes it avoids the hang */ - udelay(10); - } - } - - return ret; -} - -static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible) -{ - struct i915_ggtt *ggtt = &dev_priv->ggtt; - - if (unlikely(ggtt->do_idle_maps)) - dev_priv->mm.interruptible = interruptible; -} - void i915_check_and_clear_faults(struct drm_i915_private *dev_priv) { struct intel_engine_cs *engine; @@ -2329,12 +2304,7 @@ int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj) static void gen8_set_pte(void __iomem *addr, gen8_pte_t pte) { -#ifdef writeq writeq(pte, addr); -#else - iowrite32((u32)pte, addr); - iowrite32(pte >> 32, addr + 4); -#endif } static void gen8_ggtt_insert_page(struct i915_address_space *vm, @@ -2527,7 +2497,7 @@ static void gen8_ggtt_clear_range(struct i915_address_space *vm, first_entry, num_entries, max_entries)) num_entries = max_entries; - scratch_pte = gen8_pte_encode(px_dma(vm->scratch_page), + scratch_pte = gen8_pte_encode(vm->scratch_page.daddr, I915_CACHE_LLC, use_scratch); for (i = 0; i < num_entries; i++) @@ -2559,7 +2529,7 @@ static void gen6_ggtt_clear_range(struct i915_address_space *vm, first_entry, num_entries, max_entries)) num_entries = max_entries; - scratch_pte = vm->pte_encode(px_dma(vm->scratch_page), + scratch_pte = vm->pte_encode(vm->scratch_page.daddr, I915_CACHE_LLC, use_scratch, 0); for (i = 0; i < num_entries; i++) @@ -2638,8 +2608,7 @@ static int ggtt_bind_vma(struct i915_vma *vma, if (obj->gt_ro) pte_flags |= PTE_READ_ONLY; - vma->vm->insert_entries(vma->vm, vma->ggtt_view.pages, - vma->node.start, + vma->vm->insert_entries(vma->vm, vma->pages, vma->node.start, cache_level, pte_flags); /* @@ -2647,7 +2616,7 @@ static int ggtt_bind_vma(struct i915_vma *vma, * GLOBAL/LOCAL_BIND, it's all the same ptes. Hence unconditionally * upgrade to both bound if we bind either to avoid double-binding. */ - vma->bound |= GLOBAL_BIND | LOCAL_BIND; + vma->flags |= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND; return 0; } @@ -2669,19 +2638,17 @@ static int aliasing_gtt_bind_vma(struct i915_vma *vma, pte_flags |= PTE_READ_ONLY; - if (flags & GLOBAL_BIND) { + if (flags & I915_VMA_GLOBAL_BIND) { vma->vm->insert_entries(vma->vm, - vma->ggtt_view.pages, - vma->node.start, + vma->pages, vma->node.start, cache_level, pte_flags); } - if (flags & LOCAL_BIND) { + if (flags & I915_VMA_LOCAL_BIND) { struct i915_hw_ppgtt *appgtt = to_i915(vma->vm->dev)->mm.aliasing_ppgtt; appgtt->base.insert_entries(&appgtt->base, - vma->ggtt_view.pages, - vma->node.start, + vma->pages, vma->node.start, cache_level, pte_flags); } @@ -2690,42 +2657,36 @@ static int aliasing_gtt_bind_vma(struct i915_vma *vma, static void ggtt_unbind_vma(struct i915_vma *vma) { - struct drm_device *dev = vma->vm->dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct drm_i915_gem_object *obj = vma->obj; - const uint64_t size = min_t(uint64_t, - obj->base.size, - vma->node.size); + struct i915_hw_ppgtt *appgtt = to_i915(vma->vm->dev)->mm.aliasing_ppgtt; + const u64 size = min(vma->size, vma->node.size); - if (vma->bound & GLOBAL_BIND) { + if (vma->flags & I915_VMA_GLOBAL_BIND) vma->vm->clear_range(vma->vm, - vma->node.start, - size, + vma->node.start, size, true); - } - - if (dev_priv->mm.aliasing_ppgtt && vma->bound & LOCAL_BIND) { - struct i915_hw_ppgtt *appgtt = dev_priv->mm.aliasing_ppgtt; + if (vma->flags & I915_VMA_LOCAL_BIND && appgtt) appgtt->base.clear_range(&appgtt->base, - vma->node.start, - size, + vma->node.start, size, true); - } } void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj) { - struct drm_device *dev = obj->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - bool interruptible; + struct drm_i915_private *dev_priv = to_i915(obj->base.dev); + struct device *kdev = &dev_priv->drm.pdev->dev; + struct i915_ggtt *ggtt = &dev_priv->ggtt; - interruptible = do_idling(dev_priv); + if (unlikely(ggtt->do_idle_maps)) { + if (i915_gem_wait_for_idle(dev_priv, I915_WAIT_LOCKED)) { + DRM_ERROR("Failed to wait for idle; VT'd may hang.\n"); + /* Wait a bit, in hopes it avoids the hang */ + udelay(10); + } + } - dma_unmap_sg(&dev->pdev->dev, obj->pages->sgl, obj->pages->nents, + dma_unmap_sg(kdev, obj->pages->sgl, obj->pages->nents, PCI_DMA_BIDIRECTIONAL); - - undo_idling(dev_priv, interruptible); } static void i915_gtt_color_adjust(struct drm_mm_node *node, @@ -2736,19 +2697,14 @@ static void i915_gtt_color_adjust(struct drm_mm_node *node, if (node->color != color) *start += 4096; - if (!list_empty(&node->node_list)) { - node = list_entry(node->node_list.next, - struct drm_mm_node, - node_list); - if (node->allocated && node->color != color) - *end -= 4096; - } + node = list_first_entry_or_null(&node->node_list, + struct drm_mm_node, + node_list); + if (node && node->allocated && node->color != color) + *end -= 4096; } -static int i915_gem_setup_global_gtt(struct drm_device *dev, - u64 start, - u64 mappable_end, - u64 end) +int i915_gem_init_ggtt(struct drm_i915_private *dev_priv) { /* Let GEM Manage all of the aperture. * @@ -2759,48 +2715,15 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev, * aperture. One page should be enough to keep any prefetching inside * of the aperture. */ - struct drm_i915_private *dev_priv = to_i915(dev); struct i915_ggtt *ggtt = &dev_priv->ggtt; - struct drm_mm_node *entry; - struct drm_i915_gem_object *obj; unsigned long hole_start, hole_end; + struct drm_mm_node *entry; int ret; - BUG_ON(mappable_end > end); - - ggtt->base.start = start; - - /* Subtract the guard page before address space initialization to - * shrink the range used by drm_mm */ - ggtt->base.total = end - start - PAGE_SIZE; - i915_address_space_init(&ggtt->base, dev_priv); - ggtt->base.total += PAGE_SIZE; - ret = intel_vgt_balloon(dev_priv); if (ret) return ret; - if (!HAS_LLC(dev)) - ggtt->base.mm.color_adjust = i915_gtt_color_adjust; - - /* Mark any preallocated objects as occupied */ - list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) { - struct i915_vma *vma = i915_gem_obj_to_vma(obj, &ggtt->base); - - DRM_DEBUG_KMS("reserving preallocated space: %llx + %zx\n", - i915_gem_obj_ggtt_offset(obj), obj->base.size); - - WARN_ON(i915_gem_obj_ggtt_bound(obj)); - ret = drm_mm_reserve_node(&ggtt->base.mm, &vma->node); - if (ret) { - DRM_DEBUG_KMS("Reservation failed: %i\n", ret); - return ret; - } - vma->bound |= GLOBAL_BIND; - __i915_vma_set_map_and_fenceable(vma); - list_add_tail(&vma->vm_link, &ggtt->base.inactive_list); - } - /* Clear any non-preallocated blocks */ drm_mm_for_each_hole(entry, &ggtt->base.mm, hole_start, hole_end) { DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n", @@ -2810,18 +2733,19 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev, } /* And finally clear the reserved guard page */ - ggtt->base.clear_range(&ggtt->base, end - PAGE_SIZE, PAGE_SIZE, true); + ggtt->base.clear_range(&ggtt->base, + ggtt->base.total - PAGE_SIZE, PAGE_SIZE, + true); - if (USES_PPGTT(dev) && !USES_FULL_PPGTT(dev)) { + if (USES_PPGTT(dev_priv) && !USES_FULL_PPGTT(dev_priv)) { struct i915_hw_ppgtt *ppgtt; ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL); if (!ppgtt) return -ENOMEM; - ret = __hw_ppgtt_init(dev, ppgtt); + ret = __hw_ppgtt_init(ppgtt, dev_priv); if (ret) { - ppgtt->base.cleanup(&ppgtt->base); kfree(ppgtt); return ret; } @@ -2849,33 +2773,20 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev, } /** - * i915_gem_init_ggtt - Initialize GEM for Global GTT - * @dev: DRM device - */ -void i915_gem_init_ggtt(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = to_i915(dev); - struct i915_ggtt *ggtt = &dev_priv->ggtt; - - i915_gem_setup_global_gtt(dev, 0, ggtt->mappable_end, ggtt->base.total); -} - -/** * i915_ggtt_cleanup_hw - Clean up GGTT hardware initialization - * @dev: DRM device + * @dev_priv: i915 device */ -void i915_ggtt_cleanup_hw(struct drm_device *dev) +void i915_ggtt_cleanup_hw(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct i915_ggtt *ggtt = &dev_priv->ggtt; if (dev_priv->mm.aliasing_ppgtt) { struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; - ppgtt->base.cleanup(&ppgtt->base); + kfree(ppgtt); } - i915_gem_cleanup_stolen(dev); + i915_gem_cleanup_stolen(&dev_priv->drm); if (drm_mm_initialized(&ggtt->base.mm)) { intel_vgt_deballoon(dev_priv); @@ -2885,6 +2796,9 @@ void i915_ggtt_cleanup_hw(struct drm_device *dev) } ggtt->base.cleanup(&ggtt->base); + + arch_phys_wc_del(ggtt->mtrr); + io_mapping_fini(&ggtt->mappable); } static unsigned int gen6_get_total_gtt_size(u16 snb_gmch_ctl) @@ -2965,17 +2879,14 @@ static size_t gen9_get_stolen_size(u16 gen9_gmch_ctl) return (gen9_gmch_ctl - 0xf0 + 1) << 22; } -static int ggtt_probe_common(struct drm_device *dev, - size_t gtt_size) +static int ggtt_probe_common(struct i915_ggtt *ggtt, u64 size) { - struct drm_i915_private *dev_priv = to_i915(dev); - struct i915_ggtt *ggtt = &dev_priv->ggtt; - struct i915_page_scratch *scratch_page; - phys_addr_t ggtt_phys_addr; + struct pci_dev *pdev = ggtt->base.dev->pdev; + phys_addr_t phys_addr; + int ret; /* For Modern GENs the PTEs and register space are split in the BAR */ - ggtt_phys_addr = pci_resource_start(dev->pdev, 0) + - (pci_resource_len(dev->pdev, 0) / 2); + phys_addr = pci_resource_start(pdev, 0) + pci_resource_len(pdev, 0) / 2; /* * On BXT writes larger than 64 bit to the GTT pagetable range will be @@ -2984,25 +2895,25 @@ static int ggtt_probe_common(struct drm_device *dev, * resort to an uncached mapping. The WC issue is easily caught by the * readback check when writing GTT PTE entries. */ - if (IS_BROXTON(dev)) - ggtt->gsm = ioremap_nocache(ggtt_phys_addr, gtt_size); + if (IS_BROXTON(ggtt->base.dev)) + ggtt->gsm = ioremap_nocache(phys_addr, size); else - ggtt->gsm = ioremap_wc(ggtt_phys_addr, gtt_size); + ggtt->gsm = ioremap_wc(phys_addr, size); if (!ggtt->gsm) { - DRM_ERROR("Failed to map the gtt page table\n"); + DRM_ERROR("Failed to map the ggtt page table\n"); return -ENOMEM; } - scratch_page = alloc_scratch_page(dev); - if (IS_ERR(scratch_page)) { + ret = setup_scratch_page(ggtt->base.dev, + &ggtt->base.scratch_page, + GFP_DMA32); + if (ret) { DRM_ERROR("Scratch setup failed\n"); /* iounmap will also get called at remove, but meh */ iounmap(ggtt->gsm); - return PTR_ERR(scratch_page); + return ret; } - ggtt->base.scratch_page = scratch_page; - return 0; } @@ -3079,42 +2990,49 @@ static void chv_setup_private_ppat(struct drm_i915_private *dev_priv) I915_WRITE(GEN8_PRIVATE_PAT_HI, pat >> 32); } +static void gen6_gmch_remove(struct i915_address_space *vm) +{ + struct i915_ggtt *ggtt = i915_vm_to_ggtt(vm); + + iounmap(ggtt->gsm); + cleanup_scratch_page(vm->dev, &vm->scratch_page); +} + static int gen8_gmch_probe(struct i915_ggtt *ggtt) { - struct drm_device *dev = ggtt->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = to_i915(ggtt->base.dev); + struct pci_dev *pdev = dev_priv->drm.pdev; + unsigned int size; u16 snb_gmch_ctl; - int ret; /* TODO: We're not aware of mappable constraints on gen8 yet */ - ggtt->mappable_base = pci_resource_start(dev->pdev, 2); - ggtt->mappable_end = pci_resource_len(dev->pdev, 2); + ggtt->mappable_base = pci_resource_start(pdev, 2); + ggtt->mappable_end = pci_resource_len(pdev, 2); - if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(39))) - pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(39)); + if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(39))) + pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(39)); - pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl); + pci_read_config_word(pdev, SNB_GMCH_CTRL, &snb_gmch_ctl); - if (INTEL_INFO(dev)->gen >= 9) { + if (INTEL_GEN(dev_priv) >= 9) { ggtt->stolen_size = gen9_get_stolen_size(snb_gmch_ctl); - ggtt->size = gen8_get_total_gtt_size(snb_gmch_ctl); - } else if (IS_CHERRYVIEW(dev)) { + size = gen8_get_total_gtt_size(snb_gmch_ctl); + } else if (IS_CHERRYVIEW(dev_priv)) { ggtt->stolen_size = chv_get_stolen_size(snb_gmch_ctl); - ggtt->size = chv_get_total_gtt_size(snb_gmch_ctl); + size = chv_get_total_gtt_size(snb_gmch_ctl); } else { ggtt->stolen_size = gen8_get_stolen_size(snb_gmch_ctl); - ggtt->size = gen8_get_total_gtt_size(snb_gmch_ctl); + size = gen8_get_total_gtt_size(snb_gmch_ctl); } - ggtt->base.total = (ggtt->size / sizeof(gen8_pte_t)) << PAGE_SHIFT; + ggtt->base.total = (size / sizeof(gen8_pte_t)) << PAGE_SHIFT; - if (IS_CHERRYVIEW(dev) || IS_BROXTON(dev)) + if (IS_CHERRYVIEW(dev_priv) || IS_BROXTON(dev_priv)) chv_setup_private_ppat(dev_priv); else bdw_setup_private_ppat(dev_priv); - ret = ggtt_probe_common(dev, ggtt->size); - + ggtt->base.cleanup = gen6_gmch_remove; ggtt->base.bind_vma = ggtt_bind_vma; ggtt->base.unbind_vma = ggtt_unbind_vma; ggtt->base.insert_page = gen8_ggtt_insert_page; @@ -3126,57 +3044,65 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt) if (IS_CHERRYVIEW(dev_priv)) ggtt->base.insert_entries = gen8_ggtt_insert_entries__BKL; - return ret; + return ggtt_probe_common(ggtt, size); } static int gen6_gmch_probe(struct i915_ggtt *ggtt) { - struct drm_device *dev = ggtt->base.dev; + struct drm_i915_private *dev_priv = to_i915(ggtt->base.dev); + struct pci_dev *pdev = dev_priv->drm.pdev; + unsigned int size; u16 snb_gmch_ctl; - int ret; - ggtt->mappable_base = pci_resource_start(dev->pdev, 2); - ggtt->mappable_end = pci_resource_len(dev->pdev, 2); + ggtt->mappable_base = pci_resource_start(pdev, 2); + ggtt->mappable_end = pci_resource_len(pdev, 2); /* 64/512MB is the current min/max we actually know of, but this is just * a coarse sanity check. */ - if ((ggtt->mappable_end < (64<<20) || (ggtt->mappable_end > (512<<20)))) { + if (ggtt->mappable_end < (64<<20) || ggtt->mappable_end > (512<<20)) { DRM_ERROR("Unknown GMADR size (%llx)\n", ggtt->mappable_end); return -ENXIO; } - if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(40))) - pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(40)); - pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl); + if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(40))) + pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(40)); + pci_read_config_word(pdev, SNB_GMCH_CTRL, &snb_gmch_ctl); ggtt->stolen_size = gen6_get_stolen_size(snb_gmch_ctl); - ggtt->size = gen6_get_total_gtt_size(snb_gmch_ctl); - ggtt->base.total = (ggtt->size / sizeof(gen6_pte_t)) << PAGE_SHIFT; - ret = ggtt_probe_common(dev, ggtt->size); + size = gen6_get_total_gtt_size(snb_gmch_ctl); + ggtt->base.total = (size / sizeof(gen6_pte_t)) << PAGE_SHIFT; ggtt->base.clear_range = gen6_ggtt_clear_range; ggtt->base.insert_page = gen6_ggtt_insert_page; ggtt->base.insert_entries = gen6_ggtt_insert_entries; ggtt->base.bind_vma = ggtt_bind_vma; ggtt->base.unbind_vma = ggtt_unbind_vma; + ggtt->base.cleanup = gen6_gmch_remove; + + if (HAS_EDRAM(dev_priv)) + ggtt->base.pte_encode = iris_pte_encode; + else if (IS_HASWELL(dev_priv)) + ggtt->base.pte_encode = hsw_pte_encode; + else if (IS_VALLEYVIEW(dev_priv)) + ggtt->base.pte_encode = byt_pte_encode; + else if (INTEL_GEN(dev_priv) >= 7) + ggtt->base.pte_encode = ivb_pte_encode; + else + ggtt->base.pte_encode = snb_pte_encode; - return ret; + return ggtt_probe_common(ggtt, size); } -static void gen6_gmch_remove(struct i915_address_space *vm) +static void i915_gmch_remove(struct i915_address_space *vm) { - struct i915_ggtt *ggtt = container_of(vm, struct i915_ggtt, base); - - iounmap(ggtt->gsm); - free_scratch_page(vm->dev, vm->scratch_page); + intel_gmch_remove(); } static int i915_gmch_probe(struct i915_ggtt *ggtt) { - struct drm_device *dev = ggtt->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = to_i915(ggtt->base.dev); int ret; ret = intel_gmch_probe(dev_priv->bridge_dev, dev_priv->drm.pdev, NULL); @@ -3188,12 +3114,13 @@ static int i915_gmch_probe(struct i915_ggtt *ggtt) intel_gtt_get(&ggtt->base.total, &ggtt->stolen_size, &ggtt->mappable_base, &ggtt->mappable_end); - ggtt->do_idle_maps = needs_idle_maps(&dev_priv->drm); + ggtt->do_idle_maps = needs_idle_maps(dev_priv); ggtt->base.insert_page = i915_ggtt_insert_page; ggtt->base.insert_entries = i915_ggtt_insert_entries; ggtt->base.clear_range = i915_ggtt_clear_range; ggtt->base.bind_vma = ggtt_bind_vma; ggtt->base.unbind_vma = ggtt_unbind_vma; + ggtt->base.cleanup = i915_gmch_remove; if (unlikely(ggtt->do_idle_maps)) DRM_INFO("applying Ironlake quirks for intel_iommu\n"); @@ -3201,65 +3128,40 @@ static int i915_gmch_probe(struct i915_ggtt *ggtt) return 0; } -static void i915_gmch_remove(struct i915_address_space *vm) -{ - intel_gmch_remove(); -} - /** - * i915_ggtt_init_hw - Initialize GGTT hardware - * @dev: DRM device + * i915_ggtt_probe_hw - Probe GGTT hardware location + * @dev_priv: i915 device */ -int i915_ggtt_init_hw(struct drm_device *dev) +int i915_ggtt_probe_hw(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct i915_ggtt *ggtt = &dev_priv->ggtt; int ret; - if (INTEL_INFO(dev)->gen <= 5) { - ggtt->probe = i915_gmch_probe; - ggtt->base.cleanup = i915_gmch_remove; - } else if (INTEL_INFO(dev)->gen < 8) { - ggtt->probe = gen6_gmch_probe; - ggtt->base.cleanup = gen6_gmch_remove; - - if (HAS_EDRAM(dev)) - ggtt->base.pte_encode = iris_pte_encode; - else if (IS_HASWELL(dev)) - ggtt->base.pte_encode = hsw_pte_encode; - else if (IS_VALLEYVIEW(dev)) - ggtt->base.pte_encode = byt_pte_encode; - else if (INTEL_INFO(dev)->gen >= 7) - ggtt->base.pte_encode = ivb_pte_encode; - else - ggtt->base.pte_encode = snb_pte_encode; - } else { - ggtt->probe = gen8_gmch_probe; - ggtt->base.cleanup = gen6_gmch_remove; - } - - ggtt->base.dev = dev; - ggtt->base.is_ggtt = true; + ggtt->base.dev = &dev_priv->drm; - ret = ggtt->probe(ggtt); + if (INTEL_GEN(dev_priv) <= 5) + ret = i915_gmch_probe(ggtt); + else if (INTEL_GEN(dev_priv) < 8) + ret = gen6_gmch_probe(ggtt); + else + ret = gen8_gmch_probe(ggtt); if (ret) return ret; if ((ggtt->base.total - 1) >> 32) { DRM_ERROR("We never expected a Global GTT with more than 32bits" - "of address space! Found %lldM!\n", + " of address space! Found %lldM!\n", ggtt->base.total >> 20); ggtt->base.total = 1ULL << 32; ggtt->mappable_end = min(ggtt->mappable_end, ggtt->base.total); } - /* - * Initialise stolen early so that we may reserve preallocated - * objects for the BIOS to KMS transition. - */ - ret = i915_gem_init_stolen(dev); - if (ret) - goto out_gtt_cleanup; + if (ggtt->mappable_end > ggtt->base.total) { + DRM_ERROR("mappable aperture extends past end of GGTT," + " aperture=%llx, total=%llx\n", + ggtt->mappable_end, ggtt->base.total); + ggtt->mappable_end = ggtt->base.total; + } /* GMADR is the PCI mmio aperture into the global GTT. */ DRM_INFO("Memory usable by graphics device = %lluM\n", @@ -3272,16 +3174,55 @@ int i915_ggtt_init_hw(struct drm_device *dev) #endif return 0; +} + +/** + * i915_ggtt_init_hw - Initialize GGTT hardware + * @dev_priv: i915 device + */ +int i915_ggtt_init_hw(struct drm_i915_private *dev_priv) +{ + struct i915_ggtt *ggtt = &dev_priv->ggtt; + int ret; + + INIT_LIST_HEAD(&dev_priv->vm_list); + + /* Subtract the guard page before address space initialization to + * shrink the range used by drm_mm. + */ + ggtt->base.total -= PAGE_SIZE; + i915_address_space_init(&ggtt->base, dev_priv); + ggtt->base.total += PAGE_SIZE; + if (!HAS_LLC(dev_priv)) + ggtt->base.mm.color_adjust = i915_gtt_color_adjust; + + if (!io_mapping_init_wc(&dev_priv->ggtt.mappable, + dev_priv->ggtt.mappable_base, + dev_priv->ggtt.mappable_end)) { + ret = -EIO; + goto out_gtt_cleanup; + } + + ggtt->mtrr = arch_phys_wc_add(ggtt->mappable_base, ggtt->mappable_end); + + /* + * Initialise stolen early so that we may reserve preallocated + * objects for the BIOS to KMS transition. + */ + ret = i915_gem_init_stolen(&dev_priv->drm); + if (ret) + goto out_gtt_cleanup; + + return 0; out_gtt_cleanup: ggtt->base.cleanup(&ggtt->base); - return ret; } -int i915_ggtt_enable_hw(struct drm_device *dev) +int i915_ggtt_enable_hw(struct drm_i915_private *dev_priv) { - if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt()) + if (INTEL_GEN(dev_priv) < 6 && !intel_enable_gtt()) return -EIO; return 0; @@ -3291,8 +3232,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); struct i915_ggtt *ggtt = &dev_priv->ggtt; - struct drm_i915_gem_object *obj; - struct i915_vma *vma; + struct drm_i915_gem_object *obj, *on; i915_check_and_clear_faults(dev_priv); @@ -3300,20 +3240,32 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) ggtt->base.clear_range(&ggtt->base, ggtt->base.start, ggtt->base.total, true); - /* Cache flush objects bound into GGTT and rebind them. */ - list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) { + ggtt->base.closed = true; /* skip rewriting PTE on VMA unbind */ + + /* clflush objects bound into the GGTT and rebind them. */ + list_for_each_entry_safe(obj, on, + &dev_priv->mm.bound_list, global_list) { + bool ggtt_bound = false; + struct i915_vma *vma; + list_for_each_entry(vma, &obj->vma_list, obj_link) { if (vma->vm != &ggtt->base) continue; + if (!i915_vma_unbind(vma)) + continue; + WARN_ON(i915_vma_bind(vma, obj->cache_level, PIN_UPDATE)); + ggtt_bound = true; } - if (obj->pin_display) + if (ggtt_bound) WARN_ON(i915_gem_object_set_to_gtt_domain(obj, false)); } + ggtt->base.closed = false; + if (INTEL_INFO(dev)->gen >= 8) { if (IS_CHERRYVIEW(dev) || IS_BROXTON(dev)) chv_setup_private_ppat(dev_priv); @@ -3331,7 +3283,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) struct i915_hw_ppgtt *ppgtt; - if (vm->is_ggtt) + if (i915_is_ggtt(vm)) ppgtt = dev_priv->mm.aliasing_ppgtt; else ppgtt = i915_vm_to_ppgtt(vm); @@ -3344,65 +3296,155 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) i915_ggtt_flush(dev_priv); } +static void +i915_vma_retire(struct i915_gem_active *active, + struct drm_i915_gem_request *rq) +{ + const unsigned int idx = rq->engine->id; + struct i915_vma *vma = + container_of(active, struct i915_vma, last_read[idx]); + + GEM_BUG_ON(!i915_vma_has_active_engine(vma, idx)); + + i915_vma_clear_active(vma, idx); + if (i915_vma_is_active(vma)) + return; + + list_move_tail(&vma->vm_link, &vma->vm->inactive_list); + if (unlikely(i915_vma_is_closed(vma) && !i915_vma_is_pinned(vma))) + WARN_ON(i915_vma_unbind(vma)); +} + +void i915_vma_destroy(struct i915_vma *vma) +{ + GEM_BUG_ON(vma->node.allocated); + GEM_BUG_ON(i915_vma_is_active(vma)); + GEM_BUG_ON(!i915_vma_is_closed(vma)); + GEM_BUG_ON(vma->fence); + + list_del(&vma->vm_link); + if (!i915_vma_is_ggtt(vma)) + i915_ppgtt_put(i915_vm_to_ppgtt(vma->vm)); + + kmem_cache_free(to_i915(vma->obj->base.dev)->vmas, vma); +} + +void i915_vma_close(struct i915_vma *vma) +{ + GEM_BUG_ON(i915_vma_is_closed(vma)); + vma->flags |= I915_VMA_CLOSED; + + list_del_init(&vma->obj_link); + if (!i915_vma_is_active(vma) && !i915_vma_is_pinned(vma)) + WARN_ON(i915_vma_unbind(vma)); +} + static struct i915_vma * -__i915_gem_vma_create(struct drm_i915_gem_object *obj, - struct i915_address_space *vm, - const struct i915_ggtt_view *ggtt_view) +__i915_vma_create(struct drm_i915_gem_object *obj, + struct i915_address_space *vm, + const struct i915_ggtt_view *view) { struct i915_vma *vma; + int i; - if (WARN_ON(i915_is_ggtt(vm) != !!ggtt_view)) - return ERR_PTR(-EINVAL); + GEM_BUG_ON(vm->closed); vma = kmem_cache_zalloc(to_i915(obj->base.dev)->vmas, GFP_KERNEL); if (vma == NULL) return ERR_PTR(-ENOMEM); - INIT_LIST_HEAD(&vma->vm_link); - INIT_LIST_HEAD(&vma->obj_link); INIT_LIST_HEAD(&vma->exec_list); + for (i = 0; i < ARRAY_SIZE(vma->last_read); i++) + init_request_active(&vma->last_read[i], i915_vma_retire); + init_request_active(&vma->last_fence, NULL); + list_add(&vma->vm_link, &vm->unbound_list); vma->vm = vm; vma->obj = obj; - vma->is_ggtt = i915_is_ggtt(vm); + vma->size = obj->base.size; + + if (view) { + vma->ggtt_view = *view; + if (view->type == I915_GGTT_VIEW_PARTIAL) { + vma->size = view->params.partial.size; + vma->size <<= PAGE_SHIFT; + } else if (view->type == I915_GGTT_VIEW_ROTATED) { + vma->size = + intel_rotation_info_size(&view->params.rotated); + vma->size <<= PAGE_SHIFT; + } + } - if (i915_is_ggtt(vm)) - vma->ggtt_view = *ggtt_view; - else + if (i915_is_ggtt(vm)) { + vma->flags |= I915_VMA_GGTT; + } else { i915_ppgtt_get(i915_vm_to_ppgtt(vm)); + } list_add_tail(&vma->obj_link, &obj->vma_list); - return vma; } +static inline bool vma_matches(struct i915_vma *vma, + struct i915_address_space *vm, + const struct i915_ggtt_view *view) +{ + if (vma->vm != vm) + return false; + + if (!i915_vma_is_ggtt(vma)) + return true; + + if (!view) + return vma->ggtt_view.type == 0; + + if (vma->ggtt_view.type != view->type) + return false; + + return memcmp(&vma->ggtt_view.params, + &view->params, + sizeof(view->params)) == 0; +} + struct i915_vma * -i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj, - struct i915_address_space *vm) +i915_vma_create(struct drm_i915_gem_object *obj, + struct i915_address_space *vm, + const struct i915_ggtt_view *view) +{ + GEM_BUG_ON(view && !i915_is_ggtt(vm)); + GEM_BUG_ON(i915_gem_obj_to_vma(obj, vm, view)); + + return __i915_vma_create(obj, vm, view); +} + +struct i915_vma * +i915_gem_obj_to_vma(struct drm_i915_gem_object *obj, + struct i915_address_space *vm, + const struct i915_ggtt_view *view) { struct i915_vma *vma; - vma = i915_gem_obj_to_vma(obj, vm); - if (!vma) - vma = __i915_gem_vma_create(obj, vm, - i915_is_ggtt(vm) ? &i915_ggtt_view_normal : NULL); + list_for_each_entry_reverse(vma, &obj->vma_list, obj_link) + if (vma_matches(vma, vm, view)) + return vma; - return vma; + return NULL; } struct i915_vma * -i915_gem_obj_lookup_or_create_ggtt_vma(struct drm_i915_gem_object *obj, - const struct i915_ggtt_view *view) +i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj, + struct i915_address_space *vm, + const struct i915_ggtt_view *view) { - struct drm_device *dev = obj->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct i915_ggtt *ggtt = &dev_priv->ggtt; - struct i915_vma *vma = i915_gem_obj_to_ggtt_view(obj, view); + struct i915_vma *vma; + GEM_BUG_ON(view && !i915_is_ggtt(vm)); + + vma = i915_gem_obj_to_vma(obj, vm, view); if (!vma) - vma = __i915_gem_vma_create(obj, &ggtt->base, view); + vma = __i915_vma_create(obj, vm, view); + GEM_BUG_ON(i915_vma_is_closed(vma)); return vma; - } static struct scatterlist * @@ -3434,18 +3476,16 @@ rotate_pages(const dma_addr_t *in, unsigned int offset, } static struct sg_table * -intel_rotate_fb_obj_pages(struct intel_rotation_info *rot_info, +intel_rotate_fb_obj_pages(const struct intel_rotation_info *rot_info, struct drm_i915_gem_object *obj) { const size_t n_pages = obj->base.size / PAGE_SIZE; - unsigned int size_pages = rot_info->plane[0].width * rot_info->plane[0].height; - unsigned int size_pages_uv; + unsigned int size = intel_rotation_info_size(rot_info); struct sgt_iter sgt_iter; dma_addr_t dma_addr; unsigned long i; dma_addr_t *page_addr_list; struct sg_table *st; - unsigned int uv_start_page; struct scatterlist *sg; int ret = -ENOMEM; @@ -3456,18 +3496,12 @@ intel_rotate_fb_obj_pages(struct intel_rotation_info *rot_info, if (!page_addr_list) return ERR_PTR(ret); - /* Account for UV plane with NV12. */ - if (rot_info->pixel_format == DRM_FORMAT_NV12) - size_pages_uv = rot_info->plane[1].width * rot_info->plane[1].height; - else - size_pages_uv = 0; - /* Allocate target SG list. */ st = kmalloc(sizeof(*st), GFP_KERNEL); if (!st) goto err_st_alloc; - ret = sg_alloc_table(st, size_pages + size_pages_uv, GFP_KERNEL); + ret = sg_alloc_table(st, size, GFP_KERNEL); if (ret) goto err_sg_alloc; @@ -3480,32 +3514,14 @@ intel_rotate_fb_obj_pages(struct intel_rotation_info *rot_info, st->nents = 0; sg = st->sgl; - /* Rotate the pages. */ - sg = rotate_pages(page_addr_list, 0, - rot_info->plane[0].width, rot_info->plane[0].height, - rot_info->plane[0].width, - st, sg); - - /* Append the UV plane if NV12. */ - if (rot_info->pixel_format == DRM_FORMAT_NV12) { - uv_start_page = size_pages; - - /* Check for tile-row un-alignment. */ - if (offset_in_page(rot_info->uv_offset)) - uv_start_page--; - - rot_info->uv_start_page = uv_start_page; - - sg = rotate_pages(page_addr_list, rot_info->uv_start_page, - rot_info->plane[1].width, rot_info->plane[1].height, - rot_info->plane[1].width, - st, sg); + for (i = 0 ; i < ARRAY_SIZE(rot_info->plane); i++) { + sg = rotate_pages(page_addr_list, rot_info->plane[i].offset, + rot_info->plane[i].width, rot_info->plane[i].height, + rot_info->plane[i].stride, st, sg); } - DRM_DEBUG_KMS("Created rotated page mapping for object size %zu (%ux%u tiles, %u pages (%u plane 0)).\n", - obj->base.size, rot_info->plane[0].width, - rot_info->plane[0].height, size_pages + size_pages_uv, - size_pages); + DRM_DEBUG_KMS("Created rotated page mapping for object size %zu (%ux%u tiles, %u pages)\n", + obj->base.size, rot_info->plane[0].width, rot_info->plane[0].height, size); drm_free_large(page_addr_list); @@ -3516,10 +3532,9 @@ err_sg_alloc: err_st_alloc: drm_free_large(page_addr_list); - DRM_DEBUG_KMS("Failed to create rotated mapping for object size %zu! (%d) (%ux%u tiles, %u pages (%u plane 0))\n", - obj->base.size, ret, rot_info->plane[0].width, - rot_info->plane[0].height, size_pages + size_pages_uv, - size_pages); + DRM_DEBUG_KMS("Failed to create rotated mapping for object size %zu! (%ux%u tiles, %u pages)\n", + obj->base.size, rot_info->plane[0].width, rot_info->plane[0].height, size); + return ERR_PTR(ret); } @@ -3569,28 +3584,27 @@ i915_get_ggtt_vma_pages(struct i915_vma *vma) { int ret = 0; - if (vma->ggtt_view.pages) + if (vma->pages) return 0; if (vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL) - vma->ggtt_view.pages = vma->obj->pages; + vma->pages = vma->obj->pages; else if (vma->ggtt_view.type == I915_GGTT_VIEW_ROTATED) - vma->ggtt_view.pages = + vma->pages = intel_rotate_fb_obj_pages(&vma->ggtt_view.params.rotated, vma->obj); else if (vma->ggtt_view.type == I915_GGTT_VIEW_PARTIAL) - vma->ggtt_view.pages = - intel_partial_pages(&vma->ggtt_view, vma->obj); + vma->pages = intel_partial_pages(&vma->ggtt_view, vma->obj); else WARN_ONCE(1, "GGTT view %u not implemented!\n", vma->ggtt_view.type); - if (!vma->ggtt_view.pages) { + if (!vma->pages) { DRM_ERROR("Failed to get pages for GGTT view type %u!\n", vma->ggtt_view.type); ret = -EINVAL; - } else if (IS_ERR(vma->ggtt_view.pages)) { - ret = PTR_ERR(vma->ggtt_view.pages); - vma->ggtt_view.pages = NULL; + } else if (IS_ERR(vma->pages)) { + ret = PTR_ERR(vma->pages); + vma->pages = NULL; DRM_ERROR("Failed to get pages for VMA view type %u (%d)!\n", vma->ggtt_view.type, ret); } @@ -3611,34 +3625,32 @@ i915_get_ggtt_vma_pages(struct i915_vma *vma) int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level, u32 flags) { - int ret; u32 bind_flags; + u32 vma_flags; + int ret; if (WARN_ON(flags == 0)) return -EINVAL; bind_flags = 0; if (flags & PIN_GLOBAL) - bind_flags |= GLOBAL_BIND; + bind_flags |= I915_VMA_GLOBAL_BIND; if (flags & PIN_USER) - bind_flags |= LOCAL_BIND; + bind_flags |= I915_VMA_LOCAL_BIND; + vma_flags = vma->flags & (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND); if (flags & PIN_UPDATE) - bind_flags |= vma->bound; + bind_flags |= vma_flags; else - bind_flags &= ~vma->bound; - + bind_flags &= ~vma_flags; if (bind_flags == 0) return 0; - if (vma->bound == 0 && vma->vm->allocate_va_range) { - /* XXX: i915_vma_pin() will fix this +- hack */ - vma->pin_count++; + if (vma_flags == 0 && vma->vm->allocate_va_range) { trace_i915_va_alloc(vma); ret = vma->vm->allocate_va_range(vma->vm, vma->node.start, vma->node.size); - vma->pin_count--; if (ret) return ret; } @@ -3647,56 +3659,47 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level, if (ret) return ret; - vma->bound |= bind_flags; - + vma->flags |= bind_flags; return 0; } -/** - * i915_ggtt_view_size - Get the size of a GGTT view. - * @obj: Object the view is of. - * @view: The view in question. - * - * @return The size of the GGTT view in bytes. - */ -size_t -i915_ggtt_view_size(struct drm_i915_gem_object *obj, - const struct i915_ggtt_view *view) -{ - if (view->type == I915_GGTT_VIEW_NORMAL) { - return obj->base.size; - } else if (view->type == I915_GGTT_VIEW_ROTATED) { - return intel_rotation_info_size(&view->params.rotated) << PAGE_SHIFT; - } else if (view->type == I915_GGTT_VIEW_PARTIAL) { - return view->params.partial.size << PAGE_SHIFT; - } else { - WARN_ONCE(1, "GGTT view %u not implemented!\n", view->type); - return obj->base.size; - } -} - void __iomem *i915_vma_pin_iomap(struct i915_vma *vma) { void __iomem *ptr; + /* Access through the GTT requires the device to be awake. */ + assert_rpm_wakelock_held(to_i915(vma->vm->dev)); + lockdep_assert_held(&vma->vm->dev->struct_mutex); - if (WARN_ON(!vma->obj->map_and_fenceable)) - return ERR_PTR(-ENODEV); + if (WARN_ON(!i915_vma_is_map_and_fenceable(vma))) + return IO_ERR_PTR(-ENODEV); - GEM_BUG_ON(!vma->is_ggtt); - GEM_BUG_ON((vma->bound & GLOBAL_BIND) == 0); + GEM_BUG_ON(!i915_vma_is_ggtt(vma)); + GEM_BUG_ON((vma->flags & I915_VMA_GLOBAL_BIND) == 0); ptr = vma->iomap; if (ptr == NULL) { - ptr = io_mapping_map_wc(i915_vm_to_ggtt(vma->vm)->mappable, + ptr = io_mapping_map_wc(&i915_vm_to_ggtt(vma->vm)->mappable, vma->node.start, vma->node.size); if (ptr == NULL) - return ERR_PTR(-ENOMEM); + return IO_ERR_PTR(-ENOMEM); vma->iomap = ptr; } - vma->pin_count++; + __i915_vma_pin(vma); return ptr; } + +void i915_vma_unpin_and_release(struct i915_vma **p_vma) +{ + struct i915_vma *vma; + + vma = fetch_and_zero(p_vma); + if (!vma) + return; + + i915_vma_unpin(vma); + i915_vma_put(vma); +} diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index aa5f31d..ec78be2 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -36,7 +36,15 @@ #include <linux/io-mapping.h> +#include "i915_gem_request.h" + +#define I915_FENCE_REG_NONE -1 +#define I915_MAX_NUM_FENCES 32 +/* 32 fences + sign bit for FENCE_REG_NONE */ +#define I915_MAX_NUM_FENCE_BITS 6 + struct drm_i915_file_private; +struct drm_i915_fence_reg; typedef uint32_t gen6_pte_t; typedef uint64_t gen8_pte_t; @@ -137,12 +145,9 @@ enum i915_ggtt_view_type { }; struct intel_rotation_info { - unsigned int uv_offset; - uint32_t pixel_format; - unsigned int uv_start_page; struct { /* tiles */ - unsigned int width, height; + unsigned int width, height, stride, offset; } plane[2]; }; @@ -156,8 +161,6 @@ struct i915_ggtt_view { } partial; struct intel_rotation_info rotated; } params; - - struct sg_table *pages; }; extern const struct i915_ggtt_view i915_ggtt_view_normal; @@ -177,13 +180,38 @@ struct i915_vma { struct drm_mm_node node; struct drm_i915_gem_object *obj; struct i915_address_space *vm; + struct drm_i915_fence_reg *fence; + struct sg_table *pages; void __iomem *iomap; + u64 size; + u64 display_alignment; + + unsigned int flags; + /** + * How many users have pinned this object in GTT space. The following + * users can each hold at most one reference: pwrite/pread, execbuffer + * (objects are not allowed multiple times for the same batchbuffer), + * and the framebuffer code. When switching/pageflipping, the + * framebuffer code has at most two buffers pinned per crtc. + * + * In the worst case this is 1 + 1 + 1 + 2*2 = 7. That would fit into 3 + * bits with absolutely no headroom. So use 4 bits. + */ +#define I915_VMA_PIN_MASK 0xf +#define I915_VMA_PIN_OVERFLOW BIT(5) /** Flags and address space this VMA is bound to */ -#define GLOBAL_BIND (1<<0) -#define LOCAL_BIND (1<<1) - unsigned int bound : 4; - bool is_ggtt : 1; +#define I915_VMA_GLOBAL_BIND BIT(6) +#define I915_VMA_LOCAL_BIND BIT(7) +#define I915_VMA_BIND_MASK (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND | I915_VMA_PIN_OVERFLOW) + +#define I915_VMA_GGTT BIT(8) +#define I915_VMA_CAN_FENCE BIT(9) +#define I915_VMA_CLOSED BIT(10) + + unsigned int active; + struct i915_gem_active last_read[I915_NUM_ENGINES]; + struct i915_gem_active last_fence; /** * Support different GGTT views into the same object. @@ -208,20 +236,66 @@ struct i915_vma { struct hlist_node exec_node; unsigned long exec_handle; struct drm_i915_gem_exec_object2 *exec_entry; - - /** - * How many users have pinned this object in GTT space. The following - * users can each hold at most one reference: pwrite/pread, execbuffer - * (objects are not allowed multiple times for the same batchbuffer), - * and the framebuffer code. When switching/pageflipping, the - * framebuffer code has at most two buffers pinned per crtc. - * - * In the worst case this is 1 + 1 + 1 + 2*2 = 7. That would fit into 3 - * bits with absolutely no headroom. So use 4 bits. */ - unsigned int pin_count:4; -#define DRM_I915_GEM_OBJECT_MAX_PIN_COUNT 0xf }; +struct i915_vma * +i915_vma_create(struct drm_i915_gem_object *obj, + struct i915_address_space *vm, + const struct i915_ggtt_view *view); +void i915_vma_unpin_and_release(struct i915_vma **p_vma); + +static inline bool i915_vma_is_ggtt(const struct i915_vma *vma) +{ + return vma->flags & I915_VMA_GGTT; +} + +static inline bool i915_vma_is_map_and_fenceable(const struct i915_vma *vma) +{ + return vma->flags & I915_VMA_CAN_FENCE; +} + +static inline bool i915_vma_is_closed(const struct i915_vma *vma) +{ + return vma->flags & I915_VMA_CLOSED; +} + +static inline unsigned int i915_vma_get_active(const struct i915_vma *vma) +{ + return vma->active; +} + +static inline bool i915_vma_is_active(const struct i915_vma *vma) +{ + return i915_vma_get_active(vma); +} + +static inline void i915_vma_set_active(struct i915_vma *vma, + unsigned int engine) +{ + vma->active |= BIT(engine); +} + +static inline void i915_vma_clear_active(struct i915_vma *vma, + unsigned int engine) +{ + vma->active &= ~BIT(engine); +} + +static inline bool i915_vma_has_active_engine(const struct i915_vma *vma, + unsigned int engine) +{ + return vma->active & BIT(engine); +} + +static inline u32 i915_ggtt_offset(const struct i915_vma *vma) +{ + GEM_BUG_ON(!i915_vma_is_ggtt(vma)); + GEM_BUG_ON(!vma->node.allocated); + GEM_BUG_ON(upper_32_bits(vma->node.start)); + GEM_BUG_ON(upper_32_bits(vma->node.start + vma->node.size - 1)); + return lower_32_bits(vma->node.start); +} + struct i915_page_dma { struct page *page; union { @@ -238,10 +312,6 @@ struct i915_page_dma { #define px_page(px) (px_base(px)->page) #define px_dma(px) (px_base(px)->daddr) -struct i915_page_scratch { - struct i915_page_dma base; -}; - struct i915_page_table { struct i915_page_dma base; @@ -272,13 +342,22 @@ struct i915_pml4 { struct i915_address_space { struct drm_mm mm; struct drm_device *dev; + /* Every address space belongs to a struct file - except for the global + * GTT that is owned by the driver (and so @file is set to NULL). In + * principle, no information should leak from one context to another + * (or between files/processes etc) unless explicitly shared by the + * owner. Tracking the owner is important in order to free up per-file + * objects along with the file, to aide resource tracking, and to + * assign blame. + */ + struct drm_i915_file_private *file; struct list_head global_link; u64 start; /* Start offset always 0 for dri2 */ u64 total; /* size addr space maps (ex. 2GB for ggtt) */ - bool is_ggtt; + bool closed; - struct i915_page_scratch *scratch_page; + struct i915_page_dma scratch_page; struct i915_page_table *scratch_pt; struct i915_page_directory *scratch_pd; struct i915_page_directory_pointer *scratch_pdp; /* GEN8+ & 48b PPGTT */ @@ -306,6 +385,13 @@ struct i915_address_space { */ struct list_head inactive_list; + /** + * List of vma that have been unbound. + * + * A reference is not held on the buffer while on this list. + */ + struct list_head unbound_list; + /* FIXME: Need a more generic return type */ gen6_pte_t (*pte_encode)(dma_addr_t addr, enum i915_cache_level level, @@ -338,7 +424,7 @@ struct i915_address_space { u32 flags); }; -#define i915_is_ggtt(V) ((V)->is_ggtt) +#define i915_is_ggtt(V) (!(V)->file) /* The Graphics Translation Table is the way in which GEN hardware translates a * Graphics Virtual Address into a Physical Address. In addition to the normal @@ -349,14 +435,13 @@ struct i915_address_space { */ struct i915_ggtt { struct i915_address_space base; + struct io_mapping mappable; /* Mapping to our CPU mappable region */ size_t stolen_size; /* Total size of stolen memory */ size_t stolen_usable_size; /* Total size minus BIOS reserved */ size_t stolen_reserved_base; size_t stolen_reserved_size; - size_t size; /* Total size of Global GTT */ u64 mappable_end; /* End offset that we can CPU map */ - struct io_mapping *mappable; /* Mapping to our CPU mappable region */ phys_addr_t mappable_base; /* PA of our GMADR */ /** "Graphics Stolen Memory" holds the global PTEs */ @@ -365,8 +450,6 @@ struct i915_ggtt { bool do_idle_maps; int mtrr; - - int (*probe)(struct i915_ggtt *ggtt); }; struct i915_hw_ppgtt { @@ -380,8 +463,6 @@ struct i915_hw_ppgtt { struct i915_page_directory pd; /* GEN6-7 */ }; - struct drm_i915_file_private *file_priv; - gen6_pte_t __iomem *pd_addr; int (*enable)(struct i915_hw_ppgtt *ppgtt); @@ -521,14 +602,15 @@ i915_page_dir_dma_addr(const struct i915_hw_ppgtt *ppgtt, const unsigned n) px_dma(ppgtt->base.scratch_pd); } -int i915_ggtt_init_hw(struct drm_device *dev); -int i915_ggtt_enable_hw(struct drm_device *dev); -void i915_gem_init_ggtt(struct drm_device *dev); -void i915_ggtt_cleanup_hw(struct drm_device *dev); +int i915_ggtt_probe_hw(struct drm_i915_private *dev_priv); +int i915_ggtt_init_hw(struct drm_i915_private *dev_priv); +int i915_ggtt_enable_hw(struct drm_i915_private *dev_priv); +int i915_gem_init_ggtt(struct drm_i915_private *dev_priv); +void i915_ggtt_cleanup_hw(struct drm_i915_private *dev_priv); int i915_ppgtt_init_hw(struct drm_device *dev); void i915_ppgtt_release(struct kref *kref); -struct i915_hw_ppgtt *i915_ppgtt_create(struct drm_device *dev, +struct i915_hw_ppgtt *i915_ppgtt_create(struct drm_i915_private *dev_priv, struct drm_i915_file_private *fpriv); static inline void i915_ppgtt_get(struct i915_hw_ppgtt *ppgtt) { @@ -548,23 +630,67 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev); int __must_check i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj); void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj); -static inline bool -i915_ggtt_view_equal(const struct i915_ggtt_view *a, - const struct i915_ggtt_view *b) +/* Flags used by pin/bind&friends. */ +#define PIN_NONBLOCK BIT(0) +#define PIN_MAPPABLE BIT(1) +#define PIN_ZONE_4G BIT(2) +#define PIN_NONFAULT BIT(3) + +#define PIN_MBZ BIT(5) /* I915_VMA_PIN_OVERFLOW */ +#define PIN_GLOBAL BIT(6) /* I915_VMA_GLOBAL_BIND */ +#define PIN_USER BIT(7) /* I915_VMA_LOCAL_BIND */ +#define PIN_UPDATE BIT(8) + +#define PIN_HIGH BIT(9) +#define PIN_OFFSET_BIAS BIT(10) +#define PIN_OFFSET_FIXED BIT(11) +#define PIN_OFFSET_MASK (~4095) + +int __i915_vma_do_pin(struct i915_vma *vma, + u64 size, u64 alignment, u64 flags); +static inline int __must_check +i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags) { - if (WARN_ON(!a || !b)) - return false; - - if (a->type != b->type) - return false; - if (a->type != I915_GGTT_VIEW_NORMAL) - return !memcmp(&a->params, &b->params, sizeof(a->params)); - return true; + BUILD_BUG_ON(PIN_MBZ != I915_VMA_PIN_OVERFLOW); + BUILD_BUG_ON(PIN_GLOBAL != I915_VMA_GLOBAL_BIND); + BUILD_BUG_ON(PIN_USER != I915_VMA_LOCAL_BIND); + + /* Pin early to prevent the shrinker/eviction logic from destroying + * our vma as we insert and bind. + */ + if (likely(((++vma->flags ^ flags) & I915_VMA_BIND_MASK) == 0)) + return 0; + + return __i915_vma_do_pin(vma, size, alignment, flags); } -size_t -i915_ggtt_view_size(struct drm_i915_gem_object *obj, - const struct i915_ggtt_view *view); +static inline int i915_vma_pin_count(const struct i915_vma *vma) +{ + return vma->flags & I915_VMA_PIN_MASK; +} + +static inline bool i915_vma_is_pinned(const struct i915_vma *vma) +{ + return i915_vma_pin_count(vma); +} + +static inline void __i915_vma_pin(struct i915_vma *vma) +{ + vma->flags++; + GEM_BUG_ON(vma->flags & I915_VMA_PIN_OVERFLOW); +} + +static inline void __i915_vma_unpin(struct i915_vma *vma) +{ + GEM_BUG_ON(!i915_vma_is_pinned(vma)); + vma->flags--; +} + +static inline void i915_vma_unpin(struct i915_vma *vma) +{ + GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); + __i915_vma_unpin(vma); +} /** * i915_vma_pin_iomap - calls ioremap_wc to map the GGTT VMA via the aperture @@ -580,6 +706,7 @@ i915_ggtt_view_size(struct drm_i915_gem_object *obj, * Returns a valid iomapped pointer or ERR_PTR. */ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma); +#define IO_ERR_PTR(x) ((void __iomem *)ERR_PTR(x)) /** * i915_vma_unpin_iomap - unpins the mapping returned from i915_vma_iomap @@ -593,9 +720,14 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma); static inline void i915_vma_unpin_iomap(struct i915_vma *vma) { lockdep_assert_held(&vma->vm->dev->struct_mutex); - GEM_BUG_ON(vma->pin_count == 0); GEM_BUG_ON(vma->iomap == NULL); - vma->pin_count--; + i915_vma_unpin(vma); +} + +static inline struct page *i915_vma_first_page(struct i915_vma *vma) +{ + GEM_BUG_ON(!vma->pages); + return sg_page(vma->pages->sgl); } #endif diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.c b/drivers/gpu/drm/i915/i915_gem_render_state.c index f75bbd6..95b7e9a 100644 --- a/drivers/gpu/drm/i915/i915_gem_render_state.c +++ b/drivers/gpu/drm/i915/i915_gem_render_state.c @@ -28,10 +28,17 @@ #include "i915_drv.h" #include "intel_renderstate.h" +struct render_state { + const struct intel_renderstate_rodata *rodata; + struct i915_vma *vma; + u32 aux_batch_size; + u32 aux_batch_offset; +}; + static const struct intel_renderstate_rodata * -render_state_get_rodata(const int gen) +render_state_get_rodata(const struct drm_i915_gem_request *req) { - switch (gen) { + switch (INTEL_GEN(req->i915)) { case 6: return &gen6_null_state; case 7: @@ -45,35 +52,6 @@ render_state_get_rodata(const int gen) return NULL; } -static int render_state_init(struct render_state *so, - struct drm_i915_private *dev_priv) -{ - int ret; - - so->gen = INTEL_GEN(dev_priv); - so->rodata = render_state_get_rodata(so->gen); - if (so->rodata == NULL) - return 0; - - if (so->rodata->batch_items * 4 > 4096) - return -EINVAL; - - so->obj = i915_gem_object_create(&dev_priv->drm, 4096); - if (IS_ERR(so->obj)) - return PTR_ERR(so->obj); - - ret = i915_gem_obj_ggtt_pin(so->obj, 4096, 0); - if (ret) - goto free_gem; - - so->ggtt_offset = i915_gem_obj_ggtt_offset(so->obj); - return 0; - -free_gem: - drm_gem_object_unreference(&so->obj->base); - return ret; -} - /* * Macro to add commands to auxiliary batch. * This macro only checks for page overflow before inserting the commands, @@ -94,27 +72,28 @@ free_gem: static int render_state_setup(struct render_state *so) { - struct drm_device *dev = so->obj->base.dev; + struct drm_device *dev = so->vma->vm->dev; const struct intel_renderstate_rodata *rodata = so->rodata; + const bool has_64bit_reloc = INTEL_GEN(dev) >= 8; unsigned int i = 0, reloc_index = 0; struct page *page; u32 *d; int ret; - ret = i915_gem_object_set_to_cpu_domain(so->obj, true); + ret = i915_gem_object_set_to_cpu_domain(so->vma->obj, true); if (ret) return ret; - page = i915_gem_object_get_dirty_page(so->obj, 0); + page = i915_gem_object_get_dirty_page(so->vma->obj, 0); d = kmap(page); while (i < rodata->batch_items) { u32 s = rodata->batch[i]; if (i * 4 == rodata->reloc[reloc_index]) { - u64 r = s + so->ggtt_offset; + u64 r = s + so->vma->node.start; s = lower_32_bits(r); - if (so->gen >= 8) { + if (has_64bit_reloc) { if (i + 1 >= rodata->batch_items || rodata->batch[i + 1] != 0) { ret = -EINVAL; @@ -174,7 +153,7 @@ static int render_state_setup(struct render_state *so) kunmap(page); - ret = i915_gem_object_set_to_gtt_domain(so->obj, false); + ret = i915_gem_object_set_to_gtt_domain(so->vma->obj, false); if (ret) return ret; @@ -192,67 +171,60 @@ err_out: #undef OUT_BATCH -void i915_gem_render_state_fini(struct render_state *so) -{ - i915_gem_object_ggtt_unpin(so->obj); - drm_gem_object_unreference(&so->obj->base); -} - -int i915_gem_render_state_prepare(struct intel_engine_cs *engine, - struct render_state *so) +int i915_gem_render_state_init(struct drm_i915_gem_request *req) { + struct render_state so; + struct drm_i915_gem_object *obj; int ret; - if (WARN_ON(engine->id != RCS)) + if (WARN_ON(req->engine->id != RCS)) return -ENOENT; - ret = render_state_init(so, engine->i915); - if (ret) - return ret; - - if (so->rodata == NULL) + so.rodata = render_state_get_rodata(req); + if (!so.rodata) return 0; - ret = render_state_setup(so); - if (ret) { - i915_gem_render_state_fini(so); - return ret; - } + if (so.rodata->batch_items * 4 > 4096) + return -EINVAL; - return 0; -} + obj = i915_gem_object_create(&req->i915->drm, 4096); + if (IS_ERR(obj)) + return PTR_ERR(obj); -int i915_gem_render_state_init(struct drm_i915_gem_request *req) -{ - struct render_state so; - int ret; + so.vma = i915_vma_create(obj, &req->i915->ggtt.base, NULL); + if (IS_ERR(so.vma)) { + ret = PTR_ERR(so.vma); + goto err_obj; + } - ret = i915_gem_render_state_prepare(req->engine, &so); + ret = i915_vma_pin(so.vma, 0, 0, PIN_GLOBAL); if (ret) - return ret; + goto err_obj; - if (so.rodata == NULL) - return 0; + ret = render_state_setup(&so); + if (ret) + goto err_unpin; - ret = req->engine->dispatch_execbuffer(req, so.ggtt_offset, - so.rodata->batch_items * 4, - I915_DISPATCH_SECURE); + ret = req->engine->emit_bb_start(req, so.vma->node.start, + so.rodata->batch_items * 4, + I915_DISPATCH_SECURE); if (ret) - goto out; + goto err_unpin; if (so.aux_batch_size > 8) { - ret = req->engine->dispatch_execbuffer(req, - (so.ggtt_offset + - so.aux_batch_offset), - so.aux_batch_size, - I915_DISPATCH_SECURE); + ret = req->engine->emit_bb_start(req, + (so.vma->node.start + + so.aux_batch_offset), + so.aux_batch_size, + I915_DISPATCH_SECURE); if (ret) - goto out; + goto err_unpin; } - i915_vma_move_to_active(i915_gem_obj_to_ggtt(so.obj), req); - -out: - i915_gem_render_state_fini(&so); + i915_vma_move_to_active(so.vma, req, 0); +err_unpin: + i915_vma_unpin(so.vma); +err_obj: + i915_gem_object_put(obj); return ret; } diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.h b/drivers/gpu/drm/i915/i915_gem_render_state.h index 6aaa3a1..18cce3f 100644 --- a/drivers/gpu/drm/i915/i915_gem_render_state.h +++ b/drivers/gpu/drm/i915/i915_gem_render_state.h @@ -24,26 +24,8 @@ #ifndef _I915_GEM_RENDER_STATE_H_ #define _I915_GEM_RENDER_STATE_H_ -#include <linux/types.h> - -struct intel_renderstate_rodata { - const u32 *reloc; - const u32 *batch; - const u32 batch_items; -}; - -struct render_state { - const struct intel_renderstate_rodata *rodata; - struct drm_i915_gem_object *obj; - u64 ggtt_offset; - int gen; - u32 aux_batch_size; - u32 aux_batch_offset; -}; +struct drm_i915_gem_request; int i915_gem_render_state_init(struct drm_i915_gem_request *req); -void i915_gem_render_state_fini(struct render_state *so); -int i915_gem_render_state_prepare(struct intel_engine_cs *engine, - struct render_state *so); #endif /* _I915_GEM_RENDER_STATE_H_ */ diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c new file mode 100644 index 0000000..40978bc --- /dev/null +++ b/drivers/gpu/drm/i915/i915_gem_request.c @@ -0,0 +1,946 @@ +/* + * Copyright © 2008-2015 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#include <linux/prefetch.h> + +#include "i915_drv.h" + +static const char *i915_fence_get_driver_name(struct fence *fence) +{ + return "i915"; +} + +static const char *i915_fence_get_timeline_name(struct fence *fence) +{ + /* Timelines are bound by eviction to a VM. However, since + * we only have a global seqno at the moment, we only have + * a single timeline. Note that each timeline will have + * multiple execution contexts (fence contexts) as we allow + * engines within a single timeline to execute in parallel. + */ + return "global"; +} + +static bool i915_fence_signaled(struct fence *fence) +{ + return i915_gem_request_completed(to_request(fence)); +} + +static bool i915_fence_enable_signaling(struct fence *fence) +{ + if (i915_fence_signaled(fence)) + return false; + + intel_engine_enable_signaling(to_request(fence)); + return true; +} + +static signed long i915_fence_wait(struct fence *fence, + bool interruptible, + signed long timeout_jiffies) +{ + s64 timeout_ns, *timeout; + int ret; + + if (timeout_jiffies != MAX_SCHEDULE_TIMEOUT) { + timeout_ns = jiffies_to_nsecs(timeout_jiffies); + timeout = &timeout_ns; + } else { + timeout = NULL; + } + + ret = i915_wait_request(to_request(fence), + interruptible, timeout, + NO_WAITBOOST); + if (ret == -ETIME) + return 0; + + if (ret < 0) + return ret; + + if (timeout_jiffies != MAX_SCHEDULE_TIMEOUT) + timeout_jiffies = nsecs_to_jiffies(timeout_ns); + + return timeout_jiffies; +} + +static void i915_fence_value_str(struct fence *fence, char *str, int size) +{ + snprintf(str, size, "%u", fence->seqno); +} + +static void i915_fence_timeline_value_str(struct fence *fence, char *str, + int size) +{ + snprintf(str, size, "%u", + intel_engine_get_seqno(to_request(fence)->engine)); +} + +static void i915_fence_release(struct fence *fence) +{ + struct drm_i915_gem_request *req = to_request(fence); + + kmem_cache_free(req->i915->requests, req); +} + +const struct fence_ops i915_fence_ops = { + .get_driver_name = i915_fence_get_driver_name, + .get_timeline_name = i915_fence_get_timeline_name, + .enable_signaling = i915_fence_enable_signaling, + .signaled = i915_fence_signaled, + .wait = i915_fence_wait, + .release = i915_fence_release, + .fence_value_str = i915_fence_value_str, + .timeline_value_str = i915_fence_timeline_value_str, +}; + +int i915_gem_request_add_to_client(struct drm_i915_gem_request *req, + struct drm_file *file) +{ + struct drm_i915_private *dev_private; + struct drm_i915_file_private *file_priv; + + WARN_ON(!req || !file || req->file_priv); + + if (!req || !file) + return -EINVAL; + + if (req->file_priv) + return -EINVAL; + + dev_private = req->i915; + file_priv = file->driver_priv; + + spin_lock(&file_priv->mm.lock); + req->file_priv = file_priv; + list_add_tail(&req->client_list, &file_priv->mm.request_list); + spin_unlock(&file_priv->mm.lock); + + return 0; +} + +static inline void +i915_gem_request_remove_from_client(struct drm_i915_gem_request *request) +{ + struct drm_i915_file_private *file_priv = request->file_priv; + + if (!file_priv) + return; + + spin_lock(&file_priv->mm.lock); + list_del(&request->client_list); + request->file_priv = NULL; + spin_unlock(&file_priv->mm.lock); +} + +void i915_gem_retire_noop(struct i915_gem_active *active, + struct drm_i915_gem_request *request) +{ + /* Space left intentionally blank */ +} + +static void i915_gem_request_retire(struct drm_i915_gem_request *request) +{ + struct i915_gem_active *active, *next; + + trace_i915_gem_request_retire(request); + list_del(&request->link); + + /* We know the GPU must have read the request to have + * sent us the seqno + interrupt, so use the position + * of tail of the request to update the last known position + * of the GPU head. + * + * Note this requires that we are always called in request + * completion order. + */ + list_del(&request->ring_link); + request->ring->last_retired_head = request->postfix; + + /* Walk through the active list, calling retire on each. This allows + * objects to track their GPU activity and mark themselves as idle + * when their *last* active request is completed (updating state + * tracking lists for eviction, active references for GEM, etc). + * + * As the ->retire() may free the node, we decouple it first and + * pass along the auxiliary information (to avoid dereferencing + * the node after the callback). + */ + list_for_each_entry_safe(active, next, &request->active_list, link) { + /* In microbenchmarks or focusing upon time inside the kernel, + * we may spend an inordinate amount of time simply handling + * the retirement of requests and processing their callbacks. + * Of which, this loop itself is particularly hot due to the + * cache misses when jumping around the list of i915_gem_active. + * So we try to keep this loop as streamlined as possible and + * also prefetch the next i915_gem_active to try and hide + * the likely cache miss. + */ + prefetchw(next); + + INIT_LIST_HEAD(&active->link); + RCU_INIT_POINTER(active->request, NULL); + + active->retire(active, request); + } + + i915_gem_request_remove_from_client(request); + + if (request->previous_context) { + if (i915.enable_execlists) + intel_lr_context_unpin(request->previous_context, + request->engine); + } + + i915_gem_context_put(request->ctx); + i915_gem_request_put(request); +} + +void i915_gem_request_retire_upto(struct drm_i915_gem_request *req) +{ + struct intel_engine_cs *engine = req->engine; + struct drm_i915_gem_request *tmp; + + lockdep_assert_held(&req->i915->drm.struct_mutex); + GEM_BUG_ON(list_empty(&req->link)); + + do { + tmp = list_first_entry(&engine->request_list, + typeof(*tmp), link); + + i915_gem_request_retire(tmp); + } while (tmp != req); +} + +static int i915_gem_check_wedge(struct drm_i915_private *dev_priv) +{ + struct i915_gpu_error *error = &dev_priv->gpu_error; + + if (i915_terminally_wedged(error)) + return -EIO; + + if (i915_reset_in_progress(error)) { + /* Non-interruptible callers can't handle -EAGAIN, hence return + * -EIO unconditionally for these. + */ + if (!dev_priv->mm.interruptible) + return -EIO; + + return -EAGAIN; + } + + return 0; +} + +static int i915_gem_init_seqno(struct drm_i915_private *dev_priv, u32 seqno) +{ + struct intel_engine_cs *engine; + int ret; + + /* Carefully retire all requests without writing to the rings */ + for_each_engine(engine, dev_priv) { + ret = intel_engine_idle(engine, + I915_WAIT_INTERRUPTIBLE | + I915_WAIT_LOCKED); + if (ret) + return ret; + } + i915_gem_retire_requests(dev_priv); + + /* If the seqno wraps around, we need to clear the breadcrumb rbtree */ + if (!i915_seqno_passed(seqno, dev_priv->next_seqno)) { + while (intel_kick_waiters(dev_priv) || + intel_kick_signalers(dev_priv)) + yield(); + } + + /* Finally reset hw state */ + for_each_engine(engine, dev_priv) + intel_engine_init_seqno(engine, seqno); + + return 0; +} + +int i915_gem_set_seqno(struct drm_device *dev, u32 seqno) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + int ret; + + if (seqno == 0) + return -EINVAL; + + /* HWS page needs to be set less than what we + * will inject to ring + */ + ret = i915_gem_init_seqno(dev_priv, seqno - 1); + if (ret) + return ret; + + dev_priv->next_seqno = seqno; + return 0; +} + +static int i915_gem_get_seqno(struct drm_i915_private *dev_priv, u32 *seqno) +{ + /* reserve 0 for non-seqno */ + if (unlikely(dev_priv->next_seqno == 0)) { + int ret; + + ret = i915_gem_init_seqno(dev_priv, 0); + if (ret) + return ret; + + dev_priv->next_seqno = 1; + } + + *seqno = dev_priv->next_seqno++; + return 0; +} + +static int __i915_sw_fence_call +submit_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state) +{ + struct drm_i915_gem_request *request = + container_of(fence, typeof(*request), submit); + + /* Will be called from irq-context when using foreign DMA fences */ + + switch (state) { + case FENCE_COMPLETE: + request->engine->submit_request(request); + break; + + case FENCE_FREE: + break; + } + + return NOTIFY_DONE; +} + +/** + * i915_gem_request_alloc - allocate a request structure + * + * @engine: engine that we wish to issue the request on. + * @ctx: context that the request will be associated with. + * This can be NULL if the request is not directly related to + * any specific user context, in which case this function will + * choose an appropriate context to use. + * + * Returns a pointer to the allocated request if successful, + * or an error code if not. + */ +struct drm_i915_gem_request * +i915_gem_request_alloc(struct intel_engine_cs *engine, + struct i915_gem_context *ctx) +{ + struct drm_i915_private *dev_priv = engine->i915; + struct drm_i915_gem_request *req; + u32 seqno; + int ret; + + /* ABI: Before userspace accesses the GPU (e.g. execbuffer), report + * EIO if the GPU is already wedged, or EAGAIN to drop the struct_mutex + * and restart. + */ + ret = i915_gem_check_wedge(dev_priv); + if (ret) + return ERR_PTR(ret); + + /* Move the oldest request to the slab-cache (if not in use!) */ + req = list_first_entry_or_null(&engine->request_list, + typeof(*req), link); + if (req && i915_gem_request_completed(req)) + i915_gem_request_retire(req); + + /* Beware: Dragons be flying overhead. + * + * We use RCU to look up requests in flight. The lookups may + * race with the request being allocated from the slab freelist. + * That is the request we are writing to here, may be in the process + * of being read by __i915_gem_active_get_rcu(). As such, + * we have to be very careful when overwriting the contents. During + * the RCU lookup, we change chase the request->engine pointer, + * read the request->fence.seqno and increment the reference count. + * + * The reference count is incremented atomically. If it is zero, + * the lookup knows the request is unallocated and complete. Otherwise, + * it is either still in use, or has been reallocated and reset + * with fence_init(). This increment is safe for release as we check + * that the request we have a reference to and matches the active + * request. + * + * Before we increment the refcount, we chase the request->engine + * pointer. We must not call kmem_cache_zalloc() or else we set + * that pointer to NULL and cause a crash during the lookup. If + * we see the request is completed (based on the value of the + * old engine and seqno), the lookup is complete and reports NULL. + * If we decide the request is not completed (new engine or seqno), + * then we grab a reference and double check that it is still the + * active request - which it won't be and restart the lookup. + * + * Do not use kmem_cache_zalloc() here! + */ + req = kmem_cache_alloc(dev_priv->requests, GFP_KERNEL); + if (!req) + return ERR_PTR(-ENOMEM); + + ret = i915_gem_get_seqno(dev_priv, &seqno); + if (ret) + goto err; + + spin_lock_init(&req->lock); + fence_init(&req->fence, + &i915_fence_ops, + &req->lock, + engine->fence_context, + seqno); + + i915_sw_fence_init(&req->submit, submit_notify); + + INIT_LIST_HEAD(&req->active_list); + req->i915 = dev_priv; + req->engine = engine; + req->ctx = i915_gem_context_get(ctx); + + /* No zalloc, must clear what we need by hand */ + req->previous_context = NULL; + req->file_priv = NULL; + req->batch = NULL; + + /* + * Reserve space in the ring buffer for all the commands required to + * eventually emit this request. This is to guarantee that the + * i915_add_request() call can't fail. Note that the reserve may need + * to be redone if the request is not actually submitted straight + * away, e.g. because a GPU scheduler has deferred it. + */ + req->reserved_space = MIN_SPACE_FOR_ADD_REQUEST; + + if (i915.enable_execlists) + ret = intel_logical_ring_alloc_request_extras(req); + else + ret = intel_ring_alloc_request_extras(req); + if (ret) + goto err_ctx; + + /* Record the position of the start of the request so that + * should we detect the updated seqno part-way through the + * GPU processing the request, we never over-estimate the + * position of the head. + */ + req->head = req->ring->tail; + + return req; + +err_ctx: + i915_gem_context_put(ctx); +err: + kmem_cache_free(dev_priv->requests, req); + return ERR_PTR(ret); +} + +static int +i915_gem_request_await_request(struct drm_i915_gem_request *to, + struct drm_i915_gem_request *from) +{ + int idx, ret; + + GEM_BUG_ON(to == from); + + if (to->engine == from->engine) + return 0; + + idx = intel_engine_sync_index(from->engine, to->engine); + if (from->fence.seqno <= from->engine->semaphore.sync_seqno[idx]) + return 0; + + trace_i915_gem_ring_sync_to(to, from); + if (!i915.semaphores) { + if (!i915_spin_request(from, TASK_INTERRUPTIBLE, 2)) { + ret = i915_sw_fence_await_dma_fence(&to->submit, + &from->fence, 0, + GFP_KERNEL); + if (ret < 0) + return ret; + } + } else { + ret = to->engine->semaphore.sync_to(to, from); + if (ret) + return ret; + } + + from->engine->semaphore.sync_seqno[idx] = from->fence.seqno; + return 0; +} + +/** + * i915_gem_request_await_object - set this request to (async) wait upon a bo + * + * @to: request we are wishing to use + * @obj: object which may be in use on another ring. + * + * This code is meant to abstract object synchronization with the GPU. + * Conceptually we serialise writes between engines inside the GPU. + * We only allow one engine to write into a buffer at any time, but + * multiple readers. To ensure each has a coherent view of memory, we must: + * + * - If there is an outstanding write request to the object, the new + * request must wait for it to complete (either CPU or in hw, requests + * on the same ring will be naturally ordered). + * + * - If we are a write request (pending_write_domain is set), the new + * request must wait for outstanding read requests to complete. + * + * Returns 0 if successful, else propagates up the lower layer error. + */ +int +i915_gem_request_await_object(struct drm_i915_gem_request *to, + struct drm_i915_gem_object *obj, + bool write) +{ + struct i915_gem_active *active; + unsigned long active_mask; + int idx; + + if (write) { + active_mask = i915_gem_object_get_active(obj); + active = obj->last_read; + } else { + active_mask = 1; + active = &obj->last_write; + } + + for_each_active(active_mask, idx) { + struct drm_i915_gem_request *request; + int ret; + + request = i915_gem_active_peek(&active[idx], + &obj->base.dev->struct_mutex); + if (!request) + continue; + + ret = i915_gem_request_await_request(to, request); + if (ret) + return ret; + } + + return 0; +} + +static void i915_gem_mark_busy(const struct intel_engine_cs *engine) +{ + struct drm_i915_private *dev_priv = engine->i915; + + dev_priv->gt.active_engines |= intel_engine_flag(engine); + if (dev_priv->gt.awake) + return; + + intel_runtime_pm_get_noresume(dev_priv); + dev_priv->gt.awake = true; + + intel_enable_gt_powersave(dev_priv); + i915_update_gfx_val(dev_priv); + if (INTEL_GEN(dev_priv) >= 6) + gen6_rps_busy(dev_priv); + + queue_delayed_work(dev_priv->wq, + &dev_priv->gt.retire_work, + round_jiffies_up_relative(HZ)); +} + +/* + * NB: This function is not allowed to fail. Doing so would mean the the + * request is not being tracked for completion but the work itself is + * going to happen on the hardware. This would be a Bad Thing(tm). + */ +void __i915_add_request(struct drm_i915_gem_request *request, bool flush_caches) +{ + struct intel_engine_cs *engine = request->engine; + struct intel_ring *ring = request->ring; + struct drm_i915_gem_request *prev; + u32 request_start; + u32 reserved_tail; + int ret; + + trace_i915_gem_request_add(request); + + /* + * To ensure that this call will not fail, space for its emissions + * should already have been reserved in the ring buffer. Let the ring + * know that it is time to use that space up. + */ + request_start = ring->tail; + reserved_tail = request->reserved_space; + request->reserved_space = 0; + + /* + * Emit any outstanding flushes - execbuf can fail to emit the flush + * after having emitted the batchbuffer command. Hence we need to fix + * things up similar to emitting the lazy request. The difference here + * is that the flush _must_ happen before the next request, no matter + * what. + */ + if (flush_caches) { + ret = engine->emit_flush(request, EMIT_FLUSH); + + /* Not allowed to fail! */ + WARN(ret, "engine->emit_flush() failed: %d!\n", ret); + } + + /* Record the position of the start of the breadcrumb so that + * should we detect the updated seqno part-way through the + * GPU processing the request, we never over-estimate the + * position of the ring's HEAD. + */ + request->postfix = ring->tail; + + /* Not allowed to fail! */ + ret = engine->emit_request(request); + WARN(ret, "(%s)->emit_request failed: %d!\n", engine->name, ret); + + /* Sanity check that the reserved size was large enough. */ + ret = ring->tail - request_start; + if (ret < 0) + ret += ring->size; + WARN_ONCE(ret > reserved_tail, + "Not enough space reserved (%d bytes) " + "for adding the request (%d bytes)\n", + reserved_tail, ret); + + /* Seal the request and mark it as pending execution. Note that + * we may inspect this state, without holding any locks, during + * hangcheck. Hence we apply the barrier to ensure that we do not + * see a more recent value in the hws than we are tracking. + */ + + prev = i915_gem_active_raw(&engine->last_request, + &request->i915->drm.struct_mutex); + if (prev) + i915_sw_fence_await_sw_fence(&request->submit, &prev->submit, + &request->submitq); + + request->emitted_jiffies = jiffies; + request->previous_seqno = engine->last_submitted_seqno; + engine->last_submitted_seqno = request->fence.seqno; + i915_gem_active_set(&engine->last_request, request); + list_add_tail(&request->link, &engine->request_list); + list_add_tail(&request->ring_link, &ring->request_list); + + i915_gem_mark_busy(engine); + + local_bh_disable(); + i915_sw_fence_commit(&request->submit); + local_bh_enable(); /* Kick the execlists tasklet if just scheduled */ +} + +static void reset_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) +{ + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + if (list_empty(&wait->task_list)) + __add_wait_queue(q, wait); + spin_unlock_irqrestore(&q->lock, flags); +} + +static unsigned long local_clock_us(unsigned int *cpu) +{ + unsigned long t; + + /* Cheaply and approximately convert from nanoseconds to microseconds. + * The result and subsequent calculations are also defined in the same + * approximate microseconds units. The principal source of timing + * error here is from the simple truncation. + * + * Note that local_clock() is only defined wrt to the current CPU; + * the comparisons are no longer valid if we switch CPUs. Instead of + * blocking preemption for the entire busywait, we can detect the CPU + * switch and use that as indicator of system load and a reason to + * stop busywaiting, see busywait_stop(). + */ + *cpu = get_cpu(); + t = local_clock() >> 10; + put_cpu(); + + return t; +} + +static bool busywait_stop(unsigned long timeout, unsigned int cpu) +{ + unsigned int this_cpu; + + if (time_after(local_clock_us(&this_cpu), timeout)) + return true; + + return this_cpu != cpu; +} + +bool __i915_spin_request(const struct drm_i915_gem_request *req, + int state, unsigned long timeout_us) +{ + unsigned int cpu; + + /* When waiting for high frequency requests, e.g. during synchronous + * rendering split between the CPU and GPU, the finite amount of time + * required to set up the irq and wait upon it limits the response + * rate. By busywaiting on the request completion for a short while we + * can service the high frequency waits as quick as possible. However, + * if it is a slow request, we want to sleep as quickly as possible. + * The tradeoff between waiting and sleeping is roughly the time it + * takes to sleep on a request, on the order of a microsecond. + */ + + timeout_us += local_clock_us(&cpu); + do { + if (i915_gem_request_completed(req)) + return true; + + if (signal_pending_state(state, current)) + break; + + if (busywait_stop(timeout_us, cpu)) + break; + + cpu_relax_lowlatency(); + } while (!need_resched()); + + return false; +} + +/** + * i915_wait_request - wait until execution of request has finished + * @req: duh! + * @flags: how to wait + * @timeout: in - how long to wait (NULL forever); out - how much time remaining + * @rps: client to charge for RPS boosting + * + * Note: It is of utmost importance that the passed in seqno and reset_counter + * values have been read by the caller in an smp safe manner. Where read-side + * locks are involved, it is sufficient to read the reset_counter before + * unlocking the lock that protects the seqno. For lockless tricks, the + * reset_counter _must_ be read before, and an appropriate smp_rmb must be + * inserted. + * + * Returns 0 if the request was found within the alloted time. Else returns the + * errno with remaining time filled in timeout argument. + */ +int i915_wait_request(struct drm_i915_gem_request *req, + unsigned int flags, + s64 *timeout, + struct intel_rps_client *rps) +{ + const int state = flags & I915_WAIT_INTERRUPTIBLE ? + TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE; + DEFINE_WAIT(reset); + struct intel_wait wait; + unsigned long timeout_remain; + int ret = 0; + + might_sleep(); +#if IS_ENABLED(CONFIG_LOCKDEP) + GEM_BUG_ON(!!lockdep_is_held(&req->i915->drm.struct_mutex) != + !!(flags & I915_WAIT_LOCKED)); +#endif + + if (i915_gem_request_completed(req)) + return 0; + + timeout_remain = MAX_SCHEDULE_TIMEOUT; + if (timeout) { + if (WARN_ON(*timeout < 0)) + return -EINVAL; + + if (*timeout == 0) + return -ETIME; + + /* Record current time in case interrupted, or wedged */ + timeout_remain = nsecs_to_jiffies_timeout(*timeout); + *timeout += ktime_get_raw_ns(); + } + + trace_i915_gem_request_wait_begin(req); + + /* This client is about to stall waiting for the GPU. In many cases + * this is undesirable and limits the throughput of the system, as + * many clients cannot continue processing user input/output whilst + * blocked. RPS autotuning may take tens of milliseconds to respond + * to the GPU load and thus incurs additional latency for the client. + * We can circumvent that by promoting the GPU frequency to maximum + * before we wait. This makes the GPU throttle up much more quickly + * (good for benchmarks and user experience, e.g. window animations), + * but at a cost of spending more power processing the workload + * (bad for battery). Not all clients even want their results + * immediately and for them we should just let the GPU select its own + * frequency to maximise efficiency. To prevent a single client from + * forcing the clocks too high for the whole system, we only allow + * each client to waitboost once in a busy period. + */ + if (IS_RPS_CLIENT(rps) && INTEL_GEN(req->i915) >= 6) + gen6_rps_boost(req->i915, rps, req->emitted_jiffies); + + /* Optimistic short spin before touching IRQs */ + if (i915_spin_request(req, state, 5)) + goto complete; + + set_current_state(state); + if (flags & I915_WAIT_LOCKED) + add_wait_queue(&req->i915->gpu_error.wait_queue, &reset); + + intel_wait_init(&wait, req->fence.seqno); + if (intel_engine_add_wait(req->engine, &wait)) + /* In order to check that we haven't missed the interrupt + * as we enabled it, we need to kick ourselves to do a + * coherent check on the seqno before we sleep. + */ + goto wakeup; + + for (;;) { + if (signal_pending_state(state, current)) { + ret = -ERESTARTSYS; + break; + } + + timeout_remain = io_schedule_timeout(timeout_remain); + if (timeout_remain == 0) { + ret = -ETIME; + break; + } + + if (intel_wait_complete(&wait)) + break; + + set_current_state(state); + +wakeup: + /* Carefully check if the request is complete, giving time + * for the seqno to be visible following the interrupt. + * We also have to check in case we are kicked by the GPU + * reset in order to drop the struct_mutex. + */ + if (__i915_request_irq_complete(req)) + break; + + /* If the GPU is hung, and we hold the lock, reset the GPU + * and then check for completion. On a full reset, the engine's + * HW seqno will be advanced passed us and we are complete. + * If we do a partial reset, we have to wait for the GPU to + * resume and update the breadcrumb. + * + * If we don't hold the mutex, we can just wait for the worker + * to come along and update the breadcrumb (either directly + * itself, or indirectly by recovering the GPU). + */ + if (flags & I915_WAIT_LOCKED && + i915_reset_in_progress(&req->i915->gpu_error)) { + __set_current_state(TASK_RUNNING); + i915_reset(req->i915); + reset_wait_queue(&req->i915->gpu_error.wait_queue, + &reset); + continue; + } + + /* Only spin if we know the GPU is processing this request */ + if (i915_spin_request(req, state, 2)) + break; + } + + intel_engine_remove_wait(req->engine, &wait); + if (flags & I915_WAIT_LOCKED) + remove_wait_queue(&req->i915->gpu_error.wait_queue, &reset); + __set_current_state(TASK_RUNNING); + +complete: + trace_i915_gem_request_wait_end(req); + + if (timeout) { + *timeout -= ktime_get_raw_ns(); + if (*timeout < 0) + *timeout = 0; + + /* + * Apparently ktime isn't accurate enough and occasionally has a + * bit of mismatch in the jiffies<->nsecs<->ktime loop. So patch + * things up to make the test happy. We allow up to 1 jiffy. + * + * This is a regrssion from the timespec->ktime conversion. + */ + if (ret == -ETIME && *timeout < jiffies_to_usecs(1)*1000) + *timeout = 0; + } + + if (IS_RPS_USER(rps) && + req->fence.seqno == req->engine->last_submitted_seqno) { + /* The GPU is now idle and this client has stalled. + * Since no other client has submitted a request in the + * meantime, assume that this client is the only one + * supplying work to the GPU but is unable to keep that + * work supplied because it is waiting. Since the GPU is + * then never kept fully busy, RPS autoclocking will + * keep the clocks relatively low, causing further delays. + * Compensate by giving the synchronous client credit for + * a waitboost next time. + */ + spin_lock(&req->i915->rps.client_lock); + list_del_init(&rps->link); + spin_unlock(&req->i915->rps.client_lock); + } + + return ret; +} + +static bool engine_retire_requests(struct intel_engine_cs *engine) +{ + struct drm_i915_gem_request *request, *next; + + list_for_each_entry_safe(request, next, &engine->request_list, link) { + if (!i915_gem_request_completed(request)) + return false; + + i915_gem_request_retire(request); + } + + return true; +} + +void i915_gem_retire_requests(struct drm_i915_private *dev_priv) +{ + struct intel_engine_cs *engine; + unsigned int tmp; + + lockdep_assert_held(&dev_priv->drm.struct_mutex); + + if (dev_priv->gt.active_engines == 0) + return; + + GEM_BUG_ON(!dev_priv->gt.awake); + + for_each_engine_masked(engine, dev_priv, dev_priv->gt.active_engines, tmp) + if (engine_retire_requests(engine)) + dev_priv->gt.active_engines &= ~intel_engine_flag(engine); + + if (dev_priv->gt.active_engines == 0) + queue_delayed_work(dev_priv->wq, + &dev_priv->gt.idle_work, + msecs_to_jiffies(100)); +} diff --git a/drivers/gpu/drm/i915/i915_gem_request.h b/drivers/gpu/drm/i915/i915_gem_request.h new file mode 100644 index 0000000..974bd7b --- /dev/null +++ b/drivers/gpu/drm/i915/i915_gem_request.h @@ -0,0 +1,689 @@ +/* + * Copyright © 2008-2015 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef I915_GEM_REQUEST_H +#define I915_GEM_REQUEST_H + +#include <linux/fence.h> + +#include "i915_gem.h" +#include "i915_sw_fence.h" + +struct intel_wait { + struct rb_node node; + struct task_struct *tsk; + u32 seqno; +}; + +struct intel_signal_node { + struct rb_node node; + struct intel_wait wait; +}; + +/** + * Request queue structure. + * + * The request queue allows us to note sequence numbers that have been emitted + * and may be associated with active buffers to be retired. + * + * By keeping this list, we can avoid having to do questionable sequence + * number comparisons on buffer last_read|write_seqno. It also allows an + * emission time to be associated with the request for tracking how far ahead + * of the GPU the submission is. + * + * When modifying this structure be very aware that we perform a lockless + * RCU lookup of it that may race against reallocation of the struct + * from the slab freelist. We intentionally do not zero the structure on + * allocation so that the lookup can use the dangling pointers (and is + * cogniscent that those pointers may be wrong). Instead, everything that + * needs to be initialised must be done so explicitly. + * + * The requests are reference counted. + */ +struct drm_i915_gem_request { + struct fence fence; + spinlock_t lock; + + /** On Which ring this request was generated */ + struct drm_i915_private *i915; + + /** + * Context and ring buffer related to this request + * Contexts are refcounted, so when this request is associated with a + * context, we must increment the context's refcount, to guarantee that + * it persists while any request is linked to it. Requests themselves + * are also refcounted, so the request will only be freed when the last + * reference to it is dismissed, and the code in + * i915_gem_request_free() will then decrement the refcount on the + * context. + */ + struct i915_gem_context *ctx; + struct intel_engine_cs *engine; + struct intel_ring *ring; + struct intel_signal_node signaling; + + struct i915_sw_fence submit; + wait_queue_t submitq; + + /** GEM sequence number associated with the previous request, + * when the HWS breadcrumb is equal to this the GPU is processing + * this request. + */ + u32 previous_seqno; + + /** Position in the ring of the start of the request */ + u32 head; + + /** + * Position in the ring of the start of the postfix. + * This is required to calculate the maximum available ring space + * without overwriting the postfix. + */ + u32 postfix; + + /** Position in the ring of the end of the whole request */ + u32 tail; + + /** Position in the ring of the end of any workarounds after the tail */ + u32 wa_tail; + + /** Preallocate space in the ring for the emitting the request */ + u32 reserved_space; + + /** + * Context related to the previous request. + * As the contexts are accessed by the hardware until the switch is + * completed to a new context, the hardware may still be writing + * to the context object after the breadcrumb is visible. We must + * not unpin/unbind/prune that object whilst still active and so + * we keep the previous context pinned until the following (this) + * request is retired. + */ + struct i915_gem_context *previous_context; + + /** Batch buffer related to this request if any (used for + * error state dump only). + */ + struct i915_vma *batch; + struct list_head active_list; + + /** Time at which this request was emitted, in jiffies. */ + unsigned long emitted_jiffies; + + /** engine->request_list entry for this request */ + struct list_head link; + + /** ring->request_list entry for this request */ + struct list_head ring_link; + + struct drm_i915_file_private *file_priv; + /** file_priv list entry for this request */ + struct list_head client_list; + + /** Link in the execlist submission queue, guarded by execlist_lock. */ + struct list_head execlist_link; +}; + +extern const struct fence_ops i915_fence_ops; + +static inline bool fence_is_i915(struct fence *fence) +{ + return fence->ops == &i915_fence_ops; +} + +struct drm_i915_gem_request * __must_check +i915_gem_request_alloc(struct intel_engine_cs *engine, + struct i915_gem_context *ctx); +int i915_gem_request_add_to_client(struct drm_i915_gem_request *req, + struct drm_file *file); +void i915_gem_request_retire_upto(struct drm_i915_gem_request *req); + +static inline u32 +i915_gem_request_get_seqno(struct drm_i915_gem_request *req) +{ + return req ? req->fence.seqno : 0; +} + +static inline struct intel_engine_cs * +i915_gem_request_get_engine(struct drm_i915_gem_request *req) +{ + return req ? req->engine : NULL; +} + +static inline struct drm_i915_gem_request * +to_request(struct fence *fence) +{ + /* We assume that NULL fence/request are interoperable */ + BUILD_BUG_ON(offsetof(struct drm_i915_gem_request, fence) != 0); + GEM_BUG_ON(fence && !fence_is_i915(fence)); + return container_of(fence, struct drm_i915_gem_request, fence); +} + +static inline struct drm_i915_gem_request * +i915_gem_request_get(struct drm_i915_gem_request *req) +{ + return to_request(fence_get(&req->fence)); +} + +static inline struct drm_i915_gem_request * +i915_gem_request_get_rcu(struct drm_i915_gem_request *req) +{ + return to_request(fence_get_rcu(&req->fence)); +} + +static inline void +i915_gem_request_put(struct drm_i915_gem_request *req) +{ + fence_put(&req->fence); +} + +static inline void i915_gem_request_assign(struct drm_i915_gem_request **pdst, + struct drm_i915_gem_request *src) +{ + if (src) + i915_gem_request_get(src); + + if (*pdst) + i915_gem_request_put(*pdst); + + *pdst = src; +} + +int +i915_gem_request_await_object(struct drm_i915_gem_request *to, + struct drm_i915_gem_object *obj, + bool write); + +void __i915_add_request(struct drm_i915_gem_request *req, bool flush_caches); +#define i915_add_request(req) \ + __i915_add_request(req, true) +#define i915_add_request_no_flush(req) \ + __i915_add_request(req, false) + +struct intel_rps_client; +#define NO_WAITBOOST ERR_PTR(-1) +#define IS_RPS_CLIENT(p) (!IS_ERR(p)) +#define IS_RPS_USER(p) (!IS_ERR_OR_NULL(p)) + +int i915_wait_request(struct drm_i915_gem_request *req, + unsigned int flags, + s64 *timeout, + struct intel_rps_client *rps) + __attribute__((nonnull(1))); +#define I915_WAIT_INTERRUPTIBLE BIT(0) +#define I915_WAIT_LOCKED BIT(1) /* struct_mutex held, handle GPU reset */ + +static inline u32 intel_engine_get_seqno(struct intel_engine_cs *engine); + +/** + * Returns true if seq1 is later than seq2. + */ +static inline bool i915_seqno_passed(u32 seq1, u32 seq2) +{ + return (s32)(seq1 - seq2) >= 0; +} + +static inline bool +i915_gem_request_started(const struct drm_i915_gem_request *req) +{ + return i915_seqno_passed(intel_engine_get_seqno(req->engine), + req->previous_seqno); +} + +static inline bool +i915_gem_request_completed(const struct drm_i915_gem_request *req) +{ + return i915_seqno_passed(intel_engine_get_seqno(req->engine), + req->fence.seqno); +} + +bool __i915_spin_request(const struct drm_i915_gem_request *request, + int state, unsigned long timeout_us); +static inline bool i915_spin_request(const struct drm_i915_gem_request *request, + int state, unsigned long timeout_us) +{ + return (i915_gem_request_started(request) && + __i915_spin_request(request, state, timeout_us)); +} + +/* We treat requests as fences. This is not be to confused with our + * "fence registers" but pipeline synchronisation objects ala GL_ARB_sync. + * We use the fences to synchronize access from the CPU with activity on the + * GPU, for example, we should not rewrite an object's PTE whilst the GPU + * is reading them. We also track fences at a higher level to provide + * implicit synchronisation around GEM objects, e.g. set-domain will wait + * for outstanding GPU rendering before marking the object ready for CPU + * access, or a pageflip will wait until the GPU is complete before showing + * the frame on the scanout. + * + * In order to use a fence, the object must track the fence it needs to + * serialise with. For example, GEM objects want to track both read and + * write access so that we can perform concurrent read operations between + * the CPU and GPU engines, as well as waiting for all rendering to + * complete, or waiting for the last GPU user of a "fence register". The + * object then embeds a #i915_gem_active to track the most recent (in + * retirement order) request relevant for the desired mode of access. + * The #i915_gem_active is updated with i915_gem_active_set() to track the + * most recent fence request, typically this is done as part of + * i915_vma_move_to_active(). + * + * When the #i915_gem_active completes (is retired), it will + * signal its completion to the owner through a callback as well as mark + * itself as idle (i915_gem_active.request == NULL). The owner + * can then perform any action, such as delayed freeing of an active + * resource including itself. + */ +struct i915_gem_active; + +typedef void (*i915_gem_retire_fn)(struct i915_gem_active *, + struct drm_i915_gem_request *); + +struct i915_gem_active { + struct drm_i915_gem_request __rcu *request; + struct list_head link; + i915_gem_retire_fn retire; +}; + +void i915_gem_retire_noop(struct i915_gem_active *, + struct drm_i915_gem_request *request); + +/** + * init_request_active - prepares the activity tracker for use + * @active - the active tracker + * @func - a callback when then the tracker is retired (becomes idle), + * can be NULL + * + * init_request_active() prepares the embedded @active struct for use as + * an activity tracker, that is for tracking the last known active request + * associated with it. When the last request becomes idle, when it is retired + * after completion, the optional callback @func is invoked. + */ +static inline void +init_request_active(struct i915_gem_active *active, + i915_gem_retire_fn retire) +{ + INIT_LIST_HEAD(&active->link); + active->retire = retire ?: i915_gem_retire_noop; +} + +/** + * i915_gem_active_set - updates the tracker to watch the current request + * @active - the active tracker + * @request - the request to watch + * + * i915_gem_active_set() watches the given @request for completion. Whilst + * that @request is busy, the @active reports busy. When that @request is + * retired, the @active tracker is updated to report idle. + */ +static inline void +i915_gem_active_set(struct i915_gem_active *active, + struct drm_i915_gem_request *request) +{ + list_move(&active->link, &request->active_list); + rcu_assign_pointer(active->request, request); +} + +static inline struct drm_i915_gem_request * +__i915_gem_active_peek(const struct i915_gem_active *active) +{ + /* Inside the error capture (running with the driver in an unknown + * state), we want to bend the rules slightly (a lot). + * + * Work is in progress to make it safer, in the meantime this keeps + * the known issue from spamming the logs. + */ + return rcu_dereference_protected(active->request, 1); +} + +/** + * i915_gem_active_raw - return the active request + * @active - the active tracker + * + * i915_gem_active_raw() returns the current request being tracked, or NULL. + * It does not obtain a reference on the request for the caller, so the caller + * must hold struct_mutex. + */ +static inline struct drm_i915_gem_request * +i915_gem_active_raw(const struct i915_gem_active *active, struct mutex *mutex) +{ + return rcu_dereference_protected(active->request, + lockdep_is_held(mutex)); +} + +/** + * i915_gem_active_peek - report the active request being monitored + * @active - the active tracker + * + * i915_gem_active_peek() returns the current request being tracked if + * still active, or NULL. It does not obtain a reference on the request + * for the caller, so the caller must hold struct_mutex. + */ +static inline struct drm_i915_gem_request * +i915_gem_active_peek(const struct i915_gem_active *active, struct mutex *mutex) +{ + struct drm_i915_gem_request *request; + + request = i915_gem_active_raw(active, mutex); + if (!request || i915_gem_request_completed(request)) + return NULL; + + return request; +} + +/** + * i915_gem_active_get - return a reference to the active request + * @active - the active tracker + * + * i915_gem_active_get() returns a reference to the active request, or NULL + * if the active tracker is idle. The caller must hold struct_mutex. + */ +static inline struct drm_i915_gem_request * +i915_gem_active_get(const struct i915_gem_active *active, struct mutex *mutex) +{ + return i915_gem_request_get(i915_gem_active_peek(active, mutex)); +} + +/** + * __i915_gem_active_get_rcu - return a reference to the active request + * @active - the active tracker + * + * __i915_gem_active_get() returns a reference to the active request, or NULL + * if the active tracker is idle. The caller must hold the RCU read lock, but + * the returned pointer is safe to use outside of RCU. + */ +static inline struct drm_i915_gem_request * +__i915_gem_active_get_rcu(const struct i915_gem_active *active) +{ + /* Performing a lockless retrieval of the active request is super + * tricky. SLAB_DESTROY_BY_RCU merely guarantees that the backing + * slab of request objects will not be freed whilst we hold the + * RCU read lock. It does not guarantee that the request itself + * will not be freed and then *reused*. Viz, + * + * Thread A Thread B + * + * req = active.request + * retire(req) -> free(req); + * (req is now first on the slab freelist) + * active.request = NULL + * + * req = new submission on a new object + * ref(req) + * + * To prevent the request from being reused whilst the caller + * uses it, we take a reference like normal. Whilst acquiring + * the reference we check that it is not in a destroyed state + * (refcnt == 0). That prevents the request being reallocated + * whilst the caller holds on to it. To check that the request + * was not reallocated as we acquired the reference we have to + * check that our request remains the active request across + * the lookup, in the same manner as a seqlock. The visibility + * of the pointer versus the reference counting is controlled + * by using RCU barriers (rcu_dereference and rcu_assign_pointer). + * + * In the middle of all that, we inspect whether the request is + * complete. Retiring is lazy so the request may be completed long + * before the active tracker is updated. Querying whether the + * request is complete is far cheaper (as it involves no locked + * instructions setting cachelines to exclusive) than acquiring + * the reference, so we do it first. The RCU read lock ensures the + * pointer dereference is valid, but does not ensure that the + * seqno nor HWS is the right one! However, if the request was + * reallocated, that means the active tracker's request was complete. + * If the new request is also complete, then both are and we can + * just report the active tracker is idle. If the new request is + * incomplete, then we acquire a reference on it and check that + * it remained the active request. + * + * It is then imperative that we do not zero the request on + * reallocation, so that we can chase the dangling pointers! + * See i915_gem_request_alloc(). + */ + do { + struct drm_i915_gem_request *request; + + request = rcu_dereference(active->request); + if (!request || i915_gem_request_completed(request)) + return NULL; + + /* An especially silly compiler could decide to recompute the + * result of i915_gem_request_completed, more specifically + * re-emit the load for request->fence.seqno. A race would catch + * a later seqno value, which could flip the result from true to + * false. Which means part of the instructions below might not + * be executed, while later on instructions are executed. Due to + * barriers within the refcounting the inconsistency can't reach + * past the call to i915_gem_request_get_rcu, but not executing + * that while still executing i915_gem_request_put() creates + * havoc enough. Prevent this with a compiler barrier. + */ + barrier(); + + request = i915_gem_request_get_rcu(request); + + /* What stops the following rcu_access_pointer() from occurring + * before the above i915_gem_request_get_rcu()? If we were + * to read the value before pausing to get the reference to + * the request, we may not notice a change in the active + * tracker. + * + * The rcu_access_pointer() is a mere compiler barrier, which + * means both the CPU and compiler are free to perform the + * memory read without constraint. The compiler only has to + * ensure that any operations after the rcu_access_pointer() + * occur afterwards in program order. This means the read may + * be performed earlier by an out-of-order CPU, or adventurous + * compiler. + * + * The atomic operation at the heart of + * i915_gem_request_get_rcu(), see fence_get_rcu(), is + * atomic_inc_not_zero() which is only a full memory barrier + * when successful. That is, if i915_gem_request_get_rcu() + * returns the request (and so with the reference counted + * incremented) then the following read for rcu_access_pointer() + * must occur after the atomic operation and so confirm + * that this request is the one currently being tracked. + * + * The corresponding write barrier is part of + * rcu_assign_pointer(). + */ + if (!request || request == rcu_access_pointer(active->request)) + return rcu_pointer_handoff(request); + + i915_gem_request_put(request); + } while (1); +} + +/** + * i915_gem_active_get_unlocked - return a reference to the active request + * @active - the active tracker + * + * i915_gem_active_get_unlocked() returns a reference to the active request, + * or NULL if the active tracker is idle. The reference is obtained under RCU, + * so no locking is required by the caller. + * + * The reference should be freed with i915_gem_request_put(). + */ +static inline struct drm_i915_gem_request * +i915_gem_active_get_unlocked(const struct i915_gem_active *active) +{ + struct drm_i915_gem_request *request; + + rcu_read_lock(); + request = __i915_gem_active_get_rcu(active); + rcu_read_unlock(); + + return request; +} + +/** + * i915_gem_active_isset - report whether the active tracker is assigned + * @active - the active tracker + * + * i915_gem_active_isset() returns true if the active tracker is currently + * assigned to a request. Due to the lazy retiring, that request may be idle + * and this may report stale information. + */ +static inline bool +i915_gem_active_isset(const struct i915_gem_active *active) +{ + return rcu_access_pointer(active->request); +} + +/** + * i915_gem_active_is_idle - report whether the active tracker is idle + * @active - the active tracker + * + * i915_gem_active_is_idle() returns true if the active tracker is currently + * unassigned or if the request is complete (but not yet retired). Requires + * the caller to hold struct_mutex (but that can be relaxed if desired). + */ +static inline bool +i915_gem_active_is_idle(const struct i915_gem_active *active, + struct mutex *mutex) +{ + return !i915_gem_active_peek(active, mutex); +} + +/** + * i915_gem_active_wait - waits until the request is completed + * @active - the active request on which to wait + * + * i915_gem_active_wait() waits until the request is completed before + * returning. Note that it does not guarantee that the request is + * retired first, see i915_gem_active_retire(). + * + * i915_gem_active_wait() returns immediately if the active + * request is already complete. + */ +static inline int __must_check +i915_gem_active_wait(const struct i915_gem_active *active, struct mutex *mutex) +{ + struct drm_i915_gem_request *request; + + request = i915_gem_active_peek(active, mutex); + if (!request) + return 0; + + return i915_wait_request(request, + I915_WAIT_INTERRUPTIBLE | I915_WAIT_LOCKED, + NULL, NULL); +} + +/** + * i915_gem_active_wait_unlocked - waits until the request is completed + * @active - the active request on which to wait + * @flags - how to wait + * @timeout - how long to wait at most + * @rps - userspace client to charge for a waitboost + * + * i915_gem_active_wait_unlocked() waits until the request is completed before + * returning, without requiring any locks to be held. Note that it does not + * retire any requests before returning. + * + * This function relies on RCU in order to acquire the reference to the active + * request without holding any locks. See __i915_gem_active_get_rcu() for the + * glory details on how that is managed. Once the reference is acquired, we + * can then wait upon the request, and afterwards release our reference, + * free of any locking. + * + * This function wraps i915_wait_request(), see it for the full details on + * the arguments. + * + * Returns 0 if successful, or a negative error code. + */ +static inline int +i915_gem_active_wait_unlocked(const struct i915_gem_active *active, + unsigned int flags, + s64 *timeout, + struct intel_rps_client *rps) +{ + struct drm_i915_gem_request *request; + int ret = 0; + + request = i915_gem_active_get_unlocked(active); + if (request) { + ret = i915_wait_request(request, flags, timeout, rps); + i915_gem_request_put(request); + } + + return ret; +} + +/** + * i915_gem_active_retire - waits until the request is retired + * @active - the active request on which to wait + * + * i915_gem_active_retire() waits until the request is completed, + * and then ensures that at least the retirement handler for this + * @active tracker is called before returning. If the @active + * tracker is idle, the function returns immediately. + */ +static inline int __must_check +i915_gem_active_retire(struct i915_gem_active *active, + struct mutex *mutex) +{ + struct drm_i915_gem_request *request; + int ret; + + request = i915_gem_active_raw(active, mutex); + if (!request) + return 0; + + ret = i915_wait_request(request, + I915_WAIT_INTERRUPTIBLE | I915_WAIT_LOCKED, + NULL, NULL); + if (ret) + return ret; + + list_del_init(&active->link); + RCU_INIT_POINTER(active->request, NULL); + + active->retire(active, request); + + return 0; +} + +/* Convenience functions for peeking at state inside active's request whilst + * guarded by the struct_mutex. + */ + +static inline uint32_t +i915_gem_active_get_seqno(const struct i915_gem_active *active, + struct mutex *mutex) +{ + return i915_gem_request_get_seqno(i915_gem_active_peek(active, mutex)); +} + +static inline struct intel_engine_cs * +i915_gem_active_get_engine(const struct i915_gem_active *active, + struct mutex *mutex) +{ + return i915_gem_request_get_engine(i915_gem_active_peek(active, mutex)); +} + +#define for_each_active(mask, idx) \ + for (; mask ? idx = ffs(mask) - 1, 1 : 0; mask &= ~BIT(idx)) + +#endif /* I915_GEM_REQUEST_H */ diff --git a/drivers/gpu/drm/i915/i915_gem_shrinker.c b/drivers/gpu/drm/i915/i915_gem_shrinker.c index 6f10b42..1c237d0 100644 --- a/drivers/gpu/drm/i915/i915_gem_shrinker.c +++ b/drivers/gpu/drm/i915/i915_gem_shrinker.c @@ -48,19 +48,15 @@ static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task) #endif } -static int num_vma_bound(struct drm_i915_gem_object *obj) +static bool any_vma_pinned(struct drm_i915_gem_object *obj) { struct i915_vma *vma; - int count = 0; - list_for_each_entry(vma, &obj->vma_list, obj_link) { - if (drm_mm_node_allocated(&vma->node)) - count++; - if (vma->pin_count) - count++; - } + list_for_each_entry(vma, &obj->vma_list, obj_link) + if (i915_vma_is_pinned(vma)) + return true; - return count; + return false; } static bool swap_available(void) @@ -82,7 +78,10 @@ static bool can_release_pages(struct drm_i915_gem_object *obj) * to the GPU, simply unbinding from the GPU is not going to succeed * in releasing our pin count on the pages themselves. */ - if (obj->pages_pin_count != num_vma_bound(obj)) + if (obj->pages_pin_count > obj->bind_count) + return false; + + if (any_vma_pinned(obj)) return false; /* We can only return physical pages to the system if we can either @@ -163,17 +162,16 @@ i915_gem_shrink(struct drm_i915_private *dev_priv, */ for (phase = phases; phase->list; phase++) { struct list_head still_in_list; + struct drm_i915_gem_object *obj; if ((flags & phase->bit) == 0) continue; INIT_LIST_HEAD(&still_in_list); - while (count < target && !list_empty(phase->list)) { - struct drm_i915_gem_object *obj; - struct i915_vma *vma, *v; - - obj = list_first_entry(phase->list, - typeof(*obj), global_list); + while (count < target && + (obj = list_first_entry_or_null(phase->list, + typeof(*obj), + global_list))) { list_move_tail(&obj->global_list, &still_in_list); if (flags & I915_SHRINK_PURGEABLE && @@ -184,24 +182,21 @@ i915_gem_shrink(struct drm_i915_private *dev_priv, !is_vmalloc_addr(obj->mapping)) continue; - if ((flags & I915_SHRINK_ACTIVE) == 0 && obj->active) + if ((flags & I915_SHRINK_ACTIVE) == 0 && + i915_gem_object_is_active(obj)) continue; if (!can_release_pages(obj)) continue; - drm_gem_object_reference(&obj->base); + i915_gem_object_get(obj); /* For the unbound phase, this should be a no-op! */ - list_for_each_entry_safe(vma, v, - &obj->vma_list, obj_link) - if (i915_vma_unbind(vma)) - break; - + i915_gem_object_unbind(obj); if (i915_gem_object_put_pages(obj) == 0) count += obj->base.size >> PAGE_SHIFT; - drm_gem_object_unreference(&obj->base); + i915_gem_object_put(obj); } list_splice(&still_in_list, phase->list); } @@ -210,6 +205,8 @@ i915_gem_shrink(struct drm_i915_private *dev_priv, intel_runtime_pm_put(dev_priv); i915_gem_retire_requests(dev_priv); + /* expedite the RCU grace period to free some request slabs */ + synchronize_rcu_expedited(); return count; } @@ -230,10 +227,15 @@ i915_gem_shrink(struct drm_i915_private *dev_priv, */ unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv) { - return i915_gem_shrink(dev_priv, -1UL, - I915_SHRINK_BOUND | - I915_SHRINK_UNBOUND | - I915_SHRINK_ACTIVE); + unsigned long freed; + + freed = i915_gem_shrink(dev_priv, -1UL, + I915_SHRINK_BOUND | + I915_SHRINK_UNBOUND | + I915_SHRINK_ACTIVE); + rcu_barrier(); /* wait until our RCU delayed slab frees are completed */ + + return freed; } static bool i915_gem_shrinker_lock(struct drm_device *dev, bool *unlock) @@ -242,9 +244,6 @@ static bool i915_gem_shrinker_lock(struct drm_device *dev, bool *unlock) if (!mutex_is_locked_by(&dev->struct_mutex, current)) return false; - if (to_i915(dev)->mm.shrinker_no_lock_stealing) - return false; - *unlock = false; } else *unlock = true; @@ -273,7 +272,7 @@ i915_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc) count += obj->base.size >> PAGE_SHIFT; list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) { - if (!obj->active && can_release_pages(obj)) + if (!i915_gem_object_is_active(obj) && can_release_pages(obj)) count += obj->base.size >> PAGE_SHIFT; } @@ -321,17 +320,22 @@ i915_gem_shrinker_lock_uninterruptible(struct drm_i915_private *dev_priv, struct shrinker_lock_uninterruptible *slu, int timeout_ms) { - unsigned long timeout = msecs_to_jiffies(timeout_ms) + 1; + unsigned long timeout = jiffies + msecs_to_jiffies_timeout(timeout_ms); + + do { + if (i915_gem_wait_for_idle(dev_priv, 0) == 0 && + i915_gem_shrinker_lock(&dev_priv->drm, &slu->unlock)) + break; - while (!i915_gem_shrinker_lock(&dev_priv->drm, &slu->unlock)) { schedule_timeout_killable(1); if (fatal_signal_pending(current)) return false; - if (--timeout == 0) { + + if (time_after(jiffies, timeout)) { pr_err("Unable to lock GPU to purge memory.\n"); return false; } - } + } while (1); slu->was_interruptible = dev_priv->mm.interruptible; dev_priv->mm.interruptible = false; @@ -410,7 +414,7 @@ i915_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr return NOTIFY_DONE; /* Force everything onto the inactive lists */ - ret = i915_gem_wait_for_idle(dev_priv); + ret = i915_gem_wait_for_idle(dev_priv, I915_WAIT_LOCKED); if (ret) goto out; diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 66be299a1..59989e8 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -92,6 +92,7 @@ void i915_gem_stolen_remove_node(struct drm_i915_private *dev_priv, static unsigned long i915_stolen_to_physical(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; struct i915_ggtt *ggtt = &dev_priv->ggtt; struct resource *r; u32 base; @@ -111,33 +112,44 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev) if (INTEL_INFO(dev)->gen >= 3) { u32 bsm; - pci_read_config_dword(dev->pdev, INTEL_BSM, &bsm); + pci_read_config_dword(pdev, INTEL_BSM, &bsm); base = bsm & INTEL_BSM_MASK; } else if (IS_I865G(dev)) { + u32 tseg_size = 0; u16 toud = 0; + u8 tmp; - /* - * FIXME is the graphics stolen memory region - * always at TOUD? Ie. is it always the last - * one to be allocated by the BIOS? - */ - pci_bus_read_config_word(dev->pdev->bus, PCI_DEVFN(0, 0), + pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0), + I845_ESMRAMC, &tmp); + + if (tmp & TSEG_ENABLE) { + switch (tmp & I845_TSEG_SIZE_MASK) { + case I845_TSEG_SIZE_512K: + tseg_size = KB(512); + break; + case I845_TSEG_SIZE_1M: + tseg_size = MB(1); + break; + } + } + + pci_bus_read_config_word(pdev->bus, PCI_DEVFN(0, 0), I865_TOUD, &toud); - base = toud << 16; + base = (toud << 16) + tseg_size; } else if (IS_I85X(dev)) { u32 tseg_size = 0; u32 tom; u8 tmp; - pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0), + pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0), I85X_ESMRAMC, &tmp); if (tmp & TSEG_ENABLE) tseg_size = MB(1); - pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 1), + pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 1), I85X_DRB3, &tmp); tom = tmp * MB(32); @@ -147,7 +159,7 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev) u32 tom; u8 tmp; - pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0), + pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0), I845_ESMRAMC, &tmp); if (tmp & TSEG_ENABLE) { @@ -161,7 +173,7 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev) } } - pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0), + pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0), I830_DRB3, &tmp); tom = tmp * MB(32); @@ -171,7 +183,7 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev) u32 tom; u8 tmp; - pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0), + pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0), I830_ESMRAMC, &tmp); if (tmp & TSEG_ENABLE) { @@ -181,7 +193,7 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev) tseg_size = KB(512); } - pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0), + pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0), I830_DRB3, &tmp); tom = tmp * MB(32); @@ -685,7 +697,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, if (gtt_offset == I915_GTT_OFFSET_NONE) return obj; - vma = i915_gem_obj_lookup_or_create_vma(obj, &ggtt->base); + vma = i915_gem_obj_lookup_or_create_vma(obj, &ggtt->base, NULL); if (IS_ERR(vma)) { ret = PTR_ERR(vma); goto err; @@ -698,24 +710,25 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, */ vma->node.start = gtt_offset; vma->node.size = size; - if (drm_mm_initialized(&ggtt->base.mm)) { - ret = drm_mm_reserve_node(&ggtt->base.mm, &vma->node); - if (ret) { - DRM_DEBUG_KMS("failed to allocate stolen GTT space\n"); - goto err; - } - vma->bound |= GLOBAL_BIND; - __i915_vma_set_map_and_fenceable(vma); - list_add_tail(&vma->vm_link, &ggtt->base.inactive_list); + ret = drm_mm_reserve_node(&ggtt->base.mm, &vma->node); + if (ret) { + DRM_DEBUG_KMS("failed to allocate stolen GTT space\n"); + goto err; } + vma->pages = obj->pages; + vma->flags |= I915_VMA_GLOBAL_BIND; + __i915_vma_set_map_and_fenceable(vma); + list_move_tail(&vma->vm_link, &ggtt->base.inactive_list); + obj->bind_count++; + list_add_tail(&obj->global_list, &dev_priv->mm.bound_list); i915_gem_object_pin_pages(obj); return obj; err: - drm_gem_object_unreference(&obj->base); + i915_gem_object_put(obj); return NULL; } diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 8030199..a14b1e3 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -68,6 +68,9 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode) if (tiling_mode == I915_TILING_NONE) return true; + if (tiling_mode > I915_TILING_LAST) + return false; + if (IS_GEN2(dev) || (tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev))) tile_width = 128; @@ -113,36 +116,58 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode) return true; } -/* Is the current GTT allocation valid for the change in tiling? */ -static bool -i915_gem_object_fence_ok(struct drm_i915_gem_object *obj, int tiling_mode) +static bool i915_vma_fence_prepare(struct i915_vma *vma, int tiling_mode) { + struct drm_i915_private *dev_priv = to_i915(vma->vm->dev); u32 size; - if (tiling_mode == I915_TILING_NONE) - return true; - - if (INTEL_INFO(obj->base.dev)->gen >= 4) + if (!i915_vma_is_map_and_fenceable(vma)) return true; - if (IS_GEN3(obj->base.dev)) { - if (i915_gem_obj_ggtt_offset(obj) & ~I915_FENCE_START_MASK) + if (INTEL_GEN(dev_priv) == 3) { + if (vma->node.start & ~I915_FENCE_START_MASK) return false; } else { - if (i915_gem_obj_ggtt_offset(obj) & ~I830_FENCE_START_MASK) + if (vma->node.start & ~I830_FENCE_START_MASK) return false; } - size = i915_gem_get_gtt_size(obj->base.dev, obj->base.size, tiling_mode); - if (i915_gem_obj_ggtt_size(obj) != size) + size = i915_gem_get_ggtt_size(dev_priv, vma->size, tiling_mode); + if (vma->node.size < size) return false; - if (i915_gem_obj_ggtt_offset(obj) & (size - 1)) + if (vma->node.start & (size - 1)) return false; return true; } +/* Make the current GTT allocation valid for the change in tiling. */ +static int +i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj, int tiling_mode) +{ + struct drm_i915_private *dev_priv = to_i915(obj->base.dev); + struct i915_vma *vma; + int ret; + + if (tiling_mode == I915_TILING_NONE) + return 0; + + if (INTEL_GEN(dev_priv) >= 4) + return 0; + + list_for_each_entry(vma, &obj->vma_list, obj_link) { + if (i915_vma_fence_prepare(vma, tiling_mode)) + continue; + + ret = i915_vma_unbind(vma); + if (ret) + return ret; + } + + return 0; +} + /** * i915_gem_set_tiling - IOCTL handler to set tiling mode * @dev: DRM device @@ -164,15 +189,18 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, struct drm_i915_gem_set_tiling *args = data; struct drm_i915_private *dev_priv = to_i915(dev); struct drm_i915_gem_object *obj; - int ret = 0; + int err = 0; + + /* Make sure we don't cross-contaminate obj->tiling_and_stride */ + BUILD_BUG_ON(I915_TILING_LAST & STRIDE_MASK); - obj = to_intel_bo(drm_gem_object_lookup(file, args->handle)); - if (&obj->base == NULL) + obj = i915_gem_object_lookup(file, args->handle); + if (!obj) return -ENOENT; if (!i915_tiling_ok(dev, args->stride, obj->base.size, args->tiling_mode)) { - drm_gem_object_unreference_unlocked(&obj->base); + i915_gem_object_put_unlocked(obj); return -EINVAL; } @@ -180,7 +208,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, mutex_lock(&dev->struct_mutex); if (obj->pin_display || obj->framebuffer_references) { - ret = -EBUSY; + err = -EBUSY; goto err; } @@ -213,8 +241,8 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, } } - if (args->tiling_mode != obj->tiling_mode || - args->stride != obj->stride) { + if (args->tiling_mode != i915_gem_object_get_tiling(obj) || + args->stride != i915_gem_object_get_stride(obj)) { /* We need to rebind the object if its current allocation * no longer meets the alignment restrictions for its new * tiling mode. Otherwise we can just leave it alone, but @@ -227,34 +255,36 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, * has to also include the unfenced register the GPU uses * whilst executing a fenced command for an untiled object. */ - if (obj->map_and_fenceable && - !i915_gem_object_fence_ok(obj, args->tiling_mode)) - ret = i915_vma_unbind(i915_gem_obj_to_ggtt(obj)); - if (ret == 0) { + err = i915_gem_object_fence_prepare(obj, args->tiling_mode); + if (!err) { + struct i915_vma *vma; + if (obj->pages && obj->madv == I915_MADV_WILLNEED && dev_priv->quirks & QUIRK_PIN_SWIZZLED_PAGES) { if (args->tiling_mode == I915_TILING_NONE) i915_gem_object_unpin_pages(obj); - if (obj->tiling_mode == I915_TILING_NONE) + if (!i915_gem_object_is_tiled(obj)) i915_gem_object_pin_pages(obj); } - obj->fence_dirty = - obj->last_fenced_req || - obj->fence_reg != I915_FENCE_REG_NONE; + list_for_each_entry(vma, &obj->vma_list, obj_link) { + if (!vma->fence) + continue; - obj->tiling_mode = args->tiling_mode; - obj->stride = args->stride; + vma->fence->dirty = true; + } + obj->tiling_and_stride = + args->stride | args->tiling_mode; /* Force the fence to be reacquired for GTT access */ i915_gem_release_mmap(obj); } } /* we have to maintain this existing ABI... */ - args->stride = obj->stride; - args->tiling_mode = obj->tiling_mode; + args->stride = i915_gem_object_get_stride(obj); + args->tiling_mode = i915_gem_object_get_tiling(obj); /* Try to preallocate memory required to save swizzling on put-pages */ if (i915_gem_object_needs_bit17_swizzle(obj)) { @@ -268,12 +298,12 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, } err: - drm_gem_object_unreference(&obj->base); + i915_gem_object_put(obj); mutex_unlock(&dev->struct_mutex); intel_runtime_pm_put(dev_priv); - return ret; + return err; } /** @@ -297,14 +327,12 @@ i915_gem_get_tiling(struct drm_device *dev, void *data, struct drm_i915_private *dev_priv = to_i915(dev); struct drm_i915_gem_object *obj; - obj = to_intel_bo(drm_gem_object_lookup(file, args->handle)); - if (&obj->base == NULL) + obj = i915_gem_object_lookup(file, args->handle); + if (!obj) return -ENOENT; - mutex_lock(&dev->struct_mutex); - - args->tiling_mode = obj->tiling_mode; - switch (obj->tiling_mode) { + args->tiling_mode = READ_ONCE(obj->tiling_and_stride) & TILING_MASK; + switch (args->tiling_mode) { case I915_TILING_X: args->swizzle_mode = dev_priv->mm.bit_6_swizzle_x; break; @@ -328,8 +356,6 @@ i915_gem_get_tiling(struct drm_device *dev, void *data, if (args->swizzle_mode == I915_BIT_6_SWIZZLE_9_10_17) args->swizzle_mode = I915_BIT_6_SWIZZLE_9_10; - drm_gem_object_unreference(&obj->base); - mutex_unlock(&dev->struct_mutex); - + i915_gem_object_put_unlocked(obj); return 0; } diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c index 2314c88..e537930 100644 --- a/drivers/gpu/drm/i915/i915_gem_userptr.c +++ b/drivers/gpu/drm/i915/i915_gem_userptr.c @@ -63,33 +63,12 @@ struct i915_mmu_object { static void wait_rendering(struct drm_i915_gem_object *obj) { - struct drm_device *dev = obj->base.dev; - struct drm_i915_gem_request *requests[I915_NUM_ENGINES]; - int i, n; - - if (!obj->active) - return; - - n = 0; - for (i = 0; i < I915_NUM_ENGINES; i++) { - struct drm_i915_gem_request *req; - - req = obj->last_read_req[i]; - if (req == NULL) - continue; - - requests[n++] = i915_gem_request_reference(req); - } - - mutex_unlock(&dev->struct_mutex); - - for (i = 0; i < n; i++) - __i915_wait_request(requests[i], false, NULL, NULL); - - mutex_lock(&dev->struct_mutex); + unsigned long active = __I915_BO_ACTIVE(obj); + int idx; - for (i = 0; i < n; i++) - i915_gem_request_unreference(requests[i]); + for_each_active(active, idx) + i915_gem_active_wait_unlocked(&obj->last_read[idx], + 0, NULL, NULL); } static void cancel_userptr(struct work_struct *work) @@ -98,28 +77,19 @@ static void cancel_userptr(struct work_struct *work) struct drm_i915_gem_object *obj = mo->obj; struct drm_device *dev = obj->base.dev; + wait_rendering(obj); + mutex_lock(&dev->struct_mutex); /* Cancel any active worker and force us to re-evaluate gup */ obj->userptr.work = NULL; if (obj->pages != NULL) { - struct drm_i915_private *dev_priv = to_i915(dev); - struct i915_vma *vma, *tmp; - bool was_interruptible; - - wait_rendering(obj); - - was_interruptible = dev_priv->mm.interruptible; - dev_priv->mm.interruptible = false; - - list_for_each_entry_safe(vma, tmp, &obj->vma_list, obj_link) - WARN_ON(i915_vma_unbind(vma)); + /* We are inside a kthread context and can't be interrupted */ + WARN_ON(i915_gem_object_unbind(obj)); WARN_ON(i915_gem_object_put_pages(obj)); - - dev_priv->mm.interruptible = was_interruptible; } - drm_gem_object_unreference(&obj->base); + i915_gem_object_put(obj); mutex_unlock(&dev->struct_mutex); } @@ -572,12 +542,10 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work) } } obj->userptr.work = ERR_PTR(ret); - if (ret) - __i915_gem_userptr_set_active(obj, false); } obj->userptr.workers--; - drm_gem_object_unreference(&obj->base); + i915_gem_object_put(obj); mutex_unlock(&dev->struct_mutex); release_pages(pvec, pinned, 0); @@ -622,8 +590,7 @@ __i915_gem_userptr_get_pages_schedule(struct drm_i915_gem_object *obj, obj->userptr.work = &work->work; obj->userptr.workers++; - work->obj = obj; - drm_gem_object_reference(&obj->base); + work->obj = i915_gem_object_get(obj); work->task = current; get_task_struct(work->task); @@ -659,15 +626,14 @@ i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj) * to the vma (discard or cloning) which should prevent the more * egregious cases from causing harm. */ - if (IS_ERR(obj->userptr.work)) { - /* active flag will have been dropped already by the worker */ - ret = PTR_ERR(obj->userptr.work); - obj->userptr.work = NULL; - return ret; - } - if (obj->userptr.work) + + if (obj->userptr.work) { /* active flag should still be held for the pending work */ - return -EAGAIN; + if (IS_ERR(obj->userptr.work)) + return PTR_ERR(obj->userptr.work); + else + return -EAGAIN; + } /* Let the mmu-notifier know that we have begun and need cancellation */ ret = __i915_gem_userptr_set_active(obj, true); @@ -846,7 +812,7 @@ i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file ret = drm_gem_handle_create(file, &obj->base, &handle); /* drop reference from allocate - handle holds it now */ - drm_gem_object_unreference_unlocked(&obj->base); + i915_gem_object_put_unlocked(obj); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 9d73d22..334f15d 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -30,9 +30,9 @@ #include <generated/utsrelease.h> #include "i915_drv.h" -static const char *ring_str(int ring) +static const char *engine_str(int engine) { - switch (ring) { + switch (engine) { case RCS: return "render"; case VCS: return "bsd"; case BCS: return "blt"; @@ -42,16 +42,6 @@ static const char *ring_str(int ring) } } -static const char *pin_flag(int pinned) -{ - if (pinned > 0) - return " P"; - else if (pinned < 0) - return " p"; - else - return ""; -} - static const char *tiling_flag(int tiling) { switch (tiling) { @@ -189,7 +179,7 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m, { int i; - err_printf(m, " %s [%d]:\n", name, count); + err_printf(m, "%s [%d]:\n", name, count); while (count--) { err_printf(m, " %08x_%08x %8u %02x %02x [ ", @@ -202,13 +192,12 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m, err_printf(m, "%02x ", err->rseqno[i]); err_printf(m, "] %02x", err->wseqno); - err_puts(m, pin_flag(err->pinned)); err_puts(m, tiling_flag(err->tiling)); err_puts(m, dirty_flag(err->dirty)); err_puts(m, purgeable_flag(err->purgeable)); err_puts(m, err->userptr ? " userptr" : ""); - err_puts(m, err->ring != -1 ? " " : ""); - err_puts(m, ring_str(err->ring)); + err_puts(m, err->engine != -1 ? " " : ""); + err_puts(m, engine_str(err->engine)); err_puts(m, i915_cache_level_str(m->i915, err->cache_level)); if (err->name) @@ -221,7 +210,7 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m, } } -static const char *hangcheck_action_to_str(enum intel_ring_hangcheck_action a) +static const char *hangcheck_action_to_str(enum intel_engine_hangcheck_action a) { switch (a) { case HANGCHECK_IDLE: @@ -239,70 +228,74 @@ static const char *hangcheck_action_to_str(enum intel_ring_hangcheck_action a) return "unknown"; } -static void i915_ring_error_state(struct drm_i915_error_state_buf *m, - struct drm_device *dev, - struct drm_i915_error_state *error, - int ring_idx) +static void error_print_engine(struct drm_i915_error_state_buf *m, + struct drm_i915_error_engine *ee) { - struct drm_i915_error_ring *ring = &error->ring[ring_idx]; - - if (!ring->valid) - return; - - err_printf(m, "%s command stream:\n", ring_str(ring_idx)); - err_printf(m, " START: 0x%08x\n", ring->start); - err_printf(m, " HEAD: 0x%08x\n", ring->head); - err_printf(m, " TAIL: 0x%08x\n", ring->tail); - err_printf(m, " CTL: 0x%08x\n", ring->ctl); - err_printf(m, " HWS: 0x%08x\n", ring->hws); - err_printf(m, " ACTHD: 0x%08x %08x\n", (u32)(ring->acthd>>32), (u32)ring->acthd); - err_printf(m, " IPEIR: 0x%08x\n", ring->ipeir); - err_printf(m, " IPEHR: 0x%08x\n", ring->ipehr); - err_printf(m, " INSTDONE: 0x%08x\n", ring->instdone); - if (INTEL_INFO(dev)->gen >= 4) { - err_printf(m, " BBADDR: 0x%08x %08x\n", (u32)(ring->bbaddr>>32), (u32)ring->bbaddr); - err_printf(m, " BB_STATE: 0x%08x\n", ring->bbstate); - err_printf(m, " INSTPS: 0x%08x\n", ring->instps); + err_printf(m, "%s command stream:\n", engine_str(ee->engine_id)); + err_printf(m, " START: 0x%08x\n", ee->start); + err_printf(m, " HEAD: 0x%08x\n", ee->head); + err_printf(m, " TAIL: 0x%08x\n", ee->tail); + err_printf(m, " CTL: 0x%08x\n", ee->ctl); + err_printf(m, " MODE: 0x%08x\n", ee->mode); + err_printf(m, " HWS: 0x%08x\n", ee->hws); + err_printf(m, " ACTHD: 0x%08x %08x\n", + (u32)(ee->acthd>>32), (u32)ee->acthd); + err_printf(m, " IPEIR: 0x%08x\n", ee->ipeir); + err_printf(m, " IPEHR: 0x%08x\n", ee->ipehr); + err_printf(m, " INSTDONE: 0x%08x\n", ee->instdone); + if (ee->batchbuffer) { + u64 start = ee->batchbuffer->gtt_offset; + u64 end = start + ee->batchbuffer->gtt_size; + + err_printf(m, " batch: [0x%08x_%08x, 0x%08x_%08x]\n", + upper_32_bits(start), lower_32_bits(start), + upper_32_bits(end), lower_32_bits(end)); } - err_printf(m, " INSTPM: 0x%08x\n", ring->instpm); - err_printf(m, " FADDR: 0x%08x %08x\n", upper_32_bits(ring->faddr), - lower_32_bits(ring->faddr)); - if (INTEL_INFO(dev)->gen >= 6) { - err_printf(m, " RC PSMI: 0x%08x\n", ring->rc_psmi); - err_printf(m, " FAULT_REG: 0x%08x\n", ring->fault_reg); + if (INTEL_GEN(m->i915) >= 4) { + err_printf(m, " BBADDR: 0x%08x_%08x\n", + (u32)(ee->bbaddr>>32), (u32)ee->bbaddr); + err_printf(m, " BB_STATE: 0x%08x\n", ee->bbstate); + err_printf(m, " INSTPS: 0x%08x\n", ee->instps); + } + err_printf(m, " INSTPM: 0x%08x\n", ee->instpm); + err_printf(m, " FADDR: 0x%08x %08x\n", upper_32_bits(ee->faddr), + lower_32_bits(ee->faddr)); + if (INTEL_GEN(m->i915) >= 6) { + err_printf(m, " RC PSMI: 0x%08x\n", ee->rc_psmi); + err_printf(m, " FAULT_REG: 0x%08x\n", ee->fault_reg); err_printf(m, " SYNC_0: 0x%08x [last synced 0x%08x]\n", - ring->semaphore_mboxes[0], - ring->semaphore_seqno[0]); + ee->semaphore_mboxes[0], + ee->semaphore_seqno[0]); err_printf(m, " SYNC_1: 0x%08x [last synced 0x%08x]\n", - ring->semaphore_mboxes[1], - ring->semaphore_seqno[1]); - if (HAS_VEBOX(dev)) { + ee->semaphore_mboxes[1], + ee->semaphore_seqno[1]); + if (HAS_VEBOX(m->i915)) { err_printf(m, " SYNC_2: 0x%08x [last synced 0x%08x]\n", - ring->semaphore_mboxes[2], - ring->semaphore_seqno[2]); + ee->semaphore_mboxes[2], + ee->semaphore_seqno[2]); } } - if (USES_PPGTT(dev)) { - err_printf(m, " GFX_MODE: 0x%08x\n", ring->vm_info.gfx_mode); + if (USES_PPGTT(m->i915)) { + err_printf(m, " GFX_MODE: 0x%08x\n", ee->vm_info.gfx_mode); - if (INTEL_INFO(dev)->gen >= 8) { + if (INTEL_GEN(m->i915) >= 8) { int i; for (i = 0; i < 4; i++) err_printf(m, " PDP%d: 0x%016llx\n", - i, ring->vm_info.pdp[i]); + i, ee->vm_info.pdp[i]); } else { err_printf(m, " PP_DIR_BASE: 0x%08x\n", - ring->vm_info.pp_dir_base); + ee->vm_info.pp_dir_base); } } - err_printf(m, " seqno: 0x%08x\n", ring->seqno); - err_printf(m, " last_seqno: 0x%08x\n", ring->last_seqno); - err_printf(m, " waiting: %s\n", yesno(ring->waiting)); - err_printf(m, " ring->head: 0x%08x\n", ring->cpu_ring_head); - err_printf(m, " ring->tail: 0x%08x\n", ring->cpu_ring_tail); + err_printf(m, " seqno: 0x%08x\n", ee->seqno); + err_printf(m, " last_seqno: 0x%08x\n", ee->last_seqno); + err_printf(m, " waiting: %s\n", yesno(ee->waiting)); + err_printf(m, " ring->head: 0x%08x\n", ee->cpu_ring_head); + err_printf(m, " ring->tail: 0x%08x\n", ee->cpu_ring_tail); err_printf(m, " hangcheck: %s [%d]\n", - hangcheck_action_to_str(ring->hangcheck_action), - ring->hangcheck_score); + hangcheck_action_to_str(ee->hangcheck_action), + ee->hangcheck_score); } void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...) @@ -328,11 +321,22 @@ static void print_error_obj(struct drm_i915_error_state_buf *m, } } +static void err_print_capabilities(struct drm_i915_error_state_buf *m, + const struct intel_device_info *info) +{ +#define PRINT_FLAG(x) err_printf(m, #x ": %s\n", yesno(info->x)) +#define SEP_SEMICOLON ; + DEV_INFO_FOR_EACH_FLAG(PRINT_FLAG, SEP_SEMICOLON); +#undef PRINT_FLAG +#undef SEP_SEMICOLON +} + int i915_error_state_to_str(struct drm_i915_error_state_buf *m, const struct i915_error_state_file_priv *error_priv) { struct drm_device *dev = error_priv->dev; struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; struct drm_i915_error_state *error = error_priv->error; struct drm_i915_error_object *obj; int i, j, offset, elt; @@ -347,27 +351,28 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, err_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec, error->time.tv_usec); err_printf(m, "Kernel: " UTS_RELEASE "\n"); + err_print_capabilities(m, &error->device_info); max_hangcheck_score = 0; - for (i = 0; i < ARRAY_SIZE(error->ring); i++) { - if (error->ring[i].hangcheck_score > max_hangcheck_score) - max_hangcheck_score = error->ring[i].hangcheck_score; + for (i = 0; i < ARRAY_SIZE(error->engine); i++) { + if (error->engine[i].hangcheck_score > max_hangcheck_score) + max_hangcheck_score = error->engine[i].hangcheck_score; } - for (i = 0; i < ARRAY_SIZE(error->ring); i++) { - if (error->ring[i].hangcheck_score == max_hangcheck_score && - error->ring[i].pid != -1) { + for (i = 0; i < ARRAY_SIZE(error->engine); i++) { + if (error->engine[i].hangcheck_score == max_hangcheck_score && + error->engine[i].pid != -1) { err_printf(m, "Active process (on ring %s): %s [%d]\n", - ring_str(i), - error->ring[i].comm, - error->ring[i].pid); + engine_str(i), + error->engine[i].comm, + error->engine[i].pid); } } err_printf(m, "Reset count: %u\n", error->reset_count); err_printf(m, "Suspend count: %u\n", error->suspend_count); - err_printf(m, "PCI ID: 0x%04x\n", dev->pdev->device); - err_printf(m, "PCI Revision: 0x%02x\n", dev->pdev->revision); + err_printf(m, "PCI ID: 0x%04x\n", pdev->device); + err_printf(m, "PCI Revision: 0x%02x\n", pdev->revision); err_printf(m, "PCI Subsystem: %04x:%04x\n", - dev->pdev->subsystem_vendor, - dev->pdev->subsystem_device); + pdev->subsystem_vendor, + pdev->subsystem_device); err_printf(m, "IOMMU enabled?: %d\n", error->iommu); if (HAS_CSR(dev)) { @@ -414,36 +419,55 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, if (IS_GEN7(dev)) err_printf(m, "ERR_INT: 0x%08x\n", error->err_int); - for (i = 0; i < ARRAY_SIZE(error->ring); i++) - i915_ring_error_state(m, dev, error, i); + for (i = 0; i < ARRAY_SIZE(error->engine); i++) { + if (error->engine[i].engine_id != -1) + error_print_engine(m, &error->engine[i]); + } + + for (i = 0; i < ARRAY_SIZE(error->active_vm); i++) { + char buf[128]; + int len, first = 1; + + if (!error->active_vm[i]) + break; - for (i = 0; i < error->vm_count; i++) { - err_printf(m, "vm[%d]\n", i); + len = scnprintf(buf, sizeof(buf), "Active ("); + for (j = 0; j < ARRAY_SIZE(error->engine); j++) { + if (error->engine[j].vm != error->active_vm[i]) + continue; - print_error_buffers(m, "Active", + len += scnprintf(buf + len, sizeof(buf), "%s%s", + first ? "" : ", ", + dev_priv->engine[j].name); + first = 0; + } + scnprintf(buf + len, sizeof(buf), ")"); + print_error_buffers(m, buf, error->active_bo[i], error->active_bo_count[i]); - - print_error_buffers(m, "Pinned", - error->pinned_bo[i], - error->pinned_bo_count[i]); } - for (i = 0; i < ARRAY_SIZE(error->ring); i++) { - obj = error->ring[i].batchbuffer; + print_error_buffers(m, "Pinned (global)", + error->pinned_bo, + error->pinned_bo_count); + + for (i = 0; i < ARRAY_SIZE(error->engine); i++) { + struct drm_i915_error_engine *ee = &error->engine[i]; + + obj = ee->batchbuffer; if (obj) { err_puts(m, dev_priv->engine[i].name); - if (error->ring[i].pid != -1) + if (ee->pid != -1) err_printf(m, " (submitted by %s [%d])", - error->ring[i].comm, - error->ring[i].pid); + ee->comm, + ee->pid); err_printf(m, " --- gtt_offset = 0x%08x %08x\n", upper_32_bits(obj->gtt_offset), lower_32_bits(obj->gtt_offset)); print_error_obj(m, obj); } - obj = error->ring[i].wa_batchbuffer; + obj = ee->wa_batchbuffer; if (obj) { err_printf(m, "%s (w/a) --- gtt_offset = 0x%08x\n", dev_priv->engine[i].name, @@ -451,38 +475,43 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, print_error_obj(m, obj); } - if (error->ring[i].num_requests) { + if (ee->num_requests) { err_printf(m, "%s --- %d requests\n", dev_priv->engine[i].name, - error->ring[i].num_requests); - for (j = 0; j < error->ring[i].num_requests; j++) { - err_printf(m, " seqno 0x%08x, emitted %ld, tail 0x%08x\n", - error->ring[i].requests[j].seqno, - error->ring[i].requests[j].jiffies, - error->ring[i].requests[j].tail); + ee->num_requests); + for (j = 0; j < ee->num_requests; j++) { + err_printf(m, " pid %d, seqno 0x%08x, emitted %ld, head 0x%08x, tail 0x%08x\n", + ee->requests[j].pid, + ee->requests[j].seqno, + ee->requests[j].jiffies, + ee->requests[j].head, + ee->requests[j].tail); } } - if (error->ring[i].num_waiters) { + if (IS_ERR(ee->waiters)) { + err_printf(m, "%s --- ? waiters [unable to acquire spinlock]\n", + dev_priv->engine[i].name); + } else if (ee->num_waiters) { err_printf(m, "%s --- %d waiters\n", dev_priv->engine[i].name, - error->ring[i].num_waiters); - for (j = 0; j < error->ring[i].num_waiters; j++) { + ee->num_waiters); + for (j = 0; j < ee->num_waiters; j++) { err_printf(m, " seqno 0x%08x for %s [%d]\n", - error->ring[i].waiters[j].seqno, - error->ring[i].waiters[j].comm, - error->ring[i].waiters[j].pid); + ee->waiters[j].seqno, + ee->waiters[j].comm, + ee->waiters[j].pid); } } - if ((obj = error->ring[i].ringbuffer)) { + if ((obj = ee->ringbuffer)) { err_printf(m, "%s --- ringbuffer = 0x%08x\n", dev_priv->engine[i].name, lower_32_bits(obj->gtt_offset)); print_error_obj(m, obj); } - if ((obj = error->ring[i].hws_page)) { + if ((obj = ee->hws_page)) { u64 hws_offset = obj->gtt_offset; u32 *hws_page = &obj->pages[0][0]; @@ -504,7 +533,7 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, } } - obj = error->ring[i].wa_ctx; + obj = ee->wa_ctx; if (obj) { u64 wa_ctx_offset = obj->gtt_offset; u32 *wa_ctx_page = &obj->pages[0][0]; @@ -526,7 +555,7 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, } } - if ((obj = error->ring[i].ctx)) { + if ((obj = ee->ctx)) { err_printf(m, "%s --- HW Context = 0x%08x\n", dev_priv->engine[i].name, lower_32_bits(obj->gtt_offset)); @@ -534,7 +563,7 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, } } - if ((obj = error->semaphore_obj)) { + if ((obj = error->semaphore)) { err_printf(m, "Semaphore page = 0x%08x\n", lower_32_bits(obj->gtt_offset)); for (elt = 0; elt < PAGE_SIZE/16; elt += 4) { @@ -611,26 +640,27 @@ static void i915_error_state_free(struct kref *error_ref) typeof(*error), ref); int i; - for (i = 0; i < ARRAY_SIZE(error->ring); i++) { - i915_error_object_free(error->ring[i].batchbuffer); - i915_error_object_free(error->ring[i].wa_batchbuffer); - i915_error_object_free(error->ring[i].ringbuffer); - i915_error_object_free(error->ring[i].hws_page); - i915_error_object_free(error->ring[i].ctx); - i915_error_object_free(error->ring[i].wa_ctx); - kfree(error->ring[i].requests); - kfree(error->ring[i].waiters); + for (i = 0; i < ARRAY_SIZE(error->engine); i++) { + struct drm_i915_error_engine *ee = &error->engine[i]; + + i915_error_object_free(ee->batchbuffer); + i915_error_object_free(ee->wa_batchbuffer); + i915_error_object_free(ee->ringbuffer); + i915_error_object_free(ee->hws_page); + i915_error_object_free(ee->ctx); + i915_error_object_free(ee->wa_ctx); + + kfree(ee->requests); + if (!IS_ERR_OR_NULL(ee->waiters)) + kfree(ee->waiters); } - i915_error_object_free(error->semaphore_obj); + i915_error_object_free(error->semaphore); - for (i = 0; i < error->vm_count; i++) + for (i = 0; i < ARRAY_SIZE(error->active_bo); i++) kfree(error->active_bo[i]); - - kfree(error->active_bo); - kfree(error->active_bo_count); kfree(error->pinned_bo); - kfree(error->pinned_bo_count); + kfree(error->overlay); kfree(error->display); kfree(error); @@ -638,46 +668,45 @@ static void i915_error_state_free(struct kref *error_ref) static struct drm_i915_error_object * i915_error_object_create(struct drm_i915_private *dev_priv, - struct drm_i915_gem_object *src, - struct i915_address_space *vm) + struct i915_vma *vma) { struct i915_ggtt *ggtt = &dev_priv->ggtt; + struct drm_i915_gem_object *src; struct drm_i915_error_object *dst; - struct i915_vma *vma = NULL; int num_pages; bool use_ggtt; int i = 0; u64 reloc_offset; - if (src == NULL || src->pages == NULL) + if (!vma) + return NULL; + + src = vma->obj; + if (!src->pages) return NULL; num_pages = src->base.size >> PAGE_SHIFT; dst = kmalloc(sizeof(*dst) + num_pages * sizeof(u32 *), GFP_ATOMIC); - if (dst == NULL) + if (!dst) return NULL; - if (i915_gem_obj_bound(src, vm)) - dst->gtt_offset = i915_gem_obj_offset(src, vm); - else - dst->gtt_offset = -1; + dst->gtt_offset = vma->node.start; + dst->gtt_size = vma->node.size; reloc_offset = dst->gtt_offset; - if (i915_is_ggtt(vm)) - vma = i915_gem_obj_to_ggtt(src); use_ggtt = (src->cache_level == I915_CACHE_NONE && - vma && (vma->bound & GLOBAL_BIND) && + (vma->flags & I915_VMA_GLOBAL_BIND) && reloc_offset + num_pages * PAGE_SIZE <= ggtt->mappable_end); /* Cannot access stolen address directly, try to use the aperture */ if (src->stolen) { use_ggtt = true; - if (!(vma && vma->bound & GLOBAL_BIND)) + if (!(vma->flags & I915_VMA_GLOBAL_BIND)) goto unwind; - reloc_offset = i915_gem_obj_ggtt_offset(src); + reloc_offset = vma->node.start; if (reloc_offset + num_pages * PAGE_SIZE > ggtt->mappable_end) goto unwind; } @@ -705,7 +734,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv, * captures what the GPU read. */ - s = io_mapping_map_atomic_wc(ggtt->mappable, + s = io_mapping_map_atomic_wc(&ggtt->mappable, reloc_offset); memcpy_fromio(d, s, PAGE_SIZE); io_mapping_unmap_atomic(s); @@ -737,8 +766,24 @@ unwind: kfree(dst); return NULL; } -#define i915_error_ggtt_object_create(dev_priv, src) \ - i915_error_object_create((dev_priv), (src), &(dev_priv)->ggtt.base) + +/* The error capture is special as tries to run underneath the normal + * locking rules - so we use the raw version of the i915_gem_active lookup. + */ +static inline uint32_t +__active_get_seqno(struct i915_gem_active *active) +{ + return i915_gem_request_get_seqno(__i915_gem_active_peek(active)); +} + +static inline int +__active_get_engine_id(struct i915_gem_active *active) +{ + struct intel_engine_cs *engine; + + engine = i915_gem_request_get_engine(__i915_gem_active_peek(active)); + return engine ? engine->id : -1; +} static void capture_bo(struct drm_i915_error_buffer *err, struct i915_vma *vma) @@ -748,32 +793,34 @@ static void capture_bo(struct drm_i915_error_buffer *err, err->size = obj->base.size; err->name = obj->base.name; + for (i = 0; i < I915_NUM_ENGINES; i++) - err->rseqno[i] = i915_gem_request_get_seqno(obj->last_read_req[i]); - err->wseqno = i915_gem_request_get_seqno(obj->last_write_req); + err->rseqno[i] = __active_get_seqno(&obj->last_read[i]); + err->wseqno = __active_get_seqno(&obj->last_write); + err->engine = __active_get_engine_id(&obj->last_write); + err->gtt_offset = vma->node.start; err->read_domains = obj->base.read_domains; err->write_domain = obj->base.write_domain; - err->fence_reg = obj->fence_reg; - err->pinned = 0; - if (i915_gem_obj_is_pinned(obj)) - err->pinned = 1; - err->tiling = obj->tiling_mode; + err->fence_reg = vma->fence ? vma->fence->id : -1; + err->tiling = i915_gem_object_get_tiling(obj); err->dirty = obj->dirty; err->purgeable = obj->madv != I915_MADV_WILLNEED; err->userptr = obj->userptr.mm != NULL; - err->ring = obj->last_write_req ? - i915_gem_request_get_engine(obj->last_write_req)->id : -1; err->cache_level = obj->cache_level; } -static u32 capture_active_bo(struct drm_i915_error_buffer *err, - int count, struct list_head *head) +static u32 capture_error_bo(struct drm_i915_error_buffer *err, + int count, struct list_head *head, + bool pinned_only) { struct i915_vma *vma; int i = 0; list_for_each_entry(vma, head, vm_link) { + if (pinned_only && !i915_vma_is_pinned(vma)) + continue; + capture_bo(err++, vma); if (++i == count) break; @@ -782,28 +829,6 @@ static u32 capture_active_bo(struct drm_i915_error_buffer *err, return i; } -static u32 capture_pinned_bo(struct drm_i915_error_buffer *err, - int count, struct list_head *head, - struct i915_address_space *vm) -{ - struct drm_i915_gem_object *obj; - struct drm_i915_error_buffer * const first = err; - struct drm_i915_error_buffer * const last = err + count; - - list_for_each_entry(obj, head, global_list) { - struct i915_vma *vma; - - if (err == last) - break; - - list_for_each_entry(vma, &obj->vma_list, obj_link) - if (vma->vm == vm && vma->pin_count > 0) - capture_bo(err++, vma); - } - - return err - first; -} - /* Generate a semi-unique error code. The code is not meant to have meaning, The * code's only purpose is to try to prevent false duplicated bug reports by * grossly estimating a GPU error state. @@ -815,7 +840,7 @@ static u32 capture_pinned_bo(struct drm_i915_error_buffer *err, */ static uint32_t i915_error_generate_code(struct drm_i915_private *dev_priv, struct drm_i915_error_state *error, - int *ring_id) + int *engine_id) { uint32_t error_code = 0; int i; @@ -826,11 +851,11 @@ static uint32_t i915_error_generate_code(struct drm_i915_private *dev_priv, * strictly a client bug. Use instdone to differentiate those some. */ for (i = 0; i < I915_NUM_ENGINES; i++) { - if (error->ring[i].hangcheck_action == HANGCHECK_HUNG) { - if (ring_id) - *ring_id = i; + if (error->engine[i].hangcheck_action == HANGCHECK_HUNG) { + if (engine_id) + *engine_id = i; - return error->ring[i].ipehr ^ error->ring[i].instdone; + return error->engine[i].ipehr ^ error->engine[i].instdone; } } @@ -855,22 +880,17 @@ static void i915_gem_record_fences(struct drm_i915_private *dev_priv, } -static void gen8_record_semaphore_state(struct drm_i915_private *dev_priv, - struct drm_i915_error_state *error, +static void gen8_record_semaphore_state(struct drm_i915_error_state *error, struct intel_engine_cs *engine, - struct drm_i915_error_ring *ering) + struct drm_i915_error_engine *ee) { + struct drm_i915_private *dev_priv = engine->i915; struct intel_engine_cs *to; enum intel_engine_id id; - if (!i915_semaphore_is_enabled(dev_priv)) + if (!error->semaphore) return; - if (!error->semaphore_obj) - error->semaphore_obj = - i915_error_ggtt_object_create(dev_priv, - dev_priv->semaphore_obj); - for_each_engine_id(to, dev_priv, id) { int idx; u16 signal_offset; @@ -879,44 +899,52 @@ static void gen8_record_semaphore_state(struct drm_i915_private *dev_priv, if (engine == to) continue; - signal_offset = (GEN8_SIGNAL_OFFSET(engine, id) & (PAGE_SIZE - 1)) - / 4; - tmp = error->semaphore_obj->pages[0]; - idx = intel_ring_sync_index(engine, to); + signal_offset = + (GEN8_SIGNAL_OFFSET(engine, id) & (PAGE_SIZE - 1)) / 4; + tmp = error->semaphore->pages[0]; + idx = intel_engine_sync_index(engine, to); - ering->semaphore_mboxes[idx] = tmp[signal_offset]; - ering->semaphore_seqno[idx] = engine->semaphore.sync_seqno[idx]; + ee->semaphore_mboxes[idx] = tmp[signal_offset]; + ee->semaphore_seqno[idx] = engine->semaphore.sync_seqno[idx]; } } -static void gen6_record_semaphore_state(struct drm_i915_private *dev_priv, - struct intel_engine_cs *engine, - struct drm_i915_error_ring *ering) +static void gen6_record_semaphore_state(struct intel_engine_cs *engine, + struct drm_i915_error_engine *ee) { - ering->semaphore_mboxes[0] = I915_READ(RING_SYNC_0(engine->mmio_base)); - ering->semaphore_mboxes[1] = I915_READ(RING_SYNC_1(engine->mmio_base)); - ering->semaphore_seqno[0] = engine->semaphore.sync_seqno[0]; - ering->semaphore_seqno[1] = engine->semaphore.sync_seqno[1]; + struct drm_i915_private *dev_priv = engine->i915; + + ee->semaphore_mboxes[0] = I915_READ(RING_SYNC_0(engine->mmio_base)); + ee->semaphore_mboxes[1] = I915_READ(RING_SYNC_1(engine->mmio_base)); + ee->semaphore_seqno[0] = engine->semaphore.sync_seqno[0]; + ee->semaphore_seqno[1] = engine->semaphore.sync_seqno[1]; if (HAS_VEBOX(dev_priv)) { - ering->semaphore_mboxes[2] = + ee->semaphore_mboxes[2] = I915_READ(RING_SYNC_2(engine->mmio_base)); - ering->semaphore_seqno[2] = engine->semaphore.sync_seqno[2]; + ee->semaphore_seqno[2] = engine->semaphore.sync_seqno[2]; } } -static void engine_record_waiters(struct intel_engine_cs *engine, - struct drm_i915_error_ring *ering) +static void error_record_engine_waiters(struct intel_engine_cs *engine, + struct drm_i915_error_engine *ee) { struct intel_breadcrumbs *b = &engine->breadcrumbs; struct drm_i915_error_waiter *waiter; struct rb_node *rb; int count; - ering->num_waiters = 0; - ering->waiters = NULL; + ee->num_waiters = 0; + ee->waiters = NULL; + + if (RB_EMPTY_ROOT(&b->waiters)) + return; + + if (!spin_trylock(&b->lock)) { + ee->waiters = ERR_PTR(-EDEADLK); + return; + } - spin_lock(&b->lock); count = 0; for (rb = rb_first(&b->waiters); rb != NULL; rb = rb_next(rb)) count++; @@ -930,9 +958,13 @@ static void engine_record_waiters(struct intel_engine_cs *engine, if (!waiter) return; - ering->waiters = waiter; + if (!spin_trylock(&b->lock)) { + kfree(waiter); + ee->waiters = ERR_PTR(-EDEADLK); + return; + } - spin_lock(&b->lock); + ee->waiters = waiter; for (rb = rb_first(&b->waiters); rb; rb = rb_next(rb)) { struct intel_wait *w = container_of(rb, typeof(*w), node); @@ -941,57 +973,59 @@ static void engine_record_waiters(struct intel_engine_cs *engine, waiter->seqno = w->seqno; waiter++; - if (++ering->num_waiters == count) + if (++ee->num_waiters == count) break; } spin_unlock(&b->lock); } -static void i915_record_ring_state(struct drm_i915_private *dev_priv, - struct drm_i915_error_state *error, - struct intel_engine_cs *engine, - struct drm_i915_error_ring *ering) +static void error_record_engine_registers(struct drm_i915_error_state *error, + struct intel_engine_cs *engine, + struct drm_i915_error_engine *ee) { + struct drm_i915_private *dev_priv = engine->i915; + if (INTEL_GEN(dev_priv) >= 6) { - ering->rc_psmi = I915_READ(RING_PSMI_CTL(engine->mmio_base)); - ering->fault_reg = I915_READ(RING_FAULT_REG(engine)); + ee->rc_psmi = I915_READ(RING_PSMI_CTL(engine->mmio_base)); + ee->fault_reg = I915_READ(RING_FAULT_REG(engine)); if (INTEL_GEN(dev_priv) >= 8) - gen8_record_semaphore_state(dev_priv, error, engine, - ering); + gen8_record_semaphore_state(error, engine, ee); else - gen6_record_semaphore_state(dev_priv, engine, ering); + gen6_record_semaphore_state(engine, ee); } if (INTEL_GEN(dev_priv) >= 4) { - ering->faddr = I915_READ(RING_DMA_FADD(engine->mmio_base)); - ering->ipeir = I915_READ(RING_IPEIR(engine->mmio_base)); - ering->ipehr = I915_READ(RING_IPEHR(engine->mmio_base)); - ering->instdone = I915_READ(RING_INSTDONE(engine->mmio_base)); - ering->instps = I915_READ(RING_INSTPS(engine->mmio_base)); - ering->bbaddr = I915_READ(RING_BBADDR(engine->mmio_base)); + ee->faddr = I915_READ(RING_DMA_FADD(engine->mmio_base)); + ee->ipeir = I915_READ(RING_IPEIR(engine->mmio_base)); + ee->ipehr = I915_READ(RING_IPEHR(engine->mmio_base)); + ee->instdone = I915_READ(RING_INSTDONE(engine->mmio_base)); + ee->instps = I915_READ(RING_INSTPS(engine->mmio_base)); + ee->bbaddr = I915_READ(RING_BBADDR(engine->mmio_base)); if (INTEL_GEN(dev_priv) >= 8) { - ering->faddr |= (u64) I915_READ(RING_DMA_FADD_UDW(engine->mmio_base)) << 32; - ering->bbaddr |= (u64) I915_READ(RING_BBADDR_UDW(engine->mmio_base)) << 32; + ee->faddr |= (u64) I915_READ(RING_DMA_FADD_UDW(engine->mmio_base)) << 32; + ee->bbaddr |= (u64) I915_READ(RING_BBADDR_UDW(engine->mmio_base)) << 32; } - ering->bbstate = I915_READ(RING_BBSTATE(engine->mmio_base)); + ee->bbstate = I915_READ(RING_BBSTATE(engine->mmio_base)); } else { - ering->faddr = I915_READ(DMA_FADD_I8XX); - ering->ipeir = I915_READ(IPEIR); - ering->ipehr = I915_READ(IPEHR); - ering->instdone = I915_READ(GEN2_INSTDONE); + ee->faddr = I915_READ(DMA_FADD_I8XX); + ee->ipeir = I915_READ(IPEIR); + ee->ipehr = I915_READ(IPEHR); + ee->instdone = I915_READ(GEN2_INSTDONE); } - ering->waiting = intel_engine_has_waiter(engine); - ering->instpm = I915_READ(RING_INSTPM(engine->mmio_base)); - ering->acthd = intel_ring_get_active_head(engine); - ering->seqno = intel_engine_get_seqno(engine); - ering->last_seqno = engine->last_submitted_seqno; - ering->start = I915_READ_START(engine); - ering->head = I915_READ_HEAD(engine); - ering->tail = I915_READ_TAIL(engine); - ering->ctl = I915_READ_CTL(engine); - - if (I915_NEED_GFX_HWS(dev_priv)) { + ee->waiting = intel_engine_has_waiter(engine); + ee->instpm = I915_READ(RING_INSTPM(engine->mmio_base)); + ee->acthd = intel_engine_get_active_head(engine); + ee->seqno = intel_engine_get_seqno(engine); + ee->last_seqno = engine->last_submitted_seqno; + ee->start = I915_READ_START(engine); + ee->head = I915_READ_HEAD(engine); + ee->tail = I915_READ_TAIL(engine); + ee->ctl = I915_READ_CTL(engine); + if (INTEL_GEN(dev_priv) > 2) + ee->mode = I915_READ_MODE(engine); + + if (!HWS_NEEDS_PHYSICAL(dev_priv)) { i915_reg_t mmio; if (IS_GEN7(dev_priv)) { @@ -1017,107 +1051,150 @@ static void i915_record_ring_state(struct drm_i915_private *dev_priv, mmio = RING_HWS_PGA(engine->mmio_base); } - ering->hws = I915_READ(mmio); + ee->hws = I915_READ(mmio); } - ering->hangcheck_score = engine->hangcheck.score; - ering->hangcheck_action = engine->hangcheck.action; + ee->hangcheck_score = engine->hangcheck.score; + ee->hangcheck_action = engine->hangcheck.action; if (USES_PPGTT(dev_priv)) { int i; - ering->vm_info.gfx_mode = I915_READ(RING_MODE_GEN7(engine)); + ee->vm_info.gfx_mode = I915_READ(RING_MODE_GEN7(engine)); if (IS_GEN6(dev_priv)) - ering->vm_info.pp_dir_base = + ee->vm_info.pp_dir_base = I915_READ(RING_PP_DIR_BASE_READ(engine)); else if (IS_GEN7(dev_priv)) - ering->vm_info.pp_dir_base = + ee->vm_info.pp_dir_base = I915_READ(RING_PP_DIR_BASE(engine)); else if (INTEL_GEN(dev_priv) >= 8) for (i = 0; i < 4; i++) { - ering->vm_info.pdp[i] = + ee->vm_info.pdp[i] = I915_READ(GEN8_RING_PDP_UDW(engine, i)); - ering->vm_info.pdp[i] <<= 32; - ering->vm_info.pdp[i] |= + ee->vm_info.pdp[i] <<= 32; + ee->vm_info.pdp[i] |= I915_READ(GEN8_RING_PDP_LDW(engine, i)); } } } - -static void i915_gem_record_active_context(struct intel_engine_cs *engine, - struct drm_i915_error_state *error, - struct drm_i915_error_ring *ering) +static void engine_record_requests(struct intel_engine_cs *engine, + struct drm_i915_gem_request *first, + struct drm_i915_error_engine *ee) { - struct drm_i915_private *dev_priv = engine->i915; - struct drm_i915_gem_object *obj; + struct drm_i915_gem_request *request; + int count; - /* Currently render ring is the only HW context user */ - if (engine->id != RCS || !error->ccid) + count = 0; + request = first; + list_for_each_entry_from(request, &engine->request_list, link) + count++; + if (!count) return; - list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) { - if (!i915_gem_obj_ggtt_bound(obj)) - continue; + ee->requests = kcalloc(count, sizeof(*ee->requests), GFP_ATOMIC); + if (!ee->requests) + return; - if ((error->ccid & PAGE_MASK) == i915_gem_obj_ggtt_offset(obj)) { - ering->ctx = i915_error_ggtt_object_create(dev_priv, obj); + ee->num_requests = count; + + count = 0; + request = first; + list_for_each_entry_from(request, &engine->request_list, link) { + struct drm_i915_error_request *erq; + + if (count >= ee->num_requests) { + /* + * If the ring request list was changed in + * between the point where the error request + * list was created and dimensioned and this + * point then just exit early to avoid crashes. + * + * We don't need to communicate that the + * request list changed state during error + * state capture and that the error state is + * slightly incorrect as a consequence since we + * are typically only interested in the request + * list state at the point of error state + * capture, not in any changes happening during + * the capture. + */ break; } + + erq = &ee->requests[count++]; + erq->seqno = request->fence.seqno; + erq->jiffies = request->emitted_jiffies; + erq->head = request->head; + erq->tail = request->tail; + + rcu_read_lock(); + erq->pid = request->ctx->pid ? pid_nr(request->ctx->pid) : 0; + rcu_read_unlock(); } + ee->num_requests = count; } static void i915_gem_record_rings(struct drm_i915_private *dev_priv, struct drm_i915_error_state *error) { struct i915_ggtt *ggtt = &dev_priv->ggtt; - struct drm_i915_gem_request *request; - int i, count; + int i; + + error->semaphore = + i915_error_object_create(dev_priv, dev_priv->semaphore); for (i = 0; i < I915_NUM_ENGINES; i++) { struct intel_engine_cs *engine = &dev_priv->engine[i]; + struct drm_i915_error_engine *ee = &error->engine[i]; + struct drm_i915_gem_request *request; - error->ring[i].pid = -1; + ee->pid = -1; + ee->engine_id = -1; if (!intel_engine_initialized(engine)) continue; - error->ring[i].valid = true; + ee->engine_id = i; - i915_record_ring_state(dev_priv, error, engine, &error->ring[i]); - engine_record_waiters(engine, &error->ring[i]); + error_record_engine_registers(error, engine, ee); + error_record_engine_waiters(engine, ee); request = i915_gem_find_active_request(engine); if (request) { - struct i915_address_space *vm; - struct intel_ringbuffer *rb; + struct intel_ring *ring; + struct pid *pid; - vm = request->ctx->ppgtt ? + ee->vm = request->ctx->ppgtt ? &request->ctx->ppgtt->base : &ggtt->base; /* We need to copy these to an anonymous buffer * as the simplest method to avoid being overwritten * by userspace. */ - error->ring[i].batchbuffer = + ee->batchbuffer = i915_error_object_create(dev_priv, - request->batch_obj, - vm); + request->batch); if (HAS_BROKEN_CS_TLB(dev_priv)) - error->ring[i].wa_batchbuffer = - i915_error_ggtt_object_create(dev_priv, - engine->scratch.obj); + ee->wa_batchbuffer = + i915_error_object_create(dev_priv, + engine->scratch); + + ee->ctx = + i915_error_object_create(dev_priv, + request->ctx->engine[i].state); - if (request->pid) { + pid = request->ctx->pid; + if (pid) { struct task_struct *task; rcu_read_lock(); - task = pid_task(request->pid, PIDTYPE_PID); + task = pid_task(pid, PIDTYPE_PID); if (task) { - strcpy(error->ring[i].comm, task->comm); - error->ring[i].pid = task->pid; + strcpy(ee->comm, task->comm); + ee->pid = task->pid; } rcu_read_unlock(); } @@ -1125,153 +1202,106 @@ static void i915_gem_record_rings(struct drm_i915_private *dev_priv, error->simulated |= request->ctx->flags & CONTEXT_NO_ERROR_CAPTURE; - rb = request->ringbuf; - error->ring[i].cpu_ring_head = rb->head; - error->ring[i].cpu_ring_tail = rb->tail; - error->ring[i].ringbuffer = - i915_error_ggtt_object_create(dev_priv, - rb->obj); - } - - error->ring[i].hws_page = - i915_error_ggtt_object_create(dev_priv, - engine->status_page.obj); + ring = request->ring; + ee->cpu_ring_head = ring->head; + ee->cpu_ring_tail = ring->tail; + ee->ringbuffer = + i915_error_object_create(dev_priv, ring->vma); - if (engine->wa_ctx.obj) { - error->ring[i].wa_ctx = - i915_error_ggtt_object_create(dev_priv, - engine->wa_ctx.obj); + engine_record_requests(engine, request, ee); } - i915_gem_record_active_context(engine, error, &error->ring[i]); - - count = 0; - list_for_each_entry(request, &engine->request_list, list) - count++; - - error->ring[i].num_requests = count; - error->ring[i].requests = - kcalloc(count, sizeof(*error->ring[i].requests), - GFP_ATOMIC); - if (error->ring[i].requests == NULL) { - error->ring[i].num_requests = 0; - continue; - } - - count = 0; - list_for_each_entry(request, &engine->request_list, list) { - struct drm_i915_error_request *erq; - - if (count >= error->ring[i].num_requests) { - /* - * If the ring request list was changed in - * between the point where the error request - * list was created and dimensioned and this - * point then just exit early to avoid crashes. - * - * We don't need to communicate that the - * request list changed state during error - * state capture and that the error state is - * slightly incorrect as a consequence since we - * are typically only interested in the request - * list state at the point of error state - * capture, not in any changes happening during - * the capture. - */ - break; - } + ee->hws_page = + i915_error_object_create(dev_priv, + engine->status_page.vma); - erq = &error->ring[i].requests[count++]; - erq->seqno = request->seqno; - erq->jiffies = request->emitted_jiffies; - erq->tail = request->postfix; - } + ee->wa_ctx = + i915_error_object_create(dev_priv, engine->wa_ctx.vma); } } -/* FIXME: Since pin count/bound list is global, we duplicate what we capture per - * VM. - */ static void i915_gem_capture_vm(struct drm_i915_private *dev_priv, struct drm_i915_error_state *error, struct i915_address_space *vm, - const int ndx) + int idx) { - struct drm_i915_error_buffer *active_bo = NULL, *pinned_bo = NULL; - struct drm_i915_gem_object *obj; + struct drm_i915_error_buffer *active_bo; struct i915_vma *vma; - int i; + int count; - i = 0; + count = 0; list_for_each_entry(vma, &vm->active_list, vm_link) - i++; - error->active_bo_count[ndx] = i; - - list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) { - list_for_each_entry(vma, &obj->vma_list, obj_link) - if (vma->vm == vm && vma->pin_count > 0) - i++; - } - error->pinned_bo_count[ndx] = i - error->active_bo_count[ndx]; - - if (i) { - active_bo = kcalloc(i, sizeof(*active_bo), GFP_ATOMIC); - if (active_bo) - pinned_bo = active_bo + error->active_bo_count[ndx]; - } + count++; + active_bo = NULL; + if (count) + active_bo = kcalloc(count, sizeof(*active_bo), GFP_ATOMIC); if (active_bo) - error->active_bo_count[ndx] = - capture_active_bo(active_bo, - error->active_bo_count[ndx], - &vm->active_list); - - if (pinned_bo) - error->pinned_bo_count[ndx] = - capture_pinned_bo(pinned_bo, - error->pinned_bo_count[ndx], - &dev_priv->mm.bound_list, vm); - error->active_bo[ndx] = active_bo; - error->pinned_bo[ndx] = pinned_bo; + count = capture_error_bo(active_bo, count, &vm->active_list, false); + else + count = 0; + + error->active_vm[idx] = vm; + error->active_bo[idx] = active_bo; + error->active_bo_count[idx] = count; } -static void i915_gem_capture_buffers(struct drm_i915_private *dev_priv, - struct drm_i915_error_state *error) +static void i915_capture_active_buffers(struct drm_i915_private *dev_priv, + struct drm_i915_error_state *error) { - struct i915_address_space *vm; - int cnt = 0, i = 0; - - list_for_each_entry(vm, &dev_priv->vm_list, global_link) - cnt++; - - error->active_bo = kcalloc(cnt, sizeof(*error->active_bo), GFP_ATOMIC); - error->pinned_bo = kcalloc(cnt, sizeof(*error->pinned_bo), GFP_ATOMIC); - error->active_bo_count = kcalloc(cnt, sizeof(*error->active_bo_count), - GFP_ATOMIC); - error->pinned_bo_count = kcalloc(cnt, sizeof(*error->pinned_bo_count), - GFP_ATOMIC); - - if (error->active_bo == NULL || - error->pinned_bo == NULL || - error->active_bo_count == NULL || - error->pinned_bo_count == NULL) { - kfree(error->active_bo); - kfree(error->active_bo_count); - kfree(error->pinned_bo); - kfree(error->pinned_bo_count); - - error->active_bo = NULL; - error->active_bo_count = NULL; - error->pinned_bo = NULL; - error->pinned_bo_count = NULL; - } else { - list_for_each_entry(vm, &dev_priv->vm_list, global_link) - i915_gem_capture_vm(dev_priv, error, vm, i++); + int cnt = 0, i, j; - error->vm_count = cnt; + BUILD_BUG_ON(ARRAY_SIZE(error->engine) > ARRAY_SIZE(error->active_bo)); + BUILD_BUG_ON(ARRAY_SIZE(error->active_bo) != ARRAY_SIZE(error->active_vm)); + BUILD_BUG_ON(ARRAY_SIZE(error->active_bo) != ARRAY_SIZE(error->active_bo_count)); + + /* Scan each engine looking for unique active contexts/vm */ + for (i = 0; i < ARRAY_SIZE(error->engine); i++) { + struct drm_i915_error_engine *ee = &error->engine[i]; + bool found; + + if (!ee->vm) + continue; + + found = false; + for (j = 0; j < i && !found; j++) + found = error->engine[j].vm == ee->vm; + if (!found) + i915_gem_capture_vm(dev_priv, error, ee->vm, cnt++); } } +static void i915_capture_pinned_buffers(struct drm_i915_private *dev_priv, + struct drm_i915_error_state *error) +{ + struct i915_address_space *vm = &dev_priv->ggtt.base; + struct drm_i915_error_buffer *bo; + struct i915_vma *vma; + int count_inactive, count_active; + + count_inactive = 0; + list_for_each_entry(vma, &vm->active_list, vm_link) + count_inactive++; + + count_active = 0; + list_for_each_entry(vma, &vm->inactive_list, vm_link) + count_active++; + + bo = NULL; + if (count_inactive + count_active) + bo = kcalloc(count_inactive + count_active, + sizeof(*bo), GFP_ATOMIC); + if (!bo) + return; + + count_inactive = capture_error_bo(bo, count_inactive, + &vm->active_list, true); + count_active = capture_error_bo(bo + count_inactive, count_active, + &vm->inactive_list, true); + error->pinned_bo_count = count_inactive + count_active; + error->pinned_bo = bo; +} + /* Capture all registers which don't fit into another category. */ static void i915_capture_reg_state(struct drm_i915_private *dev_priv, struct drm_i915_error_state *error) @@ -1352,20 +1382,20 @@ static void i915_error_capture_msg(struct drm_i915_private *dev_priv, const char *error_msg) { u32 ecode; - int ring_id = -1, len; + int engine_id = -1, len; - ecode = i915_error_generate_code(dev_priv, error, &ring_id); + ecode = i915_error_generate_code(dev_priv, error, &engine_id); len = scnprintf(error->error_msg, sizeof(error->error_msg), "GPU HANG: ecode %d:%d:0x%08x", - INTEL_GEN(dev_priv), ring_id, ecode); + INTEL_GEN(dev_priv), engine_id, ecode); - if (ring_id != -1 && error->ring[ring_id].pid != -1) + if (engine_id != -1 && error->engine[engine_id].pid != -1) len += scnprintf(error->error_msg + len, sizeof(error->error_msg) - len, ", in %s [%d]", - error->ring[ring_id].comm, - error->ring[ring_id].pid); + error->engine[engine_id].comm, + error->engine[engine_id].pid); scnprintf(error->error_msg + len, sizeof(error->error_msg) - len, ", reason: %s, action: %s", @@ -1382,6 +1412,10 @@ static void i915_capture_gen_state(struct drm_i915_private *dev_priv, #endif error->reset_count = i915_reset_count(&dev_priv->gpu_error); error->suspend_count = dev_priv->suspend_count; + + memcpy(&error->device_info, + INTEL_INFO(dev_priv), + sizeof(error->device_info)); } /** @@ -1415,9 +1449,10 @@ void i915_capture_error_state(struct drm_i915_private *dev_priv, i915_capture_gen_state(dev_priv, error); i915_capture_reg_state(dev_priv, error); - i915_gem_capture_buffers(dev_priv, error); i915_gem_record_fences(dev_priv, error); i915_gem_record_rings(dev_priv, error); + i915_capture_active_buffers(dev_priv, error); + i915_capture_pinned_buffers(dev_priv, error); do_gettimeofday(&error->time); diff --git a/drivers/gpu/drm/i915/i915_guc_reg.h b/drivers/gpu/drm/i915/i915_guc_reg.h index cf5a65b..a47e1e4 100644 --- a/drivers/gpu/drm/i915/i915_guc_reg.h +++ b/drivers/gpu/drm/i915/i915_guc_reg.h @@ -103,9 +103,6 @@ #define HOST2GUC_INTERRUPT _MMIO(0xc4c8) #define HOST2GUC_TRIGGER (1<<0) -#define DRBMISC1 0x1984 -#define DOORBELL_ENABLE (1<<0) - #define GEN8_DRBREGL(x) _MMIO(0x1000 + (x) * 8) #define GEN8_DRB_VALID (1<<0) #define GEN8_DRBREGU(x) _MMIO(0x1000 + (x) * 8 + 4) diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c index 2112e02..43358e1 100644 --- a/drivers/gpu/drm/i915/i915_guc_submission.c +++ b/drivers/gpu/drm/i915/i915_guc_submission.c @@ -59,7 +59,7 @@ * WQ_TYPE_INORDER is needed to support legacy submission via GuC, which * represents in-order queue. The kernel driver packs ring tail pointer and an * ELSP context descriptor dword into Work Item. - * See guc_add_workqueue_item() + * See guc_wq_item_append() * */ @@ -114,10 +114,8 @@ static int host2guc_action(struct intel_guc *guc, u32 *data, u32 len) if (ret != -ETIMEDOUT) ret = -EIO; - DRM_ERROR("GUC: host2guc action 0x%X failed. ret=%d " - "status=0x%08X response=0x%08X\n", - data[0], ret, status, - I915_READ(SOFT_SCRATCH(15))); + DRM_WARN("Action 0x%X failed; ret=%d status=0x%08X response=0x%08X\n", + data[0], ret, status, I915_READ(SOFT_SCRATCH(15))); dev_priv->guc.action_fail += 1; dev_priv->guc.action_err = ret; @@ -183,7 +181,7 @@ static int guc_update_doorbell_id(struct intel_guc *guc, struct i915_guc_client *client, u16 new_id) { - struct sg_table *sg = guc->ctx_pool_obj->pages; + struct sg_table *sg = guc->ctx_pool_vma->pages; void *doorbell_bitmap = guc->doorbell_bitmap; struct guc_doorbell_info *doorbell; struct guc_context_desc desc; @@ -290,7 +288,7 @@ static uint32_t select_doorbell_cacheline(struct intel_guc *guc) /* * Initialise the process descriptor shared with the GuC firmware. */ -static void guc_init_proc_desc(struct intel_guc *guc, +static void guc_proc_desc_init(struct intel_guc *guc, struct i915_guc_client *client) { struct guc_process_desc *desc; @@ -322,15 +320,15 @@ static void guc_init_proc_desc(struct intel_guc *guc, * write queue, etc). */ -static void guc_init_ctx_desc(struct intel_guc *guc, +static void guc_ctx_desc_init(struct intel_guc *guc, struct i915_guc_client *client) { - struct drm_i915_gem_object *client_obj = client->client_obj; struct drm_i915_private *dev_priv = guc_to_i915(guc); struct intel_engine_cs *engine; struct i915_gem_context *ctx = client->owner; struct guc_context_desc desc; struct sg_table *sg; + unsigned int tmp; u32 gfx_addr; memset(&desc, 0, sizeof(desc)); @@ -340,10 +338,10 @@ static void guc_init_ctx_desc(struct intel_guc *guc, desc.priority = client->priority; desc.db_id = client->doorbell_id; - for_each_engine(engine, dev_priv) { + for_each_engine_masked(engine, dev_priv, client->engines, tmp) { struct intel_context *ce = &ctx->engine[engine->id]; - struct guc_execlist_context *lrc = &desc.lrc[engine->guc_id]; - struct drm_i915_gem_object *obj; + uint32_t guc_engine_id = engine->guc_id; + struct guc_execlist_context *lrc = &desc.lrc[guc_engine_id]; /* TODO: We have a design issue to be solved here. Only when we * receive the first batch, we know which engine is used by the @@ -358,30 +356,29 @@ static void guc_init_ctx_desc(struct intel_guc *guc, lrc->context_desc = lower_32_bits(ce->lrc_desc); /* The state page is after PPHWSP */ - gfx_addr = i915_gem_obj_ggtt_offset(ce->state); - lrc->ring_lcra = gfx_addr + LRC_STATE_PN * PAGE_SIZE; + lrc->ring_lcra = + i915_ggtt_offset(ce->state) + LRC_STATE_PN * PAGE_SIZE; lrc->context_id = (client->ctx_index << GUC_ELC_CTXID_OFFSET) | - (engine->guc_id << GUC_ELC_ENGINE_OFFSET); - - obj = ce->ringbuf->obj; - gfx_addr = i915_gem_obj_ggtt_offset(obj); + (guc_engine_id << GUC_ELC_ENGINE_OFFSET); - lrc->ring_begin = gfx_addr; - lrc->ring_end = gfx_addr + obj->base.size - 1; - lrc->ring_next_free_location = gfx_addr; + lrc->ring_begin = i915_ggtt_offset(ce->ring->vma); + lrc->ring_end = lrc->ring_begin + ce->ring->size - 1; + lrc->ring_next_free_location = lrc->ring_begin; lrc->ring_current_tail_pointer_value = 0; - desc.engines_used |= (1 << engine->guc_id); + desc.engines_used |= (1 << guc_engine_id); } + DRM_DEBUG_DRIVER("Host engines 0x%x => GuC engines used 0x%x\n", + client->engines, desc.engines_used); WARN_ON(desc.engines_used == 0); /* * The doorbell, process descriptor, and workqueue are all parts * of the client object, which the GuC will reference via the GGTT */ - gfx_addr = i915_gem_obj_ggtt_offset(client_obj); - desc.db_trigger_phy = sg_dma_address(client_obj->pages->sgl) + + gfx_addr = i915_ggtt_offset(client->vma); + desc.db_trigger_phy = sg_dma_address(client->vma->pages->sgl) + client->doorbell_offset; desc.db_trigger_cpu = (uintptr_t)client->client_base + client->doorbell_offset; @@ -397,12 +394,12 @@ static void guc_init_ctx_desc(struct intel_guc *guc, desc.desc_private = (uintptr_t)client; /* Pool context is pinned already */ - sg = guc->ctx_pool_obj->pages; + sg = guc->ctx_pool_vma->pages; sg_pcopy_from_buffer(sg->sgl, sg->nents, &desc, sizeof(desc), sizeof(desc) * client->ctx_index); } -static void guc_fini_ctx_desc(struct intel_guc *guc, +static void guc_ctx_desc_fini(struct intel_guc *guc, struct i915_guc_client *client) { struct guc_context_desc desc; @@ -410,13 +407,13 @@ static void guc_fini_ctx_desc(struct intel_guc *guc, memset(&desc, 0, sizeof(desc)); - sg = guc->ctx_pool_obj->pages; + sg = guc->ctx_pool_vma->pages; sg_pcopy_from_buffer(sg->sgl, sg->nents, &desc, sizeof(desc), sizeof(desc) * client->ctx_index); } /** - * i915_guc_wq_check_space() - check that the GuC can accept a request + * i915_guc_wq_reserve() - reserve space in the GuC's workqueue * @request: request associated with the commands * * Return: 0 if space is available @@ -424,39 +421,44 @@ static void guc_fini_ctx_desc(struct intel_guc *guc, * * This function must be called (and must return 0) before a request * is submitted to the GuC via i915_guc_submit() below. Once a result - * of 0 has been returned, it remains valid until (but only until) - * the next call to submit(). + * of 0 has been returned, it must be balanced by a corresponding + * call to submit(). * - * This precheck allows the caller to determine in advance that space + * Reservation allows the caller to determine in advance that space * will be available for the next submission before committing resources * to it, and helps avoid late failures with complicated recovery paths. */ -int i915_guc_wq_check_space(struct drm_i915_gem_request *request) +int i915_guc_wq_reserve(struct drm_i915_gem_request *request) { const size_t wqi_size = sizeof(struct guc_wq_item); struct i915_guc_client *gc = request->i915->guc.execbuf_client; - struct guc_process_desc *desc; + struct guc_process_desc *desc = gc->client_base + gc->proc_desc_offset; u32 freespace; + int ret; - GEM_BUG_ON(gc == NULL); - - desc = gc->client_base + gc->proc_desc_offset; - + spin_lock(&gc->wq_lock); freespace = CIRC_SPACE(gc->wq_tail, desc->head, gc->wq_size); - if (likely(freespace >= wqi_size)) - return 0; - - gc->no_wq_space += 1; + freespace -= gc->wq_rsvd; + if (likely(freespace >= wqi_size)) { + gc->wq_rsvd += wqi_size; + ret = 0; + } else { + gc->no_wq_space++; + ret = -EAGAIN; + } + spin_unlock(&gc->wq_lock); - return -EAGAIN; + return ret; } -static void guc_add_workqueue_item(struct i915_guc_client *gc, - struct drm_i915_gem_request *rq) +/* Construct a Work Item and append it to the GuC's Work Queue */ +static void guc_wq_item_append(struct i915_guc_client *gc, + struct drm_i915_gem_request *rq) { /* wqi_len is in DWords, and does not include the one-word header */ const size_t wqi_size = sizeof(struct guc_wq_item); const u32 wqi_len = wqi_size/sizeof(u32) - 1; + struct intel_engine_cs *engine = rq->engine; struct guc_process_desc *desc; struct guc_wq_item *wqi; void *base; @@ -464,7 +466,7 @@ static void guc_add_workqueue_item(struct i915_guc_client *gc, desc = gc->client_base + gc->proc_desc_offset; - /* Free space is guaranteed, see i915_guc_wq_check_space() above */ + /* Free space is guaranteed, see i915_guc_wq_reserve() above */ freespace = CIRC_SPACE(gc->wq_tail, desc->head, gc->wq_size); GEM_BUG_ON(freespace < wqi_size); @@ -482,31 +484,32 @@ static void guc_add_workqueue_item(struct i915_guc_client *gc, * workqueue buffer dw by dw. */ BUILD_BUG_ON(wqi_size != 16); + GEM_BUG_ON(gc->wq_rsvd < wqi_size); /* postincrement WQ tail for next time */ wq_off = gc->wq_tail; + GEM_BUG_ON(wq_off & (wqi_size - 1)); gc->wq_tail += wqi_size; gc->wq_tail &= gc->wq_size - 1; - GEM_BUG_ON(wq_off & (wqi_size - 1)); + gc->wq_rsvd -= wqi_size; /* WQ starts from the page after doorbell / process_desc */ wq_page = (wq_off + GUC_DB_SIZE) >> PAGE_SHIFT; wq_off &= PAGE_SIZE - 1; - base = kmap_atomic(i915_gem_object_get_page(gc->client_obj, wq_page)); + base = kmap_atomic(i915_gem_object_get_page(gc->vma->obj, wq_page)); wqi = (struct guc_wq_item *)((char *)base + wq_off); /* Now fill in the 4-word work queue item */ wqi->header = WQ_TYPE_INORDER | (wqi_len << WQ_LEN_SHIFT) | - (rq->engine->guc_id << WQ_TARGET_SHIFT) | + (engine->guc_id << WQ_TARGET_SHIFT) | WQ_NO_WCFLUSH_WAIT; /* The GuC wants only the low-order word of the context descriptor */ - wqi->context_desc = (u32)intel_lr_context_descriptor(rq->ctx, - rq->engine); + wqi->context_desc = (u32)intel_lr_context_descriptor(rq->ctx, engine); wqi->ring_tail = tail << WQ_RING_TAIL_SHIFT; - wqi->fence_id = rq->seqno; + wqi->fence_id = rq->fence.seqno; kunmap_atomic(base); } @@ -553,8 +556,8 @@ static int guc_ring_doorbell(struct i915_guc_client *gc) if (db_ret.db_status == GUC_DOORBELL_DISABLED) break; - DRM_ERROR("Cookie mismatch. Expected %d, returned %d\n", - db_cmp.cookie, db_ret.cookie); + DRM_WARN("Cookie mismatch. Expected %d, found %d\n", + db_cmp.cookie, db_ret.cookie); /* update the cookie to newly read cookie from GuC */ db_cmp.cookie = db_ret.cookie; @@ -573,26 +576,26 @@ static int guc_ring_doorbell(struct i915_guc_client *gc) * Return: 0 on success, otherwise an errno. * (Note: nonzero really shouldn't happen!) * - * The caller must have already called i915_guc_wq_check_space() above - * with a result of 0 (success) since the last request submission. This - * guarantees that there is space in the work queue for the new request, - * so enqueuing the item cannot fail. + * The caller must have already called i915_guc_wq_reserve() above with + * a result of 0 (success), guaranteeing that there is space in the work + * queue for the new request, so enqueuing the item cannot fail. * * Bad Things Will Happen if the caller violates this protocol e.g. calls - * submit() when check() says there's no space, or calls submit() multiple - * times with no intervening check(). + * submit() when _reserve() says there's no space, or calls _submit() + * a different number of times from (successful) calls to _reserve(). * * The only error here arises if the doorbell hardware isn't functioning * as expected, which really shouln't happen. */ -int i915_guc_submit(struct drm_i915_gem_request *rq) +static void i915_guc_submit(struct drm_i915_gem_request *rq) { unsigned int engine_id = rq->engine->id; struct intel_guc *guc = &rq->i915->guc; struct i915_guc_client *client = guc->execbuf_client; int b_ret; - guc_add_workqueue_item(client, rq); + spin_lock(&client->wq_lock); + guc_wq_item_append(client, rq); b_ret = guc_ring_doorbell(client); client->submissions[engine_id] += 1; @@ -601,9 +604,8 @@ int i915_guc_submit(struct drm_i915_gem_request *rq) client->b_fail += 1; guc->submissions[engine_id] += 1; - guc->last_seqno[engine_id] = rq->seqno; - - return b_ret; + guc->last_seqno[engine_id] = rq->fence.seqno; + spin_unlock(&client->wq_lock); } /* @@ -613,55 +615,48 @@ int i915_guc_submit(struct drm_i915_gem_request *rq) */ /** - * gem_allocate_guc_obj() - Allocate gem object for GuC usage - * @dev_priv: driver private data structure - * @size: size of object + * guc_allocate_vma() - Allocate a GGTT VMA for GuC usage + * @guc: the guc + * @size: size of area to allocate (both virtual space and memory) * - * This is a wrapper to create a gem obj. In order to use it inside GuC, the - * object needs to be pinned lifetime. Also we must pin it to gtt space other - * than [0, GUC_WOPCM_TOP) because this range is reserved inside GuC. + * This is a wrapper to create an object for use with the GuC. In order to + * use it inside the GuC, an object needs to be pinned lifetime, so we allocate + * both some backing storage and a range inside the Global GTT. We must pin + * it in the GGTT somewhere other than than [0, GUC_WOPCM_TOP) because that + * range is reserved inside GuC. * - * Return: A drm_i915_gem_object if successful, otherwise NULL. + * Return: A i915_vma if successful, otherwise an ERR_PTR. */ -static struct drm_i915_gem_object * -gem_allocate_guc_obj(struct drm_i915_private *dev_priv, u32 size) +static struct i915_vma *guc_allocate_vma(struct intel_guc *guc, u32 size) { + struct drm_i915_private *dev_priv = guc_to_i915(guc); struct drm_i915_gem_object *obj; + struct i915_vma *vma; + int ret; obj = i915_gem_object_create(&dev_priv->drm, size); if (IS_ERR(obj)) - return NULL; + return ERR_CAST(obj); - if (i915_gem_object_get_pages(obj)) { - drm_gem_object_unreference(&obj->base); - return NULL; - } + vma = i915_vma_create(obj, &dev_priv->ggtt.base, NULL); + if (IS_ERR(vma)) + goto err; - if (i915_gem_obj_ggtt_pin(obj, PAGE_SIZE, - PIN_OFFSET_BIAS | GUC_WOPCM_TOP)) { - drm_gem_object_unreference(&obj->base); - return NULL; + ret = i915_vma_pin(vma, 0, PAGE_SIZE, + PIN_GLOBAL | PIN_OFFSET_BIAS | GUC_WOPCM_TOP); + if (ret) { + vma = ERR_PTR(ret); + goto err; } /* Invalidate GuC TLB to let GuC take the latest updates to GTT. */ I915_WRITE(GEN8_GTCR, GEN8_GTCR_INVALIDATE); - return obj; -} - -/** - * gem_release_guc_obj() - Release gem object allocated for GuC usage - * @obj: gem obj to be released - */ -static void gem_release_guc_obj(struct drm_i915_gem_object *obj) -{ - if (!obj) - return; + return vma; - if (i915_gem_obj_is_pinned(obj)) - i915_gem_object_ggtt_unpin(obj); - - drm_gem_object_unreference(&obj->base); +err: + i915_gem_object_put(obj); + return vma; } static void @@ -688,61 +683,74 @@ guc_client_free(struct drm_i915_private *dev_priv, kunmap(kmap_to_page(client->client_base)); } - gem_release_guc_obj(client->client_obj); + i915_vma_unpin_and_release(&client->vma); if (client->ctx_index != GUC_INVALID_CTX_ID) { - guc_fini_ctx_desc(guc, client); + guc_ctx_desc_fini(guc, client); ida_simple_remove(&guc->ctx_ids, client->ctx_index); } kfree(client); } +/* Check that a doorbell register is in the expected state */ +static bool guc_doorbell_check(struct intel_guc *guc, uint16_t db_id) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + i915_reg_t drbreg = GEN8_DRBREGL(db_id); + uint32_t value = I915_READ(drbreg); + bool enabled = (value & GUC_DOORBELL_ENABLED) != 0; + bool expected = test_bit(db_id, guc->doorbell_bitmap); + + if (enabled == expected) + return true; + + DRM_DEBUG_DRIVER("Doorbell %d (reg 0x%x) 0x%x, should be %s\n", + db_id, drbreg.reg, value, + expected ? "active" : "inactive"); + + return false; +} + /* - * Borrow the first client to set up & tear down every doorbell + * Borrow the first client to set up & tear down each unused doorbell * in turn, to ensure that all doorbell h/w is (re)initialised. */ static void guc_init_doorbell_hw(struct intel_guc *guc) { - struct drm_i915_private *dev_priv = guc_to_i915(guc); struct i915_guc_client *client = guc->execbuf_client; - uint16_t db_id, i; - int err; + uint16_t db_id; + int i, err; + /* Save client's original doorbell selection */ db_id = client->doorbell_id; for (i = 0; i < GUC_MAX_DOORBELLS; ++i) { - i915_reg_t drbreg = GEN8_DRBREGL(i); - u32 value = I915_READ(drbreg); + /* Skip if doorbell is OK */ + if (guc_doorbell_check(guc, i)) + continue; err = guc_update_doorbell_id(guc, client, i); - - /* Report update failure or unexpectedly active doorbell */ - if (err || (i != db_id && (value & GUC_DOORBELL_ENABLED))) - DRM_DEBUG_DRIVER("Doorbell %d (reg 0x%x) was 0x%x, err %d\n", - i, drbreg.reg, value, err); + if (err) + DRM_DEBUG_DRIVER("Doorbell %d update failed, err %d\n", + i, err); } /* Restore to original value */ err = guc_update_doorbell_id(guc, client, db_id); if (err) - DRM_ERROR("Failed to restore doorbell to %d, err %d\n", - db_id, err); + DRM_WARN("Failed to restore doorbell to %d, err %d\n", + db_id, err); - for (i = 0; i < GUC_MAX_DOORBELLS; ++i) { - i915_reg_t drbreg = GEN8_DRBREGL(i); - u32 value = I915_READ(drbreg); - - if (i != db_id && (value & GUC_DOORBELL_ENABLED)) - DRM_DEBUG_DRIVER("Doorbell %d (reg 0x%x) finally 0x%x\n", - i, drbreg.reg, value); - - } + /* Read back & verify all doorbell registers */ + for (i = 0; i < GUC_MAX_DOORBELLS; ++i) + (void)guc_doorbell_check(guc, i); } /** * guc_client_alloc() - Allocate an i915_guc_client * @dev_priv: driver private data structure + * @engines: The set of engines to enable for this client * @priority: four levels priority _CRITICAL, _HIGH, _NORMAL and _LOW * The kernel client to replace ExecList submission is created with * NORMAL priority. Priority of a client for scheduler can be HIGH, @@ -754,22 +762,24 @@ static void guc_init_doorbell_hw(struct intel_guc *guc) */ static struct i915_guc_client * guc_client_alloc(struct drm_i915_private *dev_priv, + uint32_t engines, uint32_t priority, struct i915_gem_context *ctx) { struct i915_guc_client *client; struct intel_guc *guc = &dev_priv->guc; - struct drm_i915_gem_object *obj; + struct i915_vma *vma; uint16_t db_id; client = kzalloc(sizeof(*client), GFP_KERNEL); if (!client) return NULL; - client->doorbell_id = GUC_INVALID_DOORBELL_ID; - client->priority = priority; client->owner = ctx; client->guc = guc; + client->engines = engines; + client->priority = priority; + client->doorbell_id = GUC_INVALID_DOORBELL_ID; client->ctx_index = (uint32_t)ida_simple_get(&guc->ctx_ids, 0, GUC_MAX_GPU_CONTEXTS, GFP_KERNEL); @@ -779,13 +789,15 @@ guc_client_alloc(struct drm_i915_private *dev_priv, } /* The first page is doorbell/proc_desc. Two followed pages are wq. */ - obj = gem_allocate_guc_obj(dev_priv, GUC_DB_SIZE + GUC_WQ_SIZE); - if (!obj) + vma = guc_allocate_vma(guc, GUC_DB_SIZE + GUC_WQ_SIZE); + if (IS_ERR(vma)) goto err; /* We'll keep just the first (doorbell/proc) page permanently kmap'd. */ - client->client_obj = obj; - client->client_base = kmap(i915_gem_object_get_page(obj, 0)); + client->vma = vma; + client->client_base = kmap(i915_vma_first_page(vma)); + + spin_lock_init(&client->wq_lock); client->wq_offset = GUC_DB_SIZE; client->wq_size = GUC_WQ_SIZE; @@ -806,29 +818,26 @@ guc_client_alloc(struct drm_i915_private *dev_priv, else client->proc_desc_offset = (GUC_DB_SIZE / 2); - guc_init_proc_desc(guc, client); - guc_init_ctx_desc(guc, client); + guc_proc_desc_init(guc, client); + guc_ctx_desc_init(guc, client); if (guc_init_doorbell(guc, client, db_id)) goto err; - DRM_DEBUG_DRIVER("new priority %u client %p: ctx_index %u\n", - priority, client, client->ctx_index); + DRM_DEBUG_DRIVER("new priority %u client %p for engine(s) 0x%x: ctx_index %u\n", + priority, client, client->engines, client->ctx_index); DRM_DEBUG_DRIVER("doorbell id %u, cacheline offset 0x%x\n", client->doorbell_id, client->doorbell_offset); return client; err: - DRM_ERROR("FAILED to create priority %u GuC client!\n", priority); - guc_client_free(dev_priv, client); return NULL; } -static void guc_create_log(struct intel_guc *guc) +static void guc_log_create(struct intel_guc *guc) { - struct drm_i915_private *dev_priv = guc_to_i915(guc); - struct drm_i915_gem_object *obj; + struct i915_vma *vma; unsigned long offset; uint32_t size, flags; @@ -844,16 +853,16 @@ static void guc_create_log(struct intel_guc *guc) GUC_LOG_ISR_PAGES + 1 + GUC_LOG_CRASH_PAGES + 1) << PAGE_SHIFT; - obj = guc->log_obj; - if (!obj) { - obj = gem_allocate_guc_obj(dev_priv, size); - if (!obj) { + vma = guc->log_vma; + if (!vma) { + vma = guc_allocate_vma(guc, size); + if (IS_ERR(vma)) { /* logging will be off */ i915.guc_log_level = -1; return; } - guc->log_obj = obj; + guc->log_vma = vma; } /* each allocated unit is a page */ @@ -862,11 +871,11 @@ static void guc_create_log(struct intel_guc *guc) (GUC_LOG_ISR_PAGES << GUC_LOG_ISR_SHIFT) | (GUC_LOG_CRASH_PAGES << GUC_LOG_CRASH_SHIFT); - offset = i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT; /* in pages */ + offset = i915_ggtt_offset(vma) >> PAGE_SHIFT; /* in pages */ guc->log_flags = (offset << GUC_LOG_BUF_ADDR_SHIFT) | flags; } -static void init_guc_policies(struct guc_policies *policies) +static void guc_policies_init(struct guc_policies *policies) { struct guc_policy *policy; u32 p, i; @@ -888,10 +897,10 @@ static void init_guc_policies(struct guc_policies *policies) policies->is_valid = 1; } -static void guc_create_ads(struct intel_guc *guc) +static void guc_addon_create(struct intel_guc *guc) { struct drm_i915_private *dev_priv = guc_to_i915(guc); - struct drm_i915_gem_object *obj; + struct i915_vma *vma; struct guc_ads *ads; struct guc_policies *policies; struct guc_mmio_reg_state *reg_state; @@ -904,16 +913,16 @@ static void guc_create_ads(struct intel_guc *guc) sizeof(struct guc_mmio_reg_state) + GUC_S3_SAVE_SPACE_PAGES * PAGE_SIZE; - obj = guc->ads_obj; - if (!obj) { - obj = gem_allocate_guc_obj(dev_priv, PAGE_ALIGN(size)); - if (!obj) + vma = guc->ads_vma; + if (!vma) { + vma = guc_allocate_vma(guc, PAGE_ALIGN(size)); + if (IS_ERR(vma)) return; - guc->ads_obj = obj; + guc->ads_vma = vma; } - page = i915_gem_object_get_page(obj, 0); + page = i915_vma_first_page(vma); ads = kmap(page); /* @@ -924,17 +933,17 @@ static void guc_create_ads(struct intel_guc *guc) * to find it. */ engine = &dev_priv->engine[RCS]; - ads->golden_context_lrca = engine->status_page.gfx_addr; + ads->golden_context_lrca = engine->status_page.ggtt_offset; for_each_engine(engine, dev_priv) ads->eng_state_size[engine->guc_id] = intel_lr_context_size(engine); /* GuC scheduling policies */ policies = (void *)ads + sizeof(struct guc_ads); - init_guc_policies(policies); + guc_policies_init(policies); - ads->scheduler_policies = i915_gem_obj_ggtt_offset(obj) + - sizeof(struct guc_ads); + ads->scheduler_policies = + i915_ggtt_offset(vma) + sizeof(struct guc_ads); /* MMIO reg state */ reg_state = (void *)policies + sizeof(struct guc_policies); @@ -966,6 +975,7 @@ int i915_guc_submission_init(struct drm_i915_private *dev_priv) const size_t poolsize = GUC_MAX_GPU_CONTEXTS * ctxsize; const size_t gemsize = round_up(poolsize, PAGE_SIZE); struct intel_guc *guc = &dev_priv->guc; + struct i915_vma *vma; /* Wipe bitmap & delete client in case of reinitialisation */ bitmap_clear(guc->doorbell_bitmap, 0, GUC_MAX_DOORBELLS); @@ -974,16 +984,17 @@ int i915_guc_submission_init(struct drm_i915_private *dev_priv) if (!i915.enable_guc_submission) return 0; /* not enabled */ - if (guc->ctx_pool_obj) + if (guc->ctx_pool_vma) return 0; /* already allocated */ - guc->ctx_pool_obj = gem_allocate_guc_obj(dev_priv, gemsize); - if (!guc->ctx_pool_obj) - return -ENOMEM; + vma = guc_allocate_vma(guc, gemsize); + if (IS_ERR(vma)) + return PTR_ERR(vma); + guc->ctx_pool_vma = vma; ida_init(&guc->ctx_ids); - guc_create_log(guc); - guc_create_ads(guc); + guc_log_create(guc); + guc_addon_create(guc); return 0; } @@ -992,13 +1003,16 @@ int i915_guc_submission_enable(struct drm_i915_private *dev_priv) { struct intel_guc *guc = &dev_priv->guc; struct i915_guc_client *client; + struct intel_engine_cs *engine; + struct drm_i915_gem_request *request; /* client for execbuf submission */ client = guc_client_alloc(dev_priv, + INTEL_INFO(dev_priv)->ring_mask, GUC_CTX_PRIORITY_KMD_NORMAL, dev_priv->kernel_context); if (!client) { - DRM_ERROR("Failed to create execbuf guc_client\n"); + DRM_ERROR("Failed to create normal GuC client!\n"); return -ENOMEM; } @@ -1006,6 +1020,18 @@ int i915_guc_submission_enable(struct drm_i915_private *dev_priv) host2guc_sample_forcewake(guc, client); guc_init_doorbell_hw(guc); + /* Take over from manual control of ELSP (execlists) */ + for_each_engine(engine, dev_priv) { + engine->submit_request = i915_guc_submit; + + /* Replay the current set of previously submitted requests */ + list_for_each_entry(request, &engine->request_list, link) { + client->wq_rsvd += sizeof(struct guc_wq_item); + if (i915_sw_fence_done(&request->submit)) + i915_guc_submit(request); + } + } + return 0; } @@ -1013,6 +1039,12 @@ void i915_guc_submission_disable(struct drm_i915_private *dev_priv) { struct intel_guc *guc = &dev_priv->guc; + if (!guc->execbuf_client) + return; + + /* Revert back to manual ELSP submission */ + intel_execlists_enable_submission(dev_priv); + guc_client_free(dev_priv, guc->execbuf_client); guc->execbuf_client = NULL; } @@ -1021,16 +1053,12 @@ void i915_guc_submission_fini(struct drm_i915_private *dev_priv) { struct intel_guc *guc = &dev_priv->guc; - gem_release_guc_obj(dev_priv->guc.ads_obj); - guc->ads_obj = NULL; - - gem_release_guc_obj(dev_priv->guc.log_obj); - guc->log_obj = NULL; + i915_vma_unpin_and_release(&guc->ads_vma); + i915_vma_unpin_and_release(&guc->log_vma); - if (guc->ctx_pool_obj) + if (guc->ctx_pool_vma) ida_destroy(&guc->ctx_ids); - gem_release_guc_obj(guc->ctx_pool_obj); - guc->ctx_pool_obj = NULL; + i915_vma_unpin_and_release(&guc->ctx_pool_vma); } /** @@ -1053,7 +1081,7 @@ int intel_guc_suspend(struct drm_device *dev) /* any value greater than GUC_POWER_D0 */ data[1] = GUC_POWER_D1; /* first page is shared data with GuC */ - data[2] = i915_gem_obj_ggtt_offset(ctx->engine[RCS].state); + data[2] = i915_ggtt_offset(ctx->engine[RCS].state); return host2guc_action(guc, data, ARRAY_SIZE(data)); } @@ -1078,7 +1106,7 @@ int intel_guc_resume(struct drm_device *dev) data[0] = HOST2GUC_ACTION_EXIT_S_STATE; data[1] = GUC_POWER_D0; /* first page is shared data with GuC */ - data[2] = i915_gem_obj_ggtt_offset(ctx->engine[RCS].state); + data[2] = i915_ggtt_offset(ctx->engine[RCS].state); return host2guc_action(guc, data, ARRAY_SIZE(data)); } diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 1c2aec3..c128fdb 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -371,7 +371,7 @@ void gen6_disable_rps_interrupts(struct drm_i915_private *dev_priv) spin_lock_irq(&dev_priv->irq_lock); dev_priv->rps.interrupts_enabled = false; - I915_WRITE(GEN6_PMINTRMSK, gen6_sanitize_rps_pm_mask(dev_priv, ~0)); + I915_WRITE(GEN6_PMINTRMSK, gen6_sanitize_rps_pm_mask(dev_priv, ~0u)); __gen6_disable_pm_irq(dev_priv, dev_priv->pm_rps_events); I915_WRITE(gen6_pm_ier(dev_priv), I915_READ(gen6_pm_ier(dev_priv)) & @@ -656,12 +656,6 @@ static void i915_enable_asle_pipestat(struct drm_i915_private *dev_priv) * of horizontal active on the first line of vertical active */ -static u32 i8xx_get_vblank_counter(struct drm_device *dev, unsigned int pipe) -{ - /* Gen2 doesn't have a hardware frame counter */ - return 0; -} - /* Called from drm generic code, passed a 'crtc', which * we use as a pipe index */ @@ -978,10 +972,8 @@ static void ironlake_rps_change_irq_handler(struct drm_i915_private *dev_priv) static void notify_ring(struct intel_engine_cs *engine) { smp_store_mb(engine->breadcrumbs.irq_posted, true); - if (intel_engine_wakeup(engine)) { + if (intel_engine_wakeup(engine)) trace_i915_gem_request_notify(engine); - engine->breadcrumbs.irq_wakeups++; - } } static void vlv_c0_read(struct drm_i915_private *dev_priv, @@ -1105,9 +1097,10 @@ static void gen6_pm_rps_work(struct work_struct *work) new_delay = dev_priv->rps.cur_freq; min = dev_priv->rps.min_freq_softlimit; max = dev_priv->rps.max_freq_softlimit; - - if (client_boost) { - new_delay = dev_priv->rps.max_freq_softlimit; + if (client_boost || any_waiters(dev_priv)) + max = dev_priv->rps.max_freq; + if (client_boost && new_delay < dev_priv->rps.boost_freq) { + new_delay = dev_priv->rps.boost_freq; adj = 0; } else if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) { if (adj > 0) @@ -1122,7 +1115,7 @@ static void gen6_pm_rps_work(struct work_struct *work) new_delay = dev_priv->rps.efficient_freq; adj = 0; } - } else if (any_waiters(dev_priv)) { + } else if (client_boost || any_waiters(dev_priv)) { adj = 0; } else if (pm_iir & GEN6_PM_RP_DOWN_TIMEOUT) { if (dev_priv->rps.cur_freq > dev_priv->rps.efficient_freq) @@ -2504,57 +2497,52 @@ static void i915_reset_and_wakeup(struct drm_i915_private *dev_priv) char *error_event[] = { I915_ERROR_UEVENT "=1", NULL }; char *reset_event[] = { I915_RESET_UEVENT "=1", NULL }; char *reset_done_event[] = { I915_ERROR_UEVENT "=0", NULL }; - int ret; kobject_uevent_env(kobj, KOBJ_CHANGE, error_event); + DRM_DEBUG_DRIVER("resetting chip\n"); + kobject_uevent_env(kobj, KOBJ_CHANGE, reset_event); + /* - * Note that there's only one work item which does gpu resets, so we - * need not worry about concurrent gpu resets potentially incrementing - * error->reset_counter twice. We only need to take care of another - * racing irq/hangcheck declaring the gpu dead for a second time. A - * quick check for that is good enough: schedule_work ensures the - * correct ordering between hang detection and this work item, and since - * the reset in-progress bit is only ever set by code outside of this - * work we don't need to worry about any other races. + * In most cases it's guaranteed that we get here with an RPM + * reference held, for example because there is a pending GPU + * request that won't finish until the reset is done. This + * isn't the case at least when we get here by doing a + * simulated reset via debugs, so get an RPM reference. */ - if (i915_reset_in_progress(&dev_priv->gpu_error)) { - DRM_DEBUG_DRIVER("resetting chip\n"); - kobject_uevent_env(kobj, KOBJ_CHANGE, reset_event); - - /* - * In most cases it's guaranteed that we get here with an RPM - * reference held, for example because there is a pending GPU - * request that won't finish until the reset is done. This - * isn't the case at least when we get here by doing a - * simulated reset via debugs, so get an RPM reference. - */ - intel_runtime_pm_get(dev_priv); - - intel_prepare_reset(dev_priv); + intel_runtime_pm_get(dev_priv); + intel_prepare_reset(dev_priv); + do { /* * All state reset _must_ be completed before we update the * reset counter, for otherwise waiters might miss the reset * pending state and not properly drop locks, resulting in * deadlocks with the reset work. */ - ret = i915_reset(dev_priv); + if (mutex_trylock(&dev_priv->drm.struct_mutex)) { + i915_reset(dev_priv); + mutex_unlock(&dev_priv->drm.struct_mutex); + } - intel_finish_reset(dev_priv); + /* We need to wait for anyone holding the lock to wakeup */ + } while (wait_on_bit_timeout(&dev_priv->gpu_error.flags, + I915_RESET_IN_PROGRESS, + TASK_UNINTERRUPTIBLE, + HZ)); - intel_runtime_pm_put(dev_priv); + intel_finish_reset(dev_priv); + intel_runtime_pm_put(dev_priv); - if (ret == 0) - kobject_uevent_env(kobj, - KOBJ_CHANGE, reset_done_event); + if (!test_bit(I915_WEDGED, &dev_priv->gpu_error.flags)) + kobject_uevent_env(kobj, + KOBJ_CHANGE, reset_done_event); - /* - * Note: The wake_up also serves as a memory barrier so that - * waiters see the update value of the reset counter atomic_t. - */ - wake_up_all(&dev_priv->gpu_error.reset_queue); - } + /* + * Note: The wake_up also serves as a memory barrier so that + * waiters see the updated value of the dev_priv->gpu_error. + */ + wake_up_all(&dev_priv->gpu_error.reset_queue); } static void i915_report_and_clear_eir(struct drm_i915_private *dev_priv) @@ -2673,25 +2661,26 @@ void i915_handle_error(struct drm_i915_private *dev_priv, i915_capture_error_state(dev_priv, engine_mask, error_msg); i915_report_and_clear_eir(dev_priv); - if (engine_mask) { - atomic_or(I915_RESET_IN_PROGRESS_FLAG, - &dev_priv->gpu_error.reset_counter); + if (!engine_mask) + return; - /* - * Wakeup waiting processes so that the reset function - * i915_reset_and_wakeup doesn't deadlock trying to grab - * various locks. By bumping the reset counter first, the woken - * processes will see a reset in progress and back off, - * releasing their locks and then wait for the reset completion. - * We must do this for _all_ gpu waiters that might hold locks - * that the reset work needs to acquire. - * - * Note: The wake_up serves as the required memory barrier to - * ensure that the waiters see the updated value of the reset - * counter atomic_t. - */ - i915_error_wake_up(dev_priv); - } + if (test_and_set_bit(I915_RESET_IN_PROGRESS, + &dev_priv->gpu_error.flags)) + return; + + /* + * Wakeup waiting processes so that the reset function + * i915_reset_and_wakeup doesn't deadlock trying to grab + * various locks. By bumping the reset counter first, the woken + * processes will see a reset in progress and back off, + * releasing their locks and then wait for the reset completion. + * We must do this for _all_ gpu waiters that might hold locks + * that the reset work needs to acquire. + * + * Note: The wake_up also provides a memory barrier to ensure that the + * waiters see the updated value of the reset flags. + */ + i915_error_wake_up(dev_priv); i915_reset_and_wakeup(dev_priv); } @@ -2804,13 +2793,6 @@ static void gen8_disable_vblank(struct drm_device *dev, unsigned int pipe) } static bool -ring_idle(struct intel_engine_cs *engine, u32 seqno) -{ - return i915_seqno_passed(seqno, - READ_ONCE(engine->last_submitted_seqno)); -} - -static bool ipehr_is_semaphore_wait(struct intel_engine_cs *engine, u32 ipehr) { if (INTEL_GEN(engine->i915) >= 8) { @@ -2849,16 +2831,17 @@ semaphore_wait_to_signaller_ring(struct intel_engine_cs *engine, u32 ipehr, } } - DRM_ERROR("No signaller ring found for ring %i, ipehr 0x%08x, offset 0x%016llx\n", - engine->id, ipehr, offset); + DRM_DEBUG_DRIVER("No signaller ring found for ring %i, ipehr 0x%08x, offset 0x%016llx\n", + engine->id, ipehr, offset); - return NULL; + return ERR_PTR(-ENODEV); } static struct intel_engine_cs * semaphore_waits_for(struct intel_engine_cs *engine, u32 *seqno) { struct drm_i915_private *dev_priv = engine->i915; + void __iomem *vaddr; u32 cmd, ipehr, head; u64 offset = 0; int i, backwards; @@ -2897,6 +2880,7 @@ semaphore_waits_for(struct intel_engine_cs *engine, u32 *seqno) */ head = I915_READ_HEAD(engine) & HEAD_ADDR; backwards = (INTEL_GEN(dev_priv) >= 8) ? 5 : 4; + vaddr = (void __iomem *)engine->buffer->vaddr; for (i = backwards; i; --i) { /* @@ -2907,7 +2891,7 @@ semaphore_waits_for(struct intel_engine_cs *engine, u32 *seqno) head &= engine->buffer->size - 1; /* This here seems to blow up */ - cmd = ioread32(engine->buffer->virtual_start + head); + cmd = ioread32(vaddr + head); if (cmd == ipehr) break; @@ -2917,11 +2901,11 @@ semaphore_waits_for(struct intel_engine_cs *engine, u32 *seqno) if (!i) return NULL; - *seqno = ioread32(engine->buffer->virtual_start + head + 4) + 1; + *seqno = ioread32(vaddr + head + 4) + 1; if (INTEL_GEN(dev_priv) >= 8) { - offset = ioread32(engine->buffer->virtual_start + head + 12); + offset = ioread32(vaddr + head + 12); offset <<= 32; - offset = ioread32(engine->buffer->virtual_start + head + 8); + offset |= ioread32(vaddr + head + 8); } return semaphore_wait_to_signaller_ring(engine, ipehr, offset); } @@ -2938,6 +2922,9 @@ static int semaphore_passed(struct intel_engine_cs *engine) if (signaller == NULL) return -1; + if (IS_ERR(signaller)) + return 0; + /* Prevent pathological recursion due to driver bugs */ if (signaller->hangcheck.deadlock >= I915_NUM_ENGINES) return -1; @@ -2990,7 +2977,7 @@ static bool subunits_stuck(struct intel_engine_cs *engine) return stuck; } -static enum intel_ring_hangcheck_action +static enum intel_engine_hangcheck_action head_stuck(struct intel_engine_cs *engine, u64 acthd) { if (acthd != engine->hangcheck.acthd) { @@ -3008,11 +2995,11 @@ head_stuck(struct intel_engine_cs *engine, u64 acthd) return HANGCHECK_HUNG; } -static enum intel_ring_hangcheck_action -ring_stuck(struct intel_engine_cs *engine, u64 acthd) +static enum intel_engine_hangcheck_action +engine_stuck(struct intel_engine_cs *engine, u64 acthd) { struct drm_i915_private *dev_priv = engine->i915; - enum intel_ring_hangcheck_action ha; + enum intel_engine_hangcheck_action ha; u32 tmp; ha = head_stuck(engine, acthd); @@ -3054,22 +3041,6 @@ ring_stuck(struct intel_engine_cs *engine, u64 acthd) return HANGCHECK_HUNG; } -static unsigned long kick_waiters(struct intel_engine_cs *engine) -{ - struct drm_i915_private *i915 = engine->i915; - unsigned long irq_count = READ_ONCE(engine->breadcrumbs.irq_wakeups); - - if (engine->hangcheck.user_interrupts == irq_count && - !test_and_set_bit(engine->id, &i915->gpu_error.missed_irq_rings)) { - if (!test_bit(engine->id, &i915->gpu_error.test_irq_rings)) - DRM_ERROR("Hangcheck timer elapsed... %s idle\n", - engine->name); - - intel_engine_enable_fake_irq(engine); - } - - return irq_count; -} /* * This is called when the chip hasn't reported back with completed * batchbuffers in a long time. We keep track per ring seqno progress and @@ -3107,7 +3078,7 @@ static void i915_hangcheck_elapsed(struct work_struct *work) bool busy = intel_engine_has_waiter(engine); u64 acthd; u32 seqno; - unsigned user_interrupts; + u32 submit; semaphore_clear_deadlocks(dev_priv); @@ -3121,29 +3092,22 @@ static void i915_hangcheck_elapsed(struct work_struct *work) if (engine->irq_seqno_barrier) engine->irq_seqno_barrier(engine); - acthd = intel_ring_get_active_head(engine); + acthd = intel_engine_get_active_head(engine); seqno = intel_engine_get_seqno(engine); - - /* Reset stuck interrupts between batch advances */ - user_interrupts = 0; + submit = READ_ONCE(engine->last_submitted_seqno); if (engine->hangcheck.seqno == seqno) { - if (ring_idle(engine, seqno)) { + if (i915_seqno_passed(seqno, submit)) { engine->hangcheck.action = HANGCHECK_IDLE; - if (busy) { - /* Safeguard against driver failure */ - user_interrupts = kick_waiters(engine); - engine->hangcheck.score += BUSY; - } } else { /* We always increment the hangcheck score - * if the ring is busy and still processing + * if the engine is busy and still processing * the same request, so that no single request * can run indefinitely (such as a chain of * batches). The only time we do not increment * the hangcheck score on this ring, if this - * ring is in a legitimate wait for another - * ring. In that case the waiting ring is a + * engine is in a legitimate wait for another + * engine. In that case the waiting engine is a * victim and we want to be sure we catch the * right culprit. Then every time we do kick * the ring, add a small increment to the @@ -3151,8 +3115,8 @@ static void i915_hangcheck_elapsed(struct work_struct *work) * being repeatedly kicked and so responsible * for stalling the machine. */ - engine->hangcheck.action = ring_stuck(engine, - acthd); + engine->hangcheck.action = + engine_stuck(engine, acthd); switch (engine->hangcheck.action) { case HANGCHECK_IDLE: @@ -3195,12 +3159,12 @@ static void i915_hangcheck_elapsed(struct work_struct *work) engine->hangcheck.seqno = seqno; engine->hangcheck.acthd = acthd; - engine->hangcheck.user_interrupts = user_interrupts; busy_count += busy; } if (hung) { char msg[80]; + unsigned int tmp; int len; /* If some rings hung but others were still busy, only @@ -3210,7 +3174,7 @@ static void i915_hangcheck_elapsed(struct work_struct *work) hung &= ~stuck; len = scnprintf(msg, sizeof(msg), "%s on ", stuck == hung ? "No progress" : "Hang"); - for_each_engine_masked(engine, dev_priv, hung) + for_each_engine_masked(engine, dev_priv, hung, tmp) len += scnprintf(msg + len, sizeof(msg) - len, "%s, ", engine->name); msg[len-2] = '\0'; @@ -4536,14 +4500,15 @@ void intel_irq_init(struct drm_i915_private *dev_priv) dev_priv->rps.pm_intr_keep |= GEN6_PM_RP_UP_EI_EXPIRED; if (INTEL_INFO(dev_priv)->gen >= 8) - dev_priv->rps.pm_intr_keep |= GEN8_PMINTR_REDIRECT_TO_NON_DISP; + dev_priv->rps.pm_intr_keep |= GEN8_PMINTR_REDIRECT_TO_GUC; INIT_DELAYED_WORK(&dev_priv->gpu_error.hangcheck_work, i915_hangcheck_elapsed); if (IS_GEN2(dev_priv)) { + /* Gen2 doesn't have a hardware frame counter */ dev->max_vblank_count = 0; - dev->driver->get_vblank_counter = i8xx_get_vblank_counter; + dev->driver->get_vblank_counter = drm_vblank_no_hw_counter; } else if (IS_G4X(dev_priv) || INTEL_INFO(dev_priv)->gen >= 5) { dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */ dev->driver->get_vblank_counter = g4x_get_vblank_counter; diff --git a/drivers/gpu/drm/i915/i915_memcpy.c b/drivers/gpu/drm/i915/i915_memcpy.c new file mode 100644 index 0000000..49a0794 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_memcpy.c @@ -0,0 +1,101 @@ +/* + * Copyright © 2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#include <linux/kernel.h> +#include <asm/fpu/api.h> + +#include "i915_drv.h" + +static DEFINE_STATIC_KEY_FALSE(has_movntdqa); + +#ifdef CONFIG_AS_MOVNTDQA +static void __memcpy_ntdqa(void *dst, const void *src, unsigned long len) +{ + kernel_fpu_begin(); + + len >>= 4; + while (len >= 4) { + asm("movntdqa (%0), %%xmm0\n" + "movntdqa 16(%0), %%xmm1\n" + "movntdqa 32(%0), %%xmm2\n" + "movntdqa 48(%0), %%xmm3\n" + "movaps %%xmm0, (%1)\n" + "movaps %%xmm1, 16(%1)\n" + "movaps %%xmm2, 32(%1)\n" + "movaps %%xmm3, 48(%1)\n" + :: "r" (src), "r" (dst) : "memory"); + src += 64; + dst += 64; + len -= 4; + } + while (len--) { + asm("movntdqa (%0), %%xmm0\n" + "movaps %%xmm0, (%1)\n" + :: "r" (src), "r" (dst) : "memory"); + src += 16; + dst += 16; + } + + kernel_fpu_end(); +} +#endif + +/** + * i915_memcpy_from_wc: perform an accelerated *aligned* read from WC + * @dst: destination pointer + * @src: source pointer + * @len: how many bytes to copy + * + * i915_memcpy_from_wc copies @len bytes from @src to @dst using + * non-temporal instructions where available. Note that all arguments + * (@src, @dst) must be aligned to 16 bytes and @len must be a multiple + * of 16. + * + * To test whether accelerated reads from WC are supported, use + * i915_memcpy_from_wc(NULL, NULL, 0); + * + * Returns true if the copy was successful, false if the preconditions + * are not met. + */ +bool i915_memcpy_from_wc(void *dst, const void *src, unsigned long len) +{ + if (unlikely(((unsigned long)dst | (unsigned long)src | len) & 15)) + return false; + +#ifdef CONFIG_AS_MOVNTDQA + if (static_branch_likely(&has_movntdqa)) { + if (likely(len)) + __memcpy_ntdqa(dst, src, len); + return true; + } +#endif + + return false; +} + +void i915_memcpy_init_early(struct drm_i915_private *dev_priv) +{ + if (static_cpu_has(X86_FEATURE_XMM4_1)) + static_branch_enable(&has_movntdqa); +} diff --git a/drivers/gpu/drm/i915/i915_mm.c b/drivers/gpu/drm/i915/i915_mm.c new file mode 100644 index 0000000..e4935dd --- /dev/null +++ b/drivers/gpu/drm/i915/i915_mm.c @@ -0,0 +1,84 @@ +/* + * Copyright © 2014 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#include <linux/mm.h> +#include <linux/io-mapping.h> + +#include <asm/pgtable.h> + +#include "i915_drv.h" + +struct remap_pfn { + struct mm_struct *mm; + unsigned long pfn; + pgprot_t prot; +}; + +static int remap_pfn(pte_t *pte, pgtable_t token, + unsigned long addr, void *data) +{ + struct remap_pfn *r = data; + + /* Special PTE are not associated with any struct page */ + set_pte_at(r->mm, addr, pte, pte_mkspecial(pfn_pte(r->pfn, r->prot))); + r->pfn++; + + return 0; +} + +/** + * remap_io_mapping - remap an IO mapping to userspace + * @vma: user vma to map to + * @addr: target user address to start at + * @pfn: physical address of kernel memory + * @size: size of map area + * @iomap: the source io_mapping + * + * Note: this is only safe if the mm semaphore is held when called. + */ +int remap_io_mapping(struct vm_area_struct *vma, + unsigned long addr, unsigned long pfn, unsigned long size, + struct io_mapping *iomap) +{ + struct remap_pfn r; + int err; + + GEM_BUG_ON((vma->vm_flags & + (VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP)) != + (VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP)); + + /* We rely on prevalidation of the io-mapping to skip track_pfn(). */ + r.mm = vma->vm_mm; + r.pfn = pfn; + r.prot = __pgprot((pgprot_val(iomap->prot) & _PAGE_CACHE_MASK) | + (pgprot_val(vma->vm_page_prot) & ~_PAGE_CACHE_MASK)); + + err = apply_to_page_range(r.mm, addr, size, remap_pfn, &r); + if (unlikely(err)) { + zap_vma_ptes(vma, addr, (r.pfn - pfn) << PAGE_SHIFT); + return err; + } + + return 0; +} diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index b6e404c..768ad89 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -45,6 +45,7 @@ struct i915_params i915 __read_mostly = { .fastboot = 0, .prefault_disable = 0, .load_detect_test = 0, + .force_reset_modeset_test = 0, .reset = true, .invert_brightness = 0, .disable_display = 0, @@ -161,6 +162,11 @@ MODULE_PARM_DESC(load_detect_test, "Force-enable the VGA load detect code for testing (default:false). " "For developers only."); +module_param_named_unsafe(force_reset_modeset_test, i915.force_reset_modeset_test, bool, 0600); +MODULE_PARM_DESC(force_reset_modeset_test, + "Force a modeset during gpu reset for testing (default:false). " + "For developers only."); + module_param_named_unsafe(invert_brightness, i915.invert_brightness, int, 0600); MODULE_PARM_DESC(invert_brightness, "Invert backlight brightness " diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 0ad020b..3a0dd78 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -57,6 +57,7 @@ struct i915_params { bool fastboot; bool prefault_disable; bool load_detect_test; + bool force_reset_modeset_test; bool reset; bool disable_display; bool verbose_state_checks; diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c index 949c016..687c768 100644 --- a/drivers/gpu/drm/i915/i915_pci.c +++ b/drivers/gpu/drm/i915/i915_pci.c @@ -54,207 +54,216 @@ #define CHV_COLORS \ .color = { .degamma_lut_size = 65, .gamma_lut_size = 257 } +#define GEN2_FEATURES \ + .gen = 2, .num_pipes = 1, \ + .has_overlay = 1, .overlay_needs_physical = 1, \ + .has_gmch_display = 1, \ + .hws_needs_physical = 1, \ + .ring_mask = RENDER_RING, \ + GEN_DEFAULT_PIPEOFFSETS, \ + CURSOR_OFFSETS + static const struct intel_device_info intel_i830_info = { - .gen = 2, .is_mobile = 1, .cursor_needs_physical = 1, .num_pipes = 2, - .has_overlay = 1, .overlay_needs_physical = 1, - .ring_mask = RENDER_RING, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, + GEN2_FEATURES, + .is_mobile = 1, .cursor_needs_physical = 1, + .num_pipes = 2, /* legal, last one wins */ }; static const struct intel_device_info intel_845g_info = { - .gen = 2, .num_pipes = 1, - .has_overlay = 1, .overlay_needs_physical = 1, - .ring_mask = RENDER_RING, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, + GEN2_FEATURES, }; static const struct intel_device_info intel_i85x_info = { - .gen = 2, .is_i85x = 1, .is_mobile = 1, .num_pipes = 2, + GEN2_FEATURES, + .is_i85x = 1, .is_mobile = 1, + .num_pipes = 2, /* legal, last one wins */ .cursor_needs_physical = 1, - .has_overlay = 1, .overlay_needs_physical = 1, .has_fbc = 1, - .ring_mask = RENDER_RING, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, }; static const struct intel_device_info intel_i865g_info = { - .gen = 2, .num_pipes = 1, - .has_overlay = 1, .overlay_needs_physical = 1, - .ring_mask = RENDER_RING, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, + GEN2_FEATURES, }; +#define GEN3_FEATURES \ + .gen = 3, .num_pipes = 2, \ + .has_gmch_display = 1, \ + .ring_mask = RENDER_RING, \ + GEN_DEFAULT_PIPEOFFSETS, \ + CURSOR_OFFSETS + static const struct intel_device_info intel_i915g_info = { - .gen = 3, .is_i915g = 1, .cursor_needs_physical = 1, .num_pipes = 2, + GEN3_FEATURES, + .is_i915g = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, - .ring_mask = RENDER_RING, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, + .hws_needs_physical = 1, }; static const struct intel_device_info intel_i915gm_info = { - .gen = 3, .is_mobile = 1, .num_pipes = 2, + GEN3_FEATURES, + .is_mobile = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, .supports_tv = 1, .has_fbc = 1, - .ring_mask = RENDER_RING, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, + .hws_needs_physical = 1, }; static const struct intel_device_info intel_i945g_info = { - .gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1, .num_pipes = 2, + GEN3_FEATURES, + .has_hotplug = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, - .ring_mask = RENDER_RING, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, + .hws_needs_physical = 1, }; static const struct intel_device_info intel_i945gm_info = { - .gen = 3, .is_i945gm = 1, .is_mobile = 1, .num_pipes = 2, + GEN3_FEATURES, + .is_i945gm = 1, .is_mobile = 1, .has_hotplug = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, .supports_tv = 1, .has_fbc = 1, - .ring_mask = RENDER_RING, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, + .hws_needs_physical = 1, }; +#define GEN4_FEATURES \ + .gen = 4, .num_pipes = 2, \ + .has_hotplug = 1, \ + .has_gmch_display = 1, \ + .ring_mask = RENDER_RING, \ + GEN_DEFAULT_PIPEOFFSETS, \ + CURSOR_OFFSETS + static const struct intel_device_info intel_i965g_info = { - .gen = 4, .is_broadwater = 1, .num_pipes = 2, - .has_hotplug = 1, + GEN4_FEATURES, + .is_broadwater = 1, .has_overlay = 1, - .ring_mask = RENDER_RING, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, + .hws_needs_physical = 1, }; static const struct intel_device_info intel_i965gm_info = { - .gen = 4, .is_crestline = 1, .num_pipes = 2, - .is_mobile = 1, .has_fbc = 1, .has_hotplug = 1, + GEN4_FEATURES, + .is_crestline = 1, + .is_mobile = 1, .has_fbc = 1, .has_overlay = 1, .supports_tv = 1, - .ring_mask = RENDER_RING, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, + .hws_needs_physical = 1, }; static const struct intel_device_info intel_g33_info = { - .gen = 3, .is_g33 = 1, .num_pipes = 2, - .need_gfx_hws = 1, .has_hotplug = 1, + GEN3_FEATURES, + .is_g33 = 1, + .has_hotplug = 1, .has_overlay = 1, - .ring_mask = RENDER_RING, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, }; static const struct intel_device_info intel_g45_info = { - .gen = 4, .is_g4x = 1, .need_gfx_hws = 1, .num_pipes = 2, - .has_pipe_cxsr = 1, .has_hotplug = 1, + GEN4_FEATURES, + .is_g4x = 1, + .has_pipe_cxsr = 1, .ring_mask = RENDER_RING | BSD_RING, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, }; static const struct intel_device_info intel_gm45_info = { - .gen = 4, .is_g4x = 1, .num_pipes = 2, - .is_mobile = 1, .need_gfx_hws = 1, .has_fbc = 1, - .has_pipe_cxsr = 1, .has_hotplug = 1, + GEN4_FEATURES, + .is_g4x = 1, + .is_mobile = 1, .has_fbc = 1, + .has_pipe_cxsr = 1, .supports_tv = 1, .ring_mask = RENDER_RING | BSD_RING, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, }; static const struct intel_device_info intel_pineview_info = { - .gen = 3, .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, .num_pipes = 2, - .need_gfx_hws = 1, .has_hotplug = 1, + GEN3_FEATURES, + .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, + .has_hotplug = 1, .has_overlay = 1, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, }; +#define GEN5_FEATURES \ + .gen = 5, .num_pipes = 2, \ + .has_hotplug = 1, \ + .has_gmbus_irq = 1, \ + .ring_mask = RENDER_RING | BSD_RING, \ + GEN_DEFAULT_PIPEOFFSETS, \ + CURSOR_OFFSETS + static const struct intel_device_info intel_ironlake_d_info = { - .gen = 5, .num_pipes = 2, - .need_gfx_hws = 1, .has_hotplug = 1, - .ring_mask = RENDER_RING | BSD_RING, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, + GEN5_FEATURES, }; static const struct intel_device_info intel_ironlake_m_info = { - .gen = 5, .is_mobile = 1, .num_pipes = 2, - .need_gfx_hws = 1, .has_hotplug = 1, - .has_fbc = 1, - .ring_mask = RENDER_RING | BSD_RING, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, + GEN5_FEATURES, + .is_mobile = 1, }; +#define GEN6_FEATURES \ + .gen = 6, .num_pipes = 2, \ + .has_hotplug = 1, \ + .has_fbc = 1, \ + .ring_mask = RENDER_RING | BSD_RING | BLT_RING, \ + .has_llc = 1, \ + .has_rc6 = 1, \ + .has_rc6p = 1, \ + .has_gmbus_irq = 1, \ + .has_hw_contexts = 1, \ + GEN_DEFAULT_PIPEOFFSETS, \ + CURSOR_OFFSETS + static const struct intel_device_info intel_sandybridge_d_info = { - .gen = 6, .num_pipes = 2, - .need_gfx_hws = 1, .has_hotplug = 1, - .has_fbc = 1, - .ring_mask = RENDER_RING | BSD_RING | BLT_RING, - .has_llc = 1, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, + GEN6_FEATURES, }; static const struct intel_device_info intel_sandybridge_m_info = { - .gen = 6, .is_mobile = 1, .num_pipes = 2, - .need_gfx_hws = 1, .has_hotplug = 1, - .has_fbc = 1, - .ring_mask = RENDER_RING | BSD_RING | BLT_RING, - .has_llc = 1, - GEN_DEFAULT_PIPEOFFSETS, - CURSOR_OFFSETS, + GEN6_FEATURES, + .is_mobile = 1, }; #define GEN7_FEATURES \ .gen = 7, .num_pipes = 3, \ - .need_gfx_hws = 1, .has_hotplug = 1, \ + .has_hotplug = 1, \ .has_fbc = 1, \ .ring_mask = RENDER_RING | BSD_RING | BLT_RING, \ .has_llc = 1, \ + .has_rc6 = 1, \ + .has_rc6p = 1, \ + .has_gmbus_irq = 1, \ + .has_hw_contexts = 1, \ GEN_DEFAULT_PIPEOFFSETS, \ IVB_CURSOR_OFFSETS static const struct intel_device_info intel_ivybridge_d_info = { GEN7_FEATURES, .is_ivybridge = 1, + .has_l3_dpf = 1, }; static const struct intel_device_info intel_ivybridge_m_info = { GEN7_FEATURES, .is_ivybridge = 1, .is_mobile = 1, + .has_l3_dpf = 1, }; static const struct intel_device_info intel_ivybridge_q_info = { GEN7_FEATURES, .is_ivybridge = 1, .num_pipes = 0, /* legal, last one wins */ + .has_l3_dpf = 1, }; #define VLV_FEATURES \ .gen = 7, .num_pipes = 2, \ - .need_gfx_hws = 1, .has_hotplug = 1, \ + .has_psr = 1, \ + .has_runtime_pm = 1, \ + .has_rc6 = 1, \ + .has_gmbus_irq = 1, \ + .has_hw_contexts = 1, \ + .has_gmch_display = 1, \ + .has_hotplug = 1, \ .ring_mask = RENDER_RING | BSD_RING | BLT_RING, \ .display_mmio_offset = VLV_DISPLAY_BASE, \ GEN_DEFAULT_PIPEOFFSETS, \ CURSOR_OFFSETS -static const struct intel_device_info intel_valleyview_m_info = { - VLV_FEATURES, - .is_valleyview = 1, - .is_mobile = 1, -}; - -static const struct intel_device_info intel_valleyview_d_info = { +static const struct intel_device_info intel_valleyview_info = { VLV_FEATURES, .is_valleyview = 1, }; @@ -263,54 +272,50 @@ static const struct intel_device_info intel_valleyview_d_info = { GEN7_FEATURES, \ .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING, \ .has_ddi = 1, \ - .has_fpga_dbg = 1 - -static const struct intel_device_info intel_haswell_d_info = { - HSW_FEATURES, - .is_haswell = 1, -}; - -static const struct intel_device_info intel_haswell_m_info = { + .has_fpga_dbg = 1, \ + .has_psr = 1, \ + .has_resource_streamer = 1, \ + .has_dp_mst = 1, \ + .has_rc6p = 0 /* RC6p removed-by HSW */, \ + .has_runtime_pm = 1 + +static const struct intel_device_info intel_haswell_info = { HSW_FEATURES, .is_haswell = 1, - .is_mobile = 1, + .has_l3_dpf = 1, }; #define BDW_FEATURES \ HSW_FEATURES, \ - BDW_COLORS + BDW_COLORS, \ + .has_logical_ring_contexts = 1 -static const struct intel_device_info intel_broadwell_d_info = { +static const struct intel_device_info intel_broadwell_info = { BDW_FEATURES, .gen = 8, .is_broadwell = 1, }; -static const struct intel_device_info intel_broadwell_m_info = { - BDW_FEATURES, - .gen = 8, .is_mobile = 1, - .is_broadwell = 1, -}; - -static const struct intel_device_info intel_broadwell_gt3d_info = { +static const struct intel_device_info intel_broadwell_gt3_info = { BDW_FEATURES, .gen = 8, .is_broadwell = 1, .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING, }; -static const struct intel_device_info intel_broadwell_gt3m_info = { - BDW_FEATURES, - .gen = 8, .is_mobile = 1, - .is_broadwell = 1, - .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING, -}; - static const struct intel_device_info intel_cherryview_info = { .gen = 8, .num_pipes = 3, - .need_gfx_hws = 1, .has_hotplug = 1, + .has_hotplug = 1, .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING, .is_cherryview = 1, + .has_psr = 1, + .has_runtime_pm = 1, + .has_resource_streamer = 1, + .has_rc6 = 1, + .has_gmbus_irq = 1, + .has_hw_contexts = 1, + .has_logical_ring_contexts = 1, + .has_gmch_display = 1, .display_mmio_offset = VLV_DISPLAY_BASE, GEN_CHV_PIPEOFFSETS, CURSOR_OFFSETS, @@ -321,25 +326,41 @@ static const struct intel_device_info intel_skylake_info = { BDW_FEATURES, .is_skylake = 1, .gen = 9, + .has_csr = 1, + .has_guc = 1, + .ddb_size = 896, }; static const struct intel_device_info intel_skylake_gt3_info = { BDW_FEATURES, .is_skylake = 1, .gen = 9, + .has_csr = 1, + .has_guc = 1, + .ddb_size = 896, .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING, }; static const struct intel_device_info intel_broxton_info = { .is_broxton = 1, .gen = 9, - .need_gfx_hws = 1, .has_hotplug = 1, + .has_hotplug = 1, .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING, .num_pipes = 3, .has_ddi = 1, .has_fpga_dbg = 1, .has_fbc = 1, + .has_runtime_pm = 1, .has_pooled_eu = 0, + .has_csr = 1, + .has_resource_streamer = 1, + .has_rc6 = 1, + .has_dp_mst = 1, + .has_gmbus_irq = 1, + .has_hw_contexts = 1, + .has_logical_ring_contexts = 1, + .has_guc = 1, + .ddb_size = 512, GEN_DEFAULT_PIPEOFFSETS, IVB_CURSOR_OFFSETS, BDW_COLORS, @@ -349,12 +370,18 @@ static const struct intel_device_info intel_kabylake_info = { BDW_FEATURES, .is_kabylake = 1, .gen = 9, + .has_csr = 1, + .has_guc = 1, + .ddb_size = 896, }; static const struct intel_device_info intel_kabylake_gt3_info = { BDW_FEATURES, .is_kabylake = 1, .gen = 9, + .has_csr = 1, + .has_guc = 1, + .ddb_size = 896, .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING, }; @@ -386,14 +413,10 @@ static const struct pci_device_id pciidlist[] = { INTEL_IVB_Q_IDS(&intel_ivybridge_q_info), /* must be first IVB */ INTEL_IVB_M_IDS(&intel_ivybridge_m_info), INTEL_IVB_D_IDS(&intel_ivybridge_d_info), - INTEL_HSW_D_IDS(&intel_haswell_d_info), - INTEL_HSW_M_IDS(&intel_haswell_m_info), - INTEL_VLV_M_IDS(&intel_valleyview_m_info), - INTEL_VLV_D_IDS(&intel_valleyview_d_info), - INTEL_BDW_GT12M_IDS(&intel_broadwell_m_info), - INTEL_BDW_GT12D_IDS(&intel_broadwell_d_info), - INTEL_BDW_GT3M_IDS(&intel_broadwell_gt3m_info), - INTEL_BDW_GT3D_IDS(&intel_broadwell_gt3d_info), + INTEL_HSW_IDS(&intel_haswell_info), + INTEL_VLV_IDS(&intel_valleyview_info), + INTEL_BDW_GT12_IDS(&intel_broadwell_info), + INTEL_BDW_GT3_IDS(&intel_broadwell_gt3_info), INTEL_CHV_IDS(&intel_cherryview_info), INTEL_SKL_GT1_IDS(&intel_skylake_info), INTEL_SKL_GT2_IDS(&intel_skylake_info), diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index ce14fe0..70d9616 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -186,13 +186,13 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) #define GEN9_GRDOM_GUC (1 << 5) #define GEN8_GRDOM_MEDIA2 (1 << 7) -#define RING_PP_DIR_BASE(ring) _MMIO((ring)->mmio_base+0x228) -#define RING_PP_DIR_BASE_READ(ring) _MMIO((ring)->mmio_base+0x518) -#define RING_PP_DIR_DCLV(ring) _MMIO((ring)->mmio_base+0x220) +#define RING_PP_DIR_BASE(engine) _MMIO((engine)->mmio_base+0x228) +#define RING_PP_DIR_BASE_READ(engine) _MMIO((engine)->mmio_base+0x518) +#define RING_PP_DIR_DCLV(engine) _MMIO((engine)->mmio_base+0x220) #define PP_DIR_DCLV_2G 0xffffffff -#define GEN8_RING_PDP_UDW(ring, n) _MMIO((ring)->mmio_base+0x270 + (n) * 8 + 4) -#define GEN8_RING_PDP_LDW(ring, n) _MMIO((ring)->mmio_base+0x270 + (n) * 8) +#define GEN8_RING_PDP_UDW(engine, n) _MMIO((engine)->mmio_base+0x270 + (n) * 8 + 4) +#define GEN8_RING_PDP_LDW(engine, n) _MMIO((engine)->mmio_base+0x270 + (n) * 8) #define GEN8_R_PWR_CLK_STATE _MMIO(0x20C8) #define GEN8_RPCS_ENABLE (1 << 31) @@ -1536,6 +1536,7 @@ enum skl_disp_power_wells { #define BALANCE_LEG_MASK(port) (7<<(8+3*(port))) /* Balance leg disable bits */ #define BALANCE_LEG_DISABLE_SHIFT 23 +#define BALANCE_LEG_DISABLE(port) (1 << (23 + (port))) /* * Fence registers @@ -1647,7 +1648,7 @@ enum skl_disp_power_wells { #define ARB_MODE_BWGTLB_DISABLE (1<<9) #define ARB_MODE_SWIZZLE_BDW (1<<1) #define RENDER_HWS_PGA_GEN7 _MMIO(0x04080) -#define RING_FAULT_REG(ring) _MMIO(0x4094 + 0x100*(ring)->id) +#define RING_FAULT_REG(engine) _MMIO(0x4094 + 0x100*(engine)->hw_id) #define RING_FAULT_GTTSEL_MASK (1<<11) #define RING_FAULT_SRCID(x) (((x) >> 3) & 0xff) #define RING_FAULT_FAULT_TYPE(x) (((x) >> 1) & 0x3) @@ -1845,7 +1846,7 @@ enum skl_disp_power_wells { #define GFX_MODE _MMIO(0x2520) #define GFX_MODE_GEN7 _MMIO(0x229c) -#define RING_MODE_GEN7(ring) _MMIO((ring)->mmio_base+0x29c) +#define RING_MODE_GEN7(engine) _MMIO((engine)->mmio_base+0x29c) #define GFX_RUN_LIST_ENABLE (1<<15) #define GFX_INTERRUPT_STEERING (1<<14) #define GFX_TLB_INVALIDATE_EXPLICIT (1<<13) @@ -3659,8 +3660,17 @@ enum { #define VIDEO_DIP_ENABLE_SPD_HSW (1 << 0) /* Panel power sequencing */ -#define PP_STATUS _MMIO(0x61200) -#define PP_ON (1 << 31) +#define PPS_BASE 0x61200 +#define VLV_PPS_BASE (VLV_DISPLAY_BASE + PPS_BASE) +#define PCH_PPS_BASE 0xC7200 + +#define _MMIO_PPS(pps_idx, reg) _MMIO(dev_priv->pps_mmio_base - \ + PPS_BASE + (reg) + \ + (pps_idx) * 0x100) + +#define _PP_STATUS 0x61200 +#define PP_STATUS(pps_idx) _MMIO_PPS(pps_idx, _PP_STATUS) +#define PP_ON (1 << 31) /* * Indicates that all dependencies of the panel are on: * @@ -3668,14 +3678,14 @@ enum { * - pipe enabled * - LVDS/DVOB/DVOC on */ -#define PP_READY (1 << 30) -#define PP_SEQUENCE_NONE (0 << 28) -#define PP_SEQUENCE_POWER_UP (1 << 28) -#define PP_SEQUENCE_POWER_DOWN (2 << 28) -#define PP_SEQUENCE_MASK (3 << 28) -#define PP_SEQUENCE_SHIFT 28 -#define PP_CYCLE_DELAY_ACTIVE (1 << 27) -#define PP_SEQUENCE_STATE_MASK 0x0000000f +#define PP_READY (1 << 30) +#define PP_SEQUENCE_NONE (0 << 28) +#define PP_SEQUENCE_POWER_UP (1 << 28) +#define PP_SEQUENCE_POWER_DOWN (2 << 28) +#define PP_SEQUENCE_MASK (3 << 28) +#define PP_SEQUENCE_SHIFT 28 +#define PP_CYCLE_DELAY_ACTIVE (1 << 27) +#define PP_SEQUENCE_STATE_MASK 0x0000000f #define PP_SEQUENCE_STATE_OFF_IDLE (0x0 << 0) #define PP_SEQUENCE_STATE_OFF_S0_1 (0x1 << 0) #define PP_SEQUENCE_STATE_OFF_S0_2 (0x2 << 0) @@ -3685,11 +3695,46 @@ enum { #define PP_SEQUENCE_STATE_ON_S1_2 (0xa << 0) #define PP_SEQUENCE_STATE_ON_S1_3 (0xb << 0) #define PP_SEQUENCE_STATE_RESET (0xf << 0) -#define PP_CONTROL _MMIO(0x61204) -#define POWER_TARGET_ON (1 << 0) -#define PP_ON_DELAYS _MMIO(0x61208) -#define PP_OFF_DELAYS _MMIO(0x6120c) -#define PP_DIVISOR _MMIO(0x61210) + +#define _PP_CONTROL 0x61204 +#define PP_CONTROL(pps_idx) _MMIO_PPS(pps_idx, _PP_CONTROL) +#define PANEL_UNLOCK_REGS (0xabcd << 16) +#define PANEL_UNLOCK_MASK (0xffff << 16) +#define BXT_POWER_CYCLE_DELAY_MASK 0x1f0 +#define BXT_POWER_CYCLE_DELAY_SHIFT 4 +#define EDP_FORCE_VDD (1 << 3) +#define EDP_BLC_ENABLE (1 << 2) +#define PANEL_POWER_RESET (1 << 1) +#define PANEL_POWER_OFF (0 << 0) +#define PANEL_POWER_ON (1 << 0) + +#define _PP_ON_DELAYS 0x61208 +#define PP_ON_DELAYS(pps_idx) _MMIO_PPS(pps_idx, _PP_ON_DELAYS) +#define PANEL_PORT_SELECT_SHIFT 30 +#define PANEL_PORT_SELECT_MASK (3 << 30) +#define PANEL_PORT_SELECT_LVDS (0 << 30) +#define PANEL_PORT_SELECT_DPA (1 << 30) +#define PANEL_PORT_SELECT_DPC (2 << 30) +#define PANEL_PORT_SELECT_DPD (3 << 30) +#define PANEL_PORT_SELECT_VLV(port) ((port) << 30) +#define PANEL_POWER_UP_DELAY_MASK 0x1fff0000 +#define PANEL_POWER_UP_DELAY_SHIFT 16 +#define PANEL_LIGHT_ON_DELAY_MASK 0x1fff +#define PANEL_LIGHT_ON_DELAY_SHIFT 0 + +#define _PP_OFF_DELAYS 0x6120C +#define PP_OFF_DELAYS(pps_idx) _MMIO_PPS(pps_idx, _PP_OFF_DELAYS) +#define PANEL_POWER_DOWN_DELAY_MASK 0x1fff0000 +#define PANEL_POWER_DOWN_DELAY_SHIFT 16 +#define PANEL_LIGHT_OFF_DELAY_MASK 0x1fff +#define PANEL_LIGHT_OFF_DELAY_SHIFT 0 + +#define _PP_DIVISOR 0x61210 +#define PP_DIVISOR(pps_idx) _MMIO_PPS(pps_idx, _PP_DIVISOR) +#define PP_REFERENCE_DIVIDER_MASK 0xffffff00 +#define PP_REFERENCE_DIVIDER_SHIFT 8 +#define PANEL_POWER_CYCLE_DELAY_MASK 0x1f +#define PANEL_POWER_CYCLE_DELAY_SHIFT 0 /* Panel fitting */ #define PFIT_CONTROL _MMIO(dev_priv->info.display_mmio_offset + 0x61230) @@ -6132,6 +6177,7 @@ enum { # define GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC ((1<<10) | (1<<26)) # define GEN9_RHWO_OPTIMIZATION_DISABLE (1<<14) #define COMMON_SLICE_CHICKEN2 _MMIO(0x7014) +# define GEN9_DISABLE_GATHER_AT_SET_SHADER_COMMON_SLICE (1<<12) # define GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION (1<<8) # define GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE (1<<0) @@ -6748,77 +6794,6 @@ enum { #define PCH_LVDS _MMIO(0xe1180) #define LVDS_DETECTED (1 << 1) -/* vlv has 2 sets of panel control regs. */ -#define _PIPEA_PP_STATUS (VLV_DISPLAY_BASE + 0x61200) -#define _PIPEA_PP_CONTROL (VLV_DISPLAY_BASE + 0x61204) -#define _PIPEA_PP_ON_DELAYS (VLV_DISPLAY_BASE + 0x61208) -#define PANEL_PORT_SELECT_VLV(port) ((port) << 30) -#define _PIPEA_PP_OFF_DELAYS (VLV_DISPLAY_BASE + 0x6120c) -#define _PIPEA_PP_DIVISOR (VLV_DISPLAY_BASE + 0x61210) - -#define _PIPEB_PP_STATUS (VLV_DISPLAY_BASE + 0x61300) -#define _PIPEB_PP_CONTROL (VLV_DISPLAY_BASE + 0x61304) -#define _PIPEB_PP_ON_DELAYS (VLV_DISPLAY_BASE + 0x61308) -#define _PIPEB_PP_OFF_DELAYS (VLV_DISPLAY_BASE + 0x6130c) -#define _PIPEB_PP_DIVISOR (VLV_DISPLAY_BASE + 0x61310) - -#define VLV_PIPE_PP_STATUS(pipe) _MMIO_PIPE(pipe, _PIPEA_PP_STATUS, _PIPEB_PP_STATUS) -#define VLV_PIPE_PP_CONTROL(pipe) _MMIO_PIPE(pipe, _PIPEA_PP_CONTROL, _PIPEB_PP_CONTROL) -#define VLV_PIPE_PP_ON_DELAYS(pipe) _MMIO_PIPE(pipe, _PIPEA_PP_ON_DELAYS, _PIPEB_PP_ON_DELAYS) -#define VLV_PIPE_PP_OFF_DELAYS(pipe) _MMIO_PIPE(pipe, _PIPEA_PP_OFF_DELAYS, _PIPEB_PP_OFF_DELAYS) -#define VLV_PIPE_PP_DIVISOR(pipe) _MMIO_PIPE(pipe, _PIPEA_PP_DIVISOR, _PIPEB_PP_DIVISOR) - -#define _PCH_PP_STATUS 0xc7200 -#define _PCH_PP_CONTROL 0xc7204 -#define PANEL_UNLOCK_REGS (0xabcd << 16) -#define PANEL_UNLOCK_MASK (0xffff << 16) -#define BXT_POWER_CYCLE_DELAY_MASK (0x1f0) -#define BXT_POWER_CYCLE_DELAY_SHIFT 4 -#define EDP_FORCE_VDD (1 << 3) -#define EDP_BLC_ENABLE (1 << 2) -#define PANEL_POWER_RESET (1 << 1) -#define PANEL_POWER_OFF (0 << 0) -#define PANEL_POWER_ON (1 << 0) -#define _PCH_PP_ON_DELAYS 0xc7208 -#define PANEL_PORT_SELECT_MASK (3 << 30) -#define PANEL_PORT_SELECT_LVDS (0 << 30) -#define PANEL_PORT_SELECT_DPA (1 << 30) -#define PANEL_PORT_SELECT_DPC (2 << 30) -#define PANEL_PORT_SELECT_DPD (3 << 30) -#define PANEL_POWER_UP_DELAY_MASK (0x1fff0000) -#define PANEL_POWER_UP_DELAY_SHIFT 16 -#define PANEL_LIGHT_ON_DELAY_MASK (0x1fff) -#define PANEL_LIGHT_ON_DELAY_SHIFT 0 - -#define _PCH_PP_OFF_DELAYS 0xc720c -#define PANEL_POWER_DOWN_DELAY_MASK (0x1fff0000) -#define PANEL_POWER_DOWN_DELAY_SHIFT 16 -#define PANEL_LIGHT_OFF_DELAY_MASK (0x1fff) -#define PANEL_LIGHT_OFF_DELAY_SHIFT 0 - -#define _PCH_PP_DIVISOR 0xc7210 -#define PP_REFERENCE_DIVIDER_MASK (0xffffff00) -#define PP_REFERENCE_DIVIDER_SHIFT 8 -#define PANEL_POWER_CYCLE_DELAY_MASK (0x1f) -#define PANEL_POWER_CYCLE_DELAY_SHIFT 0 - -#define PCH_PP_STATUS _MMIO(_PCH_PP_STATUS) -#define PCH_PP_CONTROL _MMIO(_PCH_PP_CONTROL) -#define PCH_PP_ON_DELAYS _MMIO(_PCH_PP_ON_DELAYS) -#define PCH_PP_OFF_DELAYS _MMIO(_PCH_PP_OFF_DELAYS) -#define PCH_PP_DIVISOR _MMIO(_PCH_PP_DIVISOR) - -/* BXT PPS changes - 2nd set of PPS registers */ -#define _BXT_PP_STATUS2 0xc7300 -#define _BXT_PP_CONTROL2 0xc7304 -#define _BXT_PP_ON_DELAYS2 0xc7308 -#define _BXT_PP_OFF_DELAYS2 0xc730c - -#define BXT_PP_STATUS(n) _MMIO_PIPE(n, _PCH_PP_STATUS, _BXT_PP_STATUS2) -#define BXT_PP_CONTROL(n) _MMIO_PIPE(n, _PCH_PP_CONTROL, _BXT_PP_CONTROL2) -#define BXT_PP_ON_DELAYS(n) _MMIO_PIPE(n, _PCH_PP_ON_DELAYS, _BXT_PP_ON_DELAYS2) -#define BXT_PP_OFF_DELAYS(n) _MMIO_PIPE(n, _PCH_PP_OFF_DELAYS, _BXT_PP_OFF_DELAYS2) - #define _PCH_DP_B 0xe4100 #define PCH_DP_B _MMIO(_PCH_DP_B) #define _PCH_DPB_AUX_CH_CTL 0xe4110 @@ -6958,6 +6933,9 @@ enum { #define ECOBUS _MMIO(0xa180) #define FORCEWAKE_MT_ENABLE (1<<5) #define VLV_SPAREG2H _MMIO(0xA194) +#define GEN9_PWRGT_DOMAIN_STATUS _MMIO(0xA2A0) +#define GEN9_PWRGT_MEDIA_STATUS_MASK (1 << 0) +#define GEN9_PWRGT_RENDER_STATUS_MASK (1 << 1) #define GTFIFODBG _MMIO(0x120000) #define GT_FIFO_SBDEDICATE_FREE_ENTRY_CHV (0x1f << 20) @@ -7058,12 +7036,13 @@ enum { #define GEN6_RP_UP_THRESHOLD _MMIO(0xA02C) #define GEN6_RP_DOWN_THRESHOLD _MMIO(0xA030) #define GEN6_RP_CUR_UP_EI _MMIO(0xA050) -#define GEN6_CURICONT_MASK 0xffffff +#define GEN6_RP_EI_MASK 0xffffff +#define GEN6_CURICONT_MASK GEN6_RP_EI_MASK #define GEN6_RP_CUR_UP _MMIO(0xA054) -#define GEN6_CURBSYTAVG_MASK 0xffffff +#define GEN6_CURBSYTAVG_MASK GEN6_RP_EI_MASK #define GEN6_RP_PREV_UP _MMIO(0xA058) #define GEN6_RP_CUR_DOWN_EI _MMIO(0xA05C) -#define GEN6_CURIAVG_MASK 0xffffff +#define GEN6_CURIAVG_MASK GEN6_RP_EI_MASK #define GEN6_RP_CUR_DOWN _MMIO(0xA060) #define GEN6_RP_PREV_DOWN _MMIO(0xA064) #define GEN6_RP_UP_EI _MMIO(0xA068) @@ -7088,7 +7067,7 @@ enum { #define VLV_RCEDATA _MMIO(0xA0BC) #define GEN6_RC6pp_THRESHOLD _MMIO(0xA0C0) #define GEN6_PMINTRMSK _MMIO(0xA168) -#define GEN8_PMINTR_REDIRECT_TO_NON_DISP (1<<31) +#define GEN8_PMINTR_REDIRECT_TO_GUC (1<<31) #define GEN8_MISC_CTRL0 _MMIO(0xA180) #define VLV_PWRDWNUPCTL _MMIO(0xA294) #define GEN9_MEDIA_PG_IDLE_HYSTERESIS _MMIO(0xA0C4) @@ -7144,6 +7123,15 @@ enum { #define GEN6_PCODE_MAILBOX _MMIO(0x138124) #define GEN6_PCODE_READY (1<<31) +#define GEN6_PCODE_ERROR_MASK 0xFF +#define GEN6_PCODE_SUCCESS 0x0 +#define GEN6_PCODE_ILLEGAL_CMD 0x1 +#define GEN6_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE 0x2 +#define GEN6_PCODE_TIMEOUT 0x3 +#define GEN6_PCODE_UNIMPLEMENTED_CMD 0xFF +#define GEN7_PCODE_TIMEOUT 0x2 +#define GEN7_PCODE_ILLEGAL_DATA 0x3 +#define GEN7_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE 0x10 #define GEN6_PCODE_WRITE_RC6VIDS 0x4 #define GEN6_PCODE_READ_RC6VIDS 0x5 #define GEN6_ENCODE_RC6_VID(mv) (((mv) - 245) / 5) @@ -7165,6 +7153,10 @@ enum { #define HSW_PCODE_DE_WRITE_FREQ_REQ 0x17 #define DISPLAY_IPS_CONTROL 0x19 #define HSW_PCODE_DYNAMIC_DUTY_CYCLE_CONTROL 0x1A +#define GEN9_PCODE_SAGV_CONTROL 0x21 +#define GEN9_SAGV_DISABLE 0x0 +#define GEN9_SAGV_IS_DISABLED 0x1 +#define GEN9_SAGV_ENABLE 0x3 #define GEN6_PCODE_DATA _MMIO(0x138128) #define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8 #define GEN6_PCODE_FREQ_RING_RATIO_SHIFT 16 @@ -7485,6 +7477,7 @@ enum { #define _DDI_BUF_TRANS_A 0x64E00 #define _DDI_BUF_TRANS_B 0x64E60 #define DDI_BUF_TRANS_LO(port, i) _MMIO(_PORT(port, _DDI_BUF_TRANS_A, _DDI_BUF_TRANS_B) + (i) * 8) +#define DDI_BUF_BALANCE_LEG_ENABLE (1 << 31) #define DDI_BUF_TRANS_HI(port, i) _MMIO(_PORT(port, _DDI_BUF_TRANS_A, _DDI_BUF_TRANS_B) + (i) * 8 + 4) /* Sideband Interface (SBI) is programmed indirectly, via diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 5cfe4c7..a0af170 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -37,25 +37,6 @@ static void i915_save_display(struct drm_device *dev) if (INTEL_INFO(dev)->gen <= 4) dev_priv->regfile.saveDSPARB = I915_READ(DSPARB); - /* LVDS state */ - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) - dev_priv->regfile.saveLVDS = I915_READ(PCH_LVDS); - else if (INTEL_INFO(dev)->gen <= 4 && IS_MOBILE(dev) && !IS_I830(dev)) - dev_priv->regfile.saveLVDS = I915_READ(LVDS); - - /* Panel power sequencer */ - if (HAS_PCH_SPLIT(dev)) { - dev_priv->regfile.savePP_CONTROL = I915_READ(PCH_PP_CONTROL); - dev_priv->regfile.savePP_ON_DELAYS = I915_READ(PCH_PP_ON_DELAYS); - dev_priv->regfile.savePP_OFF_DELAYS = I915_READ(PCH_PP_OFF_DELAYS); - dev_priv->regfile.savePP_DIVISOR = I915_READ(PCH_PP_DIVISOR); - } else if (INTEL_INFO(dev)->gen <= 4) { - dev_priv->regfile.savePP_CONTROL = I915_READ(PP_CONTROL); - dev_priv->regfile.savePP_ON_DELAYS = I915_READ(PP_ON_DELAYS); - dev_priv->regfile.savePP_OFF_DELAYS = I915_READ(PP_OFF_DELAYS); - dev_priv->regfile.savePP_DIVISOR = I915_READ(PP_DIVISOR); - } - /* save FBC interval */ if (HAS_FBC(dev) && INTEL_INFO(dev)->gen <= 4 && !IS_G4X(dev)) dev_priv->regfile.saveFBC_CONTROL = I915_READ(FBC_CONTROL); @@ -64,33 +45,11 @@ static void i915_save_display(struct drm_device *dev) static void i915_restore_display(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); - u32 mask = 0xffffffff; /* Display arbitration */ if (INTEL_INFO(dev)->gen <= 4) I915_WRITE(DSPARB, dev_priv->regfile.saveDSPARB); - mask = ~LVDS_PORT_EN; - - /* LVDS state */ - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) - I915_WRITE(PCH_LVDS, dev_priv->regfile.saveLVDS & mask); - else if (INTEL_INFO(dev)->gen <= 4 && IS_MOBILE(dev) && !IS_I830(dev)) - I915_WRITE(LVDS, dev_priv->regfile.saveLVDS & mask); - - /* Panel power sequencer */ - if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(PCH_PP_ON_DELAYS, dev_priv->regfile.savePP_ON_DELAYS); - I915_WRITE(PCH_PP_OFF_DELAYS, dev_priv->regfile.savePP_OFF_DELAYS); - I915_WRITE(PCH_PP_DIVISOR, dev_priv->regfile.savePP_DIVISOR); - I915_WRITE(PCH_PP_CONTROL, dev_priv->regfile.savePP_CONTROL); - } else if (INTEL_INFO(dev)->gen <= 4) { - I915_WRITE(PP_ON_DELAYS, dev_priv->regfile.savePP_ON_DELAYS); - I915_WRITE(PP_OFF_DELAYS, dev_priv->regfile.savePP_OFF_DELAYS); - I915_WRITE(PP_DIVISOR, dev_priv->regfile.savePP_DIVISOR); - I915_WRITE(PP_CONTROL, dev_priv->regfile.savePP_CONTROL); - } - /* only restore FBC info on the platform that supports FBC*/ intel_fbc_global_disable(dev_priv); @@ -104,6 +63,7 @@ static void i915_restore_display(struct drm_device *dev) int i915_save_state(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; int i; mutex_lock(&dev->struct_mutex); @@ -111,7 +71,7 @@ int i915_save_state(struct drm_device *dev) i915_save_display(dev); if (IS_GEN4(dev)) - pci_read_config_word(dev->pdev, GCDGMBUS, + pci_read_config_word(pdev, GCDGMBUS, &dev_priv->regfile.saveGCDGMBUS); /* Cache mode state */ @@ -149,6 +109,7 @@ int i915_save_state(struct drm_device *dev) int i915_restore_state(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; int i; mutex_lock(&dev->struct_mutex); @@ -156,7 +117,7 @@ int i915_restore_state(struct drm_device *dev) i915_gem_restore_fences(dev); if (IS_GEN4(dev)) - pci_write_config_word(dev->pdev, GCDGMBUS, + pci_write_config_word(pdev, GCDGMBUS, dev_priv->regfile.saveGCDGMBUS); i915_restore_display(dev); diff --git a/drivers/gpu/drm/i915/i915_sw_fence.c b/drivers/gpu/drm/i915/i915_sw_fence.c new file mode 100644 index 0000000..1e5cbc5 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_sw_fence.c @@ -0,0 +1,362 @@ +/* + * (C) Copyright 2016 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/slab.h> +#include <linux/fence.h> +#include <linux/reservation.h> + +#include "i915_sw_fence.h" + +static DEFINE_SPINLOCK(i915_sw_fence_lock); + +static int __i915_sw_fence_notify(struct i915_sw_fence *fence, + enum i915_sw_fence_notify state) +{ + i915_sw_fence_notify_t fn; + + fn = (i915_sw_fence_notify_t)(fence->flags & I915_SW_FENCE_MASK); + return fn(fence, state); +} + +static void i915_sw_fence_free(struct kref *kref) +{ + struct i915_sw_fence *fence = container_of(kref, typeof(*fence), kref); + + WARN_ON(atomic_read(&fence->pending) > 0); + + if (fence->flags & I915_SW_FENCE_MASK) + __i915_sw_fence_notify(fence, FENCE_FREE); + else + kfree(fence); +} + +static void i915_sw_fence_put(struct i915_sw_fence *fence) +{ + kref_put(&fence->kref, i915_sw_fence_free); +} + +static struct i915_sw_fence *i915_sw_fence_get(struct i915_sw_fence *fence) +{ + kref_get(&fence->kref); + return fence; +} + +static void __i915_sw_fence_wake_up_all(struct i915_sw_fence *fence, + struct list_head *continuation) +{ + wait_queue_head_t *x = &fence->wait; + wait_queue_t *pos, *next; + unsigned long flags; + + atomic_set_release(&fence->pending, -1); /* 0 -> -1 [done] */ + + /* + * To prevent unbounded recursion as we traverse the graph of + * i915_sw_fences, we move the task_list from this, the next ready + * fence, to the tail of the original fence's task_list + * (and so added to the list to be woken). + */ + + spin_lock_irqsave_nested(&x->lock, flags, 1 + !!continuation); + if (continuation) { + list_for_each_entry_safe(pos, next, &x->task_list, task_list) { + if (pos->func == autoremove_wake_function) + pos->func(pos, TASK_NORMAL, 0, continuation); + else + list_move_tail(&pos->task_list, continuation); + } + } else { + LIST_HEAD(extra); + + do { + list_for_each_entry_safe(pos, next, + &x->task_list, task_list) + pos->func(pos, TASK_NORMAL, 0, &extra); + + if (list_empty(&extra)) + break; + + list_splice_tail_init(&extra, &x->task_list); + } while (1); + } + spin_unlock_irqrestore(&x->lock, flags); +} + +static void __i915_sw_fence_complete(struct i915_sw_fence *fence, + struct list_head *continuation) +{ + if (!atomic_dec_and_test(&fence->pending)) + return; + + if (fence->flags & I915_SW_FENCE_MASK && + __i915_sw_fence_notify(fence, FENCE_COMPLETE) != NOTIFY_DONE) + return; + + __i915_sw_fence_wake_up_all(fence, continuation); +} + +static void i915_sw_fence_complete(struct i915_sw_fence *fence) +{ + if (WARN_ON(i915_sw_fence_done(fence))) + return; + + __i915_sw_fence_complete(fence, NULL); +} + +static void i915_sw_fence_await(struct i915_sw_fence *fence) +{ + WARN_ON(atomic_inc_return(&fence->pending) <= 1); +} + +void i915_sw_fence_init(struct i915_sw_fence *fence, i915_sw_fence_notify_t fn) +{ + BUG_ON((unsigned long)fn & ~I915_SW_FENCE_MASK); + + init_waitqueue_head(&fence->wait); + kref_init(&fence->kref); + atomic_set(&fence->pending, 1); + fence->flags = (unsigned long)fn; +} + +void i915_sw_fence_commit(struct i915_sw_fence *fence) +{ + i915_sw_fence_complete(fence); + i915_sw_fence_put(fence); +} + +static int i915_sw_fence_wake(wait_queue_t *wq, unsigned mode, int flags, void *key) +{ + list_del(&wq->task_list); + __i915_sw_fence_complete(wq->private, key); + i915_sw_fence_put(wq->private); + return 0; +} + +static bool __i915_sw_fence_check_if_after(struct i915_sw_fence *fence, + const struct i915_sw_fence * const signaler) +{ + wait_queue_t *wq; + + if (__test_and_set_bit(I915_SW_FENCE_CHECKED_BIT, &fence->flags)) + return false; + + if (fence == signaler) + return true; + + list_for_each_entry(wq, &fence->wait.task_list, task_list) { + if (wq->func != i915_sw_fence_wake) + continue; + + if (__i915_sw_fence_check_if_after(wq->private, signaler)) + return true; + } + + return false; +} + +static void __i915_sw_fence_clear_checked_bit(struct i915_sw_fence *fence) +{ + wait_queue_t *wq; + + if (!__test_and_clear_bit(I915_SW_FENCE_CHECKED_BIT, &fence->flags)) + return; + + list_for_each_entry(wq, &fence->wait.task_list, task_list) { + if (wq->func != i915_sw_fence_wake) + continue; + + __i915_sw_fence_clear_checked_bit(wq->private); + } +} + +static bool i915_sw_fence_check_if_after(struct i915_sw_fence *fence, + const struct i915_sw_fence * const signaler) +{ + unsigned long flags; + bool err; + + if (!IS_ENABLED(CONFIG_I915_SW_FENCE_CHECK_DAG)) + return false; + + spin_lock_irqsave(&i915_sw_fence_lock, flags); + err = __i915_sw_fence_check_if_after(fence, signaler); + __i915_sw_fence_clear_checked_bit(fence); + spin_unlock_irqrestore(&i915_sw_fence_lock, flags); + + return err; +} + +int i915_sw_fence_await_sw_fence(struct i915_sw_fence *fence, + struct i915_sw_fence *signaler, + wait_queue_t *wq) +{ + unsigned long flags; + int pending; + + if (i915_sw_fence_done(signaler)) + return 0; + + /* The dependency graph must be acyclic. */ + if (unlikely(i915_sw_fence_check_if_after(fence, signaler))) + return -EINVAL; + + INIT_LIST_HEAD(&wq->task_list); + wq->flags = 0; + wq->func = i915_sw_fence_wake; + wq->private = i915_sw_fence_get(fence); + + i915_sw_fence_await(fence); + + spin_lock_irqsave(&signaler->wait.lock, flags); + if (likely(!i915_sw_fence_done(signaler))) { + __add_wait_queue_tail(&signaler->wait, wq); + pending = 1; + } else { + i915_sw_fence_wake(wq, 0, 0, NULL); + pending = 0; + } + spin_unlock_irqrestore(&signaler->wait.lock, flags); + + return pending; +} + +struct dma_fence_cb { + struct fence_cb base; + struct i915_sw_fence *fence; + struct fence *dma; + struct timer_list timer; +}; + +static void timer_i915_sw_fence_wake(unsigned long data) +{ + struct dma_fence_cb *cb = (struct dma_fence_cb *)data; + + printk(KERN_WARNING "asynchronous wait on fence %s:%s:%x timed out\n", + cb->dma->ops->get_driver_name(cb->dma), + cb->dma->ops->get_timeline_name(cb->dma), + cb->dma->seqno); + fence_put(cb->dma); + cb->dma = NULL; + + i915_sw_fence_commit(cb->fence); + cb->timer.function = NULL; +} + +static void dma_i915_sw_fence_wake(struct fence *dma, struct fence_cb *data) +{ + struct dma_fence_cb *cb = container_of(data, typeof(*cb), base); + + del_timer_sync(&cb->timer); + if (cb->timer.function) + i915_sw_fence_commit(cb->fence); + fence_put(cb->dma); + + kfree(cb); +} + +int i915_sw_fence_await_dma_fence(struct i915_sw_fence *fence, + struct fence *dma, + unsigned long timeout, + gfp_t gfp) +{ + struct dma_fence_cb *cb; + int ret; + + if (fence_is_signaled(dma)) + return 0; + + cb = kmalloc(sizeof(*cb), gfp); + if (!cb) { + if (!gfpflags_allow_blocking(gfp)) + return -ENOMEM; + + return fence_wait(dma, false); + } + + cb->fence = i915_sw_fence_get(fence); + i915_sw_fence_await(fence); + + cb->dma = NULL; + __setup_timer(&cb->timer, + timer_i915_sw_fence_wake, (unsigned long)cb, + TIMER_IRQSAFE); + if (timeout) { + cb->dma = fence_get(dma); + mod_timer(&cb->timer, round_jiffies_up(jiffies + timeout)); + } + + ret = fence_add_callback(dma, &cb->base, dma_i915_sw_fence_wake); + if (ret == 0) { + ret = 1; + } else { + dma_i915_sw_fence_wake(dma, &cb->base); + if (ret == -ENOENT) /* fence already signaled */ + ret = 0; + } + + return ret; +} + +int i915_sw_fence_await_reservation(struct i915_sw_fence *fence, + struct reservation_object *resv, + const struct fence_ops *exclude, + bool write, + unsigned long timeout, + gfp_t gfp) +{ + struct fence *excl; + int ret = 0, pending; + + if (write) { + struct fence **shared; + unsigned int count, i; + + ret = reservation_object_get_fences_rcu(resv, + &excl, &count, &shared); + if (ret) + return ret; + + for (i = 0; i < count; i++) { + if (shared[i]->ops == exclude) + continue; + + pending = i915_sw_fence_await_dma_fence(fence, + shared[i], + timeout, + gfp); + if (pending < 0) { + ret = pending; + break; + } + + ret |= pending; + } + + for (i = 0; i < count; i++) + fence_put(shared[i]); + kfree(shared); + } else { + excl = reservation_object_get_excl_rcu(resv); + } + + if (ret >= 0 && excl && excl->ops != exclude) { + pending = i915_sw_fence_await_dma_fence(fence, + excl, + timeout, + gfp); + if (pending < 0) + ret = pending; + else + ret |= pending; + } + + fence_put(excl); + + return ret; +} diff --git a/drivers/gpu/drm/i915/i915_sw_fence.h b/drivers/gpu/drm/i915/i915_sw_fence.h new file mode 100644 index 0000000..3731416 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_sw_fence.h @@ -0,0 +1,65 @@ +/* + * i915_sw_fence.h - library routines for N:M synchronisation points + * + * Copyright (C) 2016 Intel Corporation + * + * This file is released under the GPLv2. + * + */ + +#ifndef _I915_SW_FENCE_H_ +#define _I915_SW_FENCE_H_ + +#include <linux/gfp.h> +#include <linux/kref.h> +#include <linux/notifier.h> /* for NOTIFY_DONE */ +#include <linux/wait.h> + +struct completion; +struct fence; +struct fence_ops; +struct reservation_object; + +struct i915_sw_fence { + wait_queue_head_t wait; + unsigned long flags; + struct kref kref; + atomic_t pending; +}; + +#define I915_SW_FENCE_CHECKED_BIT 0 /* used internally for DAG checking */ +#define I915_SW_FENCE_PRIVATE_BIT 1 /* available for use by owner */ +#define I915_SW_FENCE_MASK (~3) + +enum i915_sw_fence_notify { + FENCE_COMPLETE, + FENCE_FREE +}; + +typedef int (*i915_sw_fence_notify_t)(struct i915_sw_fence *, + enum i915_sw_fence_notify state); +#define __i915_sw_fence_call __aligned(4) + +void i915_sw_fence_init(struct i915_sw_fence *fence, i915_sw_fence_notify_t fn); +void i915_sw_fence_commit(struct i915_sw_fence *fence); + +int i915_sw_fence_await_sw_fence(struct i915_sw_fence *fence, + struct i915_sw_fence *after, + wait_queue_t *wq); +int i915_sw_fence_await_dma_fence(struct i915_sw_fence *fence, + struct fence *dma, + unsigned long timeout, + gfp_t gfp); +int i915_sw_fence_await_reservation(struct i915_sw_fence *fence, + struct reservation_object *resv, + const struct fence_ops *exclude, + bool write, + unsigned long timeout, + gfp_t gfp); + +static inline bool i915_sw_fence_done(const struct i915_sw_fence *fence) +{ + return atomic_read(&fence->pending) < 0; +} + +#endif /* _I915_SW_FENCE_H_ */ diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index d61829e..1012eee 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -32,13 +32,16 @@ #include "intel_drv.h" #include "i915_drv.h" -#define dev_to_drm_minor(d) dev_get_drvdata((d)) +static inline struct drm_i915_private *kdev_minor_to_i915(struct device *kdev) +{ + struct drm_minor *minor = dev_get_drvdata(kdev); + return to_i915(minor->dev); +} #ifdef CONFIG_PM -static u32 calc_residency(struct drm_device *dev, +static u32 calc_residency(struct drm_i915_private *dev_priv, i915_reg_t reg) { - struct drm_i915_private *dev_priv = to_i915(dev); u64 raw_time; /* 32b value may overflow during fixed point math */ u64 units = 128ULL, div = 100000ULL; u32 ret; @@ -49,13 +52,13 @@ static u32 calc_residency(struct drm_device *dev, intel_runtime_pm_get(dev_priv); /* On VLV and CHV, residency time is in CZ units rather than 1.28us */ - if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) { + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { units = 1; div = dev_priv->czclk_freq; if (I915_READ(VLV_COUNTER_CONTROL) & VLV_COUNT_RANGE_HIGH) units <<= 8; - } else if (IS_BROXTON(dev)) { + } else if (IS_BROXTON(dev_priv)) { units = 1; div = 1200; /* 833.33ns */ } @@ -76,32 +79,32 @@ show_rc6_mask(struct device *kdev, struct device_attribute *attr, char *buf) static ssize_t show_rc6_ms(struct device *kdev, struct device_attribute *attr, char *buf) { - struct drm_minor *dminor = dev_get_drvdata(kdev); - u32 rc6_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6); + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); + u32 rc6_residency = calc_residency(dev_priv, GEN6_GT_GFX_RC6); return snprintf(buf, PAGE_SIZE, "%u\n", rc6_residency); } static ssize_t show_rc6p_ms(struct device *kdev, struct device_attribute *attr, char *buf) { - struct drm_minor *dminor = dev_to_drm_minor(kdev); - u32 rc6p_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6p); + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); + u32 rc6p_residency = calc_residency(dev_priv, GEN6_GT_GFX_RC6p); return snprintf(buf, PAGE_SIZE, "%u\n", rc6p_residency); } static ssize_t show_rc6pp_ms(struct device *kdev, struct device_attribute *attr, char *buf) { - struct drm_minor *dminor = dev_to_drm_minor(kdev); - u32 rc6pp_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6pp); + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); + u32 rc6pp_residency = calc_residency(dev_priv, GEN6_GT_GFX_RC6pp); return snprintf(buf, PAGE_SIZE, "%u\n", rc6pp_residency); } static ssize_t show_media_rc6_ms(struct device *kdev, struct device_attribute *attr, char *buf) { - struct drm_minor *dminor = dev_get_drvdata(kdev); - u32 rc6_residency = calc_residency(dminor->dev, VLV_GT_MEDIA_RC6); + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); + u32 rc6_residency = calc_residency(dev_priv, VLV_GT_MEDIA_RC6); return snprintf(buf, PAGE_SIZE, "%u\n", rc6_residency); } @@ -144,9 +147,9 @@ static struct attribute_group media_rc6_attr_group = { }; #endif -static int l3_access_valid(struct drm_device *dev, loff_t offset) +static int l3_access_valid(struct drm_i915_private *dev_priv, loff_t offset) { - if (!HAS_L3_DPF(dev)) + if (!HAS_L3_DPF(dev_priv)) return -EPERM; if (offset % 4 != 0) @@ -163,22 +166,21 @@ i915_l3_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t offset, size_t count) { - struct device *dev = kobj_to_dev(kobj); - struct drm_minor *dminor = dev_to_drm_minor(dev); - struct drm_device *drm_dev = dminor->dev; - struct drm_i915_private *dev_priv = to_i915(drm_dev); + struct device *kdev = kobj_to_dev(kobj); + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); + struct drm_device *dev = &dev_priv->drm; int slice = (int)(uintptr_t)attr->private; int ret; count = round_down(count, 4); - ret = l3_access_valid(drm_dev, offset); + ret = l3_access_valid(dev_priv, offset); if (ret) return ret; count = min_t(size_t, GEN7_L3LOG_SIZE - offset, count); - ret = i915_mutex_lock_interruptible(drm_dev); + ret = i915_mutex_lock_interruptible(dev); if (ret) return ret; @@ -189,7 +191,7 @@ i915_l3_read(struct file *filp, struct kobject *kobj, else memset(buf, 0, count); - mutex_unlock(&drm_dev->struct_mutex); + mutex_unlock(&dev->struct_mutex); return count; } @@ -199,30 +201,29 @@ i915_l3_write(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t offset, size_t count) { - struct device *dev = kobj_to_dev(kobj); - struct drm_minor *dminor = dev_to_drm_minor(dev); - struct drm_device *drm_dev = dminor->dev; - struct drm_i915_private *dev_priv = to_i915(drm_dev); + struct device *kdev = kobj_to_dev(kobj); + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); + struct drm_device *dev = &dev_priv->drm; struct i915_gem_context *ctx; u32 *temp = NULL; /* Just here to make handling failures easy */ int slice = (int)(uintptr_t)attr->private; int ret; - if (!HAS_HW_CONTEXTS(drm_dev)) + if (!HAS_HW_CONTEXTS(dev_priv)) return -ENXIO; - ret = l3_access_valid(drm_dev, offset); + ret = l3_access_valid(dev_priv, offset); if (ret) return ret; - ret = i915_mutex_lock_interruptible(drm_dev); + ret = i915_mutex_lock_interruptible(dev); if (ret) return ret; if (!dev_priv->l3_parity.remap_info[slice]) { temp = kzalloc(GEN7_L3LOG_SIZE, GFP_KERNEL); if (!temp) { - mutex_unlock(&drm_dev->struct_mutex); + mutex_unlock(&dev->struct_mutex); return -ENOMEM; } } @@ -240,7 +241,7 @@ i915_l3_write(struct file *filp, struct kobject *kobj, list_for_each_entry(ctx, &dev_priv->context_list, link) ctx->remap_slice |= (1<<slice); - mutex_unlock(&drm_dev->struct_mutex); + mutex_unlock(&dev->struct_mutex); return count; } @@ -266,13 +267,9 @@ static struct bin_attribute dpf_attrs_1 = { static ssize_t gt_act_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf) { - struct drm_minor *minor = dev_to_drm_minor(kdev); - struct drm_device *dev = minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); int ret; - flush_delayed_work(&dev_priv->rps.delayed_resume_work); - intel_runtime_pm_get(dev_priv); mutex_lock(&dev_priv->rps.hw_lock); @@ -300,59 +297,70 @@ static ssize_t gt_act_freq_mhz_show(struct device *kdev, static ssize_t gt_cur_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf) { - struct drm_minor *minor = dev_to_drm_minor(kdev); - struct drm_device *dev = minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); - int ret; + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); + + return snprintf(buf, PAGE_SIZE, "%d\n", + intel_gpu_freq(dev_priv, + dev_priv->rps.cur_freq)); +} - flush_delayed_work(&dev_priv->rps.delayed_resume_work); +static ssize_t gt_boost_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf) +{ + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); - intel_runtime_pm_get(dev_priv); + return snprintf(buf, PAGE_SIZE, "%d\n", + intel_gpu_freq(dev_priv, + dev_priv->rps.boost_freq)); +} + +static ssize_t gt_boost_freq_mhz_store(struct device *kdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); + u32 val; + ssize_t ret; + + ret = kstrtou32(buf, 0, &val); + if (ret) + return ret; + + /* Validate against (static) hardware limits */ + val = intel_freq_opcode(dev_priv, val); + if (val < dev_priv->rps.min_freq || val > dev_priv->rps.max_freq) + return -EINVAL; mutex_lock(&dev_priv->rps.hw_lock); - ret = intel_gpu_freq(dev_priv, dev_priv->rps.cur_freq); + dev_priv->rps.boost_freq = val; mutex_unlock(&dev_priv->rps.hw_lock); - intel_runtime_pm_put(dev_priv); - - return snprintf(buf, PAGE_SIZE, "%d\n", ret); + return count; } static ssize_t vlv_rpe_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf) { - struct drm_minor *minor = dev_to_drm_minor(kdev); - struct drm_device *dev = minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); - return snprintf(buf, PAGE_SIZE, - "%d\n", - intel_gpu_freq(dev_priv, dev_priv->rps.efficient_freq)); + return snprintf(buf, PAGE_SIZE, "%d\n", + intel_gpu_freq(dev_priv, + dev_priv->rps.efficient_freq)); } static ssize_t gt_max_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf) { - struct drm_minor *minor = dev_to_drm_minor(kdev); - struct drm_device *dev = minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); - int ret; + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); - flush_delayed_work(&dev_priv->rps.delayed_resume_work); - - mutex_lock(&dev_priv->rps.hw_lock); - ret = intel_gpu_freq(dev_priv, dev_priv->rps.max_freq_softlimit); - mutex_unlock(&dev_priv->rps.hw_lock); - - return snprintf(buf, PAGE_SIZE, "%d\n", ret); + return snprintf(buf, PAGE_SIZE, "%d\n", + intel_gpu_freq(dev_priv, + dev_priv->rps.max_freq_softlimit)); } static ssize_t gt_max_freq_mhz_store(struct device *kdev, struct device_attribute *attr, const char *buf, size_t count) { - struct drm_minor *minor = dev_to_drm_minor(kdev); - struct drm_device *dev = minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); u32 val; ssize_t ret; @@ -360,8 +368,6 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev, if (ret) return ret; - flush_delayed_work(&dev_priv->rps.delayed_resume_work); - intel_runtime_pm_get(dev_priv); mutex_lock(&dev_priv->rps.hw_lock); @@ -400,27 +406,18 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev, static ssize_t gt_min_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf) { - struct drm_minor *minor = dev_to_drm_minor(kdev); - struct drm_device *dev = minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); - int ret; - - flush_delayed_work(&dev_priv->rps.delayed_resume_work); + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); - mutex_lock(&dev_priv->rps.hw_lock); - ret = intel_gpu_freq(dev_priv, dev_priv->rps.min_freq_softlimit); - mutex_unlock(&dev_priv->rps.hw_lock); - - return snprintf(buf, PAGE_SIZE, "%d\n", ret); + return snprintf(buf, PAGE_SIZE, "%d\n", + intel_gpu_freq(dev_priv, + dev_priv->rps.min_freq_softlimit)); } static ssize_t gt_min_freq_mhz_store(struct device *kdev, struct device_attribute *attr, const char *buf, size_t count) { - struct drm_minor *minor = dev_to_drm_minor(kdev); - struct drm_device *dev = minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); u32 val; ssize_t ret; @@ -428,8 +425,6 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev, if (ret) return ret; - flush_delayed_work(&dev_priv->rps.delayed_resume_work); - intel_runtime_pm_get(dev_priv); mutex_lock(&dev_priv->rps.hw_lock); @@ -465,6 +460,7 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev, static DEVICE_ATTR(gt_act_freq_mhz, S_IRUGO, gt_act_freq_mhz_show, NULL); static DEVICE_ATTR(gt_cur_freq_mhz, S_IRUGO, gt_cur_freq_mhz_show, NULL); +static DEVICE_ATTR(gt_boost_freq_mhz, S_IRUGO, gt_boost_freq_mhz_show, gt_boost_freq_mhz_store); static DEVICE_ATTR(gt_max_freq_mhz, S_IRUGO | S_IWUSR, gt_max_freq_mhz_show, gt_max_freq_mhz_store); static DEVICE_ATTR(gt_min_freq_mhz, S_IRUGO | S_IWUSR, gt_min_freq_mhz_show, gt_min_freq_mhz_store); @@ -478,9 +474,7 @@ static DEVICE_ATTR(gt_RPn_freq_mhz, S_IRUGO, gt_rp_mhz_show, NULL); /* For now we have a static number of RP states */ static ssize_t gt_rp_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf) { - struct drm_minor *minor = dev_to_drm_minor(kdev); - struct drm_device *dev = minor->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); u32 val; if (attr == &dev_attr_gt_RP0_freq_mhz) @@ -498,6 +492,7 @@ static ssize_t gt_rp_mhz_show(struct device *kdev, struct device_attribute *attr static const struct attribute *gen6_attrs[] = { &dev_attr_gt_act_freq_mhz.attr, &dev_attr_gt_cur_freq_mhz.attr, + &dev_attr_gt_boost_freq_mhz.attr, &dev_attr_gt_max_freq_mhz.attr, &dev_attr_gt_min_freq_mhz.attr, &dev_attr_gt_RP0_freq_mhz.attr, @@ -509,6 +504,7 @@ static const struct attribute *gen6_attrs[] = { static const struct attribute *vlv_attrs[] = { &dev_attr_gt_act_freq_mhz.attr, &dev_attr_gt_cur_freq_mhz.attr, + &dev_attr_gt_boost_freq_mhz.attr, &dev_attr_gt_max_freq_mhz.attr, &dev_attr_gt_min_freq_mhz.attr, &dev_attr_gt_RP0_freq_mhz.attr, @@ -524,8 +520,8 @@ static ssize_t error_state_read(struct file *filp, struct kobject *kobj, { struct device *kdev = kobj_to_dev(kobj); - struct drm_minor *minor = dev_to_drm_minor(kdev); - struct drm_device *dev = minor->dev; + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); + struct drm_device *dev = &dev_priv->drm; struct i915_error_state_file_priv error_priv; struct drm_i915_error_state_buf error_str; ssize_t ret_count = 0; @@ -559,18 +555,10 @@ static ssize_t error_state_write(struct file *file, struct kobject *kobj, loff_t off, size_t count) { struct device *kdev = kobj_to_dev(kobj); - struct drm_minor *minor = dev_to_drm_minor(kdev); - struct drm_device *dev = minor->dev; - int ret; + struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); DRM_DEBUG_DRIVER("Resetting error state\n"); - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; - - i915_destroy_error_state(dev); - mutex_unlock(&dev->struct_mutex); + i915_destroy_error_state(&dev_priv->drm); return count; } @@ -583,37 +571,38 @@ static struct bin_attribute error_state_attr = { .write = error_state_write, }; -void i915_setup_sysfs(struct drm_device *dev) +void i915_setup_sysfs(struct drm_i915_private *dev_priv) { + struct device *kdev = dev_priv->drm.primary->kdev; int ret; #ifdef CONFIG_PM - if (HAS_RC6(dev)) { - ret = sysfs_merge_group(&dev->primary->kdev->kobj, + if (HAS_RC6(dev_priv)) { + ret = sysfs_merge_group(&kdev->kobj, &rc6_attr_group); if (ret) DRM_ERROR("RC6 residency sysfs setup failed\n"); } - if (HAS_RC6p(dev)) { - ret = sysfs_merge_group(&dev->primary->kdev->kobj, + if (HAS_RC6p(dev_priv)) { + ret = sysfs_merge_group(&kdev->kobj, &rc6p_attr_group); if (ret) DRM_ERROR("RC6p residency sysfs setup failed\n"); } - if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) { - ret = sysfs_merge_group(&dev->primary->kdev->kobj, + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { + ret = sysfs_merge_group(&kdev->kobj, &media_rc6_attr_group); if (ret) DRM_ERROR("Media RC6 residency sysfs setup failed\n"); } #endif - if (HAS_L3_DPF(dev)) { - ret = device_create_bin_file(dev->primary->kdev, &dpf_attrs); + if (HAS_L3_DPF(dev_priv)) { + ret = device_create_bin_file(kdev, &dpf_attrs); if (ret) DRM_ERROR("l3 parity sysfs setup failed\n"); - if (NUM_L3_SLICES(dev) > 1) { - ret = device_create_bin_file(dev->primary->kdev, + if (NUM_L3_SLICES(dev_priv) > 1) { + ret = device_create_bin_file(kdev, &dpf_attrs_1); if (ret) DRM_ERROR("l3 parity slice 1 setup failed\n"); @@ -621,30 +610,32 @@ void i915_setup_sysfs(struct drm_device *dev) } ret = 0; - if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) - ret = sysfs_create_files(&dev->primary->kdev->kobj, vlv_attrs); - else if (INTEL_INFO(dev)->gen >= 6) - ret = sysfs_create_files(&dev->primary->kdev->kobj, gen6_attrs); + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) + ret = sysfs_create_files(&kdev->kobj, vlv_attrs); + else if (INTEL_GEN(dev_priv) >= 6) + ret = sysfs_create_files(&kdev->kobj, gen6_attrs); if (ret) DRM_ERROR("RPS sysfs setup failed\n"); - ret = sysfs_create_bin_file(&dev->primary->kdev->kobj, + ret = sysfs_create_bin_file(&kdev->kobj, &error_state_attr); if (ret) DRM_ERROR("error_state sysfs setup failed\n"); } -void i915_teardown_sysfs(struct drm_device *dev) +void i915_teardown_sysfs(struct drm_i915_private *dev_priv) { - sysfs_remove_bin_file(&dev->primary->kdev->kobj, &error_state_attr); - if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) - sysfs_remove_files(&dev->primary->kdev->kobj, vlv_attrs); + struct device *kdev = dev_priv->drm.primary->kdev; + + sysfs_remove_bin_file(&kdev->kobj, &error_state_attr); + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) + sysfs_remove_files(&kdev->kobj, vlv_attrs); else - sysfs_remove_files(&dev->primary->kdev->kobj, gen6_attrs); - device_remove_bin_file(dev->primary->kdev, &dpf_attrs_1); - device_remove_bin_file(dev->primary->kdev, &dpf_attrs); + sysfs_remove_files(&kdev->kobj, gen6_attrs); + device_remove_bin_file(kdev, &dpf_attrs_1); + device_remove_bin_file(kdev, &dpf_attrs); #ifdef CONFIG_PM - sysfs_unmerge_group(&dev->primary->kdev->kobj, &rc6_attr_group); - sysfs_unmerge_group(&dev->primary->kdev->kobj, &rc6p_attr_group); + sysfs_unmerge_group(&kdev->kobj, &rc6_attr_group); + sysfs_unmerge_group(&kdev->kobj, &rc6p_attr_group); #endif } diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index 534154e..1787980 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -394,25 +394,27 @@ DEFINE_EVENT(i915_gem_object, i915_gem_object_destroy, ); TRACE_EVENT(i915_gem_evict, - TP_PROTO(struct drm_device *dev, u32 size, u32 align, unsigned flags), - TP_ARGS(dev, size, align, flags), + TP_PROTO(struct i915_address_space *vm, u32 size, u32 align, unsigned int flags), + TP_ARGS(vm, size, align, flags), TP_STRUCT__entry( __field(u32, dev) + __field(struct i915_address_space *, vm) __field(u32, size) __field(u32, align) - __field(unsigned, flags) + __field(unsigned int, flags) ), TP_fast_assign( - __entry->dev = dev->primary->index; + __entry->dev = vm->dev->primary->index; + __entry->vm = vm; __entry->size = size; __entry->align = align; __entry->flags = flags; ), - TP_printk("dev=%d, size=%d, align=%d %s", - __entry->dev, __entry->size, __entry->align, + TP_printk("dev=%d, vm=%p, size=%d, align=%d %s", + __entry->dev, __entry->vm, __entry->size, __entry->align, __entry->flags & PIN_MAPPABLE ? ", mappable" : "") ); @@ -449,10 +451,9 @@ TRACE_EVENT(i915_gem_evict_vm, ); TRACE_EVENT(i915_gem_ring_sync_to, - TP_PROTO(struct drm_i915_gem_request *to_req, - struct intel_engine_cs *from, - struct drm_i915_gem_request *req), - TP_ARGS(to_req, from, req), + TP_PROTO(struct drm_i915_gem_request *to, + struct drm_i915_gem_request *from), + TP_ARGS(to, from), TP_STRUCT__entry( __field(u32, dev) @@ -463,9 +464,9 @@ TRACE_EVENT(i915_gem_ring_sync_to, TP_fast_assign( __entry->dev = from->i915->drm.primary->index; - __entry->sync_from = from->id; - __entry->sync_to = to_req->engine->id; - __entry->seqno = i915_gem_request_get_seqno(req); + __entry->sync_from = from->engine->id; + __entry->sync_to = to->engine->id; + __entry->seqno = from->fence.seqno; ), TP_printk("dev=%u, sync-from=%u, sync-to=%u, seqno=%u", @@ -488,9 +489,9 @@ TRACE_EVENT(i915_gem_ring_dispatch, TP_fast_assign( __entry->dev = req->i915->drm.primary->index; __entry->ring = req->engine->id; - __entry->seqno = req->seqno; + __entry->seqno = req->fence.seqno; __entry->flags = flags; - intel_engine_enable_signaling(req); + fence_enable_sw_signaling(&req->fence); ), TP_printk("dev=%u, ring=%u, seqno=%u, flags=%x", @@ -533,7 +534,7 @@ DECLARE_EVENT_CLASS(i915_gem_request, TP_fast_assign( __entry->dev = req->i915->drm.primary->index; __entry->ring = req->engine->id; - __entry->seqno = req->seqno; + __entry->seqno = req->fence.seqno; ), TP_printk("dev=%u, ring=%u, seqno=%u", @@ -595,7 +596,7 @@ TRACE_EVENT(i915_gem_request_wait_begin, TP_fast_assign( __entry->dev = req->i915->drm.primary->index; __entry->ring = req->engine->id; - __entry->seqno = req->seqno; + __entry->seqno = req->fence.seqno; __entry->blocking = mutex_is_locked(&req->i915->drm.struct_mutex); ), diff --git a/drivers/gpu/drm/i915/i915_vgpu.c b/drivers/gpu/drm/i915/i915_vgpu.c index f6acb5a..dae340c 100644 --- a/drivers/gpu/drm/i915/i915_vgpu.c +++ b/drivers/gpu/drm/i915/i915_vgpu.c @@ -65,9 +65,6 @@ void i915_check_vgpu(struct drm_i915_private *dev_priv) BUILD_BUG_ON(sizeof(struct vgt_if) != VGT_PVINFO_SIZE); - if (!IS_HASWELL(dev_priv)) - return; - magic = __raw_i915_read64(dev_priv, vgtif_reg(magic)); if (magic != VGT_MAGIC) return; @@ -97,6 +94,7 @@ static struct _balloon_info_ bl_info; /** * intel_vgt_deballoon - deballoon reserved graphics address trunks + * @dev_priv: i915 device private data * * This function is called to deallocate the ballooned-out graphic memory, when * driver is unloaded or when ballooning fails. @@ -138,7 +136,7 @@ static int vgt_balloon_space(struct drm_mm *mm, /** * intel_vgt_balloon - balloon out reserved graphics address trunks - * @dev: drm device + * @dev_priv: i915 device private data * * This function is called at the initialization stage, to balloon out the * graphic address space allocated to other vGPUs, by marking these spaces as @@ -155,27 +153,27 @@ static int vgt_balloon_space(struct drm_mm *mm, * host point of view, the graphic address space is partitioned by multiple * vGPUs in different VMs. :: * - * vGPU1 view Host view - * 0 ------> +-----------+ +-----------+ - * ^ |###########| | vGPU3 | - * | |###########| +-----------+ - * | |###########| | vGPU2 | - * | +-----------+ +-----------+ - * mappable GM | available | ==> | vGPU1 | - * | +-----------+ +-----------+ - * | |###########| | | - * v |###########| | Host | - * +=======+===========+ +===========+ - * ^ |###########| | vGPU3 | - * | |###########| +-----------+ - * | |###########| | vGPU2 | - * | +-----------+ +-----------+ - * unmappable GM | available | ==> | vGPU1 | - * | +-----------+ +-----------+ - * | |###########| | | - * | |###########| | Host | - * v |###########| | | - * total GM size ------> +-----------+ +-----------+ + * vGPU1 view Host view + * 0 ------> +-----------+ +-----------+ + * ^ |###########| | vGPU3 | + * | |###########| +-----------+ + * | |###########| | vGPU2 | + * | +-----------+ +-----------+ + * mappable GM | available | ==> | vGPU1 | + * | +-----------+ +-----------+ + * | |###########| | | + * v |###########| | Host | + * +=======+===========+ +===========+ + * ^ |###########| | vGPU3 | + * | |###########| +-----------+ + * | |###########| | vGPU2 | + * | +-----------+ +-----------+ + * unmappable GM | available | ==> | vGPU1 | + * | +-----------+ +-----------+ + * | |###########| | | + * | |###########| | Host | + * v |###########| | | + * total GM size ------> +-----------+ +-----------+ * * Returns: * zero on success, non-zero if configuration invalid or ballooning failed diff --git a/drivers/gpu/drm/i915/intel_atomic_plane.c b/drivers/gpu/drm/i915/intel_atomic_plane.c index 7de7721..b82de30 100644 --- a/drivers/gpu/drm/i915/intel_atomic_plane.c +++ b/drivers/gpu/drm/i915/intel_atomic_plane.c @@ -55,7 +55,7 @@ intel_create_plane_state(struct drm_plane *plane) return NULL; state->base.plane = plane; - state->base.rotation = BIT(DRM_ROTATE_0); + state->base.rotation = DRM_ROTATE_0; state->ckey.flags = I915_SET_COLORKEY_NONE; return state; @@ -134,20 +134,6 @@ static int intel_plane_atomic_check(struct drm_plane *plane, crtc_state = to_intel_crtc_state(drm_crtc_state); - /* - * The original src/dest coordinates are stored in state->base, but - * we want to keep another copy internal to our driver that we can - * clip/modify ourselves. - */ - intel_state->src.x1 = state->src_x; - intel_state->src.y1 = state->src_y; - intel_state->src.x2 = state->src_x + state->src_w; - intel_state->src.y2 = state->src_y + state->src_h; - intel_state->dst.x1 = state->crtc_x; - intel_state->dst.y1 = state->crtc_y; - intel_state->dst.x2 = state->crtc_x + state->crtc_w; - intel_state->dst.y2 = state->crtc_y + state->crtc_h; - /* Clip all planes to CRTC size, or 0x0 if CRTC is disabled */ intel_state->clip.x1 = 0; intel_state->clip.y1 = 0; @@ -157,6 +143,7 @@ static int intel_plane_atomic_check(struct drm_plane *plane, crtc_state->base.enable ? crtc_state->pipe_src_h : 0; if (state->fb && intel_rotation_90_or_270(state->rotation)) { + char *format_name; if (!(state->fb->modifier[0] == I915_FORMAT_MOD_Y_TILED || state->fb->modifier[0] == I915_FORMAT_MOD_Yf_TILED)) { DRM_DEBUG_KMS("Y/Yf tiling required for 90/270!\n"); @@ -171,8 +158,9 @@ static int intel_plane_atomic_check(struct drm_plane *plane, switch (state->fb->pixel_format) { case DRM_FORMAT_C8: case DRM_FORMAT_RGB565: - DRM_DEBUG_KMS("Unsupported pixel format %s for 90/270!\n", - drm_get_format_name(state->fb->pixel_format)); + format_name = drm_get_format_name(state->fb->pixel_format); + DRM_DEBUG_KMS("Unsupported pixel format %s for 90/270!\n", format_name); + kfree(format_name); return -EINVAL; default: @@ -180,7 +168,7 @@ static int intel_plane_atomic_check(struct drm_plane *plane, } } - intel_state->visible = false; + intel_state->base.visible = false; ret = intel_plane->check_plane(plane, crtc_state, intel_state); if (ret) return ret; @@ -196,7 +184,7 @@ static void intel_plane_atomic_update(struct drm_plane *plane, to_intel_plane_state(plane->state); struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc; - if (intel_state->visible) + if (intel_state->base.visible) intel_plane->update_plane(plane, to_intel_crtc_state(crtc->state), intel_state); diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c index 6700a7b..6c70a5b 100644 --- a/drivers/gpu/drm/i915/intel_audio.c +++ b/drivers/gpu/drm/i915/intel_audio.c @@ -51,10 +51,10 @@ * related registers. (The notable exception is the power management, not * covered here.) * - * The struct i915_audio_component is used to interact between the graphics - * and audio drivers. The struct i915_audio_component_ops *ops in it is + * The struct &i915_audio_component is used to interact between the graphics + * and audio drivers. The struct &i915_audio_component_ops @ops in it is * defined in graphics driver and called in audio driver. The - * struct i915_audio_component_audio_ops *audio_ops is called from i915 driver. + * struct &i915_audio_component_audio_ops @audio_ops is called from i915 driver. */ static const struct { @@ -359,9 +359,7 @@ static void ilk_audio_codec_disable(struct intel_encoder *encoder) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); - struct intel_digital_port *intel_dig_port = - enc_to_dig_port(&encoder->base); - enum port port = intel_dig_port->port; + enum port port = enc_to_dig_port(&encoder->base)->port; enum pipe pipe = intel_crtc->pipe; uint32_t tmp, eldv; i915_reg_t aud_config, aud_cntrl_st2; @@ -407,13 +405,10 @@ static void ilk_audio_codec_enable(struct drm_connector *connector, { struct drm_i915_private *dev_priv = to_i915(connector->dev); struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); - struct intel_digital_port *intel_dig_port = - enc_to_dig_port(&encoder->base); - enum port port = intel_dig_port->port; + enum port port = enc_to_dig_port(&encoder->base)->port; enum pipe pipe = intel_crtc->pipe; uint8_t *eld = connector->eld; - uint32_t eldv; - uint32_t tmp; + uint32_t tmp, eldv; int len, i; i915_reg_t hdmiw_hdmiedid, aud_config, aud_cntl_st, aud_cntrl_st2; @@ -581,25 +576,27 @@ void intel_init_audio_hooks(struct drm_i915_private *dev_priv) } } -static void i915_audio_component_get_power(struct device *dev) +static void i915_audio_component_get_power(struct device *kdev) { - intel_display_power_get(dev_to_i915(dev), POWER_DOMAIN_AUDIO); + intel_display_power_get(kdev_to_i915(kdev), POWER_DOMAIN_AUDIO); } -static void i915_audio_component_put_power(struct device *dev) +static void i915_audio_component_put_power(struct device *kdev) { - intel_display_power_put(dev_to_i915(dev), POWER_DOMAIN_AUDIO); + intel_display_power_put(kdev_to_i915(kdev), POWER_DOMAIN_AUDIO); } -static void i915_audio_component_codec_wake_override(struct device *dev, +static void i915_audio_component_codec_wake_override(struct device *kdev, bool enable) { - struct drm_i915_private *dev_priv = dev_to_i915(dev); + struct drm_i915_private *dev_priv = kdev_to_i915(kdev); u32 tmp; if (!IS_SKYLAKE(dev_priv) && !IS_KABYLAKE(dev_priv)) return; + i915_audio_component_get_power(kdev); + /* * Enable/disable generating the codec wake signal, overriding the * internal logic to generate the codec wake to controller. @@ -615,12 +612,14 @@ static void i915_audio_component_codec_wake_override(struct device *dev, I915_WRITE(HSW_AUD_CHICKENBIT, tmp); usleep_range(1000, 1500); } + + i915_audio_component_put_power(kdev); } /* Get CDCLK in kHz */ -static int i915_audio_component_get_cdclk_freq(struct device *dev) +static int i915_audio_component_get_cdclk_freq(struct device *kdev) { - struct drm_i915_private *dev_priv = dev_to_i915(dev); + struct drm_i915_private *dev_priv = kdev_to_i915(kdev); if (WARN_ON_ONCE(!HAS_DDI(dev_priv))) return -ENODEV; @@ -628,10 +627,10 @@ static int i915_audio_component_get_cdclk_freq(struct device *dev) return dev_priv->cdclk_freq; } -static int i915_audio_component_sync_audio_rate(struct device *dev, +static int i915_audio_component_sync_audio_rate(struct device *kdev, int port, int rate) { - struct drm_i915_private *dev_priv = dev_to_i915(dev); + struct drm_i915_private *dev_priv = kdev_to_i915(kdev); struct intel_encoder *intel_encoder; struct intel_crtc *crtc; struct drm_display_mode *mode; @@ -648,6 +647,7 @@ static int i915_audio_component_sync_audio_rate(struct device *dev, !IS_HASWELL(dev_priv)) return 0; + i915_audio_component_get_power(kdev); mutex_lock(&dev_priv->av_mutex); /* 1. get the pipe */ intel_encoder = dev_priv->dig_port_map[port]; @@ -698,14 +698,15 @@ static int i915_audio_component_sync_audio_rate(struct device *dev, unlock: mutex_unlock(&dev_priv->av_mutex); + i915_audio_component_put_power(kdev); return err; } -static int i915_audio_component_get_eld(struct device *dev, int port, +static int i915_audio_component_get_eld(struct device *kdev, int port, bool *enabled, unsigned char *buf, int max_bytes) { - struct drm_i915_private *dev_priv = dev_to_i915(dev); + struct drm_i915_private *dev_priv = kdev_to_i915(kdev); struct intel_encoder *intel_encoder; struct intel_digital_port *intel_dig_port; const u8 *eld; @@ -739,11 +740,11 @@ static const struct i915_audio_component_ops i915_audio_component_ops = { .get_eld = i915_audio_component_get_eld, }; -static int i915_audio_component_bind(struct device *i915_dev, - struct device *hda_dev, void *data) +static int i915_audio_component_bind(struct device *i915_kdev, + struct device *hda_kdev, void *data) { struct i915_audio_component *acomp = data; - struct drm_i915_private *dev_priv = dev_to_i915(i915_dev); + struct drm_i915_private *dev_priv = kdev_to_i915(i915_kdev); int i; if (WARN_ON(acomp->ops || acomp->dev)) @@ -751,7 +752,7 @@ static int i915_audio_component_bind(struct device *i915_dev, drm_modeset_lock_all(&dev_priv->drm); acomp->ops = &i915_audio_component_ops; - acomp->dev = i915_dev; + acomp->dev = i915_kdev; BUILD_BUG_ON(MAX_PORTS != I915_MAX_PORTS); for (i = 0; i < ARRAY_SIZE(acomp->aud_sample_rate); i++) acomp->aud_sample_rate[i] = 0; @@ -761,11 +762,11 @@ static int i915_audio_component_bind(struct device *i915_dev, return 0; } -static void i915_audio_component_unbind(struct device *i915_dev, - struct device *hda_dev, void *data) +static void i915_audio_component_unbind(struct device *i915_kdev, + struct device *hda_kdev, void *data) { struct i915_audio_component *acomp = data; - struct drm_i915_private *dev_priv = dev_to_i915(i915_dev); + struct drm_i915_private *dev_priv = kdev_to_i915(i915_kdev); drm_modeset_lock_all(&dev_priv->drm); acomp->ops = NULL; diff --git a/drivers/gpu/drm/i915/intel_breadcrumbs.c b/drivers/gpu/drm/i915/intel_breadcrumbs.c index b074f3d..9bad14d 100644 --- a/drivers/gpu/drm/i915/intel_breadcrumbs.c +++ b/drivers/gpu/drm/i915/intel_breadcrumbs.c @@ -26,6 +26,40 @@ #include "i915_drv.h" +static void intel_breadcrumbs_hangcheck(unsigned long data) +{ + struct intel_engine_cs *engine = (struct intel_engine_cs *)data; + struct intel_breadcrumbs *b = &engine->breadcrumbs; + + if (!b->irq_enabled) + return; + + if (time_before(jiffies, b->timeout)) { + mod_timer(&b->hangcheck, b->timeout); + return; + } + + DRM_DEBUG("Hangcheck timer elapsed... %s idle\n", engine->name); + set_bit(engine->id, &engine->i915->gpu_error.missed_irq_rings); + mod_timer(&engine->breadcrumbs.fake_irq, jiffies + 1); + + /* Ensure that even if the GPU hangs, we get woken up. + * + * However, note that if no one is waiting, we never notice + * a gpu hang. Eventually, we will have to wait for a resource + * held by the GPU and so trigger a hangcheck. In the most + * pathological case, this will be upon memory starvation! To + * prevent this, we also queue the hangcheck from the retire + * worker. + */ + i915_queue_hangcheck(engine->i915); +} + +static unsigned long wait_timeout(void) +{ + return round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES); +} + static void intel_breadcrumbs_fake_irq(unsigned long data) { struct intel_engine_cs *engine = (struct intel_engine_cs *)data; @@ -37,10 +71,8 @@ static void intel_breadcrumbs_fake_irq(unsigned long data) * every jiffie in order to kick the oldest waiter to do the * coherent seqno check. */ - rcu_read_lock(); if (intel_engine_wakeup(engine)) mod_timer(&engine->breadcrumbs.fake_irq, jiffies + 1); - rcu_read_unlock(); } static void irq_enable(struct intel_engine_cs *engine) @@ -91,17 +123,13 @@ static void __intel_breadcrumbs_enable_irq(struct intel_breadcrumbs *b) } if (!b->irq_enabled || - test_bit(engine->id, &i915->gpu_error.missed_irq_rings)) + test_bit(engine->id, &i915->gpu_error.missed_irq_rings)) { mod_timer(&b->fake_irq, jiffies + 1); - - /* Ensure that even if the GPU hangs, we get woken up. - * - * However, note that if no one is waiting, we never notice - * a gpu hang. Eventually, we will have to wait for a resource - * held by the GPU and so trigger a hangcheck. In the most - * pathological case, this will be upon memory starvation! - */ - i915_queue_hangcheck(i915); + } else { + /* Ensure we never sleep indefinitely */ + GEM_BUG_ON(!time_after(b->timeout, jiffies)); + mod_timer(&b->hangcheck, b->timeout); + } } static void __intel_breadcrumbs_disable_irq(struct intel_breadcrumbs *b) @@ -204,7 +232,7 @@ static bool __intel_engine_add_wait(struct intel_engine_cs *engine, } rb_link_node(&wait->node, parent, p); rb_insert_color(&wait->node, &b->waiters); - GEM_BUG_ON(!first && !b->irq_seqno_bh); + GEM_BUG_ON(!first && !rcu_access_pointer(b->irq_seqno_bh)); if (completed) { struct rb_node *next = rb_next(completed); @@ -212,8 +240,9 @@ static bool __intel_engine_add_wait(struct intel_engine_cs *engine, GEM_BUG_ON(!next && !first); if (next && next != &wait->node) { GEM_BUG_ON(first); + b->timeout = wait_timeout(); b->first_wait = to_wait(next); - smp_store_mb(b->irq_seqno_bh, b->first_wait->tsk); + rcu_assign_pointer(b->irq_seqno_bh, b->first_wait->tsk); /* As there is a delay between reading the current * seqno, processing the completed tasks and selecting * the next waiter, we may have missed the interrupt @@ -238,8 +267,9 @@ static bool __intel_engine_add_wait(struct intel_engine_cs *engine, if (first) { GEM_BUG_ON(rb_first(&b->waiters) != &wait->node); + b->timeout = wait_timeout(); b->first_wait = wait; - smp_store_mb(b->irq_seqno_bh, wait->tsk); + rcu_assign_pointer(b->irq_seqno_bh, wait->tsk); /* After assigning ourselves as the new bottom-half, we must * perform a cursory check to prevent a missed interrupt. * Either we miss the interrupt whilst programming the hardware, @@ -250,7 +280,7 @@ static bool __intel_engine_add_wait(struct intel_engine_cs *engine, */ __intel_breadcrumbs_enable_irq(b); } - GEM_BUG_ON(!b->irq_seqno_bh); + GEM_BUG_ON(!rcu_access_pointer(b->irq_seqno_bh)); GEM_BUG_ON(!b->first_wait); GEM_BUG_ON(rb_first(&b->waiters) != &b->first_wait->node); @@ -270,11 +300,6 @@ bool intel_engine_add_wait(struct intel_engine_cs *engine, return first; } -void intel_engine_enable_fake_irq(struct intel_engine_cs *engine) -{ - mod_timer(&engine->breadcrumbs.fake_irq, jiffies + 1); -} - static inline bool chain_wakeup(struct rb_node *rb, int priority) { return rb && to_wait(rb)->tsk->prio <= priority; @@ -310,7 +335,7 @@ void intel_engine_remove_wait(struct intel_engine_cs *engine, const int priority = wakeup_priority(b, wait->tsk); struct rb_node *next; - GEM_BUG_ON(b->irq_seqno_bh != wait->tsk); + GEM_BUG_ON(rcu_access_pointer(b->irq_seqno_bh) != wait->tsk); /* We are the current bottom-half. Find the next candidate, * the first waiter in the queue on the remaining oldest @@ -352,14 +377,15 @@ void intel_engine_remove_wait(struct intel_engine_cs *engine, * the interrupt, or if we have to handle an * exception rather than a seqno completion. */ + b->timeout = wait_timeout(); b->first_wait = to_wait(next); - smp_store_mb(b->irq_seqno_bh, b->first_wait->tsk); + rcu_assign_pointer(b->irq_seqno_bh, b->first_wait->tsk); if (b->first_wait->seqno != wait->seqno) __intel_breadcrumbs_enable_irq(b); - wake_up_process(b->irq_seqno_bh); + wake_up_process(b->first_wait->tsk); } else { b->first_wait = NULL; - WRITE_ONCE(b->irq_seqno_bh, NULL); + rcu_assign_pointer(b->irq_seqno_bh, NULL); __intel_breadcrumbs_disable_irq(b); } } else { @@ -373,7 +399,7 @@ out_unlock: GEM_BUG_ON(b->first_wait == wait); GEM_BUG_ON(rb_first(&b->waiters) != (b->first_wait ? &b->first_wait->node : NULL)); - GEM_BUG_ON(!b->irq_seqno_bh ^ RB_EMPTY_ROOT(&b->waiters)); + GEM_BUG_ON(!rcu_access_pointer(b->irq_seqno_bh) ^ RB_EMPTY_ROOT(&b->waiters)); spin_unlock(&b->lock); } @@ -437,6 +463,10 @@ static int intel_breadcrumbs_signaler(void *arg) intel_engine_remove_wait(engine, &request->signaling.wait); + local_bh_disable(); + fence_signal(&request->fence); + local_bh_enable(); /* kick start the tasklets */ + /* Find the next oldest signal. Note that as we have * not been holding the lock, another client may * have installed an even older signal than the one @@ -452,7 +482,7 @@ static int intel_breadcrumbs_signaler(void *arg) rb_erase(&request->signaling.node, &b->signals); spin_unlock(&b->lock); - i915_gem_request_unreference(request); + i915_gem_request_put(request); } else { if (kthread_should_stop()) break; @@ -472,18 +502,14 @@ void intel_engine_enable_signaling(struct drm_i915_gem_request *request) struct rb_node *parent, **p; bool first, wakeup; - if (unlikely(READ_ONCE(request->signaling.wait.tsk))) - return; - - spin_lock(&b->lock); - if (unlikely(request->signaling.wait.tsk)) { - wakeup = false; - goto unlock; - } + /* locked by fence_enable_sw_signaling() */ + assert_spin_locked(&request->lock); request->signaling.wait.tsk = b->signaler; - request->signaling.wait.seqno = request->seqno; - i915_gem_request_reference(request); + request->signaling.wait.seqno = request->fence.seqno; + i915_gem_request_get(request); + + spin_lock(&b->lock); /* First add ourselves into the list of waiters, but register our * bottom-half as the signaller thread. As per usual, only the oldest @@ -504,8 +530,8 @@ void intel_engine_enable_signaling(struct drm_i915_gem_request *request) p = &b->signals.rb_node; while (*p) { parent = *p; - if (i915_seqno_passed(request->seqno, - to_signaler(parent)->seqno)) { + if (i915_seqno_passed(request->fence.seqno, + to_signaler(parent)->fence.seqno)) { p = &parent->rb_right; first = false; } else { @@ -517,7 +543,6 @@ void intel_engine_enable_signaling(struct drm_i915_gem_request *request) if (first) smp_store_mb(b->first_signal, request); -unlock: spin_unlock(&b->lock); if (wakeup) @@ -533,6 +558,9 @@ int intel_engine_init_breadcrumbs(struct intel_engine_cs *engine) setup_timer(&b->fake_irq, intel_breadcrumbs_fake_irq, (unsigned long)engine); + setup_timer(&b->hangcheck, + intel_breadcrumbs_hangcheck, + (unsigned long)engine); /* Spawn a thread to provide a common bottom-half for all signals. * As this is an asynchronous interface we cannot steal the current @@ -557,6 +585,7 @@ void intel_engine_fini_breadcrumbs(struct intel_engine_cs *engine) if (!IS_ERR_OR_NULL(b->signaler)) kthread_stop(b->signaler); + del_timer_sync(&b->hangcheck); del_timer_sync(&b->fake_irq); } @@ -570,11 +599,9 @@ unsigned int intel_kick_waiters(struct drm_i915_private *i915) * RCU lock, i.e. as we call wake_up_process() we must be holding the * rcu_read_lock(). */ - rcu_read_lock(); for_each_engine(engine, i915) if (unlikely(intel_engine_wakeup(engine))) mask |= intel_engine_flag(engine); - rcu_read_unlock(); return mask; } diff --git a/drivers/gpu/drm/i915/intel_color.c b/drivers/gpu/drm/i915/intel_color.c index bc0fef3..95a7277 100644 --- a/drivers/gpu/drm/i915/intel_color.c +++ b/drivers/gpu/drm/i915/intel_color.c @@ -100,13 +100,14 @@ static void i9xx_load_csc_matrix(struct drm_crtc_state *crtc_state) struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int i, pipe = intel_crtc->pipe; uint16_t coeffs[9] = { 0, }; + struct intel_crtc_state *intel_crtc_state = to_intel_crtc_state(crtc_state); if (crtc_state->ctm) { struct drm_color_ctm *ctm = (struct drm_color_ctm *)crtc_state->ctm->data; uint64_t input[9] = { 0, }; - if (intel_crtc->config->limited_color_range) { + if (intel_crtc_state->limited_color_range) { ctm_mult_by_limited(input, ctm->matrix); } else { for (i = 0; i < ARRAY_SIZE(input); i++) @@ -158,7 +159,7 @@ static void i9xx_load_csc_matrix(struct drm_crtc_state *crtc_state) * into consideration. */ for (i = 0; i < 3; i++) { - if (intel_crtc->config->limited_color_range) + if (intel_crtc_state->limited_color_range) coeffs[i * 3 + i] = I9XX_CSC_COEFF_LIMITED_RANGE; else @@ -182,7 +183,7 @@ static void i9xx_load_csc_matrix(struct drm_crtc_state *crtc_state) if (INTEL_INFO(dev)->gen > 6) { uint16_t postoff = 0; - if (intel_crtc->config->limited_color_range) + if (intel_crtc_state->limited_color_range) postoff = (16 * (1 << 12) / 255) & 0x1fff; I915_WRITE(PIPE_CSC_POSTOFF_HI(pipe), postoff); @@ -193,7 +194,7 @@ static void i9xx_load_csc_matrix(struct drm_crtc_state *crtc_state) } else { uint32_t mode = CSC_MODE_YUV_TO_RGB; - if (intel_crtc->config->limited_color_range) + if (intel_crtc_state->limited_color_range) mode |= CSC_BLACK_SCREEN_OFFSET; I915_WRITE(PIPE_CSC_MODE(pipe), mode); @@ -263,7 +264,8 @@ void intel_color_set_csc(struct drm_crtc_state *crtc_state) /* Loads the legacy palette/gamma unit for the CRTC. */ static void i9xx_load_luts_internal(struct drm_crtc *crtc, - struct drm_property_blob *blob) + struct drm_property_blob *blob, + struct intel_crtc_state *crtc_state) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = to_i915(dev); @@ -272,7 +274,7 @@ static void i9xx_load_luts_internal(struct drm_crtc *crtc, int i; if (HAS_GMCH_DISPLAY(dev)) { - if (intel_crtc_has_type(intel_crtc->config, INTEL_OUTPUT_DSI)) + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI)) assert_dsi_pll_enabled(dev_priv); else assert_pll_enabled(dev_priv, pipe); @@ -305,7 +307,8 @@ static void i9xx_load_luts_internal(struct drm_crtc *crtc, static void i9xx_load_luts(struct drm_crtc_state *crtc_state) { - i9xx_load_luts_internal(crtc_state->crtc, crtc_state->gamma_lut); + i9xx_load_luts_internal(crtc_state->crtc, crtc_state->gamma_lut, + to_intel_crtc_state(crtc_state)); } /* Loads the legacy palette/gamma unit for the CRTC on Haswell. */ @@ -323,7 +326,7 @@ static void haswell_load_luts(struct drm_crtc_state *crtc_state) * Workaround : Do not read or write the pipe palette/gamma data while * GAMMA_MODE is configured for split gamma and IPS_CTL has IPS enabled. */ - if (IS_HASWELL(dev) && intel_crtc->config->ips_enabled && + if (IS_HASWELL(dev) && intel_crtc_state->ips_enabled && (intel_crtc_state->gamma_mode == GAMMA_MODE_MODE_SPLIT)) { hsw_disable_ips(intel_crtc); reenable_ips = true; @@ -436,7 +439,8 @@ static void cherryview_load_luts(struct drm_crtc_state *state) /* Turn off degamma/gamma on CGM block. */ I915_WRITE(CGM_PIPE_MODE(pipe), (state->ctm ? CGM_PIPE_MODE_CSC : 0)); - i9xx_load_luts_internal(crtc, state->gamma_lut); + i9xx_load_luts_internal(crtc, state->gamma_lut, + to_intel_crtc_state(state)); return; } @@ -479,7 +483,7 @@ static void cherryview_load_luts(struct drm_crtc_state *state) * Also program a linear LUT in the legacy block (behind the * CGM block). */ - i9xx_load_luts_internal(crtc, NULL); + i9xx_load_luts_internal(crtc, NULL, to_intel_crtc_state(state)); } void intel_color_load_luts(struct drm_crtc_state *crtc_state) diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 827b6ef..dfbcf16 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -143,13 +143,15 @@ static void hsw_crt_get_config(struct intel_encoder *encoder, /* Note: The caller is required to filter out dpms modes not supported by the * platform. */ -static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode) +static void intel_crt_set_dpms(struct intel_encoder *encoder, + struct intel_crtc_state *crtc_state, + int mode) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crt *crt = intel_encoder_to_crt(encoder); - struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); - const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode; + struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc); + const struct drm_display_mode *adjusted_mode = &crtc_state->base.adjusted_mode; u32 adpa; if (INTEL_INFO(dev)->gen >= 5) @@ -193,23 +195,45 @@ static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode) I915_WRITE(crt->adpa_reg, adpa); } -static void intel_disable_crt(struct intel_encoder *encoder) +static void intel_disable_crt(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { - intel_crt_set_dpms(encoder, DRM_MODE_DPMS_OFF); + intel_crt_set_dpms(encoder, old_crtc_state, DRM_MODE_DPMS_OFF); } -static void pch_disable_crt(struct intel_encoder *encoder) +static void pch_disable_crt(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { } -static void pch_post_disable_crt(struct intel_encoder *encoder) +static void pch_post_disable_crt(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { - intel_disable_crt(encoder); + intel_disable_crt(encoder, old_crtc_state, old_conn_state); } -static void intel_enable_crt(struct intel_encoder *encoder) +static void hsw_post_disable_crt(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { - intel_crt_set_dpms(encoder, DRM_MODE_DPMS_ON); + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + + pch_post_disable_crt(encoder, old_crtc_state, old_conn_state); + + lpt_disable_pch_transcoder(dev_priv); + lpt_disable_iclkip(dev_priv); + + intel_ddi_fdi_post_disable(encoder, old_crtc_state, old_conn_state); +} + +static void intel_enable_crt(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) +{ + intel_crt_set_dpms(encoder, pipe_config, DRM_MODE_DPMS_ON); } static enum drm_mode_status @@ -253,7 +277,8 @@ intel_crt_mode_valid(struct drm_connector *connector, } static bool intel_crt_compute_config(struct intel_encoder *encoder, - struct intel_crtc_state *pipe_config) + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct drm_device *dev = encoder->base.dev; @@ -894,6 +919,7 @@ void intel_crt_init(struct drm_device *dev) if (HAS_DDI(dev)) { crt->base.get_config = hsw_crt_get_config; crt->base.get_hw_state = intel_ddi_get_hw_state; + crt->base.post_disable = hsw_post_disable_crt; } else { crt->base.get_config = intel_crt_get_config; crt->base.get_hw_state = intel_crt_get_hw_state; diff --git a/drivers/gpu/drm/i915/intel_csr.c b/drivers/gpu/drm/i915/intel_csr.c index 3edb958..1ea0e1f 100644 --- a/drivers/gpu/drm/i915/intel_csr.c +++ b/drivers/gpu/drm/i915/intel_csr.c @@ -32,24 +32,17 @@ * onwards to drive newly added DMC (Display microcontroller) in display * engine to save and restore the state of display engine when it enter into * low-power state and comes back to normal. - * - * Firmware loading status will be one of the below states: FW_UNINITIALIZED, - * FW_LOADED, FW_FAILED. - * - * Once the firmware is written into the registers status will be moved from - * FW_UNINITIALIZED to FW_LOADED and for any erroneous condition status will - * be moved to FW_FAILED. */ -#define I915_CSR_KBL "i915/kbl_dmc_ver1.bin" +#define I915_CSR_KBL "i915/kbl_dmc_ver1_01.bin" MODULE_FIRMWARE(I915_CSR_KBL); #define KBL_CSR_VERSION_REQUIRED CSR_VERSION(1, 1) -#define I915_CSR_SKL "i915/skl_dmc_ver1.bin" +#define I915_CSR_SKL "i915/skl_dmc_ver1_26.bin" MODULE_FIRMWARE(I915_CSR_SKL); -#define SKL_CSR_VERSION_REQUIRED CSR_VERSION(1, 23) +#define SKL_CSR_VERSION_REQUIRED CSR_VERSION(1, 26) -#define I915_CSR_BXT "i915/bxt_dmc_ver1.bin" +#define I915_CSR_BXT "i915/bxt_dmc_ver1_07.bin" MODULE_FIRMWARE(I915_CSR_BXT); #define BXT_CSR_VERSION_REQUIRED CSR_VERSION(1, 7) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index dd1d6fe..15d47c8 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -145,7 +145,7 @@ static const struct ddi_buf_trans skl_ddi_translations_dp[] = { static const struct ddi_buf_trans skl_u_ddi_translations_dp[] = { { 0x0000201B, 0x000000A2, 0x0 }, { 0x00005012, 0x00000088, 0x0 }, - { 0x80007011, 0x000000CD, 0x0 }, + { 0x80007011, 0x000000CD, 0x1 }, { 0x80009010, 0x000000C0, 0x1 }, { 0x0000201B, 0x0000009D, 0x0 }, { 0x80005012, 0x000000C0, 0x1 }, @@ -158,7 +158,7 @@ static const struct ddi_buf_trans skl_u_ddi_translations_dp[] = { static const struct ddi_buf_trans skl_y_ddi_translations_dp[] = { { 0x00000018, 0x000000A2, 0x0 }, { 0x00005012, 0x00000088, 0x0 }, - { 0x80007011, 0x000000CD, 0x0 }, + { 0x80007011, 0x000000CD, 0x3 }, { 0x80009010, 0x000000C0, 0x3 }, { 0x00000018, 0x0000009D, 0x0 }, { 0x80005012, 0x000000C0, 0x3 }, @@ -301,45 +301,34 @@ static const struct bxt_ddi_buf_trans bxt_ddi_translations_hdmi[] = { { 154, 0x9A, 1, 128, true }, /* 9: 1200 0 */ }; -static void bxt_ddi_vswing_sequence(struct drm_i915_private *dev_priv, - u32 level, enum port port, int type); - -static void ddi_get_encoder_port(struct intel_encoder *intel_encoder, - struct intel_digital_port **dig_port, - enum port *port) +enum port intel_ddi_get_encoder_port(struct intel_encoder *encoder) { - struct drm_encoder *encoder = &intel_encoder->base; - - switch (intel_encoder->type) { + switch (encoder->type) { case INTEL_OUTPUT_DP_MST: - *dig_port = enc_to_mst(encoder)->primary; - *port = (*dig_port)->port; - break; - default: - WARN(1, "Invalid DDI encoder type %d\n", intel_encoder->type); - /* fallthrough and treat as unknown */ + return enc_to_mst(&encoder->base)->primary->port; case INTEL_OUTPUT_DP: case INTEL_OUTPUT_EDP: case INTEL_OUTPUT_HDMI: case INTEL_OUTPUT_UNKNOWN: - *dig_port = enc_to_dig_port(encoder); - *port = (*dig_port)->port; - break; + return enc_to_dig_port(&encoder->base)->port; case INTEL_OUTPUT_ANALOG: - *dig_port = NULL; - *port = PORT_E; - break; + return PORT_E; + default: + MISSING_CASE(encoder->type); + return PORT_A; } } -enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder) +static const struct ddi_buf_trans * +bdw_get_buf_trans_edp(struct drm_i915_private *dev_priv, int *n_entries) { - struct intel_digital_port *dig_port; - enum port port; - - ddi_get_encoder_port(intel_encoder, &dig_port, &port); - - return port; + if (dev_priv->vbt.edp.low_vswing) { + *n_entries = ARRAY_SIZE(bdw_ddi_translations_edp); + return bdw_ddi_translations_edp; + } else { + *n_entries = ARRAY_SIZE(bdw_ddi_translations_dp); + return bdw_ddi_translations_dp; + } } static const struct ddi_buf_trans * @@ -388,39 +377,58 @@ skl_get_buf_trans_hdmi(struct drm_i915_private *dev_priv, int *n_entries) } } +static int intel_ddi_hdmi_level(struct drm_i915_private *dev_priv, enum port port) +{ + int n_hdmi_entries; + int hdmi_level; + int hdmi_default_entry; + + hdmi_level = dev_priv->vbt.ddi_port_info[port].hdmi_level_shift; + + if (IS_BROXTON(dev_priv)) + return hdmi_level; + + if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { + skl_get_buf_trans_hdmi(dev_priv, &n_hdmi_entries); + hdmi_default_entry = 8; + } else if (IS_BROADWELL(dev_priv)) { + n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi); + hdmi_default_entry = 7; + } else if (IS_HASWELL(dev_priv)) { + n_hdmi_entries = ARRAY_SIZE(hsw_ddi_translations_hdmi); + hdmi_default_entry = 6; + } else { + WARN(1, "ddi translation table missing\n"); + n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi); + hdmi_default_entry = 7; + } + + /* Choose a good default if VBT is badly populated */ + if (hdmi_level == HDMI_LEVEL_SHIFT_UNKNOWN || + hdmi_level >= n_hdmi_entries) + hdmi_level = hdmi_default_entry; + + return hdmi_level; +} + /* * Starting with Haswell, DDI port buffers must be programmed with correct - * values in advance. The buffer values are different for FDI and DP modes, - * but the HDMI/DVI fields are shared among those. So we program the DDI - * in either FDI or DP modes only, as HDMI connections will work with both - * of those + * values in advance. This function programs the correct values for + * DP/eDP/FDI use cases. */ -void intel_prepare_ddi_buffer(struct intel_encoder *encoder) +void intel_prepare_dp_ddi_buffers(struct intel_encoder *encoder) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); u32 iboost_bit = 0; - int i, n_hdmi_entries, n_dp_entries, n_edp_entries, hdmi_default_entry, - size; - int hdmi_level; - enum port port; + int i, n_dp_entries, n_edp_entries, size; + enum port port = intel_ddi_get_encoder_port(encoder); const struct ddi_buf_trans *ddi_translations_fdi; const struct ddi_buf_trans *ddi_translations_dp; const struct ddi_buf_trans *ddi_translations_edp; - const struct ddi_buf_trans *ddi_translations_hdmi; const struct ddi_buf_trans *ddi_translations; - port = intel_ddi_get_encoder_port(encoder); - hdmi_level = dev_priv->vbt.ddi_port_info[port].hdmi_level_shift; - - if (IS_BROXTON(dev_priv)) { - if (encoder->type != INTEL_OUTPUT_HDMI) - return; - - /* Vswing programming for HDMI */ - bxt_ddi_vswing_sequence(dev_priv, hdmi_level, port, - INTEL_OUTPUT_HDMI); + if (IS_BROXTON(dev_priv)) return; - } if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { ddi_translations_fdi = NULL; @@ -428,13 +436,10 @@ void intel_prepare_ddi_buffer(struct intel_encoder *encoder) skl_get_buf_trans_dp(dev_priv, &n_dp_entries); ddi_translations_edp = skl_get_buf_trans_edp(dev_priv, &n_edp_entries); - ddi_translations_hdmi = - skl_get_buf_trans_hdmi(dev_priv, &n_hdmi_entries); - hdmi_default_entry = 8; + /* If we're boosting the current, set bit 31 of trans1 */ - if (dev_priv->vbt.ddi_port_info[port].hdmi_boost_level || - dev_priv->vbt.ddi_port_info[port].dp_boost_level) - iboost_bit = 1<<31; + if (dev_priv->vbt.ddi_port_info[port].dp_boost_level) + iboost_bit = DDI_BUF_BALANCE_LEG_ENABLE; if (WARN_ON(encoder->type == INTEL_OUTPUT_EDP && port != PORT_A && port != PORT_E && @@ -443,38 +448,20 @@ void intel_prepare_ddi_buffer(struct intel_encoder *encoder) } else if (IS_BROADWELL(dev_priv)) { ddi_translations_fdi = bdw_ddi_translations_fdi; ddi_translations_dp = bdw_ddi_translations_dp; - - if (dev_priv->vbt.edp.low_vswing) { - ddi_translations_edp = bdw_ddi_translations_edp; - n_edp_entries = ARRAY_SIZE(bdw_ddi_translations_edp); - } else { - ddi_translations_edp = bdw_ddi_translations_dp; - n_edp_entries = ARRAY_SIZE(bdw_ddi_translations_dp); - } - - ddi_translations_hdmi = bdw_ddi_translations_hdmi; - + ddi_translations_edp = bdw_get_buf_trans_edp(dev_priv, &n_edp_entries); n_dp_entries = ARRAY_SIZE(bdw_ddi_translations_dp); - n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi); - hdmi_default_entry = 7; } else if (IS_HASWELL(dev_priv)) { ddi_translations_fdi = hsw_ddi_translations_fdi; ddi_translations_dp = hsw_ddi_translations_dp; ddi_translations_edp = hsw_ddi_translations_dp; - ddi_translations_hdmi = hsw_ddi_translations_hdmi; n_dp_entries = n_edp_entries = ARRAY_SIZE(hsw_ddi_translations_dp); - n_hdmi_entries = ARRAY_SIZE(hsw_ddi_translations_hdmi); - hdmi_default_entry = 6; } else { WARN(1, "ddi translation table missing\n"); ddi_translations_edp = bdw_ddi_translations_dp; ddi_translations_fdi = bdw_ddi_translations_fdi; ddi_translations_dp = bdw_ddi_translations_dp; - ddi_translations_hdmi = bdw_ddi_translations_hdmi; n_edp_entries = ARRAY_SIZE(bdw_ddi_translations_edp); n_dp_entries = ARRAY_SIZE(bdw_ddi_translations_dp); - n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi); - hdmi_default_entry = 7; } switch (encoder->type) { @@ -483,7 +470,6 @@ void intel_prepare_ddi_buffer(struct intel_encoder *encoder) size = n_edp_entries; break; case INTEL_OUTPUT_DP: - case INTEL_OUTPUT_HDMI: ddi_translations = ddi_translations_dp; size = n_dp_entries; break; @@ -501,19 +487,48 @@ void intel_prepare_ddi_buffer(struct intel_encoder *encoder) I915_WRITE(DDI_BUF_TRANS_HI(port, i), ddi_translations[i].trans2); } +} - if (encoder->type != INTEL_OUTPUT_HDMI) +/* + * Starting with Haswell, DDI port buffers must be programmed with correct + * values in advance. This function programs the correct values for + * HDMI/DVI use cases. + */ +static void intel_prepare_hdmi_ddi_buffers(struct intel_encoder *encoder) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + u32 iboost_bit = 0; + int n_hdmi_entries, hdmi_level; + enum port port = intel_ddi_get_encoder_port(encoder); + const struct ddi_buf_trans *ddi_translations_hdmi; + + if (IS_BROXTON(dev_priv)) return; - /* Choose a good default if VBT is badly populated */ - if (hdmi_level == HDMI_LEVEL_SHIFT_UNKNOWN || - hdmi_level >= n_hdmi_entries) - hdmi_level = hdmi_default_entry; + hdmi_level = intel_ddi_hdmi_level(dev_priv, port); + + if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { + ddi_translations_hdmi = skl_get_buf_trans_hdmi(dev_priv, &n_hdmi_entries); + + /* If we're boosting the current, set bit 31 of trans1 */ + if (dev_priv->vbt.ddi_port_info[port].hdmi_boost_level) + iboost_bit = DDI_BUF_BALANCE_LEG_ENABLE; + } else if (IS_BROADWELL(dev_priv)) { + ddi_translations_hdmi = bdw_ddi_translations_hdmi; + n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi); + } else if (IS_HASWELL(dev_priv)) { + ddi_translations_hdmi = hsw_ddi_translations_hdmi; + n_hdmi_entries = ARRAY_SIZE(hsw_ddi_translations_hdmi); + } else { + WARN(1, "ddi translation table missing\n"); + ddi_translations_hdmi = bdw_ddi_translations_hdmi; + n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi); + } /* Entry 9 is for HDMI: */ - I915_WRITE(DDI_BUF_TRANS_LO(port, i), + I915_WRITE(DDI_BUF_TRANS_LO(port, 9), ddi_translations_hdmi[hdmi_level].trans1 | iboost_bit); - I915_WRITE(DDI_BUF_TRANS_HI(port, i), + I915_WRITE(DDI_BUF_TRANS_HI(port, 9), ddi_translations_hdmi[hdmi_level].trans2); } @@ -531,6 +546,27 @@ static void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv, DRM_ERROR("Timeout waiting for DDI BUF %c idle bit\n", port_name(port)); } +static uint32_t hsw_pll_to_ddi_pll_sel(struct intel_shared_dpll *pll) +{ + switch (pll->id) { + case DPLL_ID_WRPLL1: + return PORT_CLK_SEL_WRPLL1; + case DPLL_ID_WRPLL2: + return PORT_CLK_SEL_WRPLL2; + case DPLL_ID_SPLL: + return PORT_CLK_SEL_SPLL; + case DPLL_ID_LCPLL_810: + return PORT_CLK_SEL_LCPLL_810; + case DPLL_ID_LCPLL_1350: + return PORT_CLK_SEL_LCPLL_1350; + case DPLL_ID_LCPLL_2700: + return PORT_CLK_SEL_LCPLL_2700; + default: + MISSING_CASE(pll->id); + return PORT_CLK_SEL_NONE; + } +} + /* Starting with Haswell, different DDI ports can work in FDI mode for * connection to the PCH-located connectors. For this, it is necessary to train * both the DDI port and PCH receiver for the desired DDI buffer settings. @@ -546,11 +582,11 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_encoder *encoder; - u32 temp, i, rx_ctl_val; + u32 temp, i, rx_ctl_val, ddi_pll_sel; for_each_encoder_on_crtc(dev, crtc, encoder) { WARN_ON(encoder->type != INTEL_OUTPUT_ANALOG); - intel_prepare_ddi_buffer(encoder); + intel_prepare_dp_ddi_buffers(encoder); } /* Set the FDI_RX_MISC pwrdn lanes and the 2 workarounds listed at the @@ -577,8 +613,9 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) I915_WRITE(FDI_RX_CTL(PIPE_A), rx_ctl_val); /* Configure Port Clock Select */ - I915_WRITE(PORT_CLK_SEL(PORT_E), intel_crtc->config->ddi_pll_sel); - WARN_ON(intel_crtc->config->ddi_pll_sel != PORT_CLK_SEL_SPLL); + ddi_pll_sel = hsw_pll_to_ddi_pll_sel(intel_crtc->config->shared_dpll); + I915_WRITE(PORT_CLK_SEL(PORT_E), ddi_pll_sel); + WARN_ON(ddi_pll_sel != PORT_CLK_SEL_SPLL); /* Start the training iterating through available voltages and emphasis, * testing each value twice. */ @@ -855,7 +892,7 @@ static void skl_ddi_clock_get(struct intel_encoder *encoder, int link_clock = 0; uint32_t dpll_ctl1, dpll; - dpll = pipe_config->ddi_pll_sel; + dpll = intel_get_shared_dpll_id(dev_priv, pipe_config->shared_dpll); dpll_ctl1 = I915_READ(DPLL_CTRL1); @@ -903,7 +940,7 @@ static void hsw_ddi_clock_get(struct intel_encoder *encoder, int link_clock = 0; u32 val, pll; - val = pipe_config->ddi_pll_sel; + val = hsw_pll_to_ddi_pll_sel(pipe_config->shared_dpll); switch (val & PORT_CLK_SEL_MASK) { case PORT_CLK_SEL_LCPLL_810: link_clock = 81000; @@ -1111,7 +1148,6 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); - struct drm_encoder *encoder = &intel_encoder->base; struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = to_i915(dev); enum pipe pipe = intel_crtc->pipe; @@ -1177,29 +1213,15 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) temp |= TRANS_DDI_MODE_SELECT_HDMI; else temp |= TRANS_DDI_MODE_SELECT_DVI; - } else if (type == INTEL_OUTPUT_ANALOG) { temp |= TRANS_DDI_MODE_SELECT_FDI; temp |= (intel_crtc->config->fdi_lanes - 1) << 1; - } else if (type == INTEL_OUTPUT_DP || type == INTEL_OUTPUT_EDP) { - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - - if (intel_dp->is_mst) { - temp |= TRANS_DDI_MODE_SELECT_DP_MST; - } else - temp |= TRANS_DDI_MODE_SELECT_DP_SST; - + temp |= TRANS_DDI_MODE_SELECT_DP_SST; temp |= DDI_PORT_WIDTH(intel_crtc->config->lane_count); } else if (type == INTEL_OUTPUT_DP_MST) { - struct intel_dp *intel_dp = &enc_to_mst(encoder)->primary->dp; - - if (intel_dp->is_mst) { - temp |= TRANS_DDI_MODE_SELECT_DP_MST; - } else - temp |= TRANS_DDI_MODE_SELECT_DP_SST; - + temp |= TRANS_DDI_MODE_SELECT_DP_MST; temp |= DDI_PORT_WIDTH(intel_crtc->config->lane_count); } else { WARN(1, "Invalid encoder type %d for pipe %c\n", @@ -1379,14 +1401,30 @@ void intel_ddi_disable_pipe_clock(struct intel_crtc *intel_crtc) TRANS_CLK_SEL_DISABLED); } -static void skl_ddi_set_iboost(struct drm_i915_private *dev_priv, - u32 level, enum port port, int type) +static void _skl_ddi_set_iboost(struct drm_i915_private *dev_priv, + enum port port, uint8_t iboost) +{ + u32 tmp; + + tmp = I915_READ(DISPIO_CR_TX_BMU_CR0); + tmp &= ~(BALANCE_LEG_MASK(port) | BALANCE_LEG_DISABLE(port)); + if (iboost) + tmp |= iboost << BALANCE_LEG_SHIFT(port); + else + tmp |= BALANCE_LEG_DISABLE(port); + I915_WRITE(DISPIO_CR_TX_BMU_CR0, tmp); +} + +static void skl_ddi_set_iboost(struct intel_encoder *encoder, u32 level) { + struct intel_digital_port *intel_dig_port = enc_to_dig_port(&encoder->base); + struct drm_i915_private *dev_priv = to_i915(intel_dig_port->base.base.dev); + enum port port = intel_dig_port->port; + int type = encoder->type; const struct ddi_buf_trans *ddi_translations; uint8_t iboost; uint8_t dp_iboost, hdmi_iboost; int n_entries; - u32 reg; /* VBT may override standard boost values */ dp_iboost = dev_priv->vbt.ddi_port_info[port].dp_boost_level; @@ -1428,16 +1466,10 @@ static void skl_ddi_set_iboost(struct drm_i915_private *dev_priv, return; } - reg = I915_READ(DISPIO_CR_TX_BMU_CR0); - reg &= ~BALANCE_LEG_MASK(port); - reg &= ~(1 << (BALANCE_LEG_DISABLE_SHIFT + port)); + _skl_ddi_set_iboost(dev_priv, port, iboost); - if (iboost) - reg |= iboost << BALANCE_LEG_SHIFT(port); - else - reg |= 1 << (BALANCE_LEG_DISABLE_SHIFT + port); - - I915_WRITE(DISPIO_CR_TX_BMU_CR0, reg); + if (port == PORT_A && intel_dig_port->max_lanes == 4) + _skl_ddi_set_iboost(dev_priv, PORT_E, iboost); } static void bxt_ddi_vswing_sequence(struct drm_i915_private *dev_priv, @@ -1568,7 +1600,7 @@ uint32_t ddi_signal_levels(struct intel_dp *intel_dp) level = translate_signal_level(signal_levels); if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) - skl_ddi_set_iboost(dev_priv, level, port, encoder->type); + skl_ddi_set_iboost(encoder, level); else if (IS_BROXTON(dev_priv)) bxt_ddi_vswing_sequence(dev_priv, level, port, encoder->type); @@ -1576,13 +1608,15 @@ uint32_t ddi_signal_levels(struct intel_dp *intel_dp) } void intel_ddi_clk_select(struct intel_encoder *encoder, - const struct intel_crtc_state *pipe_config) + struct intel_shared_dpll *pll) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); enum port port = intel_ddi_get_encoder_port(encoder); + if (WARN_ON(!pll)) + return; + if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { - uint32_t dpll = pipe_config->ddi_pll_sel; uint32_t val; /* DDI -> PLL mapping */ @@ -1590,61 +1624,91 @@ void intel_ddi_clk_select(struct intel_encoder *encoder, val &= ~(DPLL_CTRL2_DDI_CLK_OFF(port) | DPLL_CTRL2_DDI_CLK_SEL_MASK(port)); - val |= (DPLL_CTRL2_DDI_CLK_SEL(dpll, port) | + val |= (DPLL_CTRL2_DDI_CLK_SEL(pll->id, port) | DPLL_CTRL2_DDI_SEL_OVERRIDE(port)); I915_WRITE(DPLL_CTRL2, val); } else if (INTEL_INFO(dev_priv)->gen < 9) { - WARN_ON(pipe_config->ddi_pll_sel == PORT_CLK_SEL_NONE); - I915_WRITE(PORT_CLK_SEL(port), pipe_config->ddi_pll_sel); + I915_WRITE(PORT_CLK_SEL(port), hsw_pll_to_ddi_pll_sel(pll)); } } -static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder) +static void intel_ddi_pre_enable_dp(struct intel_encoder *encoder, + int link_rate, uint32_t lane_count, + struct intel_shared_dpll *pll, + bool link_mst) { - struct drm_encoder *encoder = &intel_encoder->base; - struct drm_i915_private *dev_priv = to_i915(encoder->dev); - struct intel_crtc *crtc = to_intel_crtc(encoder->crtc); - enum port port = intel_ddi_get_encoder_port(intel_encoder); - int type = intel_encoder->type; - - if (type == INTEL_OUTPUT_HDMI) { - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); - - intel_dp_dual_mode_set_tmds_output(intel_hdmi, true); - } - - intel_prepare_ddi_buffer(intel_encoder); + struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + enum port port = intel_ddi_get_encoder_port(encoder); - if (type == INTEL_OUTPUT_EDP) { - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + intel_dp_set_link_params(intel_dp, link_rate, lane_count, + link_mst); + if (encoder->type == INTEL_OUTPUT_EDP) intel_edp_panel_on(intel_dp); - } - intel_ddi_clk_select(intel_encoder, crtc->config); + intel_ddi_clk_select(encoder, pll); + intel_prepare_dp_ddi_buffers(encoder); + intel_ddi_init_dp_buf_reg(encoder); + intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); + intel_dp_start_link_train(intel_dp); + if (port != PORT_A || INTEL_GEN(dev_priv) >= 9) + intel_dp_stop_link_train(intel_dp); +} - if (type == INTEL_OUTPUT_DP || type == INTEL_OUTPUT_EDP) { - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); +static void intel_ddi_pre_enable_hdmi(struct intel_encoder *encoder, + bool has_hdmi_sink, + struct drm_display_mode *adjusted_mode, + struct intel_shared_dpll *pll) +{ + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct drm_encoder *drm_encoder = &encoder->base; + enum port port = intel_ddi_get_encoder_port(encoder); + int level = intel_ddi_hdmi_level(dev_priv, port); - intel_dp_set_link_params(intel_dp, crtc->config); + intel_dp_dual_mode_set_tmds_output(intel_hdmi, true); + intel_ddi_clk_select(encoder, pll); + intel_prepare_hdmi_ddi_buffers(encoder); + if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) + skl_ddi_set_iboost(encoder, level); + else if (IS_BROXTON(dev_priv)) + bxt_ddi_vswing_sequence(dev_priv, level, port, + INTEL_OUTPUT_HDMI); - intel_ddi_init_dp_buf_reg(intel_encoder); + intel_hdmi->set_infoframes(drm_encoder, + has_hdmi_sink, + adjusted_mode); +} - intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); - intel_dp_start_link_train(intel_dp); - if (port != PORT_A || INTEL_INFO(dev_priv)->gen >= 9) - intel_dp_stop_link_train(intel_dp); - } else if (type == INTEL_OUTPUT_HDMI) { - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); +static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) +{ + struct drm_encoder *encoder = &intel_encoder->base; + struct intel_crtc *crtc = to_intel_crtc(encoder->crtc); + int type = intel_encoder->type; - intel_hdmi->set_infoframes(encoder, - crtc->config->has_hdmi_sink, - &crtc->config->base.adjusted_mode); + if (type == INTEL_OUTPUT_DP || type == INTEL_OUTPUT_EDP) { + intel_ddi_pre_enable_dp(intel_encoder, + crtc->config->port_clock, + crtc->config->lane_count, + crtc->config->shared_dpll, + intel_crtc_has_type(crtc->config, + INTEL_OUTPUT_DP_MST)); + } + if (type == INTEL_OUTPUT_HDMI) { + intel_ddi_pre_enable_hdmi(intel_encoder, + crtc->config->has_hdmi_sink, + &crtc->config->base.adjusted_mode, + crtc->config->shared_dpll); } } -static void intel_ddi_post_disable(struct intel_encoder *intel_encoder) +static void intel_ddi_post_disable(struct intel_encoder *intel_encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct drm_encoder *encoder = &intel_encoder->base; struct drm_device *dev = encoder->dev; @@ -1654,6 +1718,8 @@ static void intel_ddi_post_disable(struct intel_encoder *intel_encoder) uint32_t val; bool wait = false; + /* old_crtc_state and old_conn_state are NULL when called from DP_MST */ + val = I915_READ(DDI_BUF_CTL(port)); if (val & DDI_BUF_CTL_ENABLE) { val &= ~DDI_BUF_CTL_ENABLE; @@ -1689,7 +1755,42 @@ static void intel_ddi_post_disable(struct intel_encoder *intel_encoder) } } -static void intel_enable_ddi(struct intel_encoder *intel_encoder) +void intel_ddi_fdi_post_disable(struct intel_encoder *intel_encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) +{ + struct drm_i915_private *dev_priv = to_i915(intel_encoder->base.dev); + uint32_t val; + + /* + * Bspec lists this as both step 13 (before DDI_BUF_CTL disable) + * and step 18 (after clearing PORT_CLK_SEL). Based on a BUN, + * step 13 is the correct place for it. Step 18 is where it was + * originally before the BUN. + */ + val = I915_READ(FDI_RX_CTL(PIPE_A)); + val &= ~FDI_RX_ENABLE; + I915_WRITE(FDI_RX_CTL(PIPE_A), val); + + intel_ddi_post_disable(intel_encoder, old_crtc_state, old_conn_state); + + val = I915_READ(FDI_RX_MISC(PIPE_A)); + val &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK); + val |= FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2); + I915_WRITE(FDI_RX_MISC(PIPE_A), val); + + val = I915_READ(FDI_RX_CTL(PIPE_A)); + val &= ~FDI_PCDCLK; + I915_WRITE(FDI_RX_CTL(PIPE_A), val); + + val = I915_READ(FDI_RX_CTL(PIPE_A)); + val &= ~FDI_RX_PLL_ENABLE; + I915_WRITE(FDI_RX_CTL(PIPE_A), val); +} + +static void intel_enable_ddi(struct intel_encoder *intel_encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct drm_encoder *encoder = &intel_encoder->base; struct drm_crtc *crtc = encoder->crtc; @@ -1718,7 +1819,7 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder) intel_edp_backlight_on(intel_dp); intel_psr_enable(intel_dp); - intel_edp_drrs_enable(intel_dp); + intel_edp_drrs_enable(intel_dp, pipe_config); } if (intel_crtc->config->has_audio) { @@ -1727,7 +1828,9 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder) } } -static void intel_disable_ddi(struct intel_encoder *intel_encoder) +static void intel_disable_ddi(struct intel_encoder *intel_encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct drm_encoder *encoder = &intel_encoder->base; struct drm_crtc *crtc = encoder->crtc; @@ -1744,7 +1847,7 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder) if (type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - intel_edp_drrs_disable(intel_dp); + intel_edp_drrs_disable(intel_dp, old_crtc_state); intel_psr_disable(intel_dp); intel_edp_backlight_off(intel_dp); } @@ -2033,7 +2136,9 @@ bxt_ddi_phy_calc_lane_lat_optim_mask(struct intel_encoder *encoder, } } -static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder) +static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); struct drm_i915_private *dev_priv = to_i915(dport->base.base.dev); @@ -2105,7 +2210,7 @@ void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp) val = DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_SCRAMBLE_DISABLE; - if (intel_dp->is_mst) + if (intel_dp->link_mst) val |= DP_TP_CTL_MODE_MST; else { val |= DP_TP_CTL_MODE_SST; @@ -2122,38 +2227,6 @@ void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp) udelay(600); } -void intel_ddi_fdi_disable(struct drm_crtc *crtc) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->dev); - struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); - uint32_t val; - - /* - * Bspec lists this as both step 13 (before DDI_BUF_CTL disable) - * and step 18 (after clearing PORT_CLK_SEL). Based on a BUN, - * step 13 is the correct place for it. Step 18 is where it was - * originally before the BUN. - */ - val = I915_READ(FDI_RX_CTL(PIPE_A)); - val &= ~FDI_RX_ENABLE; - I915_WRITE(FDI_RX_CTL(PIPE_A), val); - - intel_ddi_post_disable(intel_encoder); - - val = I915_READ(FDI_RX_MISC(PIPE_A)); - val &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK); - val |= FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2); - I915_WRITE(FDI_RX_MISC(PIPE_A), val); - - val = I915_READ(FDI_RX_CTL(PIPE_A)); - val &= ~FDI_PCDCLK; - I915_WRITE(FDI_RX_CTL(PIPE_A), val); - - val = I915_READ(FDI_RX_CTL(PIPE_A)); - val &= ~FDI_RX_PLL_ENABLE; - I915_WRITE(FDI_RX_CTL(PIPE_A), val); -} - void intel_ddi_get_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config) { @@ -2253,7 +2326,8 @@ void intel_ddi_get_config(struct intel_encoder *encoder, } static bool intel_ddi_compute_config(struct intel_encoder *encoder, - struct intel_crtc_state *pipe_config) + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); int type = encoder->type; @@ -2266,9 +2340,9 @@ static bool intel_ddi_compute_config(struct intel_encoder *encoder, pipe_config->cpu_transcoder = TRANSCODER_EDP; if (type == INTEL_OUTPUT_HDMI) - ret = intel_hdmi_compute_config(encoder, pipe_config); + ret = intel_hdmi_compute_config(encoder, pipe_config, conn_state); else - ret = intel_dp_compute_config(encoder, pipe_config); + ret = intel_dp_compute_config(encoder, pipe_config, conn_state); if (IS_BROXTON(dev_priv) && ret) pipe_config->lane_lat_optim_mask = @@ -2319,6 +2393,45 @@ intel_ddi_init_hdmi_connector(struct intel_digital_port *intel_dig_port) return connector; } +struct intel_shared_dpll * +intel_ddi_get_link_dpll(struct intel_dp *intel_dp, int clock) +{ + struct intel_connector *connector = intel_dp->attached_connector; + struct intel_encoder *encoder = connector->encoder; + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct intel_shared_dpll *pll = NULL; + struct intel_shared_dpll_config tmp_pll_config; + enum intel_dpll_id dpll_id; + + if (IS_BROXTON(dev_priv)) { + dpll_id = (enum intel_dpll_id)dig_port->port; + /* + * Select the required PLL. This works for platforms where + * there is no shared DPLL. + */ + pll = &dev_priv->shared_dplls[dpll_id]; + if (WARN_ON(pll->active_mask)) { + + DRM_ERROR("Shared DPLL in use. active_mask:%x\n", + pll->active_mask); + return NULL; + } + tmp_pll_config = pll->config; + if (!bxt_ddi_dp_set_dpll_hw_state(clock, + &pll->config.hw_state)) { + DRM_ERROR("Could not setup DPLL\n"); + pll->config = tmp_pll_config; + return NULL; + } + } else if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { + pll = skl_find_link_pll(dev_priv, clock); + } else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { + pll = hsw_ddi_dp_get_dpll(encoder, clock); + } + return pll; +} + void intel_ddi_init(struct drm_device *dev, enum port port) { struct drm_i915_private *dev_priv = to_i915(dev); diff --git a/drivers/gpu/drm/i915/intel_device_info.c b/drivers/gpu/drm/i915/intel_device_info.c index cba137f..73b6858 100644 --- a/drivers/gpu/drm/i915/intel_device_info.c +++ b/drivers/gpu/drm/i915/intel_device_info.c @@ -46,71 +46,70 @@ void intel_device_info_dump(struct drm_i915_private *dev_priv) static void cherryview_sseu_info_init(struct drm_i915_private *dev_priv) { - struct intel_device_info *info = mkwrite_device_info(dev_priv); + struct sseu_dev_info *sseu = &mkwrite_device_info(dev_priv)->sseu; u32 fuse, eu_dis; fuse = I915_READ(CHV_FUSE_GT); - info->slice_total = 1; + sseu->slice_mask = BIT(0); if (!(fuse & CHV_FGT_DISABLE_SS0)) { - info->subslice_per_slice++; + sseu->subslice_mask |= BIT(0); eu_dis = fuse & (CHV_FGT_EU_DIS_SS0_R0_MASK | CHV_FGT_EU_DIS_SS0_R1_MASK); - info->eu_total += 8 - hweight32(eu_dis); + sseu->eu_total += 8 - hweight32(eu_dis); } if (!(fuse & CHV_FGT_DISABLE_SS1)) { - info->subslice_per_slice++; + sseu->subslice_mask |= BIT(1); eu_dis = fuse & (CHV_FGT_EU_DIS_SS1_R0_MASK | CHV_FGT_EU_DIS_SS1_R1_MASK); - info->eu_total += 8 - hweight32(eu_dis); + sseu->eu_total += 8 - hweight32(eu_dis); } - info->subslice_total = info->subslice_per_slice; /* * CHV expected to always have a uniform distribution of EU * across subslices. */ - info->eu_per_subslice = info->subslice_total ? - info->eu_total / info->subslice_total : + sseu->eu_per_subslice = sseu_subslice_total(sseu) ? + sseu->eu_total / sseu_subslice_total(sseu) : 0; /* * CHV supports subslice power gating on devices with more than * one subslice, and supports EU power gating on devices with * more than one EU pair per subslice. */ - info->has_slice_pg = 0; - info->has_subslice_pg = (info->subslice_total > 1); - info->has_eu_pg = (info->eu_per_subslice > 2); + sseu->has_slice_pg = 0; + sseu->has_subslice_pg = sseu_subslice_total(sseu) > 1; + sseu->has_eu_pg = (sseu->eu_per_subslice > 2); } static void gen9_sseu_info_init(struct drm_i915_private *dev_priv) { struct intel_device_info *info = mkwrite_device_info(dev_priv); + struct sseu_dev_info *sseu = &info->sseu; int s_max = 3, ss_max = 4, eu_max = 8; int s, ss; - u32 fuse2, s_enable, ss_disable, eu_disable; + u32 fuse2, eu_disable; u8 eu_mask = 0xff; fuse2 = I915_READ(GEN8_FUSE2); - s_enable = (fuse2 & GEN8_F2_S_ENA_MASK) >> GEN8_F2_S_ENA_SHIFT; - ss_disable = (fuse2 & GEN9_F2_SS_DIS_MASK) >> GEN9_F2_SS_DIS_SHIFT; + sseu->slice_mask = (fuse2 & GEN8_F2_S_ENA_MASK) >> GEN8_F2_S_ENA_SHIFT; - info->slice_total = hweight32(s_enable); /* * The subslice disable field is global, i.e. it applies * to each of the enabled slices. */ - info->subslice_per_slice = ss_max - hweight32(ss_disable); - info->subslice_total = info->slice_total * info->subslice_per_slice; + sseu->subslice_mask = (1 << ss_max) - 1; + sseu->subslice_mask &= ~((fuse2 & GEN9_F2_SS_DIS_MASK) >> + GEN9_F2_SS_DIS_SHIFT); /* * Iterate through enabled slices and subslices to * count the total enabled EU. */ for (s = 0; s < s_max; s++) { - if (!(s_enable & BIT(s))) + if (!(sseu->slice_mask & BIT(s))) /* skip disabled slice */ continue; @@ -118,7 +117,7 @@ static void gen9_sseu_info_init(struct drm_i915_private *dev_priv) for (ss = 0; ss < ss_max; ss++) { int eu_per_ss; - if (ss_disable & BIT(ss)) + if (!(sseu->subslice_mask & BIT(ss))) /* skip disabled subslice */ continue; @@ -131,9 +130,9 @@ static void gen9_sseu_info_init(struct drm_i915_private *dev_priv) * subslices if they are unbalanced. */ if (eu_per_ss == 7) - info->subslice_7eu[s] |= BIT(ss); + sseu->subslice_7eu[s] |= BIT(ss); - info->eu_total += eu_per_ss; + sseu->eu_total += eu_per_ss; } } @@ -144,9 +143,9 @@ static void gen9_sseu_info_init(struct drm_i915_private *dev_priv) * recovery. BXT is expected to be perfectly uniform in EU * distribution. */ - info->eu_per_subslice = info->subslice_total ? - DIV_ROUND_UP(info->eu_total, - info->subslice_total) : 0; + sseu->eu_per_subslice = sseu_subslice_total(sseu) ? + DIV_ROUND_UP(sseu->eu_total, + sseu_subslice_total(sseu)) : 0; /* * SKL supports slice power gating on devices with more than * one slice, and supports EU power gating on devices with @@ -155,15 +154,15 @@ static void gen9_sseu_info_init(struct drm_i915_private *dev_priv) * supports EU power gating on devices with more than one EU * pair per subslice. */ - info->has_slice_pg = + sseu->has_slice_pg = (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) && - info->slice_total > 1; - info->has_subslice_pg = - IS_BROXTON(dev_priv) && info->subslice_total > 1; - info->has_eu_pg = info->eu_per_subslice > 2; + hweight8(sseu->slice_mask) > 1; + sseu->has_subslice_pg = + IS_BROXTON(dev_priv) && sseu_subslice_total(sseu) > 1; + sseu->has_eu_pg = sseu->eu_per_subslice > 2; if (IS_BROXTON(dev_priv)) { -#define IS_SS_DISABLED(_ss_disable, ss) (_ss_disable & BIT(ss)) +#define IS_SS_DISABLED(ss) (!(sseu->subslice_mask & BIT(ss))) /* * There is a HW issue in 2x6 fused down parts that requires * Pooled EU to be enabled as a WA. The pool configuration @@ -171,19 +170,18 @@ static void gen9_sseu_info_init(struct drm_i915_private *dev_priv) * doesn't affect if the device has all 3 subslices enabled. */ /* WaEnablePooledEuFor2x6:bxt */ - info->has_pooled_eu = ((info->subslice_per_slice == 3) || - (info->subslice_per_slice == 2 && + info->has_pooled_eu = ((hweight8(sseu->subslice_mask) == 3) || + (hweight8(sseu->subslice_mask) == 2 && INTEL_REVID(dev_priv) < BXT_REVID_C0)); - info->min_eu_in_pool = 0; + sseu->min_eu_in_pool = 0; if (info->has_pooled_eu) { - if (IS_SS_DISABLED(ss_disable, 0) || - IS_SS_DISABLED(ss_disable, 2)) - info->min_eu_in_pool = 3; - else if (IS_SS_DISABLED(ss_disable, 1)) - info->min_eu_in_pool = 6; + if (IS_SS_DISABLED(2) || IS_SS_DISABLED(0)) + sseu->min_eu_in_pool = 3; + else if (IS_SS_DISABLED(1)) + sseu->min_eu_in_pool = 6; else - info->min_eu_in_pool = 9; + sseu->min_eu_in_pool = 9; } #undef IS_SS_DISABLED } @@ -191,14 +189,20 @@ static void gen9_sseu_info_init(struct drm_i915_private *dev_priv) static void broadwell_sseu_info_init(struct drm_i915_private *dev_priv) { - struct intel_device_info *info = mkwrite_device_info(dev_priv); + struct sseu_dev_info *sseu = &mkwrite_device_info(dev_priv)->sseu; const int s_max = 3, ss_max = 3, eu_max = 8; int s, ss; - u32 fuse2, eu_disable[s_max], s_enable, ss_disable; + u32 fuse2, eu_disable[s_max]; fuse2 = I915_READ(GEN8_FUSE2); - s_enable = (fuse2 & GEN8_F2_S_ENA_MASK) >> GEN8_F2_S_ENA_SHIFT; - ss_disable = (fuse2 & GEN8_F2_SS_DIS_MASK) >> GEN8_F2_SS_DIS_SHIFT; + sseu->slice_mask = (fuse2 & GEN8_F2_S_ENA_MASK) >> GEN8_F2_S_ENA_SHIFT; + /* + * The subslice disable field is global, i.e. it applies + * to each of the enabled slices. + */ + sseu->subslice_mask = BIT(ss_max) - 1; + sseu->subslice_mask &= ~((fuse2 & GEN8_F2_SS_DIS_MASK) >> + GEN8_F2_SS_DIS_SHIFT); eu_disable[0] = I915_READ(GEN8_EU_DISABLE0) & GEN8_EU_DIS0_S0_MASK; eu_disable[1] = (I915_READ(GEN8_EU_DISABLE0) >> GEN8_EU_DIS0_S1_SHIFT) | @@ -208,28 +212,19 @@ static void broadwell_sseu_info_init(struct drm_i915_private *dev_priv) ((I915_READ(GEN8_EU_DISABLE2) & GEN8_EU_DIS2_S2_MASK) << (32 - GEN8_EU_DIS1_S2_SHIFT)); - info->slice_total = hweight32(s_enable); - - /* - * The subslice disable field is global, i.e. it applies - * to each of the enabled slices. - */ - info->subslice_per_slice = ss_max - hweight32(ss_disable); - info->subslice_total = info->slice_total * info->subslice_per_slice; - /* * Iterate through enabled slices and subslices to * count the total enabled EU. */ for (s = 0; s < s_max; s++) { - if (!(s_enable & (0x1 << s))) + if (!(sseu->slice_mask & BIT(s))) /* skip disabled slice */ continue; for (ss = 0; ss < ss_max; ss++) { u32 n_disabled; - if (ss_disable & (0x1 << ss)) + if (!(sseu->subslice_mask & BIT(ss))) /* skip disabled subslice */ continue; @@ -239,9 +234,9 @@ static void broadwell_sseu_info_init(struct drm_i915_private *dev_priv) * Record which subslices have 7 EUs. */ if (eu_max - n_disabled == 7) - info->subslice_7eu[s] |= 1 << ss; + sseu->subslice_7eu[s] |= 1 << ss; - info->eu_total += eu_max - n_disabled; + sseu->eu_total += eu_max - n_disabled; } } @@ -250,16 +245,17 @@ static void broadwell_sseu_info_init(struct drm_i915_private *dev_priv) * subslices with the exception that any one EU in any one subslice may * be fused off for die recovery. */ - info->eu_per_subslice = info->subslice_total ? - DIV_ROUND_UP(info->eu_total, info->subslice_total) : 0; + sseu->eu_per_subslice = sseu_subslice_total(sseu) ? + DIV_ROUND_UP(sseu->eu_total, + sseu_subslice_total(sseu)) : 0; /* * BDW supports slice power gating on devices with more than * one slice. */ - info->has_slice_pg = (info->slice_total > 1); - info->has_subslice_pg = 0; - info->has_eu_pg = 0; + sseu->has_slice_pg = hweight8(sseu->slice_mask) > 1; + sseu->has_subslice_pg = 0; + sseu->has_eu_pg = 0; } /* @@ -374,15 +370,19 @@ void intel_device_info_runtime_init(struct drm_i915_private *dev_priv) if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_A1)) info->has_snoop = false; - DRM_DEBUG_DRIVER("slice total: %u\n", info->slice_total); - DRM_DEBUG_DRIVER("subslice total: %u\n", info->subslice_total); - DRM_DEBUG_DRIVER("subslice per slice: %u\n", info->subslice_per_slice); - DRM_DEBUG_DRIVER("EU total: %u\n", info->eu_total); - DRM_DEBUG_DRIVER("EU per subslice: %u\n", info->eu_per_subslice); + DRM_DEBUG_DRIVER("slice mask: %04x\n", info->sseu.slice_mask); + DRM_DEBUG_DRIVER("slice total: %u\n", hweight8(info->sseu.slice_mask)); + DRM_DEBUG_DRIVER("subslice total: %u\n", + sseu_subslice_total(&info->sseu)); + DRM_DEBUG_DRIVER("subslice mask %04x\n", info->sseu.subslice_mask); + DRM_DEBUG_DRIVER("subslice per slice: %u\n", + hweight8(info->sseu.subslice_mask)); + DRM_DEBUG_DRIVER("EU total: %u\n", info->sseu.eu_total); + DRM_DEBUG_DRIVER("EU per subslice: %u\n", info->sseu.eu_per_subslice); DRM_DEBUG_DRIVER("has slice power gating: %s\n", - info->has_slice_pg ? "y" : "n"); + info->sseu.has_slice_pg ? "y" : "n"); DRM_DEBUG_DRIVER("has subslice power gating: %s\n", - info->has_subslice_pg ? "y" : "n"); + info->sseu.has_subslice_pg ? "y" : "n"); DRM_DEBUG_DRIVER("has EU power gating: %s\n", - info->has_eu_pg ? "y" : "n"); + info->sseu.has_eu_pg ? "y" : "n"); } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c457eed..8d4c35d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -34,6 +34,7 @@ #include <drm/drm_edid.h> #include <drm/drmP.h> #include "intel_drv.h" +#include "intel_frontbuffer.h" #include <drm/i915_drm.h> #include "i915_drv.h" #include "i915_gem_dmabuf.h" @@ -1201,8 +1202,8 @@ void assert_panel_unlocked(struct drm_i915_private *dev_priv, if (HAS_PCH_SPLIT(dev)) { u32 port_sel; - pp_reg = PCH_PP_CONTROL; - port_sel = I915_READ(PCH_PP_ON_DELAYS) & PANEL_PORT_SELECT_MASK; + pp_reg = PP_CONTROL(0); + port_sel = I915_READ(PP_ON_DELAYS(0)) & PANEL_PORT_SELECT_MASK; if (port_sel == PANEL_PORT_SELECT_LVDS && I915_READ(PCH_LVDS) & LVDS_PIPEB_SELECT) @@ -1210,10 +1211,10 @@ void assert_panel_unlocked(struct drm_i915_private *dev_priv, /* XXX: else fix for eDP */ } else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) { /* presumably write lock depends on pipe, not port select */ - pp_reg = VLV_PIPE_PP_CONTROL(pipe); + pp_reg = PP_CONTROL(pipe); panel_pipe = pipe; } else { - pp_reg = PP_CONTROL; + pp_reg = PP_CONTROL(0); if (I915_READ(LVDS) & LVDS_PIPEB_SELECT) panel_pipe = PIPE_B; } @@ -1906,7 +1907,7 @@ static void ironlake_disable_pch_transcoder(struct drm_i915_private *dev_priv, } } -static void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv) +void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv) { u32 val; @@ -1958,12 +1959,12 @@ static void intel_enable_pipe(struct intel_crtc *crtc) * a plane. On ILK+ the pipe PLLs are integrated, so we don't * need the check. */ - if (HAS_GMCH_DISPLAY(dev_priv)) + if (HAS_GMCH_DISPLAY(dev_priv)) { if (intel_crtc_has_type(crtc->config, INTEL_OUTPUT_DSI)) assert_dsi_pll_enabled(dev_priv); else assert_pll_enabled(dev_priv, pipe); - else { + } else { if (crtc->config->has_pch_encoder) { /* if driving the PCH, we need FDI enabled */ assert_fdi_rx_pll_enabled(dev_priv, pch_transcoder); @@ -2146,33 +2147,6 @@ intel_fill_fb_ggtt_view(struct i915_ggtt_view *view, } } -static void -intel_fill_fb_info(struct drm_i915_private *dev_priv, - struct drm_framebuffer *fb) -{ - struct intel_rotation_info *info = &to_intel_framebuffer(fb)->rot_info; - unsigned int tile_size, tile_width, tile_height, cpp; - - tile_size = intel_tile_size(dev_priv); - - cpp = drm_format_plane_cpp(fb->pixel_format, 0); - intel_tile_dims(dev_priv, &tile_width, &tile_height, - fb->modifier[0], cpp); - - info->plane[0].width = DIV_ROUND_UP(fb->pitches[0], tile_width * cpp); - info->plane[0].height = DIV_ROUND_UP(fb->height, tile_height); - - if (info->pixel_format == DRM_FORMAT_NV12) { - cpp = drm_format_plane_cpp(fb->pixel_format, 1); - intel_tile_dims(dev_priv, &tile_width, &tile_height, - fb->modifier[1], cpp); - - info->uv_offset = fb->offsets[1]; - info->plane[1].width = DIV_ROUND_UP(fb->pitches[1], tile_width * cpp); - info->plane[1].height = DIV_ROUND_UP(fb->height / 2, tile_height); - } -} - static unsigned int intel_linear_alignment(const struct drm_i915_private *dev_priv) { if (INTEL_INFO(dev_priv)->gen >= 9) @@ -2205,16 +2179,15 @@ static unsigned int intel_surf_alignment(const struct drm_i915_private *dev_priv } } -int -intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb, - unsigned int rotation) +struct i915_vma * +intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb, unsigned int rotation) { struct drm_device *dev = fb->dev; struct drm_i915_private *dev_priv = to_i915(dev); struct drm_i915_gem_object *obj = intel_fb_obj(fb); struct i915_ggtt_view view; + struct i915_vma *vma; u32 alignment; - int ret; WARN_ON(!mutex_is_locked(&dev->struct_mutex)); @@ -2239,75 +2212,112 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb, */ intel_runtime_pm_get(dev_priv); - ret = i915_gem_object_pin_to_display_plane(obj, alignment, - &view); - if (ret) - goto err_pm; - - /* Install a fence for tiled scan-out. Pre-i965 always needs a - * fence, whereas 965+ only requires a fence if using - * framebuffer compression. For simplicity, we always install - * a fence as the cost is not that onerous. - */ - if (view.type == I915_GGTT_VIEW_NORMAL) { - ret = i915_gem_object_get_fence(obj); - if (ret == -EDEADLK) { - /* - * -EDEADLK means there are no free fences - * no pending flips. - * - * This is propagated to atomic, but it uses - * -EDEADLK to force a locking recovery, so - * change the returned error to -EBUSY. - */ - ret = -EBUSY; - goto err_unpin; - } else if (ret) - goto err_unpin; + vma = i915_gem_object_pin_to_display_plane(obj, alignment, &view); + if (IS_ERR(vma)) + goto err; - i915_gem_object_pin_fence(obj); + if (i915_vma_is_map_and_fenceable(vma)) { + /* Install a fence for tiled scan-out. Pre-i965 always needs a + * fence, whereas 965+ only requires a fence if using + * framebuffer compression. For simplicity, we always, when + * possible, install a fence as the cost is not that onerous. + * + * If we fail to fence the tiled scanout, then either the + * modeset will reject the change (which is highly unlikely as + * the affected systems, all but one, do not have unmappable + * space) or we will not be able to enable full powersaving + * techniques (also likely not to apply due to various limits + * FBC and the like impose on the size of the buffer, which + * presumably we violated anyway with this unmappable buffer). + * Anyway, it is presumably better to stumble onwards with + * something and try to run the system in a "less than optimal" + * mode that matches the user configuration. + */ + if (i915_vma_get_fence(vma) == 0) + i915_vma_pin_fence(vma); } +err: intel_runtime_pm_put(dev_priv); - return 0; - -err_unpin: - i915_gem_object_unpin_from_display_plane(obj, &view); -err_pm: - intel_runtime_pm_put(dev_priv); - return ret; + return vma; } void intel_unpin_fb_obj(struct drm_framebuffer *fb, unsigned int rotation) { struct drm_i915_gem_object *obj = intel_fb_obj(fb); struct i915_ggtt_view view; + struct i915_vma *vma; WARN_ON(!mutex_is_locked(&obj->base.dev->struct_mutex)); intel_fill_fb_ggtt_view(&view, fb, rotation); + vma = i915_gem_object_to_ggtt(obj, &view); - if (view.type == I915_GGTT_VIEW_NORMAL) - i915_gem_object_unpin_fence(obj); + i915_vma_unpin_fence(vma); + i915_gem_object_unpin_from_display_plane(vma); +} - i915_gem_object_unpin_from_display_plane(obj, &view); +static int intel_fb_pitch(const struct drm_framebuffer *fb, int plane, + unsigned int rotation) +{ + if (intel_rotation_90_or_270(rotation)) + return to_intel_framebuffer(fb)->rotated[plane].pitch; + else + return fb->pitches[plane]; +} + +/* + * Convert the x/y offsets into a linear offset. + * Only valid with 0/180 degree rotation, which is fine since linear + * offset is only used with linear buffers on pre-hsw and tiled buffers + * with gen2/3, and 90/270 degree rotations isn't supported on any of them. + */ +u32 intel_fb_xy_to_linear(int x, int y, + const struct intel_plane_state *state, + int plane) +{ + const struct drm_framebuffer *fb = state->base.fb; + unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane); + unsigned int pitch = fb->pitches[plane]; + + return y * pitch + x * cpp; +} + +/* + * Add the x/y offsets derived from fb->offsets[] to the user + * specified plane src x/y offsets. The resulting x/y offsets + * specify the start of scanout from the beginning of the gtt mapping. + */ +void intel_add_fb_offsets(int *x, int *y, + const struct intel_plane_state *state, + int plane) + +{ + const struct intel_framebuffer *intel_fb = to_intel_framebuffer(state->base.fb); + unsigned int rotation = state->base.rotation; + + if (intel_rotation_90_or_270(rotation)) { + *x += intel_fb->rotated[plane].x; + *y += intel_fb->rotated[plane].y; + } else { + *x += intel_fb->normal[plane].x; + *y += intel_fb->normal[plane].y; + } } /* - * Adjust the tile offset by moving the difference into - * the x/y offsets. - * * Input tile dimensions and pitch must already be * rotated to match x and y, and in pixel units. */ -static u32 intel_adjust_tile_offset(int *x, int *y, - unsigned int tile_width, - unsigned int tile_height, - unsigned int tile_size, - unsigned int pitch_tiles, - u32 old_offset, - u32 new_offset) -{ +static u32 _intel_adjust_tile_offset(int *x, int *y, + unsigned int tile_width, + unsigned int tile_height, + unsigned int tile_size, + unsigned int pitch_tiles, + u32 old_offset, + u32 new_offset) +{ + unsigned int pitch_pixels = pitch_tiles * tile_width; unsigned int tiles; WARN_ON(old_offset & (tile_size - 1)); @@ -2319,6 +2329,54 @@ static u32 intel_adjust_tile_offset(int *x, int *y, *y += tiles / pitch_tiles * tile_height; *x += tiles % pitch_tiles * tile_width; + /* minimize x in case it got needlessly big */ + *y += *x / pitch_pixels * tile_height; + *x %= pitch_pixels; + + return new_offset; +} + +/* + * Adjust the tile offset by moving the difference into + * the x/y offsets. + */ +static u32 intel_adjust_tile_offset(int *x, int *y, + const struct intel_plane_state *state, int plane, + u32 old_offset, u32 new_offset) +{ + const struct drm_i915_private *dev_priv = to_i915(state->base.plane->dev); + const struct drm_framebuffer *fb = state->base.fb; + unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane); + unsigned int rotation = state->base.rotation; + unsigned int pitch = intel_fb_pitch(fb, plane, rotation); + + WARN_ON(new_offset > old_offset); + + if (fb->modifier[plane] != DRM_FORMAT_MOD_NONE) { + unsigned int tile_size, tile_width, tile_height; + unsigned int pitch_tiles; + + tile_size = intel_tile_size(dev_priv); + intel_tile_dims(dev_priv, &tile_width, &tile_height, + fb->modifier[plane], cpp); + + if (intel_rotation_90_or_270(rotation)) { + pitch_tiles = pitch / tile_height; + swap(tile_width, tile_height); + } else { + pitch_tiles = pitch / (tile_width * cpp); + } + + _intel_adjust_tile_offset(x, y, tile_width, tile_height, + tile_size, pitch_tiles, + old_offset, new_offset); + } else { + old_offset += *y * pitch + *x * cpp; + + *y = (old_offset - new_offset) / pitch; + *x = ((old_offset - new_offset) - *y * pitch) / cpp; + } + return new_offset; } @@ -2329,18 +2387,24 @@ static u32 intel_adjust_tile_offset(int *x, int *y, * In the 90/270 rotated case, x and y are assumed * to be already rotated to match the rotated GTT view, and * pitch is the tile_height aligned framebuffer height. + * + * This function is used when computing the derived information + * under intel_framebuffer, so using any of that information + * here is not allowed. Anything under drm_framebuffer can be + * used. This is why the user has to pass in the pitch since it + * is specified in the rotated orientation. */ -u32 intel_compute_tile_offset(int *x, int *y, - const struct drm_framebuffer *fb, int plane, - unsigned int pitch, - unsigned int rotation) +static u32 _intel_compute_tile_offset(const struct drm_i915_private *dev_priv, + int *x, int *y, + const struct drm_framebuffer *fb, int plane, + unsigned int pitch, + unsigned int rotation, + u32 alignment) { - const struct drm_i915_private *dev_priv = to_i915(fb->dev); uint64_t fb_modifier = fb->modifier[plane]; unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane); - u32 offset, offset_aligned, alignment; + u32 offset, offset_aligned; - alignment = intel_surf_alignment(dev_priv, fb_modifier); if (alignment) alignment--; @@ -2368,9 +2432,9 @@ u32 intel_compute_tile_offset(int *x, int *y, offset = (tile_rows * pitch_tiles + tiles) * tile_size; offset_aligned = offset & ~alignment; - intel_adjust_tile_offset(x, y, tile_width, tile_height, - tile_size, pitch_tiles, - offset, offset_aligned); + _intel_adjust_tile_offset(x, y, tile_width, tile_height, + tile_size, pitch_tiles, + offset, offset_aligned); } else { offset = *y * pitch + *x * cpp; offset_aligned = offset & ~alignment; @@ -2382,6 +2446,177 @@ u32 intel_compute_tile_offset(int *x, int *y, return offset_aligned; } +u32 intel_compute_tile_offset(int *x, int *y, + const struct intel_plane_state *state, + int plane) +{ + const struct drm_i915_private *dev_priv = to_i915(state->base.plane->dev); + const struct drm_framebuffer *fb = state->base.fb; + unsigned int rotation = state->base.rotation; + int pitch = intel_fb_pitch(fb, plane, rotation); + u32 alignment; + + /* AUX_DIST needs only 4K alignment */ + if (fb->pixel_format == DRM_FORMAT_NV12 && plane == 1) + alignment = 4096; + else + alignment = intel_surf_alignment(dev_priv, fb->modifier[plane]); + + return _intel_compute_tile_offset(dev_priv, x, y, fb, plane, pitch, + rotation, alignment); +} + +/* Convert the fb->offset[] linear offset into x/y offsets */ +static void intel_fb_offset_to_xy(int *x, int *y, + const struct drm_framebuffer *fb, int plane) +{ + unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane); + unsigned int pitch = fb->pitches[plane]; + u32 linear_offset = fb->offsets[plane]; + + *y = linear_offset / pitch; + *x = linear_offset % pitch / cpp; +} + +static unsigned int intel_fb_modifier_to_tiling(uint64_t fb_modifier) +{ + switch (fb_modifier) { + case I915_FORMAT_MOD_X_TILED: + return I915_TILING_X; + case I915_FORMAT_MOD_Y_TILED: + return I915_TILING_Y; + default: + return I915_TILING_NONE; + } +} + +static int +intel_fill_fb_info(struct drm_i915_private *dev_priv, + struct drm_framebuffer *fb) +{ + struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); + struct intel_rotation_info *rot_info = &intel_fb->rot_info; + u32 gtt_offset_rotated = 0; + unsigned int max_size = 0; + uint32_t format = fb->pixel_format; + int i, num_planes = drm_format_num_planes(format); + unsigned int tile_size = intel_tile_size(dev_priv); + + for (i = 0; i < num_planes; i++) { + unsigned int width, height; + unsigned int cpp, size; + u32 offset; + int x, y; + + cpp = drm_format_plane_cpp(format, i); + width = drm_format_plane_width(fb->width, format, i); + height = drm_format_plane_height(fb->height, format, i); + + intel_fb_offset_to_xy(&x, &y, fb, i); + + /* + * The fence (if used) is aligned to the start of the object + * so having the framebuffer wrap around across the edge of the + * fenced region doesn't really work. We have no API to configure + * the fence start offset within the object (nor could we probably + * on gen2/3). So it's just easier if we just require that the + * fb layout agrees with the fence layout. We already check that the + * fb stride matches the fence stride elsewhere. + */ + if (i915_gem_object_is_tiled(intel_fb->obj) && + (x + width) * cpp > fb->pitches[i]) { + DRM_DEBUG("bad fb plane %d offset: 0x%x\n", + i, fb->offsets[i]); + return -EINVAL; + } + + /* + * First pixel of the framebuffer from + * the start of the normal gtt mapping. + */ + intel_fb->normal[i].x = x; + intel_fb->normal[i].y = y; + + offset = _intel_compute_tile_offset(dev_priv, &x, &y, + fb, 0, fb->pitches[i], + DRM_ROTATE_0, tile_size); + offset /= tile_size; + + if (fb->modifier[i] != DRM_FORMAT_MOD_NONE) { + unsigned int tile_width, tile_height; + unsigned int pitch_tiles; + struct drm_rect r; + + intel_tile_dims(dev_priv, &tile_width, &tile_height, + fb->modifier[i], cpp); + + rot_info->plane[i].offset = offset; + rot_info->plane[i].stride = DIV_ROUND_UP(fb->pitches[i], tile_width * cpp); + rot_info->plane[i].width = DIV_ROUND_UP(x + width, tile_width); + rot_info->plane[i].height = DIV_ROUND_UP(y + height, tile_height); + + intel_fb->rotated[i].pitch = + rot_info->plane[i].height * tile_height; + + /* how many tiles does this plane need */ + size = rot_info->plane[i].stride * rot_info->plane[i].height; + /* + * If the plane isn't horizontally tile aligned, + * we need one more tile. + */ + if (x != 0) + size++; + + /* rotate the x/y offsets to match the GTT view */ + r.x1 = x; + r.y1 = y; + r.x2 = x + width; + r.y2 = y + height; + drm_rect_rotate(&r, + rot_info->plane[i].width * tile_width, + rot_info->plane[i].height * tile_height, + DRM_ROTATE_270); + x = r.x1; + y = r.y1; + + /* rotate the tile dimensions to match the GTT view */ + pitch_tiles = intel_fb->rotated[i].pitch / tile_height; + swap(tile_width, tile_height); + + /* + * We only keep the x/y offsets, so push all of the + * gtt offset into the x/y offsets. + */ + _intel_adjust_tile_offset(&x, &y, tile_size, + tile_width, tile_height, pitch_tiles, + gtt_offset_rotated * tile_size, 0); + + gtt_offset_rotated += rot_info->plane[i].width * rot_info->plane[i].height; + + /* + * First pixel of the framebuffer from + * the start of the rotated gtt mapping. + */ + intel_fb->rotated[i].x = x; + intel_fb->rotated[i].y = y; + } else { + size = DIV_ROUND_UP((y + height) * fb->pitches[i] + + x * cpp, tile_size); + } + + /* how many tiles in total needed in the bo */ + max_size = max(max_size, offset + size); + } + + if (max_size * tile_size > to_intel_framebuffer(fb)->obj->base.size) { + DRM_DEBUG("fb too big for bo (need %u bytes, have %zu bytes)\n", + max_size * tile_size, to_intel_framebuffer(fb)->obj->base.size); + return -EINVAL; + } + + return 0; +} + static int i9xx_format_to_fourcc(int format) { switch (format) { @@ -2465,9 +2700,8 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc, return false; } - obj->tiling_mode = plane_config->tiling; - if (obj->tiling_mode == I915_TILING_X) - obj->stride = fb->pitches[0]; + if (plane_config->tiling == I915_TILING_X) + obj->tiling_and_stride = fb->pitches[0] | I915_TILING_X; mode_cmd.pixel_format = fb->pixel_format; mode_cmd.width = fb->width; @@ -2488,7 +2722,7 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc, return true; out_unref_obj: - drm_gem_object_unreference(&obj->base); + i915_gem_object_put(obj); mutex_unlock(&dev->struct_mutex); return false; } @@ -2552,7 +2786,7 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc, continue; obj = intel_fb_obj(fb); - if (i915_gem_obj_ggtt_offset(obj) == plane_config->base) { + if (i915_gem_object_ggtt_offset(obj, NULL) == plane_config->base) { drm_framebuffer_reference(fb); goto valid_fb; } @@ -2565,7 +2799,7 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc, * simplest solution is to just disable the primary plane now and * pretend the BIOS never had it enabled. */ - to_intel_plane_state(plane_state)->visible = false; + to_intel_plane_state(plane_state)->base.visible = false; crtc_state->plane_mask &= ~(1 << drm_plane_index(primary)); intel_pre_disable_primary_noatomic(&intel_crtc->base); intel_plane->disable_plane(primary, &intel_crtc->base); @@ -2583,24 +2817,188 @@ valid_fb: plane_state->crtc_w = fb->width; plane_state->crtc_h = fb->height; - intel_state->src.x1 = plane_state->src_x; - intel_state->src.y1 = plane_state->src_y; - intel_state->src.x2 = plane_state->src_x + plane_state->src_w; - intel_state->src.y2 = plane_state->src_y + plane_state->src_h; - intel_state->dst.x1 = plane_state->crtc_x; - intel_state->dst.y1 = plane_state->crtc_y; - intel_state->dst.x2 = plane_state->crtc_x + plane_state->crtc_w; - intel_state->dst.y2 = plane_state->crtc_y + plane_state->crtc_h; + intel_state->base.src.x1 = plane_state->src_x; + intel_state->base.src.y1 = plane_state->src_y; + intel_state->base.src.x2 = plane_state->src_x + plane_state->src_w; + intel_state->base.src.y2 = plane_state->src_y + plane_state->src_h; + intel_state->base.dst.x1 = plane_state->crtc_x; + intel_state->base.dst.y1 = plane_state->crtc_y; + intel_state->base.dst.x2 = plane_state->crtc_x + plane_state->crtc_w; + intel_state->base.dst.y2 = plane_state->crtc_y + plane_state->crtc_h; obj = intel_fb_obj(fb); - if (obj->tiling_mode != I915_TILING_NONE) + if (i915_gem_object_is_tiled(obj)) dev_priv->preserve_bios_swizzle = true; drm_framebuffer_reference(fb); primary->fb = primary->state->fb = fb; primary->crtc = primary->state->crtc = &intel_crtc->base; intel_crtc->base.state->plane_mask |= (1 << drm_plane_index(primary)); - obj->frontbuffer_bits |= to_intel_plane(primary)->frontbuffer_bit; + atomic_or(to_intel_plane(primary)->frontbuffer_bit, + &obj->frontbuffer_bits); +} + +static int skl_max_plane_width(const struct drm_framebuffer *fb, int plane, + unsigned int rotation) +{ + int cpp = drm_format_plane_cpp(fb->pixel_format, plane); + + switch (fb->modifier[plane]) { + case DRM_FORMAT_MOD_NONE: + case I915_FORMAT_MOD_X_TILED: + switch (cpp) { + case 8: + return 4096; + case 4: + case 2: + case 1: + return 8192; + default: + MISSING_CASE(cpp); + break; + } + break; + case I915_FORMAT_MOD_Y_TILED: + case I915_FORMAT_MOD_Yf_TILED: + switch (cpp) { + case 8: + return 2048; + case 4: + return 4096; + case 2: + case 1: + return 8192; + default: + MISSING_CASE(cpp); + break; + } + break; + default: + MISSING_CASE(fb->modifier[plane]); + } + + return 2048; +} + +static int skl_check_main_surface(struct intel_plane_state *plane_state) +{ + const struct drm_i915_private *dev_priv = to_i915(plane_state->base.plane->dev); + const struct drm_framebuffer *fb = plane_state->base.fb; + unsigned int rotation = plane_state->base.rotation; + int x = plane_state->base.src.x1 >> 16; + int y = plane_state->base.src.y1 >> 16; + int w = drm_rect_width(&plane_state->base.src) >> 16; + int h = drm_rect_height(&plane_state->base.src) >> 16; + int max_width = skl_max_plane_width(fb, 0, rotation); + int max_height = 4096; + u32 alignment, offset, aux_offset = plane_state->aux.offset; + + if (w > max_width || h > max_height) { + DRM_DEBUG_KMS("requested Y/RGB source size %dx%d too big (limit %dx%d)\n", + w, h, max_width, max_height); + return -EINVAL; + } + + intel_add_fb_offsets(&x, &y, plane_state, 0); + offset = intel_compute_tile_offset(&x, &y, plane_state, 0); + + alignment = intel_surf_alignment(dev_priv, fb->modifier[0]); + + /* + * AUX surface offset is specified as the distance from the + * main surface offset, and it must be non-negative. Make + * sure that is what we will get. + */ + if (offset > aux_offset) + offset = intel_adjust_tile_offset(&x, &y, plane_state, 0, + offset, aux_offset & ~(alignment - 1)); + + /* + * When using an X-tiled surface, the plane blows up + * if the x offset + width exceed the stride. + * + * TODO: linear and Y-tiled seem fine, Yf untested, + */ + if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) { + int cpp = drm_format_plane_cpp(fb->pixel_format, 0); + + while ((x + w) * cpp > fb->pitches[0]) { + if (offset == 0) { + DRM_DEBUG_KMS("Unable to find suitable display surface offset\n"); + return -EINVAL; + } + + offset = intel_adjust_tile_offset(&x, &y, plane_state, 0, + offset, offset - alignment); + } + } + + plane_state->main.offset = offset; + plane_state->main.x = x; + plane_state->main.y = y; + + return 0; +} + +static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state) +{ + const struct drm_framebuffer *fb = plane_state->base.fb; + unsigned int rotation = plane_state->base.rotation; + int max_width = skl_max_plane_width(fb, 1, rotation); + int max_height = 4096; + int x = plane_state->base.src.x1 >> 17; + int y = plane_state->base.src.y1 >> 17; + int w = drm_rect_width(&plane_state->base.src) >> 17; + int h = drm_rect_height(&plane_state->base.src) >> 17; + u32 offset; + + intel_add_fb_offsets(&x, &y, plane_state, 1); + offset = intel_compute_tile_offset(&x, &y, plane_state, 1); + + /* FIXME not quite sure how/if these apply to the chroma plane */ + if (w > max_width || h > max_height) { + DRM_DEBUG_KMS("CbCr source size %dx%d too big (limit %dx%d)\n", + w, h, max_width, max_height); + return -EINVAL; + } + + plane_state->aux.offset = offset; + plane_state->aux.x = x; + plane_state->aux.y = y; + + return 0; +} + +int skl_check_plane_surface(struct intel_plane_state *plane_state) +{ + const struct drm_framebuffer *fb = plane_state->base.fb; + unsigned int rotation = plane_state->base.rotation; + int ret; + + /* Rotate src coordinates to match rotated GTT view */ + if (intel_rotation_90_or_270(rotation)) + drm_rect_rotate(&plane_state->base.src, + fb->width, fb->height, DRM_ROTATE_270); + + /* + * Handle the AUX surface first since + * the main surface setup depends on it. + */ + if (fb->pixel_format == DRM_FORMAT_NV12) { + ret = skl_check_nv12_aux_surface(plane_state); + if (ret) + return ret; + } else { + plane_state->aux.offset = ~0xfff; + plane_state->aux.x = 0; + plane_state->aux.y = 0; + } + + ret = skl_check_main_surface(plane_state); + if (ret) + return ret; + + return 0; } static void i9xx_update_primary_plane(struct drm_plane *primary, @@ -2617,9 +3015,8 @@ static void i9xx_update_primary_plane(struct drm_plane *primary, u32 dspcntr; i915_reg_t reg = DSPCNTR(plane); unsigned int rotation = plane_state->base.rotation; - int cpp = drm_format_plane_cpp(fb->pixel_format, 0); - int x = plane_state->src.x1 >> 16; - int y = plane_state->src.y1 >> 16; + int x = plane_state->base.src.x1 >> 16; + int y = plane_state->base.src.y1 >> 16; dspcntr = DISPPLANE_GAMMA_ENABLE; @@ -2670,37 +3067,31 @@ static void i9xx_update_primary_plane(struct drm_plane *primary, BUG(); } - if (INTEL_INFO(dev)->gen >= 4 && - obj->tiling_mode != I915_TILING_NONE) + if (INTEL_GEN(dev_priv) >= 4 && + fb->modifier[0] == I915_FORMAT_MOD_X_TILED) dspcntr |= DISPPLANE_TILED; if (IS_G4X(dev)) dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; - linear_offset = y * fb->pitches[0] + x * cpp; + intel_add_fb_offsets(&x, &y, plane_state, 0); - if (INTEL_INFO(dev)->gen >= 4) { + if (INTEL_INFO(dev)->gen >= 4) intel_crtc->dspaddr_offset = - intel_compute_tile_offset(&x, &y, fb, 0, - fb->pitches[0], rotation); - linear_offset -= intel_crtc->dspaddr_offset; - } else { - intel_crtc->dspaddr_offset = linear_offset; - } + intel_compute_tile_offset(&x, &y, plane_state, 0); - if (rotation == BIT(DRM_ROTATE_180)) { + if (rotation == DRM_ROTATE_180) { dspcntr |= DISPPLANE_ROTATE_180; x += (crtc_state->pipe_src_w - 1); y += (crtc_state->pipe_src_h - 1); - - /* Finding the last pixel of the last line of the display - data and adding to linear_offset*/ - linear_offset += - (crtc_state->pipe_src_h - 1) * fb->pitches[0] + - (crtc_state->pipe_src_w - 1) * cpp; } + linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0); + + if (INTEL_INFO(dev)->gen < 4) + intel_crtc->dspaddr_offset = linear_offset; + intel_crtc->adjusted_x = x; intel_crtc->adjusted_y = y; @@ -2709,11 +3100,12 @@ static void i9xx_update_primary_plane(struct drm_plane *primary, I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]); if (INTEL_INFO(dev)->gen >= 4) { I915_WRITE(DSPSURF(plane), - i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset); + intel_fb_gtt_offset(fb, rotation) + + intel_crtc->dspaddr_offset); I915_WRITE(DSPTILEOFF(plane), (y << 16) | x); I915_WRITE(DSPLINOFF(plane), linear_offset); } else - I915_WRITE(DSPADDR(plane), i915_gem_obj_ggtt_offset(obj) + linear_offset); + I915_WRITE(DSPADDR(plane), i915_gem_object_ggtt_offset(obj, NULL) + linear_offset); POSTING_READ(reg); } @@ -2741,15 +3133,13 @@ static void ironlake_update_primary_plane(struct drm_plane *primary, struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc); struct drm_framebuffer *fb = plane_state->base.fb; - struct drm_i915_gem_object *obj = intel_fb_obj(fb); int plane = intel_crtc->plane; u32 linear_offset; u32 dspcntr; i915_reg_t reg = DSPCNTR(plane); unsigned int rotation = plane_state->base.rotation; - int cpp = drm_format_plane_cpp(fb->pixel_format, 0); - int x = plane_state->src.x1 >> 16; - int y = plane_state->src.y1 >> 16; + int x = plane_state->base.src.x1 >> 16; + int y = plane_state->base.src.y1 >> 16; dspcntr = DISPPLANE_GAMMA_ENABLE; dspcntr |= DISPLAY_PLANE_ENABLE; @@ -2780,32 +3170,28 @@ static void ironlake_update_primary_plane(struct drm_plane *primary, BUG(); } - if (obj->tiling_mode != I915_TILING_NONE) + if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) dspcntr |= DISPPLANE_TILED; if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; - linear_offset = y * fb->pitches[0] + x * cpp; + intel_add_fb_offsets(&x, &y, plane_state, 0); + intel_crtc->dspaddr_offset = - intel_compute_tile_offset(&x, &y, fb, 0, - fb->pitches[0], rotation); - linear_offset -= intel_crtc->dspaddr_offset; - if (rotation == BIT(DRM_ROTATE_180)) { + intel_compute_tile_offset(&x, &y, plane_state, 0); + + if (rotation == DRM_ROTATE_180) { dspcntr |= DISPPLANE_ROTATE_180; if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) { x += (crtc_state->pipe_src_w - 1); y += (crtc_state->pipe_src_h - 1); - - /* Finding the last pixel of the last line of the display - data and adding to linear_offset*/ - linear_offset += - (crtc_state->pipe_src_h - 1) * fb->pitches[0] + - (crtc_state->pipe_src_w - 1) * cpp; } } + linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0); + intel_crtc->adjusted_x = x; intel_crtc->adjusted_y = y; @@ -2813,7 +3199,8 @@ static void ironlake_update_primary_plane(struct drm_plane *primary, I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]); I915_WRITE(DSPSURF(plane), - i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset); + intel_fb_gtt_offset(fb, rotation) + + intel_crtc->dspaddr_offset); if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { I915_WRITE(DSPOFFSET(plane), (y << 16) | x); } else { @@ -2835,32 +3222,21 @@ u32 intel_fb_stride_alignment(const struct drm_i915_private *dev_priv, } } -u32 intel_plane_obj_offset(struct intel_plane *intel_plane, - struct drm_i915_gem_object *obj, - unsigned int plane) +u32 intel_fb_gtt_offset(struct drm_framebuffer *fb, + unsigned int rotation) { + struct drm_i915_gem_object *obj = intel_fb_obj(fb); struct i915_ggtt_view view; struct i915_vma *vma; - u64 offset; - intel_fill_fb_ggtt_view(&view, intel_plane->base.state->fb, - intel_plane->base.state->rotation); + intel_fill_fb_ggtt_view(&view, fb, rotation); - vma = i915_gem_obj_to_ggtt_view(obj, &view); + vma = i915_gem_object_to_ggtt(obj, &view); if (WARN(!vma, "ggtt vma for display object not found! (view=%u)\n", - view.type)) + view.type)) return -1; - offset = vma->node.start; - - if (plane == 1) { - offset += vma->ggtt_view.params.rotated.uv_start_page * - PAGE_SIZE; - } - - WARN_ON(upper_32_bits(offset)); - - return lower_32_bits(offset); + return i915_ggtt_offset(vma); } static void skl_detach_scaler(struct intel_crtc *intel_crtc, int id) @@ -2890,6 +3266,28 @@ static void skl_detach_scalers(struct intel_crtc *intel_crtc) } } +u32 skl_plane_stride(const struct drm_framebuffer *fb, int plane, + unsigned int rotation) +{ + const struct drm_i915_private *dev_priv = to_i915(fb->dev); + u32 stride = intel_fb_pitch(fb, plane, rotation); + + /* + * The stride is either expressed as a multiple of 64 bytes chunks for + * linear buffers or in number of tiles for tiled buffers. + */ + if (intel_rotation_90_or_270(rotation)) { + int cpp = drm_format_plane_cpp(fb->pixel_format, plane); + + stride /= intel_tile_height(dev_priv, fb->modifier[0], cpp); + } else { + stride /= intel_fb_stride_alignment(dev_priv, fb->modifier[0], + fb->pixel_format); + } + + return stride; +} + u32 skl_plane_ctl_format(uint32_t pixel_format) { switch (pixel_format) { @@ -2952,17 +3350,17 @@ u32 skl_plane_ctl_tiling(uint64_t fb_modifier) u32 skl_plane_ctl_rotation(unsigned int rotation) { switch (rotation) { - case BIT(DRM_ROTATE_0): + case DRM_ROTATE_0: break; /* * DRM_ROTATE_ is counter clockwise to stay compatible with Xrandr * while i915 HW rotation is clockwise, thats why this swapping. */ - case BIT(DRM_ROTATE_90): + case DRM_ROTATE_90: return PLANE_CTL_ROTATE_270; - case BIT(DRM_ROTATE_180): + case DRM_ROTATE_180: return PLANE_CTL_ROTATE_180; - case BIT(DRM_ROTATE_270): + case DRM_ROTATE_270: return PLANE_CTL_ROTATE_90; default: MISSING_CASE(rotation); @@ -2979,22 +3377,21 @@ static void skylake_update_primary_plane(struct drm_plane *plane, struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc); struct drm_framebuffer *fb = plane_state->base.fb; - struct drm_i915_gem_object *obj = intel_fb_obj(fb); + const struct skl_wm_values *wm = &dev_priv->wm.skl_results; int pipe = intel_crtc->pipe; - u32 plane_ctl, stride_div, stride; - u32 tile_height, plane_offset, plane_size; + u32 plane_ctl; unsigned int rotation = plane_state->base.rotation; - int x_offset, y_offset; - u32 surf_addr; + u32 stride = skl_plane_stride(fb, 0, rotation); + u32 surf_addr = plane_state->main.offset; int scaler_id = plane_state->scaler_id; - int src_x = plane_state->src.x1 >> 16; - int src_y = plane_state->src.y1 >> 16; - int src_w = drm_rect_width(&plane_state->src) >> 16; - int src_h = drm_rect_height(&plane_state->src) >> 16; - int dst_x = plane_state->dst.x1; - int dst_y = plane_state->dst.y1; - int dst_w = drm_rect_width(&plane_state->dst); - int dst_h = drm_rect_height(&plane_state->dst); + int src_x = plane_state->main.x; + int src_y = plane_state->main.y; + int src_w = drm_rect_width(&plane_state->base.src) >> 16; + int src_h = drm_rect_height(&plane_state->base.src) >> 16; + int dst_x = plane_state->base.dst.x1; + int dst_y = plane_state->base.dst.y1; + int dst_w = drm_rect_width(&plane_state->base.dst); + int dst_h = drm_rect_height(&plane_state->base.dst); plane_ctl = PLANE_CTL_ENABLE | PLANE_CTL_PIPE_GAMMA_ENABLE | @@ -3005,36 +3402,22 @@ static void skylake_update_primary_plane(struct drm_plane *plane, plane_ctl |= PLANE_CTL_PLANE_GAMMA_DISABLE; plane_ctl |= skl_plane_ctl_rotation(rotation); - stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0], - fb->pixel_format); - surf_addr = intel_plane_obj_offset(to_intel_plane(plane), obj, 0); - - WARN_ON(drm_rect_width(&plane_state->src) == 0); + /* Sizes are 0 based */ + src_w--; + src_h--; + dst_w--; + dst_h--; - if (intel_rotation_90_or_270(rotation)) { - int cpp = drm_format_plane_cpp(fb->pixel_format, 0); - - /* stride = Surface height in tiles */ - tile_height = intel_tile_height(dev_priv, fb->modifier[0], cpp); - stride = DIV_ROUND_UP(fb->height, tile_height); - x_offset = stride * tile_height - src_y - src_h; - y_offset = src_x; - plane_size = (src_w - 1) << 16 | (src_h - 1); - } else { - stride = fb->pitches[0] / stride_div; - x_offset = src_x; - y_offset = src_y; - plane_size = (src_h - 1) << 16 | (src_w - 1); - } - plane_offset = y_offset << 16 | x_offset; + intel_crtc->adjusted_x = src_x; + intel_crtc->adjusted_y = src_y; - intel_crtc->adjusted_x = x_offset; - intel_crtc->adjusted_y = y_offset; + if (wm->dirty_pipes & drm_crtc_mask(&intel_crtc->base)) + skl_write_plane_wm(intel_crtc, wm, 0); I915_WRITE(PLANE_CTL(pipe, 0), plane_ctl); - I915_WRITE(PLANE_OFFSET(pipe, 0), plane_offset); - I915_WRITE(PLANE_SIZE(pipe, 0), plane_size); + I915_WRITE(PLANE_OFFSET(pipe, 0), (src_y << 16) | src_x); I915_WRITE(PLANE_STRIDE(pipe, 0), stride); + I915_WRITE(PLANE_SIZE(pipe, 0), (src_h << 16) | src_w); if (scaler_id >= 0) { uint32_t ps_ctrl = 0; @@ -3051,7 +3434,8 @@ static void skylake_update_primary_plane(struct drm_plane *plane, I915_WRITE(PLANE_POS(pipe, 0), (dst_y << 16) | dst_x); } - I915_WRITE(PLANE_SURF(pipe, 0), surf_addr); + I915_WRITE(PLANE_SURF(pipe, 0), + intel_fb_gtt_offset(fb, rotation) + surf_addr); POSTING_READ(PLANE_SURF(pipe, 0)); } @@ -3061,7 +3445,15 @@ static void skylake_disable_primary_plane(struct drm_plane *primary, { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = to_i915(dev); - int pipe = to_intel_crtc(crtc)->pipe; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + + /* + * We only populate skl_results on watermark updates, and if the + * plane's visiblity isn't actually changing neither is its watermarks. + */ + if (!crtc->primary->state->visible) + skl_write_plane_wm(intel_crtc, &dev_priv->wm.skl_results, 0); I915_WRITE(PLANE_CTL(pipe, 0), 0); I915_WRITE(PLANE_SURF(pipe, 0), 0); @@ -3093,40 +3485,113 @@ static void intel_update_primary_planes(struct drm_device *dev) for_each_crtc(dev, crtc) { struct intel_plane *plane = to_intel_plane(crtc->primary); - struct intel_plane_state *plane_state; - - drm_modeset_lock_crtc(crtc, &plane->base); - plane_state = to_intel_plane_state(plane->base.state); + struct intel_plane_state *plane_state = + to_intel_plane_state(plane->base.state); - if (plane_state->visible) + if (plane_state->base.visible) plane->update_plane(&plane->base, to_intel_crtc_state(crtc->state), plane_state); + } +} - drm_modeset_unlock_crtc(crtc); +static int +__intel_display_resume(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + int i, ret; + + intel_modeset_setup_hw_state(dev); + i915_redisable_vga(dev); + + if (!state) + return 0; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + /* + * Force recalculation even if we restore + * current state. With fast modeset this may not result + * in a modeset when the state is compatible. + */ + crtc_state->mode_changed = true; } + + /* ignore any reset values/BIOS leftovers in the WM registers */ + to_intel_atomic_state(state)->skip_intermediate_wm = true; + + ret = drm_atomic_commit(state); + + WARN_ON(ret == -EDEADLK); + return ret; +} + +static bool gpu_reset_clobbers_display(struct drm_i915_private *dev_priv) +{ + return intel_has_gpu_reset(dev_priv) && + INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv); } void intel_prepare_reset(struct drm_i915_private *dev_priv) { - /* no reset support for gen2 */ - if (IS_GEN2(dev_priv)) - return; + struct drm_device *dev = &dev_priv->drm; + struct drm_modeset_acquire_ctx *ctx = &dev_priv->reset_ctx; + struct drm_atomic_state *state; + int ret; - /* reset doesn't touch the display */ - if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv)) + /* + * Need mode_config.mutex so that we don't + * trample ongoing ->detect() and whatnot. + */ + mutex_lock(&dev->mode_config.mutex); + drm_modeset_acquire_init(ctx, 0); + while (1) { + ret = drm_modeset_lock_all_ctx(dev, ctx); + if (ret != -EDEADLK) + break; + + drm_modeset_backoff(ctx); + } + + /* reset doesn't touch the display, but flips might get nuked anyway, */ + if (!i915.force_reset_modeset_test && + !gpu_reset_clobbers_display(dev_priv)) return; - drm_modeset_lock_all(&dev_priv->drm); /* * Disabling the crtcs gracefully seems nicer. Also the * g33 docs say we should at least disable all the planes. */ - intel_display_suspend(&dev_priv->drm); + state = drm_atomic_helper_duplicate_state(dev, ctx); + if (IS_ERR(state)) { + ret = PTR_ERR(state); + state = NULL; + DRM_ERROR("Duplicating state failed with %i\n", ret); + goto err; + } + + ret = drm_atomic_helper_disable_all(dev, ctx); + if (ret) { + DRM_ERROR("Suspending crtc's failed with %i\n", ret); + goto err; + } + + dev_priv->modeset_restore_state = state; + state->acquire_ctx = ctx; + return; + +err: + drm_atomic_state_free(state); } void intel_finish_reset(struct drm_i915_private *dev_priv) { + struct drm_device *dev = &dev_priv->drm; + struct drm_modeset_acquire_ctx *ctx = &dev_priv->reset_ctx; + struct drm_atomic_state *state = dev_priv->modeset_restore_state; + int ret; + /* * Flips in the rings will be nuked by the reset, * so complete all pending flips so that user space @@ -3134,55 +3599,75 @@ void intel_finish_reset(struct drm_i915_private *dev_priv) */ intel_complete_page_flips(dev_priv); - /* no reset support for gen2 */ - if (IS_GEN2(dev_priv)) - return; + dev_priv->modeset_restore_state = NULL; + + dev_priv->modeset_restore_state = NULL; /* reset doesn't touch the display */ - if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv)) { + if (!gpu_reset_clobbers_display(dev_priv)) { + if (!state) { + /* + * Flips in the rings have been nuked by the reset, + * so update the base address of all primary + * planes to the the last fb to make sure we're + * showing the correct fb after a reset. + * + * FIXME: Atomic will make this obsolete since we won't schedule + * CS-based flips (which might get lost in gpu resets) any more. + */ + intel_update_primary_planes(dev); + } else { + ret = __intel_display_resume(dev, state); + if (ret) + DRM_ERROR("Restoring old state failed with %i\n", ret); + } + } else { /* - * Flips in the rings have been nuked by the reset, - * so update the base address of all primary - * planes to the the last fb to make sure we're - * showing the correct fb after a reset. - * - * FIXME: Atomic will make this obsolete since we won't schedule - * CS-based flips (which might get lost in gpu resets) any more. + * The display has been reset as well, + * so need a full re-initialization. */ - intel_update_primary_planes(&dev_priv->drm); - return; - } + intel_runtime_pm_disable_interrupts(dev_priv); + intel_runtime_pm_enable_interrupts(dev_priv); - /* - * The display has been reset as well, - * so need a full re-initialization. - */ - intel_runtime_pm_disable_interrupts(dev_priv); - intel_runtime_pm_enable_interrupts(dev_priv); + intel_modeset_init_hw(dev); - intel_modeset_init_hw(&dev_priv->drm); + spin_lock_irq(&dev_priv->irq_lock); + if (dev_priv->display.hpd_irq_setup) + dev_priv->display.hpd_irq_setup(dev_priv); + spin_unlock_irq(&dev_priv->irq_lock); - spin_lock_irq(&dev_priv->irq_lock); - if (dev_priv->display.hpd_irq_setup) - dev_priv->display.hpd_irq_setup(dev_priv); - spin_unlock_irq(&dev_priv->irq_lock); + ret = __intel_display_resume(dev, state); + if (ret) + DRM_ERROR("Restoring old state failed with %i\n", ret); + + intel_hpd_init(dev_priv); + } + + drm_modeset_drop_locks(ctx); + drm_modeset_acquire_fini(ctx); + mutex_unlock(&dev->mode_config.mutex); +} - intel_display_resume(&dev_priv->drm); +static bool abort_flip_on_reset(struct intel_crtc *crtc) +{ + struct i915_gpu_error *error = &to_i915(crtc->base.dev)->gpu_error; + + if (i915_reset_in_progress(error)) + return true; - intel_hpd_init(dev_priv); + if (crtc->reset_count != i915_reset_count(error)) + return true; - drm_modeset_unlock_all(&dev_priv->drm); + return false; } static bool intel_crtc_has_pending_flip(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - unsigned reset_counter; bool pending; - reset_counter = i915_reset_counter(&to_i915(dev)->gpu_error); - if (intel_crtc->reset_counter != reset_counter) + if (abort_flip_on_reset(intel_crtc)) return false; spin_lock_irq(&dev->event_lock); @@ -3825,7 +4310,7 @@ static int intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) return 0; } -static void lpt_disable_iclkip(struct drm_i915_private *dev_priv) +void lpt_disable_iclkip(struct drm_i915_private *dev_priv) { u32 temp; @@ -4248,7 +4733,7 @@ int skl_update_scaler_crtc(struct intel_crtc_state *state) intel_crtc->pipe, SKL_CRTC_INDEX); return skl_update_scaler(state, !state->base.active, SKL_CRTC_INDEX, - &state->scaler_state.scaler_id, BIT(DRM_ROTATE_0), + &state->scaler_state.scaler_id, DRM_ROTATE_0, state->pipe_src_w, state->pipe_src_h, adjusted_mode->crtc_hdisplay, adjusted_mode->crtc_vdisplay); } @@ -4273,7 +4758,7 @@ static int skl_update_scaler_plane(struct intel_crtc_state *crtc_state, struct drm_framebuffer *fb = plane_state->base.fb; int ret; - bool force_detach = !fb || !plane_state->visible; + bool force_detach = !fb || !plane_state->base.visible; DRM_DEBUG_KMS("Updating scaler for [PLANE:%d:%s] scaler_user index %u.%u\n", intel_plane->base.base.id, intel_plane->base.name, @@ -4283,10 +4768,10 @@ static int skl_update_scaler_plane(struct intel_crtc_state *crtc_state, drm_plane_index(&intel_plane->base), &plane_state->scaler_id, plane_state->base.rotation, - drm_rect_width(&plane_state->src) >> 16, - drm_rect_height(&plane_state->src) >> 16, - drm_rect_width(&plane_state->dst), - drm_rect_height(&plane_state->dst)); + drm_rect_width(&plane_state->base.src) >> 16, + drm_rect_height(&plane_state->base.src) >> 16, + drm_rect_width(&plane_state->base.dst), + drm_rect_height(&plane_state->base.dst)); if (ret || plane_state->scaler_id < 0) return ret; @@ -4564,12 +5049,11 @@ static void intel_post_plane_update(struct intel_crtc_state *old_crtc_state) struct drm_atomic_state *old_state = old_crtc_state->base.state; struct intel_crtc_state *pipe_config = to_intel_crtc_state(crtc->base.state); - struct drm_device *dev = crtc->base.dev; struct drm_plane *primary = crtc->base.primary; struct drm_plane_state *old_pri_state = drm_atomic_get_existing_plane_state(old_state, primary); - intel_frontbuffer_flip(dev, pipe_config->fb_bits); + intel_frontbuffer_flip(to_i915(crtc->base.dev), pipe_config->fb_bits); crtc->wm.cxsr_allowed = true; @@ -4584,9 +5068,9 @@ static void intel_post_plane_update(struct intel_crtc_state *old_crtc_state) intel_fbc_post_update(crtc); - if (primary_state->visible && + if (primary_state->base.visible && (needs_modeset(&pipe_config->base) || - !old_primary_state->visible)) + !old_primary_state->base.visible)) intel_post_enable_primary(&crtc->base); } } @@ -4612,8 +5096,8 @@ static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state) intel_fbc_pre_update(crtc, pipe_config, primary_state); - if (old_primary_state->visible && - (modeset || !primary_state->visible)) + if (old_primary_state->base.visible && + (modeset || !primary_state->base.visible)) intel_pre_disable_primary(&crtc->base); } @@ -4692,18 +5176,140 @@ static void intel_crtc_disable_planes(struct drm_crtc *crtc, unsigned plane_mask * to compute the mask of flip planes precisely. For the time being * consider this a flip to a NULL plane. */ - intel_frontbuffer_flip(dev, INTEL_FRONTBUFFER_ALL_MASK(pipe)); + intel_frontbuffer_flip(to_i915(dev), INTEL_FRONTBUFFER_ALL_MASK(pipe)); +} + +static void intel_encoders_pre_pll_enable(struct drm_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct drm_atomic_state *old_state) +{ + struct drm_connector_state *old_conn_state; + struct drm_connector *conn; + int i; + + for_each_connector_in_state(old_state, conn, old_conn_state, i) { + struct drm_connector_state *conn_state = conn->state; + struct intel_encoder *encoder = + to_intel_encoder(conn_state->best_encoder); + + if (conn_state->crtc != crtc) + continue; + + if (encoder->pre_pll_enable) + encoder->pre_pll_enable(encoder, crtc_state, conn_state); + } +} + +static void intel_encoders_pre_enable(struct drm_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct drm_atomic_state *old_state) +{ + struct drm_connector_state *old_conn_state; + struct drm_connector *conn; + int i; + + for_each_connector_in_state(old_state, conn, old_conn_state, i) { + struct drm_connector_state *conn_state = conn->state; + struct intel_encoder *encoder = + to_intel_encoder(conn_state->best_encoder); + + if (conn_state->crtc != crtc) + continue; + + if (encoder->pre_enable) + encoder->pre_enable(encoder, crtc_state, conn_state); + } } -static void ironlake_crtc_enable(struct drm_crtc *crtc) +static void intel_encoders_enable(struct drm_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct drm_atomic_state *old_state) { + struct drm_connector_state *old_conn_state; + struct drm_connector *conn; + int i; + + for_each_connector_in_state(old_state, conn, old_conn_state, i) { + struct drm_connector_state *conn_state = conn->state; + struct intel_encoder *encoder = + to_intel_encoder(conn_state->best_encoder); + + if (conn_state->crtc != crtc) + continue; + + encoder->enable(encoder, crtc_state, conn_state); + intel_opregion_notify_encoder(encoder, true); + } +} + +static void intel_encoders_disable(struct drm_crtc *crtc, + struct intel_crtc_state *old_crtc_state, + struct drm_atomic_state *old_state) +{ + struct drm_connector_state *old_conn_state; + struct drm_connector *conn; + int i; + + for_each_connector_in_state(old_state, conn, old_conn_state, i) { + struct intel_encoder *encoder = + to_intel_encoder(old_conn_state->best_encoder); + + if (old_conn_state->crtc != crtc) + continue; + + intel_opregion_notify_encoder(encoder, false); + encoder->disable(encoder, old_crtc_state, old_conn_state); + } +} + +static void intel_encoders_post_disable(struct drm_crtc *crtc, + struct intel_crtc_state *old_crtc_state, + struct drm_atomic_state *old_state) +{ + struct drm_connector_state *old_conn_state; + struct drm_connector *conn; + int i; + + for_each_connector_in_state(old_state, conn, old_conn_state, i) { + struct intel_encoder *encoder = + to_intel_encoder(old_conn_state->best_encoder); + + if (old_conn_state->crtc != crtc) + continue; + + if (encoder->post_disable) + encoder->post_disable(encoder, old_crtc_state, old_conn_state); + } +} + +static void intel_encoders_post_pll_disable(struct drm_crtc *crtc, + struct intel_crtc_state *old_crtc_state, + struct drm_atomic_state *old_state) +{ + struct drm_connector_state *old_conn_state; + struct drm_connector *conn; + int i; + + for_each_connector_in_state(old_state, conn, old_conn_state, i) { + struct intel_encoder *encoder = + to_intel_encoder(old_conn_state->best_encoder); + + if (old_conn_state->crtc != crtc) + continue; + + if (encoder->post_pll_disable) + encoder->post_pll_disable(encoder, old_crtc_state, old_conn_state); + } +} + +static void ironlake_crtc_enable(struct intel_crtc_state *pipe_config, + struct drm_atomic_state *old_state) +{ + struct drm_crtc *crtc = pipe_config->base.crtc; struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_encoder *encoder; int pipe = intel_crtc->pipe; - struct intel_crtc_state *pipe_config = - to_intel_crtc_state(crtc->state); if (WARN_ON(intel_crtc->active)) return; @@ -4741,9 +5347,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) intel_crtc->active = true; - for_each_encoder_on_crtc(dev, crtc, encoder) - if (encoder->pre_enable) - encoder->pre_enable(encoder); + intel_encoders_pre_enable(crtc, pipe_config, old_state); if (intel_crtc->config->has_pch_encoder) { /* Note: FDI PLL enabling _must_ be done before we enable the @@ -4773,8 +5377,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) assert_vblank_disabled(crtc); drm_crtc_vblank_on(crtc); - for_each_encoder_on_crtc(dev, crtc, encoder) - encoder->enable(encoder); + intel_encoders_enable(crtc, pipe_config, old_state); if (HAS_PCH_CPT(dev)) cpt_verify_modeset(dev, intel_crtc->pipe); @@ -4792,16 +5395,15 @@ static bool hsw_crtc_supports_ips(struct intel_crtc *crtc) return HAS_IPS(crtc->base.dev) && crtc->pipe == PIPE_A; } -static void haswell_crtc_enable(struct drm_crtc *crtc) +static void haswell_crtc_enable(struct intel_crtc_state *pipe_config, + struct drm_atomic_state *old_state) { + struct drm_crtc *crtc = pipe_config->base.crtc; struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_encoder *encoder; int pipe = intel_crtc->pipe, hsw_workaround_pipe; enum transcoder cpu_transcoder = intel_crtc->config->cpu_transcoder; - struct intel_crtc_state *pipe_config = - to_intel_crtc_state(crtc->state); if (WARN_ON(intel_crtc->active)) return; @@ -4810,9 +5412,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_set_pch_fifo_underrun_reporting(dev_priv, TRANSCODER_A, false); - for_each_encoder_on_crtc(dev, crtc, encoder) - if (encoder->pre_pll_enable) - encoder->pre_pll_enable(encoder); + intel_encoders_pre_pll_enable(crtc, pipe_config, old_state); if (intel_crtc->config->shared_dpll) intel_enable_shared_dpll(intel_crtc); @@ -4850,10 +5450,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) else intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true); - for_each_encoder_on_crtc(dev, crtc, encoder) { - if (encoder->pre_enable) - encoder->pre_enable(encoder); - } + intel_encoders_pre_enable(crtc, pipe_config, old_state); if (intel_crtc->config->has_pch_encoder) dev_priv->display.fdi_link_train(crtc); @@ -4894,10 +5491,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) assert_vblank_disabled(crtc); drm_crtc_vblank_on(crtc); - for_each_encoder_on_crtc(dev, crtc, encoder) { - encoder->enable(encoder); - intel_opregion_notify_encoder(encoder, true); - } + intel_encoders_enable(crtc, pipe_config, old_state); if (intel_crtc->config->has_pch_encoder) { intel_wait_for_vblank(dev, pipe); @@ -4931,12 +5525,13 @@ static void ironlake_pfit_disable(struct intel_crtc *crtc, bool force) } } -static void ironlake_crtc_disable(struct drm_crtc *crtc) +static void ironlake_crtc_disable(struct intel_crtc_state *old_crtc_state, + struct drm_atomic_state *old_state) { + struct drm_crtc *crtc = old_crtc_state->base.crtc; struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_encoder *encoder; int pipe = intel_crtc->pipe; /* @@ -4949,8 +5544,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) intel_set_pch_fifo_underrun_reporting(dev_priv, pipe, false); } - for_each_encoder_on_crtc(dev, crtc, encoder) - encoder->disable(encoder); + intel_encoders_disable(crtc, old_crtc_state, old_state); drm_crtc_vblank_off(crtc); assert_vblank_disabled(crtc); @@ -4962,9 +5556,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) if (intel_crtc->config->has_pch_encoder) ironlake_fdi_disable(crtc); - for_each_encoder_on_crtc(dev, crtc, encoder) - if (encoder->post_disable) - encoder->post_disable(encoder); + intel_encoders_post_disable(crtc, old_crtc_state, old_state); if (intel_crtc->config->has_pch_encoder) { ironlake_disable_pch_transcoder(dev_priv, pipe); @@ -4994,22 +5586,20 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) intel_set_pch_fifo_underrun_reporting(dev_priv, pipe, true); } -static void haswell_crtc_disable(struct drm_crtc *crtc) +static void haswell_crtc_disable(struct intel_crtc_state *old_crtc_state, + struct drm_atomic_state *old_state) { + struct drm_crtc *crtc = old_crtc_state->base.crtc; struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_encoder *encoder; enum transcoder cpu_transcoder = intel_crtc->config->cpu_transcoder; if (intel_crtc->config->has_pch_encoder) intel_set_pch_fifo_underrun_reporting(dev_priv, TRANSCODER_A, false); - for_each_encoder_on_crtc(dev, crtc, encoder) { - intel_opregion_notify_encoder(encoder, false); - encoder->disable(encoder); - } + intel_encoders_disable(crtc, old_crtc_state, old_state); drm_crtc_vblank_off(crtc); assert_vblank_disabled(crtc); @@ -5032,18 +5622,11 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) if (!transcoder_is_dsi(cpu_transcoder)) intel_ddi_disable_pipe_clock(intel_crtc); - for_each_encoder_on_crtc(dev, crtc, encoder) - if (encoder->post_disable) - encoder->post_disable(encoder); - - if (intel_crtc->config->has_pch_encoder) { - lpt_disable_pch_transcoder(dev_priv); - lpt_disable_iclkip(dev_priv); - intel_ddi_fdi_disable(crtc); + intel_encoders_post_disable(crtc, old_crtc_state, old_state); + if (old_crtc_state->has_pch_encoder) intel_set_pch_fifo_underrun_reporting(dev_priv, TRANSCODER_A, true); - } } static void i9xx_pfit_enable(struct intel_crtc *crtc) @@ -5691,15 +6274,7 @@ static bool skl_cdclk_pcu_ready(struct drm_i915_private *dev_priv) static bool skl_cdclk_wait_for_pcu_ready(struct drm_i915_private *dev_priv) { - unsigned int i; - - for (i = 0; i < 15; i++) { - if (skl_cdclk_pcu_ready(dev_priv)) - return true; - udelay(10); - } - - return false; + return _wait_for(skl_cdclk_pcu_ready(dev_priv), 3000, 10) == 0; } static void skl_set_cdclk(struct drm_i915_private *dev_priv, int cdclk, int vco) @@ -6107,14 +6682,13 @@ static void valleyview_modeset_commit_cdclk(struct drm_atomic_state *old_state) intel_display_power_put(dev_priv, POWER_DOMAIN_PIPE_A); } -static void valleyview_crtc_enable(struct drm_crtc *crtc) +static void valleyview_crtc_enable(struct intel_crtc_state *pipe_config, + struct drm_atomic_state *old_state) { + struct drm_crtc *crtc = pipe_config->base.crtc; struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_encoder *encoder; - struct intel_crtc_state *pipe_config = - to_intel_crtc_state(crtc->state); int pipe = intel_crtc->pipe; if (WARN_ON(intel_crtc->active)) @@ -6139,9 +6713,7 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true); - for_each_encoder_on_crtc(dev, crtc, encoder) - if (encoder->pre_pll_enable) - encoder->pre_pll_enable(encoder); + intel_encoders_pre_pll_enable(crtc, pipe_config, old_state); if (IS_CHERRYVIEW(dev)) { chv_prepare_pll(intel_crtc, intel_crtc->config); @@ -6151,9 +6723,7 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) vlv_enable_pll(intel_crtc, intel_crtc->config); } - for_each_encoder_on_crtc(dev, crtc, encoder) - if (encoder->pre_enable) - encoder->pre_enable(encoder); + intel_encoders_pre_enable(crtc, pipe_config, old_state); i9xx_pfit_enable(intel_crtc); @@ -6165,8 +6735,7 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) assert_vblank_disabled(crtc); drm_crtc_vblank_on(crtc); - for_each_encoder_on_crtc(dev, crtc, encoder) - encoder->enable(encoder); + intel_encoders_enable(crtc, pipe_config, old_state); } static void i9xx_set_pll_dividers(struct intel_crtc *crtc) @@ -6178,14 +6747,13 @@ static void i9xx_set_pll_dividers(struct intel_crtc *crtc) I915_WRITE(FP1(crtc->pipe), crtc->config->dpll_hw_state.fp1); } -static void i9xx_crtc_enable(struct drm_crtc *crtc) +static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config, + struct drm_atomic_state *old_state) { + struct drm_crtc *crtc = pipe_config->base.crtc; struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_encoder *encoder; - struct intel_crtc_state *pipe_config = - to_intel_crtc_state(crtc->state); enum pipe pipe = intel_crtc->pipe; if (WARN_ON(intel_crtc->active)) @@ -6206,9 +6774,7 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) if (!IS_GEN2(dev)) intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true); - for_each_encoder_on_crtc(dev, crtc, encoder) - if (encoder->pre_enable) - encoder->pre_enable(encoder); + intel_encoders_pre_enable(crtc, pipe_config, old_state); i9xx_enable_pll(intel_crtc); @@ -6222,8 +6788,7 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) assert_vblank_disabled(crtc); drm_crtc_vblank_on(crtc); - for_each_encoder_on_crtc(dev, crtc, encoder) - encoder->enable(encoder); + intel_encoders_enable(crtc, pipe_config, old_state); } static void i9xx_pfit_disable(struct intel_crtc *crtc) @@ -6241,12 +6806,13 @@ static void i9xx_pfit_disable(struct intel_crtc *crtc) I915_WRITE(PFIT_CONTROL, 0); } -static void i9xx_crtc_disable(struct drm_crtc *crtc) +static void i9xx_crtc_disable(struct intel_crtc_state *old_crtc_state, + struct drm_atomic_state *old_state) { + struct drm_crtc *crtc = old_crtc_state->base.crtc; struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_encoder *encoder; int pipe = intel_crtc->pipe; /* @@ -6256,8 +6822,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) if (IS_GEN2(dev)) intel_wait_for_vblank(dev, pipe); - for_each_encoder_on_crtc(dev, crtc, encoder) - encoder->disable(encoder); + intel_encoders_disable(crtc, old_crtc_state, old_state); drm_crtc_vblank_off(crtc); assert_vblank_disabled(crtc); @@ -6266,9 +6831,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) i9xx_pfit_disable(intel_crtc); - for_each_encoder_on_crtc(dev, crtc, encoder) - if (encoder->post_disable) - encoder->post_disable(encoder); + intel_encoders_post_disable(crtc, old_crtc_state, old_state); if (!intel_crtc_has_type(intel_crtc->config, INTEL_OUTPUT_DSI)) { if (IS_CHERRYVIEW(dev)) @@ -6279,9 +6842,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) i9xx_disable_pll(intel_crtc); } - for_each_encoder_on_crtc(dev, crtc, encoder) - if (encoder->post_pll_disable) - encoder->post_pll_disable(encoder); + intel_encoders_post_pll_disable(crtc, old_crtc_state, old_state); if (!IS_GEN2(dev)) intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false); @@ -6294,20 +6855,34 @@ static void intel_crtc_disable_noatomic(struct drm_crtc *crtc) struct drm_i915_private *dev_priv = to_i915(crtc->dev); enum intel_display_power_domain domain; unsigned long domains; + struct drm_atomic_state *state; + struct intel_crtc_state *crtc_state; + int ret; if (!intel_crtc->active) return; - if (to_intel_plane_state(crtc->primary->state)->visible) { + if (to_intel_plane_state(crtc->primary->state)->base.visible) { WARN_ON(intel_crtc->flip_work); intel_pre_disable_primary_noatomic(crtc); intel_crtc_disable_planes(crtc, 1 << drm_plane_index(crtc->primary)); - to_intel_plane_state(crtc->primary->state)->visible = false; + to_intel_plane_state(crtc->primary->state)->base.visible = false; } - dev_priv->display.crtc_disable(crtc); + state = drm_atomic_state_alloc(crtc->dev); + state->acquire_ctx = crtc->dev->mode_config.acquire_ctx; + + /* Everything's already locked, -EDEADLK can't happen. */ + crtc_state = intel_atomic_get_crtc_state(state, intel_crtc); + ret = drm_atomic_add_affected_connectors(state, crtc); + + WARN_ON(IS_ERR(crtc_state) || ret); + + dev_priv->display.crtc_disable(crtc_state, state); + + drm_atomic_state_free(state); DRM_DEBUG_KMS("[CRTC:%d:%s] hw state adjusted, was enabled, now disabled\n", crtc->base.id, crtc->name); @@ -6822,9 +7397,10 @@ static int i9xx_misc_get_display_clock_speed(struct drm_device *dev) static int pnv_get_display_clock_speed(struct drm_device *dev) { + struct pci_dev *pdev = dev->pdev; u16 gcfgc = 0; - pci_read_config_word(dev->pdev, GCFGC, &gcfgc); + pci_read_config_word(pdev, GCFGC, &gcfgc); switch (gcfgc & GC_DISPLAY_CLOCK_MASK) { case GC_DISPLAY_CLOCK_267_MHZ_PNV: @@ -6846,9 +7422,10 @@ static int pnv_get_display_clock_speed(struct drm_device *dev) static int i915gm_get_display_clock_speed(struct drm_device *dev) { + struct pci_dev *pdev = dev->pdev; u16 gcfgc = 0; - pci_read_config_word(dev->pdev, GCFGC, &gcfgc); + pci_read_config_word(pdev, GCFGC, &gcfgc); if (gcfgc & GC_LOW_FREQUENCY_ENABLE) return 133333; @@ -6870,6 +7447,7 @@ static int i865_get_display_clock_speed(struct drm_device *dev) static int i85x_get_display_clock_speed(struct drm_device *dev) { + struct pci_dev *pdev = dev->pdev; u16 hpllcc = 0; /* @@ -6877,10 +7455,10 @@ static int i85x_get_display_clock_speed(struct drm_device *dev) * encoding is different :( * FIXME is this the right way to detect 852GM/852GMV? */ - if (dev->pdev->revision == 0x1) + if (pdev->revision == 0x1) return 133333; - pci_bus_read_config_word(dev->pdev->bus, + pci_bus_read_config_word(pdev->bus, PCI_DEVFN(0, 3), HPLLCC, &hpllcc); /* Assume that the hardware is in the high speed state. This @@ -6981,10 +7559,11 @@ static unsigned int intel_hpll_vco(struct drm_device *dev) static int gm45_get_display_clock_speed(struct drm_device *dev) { + struct pci_dev *pdev = dev->pdev; unsigned int cdclk_sel, vco = intel_hpll_vco(dev); uint16_t tmp = 0; - pci_read_config_word(dev->pdev, GCFGC, &tmp); + pci_read_config_word(pdev, GCFGC, &tmp); cdclk_sel = (tmp >> 12) & 0x1; @@ -7003,6 +7582,7 @@ static int gm45_get_display_clock_speed(struct drm_device *dev) static int i965gm_get_display_clock_speed(struct drm_device *dev) { + struct pci_dev *pdev = dev->pdev; static const uint8_t div_3200[] = { 16, 10, 8 }; static const uint8_t div_4000[] = { 20, 12, 10 }; static const uint8_t div_5333[] = { 24, 16, 14 }; @@ -7010,7 +7590,7 @@ static int i965gm_get_display_clock_speed(struct drm_device *dev) unsigned int cdclk_sel, vco = intel_hpll_vco(dev); uint16_t tmp = 0; - pci_read_config_word(dev->pdev, GCFGC, &tmp); + pci_read_config_word(pdev, GCFGC, &tmp); cdclk_sel = ((tmp >> 8) & 0x1f) - 1; @@ -7040,6 +7620,7 @@ fail: static int g33_get_display_clock_speed(struct drm_device *dev) { + struct pci_dev *pdev = dev->pdev; static const uint8_t div_3200[] = { 12, 10, 8, 7, 5, 16 }; static const uint8_t div_4000[] = { 14, 12, 10, 8, 6, 20 }; static const uint8_t div_4800[] = { 20, 14, 12, 10, 8, 24 }; @@ -7048,7 +7629,7 @@ static int g33_get_display_clock_speed(struct drm_device *dev) unsigned int cdclk_sel, vco = intel_hpll_vco(dev); uint16_t tmp = 0; - pci_read_config_word(dev->pdev, GCFGC, &tmp); + pci_read_config_word(pdev, GCFGC, &tmp); cdclk_sel = (tmp >> 4) & 0x7; @@ -9214,7 +9795,7 @@ skylake_get_initial_plane_config(struct intel_crtc *crtc, return; error: - kfree(fb); + kfree(intel_fb); } static void ironlake_get_pfit_config(struct intel_crtc *crtc, @@ -9420,7 +10001,7 @@ static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv) I915_STATE_WARN(I915_READ(SPLL_CTL) & SPLL_PLL_ENABLE, "SPLL enabled\n"); I915_STATE_WARN(I915_READ(WRPLL_CTL(0)) & WRPLL_PLL_ENABLE, "WRPLL1 enabled\n"); I915_STATE_WARN(I915_READ(WRPLL_CTL(1)) & WRPLL_PLL_ENABLE, "WRPLL2 enabled\n"); - I915_STATE_WARN(I915_READ(PCH_PP_STATUS) & PP_ON, "Panel power on\n"); + I915_STATE_WARN(I915_READ(PP_STATUS(0)) & PP_ON, "Panel power on\n"); I915_STATE_WARN(I915_READ(BLC_PWM_CPU_CTL2) & BLM_PWM_ENABLE, "CPU PWM1 enabled\n"); if (IS_HASWELL(dev)) @@ -9459,7 +10040,7 @@ static void hsw_write_dcomp(struct drm_i915_private *dev_priv, uint32_t val) mutex_lock(&dev_priv->rps.hw_lock); if (sandybridge_pcode_write(dev_priv, GEN6_PCODE_WRITE_D_COMP, val)) - DRM_ERROR("Failed to write to D_COMP\n"); + DRM_DEBUG_KMS("Failed to write to D_COMP\n"); mutex_unlock(&dev_priv->rps.hw_lock); } else { I915_WRITE(D_COMP_BDW, val); @@ -9867,15 +10448,12 @@ static void bxt_get_ddi_pll(struct drm_i915_private *dev_priv, switch (port) { case PORT_A: - pipe_config->ddi_pll_sel = SKL_DPLL0; id = DPLL_ID_SKL_DPLL0; break; case PORT_B: - pipe_config->ddi_pll_sel = SKL_DPLL1; id = DPLL_ID_SKL_DPLL1; break; case PORT_C: - pipe_config->ddi_pll_sel = SKL_DPLL2; id = DPLL_ID_SKL_DPLL2; break; default: @@ -9894,25 +10472,10 @@ static void skylake_get_ddi_pll(struct drm_i915_private *dev_priv, u32 temp; temp = I915_READ(DPLL_CTRL2) & DPLL_CTRL2_DDI_CLK_SEL_MASK(port); - pipe_config->ddi_pll_sel = temp >> (port * 3 + 1); + id = temp >> (port * 3 + 1); - switch (pipe_config->ddi_pll_sel) { - case SKL_DPLL0: - id = DPLL_ID_SKL_DPLL0; - break; - case SKL_DPLL1: - id = DPLL_ID_SKL_DPLL1; - break; - case SKL_DPLL2: - id = DPLL_ID_SKL_DPLL2; - break; - case SKL_DPLL3: - id = DPLL_ID_SKL_DPLL3; - break; - default: - MISSING_CASE(pipe_config->ddi_pll_sel); + if (WARN_ON(id < SKL_DPLL0 || id > SKL_DPLL3)) return; - } pipe_config->shared_dpll = intel_get_shared_dpll_by_id(dev_priv, id); } @@ -9922,10 +10485,9 @@ static void haswell_get_ddi_pll(struct drm_i915_private *dev_priv, struct intel_crtc_state *pipe_config) { enum intel_dpll_id id; + uint32_t ddi_pll_sel = I915_READ(PORT_CLK_SEL(port)); - pipe_config->ddi_pll_sel = I915_READ(PORT_CLK_SEL(port)); - - switch (pipe_config->ddi_pll_sel) { + switch (ddi_pll_sel) { case PORT_CLK_SEL_WRPLL1: id = DPLL_ID_WRPLL1; break; @@ -9945,7 +10507,7 @@ static void haswell_get_ddi_pll(struct drm_i915_private *dev_priv, id = DPLL_ID_LCPLL_2700; break; default: - MISSING_CASE(pipe_config->ddi_pll_sel); + MISSING_CASE(ddi_pll_sel); /* fall through */ case PORT_CLK_SEL_NONE: return; @@ -10178,7 +10740,7 @@ static void i845_update_cursor(struct drm_crtc *crtc, u32 base, struct intel_crtc *intel_crtc = to_intel_crtc(crtc); uint32_t cntl = 0, size = 0; - if (plane_state && plane_state->visible) { + if (plane_state && plane_state->base.visible) { unsigned int width = plane_state->base.crtc_w; unsigned int height = plane_state->base.crtc_h; unsigned int stride = roundup_pow_of_two(width) * 4; @@ -10239,10 +10801,14 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + const struct skl_wm_values *wm = &dev_priv->wm.skl_results; int pipe = intel_crtc->pipe; uint32_t cntl = 0; - if (plane_state && plane_state->visible) { + if (INTEL_GEN(dev_priv) >= 9 && wm->dirty_pipes & drm_crtc_mask(crtc)) + skl_write_cursor_wm(intel_crtc, wm); + + if (plane_state && plane_state->base.visible) { cntl = MCURSOR_GAMMA_ENABLE; switch (plane_state->base.crtc_w) { case 64: @@ -10263,7 +10829,7 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base, if (HAS_DDI(dev)) cntl |= CURSOR_PIPE_CSC_ENABLE; - if (plane_state->base.rotation == BIT(DRM_ROTATE_180)) + if (plane_state->base.rotation == DRM_ROTATE_180) cntl |= CURSOR_ROTATE_180; } @@ -10309,7 +10875,7 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, /* ILK+ do this automagically */ if (HAS_GMCH_DISPLAY(dev) && - plane_state->base.rotation == BIT(DRM_ROTATE_180)) { + plane_state->base.rotation == DRM_ROTATE_180) { base += (plane_state->base.crtc_h * plane_state->base.crtc_w - 1) * 4; } @@ -10442,7 +11008,7 @@ intel_framebuffer_create_for_mode(struct drm_device *dev, fb = intel_framebuffer_create(dev, &mode_cmd, obj); if (IS_ERR(fb)) - drm_gem_object_unreference_unlocked(&obj->base); + i915_gem_object_put_unlocked(obj); return fb; } @@ -10953,13 +11519,13 @@ static void intel_unpin_work_fn(struct work_struct *__work) mutex_lock(&dev->struct_mutex); intel_unpin_fb_obj(work->old_fb, primary->state->rotation); - drm_gem_object_unreference(&work->pending_flip_obj->base); - - if (work->flip_queued_req) - i915_gem_request_assign(&work->flip_queued_req, NULL); + i915_gem_object_put(work->pending_flip_obj); mutex_unlock(&dev->struct_mutex); - intel_frontbuffer_flip_complete(dev, to_intel_plane(primary)->frontbuffer_bit); + i915_gem_request_put(work->flip_queued_req); + + intel_frontbuffer_flip_complete(to_i915(dev), + to_intel_plane(primary)->frontbuffer_bit); intel_fbc_post_update(crtc); drm_framebuffer_unreference(work->old_fb); @@ -10980,10 +11546,8 @@ static bool __pageflip_finished_cs(struct intel_crtc *crtc, { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); - unsigned reset_counter; - reset_counter = i915_reset_counter(&dev_priv->gpu_error); - if (crtc->reset_counter != reset_counter) + if (abort_flip_on_reset(crtc)) return true; /* @@ -11124,7 +11688,7 @@ static int intel_gen2_queue_flip(struct drm_device *dev, struct drm_i915_gem_request *req, uint32_t flags) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); u32 flip_mask; int ret; @@ -11140,13 +11704,13 @@ static int intel_gen2_queue_flip(struct drm_device *dev, flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; else flip_mask = MI_WAIT_FOR_PLANE_A_FLIP; - intel_ring_emit(engine, MI_WAIT_FOR_EVENT | flip_mask); - intel_ring_emit(engine, MI_NOOP); - intel_ring_emit(engine, MI_DISPLAY_FLIP | + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | flip_mask); + intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, MI_DISPLAY_FLIP | MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); - intel_ring_emit(engine, fb->pitches[0]); - intel_ring_emit(engine, intel_crtc->flip_work->gtt_offset); - intel_ring_emit(engine, 0); /* aux display base address, unused */ + intel_ring_emit(ring, fb->pitches[0]); + intel_ring_emit(ring, intel_crtc->flip_work->gtt_offset); + intel_ring_emit(ring, 0); /* aux display base address, unused */ return 0; } @@ -11158,7 +11722,7 @@ static int intel_gen3_queue_flip(struct drm_device *dev, struct drm_i915_gem_request *req, uint32_t flags) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); u32 flip_mask; int ret; @@ -11171,13 +11735,13 @@ static int intel_gen3_queue_flip(struct drm_device *dev, flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; else flip_mask = MI_WAIT_FOR_PLANE_A_FLIP; - intel_ring_emit(engine, MI_WAIT_FOR_EVENT | flip_mask); - intel_ring_emit(engine, MI_NOOP); - intel_ring_emit(engine, MI_DISPLAY_FLIP_I915 | + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | flip_mask); + intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 | MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); - intel_ring_emit(engine, fb->pitches[0]); - intel_ring_emit(engine, intel_crtc->flip_work->gtt_offset); - intel_ring_emit(engine, MI_NOOP); + intel_ring_emit(ring, fb->pitches[0]); + intel_ring_emit(ring, intel_crtc->flip_work->gtt_offset); + intel_ring_emit(ring, MI_NOOP); return 0; } @@ -11189,7 +11753,7 @@ static int intel_gen4_queue_flip(struct drm_device *dev, struct drm_i915_gem_request *req, uint32_t flags) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); uint32_t pf, pipesrc; @@ -11203,11 +11767,11 @@ static int intel_gen4_queue_flip(struct drm_device *dev, * Display Registers (which do not change across a page-flip) * so we need only reprogram the base address. */ - intel_ring_emit(engine, MI_DISPLAY_FLIP | + intel_ring_emit(ring, MI_DISPLAY_FLIP | MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); - intel_ring_emit(engine, fb->pitches[0]); - intel_ring_emit(engine, intel_crtc->flip_work->gtt_offset | - obj->tiling_mode); + intel_ring_emit(ring, fb->pitches[0]); + intel_ring_emit(ring, intel_crtc->flip_work->gtt_offset | + intel_fb_modifier_to_tiling(fb->modifier[0])); /* XXX Enabling the panel-fitter across page-flip is so far * untested on non-native modes, so ignore it for now. @@ -11215,7 +11779,7 @@ static int intel_gen4_queue_flip(struct drm_device *dev, */ pf = 0; pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff; - intel_ring_emit(engine, pf | pipesrc); + intel_ring_emit(ring, pf | pipesrc); return 0; } @@ -11227,7 +11791,7 @@ static int intel_gen6_queue_flip(struct drm_device *dev, struct drm_i915_gem_request *req, uint32_t flags) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); uint32_t pf, pipesrc; @@ -11237,10 +11801,11 @@ static int intel_gen6_queue_flip(struct drm_device *dev, if (ret) return ret; - intel_ring_emit(engine, MI_DISPLAY_FLIP | + intel_ring_emit(ring, MI_DISPLAY_FLIP | MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); - intel_ring_emit(engine, fb->pitches[0] | obj->tiling_mode); - intel_ring_emit(engine, intel_crtc->flip_work->gtt_offset); + intel_ring_emit(ring, fb->pitches[0] | + intel_fb_modifier_to_tiling(fb->modifier[0])); + intel_ring_emit(ring, intel_crtc->flip_work->gtt_offset); /* Contrary to the suggestions in the documentation, * "Enable Panel Fitter" does not seem to be required when page @@ -11250,7 +11815,7 @@ static int intel_gen6_queue_flip(struct drm_device *dev, */ pf = 0; pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff; - intel_ring_emit(engine, pf | pipesrc); + intel_ring_emit(ring, pf | pipesrc); return 0; } @@ -11262,7 +11827,7 @@ static int intel_gen7_queue_flip(struct drm_device *dev, struct drm_i915_gem_request *req, uint32_t flags) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); uint32_t plane_bit = 0; int len, ret; @@ -11283,7 +11848,7 @@ static int intel_gen7_queue_flip(struct drm_device *dev, } len = 4; - if (engine->id == RCS) { + if (req->engine->id == RCS) { len += 6; /* * On Gen 8, SRM is now taking an extra dword to accommodate @@ -11321,30 +11886,32 @@ static int intel_gen7_queue_flip(struct drm_device *dev, * for the RCS also doesn't appear to drop events. Setting the DERRMR * to zero does lead to lockups within MI_DISPLAY_FLIP. */ - if (engine->id == RCS) { - intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(1)); - intel_ring_emit_reg(engine, DERRMR); - intel_ring_emit(engine, ~(DERRMR_PIPEA_PRI_FLIP_DONE | + if (req->engine->id == RCS) { + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1)); + intel_ring_emit_reg(ring, DERRMR); + intel_ring_emit(ring, ~(DERRMR_PIPEA_PRI_FLIP_DONE | DERRMR_PIPEB_PRI_FLIP_DONE | DERRMR_PIPEC_PRI_FLIP_DONE)); if (IS_GEN8(dev)) - intel_ring_emit(engine, MI_STORE_REGISTER_MEM_GEN8 | + intel_ring_emit(ring, MI_STORE_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT); else - intel_ring_emit(engine, MI_STORE_REGISTER_MEM | + intel_ring_emit(ring, MI_STORE_REGISTER_MEM | MI_SRM_LRM_GLOBAL_GTT); - intel_ring_emit_reg(engine, DERRMR); - intel_ring_emit(engine, engine->scratch.gtt_offset + 256); + intel_ring_emit_reg(ring, DERRMR); + intel_ring_emit(ring, + i915_ggtt_offset(req->engine->scratch) + 256); if (IS_GEN8(dev)) { - intel_ring_emit(engine, 0); - intel_ring_emit(engine, MI_NOOP); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, MI_NOOP); } } - intel_ring_emit(engine, MI_DISPLAY_FLIP_I915 | plane_bit); - intel_ring_emit(engine, (fb->pitches[0] | obj->tiling_mode)); - intel_ring_emit(engine, intel_crtc->flip_work->gtt_offset); - intel_ring_emit(engine, (MI_NOOP)); + intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 | plane_bit); + intel_ring_emit(ring, fb->pitches[0] | + intel_fb_modifier_to_tiling(fb->modifier[0])); + intel_ring_emit(ring, intel_crtc->flip_work->gtt_offset); + intel_ring_emit(ring, (MI_NOOP)); return 0; } @@ -11379,7 +11946,8 @@ static bool use_mmio_flip(struct intel_engine_cs *engine, if (resv && !reservation_object_test_signaled_rcu(resv, false)) return true; - return engine != i915_gem_request_get_engine(obj->last_write_req); + return engine != i915_gem_active_get_engine(&obj->last_write, + &obj->base.dev->struct_mutex); } static void skl_do_mmio_flip(struct intel_crtc *intel_crtc, @@ -11390,7 +11958,7 @@ static void skl_do_mmio_flip(struct intel_crtc *intel_crtc, struct drm_i915_private *dev_priv = to_i915(dev); struct drm_framebuffer *fb = intel_crtc->base.primary->fb; const enum pipe pipe = intel_crtc->pipe; - u32 ctl, stride, tile_height; + u32 ctl, stride = skl_plane_stride(fb, 0, rotation); ctl = I915_READ(PLANE_CTL(pipe, 0)); ctl &= ~PLANE_CTL_TILED_MASK; @@ -11411,20 +11979,6 @@ static void skl_do_mmio_flip(struct intel_crtc *intel_crtc, } /* - * The stride is either expressed as a multiple of 64 bytes chunks for - * linear buffers or in number of tiles for tiled buffers. - */ - if (intel_rotation_90_or_270(rotation)) { - /* stride = Surface height in tiles */ - tile_height = intel_tile_height(dev_priv, fb->modifier[0], 0); - stride = DIV_ROUND_UP(fb->height, tile_height); - } else { - stride = fb->pitches[0] / - intel_fb_stride_alignment(dev_priv, fb->modifier[0], - fb->pixel_format); - } - - /* * Both PLANE_CTL and PLANE_STRIDE are not updated on vblank but on * PLANE_SURF updates, the update is then guaranteed to be atomic. */ @@ -11440,15 +11994,13 @@ static void ilk_do_mmio_flip(struct intel_crtc *intel_crtc, { struct drm_device *dev = intel_crtc->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_framebuffer *intel_fb = - to_intel_framebuffer(intel_crtc->base.primary->fb); - struct drm_i915_gem_object *obj = intel_fb->obj; + struct drm_framebuffer *fb = intel_crtc->base.primary->fb; i915_reg_t reg = DSPCNTR(intel_crtc->plane); u32 dspcntr; dspcntr = I915_READ(reg); - if (obj->tiling_mode != I915_TILING_NONE) + if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) dspcntr |= DISPPLANE_TILED; else dspcntr &= ~DISPPLANE_TILED; @@ -11471,9 +12023,8 @@ static void intel_mmio_flip_work_func(struct work_struct *w) struct reservation_object *resv; if (work->flip_queued_req) - WARN_ON(__i915_wait_request(work->flip_queued_req, - false, NULL, - &dev_priv->rps.mmioflips)); + WARN_ON(i915_wait_request(work->flip_queued_req, + 0, NULL, NO_WAITBOOST)); /* For framebuffer backed by dmabuf, wait for fence */ resv = i915_gem_object_get_dmabuf_resv(obj); @@ -11584,7 +12135,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, struct intel_flip_work *work; struct intel_engine_cs *engine; bool mmio_flip; - struct drm_i915_gem_request *request = NULL; + struct drm_i915_gem_request *request; + struct i915_vma *vma; int ret; /* @@ -11650,22 +12202,18 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, /* Reference the objects for the scheduled work. */ drm_framebuffer_reference(work->old_fb); - drm_gem_object_reference(&obj->base); crtc->primary->fb = fb; update_state_fb(crtc->primary); - intel_fbc_pre_update(intel_crtc, intel_crtc->config, - to_intel_plane_state(primary->state)); - - work->pending_flip_obj = obj; + work->pending_flip_obj = i915_gem_object_get(obj); ret = i915_mutex_lock_interruptible(dev); if (ret) goto cleanup; - intel_crtc->reset_counter = i915_reset_counter(&dev_priv->gpu_error); - if (__i915_reset_in_progress_or_wedged(intel_crtc->reset_counter)) { + intel_crtc->reset_count = i915_reset_count(&dev_priv->gpu_error); + if (i915_reset_in_progress_or_wedged(&dev_priv->gpu_error)) { ret = -EIO; goto cleanup; } @@ -11677,13 +12225,14 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) { engine = &dev_priv->engine[BCS]; - if (obj->tiling_mode != intel_fb_obj(work->old_fb)->tiling_mode) + if (fb->modifier[0] != old_fb->modifier[0]) /* vlv: DISPLAY_FLIP fails to change tiling */ engine = NULL; } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) { engine = &dev_priv->engine[BCS]; } else if (INTEL_INFO(dev)->gen >= 7) { - engine = i915_gem_request_get_engine(obj->last_write_req); + engine = i915_gem_active_get_engine(&obj->last_write, + &obj->base.dev->struct_mutex); if (engine == NULL || engine->id != RCS) engine = &dev_priv->engine[BCS]; } else { @@ -11692,47 +12241,52 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, mmio_flip = use_mmio_flip(engine, obj); - /* When using CS flips, we want to emit semaphores between rings. - * However, when using mmio flips we will create a task to do the - * synchronisation, so all we want here is to pin the framebuffer - * into the display plane and skip any waits. - */ - if (!mmio_flip) { - ret = i915_gem_object_sync(obj, engine, &request); - if (!ret && !request) { - request = i915_gem_request_alloc(engine, NULL); - ret = PTR_ERR_OR_ZERO(request); - } - - if (ret) - goto cleanup_pending; - } - - ret = intel_pin_and_fence_fb_obj(fb, primary->state->rotation); - if (ret) + vma = intel_pin_and_fence_fb_obj(fb, primary->state->rotation); + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); goto cleanup_pending; + } - work->gtt_offset = intel_plane_obj_offset(to_intel_plane(primary), - obj, 0); + work->gtt_offset = intel_fb_gtt_offset(fb, primary->state->rotation); work->gtt_offset += intel_crtc->dspaddr_offset; work->rotation = crtc->primary->state->rotation; + /* + * There's the potential that the next frame will not be compatible with + * FBC, so we want to call pre_update() before the actual page flip. + * The problem is that pre_update() caches some information about the fb + * object, so we want to do this only after the object is pinned. Let's + * be on the safe side and do this immediately before scheduling the + * flip. + */ + intel_fbc_pre_update(intel_crtc, intel_crtc->config, + to_intel_plane_state(primary->state)); + if (mmio_flip) { INIT_WORK(&work->mmio_work, intel_mmio_flip_work_func); - i915_gem_request_assign(&work->flip_queued_req, - obj->last_write_req); - + work->flip_queued_req = i915_gem_active_get(&obj->last_write, + &obj->base.dev->struct_mutex); schedule_work(&work->mmio_work); } else { - i915_gem_request_assign(&work->flip_queued_req, request); + request = i915_gem_request_alloc(engine, engine->last_context); + if (IS_ERR(request)) { + ret = PTR_ERR(request); + goto cleanup_unpin; + } + + ret = i915_gem_request_await_object(request, obj, false); + if (ret) + goto cleanup_request; + ret = dev_priv->display.queue_flip(dev, crtc, fb, obj, request, page_flip_flags); if (ret) - goto cleanup_unpin; + goto cleanup_request; intel_mark_page_flip_active(intel_crtc, work); + work->flip_queued_req = i915_gem_request_get(request); i915_add_request_no_flush(request); } @@ -11740,25 +12294,25 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, to_intel_plane(primary)->frontbuffer_bit); mutex_unlock(&dev->struct_mutex); - intel_frontbuffer_flip_prepare(dev, + intel_frontbuffer_flip_prepare(to_i915(dev), to_intel_plane(primary)->frontbuffer_bit); trace_i915_flip_request(intel_crtc->plane, obj); return 0; +cleanup_request: + i915_add_request_no_flush(request); cleanup_unpin: intel_unpin_fb_obj(fb, crtc->primary->state->rotation); cleanup_pending: - if (!IS_ERR_OR_NULL(request)) - i915_add_request_no_flush(request); atomic_dec(&intel_crtc->unpin_work_count); mutex_unlock(&dev->struct_mutex); cleanup: crtc->primary->fb = old_fb; update_state_fb(crtc->primary); - drm_gem_object_unreference_unlocked(&obj->base); + i915_gem_object_put_unlocked(obj); drm_framebuffer_unreference(work->old_fb); spin_lock_irq(&dev->event_lock); @@ -11826,7 +12380,7 @@ static bool intel_wm_need_update(struct drm_plane *plane, struct intel_plane_state *cur = to_intel_plane_state(plane->state); /* Update watermarks on tiling or size changes. */ - if (new->visible != cur->visible) + if (new->base.visible != cur->base.visible) return true; if (!cur->base.fb || !new->base.fb) @@ -11834,10 +12388,10 @@ static bool intel_wm_need_update(struct drm_plane *plane, if (cur->base.fb->modifier[0] != new->base.fb->modifier[0] || cur->base.rotation != new->base.rotation || - drm_rect_width(&new->src) != drm_rect_width(&cur->src) || - drm_rect_height(&new->src) != drm_rect_height(&cur->src) || - drm_rect_width(&new->dst) != drm_rect_width(&cur->dst) || - drm_rect_height(&new->dst) != drm_rect_height(&cur->dst)) + drm_rect_width(&new->base.src) != drm_rect_width(&cur->base.src) || + drm_rect_height(&new->base.src) != drm_rect_height(&cur->base.src) || + drm_rect_width(&new->base.dst) != drm_rect_width(&cur->base.dst) || + drm_rect_height(&new->base.dst) != drm_rect_height(&cur->base.dst)) return true; return false; @@ -11845,10 +12399,10 @@ static bool intel_wm_need_update(struct drm_plane *plane, static bool needs_scaling(struct intel_plane_state *state) { - int src_w = drm_rect_width(&state->src) >> 16; - int src_h = drm_rect_height(&state->src) >> 16; - int dst_w = drm_rect_width(&state->dst); - int dst_h = drm_rect_height(&state->dst); + int src_w = drm_rect_width(&state->base.src) >> 16; + int src_h = drm_rect_height(&state->base.src) >> 16; + int dst_w = drm_rect_width(&state->base.dst); + int dst_h = drm_rect_height(&state->base.dst); return (src_w != dst_w || src_h != dst_h); } @@ -11879,8 +12433,8 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state, return ret; } - was_visible = old_plane_state->visible; - visible = to_intel_plane_state(plane_state)->visible; + was_visible = old_plane_state->base.visible; + visible = to_intel_plane_state(plane_state)->base.visible; if (!was_crtc_enabled && WARN_ON(was_visible)) was_visible = false; @@ -11896,7 +12450,7 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state, * only combine the results from all planes in the current place? */ if (!is_crtc_enabled) - to_intel_plane_state(plane_state)->visible = visible = false; + to_intel_plane_state(plane_state)->base.visible = visible = false; if (!was_visible && !visible) return 0; @@ -12114,21 +12668,11 @@ connected_sink_compute_bpp(struct intel_connector *connector, pipe_config->pipe_bpp = connector->base.display_info.bpc*3; } - /* Clamp bpp to default limit on screens without EDID 1.4 */ - if (connector->base.display_info.bpc == 0) { - int type = connector->base.connector_type; - int clamp_bpp = 24; - - /* Fall back to 18 bpp when DP sink capability is unknown. */ - if (type == DRM_MODE_CONNECTOR_DisplayPort || - type == DRM_MODE_CONNECTOR_eDP) - clamp_bpp = 18; - - if (bpp > clamp_bpp) { - DRM_DEBUG_KMS("clamping display bpp (was %d) to default limit of %d\n", - bpp, clamp_bpp); - pipe_config->pipe_bpp = clamp_bpp; - } + /* Clamp bpp to 8 on screens without EDID 1.4 */ + if (connector->base.display_info.bpc == 0 && bpp > 24) { + DRM_DEBUG_KMS("clamping display bpp (was %d) to default limit of 24\n", + bpp); + pipe_config->pipe_bpp = 24; } } @@ -12244,10 +12788,9 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc, DRM_DEBUG_KMS("double wide: %i\n", pipe_config->double_wide); if (IS_BROXTON(dev)) { - DRM_DEBUG_KMS("ddi_pll_sel: %u; dpll_hw_state: ebb0: 0x%x, ebb4: 0x%x," + DRM_DEBUG_KMS("dpll_hw_state: ebb0: 0x%x, ebb4: 0x%x," "pll0: 0x%x, pll1: 0x%x, pll2: 0x%x, pll3: 0x%x, " "pll6: 0x%x, pll8: 0x%x, pll9: 0x%x, pll10: 0x%x, pcsdw12: 0x%x\n", - pipe_config->ddi_pll_sel, pipe_config->dpll_hw_state.ebb0, pipe_config->dpll_hw_state.ebb4, pipe_config->dpll_hw_state.pll0, @@ -12260,15 +12803,13 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc, pipe_config->dpll_hw_state.pll10, pipe_config->dpll_hw_state.pcsdw12); } else if (IS_SKYLAKE(dev) || IS_KABYLAKE(dev)) { - DRM_DEBUG_KMS("ddi_pll_sel: %u; dpll_hw_state: " + DRM_DEBUG_KMS("dpll_hw_state: " "ctrl1: 0x%x, cfgcr1: 0x%x, cfgcr2: 0x%x\n", - pipe_config->ddi_pll_sel, pipe_config->dpll_hw_state.ctrl1, pipe_config->dpll_hw_state.cfgcr1, pipe_config->dpll_hw_state.cfgcr2); } else if (HAS_DDI(dev)) { - DRM_DEBUG_KMS("ddi_pll_sel: 0x%x; dpll_hw_state: wrpll: 0x%x spll: 0x%x\n", - pipe_config->ddi_pll_sel, + DRM_DEBUG_KMS("dpll_hw_state: wrpll: 0x%x spll: 0x%x\n", pipe_config->dpll_hw_state.wrpll, pipe_config->dpll_hw_state.spll); } else { @@ -12282,6 +12823,7 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc, DRM_DEBUG_KMS("planes on this crtc\n"); list_for_each_entry(plane, &dev->mode_config.plane_list, head) { + char *format_name; intel_plane = to_intel_plane(plane); if (intel_plane->pipe != crtc->pipe) continue; @@ -12294,19 +12836,23 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc, continue; } + format_name = drm_get_format_name(fb->pixel_format); + DRM_DEBUG_KMS("[PLANE:%d:%s] enabled", plane->base.id, plane->name); DRM_DEBUG_KMS("\tFB:%d, fb = %ux%u format = %s", - fb->base.id, fb->width, fb->height, - drm_get_format_name(fb->pixel_format)); + fb->base.id, fb->width, fb->height, format_name); DRM_DEBUG_KMS("\tscaler:%d src %dx%d+%d+%d dst %dx%d+%d+%d\n", state->scaler_id, - state->src.x1 >> 16, state->src.y1 >> 16, - drm_rect_width(&state->src) >> 16, - drm_rect_height(&state->src) >> 16, - state->dst.x1, state->dst.y1, - drm_rect_width(&state->dst), - drm_rect_height(&state->dst)); + state->base.src.x1 >> 16, + state->base.src.y1 >> 16, + drm_rect_width(&state->base.src) >> 16, + drm_rect_height(&state->base.src) >> 16, + state->base.dst.x1, state->base.dst.y1, + drm_rect_width(&state->base.dst), + drm_rect_height(&state->base.dst)); + + kfree(format_name); } } @@ -12315,6 +12861,7 @@ static bool check_digital_port_conflicts(struct drm_atomic_state *state) struct drm_device *dev = state->dev; struct drm_connector *connector; unsigned int used_ports = 0; + unsigned int used_mst_ports = 0; /* * Walk the connector list instead of the encoder @@ -12351,11 +12898,20 @@ static bool check_digital_port_conflicts(struct drm_atomic_state *state) return false; used_ports |= port_mask; + break; + case INTEL_OUTPUT_DP_MST: + used_mst_ports |= + 1 << enc_to_mst(&encoder->base)->primary->port; + break; default: break; } } + /* can't mix MST and SST/HDMI on the same port */ + if (used_ports & used_mst_ports) + return false; + return true; } @@ -12366,7 +12922,6 @@ clear_intel_crtc_state(struct intel_crtc_state *crtc_state) struct intel_crtc_scaler_state scaler_state; struct intel_dpll_hw_state dpll_hw_state; struct intel_shared_dpll *shared_dpll; - uint32_t ddi_pll_sel; bool force_thru; /* FIXME: before the switch to atomic started, a new pipe_config was @@ -12378,7 +12933,6 @@ clear_intel_crtc_state(struct intel_crtc_state *crtc_state) scaler_state = crtc_state->scaler_state; shared_dpll = crtc_state->shared_dpll; dpll_hw_state = crtc_state->dpll_hw_state; - ddi_pll_sel = crtc_state->ddi_pll_sel; force_thru = crtc_state->pch_pfit.force_thru; memset(crtc_state, 0, sizeof *crtc_state); @@ -12387,7 +12941,6 @@ clear_intel_crtc_state(struct intel_crtc_state *crtc_state) crtc_state->scaler_state = scaler_state; crtc_state->shared_dpll = shared_dpll; crtc_state->dpll_hw_state = dpll_hw_state; - crtc_state->ddi_pll_sel = ddi_pll_sel; crtc_state->pch_pfit.force_thru = force_thru; } @@ -12475,7 +13028,7 @@ encoder_retry: encoder = to_intel_encoder(connector_state->best_encoder); - if (!(encoder->compute_config(encoder, pipe_config))) { + if (!(encoder->compute_config(encoder, pipe_config, connector_state))) { DRM_DEBUG_KMS("Encoder config failure\n"); goto fail; } @@ -12563,12 +13116,6 @@ static bool intel_fuzzy_clock_check(int clock1, int clock2) return false; } -#define for_each_intel_crtc_masked(dev, mask, intel_crtc) \ - list_for_each_entry((intel_crtc), \ - &(dev)->mode_config.crtc_list, \ - base.head) \ - for_each_if (mask & (1 <<(intel_crtc)->pipe)) - static bool intel_compare_m_n(unsigned int m, unsigned int n, unsigned int m2, unsigned int n2, @@ -12816,8 +13363,6 @@ intel_pipe_config_compare(struct drm_device *dev, PIPE_CONF_CHECK_I(double_wide); - PIPE_CONF_CHECK_X(ddi_pll_sel); - PIPE_CONF_CHECK_P(shared_dpll); PIPE_CONF_CHECK_X(dpll_hw_state.dpll); PIPE_CONF_CHECK_X(dpll_hw_state.dpll_md); @@ -12899,16 +13444,23 @@ static void verify_wm_state(struct drm_crtc *crtc, hw_entry->start, hw_entry->end); } - /* cursor */ - hw_entry = &hw_ddb.plane[pipe][PLANE_CURSOR]; - sw_entry = &sw_ddb->plane[pipe][PLANE_CURSOR]; - - if (!skl_ddb_entry_equal(hw_entry, sw_entry)) { - DRM_ERROR("mismatch in DDB state pipe %c cursor " - "(expected (%u,%u), found (%u,%u))\n", - pipe_name(pipe), - sw_entry->start, sw_entry->end, - hw_entry->start, hw_entry->end); + /* + * cursor + * If the cursor plane isn't active, we may not have updated it's ddb + * allocation. In that case since the ddb allocation will be updated + * once the plane becomes visible, we can skip this check + */ + if (intel_crtc->cursor_addr) { + hw_entry = &hw_ddb.plane[pipe][PLANE_CURSOR]; + sw_entry = &sw_ddb->plane[pipe][PLANE_CURSOR]; + + if (!skl_ddb_entry_equal(hw_entry, sw_entry)) { + DRM_ERROR("mismatch in DDB state pipe %c cursor " + "(expected (%u,%u), found (%u,%u))\n", + pipe_name(pipe), + sw_entry->start, sw_entry->end, + hw_entry->start, hw_entry->end); + } } } @@ -13523,8 +14075,9 @@ static int intel_atomic_prepare_commit(struct drm_device *dev, if (!intel_plane_state->wait_req) continue; - ret = __i915_wait_request(intel_plane_state->wait_req, - true, NULL, NULL); + ret = i915_wait_request(intel_plane_state->wait_req, + I915_WAIT_INTERRUPTIBLE, + NULL, NULL); if (ret) { /* Any hang should be swallowed by the wait */ WARN_ON(ret == -EIO); @@ -13614,6 +14167,111 @@ static bool needs_vblank_wait(struct intel_crtc_state *crtc_state) return false; } +static void intel_update_crtc(struct drm_crtc *crtc, + struct drm_atomic_state *state, + struct drm_crtc_state *old_crtc_state, + unsigned int *crtc_vblank_mask) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_crtc_state *pipe_config = to_intel_crtc_state(crtc->state); + bool modeset = needs_modeset(crtc->state); + + if (modeset) { + update_scanline_offset(intel_crtc); + dev_priv->display.crtc_enable(pipe_config, state); + } else { + intel_pre_plane_update(to_intel_crtc_state(old_crtc_state)); + } + + if (drm_atomic_get_existing_plane_state(state, crtc->primary)) { + intel_fbc_enable( + intel_crtc, pipe_config, + to_intel_plane_state(crtc->primary->state)); + } + + drm_atomic_helper_commit_planes_on_crtc(old_crtc_state); + + if (needs_vblank_wait(pipe_config)) + *crtc_vblank_mask |= drm_crtc_mask(crtc); +} + +static void intel_update_crtcs(struct drm_atomic_state *state, + unsigned int *crtc_vblank_mask) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state; + int i; + + for_each_crtc_in_state(state, crtc, old_crtc_state, i) { + if (!crtc->state->active) + continue; + + intel_update_crtc(crtc, state, old_crtc_state, + crtc_vblank_mask); + } +} + +static void skl_update_crtcs(struct drm_atomic_state *state, + unsigned int *crtc_vblank_mask) +{ + struct drm_device *dev = state->dev; + struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_atomic_state *intel_state = to_intel_atomic_state(state); + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state; + struct skl_ddb_allocation *new_ddb = &intel_state->wm_results.ddb; + struct skl_ddb_allocation *cur_ddb = &dev_priv->wm.skl_hw.ddb; + unsigned int updated = 0; + bool progress; + enum pipe pipe; + + /* + * Whenever the number of active pipes changes, we need to make sure we + * update the pipes in the right order so that their ddb allocations + * never overlap with eachother inbetween CRTC updates. Otherwise we'll + * cause pipe underruns and other bad stuff. + */ + do { + int i; + progress = false; + + for_each_crtc_in_state(state, crtc, old_crtc_state, i) { + bool vbl_wait = false; + unsigned int cmask = drm_crtc_mask(crtc); + pipe = to_intel_crtc(crtc)->pipe; + + if (updated & cmask || !crtc->state->active) + continue; + if (skl_ddb_allocation_overlaps(state, cur_ddb, new_ddb, + pipe)) + continue; + + updated |= cmask; + + /* + * If this is an already active pipe, it's DDB changed, + * and this isn't the last pipe that needs updating + * then we need to wait for a vblank to pass for the + * new ddb allocation to take effect. + */ + if (!skl_ddb_allocation_equals(cur_ddb, new_ddb, pipe) && + !crtc->state->active_changed && + intel_state->wm_results.dirty_pipes != updated) + vbl_wait = true; + + intel_update_crtc(crtc, state, old_crtc_state, + crtc_vblank_mask); + + if (vbl_wait) + intel_wait_for_vblank(dev, pipe); + + progress = true; + } + } while (progress); +} + static void intel_atomic_commit_tail(struct drm_atomic_state *state) { struct drm_device *dev = state->dev; @@ -13636,8 +14294,8 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state) if (!intel_plane_state->wait_req) continue; - ret = __i915_wait_request(intel_plane_state->wait_req, - true, NULL, NULL); + ret = i915_wait_request(intel_plane_state->wait_req, + 0, NULL, NULL); /* EIO should be eaten, and we can't get interrupted in the * worker, and blocking commits have waited already. */ WARN_ON(ret); @@ -13673,7 +14331,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state) if (old_crtc_state->active) { intel_crtc_disable_planes(crtc, old_crtc_state->plane_mask); - dev_priv->display.crtc_disable(crtc); + dev_priv->display.crtc_disable(to_intel_crtc_state(old_crtc_state), state); intel_crtc->active = false; intel_fbc_disable(intel_crtc); intel_disable_shared_dpll(intel_crtc); @@ -13702,20 +14360,19 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state) intel_state->cdclk_pll_vco != dev_priv->cdclk_pll.vco)) dev_priv->display.modeset_commit_cdclk(state); + /* + * SKL workaround: bspec recommends we disable the SAGV when we + * have more then one pipe enabled + */ + if (IS_SKYLAKE(dev_priv) && !skl_can_enable_sagv(state)) + skl_disable_sagv(dev_priv); + intel_modeset_verify_disabled(dev); } - /* Now enable the clocks, plane, pipe, and connectors that we set up. */ + /* Complete the events for pipes that have now been disabled */ for_each_crtc_in_state(state, crtc, old_crtc_state, i) { - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); bool modeset = needs_modeset(crtc->state); - struct intel_crtc_state *pipe_config = - to_intel_crtc_state(crtc->state); - - if (modeset && crtc->state->active) { - update_scanline_offset(to_intel_crtc(crtc)); - dev_priv->display.crtc_enable(crtc); - } /* Complete events for now disable pipes here. */ if (modeset && !crtc->state->active && crtc->state->event) { @@ -13725,21 +14382,11 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state) crtc->state->event = NULL; } - - if (!modeset) - intel_pre_plane_update(to_intel_crtc_state(old_crtc_state)); - - if (crtc->state->active && - drm_atomic_get_existing_plane_state(state, crtc->primary)) - intel_fbc_enable(intel_crtc, pipe_config, to_intel_plane_state(crtc->primary->state)); - - if (crtc->state->active) - drm_atomic_helper_commit_planes_on_crtc(old_crtc_state); - - if (pipe_config->base.active && needs_vblank_wait(pipe_config)) - crtc_vblank_mask |= 1 << i; } + /* Now enable the clocks, plane, pipe, and connectors that we set up. */ + dev_priv->display.update_crtcs(state, &crtc_vblank_mask); + /* FIXME: We should call drm_atomic_helper_commit_hw_done() here * already, but still need the state for the delayed optimization. To * fix this: @@ -13775,6 +14422,10 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state) intel_modeset_verify_crtc(crtc, old_crtc_state, crtc->state); } + if (IS_SKYLAKE(dev_priv) && intel_state->modeset && + skl_can_enable_sagv(state)) + skl_enable_sagv(dev_priv); + drm_atomic_helper_commit_hw_done(state); if (intel_state->modeset) @@ -13814,19 +14465,12 @@ static void intel_atomic_track_fbs(struct drm_atomic_state *state) { struct drm_plane_state *old_plane_state; struct drm_plane *plane; - struct drm_i915_gem_object *obj, *old_obj; - struct intel_plane *intel_plane; int i; - mutex_lock(&state->dev->struct_mutex); - for_each_plane_in_state(state, plane, old_plane_state, i) { - obj = intel_fb_obj(plane->state->fb); - old_obj = intel_fb_obj(old_plane_state->fb); - intel_plane = to_intel_plane(plane); - - i915_gem_track_fb(old_obj, obj, intel_plane->frontbuffer_bit); - } - mutex_unlock(&state->dev->struct_mutex); + for_each_plane_in_state(state, plane, old_plane_state, i) + i915_gem_track_fb(intel_fb_obj(old_plane_state->fb), + intel_fb_obj(plane->state->fb), + to_intel_plane(plane)->frontbuffer_bit); } /** @@ -13922,8 +14566,6 @@ out: drm_atomic_state_free(state); } -#undef for_each_intel_crtc_masked - /* * FIXME: Remove this once i915 is fully DRIVER_ATOMIC by calling * drm_atomic_helper_legacy_gamma_set() directly. @@ -13992,7 +14634,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { */ int intel_prepare_plane_fb(struct drm_plane *plane, - const struct drm_plane_state *new_state) + struct drm_plane_state *new_state) { struct drm_device *dev = plane->dev; struct drm_framebuffer *fb = new_state->fb; @@ -14051,15 +14693,17 @@ intel_prepare_plane_fb(struct drm_plane *plane, if (ret) DRM_DEBUG_KMS("failed to attach phys object\n"); } else { - ret = intel_pin_and_fence_fb_obj(fb, new_state->rotation); + struct i915_vma *vma; + + vma = intel_pin_and_fence_fb_obj(fb, new_state->rotation); + if (IS_ERR(vma)) + ret = PTR_ERR(vma); } if (ret == 0) { - struct intel_plane_state *plane_state = - to_intel_plane_state(new_state); - - i915_gem_request_assign(&plane_state->wait_req, - obj->last_write_req); + to_intel_plane_state(new_state)->wait_req = + i915_gem_active_get(&obj->last_write, + &obj->base.dev->struct_mutex); } return ret; @@ -14076,10 +14720,11 @@ intel_prepare_plane_fb(struct drm_plane *plane, */ void intel_cleanup_plane_fb(struct drm_plane *plane, - const struct drm_plane_state *old_state) + struct drm_plane_state *old_state) { struct drm_device *dev = plane->dev; struct intel_plane_state *old_intel_state; + struct intel_plane_state *intel_state = to_intel_plane_state(plane->state); struct drm_i915_gem_object *old_obj = intel_fb_obj(old_state->fb); struct drm_i915_gem_object *obj = intel_fb_obj(plane->state->fb); @@ -14092,6 +14737,7 @@ intel_cleanup_plane_fb(struct drm_plane *plane, !INTEL_INFO(dev)->cursor_needs_physical)) intel_unpin_fb_obj(old_state->fb, old_state->rotation); + i915_gem_request_assign(&intel_state->wait_req, NULL); i915_gem_request_assign(&old_intel_state->wait_req, NULL); } @@ -14126,13 +14772,14 @@ intel_check_primary_plane(struct drm_plane *plane, struct intel_crtc_state *crtc_state, struct intel_plane_state *state) { + struct drm_i915_private *dev_priv = to_i915(plane->dev); struct drm_crtc *crtc = state->base.crtc; - struct drm_framebuffer *fb = state->base.fb; int min_scale = DRM_PLANE_HELPER_NO_SCALING; int max_scale = DRM_PLANE_HELPER_NO_SCALING; bool can_position = false; + int ret; - if (INTEL_INFO(plane->dev)->gen >= 9) { + if (INTEL_GEN(dev_priv) >= 9) { /* use scaler when colorkey is not required */ if (state->ckey.flags == I915_SET_COLORKEY_NONE) { min_scale = 1; @@ -14141,22 +14788,35 @@ intel_check_primary_plane(struct drm_plane *plane, can_position = true; } - return drm_plane_helper_check_update(plane, crtc, fb, &state->src, - &state->dst, &state->clip, - state->base.rotation, - min_scale, max_scale, - can_position, true, - &state->visible); + ret = drm_plane_helper_check_state(&state->base, + &state->clip, + min_scale, max_scale, + can_position, true); + if (ret) + return ret; + + if (!state->base.fb) + return 0; + + if (INTEL_GEN(dev_priv) >= 9) { + ret = skl_check_plane_surface(state); + if (ret) + return ret; + } + + return 0; } static void intel_begin_crtc_commit(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_crtc_state *old_intel_state = to_intel_crtc_state(old_crtc_state); bool modeset = needs_modeset(crtc->state); + enum pipe pipe = intel_crtc->pipe; /* Perform vblank evasion around commit operation */ intel_pipe_update_start(intel_crtc); @@ -14171,8 +14831,12 @@ static void intel_begin_crtc_commit(struct drm_crtc *crtc, if (to_intel_crtc_state(crtc->state)->update_pipe) intel_update_pipe_config(intel_crtc, old_intel_state); - else if (INTEL_INFO(dev)->gen >= 9) + else if (INTEL_GEN(dev_priv) >= 9) { skl_detach_scalers(intel_crtc); + + I915_WRITE(PIPE_WM_LINETIME(pipe), + dev_priv->wm.skl_hw.wm_linetime[pipe]); + } } static void intel_finish_crtc_commit(struct drm_crtc *crtc, @@ -14306,11 +14970,11 @@ fail: void intel_create_rotation_property(struct drm_device *dev, struct intel_plane *plane) { if (!dev->mode_config.rotation_property) { - unsigned long flags = BIT(DRM_ROTATE_0) | - BIT(DRM_ROTATE_180); + unsigned long flags = DRM_ROTATE_0 | + DRM_ROTATE_180; if (INTEL_INFO(dev)->gen >= 9) - flags |= BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270); + flags |= DRM_ROTATE_90 | DRM_ROTATE_270; dev->mode_config.rotation_property = drm_mode_create_rotation_property(dev, flags); @@ -14326,19 +14990,17 @@ intel_check_cursor_plane(struct drm_plane *plane, struct intel_crtc_state *crtc_state, struct intel_plane_state *state) { - struct drm_crtc *crtc = crtc_state->base.crtc; struct drm_framebuffer *fb = state->base.fb; struct drm_i915_gem_object *obj = intel_fb_obj(fb); enum pipe pipe = to_intel_plane(plane)->pipe; unsigned stride; int ret; - ret = drm_plane_helper_check_update(plane, crtc, fb, &state->src, - &state->dst, &state->clip, - state->base.rotation, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - true, true, &state->visible); + ret = drm_plane_helper_check_state(&state->base, + &state->clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true); if (ret) return ret; @@ -14375,7 +15037,7 @@ intel_check_cursor_plane(struct drm_plane *plane, * Refuse the put the cursor into that compromised position. */ if (IS_CHERRYVIEW(plane->dev) && pipe == PIPE_C && - state->visible && state->base.crtc_x < 0) { + state->base.visible && state->base.crtc_x < 0) { DRM_DEBUG_KMS("CHV cursor C not allowed to straddle the left screen edge\n"); return -EINVAL; } @@ -14407,7 +15069,7 @@ intel_update_cursor_plane(struct drm_plane *plane, if (!obj) addr = 0; else if (!INTEL_INFO(dev)->cursor_needs_physical) - addr = i915_gem_obj_ggtt_offset(obj); + addr = i915_gem_object_ggtt_offset(obj, NULL); else addr = obj->phys_handle->busaddr; @@ -14453,8 +15115,8 @@ static struct drm_plane *intel_cursor_plane_create(struct drm_device *dev, if (!dev->mode_config.rotation_property) dev->mode_config.rotation_property = drm_mode_create_rotation_property(dev, - BIT(DRM_ROTATE_0) | - BIT(DRM_ROTATE_180)); + DRM_ROTATE_0 | + DRM_ROTATE_180); if (dev->mode_config.rotation_property) drm_object_attach_property(&cursor->base.base, dev->mode_config.rotation_property, @@ -14660,12 +15322,50 @@ static bool intel_crt_present(struct drm_device *dev) return true; } +void intel_pps_unlock_regs_wa(struct drm_i915_private *dev_priv) +{ + int pps_num; + int pps_idx; + + if (HAS_DDI(dev_priv)) + return; + /* + * This w/a is needed at least on CPT/PPT, but to be sure apply it + * everywhere where registers can be write protected. + */ + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) + pps_num = 2; + else + pps_num = 1; + + for (pps_idx = 0; pps_idx < pps_num; pps_idx++) { + u32 val = I915_READ(PP_CONTROL(pps_idx)); + + val = (val & ~PANEL_UNLOCK_MASK) | PANEL_UNLOCK_REGS; + I915_WRITE(PP_CONTROL(pps_idx), val); + } +} + +static void intel_pps_init(struct drm_i915_private *dev_priv) +{ + if (HAS_PCH_SPLIT(dev_priv) || IS_BROXTON(dev_priv)) + dev_priv->pps_mmio_base = PCH_PPS_BASE; + else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) + dev_priv->pps_mmio_base = VLV_PPS_BASE; + else + dev_priv->pps_mmio_base = PPS_BASE; + + intel_pps_unlock_regs_wa(dev_priv); +} + static void intel_setup_outputs(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); struct intel_encoder *encoder; bool dpd_is_edp = false; + intel_pps_init(dev_priv); + /* * intel_edp_init_connector() depends on this completing first, to * prevent the registeration of both eDP and LVDS and the incorrect @@ -14853,7 +15553,7 @@ static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb) drm_framebuffer_cleanup(fb); mutex_lock(&dev->struct_mutex); WARN_ON(!intel_fb->obj->framebuffer_references--); - drm_gem_object_unreference(&intel_fb->obj->base); + i915_gem_object_put(intel_fb->obj); mutex_unlock(&dev->struct_mutex); kfree(intel_fb); } @@ -14933,24 +15633,27 @@ static int intel_framebuffer_init(struct drm_device *dev, struct drm_i915_gem_object *obj) { struct drm_i915_private *dev_priv = to_i915(dev); - unsigned int aligned_height; + unsigned int tiling = i915_gem_object_get_tiling(obj); int ret; u32 pitch_limit, stride_alignment; + char *format_name; WARN_ON(!mutex_is_locked(&dev->struct_mutex)); if (mode_cmd->flags & DRM_MODE_FB_MODIFIERS) { - /* Enforce that fb modifier and tiling mode match, but only for - * X-tiled. This is needed for FBC. */ - if (!!(obj->tiling_mode == I915_TILING_X) != - !!(mode_cmd->modifier[0] == I915_FORMAT_MOD_X_TILED)) { + /* + * If there's a fence, enforce that + * the fb modifier and tiling mode match. + */ + if (tiling != I915_TILING_NONE && + tiling != intel_fb_modifier_to_tiling(mode_cmd->modifier[0])) { DRM_DEBUG("tiling_mode doesn't match fb modifier\n"); return -EINVAL; } } else { - if (obj->tiling_mode == I915_TILING_X) + if (tiling == I915_TILING_X) { mode_cmd->modifier[0] = I915_FORMAT_MOD_X_TILED; - else if (obj->tiling_mode == I915_TILING_Y) { + } else if (tiling == I915_TILING_Y) { DRM_DEBUG("No Y tiling for legacy addfb\n"); return -EINVAL; } @@ -14974,6 +15677,16 @@ static int intel_framebuffer_init(struct drm_device *dev, return -EINVAL; } + /* + * gen2/3 display engine uses the fence if present, + * so the tiling mode must match the fb modifier exactly. + */ + if (INTEL_INFO(dev_priv)->gen < 4 && + tiling != intel_fb_modifier_to_tiling(mode_cmd->modifier[0])) { + DRM_DEBUG("tiling_mode must match fb modifier exactly on gen2/3\n"); + return -EINVAL; + } + stride_alignment = intel_fb_stride_alignment(dev_priv, mode_cmd->modifier[0], mode_cmd->pixel_format); @@ -14993,10 +15706,15 @@ static int intel_framebuffer_init(struct drm_device *dev, return -EINVAL; } - if (mode_cmd->modifier[0] == I915_FORMAT_MOD_X_TILED && - mode_cmd->pitches[0] != obj->stride) { + /* + * If there's a fence, enforce that + * the fb pitch and fence stride match. + */ + if (tiling != I915_TILING_NONE && + mode_cmd->pitches[0] != i915_gem_object_get_stride(obj)) { DRM_DEBUG("pitch (%d) must match tiling stride (%d)\n", - mode_cmd->pitches[0], obj->stride); + mode_cmd->pitches[0], + i915_gem_object_get_stride(obj)); return -EINVAL; } @@ -15009,16 +15727,18 @@ static int intel_framebuffer_init(struct drm_device *dev, break; case DRM_FORMAT_XRGB1555: if (INTEL_INFO(dev)->gen > 3) { - DRM_DEBUG("unsupported pixel format: %s\n", - drm_get_format_name(mode_cmd->pixel_format)); + format_name = drm_get_format_name(mode_cmd->pixel_format); + DRM_DEBUG("unsupported pixel format: %s\n", format_name); + kfree(format_name); return -EINVAL; } break; case DRM_FORMAT_ABGR8888: if (!IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev) && INTEL_INFO(dev)->gen < 9) { - DRM_DEBUG("unsupported pixel format: %s\n", - drm_get_format_name(mode_cmd->pixel_format)); + format_name = drm_get_format_name(mode_cmd->pixel_format); + DRM_DEBUG("unsupported pixel format: %s\n", format_name); + kfree(format_name); return -EINVAL; } break; @@ -15026,15 +15746,17 @@ static int intel_framebuffer_init(struct drm_device *dev, case DRM_FORMAT_XRGB2101010: case DRM_FORMAT_XBGR2101010: if (INTEL_INFO(dev)->gen < 4) { - DRM_DEBUG("unsupported pixel format: %s\n", - drm_get_format_name(mode_cmd->pixel_format)); + format_name = drm_get_format_name(mode_cmd->pixel_format); + DRM_DEBUG("unsupported pixel format: %s\n", format_name); + kfree(format_name); return -EINVAL; } break; case DRM_FORMAT_ABGR2101010: if (!IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev)) { - DRM_DEBUG("unsupported pixel format: %s\n", - drm_get_format_name(mode_cmd->pixel_format)); + format_name = drm_get_format_name(mode_cmd->pixel_format); + DRM_DEBUG("unsupported pixel format: %s\n", format_name); + kfree(format_name); return -EINVAL; } break; @@ -15043,14 +15765,16 @@ static int intel_framebuffer_init(struct drm_device *dev, case DRM_FORMAT_YVYU: case DRM_FORMAT_VYUY: if (INTEL_INFO(dev)->gen < 5) { - DRM_DEBUG("unsupported pixel format: %s\n", - drm_get_format_name(mode_cmd->pixel_format)); + format_name = drm_get_format_name(mode_cmd->pixel_format); + DRM_DEBUG("unsupported pixel format: %s\n", format_name); + kfree(format_name); return -EINVAL; } break; default: - DRM_DEBUG("unsupported pixel format: %s\n", - drm_get_format_name(mode_cmd->pixel_format)); + format_name = drm_get_format_name(mode_cmd->pixel_format); + DRM_DEBUG("unsupported pixel format: %s\n", format_name); + kfree(format_name); return -EINVAL; } @@ -15058,17 +15782,12 @@ static int intel_framebuffer_init(struct drm_device *dev, if (mode_cmd->offsets[0] != 0) return -EINVAL; - aligned_height = intel_fb_align_height(dev, mode_cmd->height, - mode_cmd->pixel_format, - mode_cmd->modifier[0]); - /* FIXME drm helper for size checks (especially planar formats)? */ - if (obj->base.size < aligned_height * mode_cmd->pitches[0]) - return -EINVAL; - drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd); intel_fb->obj = obj; - intel_fill_fb_info(dev_priv, &intel_fb->base); + ret = intel_fill_fb_info(dev_priv, &intel_fb->base); + if (ret) + return ret; ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs); if (ret) { @@ -15090,13 +15809,13 @@ intel_user_framebuffer_create(struct drm_device *dev, struct drm_i915_gem_object *obj; struct drm_mode_fb_cmd2 mode_cmd = *user_mode_cmd; - obj = to_intel_bo(drm_gem_object_lookup(filp, mode_cmd.handles[0])); - if (&obj->base == NULL) + obj = i915_gem_object_lookup(filp, mode_cmd.handles[0]); + if (!obj) return ERR_PTR(-ENOENT); fb = intel_framebuffer_create(dev, &mode_cmd, obj); if (IS_ERR(fb)) - drm_gem_object_unreference_unlocked(&obj->base); + i915_gem_object_put_unlocked(obj); return fb; } @@ -15279,6 +15998,11 @@ void intel_init_display_hooks(struct drm_i915_private *dev_priv) skl_modeset_calc_cdclk; } + if (dev_priv->info.gen >= 9) + dev_priv->display.update_crtcs = skl_update_crtcs; + else + dev_priv->display.update_crtcs = intel_update_crtcs; + switch (INTEL_INFO(dev_priv)->gen) { case 2: dev_priv->display.queue_flip = intel_gen2_queue_flip; @@ -15480,15 +16204,16 @@ static void intel_init_quirks(struct drm_device *dev) static void i915_disable_vga(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; u8 sr1; i915_reg_t vga_reg = i915_vgacntrl_reg(dev); /* WaEnableVGAAccessThroughIOPort:ctg,elk,ilk,snb,ivb,vlv,hsw */ - vga_get_uninterruptible(dev->pdev, VGA_RSRC_LEGACY_IO); + vga_get_uninterruptible(pdev, VGA_RSRC_LEGACY_IO); outb(SR01, VGA_SR_INDEX); sr1 = inb(VGA_SR_DATA); outb(sr1 | 1<<5, VGA_SR_DATA); - vga_put(dev->pdev, VGA_RSRC_LEGACY_IO); + vga_put(pdev, VGA_RSRC_LEGACY_IO); udelay(300); I915_WRITE(vga_reg, VGA_DISP_DISABLE); @@ -15504,7 +16229,6 @@ void intel_modeset_init_hw(struct drm_device *dev) dev_priv->atomic_cdclk_freq = dev_priv->cdclk_freq; intel_init_clock_gating(dev); - intel_enable_gt_powersave(dev_priv); } /* @@ -15771,15 +16495,22 @@ static bool intel_crtc_has_encoders(struct intel_crtc *crtc) return false; } -static bool intel_encoder_has_connectors(struct intel_encoder *encoder) +static struct intel_connector *intel_encoder_find_connector(struct intel_encoder *encoder) { struct drm_device *dev = encoder->base.dev; struct intel_connector *connector; for_each_connector_on_encoder(dev, &encoder->base, connector) - return true; + return connector; - return false; + return NULL; +} + +static bool has_pch_trancoder(struct drm_i915_private *dev_priv, + enum transcoder pch_transcoder) +{ + return HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv) || + (HAS_PCH_LPT_H(dev_priv) && pch_transcoder == TRANSCODER_A); } static void intel_sanitize_crtc(struct intel_crtc *crtc) @@ -15825,7 +16556,7 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) * Temporarily change the plane mapping and disable everything * ... */ plane = crtc->plane; - to_intel_plane_state(crtc->base.primary->state)->visible = true; + to_intel_plane_state(crtc->base.primary->state)->base.visible = true; crtc->plane = !plane; intel_crtc_disable_noatomic(&crtc->base); crtc->plane = plane; @@ -15860,14 +16591,23 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) * worst a fifo underrun happens which also sets this to false. */ crtc->cpu_fifo_underrun_disabled = true; - crtc->pch_fifo_underrun_disabled = true; + /* + * We track the PCH trancoder underrun reporting state + * within the crtc. With crtc for pipe A housing the underrun + * reporting state for PCH transcoder A, crtc for pipe B housing + * it for PCH transcoder B, etc. LPT-H has only PCH transcoder A, + * and marking underrun reporting as disabled for the non-existing + * PCH transcoders B and C would prevent enabling the south + * error interrupt (see cpt_can_enable_serr_int()). + */ + if (has_pch_trancoder(dev_priv, (enum transcoder)crtc->pipe)) + crtc->pch_fifo_underrun_disabled = true; } } static void intel_sanitize_encoder(struct intel_encoder *encoder) { struct intel_connector *connector; - struct drm_device *dev = encoder->base.dev; /* We need to check both for a crtc link (meaning that the * encoder is active and trying to read from a pipe) and the @@ -15875,7 +16615,8 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder) bool has_active_crtc = encoder->base.crtc && to_intel_crtc(encoder->base.crtc)->active; - if (intel_encoder_has_connectors(encoder) && !has_active_crtc) { + connector = intel_encoder_find_connector(encoder); + if (connector && !has_active_crtc) { DRM_DEBUG_KMS("[ENCODER:%d:%s] has active connectors but no active pipe!\n", encoder->base.base.id, encoder->base.name); @@ -15884,12 +16625,14 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder) * fallout from our resume register restoring. Disable * the encoder manually again. */ if (encoder->base.crtc) { + struct drm_crtc_state *crtc_state = encoder->base.crtc->state; + DRM_DEBUG_KMS("[ENCODER:%d:%s] manually disabled\n", encoder->base.base.id, encoder->base.name); - encoder->disable(encoder); + encoder->disable(encoder, to_intel_crtc_state(crtc_state), connector->base.state); if (encoder->post_disable) - encoder->post_disable(encoder); + encoder->post_disable(encoder, to_intel_crtc_state(crtc_state), connector->base.state); } encoder->base.crtc = NULL; @@ -15897,12 +16640,9 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder) * a bug in one of the get_hw_state functions. Or someplace else * in our code, like the register restore mess on resume. Clamp * things to off as a safer default. */ - for_each_intel_connector(dev, connector) { - if (connector->encoder != encoder) - continue; - connector->base.dpms = DRM_MODE_DPMS_OFF; - connector->base.encoder = NULL; - } + + connector->base.dpms = DRM_MODE_DPMS_OFF; + connector->base.encoder = NULL; } /* Enabled encoders without active connectors will be fixed in * the crtc fixup. */ @@ -15952,10 +16692,10 @@ static void readout_plane_state(struct intel_crtc *crtc) struct intel_plane_state *plane_state = to_intel_plane_state(primary->state); - plane_state->visible = crtc->active && + plane_state->base.visible = crtc->active && primary_get_hw_state(to_intel_plane(primary)); - if (plane_state->visible) + if (plane_state->base.visible) crtc->base.state->plane_mask |= 1 << drm_plane_index(primary); } @@ -16174,9 +16914,10 @@ void intel_display_resume(struct drm_device *dev) struct drm_atomic_state *state = dev_priv->modeset_restore_state; struct drm_modeset_acquire_ctx ctx; int ret; - bool setup = false; dev_priv->modeset_restore_state = NULL; + if (state) + state->acquire_ctx = &ctx; /* * This is a cludge because with real atomic modeset mode_config.mutex @@ -16187,43 +16928,17 @@ void intel_display_resume(struct drm_device *dev) mutex_lock(&dev->mode_config.mutex); drm_modeset_acquire_init(&ctx, 0); -retry: - ret = drm_modeset_lock_all_ctx(dev, &ctx); - - if (ret == 0 && !setup) { - setup = true; - - intel_modeset_setup_hw_state(dev); - i915_redisable_vga(dev); - } - - if (ret == 0 && state) { - struct drm_crtc_state *crtc_state; - struct drm_crtc *crtc; - int i; - - state->acquire_ctx = &ctx; - - /* ignore any reset values/BIOS leftovers in the WM registers */ - to_intel_atomic_state(state)->skip_intermediate_wm = true; - - for_each_crtc_in_state(state, crtc, crtc_state, i) { - /* - * Force recalculation even if we restore - * current state. With fast modeset this may not result - * in a modeset when the state is compatible. - */ - crtc_state->mode_changed = true; - } - - ret = drm_atomic_commit(state); - } + while (1) { + ret = drm_modeset_lock_all_ctx(dev, &ctx); + if (ret != -EDEADLK) + break; - if (ret == -EDEADLK) { drm_modeset_backoff(&ctx); - goto retry; } + if (!ret) + ret = __intel_display_resume(dev, state); + drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); mutex_unlock(&dev->mode_config.mutex); @@ -16239,7 +16954,6 @@ void intel_modeset_gem_init(struct drm_device *dev) struct drm_i915_private *dev_priv = to_i915(dev); struct drm_crtc *c; struct drm_i915_gem_object *obj; - int ret; intel_init_gt_powersave(dev_priv); @@ -16253,15 +16967,17 @@ void intel_modeset_gem_init(struct drm_device *dev) * for this. */ for_each_crtc(dev, c) { + struct i915_vma *vma; + obj = intel_fb_obj(c->primary->fb); if (obj == NULL) continue; mutex_lock(&dev->struct_mutex); - ret = intel_pin_and_fence_fb_obj(c->primary->fb, + vma = intel_pin_and_fence_fb_obj(c->primary->fb, c->primary->state->rotation); mutex_unlock(&dev->struct_mutex); - if (ret) { + if (IS_ERR(vma)) { DRM_ERROR("failed to pin boot fb on pipe %d\n", to_intel_crtc(c)->pipe); drm_framebuffer_unreference(c->primary->fb); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 21b04c3..acd0c51 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -190,6 +190,29 @@ intel_dp_max_data_rate(int max_link_clock, int max_lanes) return (max_link_clock * max_lanes * 8) / 10; } +static int +intel_dp_downstream_max_dotclock(struct intel_dp *intel_dp) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct intel_encoder *encoder = &intel_dig_port->base; + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + int max_dotclk = dev_priv->max_dotclk_freq; + int ds_max_dotclk; + + int type = intel_dp->downstream_ports[0] & DP_DS_PORT_TYPE_MASK; + + if (type != DP_DS_PORT_TYPE_VGA) + return max_dotclk; + + ds_max_dotclk = drm_dp_downstream_max_clock(intel_dp->dpcd, + intel_dp->downstream_ports); + + if (ds_max_dotclk != 0) + max_dotclk = min(max_dotclk, ds_max_dotclk); + + return max_dotclk; +} + static enum drm_mode_status intel_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) @@ -199,7 +222,9 @@ intel_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; int target_clock = mode->clock; int max_rate, mode_rate, max_lanes, max_link_clock; - int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; + int max_dotclk; + + max_dotclk = intel_dp_downstream_max_dotclock(intel_dp); if (is_edp(intel_dp) && fixed_mode) { if (mode->hdisplay > fixed_mode->hdisplay) @@ -256,6 +281,8 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev, static void intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, struct intel_dp *intel_dp); +static void +intel_dp_pps_init(struct drm_device *dev, struct intel_dp *intel_dp); static void pps_lock(struct intel_dp *intel_dp) { @@ -463,13 +490,13 @@ typedef bool (*vlv_pipe_check)(struct drm_i915_private *dev_priv, static bool vlv_pipe_has_pp_on(struct drm_i915_private *dev_priv, enum pipe pipe) { - return I915_READ(VLV_PIPE_PP_STATUS(pipe)) & PP_ON; + return I915_READ(PP_STATUS(pipe)) & PP_ON; } static bool vlv_pipe_has_vdd_on(struct drm_i915_private *dev_priv, enum pipe pipe) { - return I915_READ(VLV_PIPE_PP_CONTROL(pipe)) & EDP_FORCE_VDD; + return I915_READ(PP_CONTROL(pipe)) & EDP_FORCE_VDD; } static bool vlv_pipe_any(struct drm_i915_private *dev_priv, @@ -486,7 +513,7 @@ vlv_initial_pps_pipe(struct drm_i915_private *dev_priv, enum pipe pipe; for (pipe = PIPE_A; pipe <= PIPE_B; pipe++) { - u32 port_sel = I915_READ(VLV_PIPE_PP_ON_DELAYS(pipe)) & + u32 port_sel = I915_READ(PP_ON_DELAYS(pipe)) & PANEL_PORT_SELECT_MASK; if (port_sel != PANEL_PORT_SELECT_VLV(port)) @@ -583,30 +610,21 @@ static void intel_pps_get_registers(struct drm_i915_private *dev_priv, struct intel_dp *intel_dp, struct pps_registers *regs) { + int pps_idx = 0; + memset(regs, 0, sizeof(*regs)); - if (IS_BROXTON(dev_priv)) { - int idx = bxt_power_sequencer_idx(intel_dp); - - regs->pp_ctrl = BXT_PP_CONTROL(idx); - regs->pp_stat = BXT_PP_STATUS(idx); - regs->pp_on = BXT_PP_ON_DELAYS(idx); - regs->pp_off = BXT_PP_OFF_DELAYS(idx); - } else if (HAS_PCH_SPLIT(dev_priv)) { - regs->pp_ctrl = PCH_PP_CONTROL; - regs->pp_stat = PCH_PP_STATUS; - regs->pp_on = PCH_PP_ON_DELAYS; - regs->pp_off = PCH_PP_OFF_DELAYS; - regs->pp_div = PCH_PP_DIVISOR; - } else { - enum pipe pipe = vlv_power_sequencer_pipe(intel_dp); + if (IS_BROXTON(dev_priv)) + pps_idx = bxt_power_sequencer_idx(intel_dp); + else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) + pps_idx = vlv_power_sequencer_pipe(intel_dp); - regs->pp_ctrl = VLV_PIPE_PP_CONTROL(pipe); - regs->pp_stat = VLV_PIPE_PP_STATUS(pipe); - regs->pp_on = VLV_PIPE_PP_ON_DELAYS(pipe); - regs->pp_off = VLV_PIPE_PP_OFF_DELAYS(pipe); - regs->pp_div = VLV_PIPE_PP_DIVISOR(pipe); - } + regs->pp_ctrl = PP_CONTROL(pps_idx); + regs->pp_stat = PP_STATUS(pps_idx); + regs->pp_on = PP_ON_DELAYS(pps_idx); + regs->pp_off = PP_OFF_DELAYS(pps_idx); + if (!IS_BROXTON(dev_priv)) + regs->pp_div = PP_DIVISOR(pps_idx); } static i915_reg_t @@ -651,8 +669,8 @@ static int edp_notify_handler(struct notifier_block *this, unsigned long code, i915_reg_t pp_ctrl_reg, pp_div_reg; u32 pp_div; - pp_ctrl_reg = VLV_PIPE_PP_CONTROL(pipe); - pp_div_reg = VLV_PIPE_PP_DIVISOR(pipe); + pp_ctrl_reg = PP_CONTROL(pipe); + pp_div_reg = PP_DIVISOR(pipe); pp_div = I915_READ(pp_div_reg); pp_div &= PP_REFERENCE_DIVIDER_MASK; @@ -1041,10 +1059,10 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) if (WARN_ON(txsize > 20)) return -E2BIG; + WARN_ON(!msg->buffer != !msg->size); + if (msg->buffer) memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size); - else - WARN_ON(msg->size); ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize); if (ret > 0) { @@ -1250,7 +1268,7 @@ intel_dp_aux_fini(struct intel_dp *intel_dp) } static void -intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector) +intel_dp_aux_init(struct intel_dp *intel_dp) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); enum port port = intel_dig_port->port; @@ -1426,6 +1444,44 @@ static void intel_dp_print_rates(struct intel_dp *intel_dp) DRM_DEBUG_KMS("common rates: %s\n", str); } +static void intel_dp_print_hw_revision(struct intel_dp *intel_dp) +{ + uint8_t rev; + int len; + + if ((drm_debug & DRM_UT_KMS) == 0) + return; + + if (!(intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] & + DP_DWN_STRM_PORT_PRESENT)) + return; + + len = drm_dp_dpcd_read(&intel_dp->aux, DP_BRANCH_HW_REV, &rev, 1); + if (len < 0) + return; + + DRM_DEBUG_KMS("sink hw revision: %d.%d\n", (rev & 0xf0) >> 4, rev & 0xf); +} + +static void intel_dp_print_sw_revision(struct intel_dp *intel_dp) +{ + uint8_t rev[2]; + int len; + + if ((drm_debug & DRM_UT_KMS) == 0) + return; + + if (!(intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] & + DP_DWN_STRM_PORT_PRESENT)) + return; + + len = drm_dp_dpcd_read(&intel_dp->aux, DP_BRANCH_SW_REV, &rev, 2); + if (len < 0) + return; + + DRM_DEBUG_KMS("sink sw revision: %d.%d\n", rev[0], rev[1]); +} + static int rate_to_index(int find, const int *rates) { int i = 0; @@ -1447,7 +1503,7 @@ intel_dp_max_link_rate(struct intel_dp *intel_dp) if (WARN_ON(len <= 0)) return 162000; - return rates[rate_to_index(0, rates) - 1]; + return rates[len - 1]; } int intel_dp_rate_select(struct intel_dp *intel_dp, int rate) @@ -1468,9 +1524,24 @@ void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock, } } +static int intel_dp_compute_bpp(struct intel_dp *intel_dp, + struct intel_crtc_state *pipe_config) +{ + int bpp, bpc; + + bpp = pipe_config->pipe_bpp; + bpc = drm_dp_downstream_max_bpc(intel_dp->dpcd, intel_dp->downstream_ports); + + if (bpc > 0) + bpp = min(bpp, 3*bpc); + + return bpp; +} + bool intel_dp_compute_config(struct intel_encoder *encoder, - struct intel_crtc_state *pipe_config) + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); @@ -1533,7 +1604,7 @@ intel_dp_compute_config(struct intel_encoder *encoder, /* Walk through all bpp values. Luckily they're all nicely spaced with 2 * bpc in between. */ - bpp = pipe_config->pipe_bpp; + bpp = intel_dp_compute_bpp(intel_dp, pipe_config); if (is_edp(intel_dp)) { /* Get bpp from vbt only for panels that dont have bpp in edid */ @@ -1647,22 +1718,28 @@ found: } void intel_dp_set_link_params(struct intel_dp *intel_dp, - const struct intel_crtc_state *pipe_config) + int link_rate, uint8_t lane_count, + bool link_mst) { - intel_dp->link_rate = pipe_config->port_clock; - intel_dp->lane_count = pipe_config->lane_count; + intel_dp->link_rate = link_rate; + intel_dp->lane_count = lane_count; + intel_dp->link_mst = link_mst; } -static void intel_dp_prepare(struct intel_encoder *encoder) +static void intel_dp_prepare(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); enum port port = dp_to_dig_port(intel_dp)->port; struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); - const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode; + const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; - intel_dp_set_link_params(intel_dp, crtc->config); + intel_dp_set_link_params(intel_dp, pipe_config->port_clock, + pipe_config->lane_count, + intel_crtc_has_type(pipe_config, + INTEL_OUTPUT_DP_MST)); /* * There are four kinds of DP registers: @@ -1688,7 +1765,7 @@ static void intel_dp_prepare(struct intel_encoder *encoder) /* Handle DP bits in common between all three register formats */ intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; - intel_dp->DP |= DP_PORT_WIDTH(crtc->config->lane_count); + intel_dp->DP |= DP_PORT_WIDTH(pipe_config->lane_count); /* Split out the IBX/CPU vs CPT settings */ @@ -1716,7 +1793,7 @@ static void intel_dp_prepare(struct intel_encoder *encoder) I915_WRITE(TRANS_DP_CTL(crtc->pipe), trans_dp); } else { if (!HAS_PCH_SPLIT(dev) && !IS_VALLEYVIEW(dev) && - !IS_CHERRYVIEW(dev) && crtc->config->limited_color_range) + !IS_CHERRYVIEW(dev) && pipe_config->limited_color_range) intel_dp->DP |= DP_COLOR_RANGE_16_235; if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) @@ -1835,7 +1912,8 @@ static u32 ironlake_get_pp_control(struct intel_dp *intel_dp) lockdep_assert_held(&dev_priv->pps_mutex); control = I915_READ(_pp_ctrl_reg(intel_dp)); - if (!IS_BROXTON(dev)) { + if (WARN_ON(!HAS_DDI(dev_priv) && + (control & PANEL_UNLOCK_MASK) != PANEL_UNLOCK_REGS)) { control &= ~PANEL_UNLOCK_MASK; control |= PANEL_UNLOCK_REGS; } @@ -1956,7 +2034,7 @@ static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp) DRM_DEBUG_KMS("PP_STATUS: 0x%08x PP_CONTROL: 0x%08x\n", I915_READ(pp_stat_reg), I915_READ(pp_ctrl_reg)); - if ((pp & POWER_TARGET_ON) == 0) + if ((pp & PANEL_POWER_ON) == 0) intel_dp->panel_power_off_time = ktime_get_boottime(); power_domain = intel_display_port_aux_power_domain(intel_encoder); @@ -2043,7 +2121,7 @@ static void edp_panel_on(struct intel_dp *intel_dp) POSTING_READ(pp_ctrl_reg); } - pp |= POWER_TARGET_ON; + pp |= PANEL_POWER_ON; if (!IS_GEN5(dev)) pp |= PANEL_POWER_RESET; @@ -2095,7 +2173,7 @@ static void edp_panel_off(struct intel_dp *intel_dp) pp = ironlake_get_pp_control(intel_dp); /* We need to switch off panel power _and_ force vdd, for otherwise some * panels get very unhappy and cease to work. */ - pp &= ~(POWER_TARGET_ON | PANEL_POWER_RESET | EDP_FORCE_VDD | + pp &= ~(PANEL_POWER_ON | PANEL_POWER_RESET | EDP_FORCE_VDD | EDP_BLC_ENABLE); pp_ctrl_reg = _pp_ctrl_reg(intel_dp); @@ -2254,10 +2332,10 @@ static void assert_edp_pll(struct drm_i915_private *dev_priv, bool state) #define assert_edp_pll_enabled(d) assert_edp_pll((d), true) #define assert_edp_pll_disabled(d) assert_edp_pll((d), false) -static void ironlake_edp_pll_on(struct intel_dp *intel_dp) +static void ironlake_edp_pll_on(struct intel_dp *intel_dp, + struct intel_crtc_state *pipe_config) { - struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - struct intel_crtc *crtc = to_intel_crtc(intel_dig_port->base.base.crtc); + struct intel_crtc *crtc = to_intel_crtc(pipe_config->base.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); assert_pipe_disabled(dev_priv, crtc->pipe); @@ -2265,11 +2343,11 @@ static void ironlake_edp_pll_on(struct intel_dp *intel_dp) assert_edp_pll_disabled(dev_priv); DRM_DEBUG_KMS("enabling eDP PLL for clock %d\n", - crtc->config->port_clock); + pipe_config->port_clock); intel_dp->DP &= ~DP_PLL_FREQ_MASK; - if (crtc->config->port_clock == 162000) + if (pipe_config->port_clock == 162000) intel_dp->DP |= DP_PLL_FREQ_162MHZ; else intel_dp->DP |= DP_PLL_FREQ_270MHZ; @@ -2478,16 +2556,17 @@ static void intel_dp_get_config(struct intel_encoder *encoder, } } -static void intel_disable_dp(struct intel_encoder *encoder) +static void intel_disable_dp(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); - struct drm_device *dev = encoder->base.dev; - struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); - if (crtc->config->has_audio) + if (old_crtc_state->has_audio) intel_audio_codec_disable(encoder); - if (HAS_PSR(dev) && !HAS_DDI(dev)) + if (HAS_PSR(dev_priv) && !HAS_DDI(dev_priv)) intel_psr_disable(intel_dp); /* Make sure the panel is off before trying to change the mode. But also @@ -2498,11 +2577,13 @@ static void intel_disable_dp(struct intel_encoder *encoder) intel_edp_panel_off(intel_dp); /* disable the port before the pipe on g4x */ - if (INTEL_INFO(dev)->gen < 5) + if (INTEL_GEN(dev_priv) < 5) intel_dp_link_down(intel_dp); } -static void ilk_post_disable_dp(struct intel_encoder *encoder) +static void ilk_post_disable_dp(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); enum port port = dp_to_dig_port(intel_dp)->port; @@ -2514,14 +2595,18 @@ static void ilk_post_disable_dp(struct intel_encoder *encoder) ironlake_edp_pll_off(intel_dp); } -static void vlv_post_disable_dp(struct intel_encoder *encoder) +static void vlv_post_disable_dp(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); intel_dp_link_down(intel_dp); } -static void chv_post_disable_dp(struct intel_encoder *encoder) +static void chv_post_disable_dp(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); struct drm_device *dev = encoder->base.dev; @@ -2547,6 +2632,10 @@ _intel_dp_set_link_train(struct intel_dp *intel_dp, struct drm_i915_private *dev_priv = to_i915(dev); enum port port = intel_dig_port->port; + if (dp_train_pat & DP_TRAINING_PATTERN_MASK) + DRM_DEBUG_KMS("Using DP training pattern TPS%d\n", + dp_train_pat & DP_TRAINING_PATTERN_MASK); + if (HAS_DDI(dev)) { uint32_t temp = I915_READ(DP_TP_CTL(port)); @@ -2588,7 +2677,7 @@ _intel_dp_set_link_train(struct intel_dp *intel_dp, *DP |= DP_LINK_TRAIN_PAT_2_CPT; break; case DP_TRAINING_PATTERN_3: - DRM_ERROR("DP training pattern 3 not supported\n"); + DRM_DEBUG_KMS("TPS3 not supported, using TPS2 instead\n"); *DP |= DP_LINK_TRAIN_PAT_2_CPT; break; } @@ -2613,7 +2702,7 @@ _intel_dp_set_link_train(struct intel_dp *intel_dp, if (IS_CHERRYVIEW(dev)) { *DP |= DP_LINK_TRAIN_PAT_3_CHV; } else { - DRM_ERROR("DP training pattern 3 not supported\n"); + DRM_DEBUG_KMS("TPS3 not supported, using TPS2 instead\n"); *DP |= DP_LINK_TRAIN_PAT_2; } break; @@ -2621,19 +2710,15 @@ _intel_dp_set_link_train(struct intel_dp *intel_dp, } } -static void intel_dp_enable_port(struct intel_dp *intel_dp) +static void intel_dp_enable_port(struct intel_dp *intel_dp, + struct intel_crtc_state *old_crtc_state) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *crtc = - to_intel_crtc(dp_to_dig_port(intel_dp)->base.base.crtc); /* enable with pattern 1 (as per spec) */ - _intel_dp_set_link_train(intel_dp, &intel_dp->DP, - DP_TRAINING_PATTERN_1); - I915_WRITE(intel_dp->output_reg, intel_dp->DP); - POSTING_READ(intel_dp->output_reg); + intel_dp_program_link_training_pattern(intel_dp, DP_TRAINING_PATTERN_1); /* * Magic for VLV/CHV. We _must_ first set up the register @@ -2642,14 +2727,15 @@ static void intel_dp_enable_port(struct intel_dp *intel_dp) * fail when the power sequencer is freshly used for this port. */ intel_dp->DP |= DP_PORT_EN; - if (crtc->config->has_audio) + if (old_crtc_state->has_audio) intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE; I915_WRITE(intel_dp->output_reg, intel_dp->DP); POSTING_READ(intel_dp->output_reg); } -static void intel_enable_dp(struct intel_encoder *encoder) +static void intel_enable_dp(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); struct drm_device *dev = encoder->base.dev; @@ -2666,7 +2752,7 @@ static void intel_enable_dp(struct intel_encoder *encoder) if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) vlv_init_panel_power_sequencer(intel_dp); - intel_dp_enable_port(intel_dp); + intel_dp_enable_port(intel_dp, pipe_config); edp_panel_vdd_on(intel_dp); edp_panel_on(intel_dp); @@ -2678,7 +2764,7 @@ static void intel_enable_dp(struct intel_encoder *encoder) unsigned int lane_mask = 0x0; if (IS_CHERRYVIEW(dev)) - lane_mask = intel_dp_unused_lane_mask(crtc->config->lane_count); + lane_mask = intel_dp_unused_lane_mask(pipe_config->lane_count); vlv_wait_port_ready(dev_priv, dp_to_dig_port(intel_dp), lane_mask); @@ -2688,22 +2774,26 @@ static void intel_enable_dp(struct intel_encoder *encoder) intel_dp_start_link_train(intel_dp); intel_dp_stop_link_train(intel_dp); - if (crtc->config->has_audio) { + if (pipe_config->has_audio) { DRM_DEBUG_DRIVER("Enabling DP audio on pipe %c\n", pipe_name(pipe)); intel_audio_codec_enable(encoder); } } -static void g4x_enable_dp(struct intel_encoder *encoder) +static void g4x_enable_dp(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); - intel_enable_dp(encoder); + intel_enable_dp(encoder, pipe_config); intel_edp_backlight_on(intel_dp); } -static void vlv_enable_dp(struct intel_encoder *encoder) +static void vlv_enable_dp(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); @@ -2711,16 +2801,18 @@ static void vlv_enable_dp(struct intel_encoder *encoder) intel_psr_enable(intel_dp); } -static void g4x_pre_enable_dp(struct intel_encoder *encoder) +static void g4x_pre_enable_dp(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); enum port port = dp_to_dig_port(intel_dp)->port; - intel_dp_prepare(encoder); + intel_dp_prepare(encoder, pipe_config); /* Only ilk+ has port A */ if (port == PORT_A) - ironlake_edp_pll_on(intel_dp); + ironlake_edp_pll_on(intel_dp, pipe_config); } static void vlv_detach_power_sequencer(struct intel_dp *intel_dp) @@ -2728,7 +2820,7 @@ static void vlv_detach_power_sequencer(struct intel_dp *intel_dp) struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct drm_i915_private *dev_priv = to_i915(intel_dig_port->base.base.dev); enum pipe pipe = intel_dp->pps_pipe; - i915_reg_t pp_on_reg = VLV_PIPE_PP_ON_DELAYS(pipe); + i915_reg_t pp_on_reg = PP_ON_DELAYS(pipe); edp_panel_vdd_off_sync(intel_dp); @@ -2826,38 +2918,48 @@ static void vlv_init_panel_power_sequencer(struct intel_dp *intel_dp) intel_dp_init_panel_power_sequencer_registers(dev, intel_dp); } -static void vlv_pre_enable_dp(struct intel_encoder *encoder) +static void vlv_pre_enable_dp(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { vlv_phy_pre_encoder_enable(encoder); - intel_enable_dp(encoder); + intel_enable_dp(encoder, pipe_config); } -static void vlv_dp_pre_pll_enable(struct intel_encoder *encoder) +static void vlv_dp_pre_pll_enable(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { - intel_dp_prepare(encoder); + intel_dp_prepare(encoder, pipe_config); vlv_phy_pre_pll_enable(encoder); } -static void chv_pre_enable_dp(struct intel_encoder *encoder) +static void chv_pre_enable_dp(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { chv_phy_pre_encoder_enable(encoder); - intel_enable_dp(encoder); + intel_enable_dp(encoder, pipe_config); /* Second common lane will stay alive on its own now */ chv_phy_release_cl2_override(encoder); } -static void chv_dp_pre_pll_enable(struct intel_encoder *encoder) +static void chv_dp_pre_pll_enable(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { - intel_dp_prepare(encoder); + intel_dp_prepare(encoder, pipe_config); chv_phy_pre_pll_enable(encoder); } -static void chv_dp_post_pll_disable(struct intel_encoder *encoder) +static void chv_dp_post_pll_disable(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { chv_phy_post_pll_disable(encoder); } @@ -3395,84 +3497,67 @@ intel_dp_link_down(struct intel_dp *intel_dp) } static bool -intel_dp_get_dpcd(struct intel_dp *intel_dp) +intel_dp_read_dpcd(struct intel_dp *intel_dp) { - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - struct drm_device *dev = dig_port->base.base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - if (drm_dp_dpcd_read(&intel_dp->aux, 0x000, intel_dp->dpcd, sizeof(intel_dp->dpcd)) < 0) return false; /* aux transfer failed */ DRM_DEBUG_KMS("DPCD: %*ph\n", (int) sizeof(intel_dp->dpcd), intel_dp->dpcd); - if (intel_dp->dpcd[DP_DPCD_REV] == 0) - return false; /* DPCD not present */ + return intel_dp->dpcd[DP_DPCD_REV] != 0; +} - if (drm_dp_dpcd_read(&intel_dp->aux, DP_SINK_COUNT, - &intel_dp->sink_count, 1) < 0) - return false; +static bool +intel_edp_init_dpcd(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = + to_i915(dp_to_dig_port(intel_dp)->base.base.dev); - /* - * Sink count can change between short pulse hpd hence - * a member variable in intel_dp will track any changes - * between short pulse interrupts. - */ - intel_dp->sink_count = DP_GET_SINK_COUNT(intel_dp->sink_count); + /* this function is meant to be called only once */ + WARN_ON(intel_dp->dpcd[DP_DPCD_REV] != 0); - /* - * SINK_COUNT == 0 and DOWNSTREAM_PORT_PRESENT == 1 implies that - * a dongle is present but no display. Unless we require to know - * if a dongle is present or not, we don't need to update - * downstream port information. So, an early return here saves - * time from performing other operations which are not required. - */ - if (!is_edp(intel_dp) && !intel_dp->sink_count) + if (!intel_dp_read_dpcd(intel_dp)) return false; - /* Check if the panel supports PSR */ - memset(intel_dp->psr_dpcd, 0, sizeof(intel_dp->psr_dpcd)); - if (is_edp(intel_dp)) { - drm_dp_dpcd_read(&intel_dp->aux, DP_PSR_SUPPORT, - intel_dp->psr_dpcd, - sizeof(intel_dp->psr_dpcd)); - if (intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED) { - dev_priv->psr.sink_support = true; - DRM_DEBUG_KMS("Detected EDP PSR Panel.\n"); - } - - if (INTEL_INFO(dev)->gen >= 9 && - (intel_dp->psr_dpcd[0] & DP_PSR2_IS_SUPPORTED)) { - uint8_t frame_sync_cap; - - dev_priv->psr.sink_support = true; - drm_dp_dpcd_read(&intel_dp->aux, - DP_SINK_DEVICE_AUX_FRAME_SYNC_CAP, - &frame_sync_cap, 1); - dev_priv->psr.aux_frame_sync = frame_sync_cap ? true : false; - /* PSR2 needs frame sync as well */ - dev_priv->psr.psr2_support = dev_priv->psr.aux_frame_sync; - DRM_DEBUG_KMS("PSR2 %s on sink", - dev_priv->psr.psr2_support ? "supported" : "not supported"); - } + if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) + dev_priv->no_aux_handshake = intel_dp->dpcd[DP_MAX_DOWNSPREAD] & + DP_NO_AUX_HANDSHAKE_LINK_TRAINING; - /* Read the eDP Display control capabilities registers */ - memset(intel_dp->edp_dpcd, 0, sizeof(intel_dp->edp_dpcd)); - if ((intel_dp->dpcd[DP_EDP_CONFIGURATION_CAP] & DP_DPCD_DISPLAY_CONTROL_CAPABLE) && - (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_DPCD_REV, - intel_dp->edp_dpcd, sizeof(intel_dp->edp_dpcd)) == - sizeof(intel_dp->edp_dpcd))) - DRM_DEBUG_KMS("EDP DPCD : %*ph\n", (int) sizeof(intel_dp->edp_dpcd), - intel_dp->edp_dpcd); - } - - DRM_DEBUG_KMS("Display Port TPS3 support: source %s, sink %s\n", - yesno(intel_dp_source_supports_hbr2(intel_dp)), - yesno(drm_dp_tps3_supported(intel_dp->dpcd))); + /* Check if the panel supports PSR */ + drm_dp_dpcd_read(&intel_dp->aux, DP_PSR_SUPPORT, + intel_dp->psr_dpcd, + sizeof(intel_dp->psr_dpcd)); + if (intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED) { + dev_priv->psr.sink_support = true; + DRM_DEBUG_KMS("Detected EDP PSR Panel.\n"); + } + + if (INTEL_GEN(dev_priv) >= 9 && + (intel_dp->psr_dpcd[0] & DP_PSR2_IS_SUPPORTED)) { + uint8_t frame_sync_cap; + + dev_priv->psr.sink_support = true; + drm_dp_dpcd_read(&intel_dp->aux, + DP_SINK_DEVICE_AUX_FRAME_SYNC_CAP, + &frame_sync_cap, 1); + dev_priv->psr.aux_frame_sync = frame_sync_cap ? true : false; + /* PSR2 needs frame sync as well */ + dev_priv->psr.psr2_support = dev_priv->psr.aux_frame_sync; + DRM_DEBUG_KMS("PSR2 %s on sink", + dev_priv->psr.psr2_support ? "supported" : "not supported"); + } + + /* Read the eDP Display control capabilities registers */ + if ((intel_dp->dpcd[DP_EDP_CONFIGURATION_CAP] & DP_DPCD_DISPLAY_CONTROL_CAPABLE) && + drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_DPCD_REV, + intel_dp->edp_dpcd, sizeof(intel_dp->edp_dpcd) == + sizeof(intel_dp->edp_dpcd))) + DRM_DEBUG_KMS("EDP DPCD : %*ph\n", (int) sizeof(intel_dp->edp_dpcd), + intel_dp->edp_dpcd); /* Intermediate frequency support */ - if (is_edp(intel_dp) && (intel_dp->edp_dpcd[0] >= 0x03)) { /* eDp v1.4 or higher */ + if (intel_dp->edp_dpcd[0] >= 0x03) { /* eDp v1.4 or higher */ __le16 sink_rates[DP_MAX_SUPPORTED_RATES]; int i; @@ -3491,7 +3576,36 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) intel_dp->num_sink_rates = i; } - intel_dp_print_rates(intel_dp); + return true; +} + + +static bool +intel_dp_get_dpcd(struct intel_dp *intel_dp) +{ + if (!intel_dp_read_dpcd(intel_dp)) + return false; + + if (drm_dp_dpcd_read(&intel_dp->aux, DP_SINK_COUNT, + &intel_dp->sink_count, 1) < 0) + return false; + + /* + * Sink count can change between short pulse hpd hence + * a member variable in intel_dp will track any changes + * between short pulse interrupts. + */ + intel_dp->sink_count = DP_GET_SINK_COUNT(intel_dp->sink_count); + + /* + * SINK_COUNT == 0 and DOWNSTREAM_PORT_PRESENT == 1 implies that + * a dongle is present but no display. Unless we require to know + * if a dongle is present or not, we don't need to update + * downstream port information. So, an early return here saves + * time from performing other operations which are not required. + */ + if (!is_edp(intel_dp) && !intel_dp->sink_count) + return false; if (!(intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT)) @@ -3526,7 +3640,7 @@ intel_dp_probe_oui(struct intel_dp *intel_dp) } static bool -intel_dp_probe_mst(struct intel_dp *intel_dp) +intel_dp_can_mst(struct intel_dp *intel_dp) { u8 buf[1]; @@ -3539,18 +3653,30 @@ intel_dp_probe_mst(struct intel_dp *intel_dp) if (intel_dp->dpcd[DP_DPCD_REV] < 0x12) return false; - if (drm_dp_dpcd_read(&intel_dp->aux, DP_MSTM_CAP, buf, 1)) { - if (buf[0] & DP_MST_CAP) { - DRM_DEBUG_KMS("Sink is MST capable\n"); - intel_dp->is_mst = true; - } else { - DRM_DEBUG_KMS("Sink is not MST capable\n"); - intel_dp->is_mst = false; - } - } + if (drm_dp_dpcd_read(&intel_dp->aux, DP_MSTM_CAP, buf, 1) != 1) + return false; + + return buf[0] & DP_MST_CAP; +} + +static void +intel_dp_configure_mst(struct intel_dp *intel_dp) +{ + if (!i915.enable_dp_mst) + return; - drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst); - return intel_dp->is_mst; + if (!intel_dp->can_mst) + return; + + intel_dp->is_mst = intel_dp_can_mst(intel_dp); + + if (intel_dp->is_mst) + DRM_DEBUG_KMS("Sink is MST capable\n"); + else + DRM_DEBUG_KMS("Sink is not MST capable\n"); + + drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, + intel_dp->is_mst); } static int intel_dp_sink_crc_stop(struct intel_dp *intel_dp) @@ -3909,7 +4035,7 @@ static bool intel_dp_short_pulse(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); - u8 sink_irq_vector; + u8 sink_irq_vector = 0; u8 old_sink_count = intel_dp->sink_count; bool ret; @@ -3936,7 +4062,8 @@ intel_dp_short_pulse(struct intel_dp *intel_dp) /* Try to read the source of the interrupt */ if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 && - intel_dp_get_sink_irq(intel_dp, &sink_irq_vector)) { + intel_dp_get_sink_irq(intel_dp, &sink_irq_vector) && + sink_irq_vector != 0) { /* Clear interrupt source */ drm_dp_dpcd_writeb(&intel_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR, @@ -3980,6 +4107,9 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp) connector_status_connected : connector_status_disconnected; } + if (intel_dp_can_mst(intel_dp)) + return connector_status_connected; + /* If no HPD, poke DDC gently */ if (drm_probe_ddc(&intel_dp->aux.ddc)) return connector_status_connected; @@ -4148,7 +4278,7 @@ static bool bxt_digital_port_connected(struct drm_i915_private *dev_priv, * * Return %true if @port is connected, %false otherwise. */ -bool intel_digital_port_connected(struct drm_i915_private *dev_priv, +static bool intel_digital_port_connected(struct drm_i915_private *dev_priv, struct intel_digital_port *port) { if (HAS_PCH_IBX(dev_priv)) @@ -4217,8 +4347,7 @@ intel_dp_long_pulse(struct intel_connector *intel_connector) struct drm_device *dev = connector->dev; enum drm_connector_status status; enum intel_display_power_domain power_domain; - bool ret; - u8 sink_irq_vector; + u8 sink_irq_vector = 0; power_domain = intel_display_port_aux_power_domain(intel_encoder); intel_display_power_get(to_i915(dev), power_domain); @@ -4252,10 +4381,20 @@ intel_dp_long_pulse(struct intel_connector *intel_connector) if (intel_encoder->type != INTEL_OUTPUT_EDP) intel_encoder->type = INTEL_OUTPUT_DP; + DRM_DEBUG_KMS("Display Port TPS3 support: source %s, sink %s\n", + yesno(intel_dp_source_supports_hbr2(intel_dp)), + yesno(drm_dp_tps3_supported(intel_dp->dpcd))); + + intel_dp_print_rates(intel_dp); + intel_dp_probe_oui(intel_dp); - ret = intel_dp_probe_mst(intel_dp); - if (ret) { + intel_dp_print_hw_revision(intel_dp); + intel_dp_print_sw_revision(intel_dp); + + intel_dp_configure_mst(intel_dp); + + if (intel_dp->is_mst) { /* * If we are in MST mode then this connector * won't appear connected or have anything @@ -4290,7 +4429,8 @@ intel_dp_long_pulse(struct intel_connector *intel_connector) /* Try to read the source of the interrupt */ if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 && - intel_dp_get_sink_irq(intel_dp, &sink_irq_vector)) { + intel_dp_get_sink_irq(intel_dp, &sink_irq_vector) && + sink_irq_vector != 0) { /* Clear interrupt source */ drm_dp_dpcd_writeb(&intel_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR, @@ -4630,13 +4770,8 @@ void intel_dp_encoder_reset(struct drm_encoder *encoder) pps_lock(intel_dp); - /* - * Read out the current power sequencer assignment, - * in case the BIOS did something with it. - */ - if (IS_VALLEYVIEW(encoder->dev) || IS_CHERRYVIEW(encoder->dev)) - vlv_initial_power_sequencer_setup(intel_dp); - + /* Reinit the power sequencer, in case BIOS did something with it. */ + intel_dp_pps_init(encoder->dev, intel_dp); intel_edp_panel_vdd_sanitize(intel_dp); pps_unlock(intel_dp); @@ -4984,9 +5119,21 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, I915_READ(regs.pp_div)); } +static void intel_dp_pps_init(struct drm_device *dev, + struct intel_dp *intel_dp) +{ + if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) { + vlv_initial_power_sequencer_setup(intel_dp); + } else { + intel_dp_init_panel_power_sequencer(dev, intel_dp); + intel_dp_init_panel_power_sequencer_registers(dev, intel_dp); + } +} + /** * intel_dp_set_drrs_state - program registers for RR switch to take effect - * @dev: DRM device + * @dev_priv: i915 device + * @crtc_state: a pointer to the active intel_crtc_state * @refresh_rate: RR to be programmed * * This function gets called when refresh rate (RR) has to be changed from @@ -4996,14 +5143,14 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, * * The caller of this function needs to take a lock on dev_priv->drrs. */ -static void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate) +static void intel_dp_set_drrs_state(struct drm_i915_private *dev_priv, + struct intel_crtc_state *crtc_state, + int refresh_rate) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_encoder *encoder; struct intel_digital_port *dig_port = NULL; struct intel_dp *intel_dp = dev_priv->drrs.dp; - struct intel_crtc_state *config = NULL; - struct intel_crtc *intel_crtc = NULL; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc); enum drrs_refresh_rate_type index = DRRS_HIGH_RR; if (refresh_rate <= 0) { @@ -5030,8 +5177,6 @@ static void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate) return; } - config = intel_crtc->config; - if (dev_priv->drrs.type < SEAMLESS_DRRS_SUPPORT) { DRM_DEBUG_KMS("Only Seamless DRRS supported.\n"); return; @@ -5047,12 +5192,12 @@ static void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate) return; } - if (!intel_crtc->active) { + if (!crtc_state->base.active) { DRM_DEBUG_KMS("eDP encoder disabled. CRTC not Active\n"); return; } - if (INTEL_INFO(dev)->gen >= 8 && !IS_CHERRYVIEW(dev)) { + if (INTEL_GEN(dev_priv) >= 8 && !IS_CHERRYVIEW(dev_priv)) { switch (index) { case DRRS_HIGH_RR: intel_dp_set_m_n(intel_crtc, M1_N1); @@ -5064,18 +5209,18 @@ static void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate) default: DRM_ERROR("Unsupported refreshrate type\n"); } - } else if (INTEL_INFO(dev)->gen > 6) { - i915_reg_t reg = PIPECONF(intel_crtc->config->cpu_transcoder); + } else if (INTEL_GEN(dev_priv) > 6) { + i915_reg_t reg = PIPECONF(crtc_state->cpu_transcoder); u32 val; val = I915_READ(reg); if (index > DRRS_HIGH_RR) { - if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) val |= PIPECONF_EDP_RR_MODE_SWITCH_VLV; else val |= PIPECONF_EDP_RR_MODE_SWITCH; } else { - if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) val &= ~PIPECONF_EDP_RR_MODE_SWITCH_VLV; else val &= ~PIPECONF_EDP_RR_MODE_SWITCH; @@ -5091,18 +5236,17 @@ static void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate) /** * intel_edp_drrs_enable - init drrs struct if supported * @intel_dp: DP struct + * @crtc_state: A pointer to the active crtc state. * * Initializes frontbuffer_bits and drrs.dp */ -void intel_edp_drrs_enable(struct intel_dp *intel_dp) +void intel_edp_drrs_enable(struct intel_dp *intel_dp, + struct intel_crtc_state *crtc_state) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - struct drm_crtc *crtc = dig_port->base.base.crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - if (!intel_crtc->config->has_drrs) { + if (!crtc_state->has_drrs) { DRM_DEBUG_KMS("Panel doesn't support DRRS\n"); return; } @@ -5124,17 +5268,16 @@ unlock: /** * intel_edp_drrs_disable - Disable DRRS * @intel_dp: DP struct + * @old_crtc_state: Pointer to old crtc_state. * */ -void intel_edp_drrs_disable(struct intel_dp *intel_dp) +void intel_edp_drrs_disable(struct intel_dp *intel_dp, + struct intel_crtc_state *old_crtc_state) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - struct drm_crtc *crtc = dig_port->base.base.crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - if (!intel_crtc->config->has_drrs) + if (!old_crtc_state->has_drrs) return; mutex_lock(&dev_priv->drrs.mutex); @@ -5144,9 +5287,8 @@ void intel_edp_drrs_disable(struct intel_dp *intel_dp) } if (dev_priv->drrs.refresh_rate_type == DRRS_LOW_RR) - intel_dp_set_drrs_state(&dev_priv->drm, - intel_dp->attached_connector->panel. - fixed_mode->vrefresh); + intel_dp_set_drrs_state(dev_priv, old_crtc_state, + intel_dp->attached_connector->panel.fixed_mode->vrefresh); dev_priv->drrs.dp = NULL; mutex_unlock(&dev_priv->drrs.mutex); @@ -5175,10 +5317,12 @@ static void intel_edp_drrs_downclock_work(struct work_struct *work) if (dev_priv->drrs.busy_frontbuffer_bits) goto unlock; - if (dev_priv->drrs.refresh_rate_type != DRRS_LOW_RR) - intel_dp_set_drrs_state(&dev_priv->drm, - intel_dp->attached_connector->panel. - downclock_mode->vrefresh); + if (dev_priv->drrs.refresh_rate_type != DRRS_LOW_RR) { + struct drm_crtc *crtc = dp_to_dig_port(intel_dp)->base.base.crtc; + + intel_dp_set_drrs_state(dev_priv, to_intel_crtc(crtc)->config, + intel_dp->attached_connector->panel.downclock_mode->vrefresh); + } unlock: mutex_unlock(&dev_priv->drrs.mutex); @@ -5186,7 +5330,7 @@ unlock: /** * intel_edp_drrs_invalidate - Disable Idleness DRRS - * @dev: DRM device + * @dev_priv: i915 device * @frontbuffer_bits: frontbuffer plane tracking bits * * This function gets called everytime rendering on the given planes start. @@ -5194,10 +5338,9 @@ unlock: * * Dirty frontbuffers relevant to DRRS are tracked in busy_frontbuffer_bits. */ -void intel_edp_drrs_invalidate(struct drm_device *dev, - unsigned frontbuffer_bits) +void intel_edp_drrs_invalidate(struct drm_i915_private *dev_priv, + unsigned int frontbuffer_bits) { - struct drm_i915_private *dev_priv = to_i915(dev); struct drm_crtc *crtc; enum pipe pipe; @@ -5220,16 +5363,15 @@ void intel_edp_drrs_invalidate(struct drm_device *dev, /* invalidate means busy screen hence upclock */ if (frontbuffer_bits && dev_priv->drrs.refresh_rate_type == DRRS_LOW_RR) - intel_dp_set_drrs_state(&dev_priv->drm, - dev_priv->drrs.dp->attached_connector->panel. - fixed_mode->vrefresh); + intel_dp_set_drrs_state(dev_priv, to_intel_crtc(crtc)->config, + dev_priv->drrs.dp->attached_connector->panel.fixed_mode->vrefresh); mutex_unlock(&dev_priv->drrs.mutex); } /** * intel_edp_drrs_flush - Restart Idleness DRRS - * @dev: DRM device + * @dev_priv: i915 device * @frontbuffer_bits: frontbuffer plane tracking bits * * This function gets called every time rendering on the given planes has @@ -5239,10 +5381,9 @@ void intel_edp_drrs_invalidate(struct drm_device *dev, * * Dirty frontbuffers relevant to DRRS are tracked in busy_frontbuffer_bits. */ -void intel_edp_drrs_flush(struct drm_device *dev, - unsigned frontbuffer_bits) +void intel_edp_drrs_flush(struct drm_i915_private *dev_priv, + unsigned int frontbuffer_bits) { - struct drm_i915_private *dev_priv = to_i915(dev); struct drm_crtc *crtc; enum pipe pipe; @@ -5265,9 +5406,8 @@ void intel_edp_drrs_flush(struct drm_device *dev, /* flush means busy screen hence upclock */ if (frontbuffer_bits && dev_priv->drrs.refresh_rate_type == DRRS_LOW_RR) - intel_dp_set_drrs_state(&dev_priv->drm, - dev_priv->drrs.dp->attached_connector->panel. - fixed_mode->vrefresh); + intel_dp_set_drrs_state(dev_priv, to_intel_crtc(crtc)->config, + dev_priv->drrs.dp->attached_connector->panel.fixed_mode->vrefresh); /* * flush also means no more activity hence schedule downclock, if all @@ -5400,27 +5540,15 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, pps_lock(intel_dp); intel_dp_init_panel_power_timestamps(intel_dp); - - if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) { - vlv_initial_power_sequencer_setup(intel_dp); - } else { - intel_dp_init_panel_power_sequencer(dev, intel_dp); - intel_dp_init_panel_power_sequencer_registers(dev, intel_dp); - } - + intel_dp_pps_init(dev, intel_dp); intel_edp_panel_vdd_sanitize(intel_dp); pps_unlock(intel_dp); /* Cache DPCD and EDID for edp. */ - has_dpcd = intel_dp_get_dpcd(intel_dp); + has_dpcd = intel_edp_init_dpcd(intel_dp); - if (has_dpcd) { - if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) - dev_priv->no_aux_handshake = - intel_dp->dpcd[DP_MAX_DOWNSPREAD] & - DP_NO_AUX_HANDSHAKE_LINK_TRAINING; - } else { + if (!has_dpcd) { /* if this fails, presume the device is a ghost */ DRM_INFO("failed to retrieve link info, disabling eDP\n"); goto out_vdd_off; @@ -5576,7 +5704,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, connector->interlace_allowed = true; connector->doublescan_allowed = 0; - intel_dp_aux_init(intel_dp, intel_connector); + intel_dp_aux_init(intel_dp); INIT_DELAYED_WORK(&intel_dp->panel_vdd_work, edp_panel_vdd_work); diff --git a/drivers/gpu/drm/i915/intel_dp_link_training.c b/drivers/gpu/drm/i915/intel_dp_link_training.c index 60fb39c..c438b02 100644 --- a/drivers/gpu/drm/i915/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/intel_dp_link_training.c @@ -24,6 +24,15 @@ #include "intel_drv.h" static void +intel_dp_dump_link_status(const uint8_t link_status[DP_LINK_STATUS_SIZE]) +{ + + DRM_DEBUG_KMS("ln0_1:0x%x ln2_3:0x%x align:0x%x sink:0x%x adj_req0_1:0x%x adj_req2_3:0x%x", + link_status[0], link_status[1], link_status[2], + link_status[3], link_status[4], link_status[5]); +} + +static void intel_get_adjust_train(struct intel_dp *intel_dp, const uint8_t link_status[DP_LINK_STATUS_SIZE]) { @@ -103,13 +112,24 @@ intel_dp_update_link_train(struct intel_dp *intel_dp) return ret == intel_dp->lane_count; } +static bool intel_dp_link_max_vswing_reached(struct intel_dp *intel_dp) +{ + int lane; + + for (lane = 0; lane < intel_dp->lane_count; lane++) + if ((intel_dp->train_set[lane] & + DP_TRAIN_MAX_SWING_REACHED) == 0) + return false; + + return true; +} + /* Enable corresponding port and start training pattern 1 */ -static void +static bool intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp) { - int i; uint8_t voltage; - int voltage_tries, loop_tries; + int voltage_tries, max_vswing_tries; uint8_t link_config[2]; uint8_t link_bw, rate_select; @@ -125,6 +145,7 @@ intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp) if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_BW_SET, link_config, 2); + if (intel_dp->num_sink_rates) drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_RATE_SET, &rate_select, 1); @@ -140,60 +161,54 @@ intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp) DP_TRAINING_PATTERN_1 | DP_LINK_SCRAMBLING_DISABLE)) { DRM_ERROR("failed to enable link training\n"); - return; + return false; } - voltage = 0xff; - voltage_tries = 0; - loop_tries = 0; + voltage_tries = 1; + max_vswing_tries = 0; for (;;) { uint8_t link_status[DP_LINK_STATUS_SIZE]; drm_dp_link_train_clock_recovery_delay(intel_dp->dpcd); + if (!intel_dp_get_link_status(intel_dp, link_status)) { DRM_ERROR("failed to get link status\n"); - break; + return false; } if (drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) { DRM_DEBUG_KMS("clock recovery OK\n"); - break; + return true; } - /* Check to see if we've tried the max voltage */ - for (i = 0; i < intel_dp->lane_count; i++) - if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) - break; - if (i == intel_dp->lane_count) { - ++loop_tries; - if (loop_tries == 5) { - DRM_ERROR("too many full retries, give up\n"); - break; - } - intel_dp_reset_link_train(intel_dp, - DP_TRAINING_PATTERN_1 | - DP_LINK_SCRAMBLING_DISABLE); - voltage_tries = 0; - continue; + if (voltage_tries == 5) { + DRM_DEBUG_KMS("Same voltage tried 5 times\n"); + return false; + } + + if (max_vswing_tries == 1) { + DRM_DEBUG_KMS("Max Voltage Swing reached\n"); + return false; } - /* Check to see if we've tried the same voltage 5 times */ - if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { - ++voltage_tries; - if (voltage_tries == 5) { - DRM_ERROR("too many voltage retries, give up\n"); - break; - } - } else - voltage_tries = 0; voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; /* Update training set as requested by target */ intel_get_adjust_train(intel_dp, link_status); if (!intel_dp_update_link_train(intel_dp)) { DRM_ERROR("failed to update link training\n"); - break; + return false; } + + if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == + voltage) + ++voltage_tries; + else + voltage_tries = 1; + + if (intel_dp_link_max_vswing_reached(intel_dp)) + ++max_vswing_tries; + } } @@ -229,12 +244,12 @@ static u32 intel_dp_training_pattern(struct intel_dp *intel_dp) return training_pattern; } -static void +static bool intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp) { - bool channel_eq = false; - int tries, cr_tries; + int tries; u32 training_pattern; + uint8_t link_status[DP_LINK_STATUS_SIZE]; training_pattern = intel_dp_training_pattern(intel_dp); @@ -243,19 +258,11 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp) training_pattern | DP_LINK_SCRAMBLING_DISABLE)) { DRM_ERROR("failed to start channel equalization\n"); - return; + return false; } - tries = 0; - cr_tries = 0; - channel_eq = false; - for (;;) { - uint8_t link_status[DP_LINK_STATUS_SIZE]; - - if (cr_tries > 5) { - DRM_ERROR("failed to train DP, aborting\n"); - break; - } + intel_dp->channel_eq_status = false; + for (tries = 0; tries < 5; tries++) { drm_dp_link_train_channel_eq_delay(intel_dp->dpcd); if (!intel_dp_get_link_status(intel_dp, link_status)) { @@ -266,44 +273,38 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp) /* Make sure clock is still ok */ if (!drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) { - intel_dp_link_training_clock_recovery(intel_dp); - intel_dp_set_link_train(intel_dp, - training_pattern | - DP_LINK_SCRAMBLING_DISABLE); - cr_tries++; - continue; + intel_dp_dump_link_status(link_status); + DRM_DEBUG_KMS("Clock recovery check failed, cannot " + "continue channel equalization\n"); + break; } if (drm_dp_channel_eq_ok(link_status, intel_dp->lane_count)) { - channel_eq = true; + intel_dp->channel_eq_status = true; + DRM_DEBUG_KMS("Channel EQ done. DP Training " + "successful\n"); break; } - /* Try 5 times, then try clock recovery if that fails */ - if (tries > 5) { - intel_dp_link_training_clock_recovery(intel_dp); - intel_dp_set_link_train(intel_dp, - training_pattern | - DP_LINK_SCRAMBLING_DISABLE); - tries = 0; - cr_tries++; - continue; - } - /* Update training set as requested by target */ intel_get_adjust_train(intel_dp, link_status); if (!intel_dp_update_link_train(intel_dp)) { DRM_ERROR("failed to update link training\n"); break; } - ++tries; + } + + /* Try 5 times, else fail and try at lower BW */ + if (tries == 5) { + intel_dp_dump_link_status(link_status); + DRM_DEBUG_KMS("Channel equalization failed 5 times\n"); } intel_dp_set_idle_link_train(intel_dp); - if (channel_eq) - DRM_DEBUG_KMS("Channel EQ done. DP Training successful\n"); + return intel_dp->channel_eq_status; + } void intel_dp_stop_link_train(struct intel_dp *intel_dp) diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c index 68a005d..54a9d76 100644 --- a/drivers/gpu/drm/i915/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/intel_dp_mst.c @@ -31,18 +31,16 @@ #include <drm/drm_edid.h> static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, - struct intel_crtc_state *pipe_config) + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); struct intel_digital_port *intel_dig_port = intel_mst->primary; struct intel_dp *intel_dp = &intel_dig_port->dp; struct drm_atomic_state *state; - int bpp, i; + int bpp; int lane_count, slots; const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; - struct drm_connector *drm_connector; - struct intel_connector *connector, *found = NULL; - struct drm_connector_state *connector_state; int mst_pbn; pipe_config->dp_encoder_is_mst = true; @@ -54,7 +52,6 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, */ lane_count = drm_dp_max_lane_count(intel_dp->dpcd); - pipe_config->lane_count = lane_count; pipe_config->pipe_bpp = 24; @@ -62,20 +59,6 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, state = pipe_config->base.state; - for_each_connector_in_state(state, drm_connector, connector_state, i) { - connector = to_intel_connector(drm_connector); - - if (connector_state->best_encoder == &encoder->base) { - found = connector; - break; - } - } - - if (!found) { - DRM_ERROR("can't find connector\n"); - return false; - } - mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, bpp); pipe_config->pbn = mst_pbn; @@ -92,16 +75,20 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, } -static void intel_mst_disable_dp(struct intel_encoder *encoder) +static void intel_mst_disable_dp(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); struct intel_digital_port *intel_dig_port = intel_mst->primary; struct intel_dp *intel_dp = &intel_dig_port->dp; + struct intel_connector *connector = + to_intel_connector(old_conn_state->connector); int ret; DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links); - drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, intel_mst->connector->port); + drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, connector->port); ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr); if (ret) { @@ -109,11 +96,15 @@ static void intel_mst_disable_dp(struct intel_encoder *encoder) } } -static void intel_mst_post_disable_dp(struct intel_encoder *encoder) +static void intel_mst_post_disable_dp(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); struct intel_digital_port *intel_dig_port = intel_mst->primary; struct intel_dp *intel_dp = &intel_dig_port->dp; + struct intel_connector *connector = + to_intel_connector(old_conn_state->connector); DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links); @@ -122,59 +113,51 @@ static void intel_mst_post_disable_dp(struct intel_encoder *encoder) /* and this can also fail */ drm_dp_update_payload_part2(&intel_dp->mst_mgr); - drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, intel_mst->connector->port); + drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, connector->port); intel_dp->active_mst_links--; intel_mst->connector = NULL; if (intel_dp->active_mst_links == 0) { - intel_dig_port->base.post_disable(&intel_dig_port->base); + intel_dig_port->base.post_disable(&intel_dig_port->base, + NULL, NULL); + intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF); } } -static void intel_mst_pre_enable_dp(struct intel_encoder *encoder) +static void intel_mst_pre_enable_dp(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); struct intel_digital_port *intel_dig_port = intel_mst->primary; struct intel_dp *intel_dp = &intel_dig_port->dp; - struct drm_device *dev = encoder->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); enum port port = intel_dig_port->port; + struct intel_connector *connector = + to_intel_connector(conn_state->connector); int ret; uint32_t temp; - struct intel_connector *found = NULL, *connector; int slots; - struct drm_crtc *crtc = encoder->base.crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - for_each_intel_connector(dev, connector) { - if (connector->base.state->best_encoder == &encoder->base) { - found = connector; - break; - } - } - - if (!found) { - DRM_ERROR("can't find connector\n"); - return; - } /* MST encoders are bound to a crtc, not to a connector, * force the mapping here for get_hw_state. */ - found->encoder = encoder; + connector->encoder = encoder; + intel_mst->connector = connector; DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links); - intel_mst->connector = found; - if (intel_dp->active_mst_links == 0) { - intel_prepare_ddi_buffer(&intel_dig_port->base); - - intel_ddi_clk_select(&intel_dig_port->base, intel_crtc->config); + intel_ddi_clk_select(&intel_dig_port->base, + pipe_config->shared_dpll); - intel_dp_set_link_params(intel_dp, intel_crtc->config); + intel_prepare_dp_ddi_buffers(&intel_dig_port->base); + intel_dp_set_link_params(intel_dp, + pipe_config->port_clock, + pipe_config->lane_count, + true); intel_ddi_init_dp_buf_reg(&intel_dig_port->base); @@ -185,8 +168,8 @@ static void intel_mst_pre_enable_dp(struct intel_encoder *encoder) } ret = drm_dp_mst_allocate_vcpi(&intel_dp->mst_mgr, - intel_mst->connector->port, - intel_crtc->config->pbn, &slots); + connector->port, + pipe_config->pbn, &slots); if (ret == false) { DRM_ERROR("failed to allocate vcpi\n"); return; @@ -200,13 +183,14 @@ static void intel_mst_pre_enable_dp(struct intel_encoder *encoder) ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr); } -static void intel_mst_enable_dp(struct intel_encoder *encoder) +static void intel_mst_enable_dp(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); struct intel_digital_port *intel_dig_port = intel_mst->primary; struct intel_dp *intel_dp = &intel_dig_port->dp; - struct drm_device *dev = intel_dig_port->base.base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); enum port port = intel_dig_port->port; int ret; @@ -239,9 +223,8 @@ static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder, { struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); struct intel_digital_port *intel_dig_port = intel_mst->primary; - struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); - struct drm_device *dev = encoder->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_crtc *crtc = to_intel_crtc(pipe_config->base.crtc); + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); enum transcoder cpu_transcoder = pipe_config->cpu_transcoder; u32 temp, flags = 0; diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.c b/drivers/gpu/drm/i915/intel_dpll_mgr.c index 5c1f2d2..c26d18a 100644 --- a/drivers/gpu/drm/i915/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/intel_dpll_mgr.c @@ -24,6 +24,44 @@ #include "intel_drv.h" struct intel_shared_dpll * +skl_find_link_pll(struct drm_i915_private *dev_priv, int clock) +{ + struct intel_shared_dpll *pll = NULL; + struct intel_dpll_hw_state dpll_hw_state; + enum intel_dpll_id i; + bool found = false; + + if (!skl_ddi_dp_set_dpll_hw_state(clock, &dpll_hw_state)) + return pll; + + for (i = DPLL_ID_SKL_DPLL1; i <= DPLL_ID_SKL_DPLL3; i++) { + pll = &dev_priv->shared_dplls[i]; + + /* Only want to check enabled timings first */ + if (pll->config.crtc_mask == 0) + continue; + + if (memcmp(&dpll_hw_state, &pll->config.hw_state, + sizeof(pll->config.hw_state)) == 0) { + found = true; + break; + } + } + + /* Ok no matching timings, maybe there's a free one? */ + for (i = DPLL_ID_SKL_DPLL1; + ((found == false) && (i <= DPLL_ID_SKL_DPLL3)); i++) { + pll = &dev_priv->shared_dplls[i]; + if (pll->config.crtc_mask == 0) { + pll->config.hw_state = dpll_hw_state; + break; + } + } + + return pll; +} + +struct intel_shared_dpll * intel_get_shared_dpll_by_id(struct drm_i915_private *dev_priv, enum intel_dpll_id id) { @@ -452,26 +490,6 @@ static bool hsw_ddi_spll_get_hw_state(struct drm_i915_private *dev_priv, return val & SPLL_PLL_ENABLE; } -static uint32_t hsw_pll_to_ddi_pll_sel(struct intel_shared_dpll *pll) -{ - switch (pll->id) { - case DPLL_ID_WRPLL1: - return PORT_CLK_SEL_WRPLL1; - case DPLL_ID_WRPLL2: - return PORT_CLK_SEL_WRPLL2; - case DPLL_ID_SPLL: - return PORT_CLK_SEL_SPLL; - case DPLL_ID_LCPLL_810: - return PORT_CLK_SEL_LCPLL_810; - case DPLL_ID_LCPLL_1350: - return PORT_CLK_SEL_LCPLL_1350; - case DPLL_ID_LCPLL_2700: - return PORT_CLK_SEL_LCPLL_2700; - default: - return PORT_CLK_SEL_NONE; - } -} - #define LC_FREQ 2700 #define LC_FREQ_2K U64_C(LC_FREQ * 2000) @@ -687,11 +705,65 @@ hsw_ddi_calculate_wrpll(int clock /* in Hz */, *r2_out = best.r2; } +static struct intel_shared_dpll *hsw_ddi_hdmi_get_dpll(int clock, + struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state) +{ + struct intel_shared_dpll *pll; + uint32_t val; + unsigned int p, n2, r2; + + hsw_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p); + + val = WRPLL_PLL_ENABLE | WRPLL_PLL_LCPLL | + WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) | + WRPLL_DIVIDER_POST(p); + + crtc_state->dpll_hw_state.wrpll = val; + + pll = intel_find_shared_dpll(crtc, crtc_state, + DPLL_ID_WRPLL1, DPLL_ID_WRPLL2); + + if (!pll) + return NULL; + + return pll; +} + +struct intel_shared_dpll *hsw_ddi_dp_get_dpll(struct intel_encoder *encoder, + int clock) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct intel_shared_dpll *pll; + enum intel_dpll_id pll_id; + + switch (clock / 2) { + case 81000: + pll_id = DPLL_ID_LCPLL_810; + break; + case 135000: + pll_id = DPLL_ID_LCPLL_1350; + break; + case 270000: + pll_id = DPLL_ID_LCPLL_2700; + break; + default: + DRM_DEBUG_KMS("Invalid clock for DP: %d\n", clock); + return NULL; + } + + pll = intel_get_shared_dpll_by_id(dev_priv, pll_id); + + if (!pll) + return NULL; + + return pll; +} + static struct intel_shared_dpll * hsw_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, struct intel_encoder *encoder) { - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); struct intel_shared_dpll *pll; int clock = crtc_state->port_clock; @@ -699,41 +771,12 @@ hsw_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, sizeof(crtc_state->dpll_hw_state)); if (encoder->type == INTEL_OUTPUT_HDMI) { - uint32_t val; - unsigned p, n2, r2; - - hsw_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p); - - val = WRPLL_PLL_ENABLE | WRPLL_PLL_LCPLL | - WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) | - WRPLL_DIVIDER_POST(p); - - crtc_state->dpll_hw_state.wrpll = val; - - pll = intel_find_shared_dpll(crtc, crtc_state, - DPLL_ID_WRPLL1, DPLL_ID_WRPLL2); + pll = hsw_ddi_hdmi_get_dpll(clock, crtc, crtc_state); } else if (encoder->type == INTEL_OUTPUT_DP || encoder->type == INTEL_OUTPUT_DP_MST || encoder->type == INTEL_OUTPUT_EDP) { - enum intel_dpll_id pll_id; - - switch (clock / 2) { - case 81000: - pll_id = DPLL_ID_LCPLL_810; - break; - case 135000: - pll_id = DPLL_ID_LCPLL_1350; - break; - case 270000: - pll_id = DPLL_ID_LCPLL_2700; - break; - default: - DRM_DEBUG_KMS("Invalid clock for DP: %d\n", clock); - return NULL; - } - - pll = intel_get_shared_dpll_by_id(dev_priv, pll_id); + pll = hsw_ddi_dp_get_dpll(encoder, clock); } else if (encoder->type == INTEL_OUTPUT_ANALOG) { if (WARN_ON(crtc_state->port_clock / 2 != 135000)) @@ -751,14 +794,11 @@ hsw_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, if (!pll) return NULL; - crtc_state->ddi_pll_sel = hsw_pll_to_ddi_pll_sel(pll); - intel_reference_shared_dpll(pll, crtc_state); return pll; } - static const struct intel_shared_dpll_funcs hsw_ddi_wrpll_funcs = { .enable = hsw_ddi_wrpll_enable, .disable = hsw_ddi_wrpll_disable, @@ -1194,75 +1234,110 @@ skip_remaining_dividers: return true; } -static struct intel_shared_dpll * -skl_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, - struct intel_encoder *encoder) +static bool skl_ddi_hdmi_pll_dividers(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state, + int clock) { - struct intel_shared_dpll *pll; uint32_t ctrl1, cfgcr1, cfgcr2; - int clock = crtc_state->port_clock; + struct skl_wrpll_params wrpll_params = { 0, }; /* * See comment in intel_dpll_hw_state to understand why we always use 0 * as the DPLL id in this function. */ - ctrl1 = DPLL_CTRL1_OVERRIDE(0); - if (encoder->type == INTEL_OUTPUT_HDMI) { - struct skl_wrpll_params wrpll_params = { 0, }; + ctrl1 |= DPLL_CTRL1_HDMI_MODE(0); - ctrl1 |= DPLL_CTRL1_HDMI_MODE(0); + if (!skl_ddi_calculate_wrpll(clock * 1000, &wrpll_params)) + return false; - if (!skl_ddi_calculate_wrpll(clock * 1000, &wrpll_params)) - return NULL; + cfgcr1 = DPLL_CFGCR1_FREQ_ENABLE | + DPLL_CFGCR1_DCO_FRACTION(wrpll_params.dco_fraction) | + wrpll_params.dco_integer; + + cfgcr2 = DPLL_CFGCR2_QDIV_RATIO(wrpll_params.qdiv_ratio) | + DPLL_CFGCR2_QDIV_MODE(wrpll_params.qdiv_mode) | + DPLL_CFGCR2_KDIV(wrpll_params.kdiv) | + DPLL_CFGCR2_PDIV(wrpll_params.pdiv) | + wrpll_params.central_freq; + + memset(&crtc_state->dpll_hw_state, 0, + sizeof(crtc_state->dpll_hw_state)); + + crtc_state->dpll_hw_state.ctrl1 = ctrl1; + crtc_state->dpll_hw_state.cfgcr1 = cfgcr1; + crtc_state->dpll_hw_state.cfgcr2 = cfgcr2; + return true; +} - cfgcr1 = DPLL_CFGCR1_FREQ_ENABLE | - DPLL_CFGCR1_DCO_FRACTION(wrpll_params.dco_fraction) | - wrpll_params.dco_integer; - cfgcr2 = DPLL_CFGCR2_QDIV_RATIO(wrpll_params.qdiv_ratio) | - DPLL_CFGCR2_QDIV_MODE(wrpll_params.qdiv_mode) | - DPLL_CFGCR2_KDIV(wrpll_params.kdiv) | - DPLL_CFGCR2_PDIV(wrpll_params.pdiv) | - wrpll_params.central_freq; +bool skl_ddi_dp_set_dpll_hw_state(int clock, + struct intel_dpll_hw_state *dpll_hw_state) +{ + uint32_t ctrl1; + + /* + * See comment in intel_dpll_hw_state to understand why we always use 0 + * as the DPLL id in this function. + */ + ctrl1 = DPLL_CTRL1_OVERRIDE(0); + switch (clock / 2) { + case 81000: + ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_810, 0); + break; + case 135000: + ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1350, 0); + break; + case 270000: + ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_2700, 0); + break; + /* eDP 1.4 rates */ + case 162000: + ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1620, 0); + break; + case 108000: + ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1080, 0); + break; + case 216000: + ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_2160, 0); + break; + } + + dpll_hw_state->ctrl1 = ctrl1; + return true; +} + +static struct intel_shared_dpll * +skl_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, + struct intel_encoder *encoder) +{ + struct intel_shared_dpll *pll; + int clock = crtc_state->port_clock; + bool bret; + struct intel_dpll_hw_state dpll_hw_state; + + memset(&dpll_hw_state, 0, sizeof(dpll_hw_state)); + + if (encoder->type == INTEL_OUTPUT_HDMI) { + bret = skl_ddi_hdmi_pll_dividers(crtc, crtc_state, clock); + if (!bret) { + DRM_DEBUG_KMS("Could not get HDMI pll dividers.\n"); + return NULL; + } } else if (encoder->type == INTEL_OUTPUT_DP || encoder->type == INTEL_OUTPUT_DP_MST || encoder->type == INTEL_OUTPUT_EDP) { - switch (crtc_state->port_clock / 2) { - case 81000: - ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_810, 0); - break; - case 135000: - ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1350, 0); - break; - case 270000: - ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_2700, 0); - break; - /* eDP 1.4 rates */ - case 162000: - ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1620, 0); - break; - case 108000: - ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1080, 0); - break; - case 216000: - ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_2160, 0); - break; + bret = skl_ddi_dp_set_dpll_hw_state(clock, &dpll_hw_state); + if (!bret) { + DRM_DEBUG_KMS("Could not set DP dpll HW state.\n"); + return NULL; } - - cfgcr1 = cfgcr2 = 0; + crtc_state->dpll_hw_state = dpll_hw_state; } else { return NULL; } - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - - crtc_state->dpll_hw_state.ctrl1 = ctrl1; - crtc_state->dpll_hw_state.cfgcr1 = cfgcr1; - crtc_state->dpll_hw_state.cfgcr2 = cfgcr2; - if (encoder->type == INTEL_OUTPUT_EDP) pll = intel_find_shared_dpll(crtc, crtc_state, DPLL_ID_SKL_DPLL0, @@ -1274,8 +1349,6 @@ skl_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, if (!pll) return NULL; - crtc_state->ddi_pll_sel = pll->id; - intel_reference_shared_dpll(pll, crtc_state); return pll; @@ -1484,6 +1557,8 @@ struct bxt_clk_div { uint32_t m2_frac; bool m2_frac_en; uint32_t n; + + int vco; }; /* pre-calculated values for DP linkrates */ @@ -1497,57 +1572,60 @@ static const struct bxt_clk_div bxt_dp_clk_val[] = { {432000, 3, 1, 32, 1677722, 1, 1} }; -static struct intel_shared_dpll * -bxt_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, - struct intel_encoder *encoder) +static bool +bxt_ddi_hdmi_pll_dividers(struct intel_crtc *intel_crtc, + struct intel_crtc_state *crtc_state, int clock, + struct bxt_clk_div *clk_div) { - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - struct intel_shared_dpll *pll; - enum intel_dpll_id i; - struct intel_digital_port *intel_dig_port; - struct bxt_clk_div clk_div = {0}; - int vco = 0; - uint32_t prop_coef, int_coef, gain_ctl, targ_cnt; - uint32_t lanestagger; - int clock = crtc_state->port_clock; + struct dpll best_clock; - if (encoder->type == INTEL_OUTPUT_HDMI) { - struct dpll best_clock; + /* Calculate HDMI div */ + /* + * FIXME: tie the following calculation into + * i9xx_crtc_compute_clock + */ + if (!bxt_find_best_dpll(crtc_state, clock, &best_clock)) { + DRM_DEBUG_DRIVER("no PLL dividers found for clock %d pipe %c\n", + clock, pipe_name(intel_crtc->pipe)); + return false; + } - /* Calculate HDMI div */ - /* - * FIXME: tie the following calculation into - * i9xx_crtc_compute_clock - */ - if (!bxt_find_best_dpll(crtc_state, clock, &best_clock)) { - DRM_DEBUG_DRIVER("no PLL dividers found for clock %d pipe %c\n", - clock, pipe_name(crtc->pipe)); - return NULL; - } + clk_div->p1 = best_clock.p1; + clk_div->p2 = best_clock.p2; + WARN_ON(best_clock.m1 != 2); + clk_div->n = best_clock.n; + clk_div->m2_int = best_clock.m2 >> 22; + clk_div->m2_frac = best_clock.m2 & ((1 << 22) - 1); + clk_div->m2_frac_en = clk_div->m2_frac != 0; - clk_div.p1 = best_clock.p1; - clk_div.p2 = best_clock.p2; - WARN_ON(best_clock.m1 != 2); - clk_div.n = best_clock.n; - clk_div.m2_int = best_clock.m2 >> 22; - clk_div.m2_frac = best_clock.m2 & ((1 << 22) - 1); - clk_div.m2_frac_en = clk_div.m2_frac != 0; + clk_div->vco = best_clock.vco; - vco = best_clock.vco; - } else if (encoder->type == INTEL_OUTPUT_DP || - encoder->type == INTEL_OUTPUT_EDP) { - int i; + return true; +} - clk_div = bxt_dp_clk_val[0]; - for (i = 0; i < ARRAY_SIZE(bxt_dp_clk_val); ++i) { - if (bxt_dp_clk_val[i].clock == clock) { - clk_div = bxt_dp_clk_val[i]; - break; - } +static void bxt_ddi_dp_pll_dividers(int clock, struct bxt_clk_div *clk_div) +{ + int i; + + *clk_div = bxt_dp_clk_val[0]; + for (i = 0; i < ARRAY_SIZE(bxt_dp_clk_val); ++i) { + if (bxt_dp_clk_val[i].clock == clock) { + *clk_div = bxt_dp_clk_val[i]; + break; } - vco = clock * 10 / 2 * clk_div.p1 * clk_div.p2; } + clk_div->vco = clock * 10 / 2 * clk_div->p1 * clk_div->p2; +} + +static bool bxt_ddi_set_dpll_hw_state(int clock, + struct bxt_clk_div *clk_div, + struct intel_dpll_hw_state *dpll_hw_state) +{ + int vco = clk_div->vco; + uint32_t prop_coef, int_coef, gain_ctl, targ_cnt; + uint32_t lanestagger; + if (vco >= 6200000 && vco <= 6700000) { prop_coef = 4; int_coef = 9; @@ -1566,12 +1644,9 @@ bxt_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, targ_cnt = 9; } else { DRM_ERROR("Invalid VCO\n"); - return NULL; + return false; } - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); - if (clock > 270000) lanestagger = 0x18; else if (clock > 135000) @@ -1583,35 +1658,75 @@ bxt_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, else lanestagger = 0x02; - crtc_state->dpll_hw_state.ebb0 = - PORT_PLL_P1(clk_div.p1) | PORT_PLL_P2(clk_div.p2); - crtc_state->dpll_hw_state.pll0 = clk_div.m2_int; - crtc_state->dpll_hw_state.pll1 = PORT_PLL_N(clk_div.n); - crtc_state->dpll_hw_state.pll2 = clk_div.m2_frac; + dpll_hw_state->ebb0 = PORT_PLL_P1(clk_div->p1) | PORT_PLL_P2(clk_div->p2); + dpll_hw_state->pll0 = clk_div->m2_int; + dpll_hw_state->pll1 = PORT_PLL_N(clk_div->n); + dpll_hw_state->pll2 = clk_div->m2_frac; - if (clk_div.m2_frac_en) - crtc_state->dpll_hw_state.pll3 = - PORT_PLL_M2_FRAC_ENABLE; + if (clk_div->m2_frac_en) + dpll_hw_state->pll3 = PORT_PLL_M2_FRAC_ENABLE; - crtc_state->dpll_hw_state.pll6 = - prop_coef | PORT_PLL_INT_COEFF(int_coef); - crtc_state->dpll_hw_state.pll6 |= - PORT_PLL_GAIN_CTL(gain_ctl); + dpll_hw_state->pll6 = prop_coef | PORT_PLL_INT_COEFF(int_coef); + dpll_hw_state->pll6 |= PORT_PLL_GAIN_CTL(gain_ctl); - crtc_state->dpll_hw_state.pll8 = targ_cnt; + dpll_hw_state->pll8 = targ_cnt; - crtc_state->dpll_hw_state.pll9 = 5 << PORT_PLL_LOCK_THRESHOLD_SHIFT; + dpll_hw_state->pll9 = 5 << PORT_PLL_LOCK_THRESHOLD_SHIFT; - crtc_state->dpll_hw_state.pll10 = + dpll_hw_state->pll10 = PORT_PLL_DCO_AMP(PORT_PLL_DCO_AMP_DEFAULT) | PORT_PLL_DCO_AMP_OVR_EN_H; - crtc_state->dpll_hw_state.ebb4 = PORT_PLL_10BIT_CLK_ENABLE; + dpll_hw_state->ebb4 = PORT_PLL_10BIT_CLK_ENABLE; - crtc_state->dpll_hw_state.pcsdw12 = - LANESTAGGER_STRAP_OVRD | lanestagger; + dpll_hw_state->pcsdw12 = LANESTAGGER_STRAP_OVRD | lanestagger; - intel_dig_port = enc_to_dig_port(&encoder->base); + return true; +} + +bool bxt_ddi_dp_set_dpll_hw_state(int clock, + struct intel_dpll_hw_state *dpll_hw_state) +{ + struct bxt_clk_div clk_div = {0}; + + bxt_ddi_dp_pll_dividers(clock, &clk_div); + + return bxt_ddi_set_dpll_hw_state(clock, &clk_div, dpll_hw_state); +} + +static struct intel_shared_dpll * +bxt_get_dpll(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct intel_encoder *encoder) +{ + struct bxt_clk_div clk_div = {0}; + struct intel_dpll_hw_state dpll_hw_state = {0}; + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + struct intel_digital_port *intel_dig_port; + struct intel_shared_dpll *pll; + int i, clock = crtc_state->port_clock; + + if (encoder->type == INTEL_OUTPUT_HDMI + && !bxt_ddi_hdmi_pll_dividers(crtc, crtc_state, + clock, &clk_div)) + return NULL; + + if ((encoder->type == INTEL_OUTPUT_DP || + encoder->type == INTEL_OUTPUT_EDP) && + !bxt_ddi_dp_set_dpll_hw_state(clock, &dpll_hw_state)) + return NULL; + + memset(&crtc_state->dpll_hw_state, 0, + sizeof(crtc_state->dpll_hw_state)); + + crtc_state->dpll_hw_state = dpll_hw_state; + + if (encoder->type == INTEL_OUTPUT_DP_MST) { + struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); + + intel_dig_port = intel_mst->primary; + } else + intel_dig_port = enc_to_dig_port(&encoder->base); /* 1:1 mapping between ports and PLLs */ i = (enum intel_dpll_id) intel_dig_port->port; @@ -1622,9 +1737,6 @@ bxt_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, intel_reference_shared_dpll(pll, crtc_state); - /* shared DPLL id 0 is DPLL A */ - crtc_state->ddi_pll_sel = pll->id; - return pll; } diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.h b/drivers/gpu/drm/i915/intel_dpll_mgr.h index 89c5ada..f438535 100644 --- a/drivers/gpu/drm/i915/intel_dpll_mgr.h +++ b/drivers/gpu/drm/i915/intel_dpll_mgr.h @@ -160,5 +160,20 @@ void intel_disable_shared_dpll(struct intel_crtc *crtc); void intel_shared_dpll_commit(struct drm_atomic_state *state); void intel_shared_dpll_init(struct drm_device *dev); +/* BXT dpll related functions */ +bool bxt_ddi_dp_set_dpll_hw_state(int clock, + struct intel_dpll_hw_state *dpll_hw_state); + + +/* SKL dpll related functions */ +bool skl_ddi_dp_set_dpll_hw_state(int clock, + struct intel_dpll_hw_state *dpll_hw_state); +struct intel_shared_dpll *skl_find_link_pll(struct drm_i915_private *dev_priv, + int clock); + + +/* HSW dpll related functions */ +struct intel_shared_dpll *hsw_ddi_dp_get_dpll(struct intel_encoder *encoder, + int clock); #endif /* _INTEL_DPLL_MGR_H_ */ diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index cc937a1..8fd16ad 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -52,11 +52,15 @@ */ #define _wait_for(COND, US, W) ({ \ unsigned long timeout__ = jiffies + usecs_to_jiffies(US) + 1; \ - int ret__ = 0; \ - while (!(COND)) { \ - if (time_after(jiffies, timeout__)) { \ - if (!(COND)) \ - ret__ = -ETIMEDOUT; \ + int ret__; \ + for (;;) { \ + bool expired__ = time_after(jiffies, timeout__); \ + if (COND) { \ + ret__ = 0; \ + break; \ + } \ + if (expired__) { \ + ret__ = -ETIMEDOUT; \ break; \ } \ if ((W) && drm_can_sleep()) { \ @@ -178,11 +182,22 @@ struct intel_framebuffer { struct drm_framebuffer base; struct drm_i915_gem_object *obj; struct intel_rotation_info rot_info; + + /* for each plane in the normal GTT view */ + struct { + unsigned int x, y; + } normal[2]; + /* for each plane in the rotated GTT view */ + struct { + unsigned int x, y; + unsigned int pitch; /* pixels */ + } rotated[2]; }; struct intel_fbdev { struct drm_fb_helper helper; struct intel_framebuffer *fb; + struct i915_vma *vma; async_cookie_t cookie; int preferred_bpp; }; @@ -194,14 +209,26 @@ struct intel_encoder { unsigned int cloneable; void (*hot_plug)(struct intel_encoder *); bool (*compute_config)(struct intel_encoder *, - struct intel_crtc_state *); - void (*pre_pll_enable)(struct intel_encoder *); - void (*pre_enable)(struct intel_encoder *); - void (*enable)(struct intel_encoder *); - void (*mode_set)(struct intel_encoder *intel_encoder); - void (*disable)(struct intel_encoder *); - void (*post_disable)(struct intel_encoder *); - void (*post_pll_disable)(struct intel_encoder *); + struct intel_crtc_state *, + struct drm_connector_state *); + void (*pre_pll_enable)(struct intel_encoder *, + struct intel_crtc_state *, + struct drm_connector_state *); + void (*pre_enable)(struct intel_encoder *, + struct intel_crtc_state *, + struct drm_connector_state *); + void (*enable)(struct intel_encoder *, + struct intel_crtc_state *, + struct drm_connector_state *); + void (*disable)(struct intel_encoder *, + struct intel_crtc_state *, + struct drm_connector_state *); + void (*post_disable)(struct intel_encoder *, + struct intel_crtc_state *, + struct drm_connector_state *); + void (*post_pll_disable)(struct intel_encoder *, + struct intel_crtc_state *, + struct drm_connector_state *); /* Read out the current hw state of this connector, returning true if * the encoder is active. If the encoder is enabled it also set the pipe * it is connected to in the pipe parameter. */ @@ -338,10 +365,16 @@ struct intel_atomic_state { struct intel_plane_state { struct drm_plane_state base; - struct drm_rect src; - struct drm_rect dst; struct drm_rect clip; - bool visible; + + struct { + u32 offset; + int x, y; + } main; + struct { + u32 offset; + int x, y; + } aux; /* * scaler_id @@ -561,12 +594,6 @@ struct intel_crtc_state { /* Selected dpll when shared or NULL. */ struct intel_shared_dpll *shared_dpll; - /* - * - PORT_CLK_SEL for DDI ports on HSW/BDW. - * - enum skl_dpll on SKL - */ - uint32_t ddi_pll_sel; - /* Actual register state of the dpll, for shared dpll cross-checking. */ struct intel_dpll_hw_state dpll_hw_state; @@ -683,8 +710,8 @@ struct intel_crtc { struct intel_crtc_state *config; - /* reset counter value when the last flip was submitted */ - unsigned int reset_counter; + /* global reset count when the last flip was submitted */ + unsigned int reset_count; /* Access to these should be protected by dev_priv->irq_lock. */ bool cpu_fifo_underrun_disabled; @@ -852,8 +879,10 @@ struct intel_dp { int link_rate; uint8_t lane_count; uint8_t sink_count; + bool link_mst; bool has_audio; bool detect_done; + bool channel_eq_status; enum hdmi_force_audio force_audio; bool limited_color_range; bool color_range_auto; @@ -1106,8 +1135,11 @@ void intel_crt_reset(struct drm_encoder *encoder); /* intel_ddi.c */ void intel_ddi_clk_select(struct intel_encoder *encoder, - const struct intel_crtc_state *pipe_config); -void intel_prepare_ddi_buffer(struct intel_encoder *encoder); + struct intel_shared_dpll *pll); +void intel_ddi_fdi_post_disable(struct intel_encoder *intel_encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state); +void intel_prepare_dp_ddi_buffers(struct intel_encoder *encoder); void hsw_fdi_link_train(struct drm_crtc *crtc); void intel_ddi_init(struct drm_device *dev, enum port port); enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder); @@ -1122,7 +1154,6 @@ bool intel_ddi_pll_select(struct intel_crtc *crtc, void intel_ddi_set_pipe_settings(struct drm_crtc *crtc); void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp); bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector); -void intel_ddi_fdi_disable(struct drm_crtc *crtc); void intel_ddi_get_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config); struct intel_encoder * @@ -1133,22 +1164,12 @@ void intel_ddi_clock_get(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config); void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state); uint32_t ddi_signal_levels(struct intel_dp *intel_dp); - -/* intel_frontbuffer.c */ -void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj, - enum fb_op_origin origin); -void intel_frontbuffer_flip_prepare(struct drm_device *dev, - unsigned frontbuffer_bits); -void intel_frontbuffer_flip_complete(struct drm_device *dev, - unsigned frontbuffer_bits); -void intel_frontbuffer_flip(struct drm_device *dev, - unsigned frontbuffer_bits); +struct intel_shared_dpll *intel_ddi_get_link_dpll(struct intel_dp *intel_dp, + int clock); unsigned int intel_fb_align_height(struct drm_device *dev, unsigned int height, uint32_t pixel_format, uint64_t fb_format_modifier); -void intel_fb_obj_flush(struct drm_i915_gem_object *obj, bool retire, - enum fb_op_origin origin); u32 intel_fb_stride_alignment(const struct drm_i915_private *dev_priv, uint64_t fb_modifier, uint32_t pixel_format); @@ -1164,14 +1185,22 @@ void skl_set_preferred_cdclk_vco(struct drm_i915_private *dev_priv, int vco); void intel_update_rawclk(struct drm_i915_private *dev_priv); int vlv_get_cck_clock(struct drm_i915_private *dev_priv, const char *name, u32 reg, int ref_freq); +void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv); +void lpt_disable_iclkip(struct drm_i915_private *dev_priv); extern const struct drm_plane_funcs intel_plane_funcs; void intel_init_display_hooks(struct drm_i915_private *dev_priv); +unsigned int intel_fb_xy_to_linear(int x, int y, + const struct intel_plane_state *state, + int plane); +void intel_add_fb_offsets(int *x, int *y, + const struct intel_plane_state *state, int plane); unsigned int intel_rotation_info_size(const struct intel_rotation_info *rot_info); bool intel_has_pending_fb_unpin(struct drm_device *dev); void intel_mark_busy(struct drm_i915_private *dev_priv); void intel_mark_idle(struct drm_i915_private *dev_priv); void intel_crtc_restore_mode(struct drm_crtc *crtc); int intel_display_suspend(struct drm_device *dev); +void intel_pps_unlock_regs_wa(struct drm_i915_private *dev_priv); void intel_encoder_destroy(struct drm_encoder *encoder); int intel_connector_init(struct intel_connector *); struct intel_connector *intel_connector_alloc(void); @@ -1227,8 +1256,8 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, void intel_release_load_detect_pipe(struct drm_connector *connector, struct intel_load_detect_pipe *old, struct drm_modeset_acquire_ctx *ctx); -int intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb, - unsigned int rotation); +struct i915_vma * +intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb, unsigned int rotation); void intel_unpin_fb_obj(struct drm_framebuffer *fb, unsigned int rotation); struct drm_framebuffer * __intel_framebuffer_create(struct drm_device *dev, @@ -1238,9 +1267,9 @@ void intel_finish_page_flip_cs(struct drm_i915_private *dev_priv, int pipe); void intel_finish_page_flip_mmio(struct drm_i915_private *dev_priv, int pipe); void intel_check_page_flip(struct drm_i915_private *dev_priv, int pipe); int intel_prepare_plane_fb(struct drm_plane *plane, - const struct drm_plane_state *new_state); + struct drm_plane_state *new_state); void intel_cleanup_plane_fb(struct drm_plane *plane, - const struct drm_plane_state *old_state); + struct drm_plane_state *old_state); int intel_plane_atomic_get_property(struct drm_plane *plane, const struct drm_plane_state *state, struct drm_property *property, @@ -1258,7 +1287,7 @@ unsigned int intel_tile_height(const struct drm_i915_private *dev_priv, static inline bool intel_rotation_90_or_270(unsigned int rotation) { - return rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270)); + return rotation & (DRM_ROTATE_90 | DRM_ROTATE_270); } void intel_create_rotation_property(struct drm_device *dev, @@ -1290,9 +1319,7 @@ void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, bool state); #define assert_pipe_enabled(d, p) assert_pipe(d, p, true) #define assert_pipe_disabled(d, p) assert_pipe(d, p, false) u32 intel_compute_tile_offset(int *x, int *y, - const struct drm_framebuffer *fb, int plane, - unsigned int pitch, - unsigned int rotation); + const struct intel_plane_state *state, int plane); void intel_prepare_reset(struct drm_i915_private *dev_priv); void intel_finish_reset(struct drm_i915_private *dev_priv); void hsw_enable_pc8(struct drm_i915_private *dev_priv); @@ -1335,13 +1362,14 @@ void intel_mode_from_pipe_config(struct drm_display_mode *mode, int skl_update_scaler_crtc(struct intel_crtc_state *crtc_state); int skl_max_scale(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state); -u32 intel_plane_obj_offset(struct intel_plane *intel_plane, - struct drm_i915_gem_object *obj, - unsigned int plane); +u32 intel_fb_gtt_offset(struct drm_framebuffer *fb, unsigned int rotation); u32 skl_plane_ctl_format(uint32_t pixel_format); u32 skl_plane_ctl_tiling(uint64_t fb_modifier); u32 skl_plane_ctl_rotation(unsigned int rotation); +u32 skl_plane_stride(const struct drm_framebuffer *fb, int plane, + unsigned int rotation); +int skl_check_plane_surface(struct intel_plane_state *plane_state); /* intel_csr.c */ void intel_csr_ucode_init(struct drm_i915_private *); @@ -1355,7 +1383,8 @@ bool intel_dp_init(struct drm_device *dev, i915_reg_t output_reg, enum port port bool intel_dp_init_connector(struct intel_digital_port *intel_dig_port, struct intel_connector *intel_connector); void intel_dp_set_link_params(struct intel_dp *intel_dp, - const struct intel_crtc_state *pipe_config); + int link_rate, uint8_t lane_count, + bool link_mst); void intel_dp_start_link_train(struct intel_dp *intel_dp); void intel_dp_stop_link_train(struct intel_dp *intel_dp); void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode); @@ -1364,7 +1393,8 @@ void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder); void intel_dp_encoder_destroy(struct drm_encoder *encoder); int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc); bool intel_dp_compute_config(struct intel_encoder *encoder, - struct intel_crtc_state *pipe_config); + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state); bool intel_dp_is_edp(struct drm_device *dev, enum port port); enum irqreturn intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd); @@ -1382,13 +1412,14 @@ void intel_dp_hot_plug(struct intel_encoder *intel_encoder); void intel_power_sequencer_reset(struct drm_i915_private *dev_priv); uint32_t intel_dp_pack_aux(const uint8_t *src, int src_bytes); void intel_plane_destroy(struct drm_plane *plane); -void intel_edp_drrs_enable(struct intel_dp *intel_dp); -void intel_edp_drrs_disable(struct intel_dp *intel_dp); -void intel_edp_drrs_invalidate(struct drm_device *dev, - unsigned frontbuffer_bits); -void intel_edp_drrs_flush(struct drm_device *dev, unsigned frontbuffer_bits); -bool intel_digital_port_connected(struct drm_i915_private *dev_priv, - struct intel_digital_port *port); +void intel_edp_drrs_enable(struct intel_dp *intel_dp, + struct intel_crtc_state *crtc_state); +void intel_edp_drrs_disable(struct intel_dp *intel_dp, + struct intel_crtc_state *crtc_state); +void intel_edp_drrs_invalidate(struct drm_i915_private *dev_priv, + unsigned int frontbuffer_bits); +void intel_edp_drrs_flush(struct drm_i915_private *dev_priv, + unsigned int frontbuffer_bits); void intel_dp_program_link_training_pattern(struct intel_dp *intel_dp, @@ -1488,7 +1519,8 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, struct intel_connector *intel_connector); struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder); bool intel_hdmi_compute_config(struct intel_encoder *encoder, - struct intel_crtc_state *pipe_config); + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state); void intel_dp_dual_mode_set_tmds_output(struct intel_hdmi *hdmi, bool enable); @@ -1561,13 +1593,13 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con /* intel_psr.c */ void intel_psr_enable(struct intel_dp *intel_dp); void intel_psr_disable(struct intel_dp *intel_dp); -void intel_psr_invalidate(struct drm_device *dev, +void intel_psr_invalidate(struct drm_i915_private *dev_priv, unsigned frontbuffer_bits); -void intel_psr_flush(struct drm_device *dev, +void intel_psr_flush(struct drm_i915_private *dev_priv, unsigned frontbuffer_bits, enum fb_op_origin origin); void intel_psr_init(struct drm_device *dev); -void intel_psr_single_frame_update(struct drm_device *dev, +void intel_psr_single_frame_update(struct drm_i915_private *dev_priv, unsigned frontbuffer_bits); /* intel_runtime_pm.c */ @@ -1667,13 +1699,6 @@ enable_rpm_wakeref_asserts(struct drm_i915_private *dev_priv) atomic_dec(&dev_priv->pm.wakeref_count); } -/* TODO: convert users of these to rely instead on proper RPM refcounting */ -#define DISABLE_RPM_WAKEREF_ASSERTS(dev_priv) \ - disable_rpm_wakeref_asserts(dev_priv) - -#define ENABLE_RPM_WAKEREF_ASSERTS(dev_priv) \ - enable_rpm_wakeref_asserts(dev_priv) - void intel_runtime_pm_get(struct drm_i915_private *dev_priv); bool intel_runtime_pm_get_if_in_use(struct drm_i915_private *dev_priv); void intel_runtime_pm_get_noresume(struct drm_i915_private *dev_priv); @@ -1699,11 +1724,11 @@ void intel_gpu_ips_init(struct drm_i915_private *dev_priv); void intel_gpu_ips_teardown(void); void intel_init_gt_powersave(struct drm_i915_private *dev_priv); void intel_cleanup_gt_powersave(struct drm_i915_private *dev_priv); +void intel_sanitize_gt_powersave(struct drm_i915_private *dev_priv); void intel_enable_gt_powersave(struct drm_i915_private *dev_priv); +void intel_autoenable_gt_powersave(struct drm_i915_private *dev_priv); void intel_disable_gt_powersave(struct drm_i915_private *dev_priv); void intel_suspend_gt_powersave(struct drm_i915_private *dev_priv); -void intel_reset_gt_powersave(struct drm_i915_private *dev_priv); -void gen6_update_ring_freq(struct drm_i915_private *dev_priv); void gen6_rps_busy(struct drm_i915_private *dev_priv); void gen6_rps_reset_ei(struct drm_i915_private *dev_priv); void gen6_rps_idle(struct drm_i915_private *dev_priv); @@ -1716,6 +1741,21 @@ void ilk_wm_get_hw_state(struct drm_device *dev); void skl_wm_get_hw_state(struct drm_device *dev); void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv, struct skl_ddb_allocation *ddb /* out */); +bool skl_can_enable_sagv(struct drm_atomic_state *state); +int skl_enable_sagv(struct drm_i915_private *dev_priv); +int skl_disable_sagv(struct drm_i915_private *dev_priv); +bool skl_ddb_allocation_equals(const struct skl_ddb_allocation *old, + const struct skl_ddb_allocation *new, + enum pipe pipe); +bool skl_ddb_allocation_overlaps(struct drm_atomic_state *state, + const struct skl_ddb_allocation *old, + const struct skl_ddb_allocation *new, + enum pipe pipe); +void skl_write_cursor_wm(struct intel_crtc *intel_crtc, + const struct skl_wm_values *wm); +void skl_write_plane_wm(struct intel_crtc *intel_crtc, + const struct skl_wm_values *wm, + int plane); uint32_t ilk_pipe_pixel_rate(const struct intel_crtc_state *pipe_config); bool ilk_disable_lp_wm(struct drm_device *dev); int sanitize_rc6_option(struct drm_i915_private *dev_priv, int enable_rc6); diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index de8e9fb..b2e3d3a 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -312,7 +312,8 @@ static inline bool is_cmd_mode(struct intel_dsi *intel_dsi) } static bool intel_dsi_compute_config(struct intel_encoder *encoder, - struct intel_crtc_state *pipe_config) + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_dsi *intel_dsi = container_of(encoder, struct intel_dsi, @@ -533,14 +534,15 @@ static void intel_dsi_enable(struct intel_encoder *encoder) intel_panel_enable_backlight(intel_dsi->attached_connector); } -static void intel_dsi_prepare(struct intel_encoder *intel_encoder); +static void intel_dsi_prepare(struct intel_encoder *intel_encoder, + struct intel_crtc_state *pipe_config); -static void intel_dsi_pre_enable(struct intel_encoder *encoder) +static void intel_dsi_pre_enable(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { - struct drm_device *dev = encoder->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); - struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); enum port port; DRM_DEBUG_KMS("\n"); @@ -550,9 +552,9 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder) * lock. It needs to be fully powered down to fix it. */ intel_disable_dsi_pll(encoder); - intel_enable_dsi_pll(encoder, crtc->config); + intel_enable_dsi_pll(encoder, pipe_config); - intel_dsi_prepare(encoder); + intel_dsi_prepare(encoder, pipe_config); /* Panel Enable over CRC PMIC */ if (intel_dsi->gpio_panel) @@ -582,7 +584,9 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder) intel_dsi_enable(encoder); } -static void intel_dsi_enable_nop(struct intel_encoder *encoder) +static void intel_dsi_enable_nop(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { DRM_DEBUG_KMS("\n"); @@ -592,7 +596,9 @@ static void intel_dsi_enable_nop(struct intel_encoder *encoder) */ } -static void intel_dsi_pre_disable(struct intel_encoder *encoder) +static void intel_dsi_pre_disable(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); enum port port; @@ -694,7 +700,9 @@ static void intel_dsi_clear_device_ready(struct intel_encoder *encoder) intel_disable_dsi_pll(encoder); } -static void intel_dsi_post_disable(struct intel_encoder *encoder) +static void intel_dsi_post_disable(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); @@ -819,6 +827,7 @@ static void bxt_dsi_get_pipe_config(struct intel_encoder *encoder, u16 crtc_htotal_sw, crtc_hsync_start_sw, crtc_hsync_end_sw, crtc_hblank_start_sw, crtc_hblank_end_sw; + /* FIXME: hw readout should not depend on SW state */ intel_crtc = to_intel_crtc(encoder->base.crtc); adjusted_mode_sw = &intel_crtc->config->base.adjusted_mode; @@ -1104,14 +1113,15 @@ static u32 pixel_format_to_reg(enum mipi_dsi_pixel_format fmt) } } -static void intel_dsi_prepare(struct intel_encoder *intel_encoder) +static void intel_dsi_prepare(struct intel_encoder *intel_encoder, + struct intel_crtc_state *pipe_config) { struct drm_encoder *encoder = &intel_encoder->base; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); + struct intel_crtc *intel_crtc = to_intel_crtc(pipe_config->base.crtc); struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder); - const struct drm_display_mode *adjusted_mode = &intel_crtc->config->base.adjusted_mode; + const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; enum port port; unsigned int bpp = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); u32 val, tmp; @@ -1348,7 +1358,7 @@ static int intel_dsi_set_property(struct drm_connector *connector, intel_connector->panel.fitting_mode = val; } - crtc = intel_attached_encoder(connector)->base.crtc; + crtc = connector->state->crtc; if (crtc && crtc->state->enable) { /* * If the CRTC is enabled, the display will be changed diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 47bdf9d..2e452c5 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -174,7 +174,9 @@ static void intel_dvo_get_config(struct intel_encoder *encoder, pipe_config->base.adjusted_mode.crtc_clock = pipe_config->port_clock; } -static void intel_disable_dvo(struct intel_encoder *encoder) +static void intel_disable_dvo(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_dvo *intel_dvo = enc_to_dvo(encoder); @@ -186,17 +188,18 @@ static void intel_disable_dvo(struct intel_encoder *encoder) I915_READ(dvo_reg); } -static void intel_enable_dvo(struct intel_encoder *encoder) +static void intel_enable_dvo(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_dvo *intel_dvo = enc_to_dvo(encoder); - struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); i915_reg_t dvo_reg = intel_dvo->dev.dvo_reg; u32 temp = I915_READ(dvo_reg); intel_dvo->dev.dev_ops->mode_set(&intel_dvo->dev, - &crtc->config->base.mode, - &crtc->config->base.adjusted_mode); + &pipe_config->base.mode, + &pipe_config->base.adjusted_mode); I915_WRITE(dvo_reg, temp | DVO_ENABLE); I915_READ(dvo_reg); @@ -235,7 +238,8 @@ intel_dvo_mode_valid(struct drm_connector *connector, } static bool intel_dvo_compute_config(struct intel_encoder *encoder, - struct intel_crtc_state *pipe_config) + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct intel_dvo *intel_dvo = enc_to_dvo(encoder); const struct drm_display_mode *fixed_mode = @@ -253,12 +257,13 @@ static bool intel_dvo_compute_config(struct intel_encoder *encoder, return true; } -static void intel_dvo_pre_enable(struct intel_encoder *encoder) +static void intel_dvo_pre_enable(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { - struct drm_device *dev = encoder->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); - const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode; + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct intel_crtc *crtc = to_intel_crtc(pipe_config->base.crtc); + const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; struct intel_dvo *intel_dvo = enc_to_dvo(encoder); int pipe = crtc->pipe; u32 dvo_val; @@ -554,7 +559,6 @@ void intel_dvo_init(struct drm_device *dev) return; } - drm_encoder_cleanup(&intel_encoder->base); kfree(intel_dvo); kfree(intel_connector); } diff --git a/drivers/gpu/drm/i915/intel_engine_cs.c b/drivers/gpu/drm/i915/intel_engine_cs.c new file mode 100644 index 0000000..e405f10 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_engine_cs.c @@ -0,0 +1,336 @@ +/* + * Copyright © 2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#include "i915_drv.h" +#include "intel_ringbuffer.h" +#include "intel_lrc.h" + +static const struct engine_info { + const char *name; + unsigned exec_id; + enum intel_engine_hw_id hw_id; + u32 mmio_base; + unsigned irq_shift; + int (*init_legacy)(struct intel_engine_cs *engine); + int (*init_execlists)(struct intel_engine_cs *engine); +} intel_engines[] = { + [RCS] = { + .name = "render ring", + .exec_id = I915_EXEC_RENDER, + .hw_id = RCS_HW, + .mmio_base = RENDER_RING_BASE, + .irq_shift = GEN8_RCS_IRQ_SHIFT, + .init_execlists = logical_render_ring_init, + .init_legacy = intel_init_render_ring_buffer, + }, + [BCS] = { + .name = "blitter ring", + .exec_id = I915_EXEC_BLT, + .hw_id = BCS_HW, + .mmio_base = BLT_RING_BASE, + .irq_shift = GEN8_BCS_IRQ_SHIFT, + .init_execlists = logical_xcs_ring_init, + .init_legacy = intel_init_blt_ring_buffer, + }, + [VCS] = { + .name = "bsd ring", + .exec_id = I915_EXEC_BSD, + .hw_id = VCS_HW, + .mmio_base = GEN6_BSD_RING_BASE, + .irq_shift = GEN8_VCS1_IRQ_SHIFT, + .init_execlists = logical_xcs_ring_init, + .init_legacy = intel_init_bsd_ring_buffer, + }, + [VCS2] = { + .name = "bsd2 ring", + .exec_id = I915_EXEC_BSD, + .hw_id = VCS2_HW, + .mmio_base = GEN8_BSD2_RING_BASE, + .irq_shift = GEN8_VCS2_IRQ_SHIFT, + .init_execlists = logical_xcs_ring_init, + .init_legacy = intel_init_bsd2_ring_buffer, + }, + [VECS] = { + .name = "video enhancement ring", + .exec_id = I915_EXEC_VEBOX, + .hw_id = VECS_HW, + .mmio_base = VEBOX_RING_BASE, + .irq_shift = GEN8_VECS_IRQ_SHIFT, + .init_execlists = logical_xcs_ring_init, + .init_legacy = intel_init_vebox_ring_buffer, + }, +}; + +static struct intel_engine_cs * +intel_engine_setup(struct drm_i915_private *dev_priv, + enum intel_engine_id id) +{ + const struct engine_info *info = &intel_engines[id]; + struct intel_engine_cs *engine = &dev_priv->engine[id]; + + engine->id = id; + engine->i915 = dev_priv; + engine->name = info->name; + engine->exec_id = info->exec_id; + engine->hw_id = engine->guc_id = info->hw_id; + engine->mmio_base = info->mmio_base; + engine->irq_shift = info->irq_shift; + + return engine; +} + +/** + * intel_engines_init() - allocate, populate and init the Engine Command Streamers + * @dev: DRM device. + * + * Return: non-zero if the initialization failed. + */ +int intel_engines_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_device_info *device_info = mkwrite_device_info(dev_priv); + unsigned int mask = 0; + int (*init)(struct intel_engine_cs *engine); + unsigned int i; + int ret; + + WARN_ON(INTEL_INFO(dev_priv)->ring_mask == 0); + WARN_ON(INTEL_INFO(dev_priv)->ring_mask & + GENMASK(sizeof(mask) * BITS_PER_BYTE - 1, I915_NUM_ENGINES)); + + for (i = 0; i < ARRAY_SIZE(intel_engines); i++) { + if (!HAS_ENGINE(dev_priv, i)) + continue; + + if (i915.enable_execlists) + init = intel_engines[i].init_execlists; + else + init = intel_engines[i].init_legacy; + + if (!init) + continue; + + ret = init(intel_engine_setup(dev_priv, i)); + if (ret) + goto cleanup; + + mask |= ENGINE_MASK(i); + } + + /* + * Catch failures to update intel_engines table when the new engines + * are added to the driver by a warning and disabling the forgotten + * engines. + */ + if (WARN_ON(mask != INTEL_INFO(dev_priv)->ring_mask)) + device_info->ring_mask = mask; + + device_info->num_rings = hweight32(mask); + + return 0; + +cleanup: + for (i = 0; i < I915_NUM_ENGINES; i++) { + if (i915.enable_execlists) + intel_logical_ring_cleanup(&dev_priv->engine[i]); + else + intel_engine_cleanup(&dev_priv->engine[i]); + } + + return ret; +} + +void intel_engine_init_seqno(struct intel_engine_cs *engine, u32 seqno) +{ + struct drm_i915_private *dev_priv = engine->i915; + + /* Our semaphore implementation is strictly monotonic (i.e. we proceed + * so long as the semaphore value in the register/page is greater + * than the sync value), so whenever we reset the seqno, + * so long as we reset the tracking semaphore value to 0, it will + * always be before the next request's seqno. If we don't reset + * the semaphore value, then when the seqno moves backwards all + * future waits will complete instantly (causing rendering corruption). + */ + if (IS_GEN6(dev_priv) || IS_GEN7(dev_priv)) { + I915_WRITE(RING_SYNC_0(engine->mmio_base), 0); + I915_WRITE(RING_SYNC_1(engine->mmio_base), 0); + if (HAS_VEBOX(dev_priv)) + I915_WRITE(RING_SYNC_2(engine->mmio_base), 0); + } + if (dev_priv->semaphore) { + struct page *page = i915_vma_first_page(dev_priv->semaphore); + void *semaphores; + + /* Semaphores are in noncoherent memory, flush to be safe */ + semaphores = kmap(page); + memset(semaphores + GEN8_SEMAPHORE_OFFSET(engine->id, 0), + 0, I915_NUM_ENGINES * gen8_semaphore_seqno_size); + drm_clflush_virt_range(semaphores + GEN8_SEMAPHORE_OFFSET(engine->id, 0), + I915_NUM_ENGINES * gen8_semaphore_seqno_size); + kunmap(page); + } + memset(engine->semaphore.sync_seqno, 0, + sizeof(engine->semaphore.sync_seqno)); + + intel_write_status_page(engine, I915_GEM_HWS_INDEX, seqno); + if (engine->irq_seqno_barrier) + engine->irq_seqno_barrier(engine); + engine->last_submitted_seqno = seqno; + + engine->hangcheck.seqno = seqno; + + /* After manually advancing the seqno, fake the interrupt in case + * there are any waiters for that seqno. + */ + intel_engine_wakeup(engine); +} + +void intel_engine_init_hangcheck(struct intel_engine_cs *engine) +{ + memset(&engine->hangcheck, 0, sizeof(engine->hangcheck)); + clear_bit(engine->id, &engine->i915->gpu_error.missed_irq_rings); + if (intel_engine_has_waiter(engine)) + i915_queue_hangcheck(engine->i915); +} + +static void intel_engine_init_requests(struct intel_engine_cs *engine) +{ + init_request_active(&engine->last_request, NULL); + INIT_LIST_HEAD(&engine->request_list); +} + +/** + * intel_engines_setup_common - setup engine state not requiring hw access + * @engine: Engine to setup. + * + * Initializes @engine@ structure members shared between legacy and execlists + * submission modes which do not require hardware access. + * + * Typically done early in the submission mode specific engine setup stage. + */ +void intel_engine_setup_common(struct intel_engine_cs *engine) +{ + INIT_LIST_HEAD(&engine->execlist_queue); + spin_lock_init(&engine->execlist_lock); + + engine->fence_context = fence_context_alloc(1); + + intel_engine_init_requests(engine); + intel_engine_init_hangcheck(engine); + i915_gem_batch_pool_init(engine, &engine->batch_pool); + + intel_engine_init_cmd_parser(engine); +} + +int intel_engine_create_scratch(struct intel_engine_cs *engine, int size) +{ + struct drm_i915_gem_object *obj; + struct i915_vma *vma; + int ret; + + WARN_ON(engine->scratch); + + obj = i915_gem_object_create_stolen(&engine->i915->drm, size); + if (!obj) + obj = i915_gem_object_create(&engine->i915->drm, size); + if (IS_ERR(obj)) { + DRM_ERROR("Failed to allocate scratch page\n"); + return PTR_ERR(obj); + } + + vma = i915_vma_create(obj, &engine->i915->ggtt.base, NULL); + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); + goto err_unref; + } + + ret = i915_vma_pin(vma, 0, 4096, PIN_GLOBAL | PIN_HIGH); + if (ret) + goto err_unref; + + engine->scratch = vma; + DRM_DEBUG_DRIVER("%s pipe control offset: 0x%08x\n", + engine->name, i915_ggtt_offset(vma)); + return 0; + +err_unref: + i915_gem_object_put(obj); + return ret; +} + +static void intel_engine_cleanup_scratch(struct intel_engine_cs *engine) +{ + i915_vma_unpin_and_release(&engine->scratch); +} + +/** + * intel_engines_init_common - initialize cengine state which might require hw access + * @engine: Engine to initialize. + * + * Initializes @engine@ structure members shared between legacy and execlists + * submission modes which do require hardware access. + * + * Typcally done at later stages of submission mode specific engine setup. + * + * Returns zero on success or an error code on failure. + */ +int intel_engine_init_common(struct intel_engine_cs *engine) +{ + int ret; + + ret = intel_engine_init_breadcrumbs(engine); + if (ret) + return ret; + + return 0; +} + +void intel_engine_reset_irq(struct intel_engine_cs *engine) +{ + struct drm_i915_private *dev_priv = engine->i915; + + spin_lock_irq(&dev_priv->irq_lock); + if (intel_engine_has_waiter(engine)) + engine->irq_enable(engine); + else + engine->irq_disable(engine); + spin_unlock_irq(&dev_priv->irq_lock); +} + +/** + * intel_engines_cleanup_common - cleans up the engine state created by + * the common initiailizers. + * @engine: Engine to cleanup. + * + * This cleans up everything created by the common helpers. + */ +void intel_engine_cleanup_common(struct intel_engine_cs *engine) +{ + intel_engine_cleanup_scratch(engine); + + intel_engine_fini_breadcrumbs(engine); + intel_engine_cleanup_cmd_parser(engine); + i915_gem_batch_pool_fini(&engine->batch_pool); +} diff --git a/drivers/gpu/drm/i915/intel_fbc.c b/drivers/gpu/drm/i915/intel_fbc.c index 6a7ad3e..faa6762 100644 --- a/drivers/gpu/drm/i915/intel_fbc.c +++ b/drivers/gpu/drm/i915/intel_fbc.c @@ -190,9 +190,13 @@ static void g4x_fbc_activate(struct drm_i915_private *dev_priv) dpfc_ctl |= DPFC_CTL_LIMIT_2X; else dpfc_ctl |= DPFC_CTL_LIMIT_1X; - dpfc_ctl |= DPFC_CTL_FENCE_EN | params->fb.fence_reg; - I915_WRITE(DPFC_FENCE_YOFF, params->crtc.fence_y_offset); + if (params->fb.fence_reg != I915_FENCE_REG_NONE) { + dpfc_ctl |= DPFC_CTL_FENCE_EN | params->fb.fence_reg; + I915_WRITE(DPFC_FENCE_YOFF, params->crtc.fence_y_offset); + } else { + I915_WRITE(DPFC_FENCE_YOFF, 0); + } /* enable it... */ I915_WRITE(DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN); @@ -244,21 +248,29 @@ static void ilk_fbc_activate(struct drm_i915_private *dev_priv) dpfc_ctl |= DPFC_CTL_LIMIT_1X; break; } - dpfc_ctl |= DPFC_CTL_FENCE_EN; - if (IS_GEN5(dev_priv)) - dpfc_ctl |= params->fb.fence_reg; + + if (params->fb.fence_reg != I915_FENCE_REG_NONE) { + dpfc_ctl |= DPFC_CTL_FENCE_EN; + if (IS_GEN5(dev_priv)) + dpfc_ctl |= params->fb.fence_reg; + if (IS_GEN6(dev_priv)) { + I915_WRITE(SNB_DPFC_CTL_SA, + SNB_CPU_FENCE_ENABLE | params->fb.fence_reg); + I915_WRITE(DPFC_CPU_FENCE_OFFSET, + params->crtc.fence_y_offset); + } + } else { + if (IS_GEN6(dev_priv)) { + I915_WRITE(SNB_DPFC_CTL_SA, 0); + I915_WRITE(DPFC_CPU_FENCE_OFFSET, 0); + } + } I915_WRITE(ILK_DPFC_FENCE_YOFF, params->crtc.fence_y_offset); I915_WRITE(ILK_FBC_RT_BASE, params->fb.ggtt_offset | ILK_FBC_RT_VALID); /* enable it... */ I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN); - if (IS_GEN6(dev_priv)) { - I915_WRITE(SNB_DPFC_CTL_SA, - SNB_CPU_FENCE_ENABLE | params->fb.fence_reg); - I915_WRITE(DPFC_CPU_FENCE_OFFSET, params->crtc.fence_y_offset); - } - intel_fbc_recompress(dev_priv); } @@ -305,7 +317,15 @@ static void gen7_fbc_activate(struct drm_i915_private *dev_priv) break; } - dpfc_ctl |= IVB_DPFC_CTL_FENCE_EN; + if (params->fb.fence_reg != I915_FENCE_REG_NONE) { + dpfc_ctl |= IVB_DPFC_CTL_FENCE_EN; + I915_WRITE(SNB_DPFC_CTL_SA, + SNB_CPU_FENCE_ENABLE | params->fb.fence_reg); + I915_WRITE(DPFC_CPU_FENCE_OFFSET, params->crtc.fence_y_offset); + } else { + I915_WRITE(SNB_DPFC_CTL_SA,0); + I915_WRITE(DPFC_CPU_FENCE_OFFSET, 0); + } if (dev_priv->fbc.false_color) dpfc_ctl |= FBC_CTL_FALSE_COLOR; @@ -324,10 +344,6 @@ static void gen7_fbc_activate(struct drm_i915_private *dev_priv) I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN); - I915_WRITE(SNB_DPFC_CTL_SA, - SNB_CPU_FENCE_ENABLE | params->fb.fence_reg); - I915_WRITE(DPFC_CPU_FENCE_OFFSET, params->crtc.fence_y_offset); - intel_fbc_recompress(dev_priv); } @@ -494,7 +510,7 @@ static bool multiple_pipes_ok(struct intel_crtc *crtc, if (!no_fbc_on_multiple_pipes(dev_priv)) return true; - if (plane_state->visible) + if (plane_state->base.visible) fbc->visible_pipes_mask |= (1 << pipe); else fbc->visible_pipes_mask &= ~(1 << pipe); @@ -709,6 +725,14 @@ static bool intel_fbc_hw_tracking_covers_screen(struct intel_crtc *crtc) return effective_w <= max_w && effective_h <= max_h; } +/* XXX replace me when we have VMA tracking for intel_plane_state */ +static int get_fence_id(struct drm_framebuffer *fb) +{ + struct i915_vma *vma = i915_gem_object_to_ggtt(intel_fb_obj(fb), NULL); + + return vma && vma->fence ? vma->fence->id : I915_FENCE_REG_NONE; +} + static void intel_fbc_update_state_cache(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, struct intel_plane_state *plane_state) @@ -725,9 +749,9 @@ static void intel_fbc_update_state_cache(struct intel_crtc *crtc, ilk_pipe_pixel_rate(crtc_state); cache->plane.rotation = plane_state->base.rotation; - cache->plane.src_w = drm_rect_width(&plane_state->src) >> 16; - cache->plane.src_h = drm_rect_height(&plane_state->src) >> 16; - cache->plane.visible = plane_state->visible; + cache->plane.src_w = drm_rect_width(&plane_state->base.src) >> 16; + cache->plane.src_h = drm_rect_height(&plane_state->base.src) >> 16; + cache->plane.visible = plane_state->base.visible; if (!cache->plane.visible) return; @@ -737,11 +761,11 @@ static void intel_fbc_update_state_cache(struct intel_crtc *crtc, /* FIXME: We lack the proper locking here, so only run this on the * platforms that need. */ if (IS_GEN(dev_priv, 5, 6)) - cache->fb.ilk_ggtt_offset = i915_gem_obj_ggtt_offset(obj); + cache->fb.ilk_ggtt_offset = i915_gem_object_ggtt_offset(obj, NULL); cache->fb.pixel_format = fb->pixel_format; cache->fb.stride = fb->pitches[0]; - cache->fb.fence_reg = obj->fence_reg; - cache->fb.tiling_mode = obj->tiling_mode; + cache->fb.fence_reg = get_fence_id(fb); + cache->fb.tiling_mode = i915_gem_object_get_tiling(obj); } static bool intel_fbc_can_activate(struct intel_crtc *crtc) @@ -768,6 +792,10 @@ static bool intel_fbc_can_activate(struct intel_crtc *crtc) /* The use of a CPU fence is mandatory in order to detect writes * by the CPU to the scanout and trigger updates to the FBC. + * + * Note that is possible for a tiled surface to be unmappable (and + * so have no fence associated with it) due to aperture constaints + * at the time of pinning. */ if (cache->fb.tiling_mode != I915_TILING_X || cache->fb.fence_reg == I915_FENCE_REG_NONE) { @@ -775,7 +803,7 @@ static bool intel_fbc_can_activate(struct intel_crtc *crtc) return false; } if (INTEL_INFO(dev_priv)->gen <= 4 && !IS_G4X(dev_priv) && - cache->plane.rotation != BIT(DRM_ROTATE_0)) { + cache->plane.rotation != DRM_ROTATE_0) { fbc->no_fbc_reason = "rotation unsupported"; return false; } @@ -1050,7 +1078,7 @@ void intel_fbc_choose_crtc(struct drm_i915_private *dev_priv, struct intel_plane_state *intel_plane_state = to_intel_plane_state(plane_state); - if (!intel_plane_state->visible) + if (!intel_plane_state->base.visible) continue; for_each_crtc_in_state(state, crtc, crtc_state, j) { @@ -1075,6 +1103,8 @@ out: /** * intel_fbc_enable: tries to enable FBC on the CRTC * @crtc: the CRTC + * @crtc_state: corresponding &drm_crtc_state for @crtc + * @plane_state: corresponding &drm_plane_state for the primary plane of @crtc * * This function checks if the given CRTC was chosen for FBC, then enables it if * possible. Notice that it doesn't activate FBC. It is valid to call @@ -1163,11 +1193,8 @@ void intel_fbc_disable(struct intel_crtc *crtc) return; mutex_lock(&fbc->lock); - if (fbc->crtc == crtc) { - WARN_ON(!fbc->enabled); - WARN_ON(fbc->active); + if (fbc->crtc == crtc) __intel_fbc_disable(dev_priv); - } mutex_unlock(&fbc->lock); cancel_work_sync(&fbc->work.work); @@ -1212,7 +1239,7 @@ void intel_fbc_init_pipe_state(struct drm_i915_private *dev_priv) for_each_intel_crtc(&dev_priv->drm, crtc) if (intel_crtc_active(&crtc->base) && - to_intel_plane_state(crtc->base.primary->state)->visible) + to_intel_plane_state(crtc->base.primary->state)->base.visible) dev_priv->fbc.visible_pipes_mask |= (1 << crtc->pipe); } @@ -1230,12 +1257,29 @@ static int intel_sanitize_fbc_option(struct drm_i915_private *dev_priv) if (i915.enable_fbc >= 0) return !!i915.enable_fbc; + if (!HAS_FBC(dev_priv)) + return 0; + if (IS_BROADWELL(dev_priv)) return 1; return 0; } +static bool need_fbc_vtd_wa(struct drm_i915_private *dev_priv) +{ +#ifdef CONFIG_INTEL_IOMMU + /* WaFbcTurnOffFbcWhenHyperVisorIsUsed:skl,bxt */ + if (intel_iommu_gfx_mapped && + (IS_SKYLAKE(dev_priv) || IS_BROXTON(dev_priv))) { + DRM_INFO("Disabling framebuffer compression (FBC) to prevent screen flicker with VT-d enabled\n"); + return true; + } +#endif + + return false; +} + /** * intel_fbc_init - Initialize FBC * @dev_priv: the i915 device @@ -1253,6 +1297,9 @@ void intel_fbc_init(struct drm_i915_private *dev_priv) fbc->active = false; fbc->work.scheduled = false; + if (need_fbc_vtd_wa(dev_priv)) + mkwrite_device_info(dev_priv)->has_fbc = false; + i915.enable_fbc = intel_sanitize_fbc_option(dev_priv); DRM_DEBUG_KMS("Sanitized enable_fbc value: %d\n", i915.enable_fbc); diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 86b00c6..b7098f9 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -34,7 +34,6 @@ #include <linux/tty.h> #include <linux/sysrq.h> #include <linux/delay.h> -#include <linux/fb.h> #include <linux/init.h> #include <linux/vga_switcheroo.h> @@ -42,6 +41,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_fb_helper.h> #include "intel_drv.h" +#include "intel_frontbuffer.h" #include <drm/i915_drm.h> #include "i915_drv.h" @@ -159,7 +159,7 @@ static int intelfb_alloc(struct drm_fb_helper *helper, fb = __intel_framebuffer_create(dev, &mode_cmd, obj); if (IS_ERR(fb)) { - drm_gem_object_unreference(&obj->base); + i915_gem_object_put(obj); ret = PTR_ERR(fb); goto out; } @@ -183,13 +183,13 @@ static int intelfb_create(struct drm_fb_helper *helper, struct intel_framebuffer *intel_fb = ifbdev->fb; struct drm_device *dev = helper->dev; struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; struct i915_ggtt *ggtt = &dev_priv->ggtt; struct fb_info *info; struct drm_framebuffer *fb; struct i915_vma *vma; - struct drm_i915_gem_object *obj; bool prealloc = false; - void *vaddr; + void __iomem *vaddr; int ret; if (intel_fb && @@ -215,17 +215,17 @@ static int intelfb_create(struct drm_fb_helper *helper, sizes->fb_height = intel_fb->base.height; } - obj = intel_fb->obj; - mutex_lock(&dev->struct_mutex); /* Pin the GGTT vma for our access via info->screen_base. * This also validates that any existing fb inherited from the * BIOS is suitable for own access. */ - ret = intel_pin_and_fence_fb_obj(&ifbdev->fb->base, BIT(DRM_ROTATE_0)); - if (ret) + vma = intel_pin_and_fence_fb_obj(&ifbdev->fb->base, DRM_ROTATE_0); + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); goto out_unlock; + } info = drm_fb_helper_alloc_fbi(helper); if (IS_ERR(info)) { @@ -245,13 +245,11 @@ static int intelfb_create(struct drm_fb_helper *helper, info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; info->fbops = &intelfb_ops; - vma = i915_gem_obj_to_ggtt(obj); - /* setup aperture base/size for vesafb takeover */ info->apertures->ranges[0].base = dev->mode_config.fb_base; info->apertures->ranges[0].size = ggtt->mappable_end; - info->fix.smem_start = dev->mode_config.fb_base + vma->node.start; + info->fix.smem_start = dev->mode_config.fb_base + i915_ggtt_offset(vma); info->fix.smem_len = vma->node.size; vaddr = i915_vma_pin_iomap(vma); @@ -273,23 +271,23 @@ static int intelfb_create(struct drm_fb_helper *helper, * If the object is stolen however, it will be full of whatever * garbage was left in there. */ - if (ifbdev->fb->obj->stolen && !prealloc) + if (intel_fb->obj->stolen && !prealloc) memset_io(info->screen_base, 0, info->screen_size); /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ - DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08llx, bo %p\n", - fb->width, fb->height, - i915_gem_obj_ggtt_offset(obj), obj); + DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x\n", + fb->width, fb->height, i915_ggtt_offset(vma)); + ifbdev->vma = vma; mutex_unlock(&dev->struct_mutex); - vga_switcheroo_client_fb_set(dev->pdev, info); + vga_switcheroo_client_fb_set(pdev, info); return 0; out_destroy_fbi: drm_fb_helper_release_fbi(helper); out_unpin: - intel_unpin_fb_obj(&ifbdev->fb->base, BIT(DRM_ROTATE_0)); + intel_unpin_fb_obj(&ifbdev->fb->base, DRM_ROTATE_0); out_unlock: mutex_unlock(&dev->struct_mutex); return ret; @@ -554,7 +552,7 @@ static void intel_fbdev_destroy(struct intel_fbdev *ifbdev) if (ifbdev->fb) { mutex_lock(&ifbdev->helper.dev->struct_mutex); - intel_unpin_fb_obj(&ifbdev->fb->base, BIT(DRM_ROTATE_0)); + intel_unpin_fb_obj(&ifbdev->fb->base, DRM_ROTATE_0); mutex_unlock(&ifbdev->helper.dev->struct_mutex); drm_framebuffer_remove(&ifbdev->fb->base); @@ -768,7 +766,7 @@ void intel_fbdev_fini(struct drm_device *dev) if (!ifbdev) return; - flush_work(&dev_priv->fbdev_suspend_work); + cancel_work_sync(&dev_priv->fbdev_suspend_work); if (!current_is_async()) intel_fbdev_sync(ifbdev); @@ -782,7 +780,7 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous struct intel_fbdev *ifbdev = dev_priv->fbdev; struct fb_info *info; - if (!ifbdev) + if (!ifbdev || !ifbdev->fb) return; info = ifbdev->helper.fbdev; @@ -827,31 +825,28 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous void intel_fbdev_output_poll_changed(struct drm_device *dev) { - struct drm_i915_private *dev_priv = to_i915(dev); - if (dev_priv->fbdev) - drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper); + struct intel_fbdev *ifbdev = to_i915(dev)->fbdev; + + if (ifbdev && ifbdev->fb) + drm_fb_helper_hotplug_event(&ifbdev->helper); } void intel_fbdev_restore_mode(struct drm_device *dev) { - int ret; - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_fbdev *ifbdev = dev_priv->fbdev; - struct drm_fb_helper *fb_helper; + struct intel_fbdev *ifbdev = to_i915(dev)->fbdev; if (!ifbdev) return; intel_fbdev_sync(ifbdev); + if (!ifbdev->fb) + return; - fb_helper = &ifbdev->helper; - - ret = drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper); - if (ret) { + if (drm_fb_helper_restore_fbdev_mode_unlocked(&ifbdev->helper)) { DRM_DEBUG("failed to restore crtc mode\n"); } else { - mutex_lock(&fb_helper->dev->struct_mutex); + mutex_lock(&dev->struct_mutex); intel_fb_obj_invalidate(ifbdev->fb->obj, ORIGIN_GTT); - mutex_unlock(&fb_helper->dev->struct_mutex); + mutex_unlock(&dev->struct_mutex); } } diff --git a/drivers/gpu/drm/i915/intel_frontbuffer.c b/drivers/gpu/drm/i915/intel_frontbuffer.c index ac85357..966de4c 100644 --- a/drivers/gpu/drm/i915/intel_frontbuffer.c +++ b/drivers/gpu/drm/i915/intel_frontbuffer.c @@ -63,47 +63,30 @@ #include <drm/drmP.h> #include "intel_drv.h" +#include "intel_frontbuffer.h" #include "i915_drv.h" -/** - * intel_fb_obj_invalidate - invalidate frontbuffer object - * @obj: GEM object to invalidate - * @origin: which operation caused the invalidation - * - * This function gets called every time rendering on the given object starts and - * frontbuffer caching (fbc, low refresh rate for DRRS, panel self refresh) must - * be invalidated. For ORIGIN_CS any subsequent invalidation will be delayed - * until the rendering completes or a flip on this frontbuffer plane is - * scheduled. - */ -void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj, - enum fb_op_origin origin) +void __intel_fb_obj_invalidate(struct drm_i915_gem_object *obj, + enum fb_op_origin origin, + unsigned int frontbuffer_bits) { - struct drm_device *dev = obj->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - - if (!obj->frontbuffer_bits) - return; + struct drm_i915_private *dev_priv = to_i915(obj->base.dev); if (origin == ORIGIN_CS) { - mutex_lock(&dev_priv->fb_tracking.lock); - dev_priv->fb_tracking.busy_bits - |= obj->frontbuffer_bits; - dev_priv->fb_tracking.flip_bits - &= ~obj->frontbuffer_bits; - mutex_unlock(&dev_priv->fb_tracking.lock); + spin_lock(&dev_priv->fb_tracking.lock); + dev_priv->fb_tracking.busy_bits |= frontbuffer_bits; + dev_priv->fb_tracking.flip_bits &= ~frontbuffer_bits; + spin_unlock(&dev_priv->fb_tracking.lock); } - intel_psr_invalidate(dev, obj->frontbuffer_bits); - intel_edp_drrs_invalidate(dev, obj->frontbuffer_bits); - intel_fbc_invalidate(dev_priv, obj->frontbuffer_bits, origin); + intel_psr_invalidate(dev_priv, frontbuffer_bits); + intel_edp_drrs_invalidate(dev_priv, frontbuffer_bits); + intel_fbc_invalidate(dev_priv, frontbuffer_bits, origin); } /** * intel_frontbuffer_flush - flush frontbuffer - * @dev: DRM device + * @dev_priv: i915 device * @frontbuffer_bits: frontbuffer plane tracking bits * @origin: which operation caused the flush * @@ -113,64 +96,45 @@ void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj, * * Can be called without any locks held. */ -static void intel_frontbuffer_flush(struct drm_device *dev, +static void intel_frontbuffer_flush(struct drm_i915_private *dev_priv, unsigned frontbuffer_bits, enum fb_op_origin origin) { - struct drm_i915_private *dev_priv = to_i915(dev); - /* Delay flushing when rings are still busy.*/ - mutex_lock(&dev_priv->fb_tracking.lock); + spin_lock(&dev_priv->fb_tracking.lock); frontbuffer_bits &= ~dev_priv->fb_tracking.busy_bits; - mutex_unlock(&dev_priv->fb_tracking.lock); + spin_unlock(&dev_priv->fb_tracking.lock); if (!frontbuffer_bits) return; - intel_edp_drrs_flush(dev, frontbuffer_bits); - intel_psr_flush(dev, frontbuffer_bits, origin); + intel_edp_drrs_flush(dev_priv, frontbuffer_bits); + intel_psr_flush(dev_priv, frontbuffer_bits, origin); intel_fbc_flush(dev_priv, frontbuffer_bits, origin); } -/** - * intel_fb_obj_flush - flush frontbuffer object - * @obj: GEM object to flush - * @retire: set when retiring asynchronous rendering - * @origin: which operation caused the flush - * - * This function gets called every time rendering on the given object has - * completed and frontbuffer caching can be started again. If @retire is true - * then any delayed flushes will be unblocked. - */ -void intel_fb_obj_flush(struct drm_i915_gem_object *obj, - bool retire, enum fb_op_origin origin) +void __intel_fb_obj_flush(struct drm_i915_gem_object *obj, + bool retire, + enum fb_op_origin origin, + unsigned int frontbuffer_bits) { - struct drm_device *dev = obj->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - unsigned frontbuffer_bits; - - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - - if (!obj->frontbuffer_bits) - return; - - frontbuffer_bits = obj->frontbuffer_bits; + struct drm_i915_private *dev_priv = to_i915(obj->base.dev); if (retire) { - mutex_lock(&dev_priv->fb_tracking.lock); + spin_lock(&dev_priv->fb_tracking.lock); /* Filter out new bits since rendering started. */ frontbuffer_bits &= dev_priv->fb_tracking.busy_bits; - dev_priv->fb_tracking.busy_bits &= ~frontbuffer_bits; - mutex_unlock(&dev_priv->fb_tracking.lock); + spin_unlock(&dev_priv->fb_tracking.lock); } - intel_frontbuffer_flush(dev, frontbuffer_bits, origin); + if (frontbuffer_bits) + intel_frontbuffer_flush(dev_priv, frontbuffer_bits, origin); } /** * intel_frontbuffer_flip_prepare - prepare asynchronous frontbuffer flip - * @dev: DRM device + * @dev_priv: i915 device * @frontbuffer_bits: frontbuffer plane tracking bits * * This function gets called after scheduling a flip on @obj. The actual @@ -180,23 +144,21 @@ void intel_fb_obj_flush(struct drm_i915_gem_object *obj, * * Can be called without any locks held. */ -void intel_frontbuffer_flip_prepare(struct drm_device *dev, +void intel_frontbuffer_flip_prepare(struct drm_i915_private *dev_priv, unsigned frontbuffer_bits) { - struct drm_i915_private *dev_priv = to_i915(dev); - - mutex_lock(&dev_priv->fb_tracking.lock); + spin_lock(&dev_priv->fb_tracking.lock); dev_priv->fb_tracking.flip_bits |= frontbuffer_bits; /* Remove stale busy bits due to the old buffer. */ dev_priv->fb_tracking.busy_bits &= ~frontbuffer_bits; - mutex_unlock(&dev_priv->fb_tracking.lock); + spin_unlock(&dev_priv->fb_tracking.lock); - intel_psr_single_frame_update(dev, frontbuffer_bits); + intel_psr_single_frame_update(dev_priv, frontbuffer_bits); } /** * intel_frontbuffer_flip_complete - complete asynchronous frontbuffer flip - * @dev: DRM device + * @dev_priv: i915 device * @frontbuffer_bits: frontbuffer plane tracking bits * * This function gets called after the flip has been latched and will complete @@ -204,23 +166,23 @@ void intel_frontbuffer_flip_prepare(struct drm_device *dev, * * Can be called without any locks held. */ -void intel_frontbuffer_flip_complete(struct drm_device *dev, +void intel_frontbuffer_flip_complete(struct drm_i915_private *dev_priv, unsigned frontbuffer_bits) { - struct drm_i915_private *dev_priv = to_i915(dev); - - mutex_lock(&dev_priv->fb_tracking.lock); + spin_lock(&dev_priv->fb_tracking.lock); /* Mask any cancelled flips. */ frontbuffer_bits &= dev_priv->fb_tracking.flip_bits; dev_priv->fb_tracking.flip_bits &= ~frontbuffer_bits; - mutex_unlock(&dev_priv->fb_tracking.lock); + spin_unlock(&dev_priv->fb_tracking.lock); - intel_frontbuffer_flush(dev, frontbuffer_bits, ORIGIN_FLIP); + if (frontbuffer_bits) + intel_frontbuffer_flush(dev_priv, + frontbuffer_bits, ORIGIN_FLIP); } /** * intel_frontbuffer_flip - synchronous frontbuffer flip - * @dev: DRM device + * @dev_priv: i915 device * @frontbuffer_bits: frontbuffer plane tracking bits * * This function gets called after scheduling a flip on @obj. This is for @@ -229,15 +191,13 @@ void intel_frontbuffer_flip_complete(struct drm_device *dev, * * Can be called without any locks held. */ -void intel_frontbuffer_flip(struct drm_device *dev, +void intel_frontbuffer_flip(struct drm_i915_private *dev_priv, unsigned frontbuffer_bits) { - struct drm_i915_private *dev_priv = to_i915(dev); - - mutex_lock(&dev_priv->fb_tracking.lock); + spin_lock(&dev_priv->fb_tracking.lock); /* Remove stale busy bits due to the old buffer. */ dev_priv->fb_tracking.busy_bits &= ~frontbuffer_bits; - mutex_unlock(&dev_priv->fb_tracking.lock); + spin_unlock(&dev_priv->fb_tracking.lock); - intel_frontbuffer_flush(dev, frontbuffer_bits, ORIGIN_FLIP); + intel_frontbuffer_flush(dev_priv, frontbuffer_bits, ORIGIN_FLIP); } diff --git a/drivers/gpu/drm/i915/intel_frontbuffer.h b/drivers/gpu/drm/i915/intel_frontbuffer.h new file mode 100644 index 0000000..76ceb53 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_frontbuffer.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014-2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __INTEL_FRONTBUFFER_H__ +#define __INTEL_FRONTBUFFER_H__ + +struct drm_i915_private; +struct drm_i915_gem_object; + +void intel_frontbuffer_flip_prepare(struct drm_i915_private *dev_priv, + unsigned frontbuffer_bits); +void intel_frontbuffer_flip_complete(struct drm_i915_private *dev_priv, + unsigned frontbuffer_bits); +void intel_frontbuffer_flip(struct drm_i915_private *dev_priv, + unsigned frontbuffer_bits); + +void __intel_fb_obj_invalidate(struct drm_i915_gem_object *obj, + enum fb_op_origin origin, + unsigned int frontbuffer_bits); +void __intel_fb_obj_flush(struct drm_i915_gem_object *obj, + bool retire, + enum fb_op_origin origin, + unsigned int frontbuffer_bits); + +/** + * intel_fb_obj_invalidate - invalidate frontbuffer object + * @obj: GEM object to invalidate + * @origin: which operation caused the invalidation + * + * This function gets called every time rendering on the given object starts and + * frontbuffer caching (fbc, low refresh rate for DRRS, panel self refresh) must + * be invalidated. For ORIGIN_CS any subsequent invalidation will be delayed + * until the rendering completes or a flip on this frontbuffer plane is + * scheduled. + */ +static inline void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj, + enum fb_op_origin origin) +{ + unsigned int frontbuffer_bits; + + frontbuffer_bits = atomic_read(&obj->frontbuffer_bits); + if (!frontbuffer_bits) + return; + + __intel_fb_obj_invalidate(obj, origin, frontbuffer_bits); +} + +/** + * intel_fb_obj_flush - flush frontbuffer object + * @obj: GEM object to flush + * @retire: set when retiring asynchronous rendering + * @origin: which operation caused the flush + * + * This function gets called every time rendering on the given object has + * completed and frontbuffer caching can be started again. If @retire is true + * then any delayed flushes will be unblocked. + */ +static inline void intel_fb_obj_flush(struct drm_i915_gem_object *obj, + bool retire, + enum fb_op_origin origin) +{ + unsigned int frontbuffer_bits; + + frontbuffer_bits = atomic_read(&obj->frontbuffer_bits); + if (!frontbuffer_bits) + return; + + __intel_fb_obj_flush(obj, retire, origin, frontbuffer_bits); +} + +#endif /* __INTEL_FRONTBUFFER_H__ */ diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h index 3e3e743..b1ba869 100644 --- a/drivers/gpu/drm/i915/intel_guc.h +++ b/drivers/gpu/drm/i915/intel_guc.h @@ -63,26 +63,27 @@ struct drm_i915_gem_request; * retcode: errno from last guc_submit() */ struct i915_guc_client { - struct drm_i915_gem_object *client_obj; + struct i915_vma *vma; void *client_base; /* first page (only) of above */ struct i915_gem_context *owner; struct intel_guc *guc; + + uint32_t engines; /* bitmap of (host) engine ids */ uint32_t priority; uint32_t ctx_index; - uint32_t proc_desc_offset; + uint32_t doorbell_offset; uint32_t cookie; uint16_t doorbell_id; - uint16_t padding; /* Maintain alignment */ + uint16_t padding[3]; /* Maintain alignment */ + spinlock_t wq_lock; uint32_t wq_offset; uint32_t wq_size; uint32_t wq_tail; - uint32_t unused; /* Was 'wq_head' */ - + uint32_t wq_rsvd; uint32_t no_wq_space; - uint32_t q_fail; /* No longer used */ uint32_t b_fail; int retcode; @@ -125,11 +126,10 @@ struct intel_guc_fw { struct intel_guc { struct intel_guc_fw guc_fw; uint32_t log_flags; - struct drm_i915_gem_object *log_obj; - - struct drm_i915_gem_object *ads_obj; + struct i915_vma *log_vma; - struct drm_i915_gem_object *ctx_pool_obj; + struct i915_vma *ads_vma; + struct i915_vma *ctx_pool_vma; struct ida ctx_ids; struct i915_guc_client *execbuf_client; @@ -159,8 +159,7 @@ extern int intel_guc_resume(struct drm_device *dev); /* i915_guc_submission.c */ int i915_guc_submission_init(struct drm_i915_private *dev_priv); int i915_guc_submission_enable(struct drm_i915_private *dev_priv); -int i915_guc_wq_check_space(struct drm_i915_gem_request *rq); -int i915_guc_submit(struct drm_i915_gem_request *rq); +int i915_guc_wq_reserve(struct drm_i915_gem_request *rq); void i915_guc_submission_disable(struct drm_i915_private *dev_priv); void i915_guc_submission_fini(struct drm_i915_private *dev_priv); diff --git a/drivers/gpu/drm/i915/intel_guc_fwif.h b/drivers/gpu/drm/i915/intel_guc_fwif.h index 944786d..e40db2d 100644 --- a/drivers/gpu/drm/i915/intel_guc_fwif.h +++ b/drivers/gpu/drm/i915/intel_guc_fwif.h @@ -155,6 +155,7 @@ * * +-------------------------------+ * | guc_css_header | + * | | * | contains major/minor version | * +-------------------------------+ * | uCode | @@ -176,10 +177,10 @@ * * 1. Header, uCode and RSA are must-have components. * 2. All firmware components, if they present, are in the sequence illustrated - * in the layout table above. + * in the layout table above. * 3. Length info of each component can be found in header, in dwords. * 4. Modulus and exponent key are not required by driver. They may not appear - * in fw. So driver will load a truncated firmware in this case. + * in fw. So driver will load a truncated firmware in this case. */ struct guc_css_header { diff --git a/drivers/gpu/drm/i915/intel_guc_loader.c b/drivers/gpu/drm/i915/intel_guc_loader.c index 605c696..6fd39ef 100644 --- a/drivers/gpu/drm/i915/intel_guc_loader.c +++ b/drivers/gpu/drm/i915/intel_guc_loader.c @@ -59,13 +59,25 @@ * */ -#define I915_SKL_GUC_UCODE "i915/skl_guc_ver6_1.bin" +#define SKL_FW_MAJOR 6 +#define SKL_FW_MINOR 1 + +#define BXT_FW_MAJOR 8 +#define BXT_FW_MINOR 7 + +#define KBL_FW_MAJOR 9 +#define KBL_FW_MINOR 14 + +#define GUC_FW_PATH(platform, major, minor) \ + "i915/" __stringify(platform) "_guc_ver" __stringify(major) "_" __stringify(minor) ".bin" + +#define I915_SKL_GUC_UCODE GUC_FW_PATH(skl, SKL_FW_MAJOR, SKL_FW_MINOR) MODULE_FIRMWARE(I915_SKL_GUC_UCODE); -#define I915_BXT_GUC_UCODE "i915/bxt_guc_ver8_7.bin" +#define I915_BXT_GUC_UCODE GUC_FW_PATH(bxt, BXT_FW_MAJOR, BXT_FW_MINOR) MODULE_FIRMWARE(I915_BXT_GUC_UCODE); -#define I915_KBL_GUC_UCODE "i915/kbl_guc_ver9_14.bin" +#define I915_KBL_GUC_UCODE GUC_FW_PATH(kbl, KBL_FW_MAJOR, KBL_FW_MINOR) MODULE_FIRMWARE(I915_KBL_GUC_UCODE); /* User-friendly representation of an enum */ @@ -85,7 +97,7 @@ const char *intel_guc_fw_status_repr(enum intel_guc_fw_status status) } }; -static void direct_interrupts_to_host(struct drm_i915_private *dev_priv) +static void guc_interrupts_release(struct drm_i915_private *dev_priv) { struct intel_engine_cs *engine; int irqs; @@ -102,7 +114,7 @@ static void direct_interrupts_to_host(struct drm_i915_private *dev_priv) I915_WRITE(GUC_WD_VECS_IER, 0); } -static void direct_interrupts_to_guc(struct drm_i915_private *dev_priv) +static void guc_interrupts_capture(struct drm_i915_private *dev_priv) { struct intel_engine_cs *engine; int irqs; @@ -122,13 +134,28 @@ static void direct_interrupts_to_guc(struct drm_i915_private *dev_priv) I915_WRITE(GUC_WD_VECS_IER, ~irqs); /* - * If GuC has routed PM interrupts to itself, don't keep it. - * and keep other interrupts those are unmasked by GuC. - */ + * The REDIRECT_TO_GUC bit of the PMINTRMSK register directs all + * (unmasked) PM interrupts to the GuC. All other bits of this + * register *disable* generation of a specific interrupt. + * + * 'pm_intr_keep' indicates bits that are NOT to be set when + * writing to the PM interrupt mask register, i.e. interrupts + * that must not be disabled. + * + * If the GuC is handling these interrupts, then we must not let + * the PM code disable ANY interrupt that the GuC is expecting. + * So for each ENABLED (0) bit in this register, we must SET the + * bit in pm_intr_keep so that it's left enabled for the GuC. + * + * OTOH the REDIRECT_TO_GUC bit is initially SET in pm_intr_keep + * (so interrupts go to the DISPLAY unit at first); but here we + * need to CLEAR that bit, which will result in the register bit + * being left SET! + */ tmp = I915_READ(GEN6_PMINTRMSK); - if (tmp & GEN8_PMINTR_REDIRECT_TO_NON_DISP) { - dev_priv->rps.pm_intr_keep |= ~(tmp & ~GEN8_PMINTR_REDIRECT_TO_NON_DISP); - dev_priv->rps.pm_intr_keep &= ~GEN8_PMINTR_REDIRECT_TO_NON_DISP; + if (tmp & GEN8_PMINTR_REDIRECT_TO_GUC) { + dev_priv->rps.pm_intr_keep |= ~tmp; + dev_priv->rps.pm_intr_keep &= ~GEN8_PMINTR_REDIRECT_TO_GUC; } } @@ -140,17 +167,24 @@ static u32 get_gttype(struct drm_i915_private *dev_priv) static u32 get_core_family(struct drm_i915_private *dev_priv) { - switch (INTEL_INFO(dev_priv)->gen) { + u32 gen = INTEL_GEN(dev_priv); + + switch (gen) { case 9: return GFXCORE_FAMILY_GEN9; default: - DRM_ERROR("GUC: unsupported core family\n"); + WARN(1, "GEN%d does not support GuC operation!\n", gen); return GFXCORE_FAMILY_UNKNOWN; } } -static void set_guc_init_params(struct drm_i915_private *dev_priv) +/* + * Initialise the GuC parameter block before starting the firmware + * transfer. These parameters are read by the firmware on startup + * and cannot be changed thereafter. + */ +static void guc_params_init(struct drm_i915_private *dev_priv) { struct intel_guc *guc = &dev_priv->guc; u32 params[GUC_CTL_MAX_DWORDS]; @@ -181,16 +215,15 @@ static void set_guc_init_params(struct drm_i915_private *dev_priv) i915.guc_log_level << GUC_LOG_VERBOSITY_SHIFT; } - if (guc->ads_obj) { - u32 ads = (u32)i915_gem_obj_ggtt_offset(guc->ads_obj) - >> PAGE_SHIFT; + if (guc->ads_vma) { + u32 ads = i915_ggtt_offset(guc->ads_vma) >> PAGE_SHIFT; params[GUC_CTL_DEBUG] |= ads << GUC_ADS_ADDR_SHIFT; params[GUC_CTL_DEBUG] |= GUC_ADS_ENABLED; } /* If GuC submission is enabled, set up additional parameters here */ if (i915.enable_guc_submission) { - u32 pgs = i915_gem_obj_ggtt_offset(dev_priv->guc.ctx_pool_obj); + u32 pgs = i915_ggtt_offset(dev_priv->guc.ctx_pool_vma); u32 ctx_in_16 = GUC_MAX_GPU_CONTEXTS / 16; pgs >>= PAGE_SHIFT; @@ -238,12 +271,12 @@ static inline bool guc_ucode_response(struct drm_i915_private *dev_priv, * Note that GuC needs the CSS header plus uKernel code to be copied by the * DMA engine in one operation, whereas the RSA signature is loaded via MMIO. */ -static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv) +static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv, + struct i915_vma *vma) { struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; - struct drm_i915_gem_object *fw_obj = guc_fw->guc_fw_obj; unsigned long offset; - struct sg_table *sg = fw_obj->pages; + struct sg_table *sg = vma->pages; u32 status, rsa[UOS_RSA_SCRATCH_MAX_COUNT]; int i, ret = 0; @@ -260,7 +293,7 @@ static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv) I915_WRITE(DMA_COPY_SIZE, guc_fw->header_size + guc_fw->ucode_size); /* Set the source address for the new blob */ - offset = i915_gem_obj_ggtt_offset(fw_obj) + guc_fw->header_offset; + offset = i915_ggtt_offset(vma) + guc_fw->header_offset; I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset)); I915_WRITE(DMA_ADDR_0_HIGH, upper_32_bits(offset) & 0xFFFF); @@ -315,6 +348,7 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv) { struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; struct drm_device *dev = &dev_priv->drm; + struct i915_vma *vma; int ret; ret = i915_gem_object_set_to_gtt_domain(guc_fw->guc_fw_obj, false); @@ -323,10 +357,10 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv) return ret; } - ret = i915_gem_obj_ggtt_pin(guc_fw->guc_fw_obj, 0, 0); - if (ret) { - DRM_DEBUG_DRIVER("pin failed %d\n", ret); - return ret; + vma = i915_gem_object_ggtt_pin(guc_fw->guc_fw_obj, NULL, 0, 0, 0); + if (IS_ERR(vma)) { + DRM_DEBUG_DRIVER("pin failed %d\n", (int)PTR_ERR(vma)); + return PTR_ERR(vma); } /* Invalidate GuC TLB to let GuC take the latest updates to GTT. */ @@ -349,7 +383,9 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv) } /* WaC6DisallowByGfxPause*/ - I915_WRITE(GEN6_GFXPAUSE, 0x30FFF); + if (IS_SKL_REVID(dev, 0, SKL_REVID_C0) || + IS_BXT_REVID(dev, 0, BXT_REVID_B0)) + I915_WRITE(GEN6_GFXPAUSE, 0x30FFF); if (IS_BROXTON(dev)) I915_WRITE(GEN9LP_GT_PM_CONFIG, GT_DOORBELL_ENABLE); @@ -361,13 +397,13 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv) I915_WRITE(GEN7_MISCCPCTL, (GEN8_DOP_CLOCK_GATE_GUC_ENABLE | I915_READ(GEN7_MISCCPCTL))); - /* allows for 5us before GT can go to RC6 */ + /* allows for 5us (in 10ns units) before GT can go to RC6 */ I915_WRITE(GUC_ARAT_C6DIS, 0x1FF); } - set_guc_init_params(dev_priv); + guc_params_init(dev_priv); - ret = guc_ucode_xfer_dma(dev_priv); + ret = guc_ucode_xfer_dma(dev_priv, vma); intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); @@ -375,12 +411,12 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv) * We keep the object pages for reuse during resume. But we can unpin it * now that DMA has completed, so it doesn't continue to take up space. */ - i915_gem_object_ggtt_unpin(guc_fw->guc_fw_obj); + i915_vma_unpin(vma); return ret; } -static int i915_reset_guc(struct drm_i915_private *dev_priv) +static int guc_hw_reset(struct drm_i915_private *dev_priv) { int ret; u32 guc_status; @@ -433,7 +469,7 @@ int intel_guc_setup(struct drm_device *dev) goto fail; } else if (*fw_path == '\0') { /* Device has a GuC but we don't know what f/w to load? */ - DRM_INFO("No GuC firmware known for this platform\n"); + WARN(1, "No GuC firmware known for this platform!\n"); err = -ENODEV; goto fail; } @@ -447,7 +483,7 @@ int intel_guc_setup(struct drm_device *dev) goto fail; } - direct_interrupts_to_host(dev_priv); + guc_interrupts_release(dev_priv); guc_fw->guc_fw_load_status = GUC_FIRMWARE_PENDING; @@ -470,11 +506,9 @@ int intel_guc_setup(struct drm_device *dev) * Always reset the GuC just before (re)loading, so * that the state and timing are fairly predictable */ - err = i915_reset_guc(dev_priv); - if (err) { - DRM_ERROR("GuC reset failed: %d\n", err); + err = guc_hw_reset(dev_priv); + if (err) goto fail; - } err = guc_ucode_xfer(dev_priv); if (!err) @@ -497,7 +531,7 @@ int intel_guc_setup(struct drm_device *dev) err = i915_guc_submission_enable(dev_priv); if (err) goto fail; - direct_interrupts_to_guc(dev_priv); + guc_interrupts_capture(dev_priv); } return 0; @@ -506,7 +540,7 @@ fail: if (guc_fw->guc_fw_load_status == GUC_FIRMWARE_PENDING) guc_fw->guc_fw_load_status = GUC_FIRMWARE_FAIL; - direct_interrupts_to_host(dev_priv); + guc_interrupts_release(dev_priv); i915_guc_submission_disable(dev_priv); i915_guc_submission_fini(dev_priv); @@ -532,15 +566,15 @@ fail: else if (err == 0) DRM_INFO("GuC firmware load skipped\n"); else if (ret != -EIO) - DRM_INFO("GuC firmware load failed: %d\n", err); + DRM_NOTE("GuC firmware load failed: %d\n", err); else - DRM_ERROR("GuC firmware load failed: %d\n", err); + DRM_WARN("GuC firmware load failed: %d\n", err); if (i915.enable_guc_submission) { if (fw_path == NULL) DRM_INFO("GuC submission without firmware not supported\n"); if (ret == 0) - DRM_INFO("Falling back from GuC submission to execlist mode\n"); + DRM_NOTE("Falling back from GuC submission to execlist mode\n"); else DRM_ERROR("GuC init failed: %d\n", ret); } @@ -551,6 +585,7 @@ fail: static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw) { + struct pci_dev *pdev = dev->pdev; struct drm_i915_gem_object *obj; const struct firmware *fw; struct guc_css_header *css; @@ -560,7 +595,7 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw) DRM_DEBUG_DRIVER("before requesting firmware: GuC fw fetch status %s\n", intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status)); - err = request_firmware(&fw, guc_fw->guc_fw_path, &dev->pdev->dev); + err = request_firmware(&fw, guc_fw->guc_fw_path, &pdev->dev); if (err) goto fail; if (!fw) @@ -571,7 +606,7 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw) /* Check the size of the blob before examining buffer contents */ if (fw->size < sizeof(struct guc_css_header)) { - DRM_ERROR("Firmware header is missing\n"); + DRM_NOTE("Firmware header is missing\n"); goto fail; } @@ -583,7 +618,7 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw) css->key_size_dw - css->exponent_size_dw) * sizeof(u32); if (guc_fw->header_size != sizeof(struct guc_css_header)) { - DRM_ERROR("CSS header definition mismatch\n"); + DRM_NOTE("CSS header definition mismatch\n"); goto fail; } @@ -593,7 +628,7 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw) /* now RSA */ if (css->key_size_dw != UOS_RSA_SCRATCH_MAX_COUNT) { - DRM_ERROR("RSA key size is bad\n"); + DRM_NOTE("RSA key size is bad\n"); goto fail; } guc_fw->rsa_offset = guc_fw->ucode_offset + guc_fw->ucode_size; @@ -602,14 +637,14 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw) /* At least, it should have header, uCode and RSA. Size of all three. */ size = guc_fw->header_size + guc_fw->ucode_size + guc_fw->rsa_size; if (fw->size < size) { - DRM_ERROR("Missing firmware components\n"); + DRM_NOTE("Missing firmware components\n"); goto fail; } /* Header and uCode will be loaded to WOPCM. Size of the two. */ size = guc_fw->header_size + guc_fw->ucode_size; if (size > guc_wopcm_size(to_i915(dev))) { - DRM_ERROR("Firmware is too large to fit in WOPCM\n"); + DRM_NOTE("Firmware is too large to fit in WOPCM\n"); goto fail; } @@ -624,7 +659,7 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw) if (guc_fw->guc_fw_major_found != guc_fw->guc_fw_major_wanted || guc_fw->guc_fw_minor_found < guc_fw->guc_fw_minor_wanted) { - DRM_ERROR("GuC firmware version %d.%d, required %d.%d\n", + DRM_NOTE("GuC firmware version %d.%d, required %d.%d\n", guc_fw->guc_fw_major_found, guc_fw->guc_fw_minor_found, guc_fw->guc_fw_major_wanted, guc_fw->guc_fw_minor_wanted); err = -ENOEXEC; @@ -654,15 +689,15 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw) return; fail: + DRM_WARN("Failed to fetch valid GuC firmware from %s (error %d)\n", + guc_fw->guc_fw_path, err); DRM_DEBUG_DRIVER("GuC fw fetch status FAIL; err %d, fw %p, obj %p\n", err, fw, guc_fw->guc_fw_obj); - DRM_ERROR("Failed to fetch GuC firmware from %s (error %d)\n", - guc_fw->guc_fw_path, err); mutex_lock(&dev->struct_mutex); obj = guc_fw->guc_fw_obj; if (obj) - drm_gem_object_unreference(&obj->base); + i915_gem_object_put(obj); guc_fw->guc_fw_obj = NULL; mutex_unlock(&dev->struct_mutex); @@ -695,16 +730,16 @@ void intel_guc_init(struct drm_device *dev) fw_path = NULL; } else if (IS_SKYLAKE(dev)) { fw_path = I915_SKL_GUC_UCODE; - guc_fw->guc_fw_major_wanted = 6; - guc_fw->guc_fw_minor_wanted = 1; + guc_fw->guc_fw_major_wanted = SKL_FW_MAJOR; + guc_fw->guc_fw_minor_wanted = SKL_FW_MINOR; } else if (IS_BROXTON(dev)) { fw_path = I915_BXT_GUC_UCODE; - guc_fw->guc_fw_major_wanted = 8; - guc_fw->guc_fw_minor_wanted = 7; + guc_fw->guc_fw_major_wanted = BXT_FW_MAJOR; + guc_fw->guc_fw_minor_wanted = BXT_FW_MINOR; } else if (IS_KABYLAKE(dev)) { fw_path = I915_KBL_GUC_UCODE; - guc_fw->guc_fw_major_wanted = 9; - guc_fw->guc_fw_minor_wanted = 14; + guc_fw->guc_fw_major_wanted = KBL_FW_MAJOR; + guc_fw->guc_fw_minor_wanted = KBL_FW_MINOR; } else { fw_path = ""; /* unknown device */ } @@ -738,12 +773,12 @@ void intel_guc_fini(struct drm_device *dev) struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; mutex_lock(&dev->struct_mutex); - direct_interrupts_to_host(dev_priv); + guc_interrupts_release(dev_priv); i915_guc_submission_disable(dev_priv); i915_guc_submission_fini(dev_priv); if (guc_fw->guc_fw_obj) - drm_gem_object_unreference(&guc_fw->guc_fw_obj->base); + i915_gem_object_put(guc_fw->guc_fw_obj); guc_fw->guc_fw_obj = NULL; mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 4df9f38..c51073f 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -985,7 +985,9 @@ static void intel_enable_hdmi_audio(struct intel_encoder *encoder) intel_audio_codec_enable(encoder); } -static void g4x_enable_hdmi(struct intel_encoder *encoder) +static void g4x_enable_hdmi(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); @@ -1006,7 +1008,9 @@ static void g4x_enable_hdmi(struct intel_encoder *encoder) intel_enable_hdmi_audio(encoder); } -static void ibx_enable_hdmi(struct intel_encoder *encoder) +static void ibx_enable_hdmi(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); @@ -1055,7 +1059,9 @@ static void ibx_enable_hdmi(struct intel_encoder *encoder) intel_enable_hdmi_audio(encoder); } -static void cpt_enable_hdmi(struct intel_encoder *encoder) +static void cpt_enable_hdmi(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); @@ -1108,11 +1114,15 @@ static void cpt_enable_hdmi(struct intel_encoder *encoder) intel_enable_hdmi_audio(encoder); } -static void vlv_enable_hdmi(struct intel_encoder *encoder) +static void vlv_enable_hdmi(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { } -static void intel_disable_hdmi(struct intel_encoder *encoder) +static void intel_disable_hdmi(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); @@ -1164,17 +1174,21 @@ static void intel_disable_hdmi(struct intel_encoder *encoder) intel_dp_dual_mode_set_tmds_output(intel_hdmi, false); } -static void g4x_disable_hdmi(struct intel_encoder *encoder) +static void g4x_disable_hdmi(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); if (crtc->config->has_audio) intel_audio_codec_disable(encoder); - intel_disable_hdmi(encoder); + intel_disable_hdmi(encoder, old_crtc_state, old_conn_state); } -static void pch_disable_hdmi(struct intel_encoder *encoder) +static void pch_disable_hdmi(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); @@ -1182,9 +1196,11 @@ static void pch_disable_hdmi(struct intel_encoder *encoder) intel_audio_codec_disable(encoder); } -static void pch_post_disable_hdmi(struct intel_encoder *encoder) +static void pch_post_disable_hdmi(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { - intel_disable_hdmi(encoder); + intel_disable_hdmi(encoder, old_crtc_state, old_conn_state); } static int intel_hdmi_source_max_tmds_clock(struct drm_i915_private *dev_priv) @@ -1285,7 +1301,8 @@ static bool hdmi_12bpc_possible(struct intel_crtc_state *crtc_state) } bool intel_hdmi_compute_config(struct intel_encoder *encoder, - struct intel_crtc_state *pipe_config) + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); struct drm_device *dev = encoder->base.dev; @@ -1422,24 +1439,22 @@ intel_hdmi_dp_dual_mode_detect(struct drm_connector *connector, bool has_edid) } static bool -intel_hdmi_set_edid(struct drm_connector *connector, bool force) +intel_hdmi_set_edid(struct drm_connector *connector) { struct drm_i915_private *dev_priv = to_i915(connector->dev); struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); - struct edid *edid = NULL; + struct edid *edid; bool connected = false; - if (force) { - intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS); + intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS); - edid = drm_get_edid(connector, - intel_gmbus_get_adapter(dev_priv, - intel_hdmi->ddc_bus)); + edid = drm_get_edid(connector, + intel_gmbus_get_adapter(dev_priv, + intel_hdmi->ddc_bus)); - intel_hdmi_dp_dual_mode_detect(connector, edid != NULL); + intel_hdmi_dp_dual_mode_detect(connector, edid != NULL); - intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS); - } + intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS); to_intel_connector(connector)->detect_edid = edid; if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) { @@ -1465,37 +1480,16 @@ static enum drm_connector_status intel_hdmi_detect(struct drm_connector *connector, bool force) { enum drm_connector_status status; - struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); struct drm_i915_private *dev_priv = to_i915(connector->dev); - bool live_status = false; - unsigned int try; DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS); - for (try = 0; !live_status && try < 9; try++) { - if (try) - msleep(10); - live_status = intel_digital_port_connected(dev_priv, - hdmi_to_dig_port(intel_hdmi)); - } - - if (!live_status) { - DRM_DEBUG_KMS("HDMI live status down\n"); - /* - * Live status register is not reliable on all intel platforms. - * So consider live_status only for certain platforms, for - * others, read EDID to determine presence of sink. - */ - if (INTEL_INFO(dev_priv)->gen < 7 || IS_IVYBRIDGE(dev_priv)) - live_status = true; - } - intel_hdmi_unset_edid(connector); - if (intel_hdmi_set_edid(connector, live_status)) { + if (intel_hdmi_set_edid(connector)) { struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI; @@ -1521,7 +1515,7 @@ intel_hdmi_force(struct drm_connector *connector) if (connector->status != connector_status_connected) return; - intel_hdmi_set_edid(connector, true); + intel_hdmi_set_edid(connector); hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI; } @@ -1638,7 +1632,9 @@ done: return 0; } -static void intel_hdmi_pre_enable(struct intel_encoder *encoder) +static void intel_hdmi_pre_enable(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); @@ -1651,7 +1647,9 @@ static void intel_hdmi_pre_enable(struct intel_encoder *encoder) adjusted_mode); } -static void vlv_hdmi_pre_enable(struct intel_encoder *encoder) +static void vlv_hdmi_pre_enable(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); struct intel_hdmi *intel_hdmi = &dport->hdmi; @@ -1671,37 +1669,47 @@ static void vlv_hdmi_pre_enable(struct intel_encoder *encoder) intel_crtc->config->has_hdmi_sink, adjusted_mode); - g4x_enable_hdmi(encoder); + g4x_enable_hdmi(encoder, pipe_config, conn_state); vlv_wait_port_ready(dev_priv, dport, 0x0); } -static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder) +static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { intel_hdmi_prepare(encoder); vlv_phy_pre_pll_enable(encoder); } -static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) +static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { intel_hdmi_prepare(encoder); chv_phy_pre_pll_enable(encoder); } -static void chv_hdmi_post_pll_disable(struct intel_encoder *encoder) +static void chv_hdmi_post_pll_disable(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { chv_phy_post_pll_disable(encoder); } -static void vlv_hdmi_post_disable(struct intel_encoder *encoder) +static void vlv_hdmi_post_disable(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { /* Reset lanes to avoid HDMI flicker (VLV w/a) */ vlv_phy_reset_lanes(encoder); } -static void chv_hdmi_post_disable(struct intel_encoder *encoder) +static void chv_hdmi_post_disable(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); @@ -1714,7 +1722,9 @@ static void chv_hdmi_post_disable(struct intel_encoder *encoder) mutex_unlock(&dev_priv->sb_lock); } -static void chv_hdmi_pre_enable(struct intel_encoder *encoder) +static void chv_hdmi_pre_enable(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); struct intel_hdmi *intel_hdmi = &dport->hdmi; @@ -1734,7 +1744,7 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder) intel_crtc->config->has_hdmi_sink, adjusted_mode); - g4x_enable_hdmi(encoder); + g4x_enable_hdmi(encoder, pipe_config, conn_state); vlv_wait_port_ready(dev_priv, dport, 0x0); diff --git a/drivers/gpu/drm/i915/intel_hotplug.c b/drivers/gpu/drm/i915/intel_hotplug.c index f48957e..334d47b 100644 --- a/drivers/gpu/drm/i915/intel_hotplug.c +++ b/drivers/gpu/drm/i915/intel_hotplug.c @@ -477,7 +477,8 @@ void intel_hpd_init(struct drm_i915_private *dev_priv) spin_unlock_irq(&dev_priv->irq_lock); } -void i915_hpd_poll_init_work(struct work_struct *work) { +static void i915_hpd_poll_init_work(struct work_struct *work) +{ struct drm_i915_private *dev_priv = container_of(work, struct drm_i915_private, hotplug.poll_init_work); @@ -525,7 +526,6 @@ void i915_hpd_poll_init_work(struct work_struct *work) { /** * intel_hpd_poll_init - enables/disables polling for connectors with hpd * @dev_priv: i915 device instance - * @enabled: Whether to enable or disable polling * * This function enables polling for all connectors, regardless of whether or * not they support hotplug detection. Under certain conditions HPD may not be diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 1f266d7..79aab9a 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -255,67 +255,59 @@ intel_gpio_setup(struct intel_gmbus *bus, unsigned int pin) algo->data = bus; } -static int -gmbus_wait_hw_status(struct drm_i915_private *dev_priv, - u32 gmbus2_status, - u32 gmbus4_irq_en) +static int gmbus_wait(struct drm_i915_private *dev_priv, u32 status, u32 irq_en) { - int i; - u32 gmbus2 = 0; DEFINE_WAIT(wait); - - if (!HAS_GMBUS_IRQ(dev_priv)) - gmbus4_irq_en = 0; + u32 gmbus2; + int ret; /* Important: The hw handles only the first bit, so set only one! Since * we also need to check for NAKs besides the hw ready/idle signal, we - * need to wake up periodically and check that ourselves. */ - I915_WRITE(GMBUS4, gmbus4_irq_en); - - for (i = 0; i < msecs_to_jiffies_timeout(50); i++) { - prepare_to_wait(&dev_priv->gmbus_wait_queue, &wait, - TASK_UNINTERRUPTIBLE); + * need to wake up periodically and check that ourselves. + */ + if (!HAS_GMBUS_IRQ(dev_priv)) + irq_en = 0; - gmbus2 = I915_READ_NOTRACE(GMBUS2); - if (gmbus2 & (GMBUS_SATOER | gmbus2_status)) - break; + add_wait_queue(&dev_priv->gmbus_wait_queue, &wait); + I915_WRITE_FW(GMBUS4, irq_en); - schedule_timeout(1); - } - finish_wait(&dev_priv->gmbus_wait_queue, &wait); + status |= GMBUS_SATOER; + ret = wait_for_us((gmbus2 = I915_READ_FW(GMBUS2)) & status, 2); + if (ret) + ret = wait_for((gmbus2 = I915_READ_FW(GMBUS2)) & status, 50); - I915_WRITE(GMBUS4, 0); + I915_WRITE_FW(GMBUS4, 0); + remove_wait_queue(&dev_priv->gmbus_wait_queue, &wait); if (gmbus2 & GMBUS_SATOER) return -ENXIO; - if (gmbus2 & gmbus2_status) - return 0; - return -ETIMEDOUT; + + return ret; } static int gmbus_wait_idle(struct drm_i915_private *dev_priv) { + DEFINE_WAIT(wait); + u32 irq_enable; int ret; - if (!HAS_GMBUS_IRQ(dev_priv)) - return intel_wait_for_register(dev_priv, - GMBUS2, GMBUS_ACTIVE, 0, - 10); - /* Important: The hw handles only the first bit, so set only one! */ - I915_WRITE(GMBUS4, GMBUS_IDLE_EN); + irq_enable = 0; + if (HAS_GMBUS_IRQ(dev_priv)) + irq_enable = GMBUS_IDLE_EN; - ret = wait_event_timeout(dev_priv->gmbus_wait_queue, - (I915_READ_NOTRACE(GMBUS2) & GMBUS_ACTIVE) == 0, - msecs_to_jiffies_timeout(10)); + add_wait_queue(&dev_priv->gmbus_wait_queue, &wait); + I915_WRITE_FW(GMBUS4, irq_enable); - I915_WRITE(GMBUS4, 0); + ret = intel_wait_for_register_fw(dev_priv, + GMBUS2, GMBUS_ACTIVE, 0, + 10); - if (ret) - return 0; - else - return -ETIMEDOUT; + I915_WRITE_FW(GMBUS4, 0); + remove_wait_queue(&dev_priv->gmbus_wait_queue, &wait); + + return ret; } static int @@ -323,22 +315,21 @@ gmbus_xfer_read_chunk(struct drm_i915_private *dev_priv, unsigned short addr, u8 *buf, unsigned int len, u32 gmbus1_index) { - I915_WRITE(GMBUS1, - gmbus1_index | - GMBUS_CYCLE_WAIT | - (len << GMBUS_BYTE_COUNT_SHIFT) | - (addr << GMBUS_SLAVE_ADDR_SHIFT) | - GMBUS_SLAVE_READ | GMBUS_SW_RDY); + I915_WRITE_FW(GMBUS1, + gmbus1_index | + GMBUS_CYCLE_WAIT | + (len << GMBUS_BYTE_COUNT_SHIFT) | + (addr << GMBUS_SLAVE_ADDR_SHIFT) | + GMBUS_SLAVE_READ | GMBUS_SW_RDY); while (len) { int ret; u32 val, loop = 0; - ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY, - GMBUS_HW_RDY_EN); + ret = gmbus_wait(dev_priv, GMBUS_HW_RDY, GMBUS_HW_RDY_EN); if (ret) return ret; - val = I915_READ(GMBUS3); + val = I915_READ_FW(GMBUS3); do { *buf++ = val & 0xff; val >>= 8; @@ -385,12 +376,12 @@ gmbus_xfer_write_chunk(struct drm_i915_private *dev_priv, len -= 1; } - I915_WRITE(GMBUS3, val); - I915_WRITE(GMBUS1, - GMBUS_CYCLE_WAIT | - (chunk_size << GMBUS_BYTE_COUNT_SHIFT) | - (addr << GMBUS_SLAVE_ADDR_SHIFT) | - GMBUS_SLAVE_WRITE | GMBUS_SW_RDY); + I915_WRITE_FW(GMBUS3, val); + I915_WRITE_FW(GMBUS1, + GMBUS_CYCLE_WAIT | + (chunk_size << GMBUS_BYTE_COUNT_SHIFT) | + (addr << GMBUS_SLAVE_ADDR_SHIFT) | + GMBUS_SLAVE_WRITE | GMBUS_SW_RDY); while (len) { int ret; @@ -399,10 +390,9 @@ gmbus_xfer_write_chunk(struct drm_i915_private *dev_priv, val |= *buf++ << (8 * loop); } while (--len && ++loop < 4); - I915_WRITE(GMBUS3, val); + I915_WRITE_FW(GMBUS3, val); - ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY, - GMBUS_HW_RDY_EN); + ret = gmbus_wait(dev_priv, GMBUS_HW_RDY, GMBUS_HW_RDY_EN); if (ret) return ret; } @@ -460,13 +450,13 @@ gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs) /* GMBUS5 holds 16-bit index */ if (gmbus5) - I915_WRITE(GMBUS5, gmbus5); + I915_WRITE_FW(GMBUS5, gmbus5); ret = gmbus_xfer_read(dev_priv, &msgs[1], gmbus1_index); /* Clear GMBUS5 after each index transfer */ if (gmbus5) - I915_WRITE(GMBUS5, 0); + I915_WRITE_FW(GMBUS5, 0); return ret; } @@ -478,11 +468,15 @@ do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) struct intel_gmbus, adapter); struct drm_i915_private *dev_priv = bus->dev_priv; + const unsigned int fw = + intel_uncore_forcewake_for_reg(dev_priv, GMBUS0, + FW_REG_READ | FW_REG_WRITE); int i = 0, inc, try = 0; int ret = 0; + intel_uncore_forcewake_get(dev_priv, fw); retry: - I915_WRITE(GMBUS0, bus->reg0); + I915_WRITE_FW(GMBUS0, bus->reg0); for (; i < num; i += inc) { inc = 1; @@ -496,8 +490,8 @@ retry: } if (!ret) - ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE, - GMBUS_HW_WAIT_EN); + ret = gmbus_wait(dev_priv, + GMBUS_HW_WAIT_PHASE, GMBUS_HW_WAIT_EN); if (ret == -ETIMEDOUT) goto timeout; else if (ret) @@ -508,7 +502,7 @@ retry: * a STOP on the very first cycle. To simplify the code we * unconditionally generate the STOP condition with an additional gmbus * cycle. */ - I915_WRITE(GMBUS1, GMBUS_CYCLE_STOP | GMBUS_SW_RDY); + I915_WRITE_FW(GMBUS1, GMBUS_CYCLE_STOP | GMBUS_SW_RDY); /* Mark the GMBUS interface as disabled after waiting for idle. * We will re-enable it at the start of the next xfer, @@ -519,7 +513,7 @@ retry: adapter->name); ret = -ETIMEDOUT; } - I915_WRITE(GMBUS0, 0); + I915_WRITE_FW(GMBUS0, 0); ret = ret ?: i; goto out; @@ -548,9 +542,9 @@ clear_err: * of resetting the GMBUS controller and so clearing the * BUS_ERROR raised by the slave's NAK. */ - I915_WRITE(GMBUS1, GMBUS_SW_CLR_INT); - I915_WRITE(GMBUS1, 0); - I915_WRITE(GMBUS0, 0); + I915_WRITE_FW(GMBUS1, GMBUS_SW_CLR_INT); + I915_WRITE_FW(GMBUS1, 0); + I915_WRITE_FW(GMBUS0, 0); DRM_DEBUG_KMS("GMBUS [%s] NAK for addr: %04x %c(%d)\n", adapter->name, msgs[i].addr, @@ -573,7 +567,7 @@ clear_err: timeout: DRM_DEBUG_KMS("GMBUS [%s] timed out, falling back to bit banging on pin %d\n", bus->adapter.name, bus->reg0 & 0xff); - I915_WRITE(GMBUS0, 0); + I915_WRITE_FW(GMBUS0, 0); /* * Hardware may not support GMBUS over these pins? Try GPIO bitbanging @@ -582,6 +576,7 @@ timeout: ret = -EAGAIN; out: + intel_uncore_forcewake_put(dev_priv, fw); return ret; } @@ -633,6 +628,7 @@ static const struct i2c_algorithm gmbus_algorithm = { int intel_setup_gmbus(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; struct intel_gmbus *bus; unsigned int pin; int ret; @@ -663,7 +659,7 @@ int intel_setup_gmbus(struct drm_device *dev) "i915 gmbus %s", get_gmbus_pin(dev_priv, pin)->name); - bus->adapter.dev.parent = &dev->pdev->dev; + bus->adapter.dev.parent = &pdev->dev; bus->dev_priv = dev_priv; bus->adapter.algo = &gmbus_algorithm; diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 414ddda..25114336 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -156,6 +156,11 @@ #define GEN8_CTX_STATUS_COMPLETE (1 << 4) #define GEN8_CTX_STATUS_LITE_RESTORE (1 << 15) +#define GEN8_CTX_STATUS_COMPLETED_MASK \ + (GEN8_CTX_STATUS_ACTIVE_IDLE | \ + GEN8_CTX_STATUS_PREEMPTED | \ + GEN8_CTX_STATUS_ELEMENT_SWITCH) + #define CTX_LRI_HEADER_0 0x01 #define CTX_CONTEXT_CONTROL 0x02 #define CTX_RING_HEAD 0x04 @@ -263,12 +268,10 @@ logical_ring_init_platform_invariants(struct intel_engine_cs *engine) { struct drm_i915_private *dev_priv = engine->i915; - if (IS_GEN8(dev_priv) || IS_GEN9(dev_priv)) - engine->idle_lite_restore_wa = ~0; - - engine->disable_lite_restore_wa = (IS_SKL_REVID(dev_priv, 0, SKL_REVID_B0) || - IS_BXT_REVID(dev_priv, 0, BXT_REVID_A1)) && - (engine->id == VCS || engine->id == VCS2); + engine->disable_lite_restore_wa = + (IS_SKL_REVID(dev_priv, 0, SKL_REVID_B0) || + IS_BXT_REVID(dev_priv, 0, BXT_REVID_A1)) && + (engine->id == VCS || engine->id == VCS2); engine->ctx_desc_template = GEN8_CTX_VALID; if (IS_GEN8(dev_priv)) @@ -288,7 +291,6 @@ logical_ring_init_platform_invariants(struct intel_engine_cs *engine) /** * intel_lr_context_descriptor_update() - calculate & cache the descriptor * descriptor for a pinned context - * * @ctx: Context to work on * @engine: Engine the descriptor will be used with * @@ -297,12 +299,13 @@ logical_ring_init_platform_invariants(struct intel_engine_cs *engine) * expensive to calculate, we'll just do it once and cache the result, * which remains valid until the context is unpinned. * - * This is what a descriptor looks like, from LSB to MSB: - * bits 0-11: flags, GEN8_CTX_* (cached in ctx_desc_template) - * bits 12-31: LRCA, GTT address of (the HWSP of) this context - * bits 32-52: ctx ID, a globally unique tag - * bits 53-54: mbz, reserved for use by hardware - * bits 55-63: group ID, currently unused and set to 0 + * This is what a descriptor looks like, from LSB to MSB:: + * + * bits 0-11: flags, GEN8_CTX_* (cached in ctx_desc_template) + * bits 12-31: LRCA, GTT address of (the HWSP of) this context + * bits 32-52: ctx ID, a globally unique tag + * bits 53-54: mbz, reserved for use by hardware + * bits 55-63: group ID, currently unused and set to 0 */ static void intel_lr_context_descriptor_update(struct i915_gem_context *ctx, @@ -315,7 +318,7 @@ intel_lr_context_descriptor_update(struct i915_gem_context *ctx, desc = ctx->desc_template; /* bits 3-4 */ desc |= engine->ctx_desc_template; /* bits 0-11 */ - desc |= ce->lrc_vma->node.start + LRC_PPHWSP_PN * PAGE_SIZE; + desc |= i915_ggtt_offset(ce->state) + LRC_PPHWSP_PN * PAGE_SIZE; /* bits 12-31 */ desc |= (u64)ctx->hw_id << GEN8_CTX_ID_SHIFT; /* bits 32-52 */ @@ -328,34 +331,18 @@ uint64_t intel_lr_context_descriptor(struct i915_gem_context *ctx, return ctx->engine[engine->id].lrc_desc; } -static void execlists_elsp_write(struct drm_i915_gem_request *rq0, - struct drm_i915_gem_request *rq1) +static inline void +execlists_context_status_change(struct drm_i915_gem_request *rq, + unsigned long status) { + /* + * Only used when GVT-g is enabled now. When GVT-g is disabled, + * The compiler should eliminate this function as dead-code. + */ + if (!IS_ENABLED(CONFIG_DRM_I915_GVT)) + return; - struct intel_engine_cs *engine = rq0->engine; - struct drm_i915_private *dev_priv = rq0->i915; - uint64_t desc[2]; - - if (rq1) { - desc[1] = intel_lr_context_descriptor(rq1->ctx, rq1->engine); - rq1->elsp_submitted++; - } else { - desc[1] = 0; - } - - desc[0] = intel_lr_context_descriptor(rq0->ctx, rq0->engine); - rq0->elsp_submitted++; - - /* You must always write both descriptors in the order below. */ - I915_WRITE_FW(RING_ELSP(engine), upper_32_bits(desc[1])); - I915_WRITE_FW(RING_ELSP(engine), lower_32_bits(desc[1])); - - I915_WRITE_FW(RING_ELSP(engine), upper_32_bits(desc[0])); - /* The context is automatically loaded after the following */ - I915_WRITE_FW(RING_ELSP(engine), lower_32_bits(desc[0])); - - /* ELSP is a wo register, use another nearby reg for posting */ - POSTING_READ_FW(RING_EXECLIST_STATUS_LO(engine)); + atomic_notifier_call_chain(&rq->ctx->status_notifier, status, rq); } static void @@ -367,13 +354,13 @@ execlists_update_context_pdps(struct i915_hw_ppgtt *ppgtt, u32 *reg_state) ASSIGN_CTX_PDP(ppgtt, reg_state, 0); } -static void execlists_update_context(struct drm_i915_gem_request *rq) +static u64 execlists_update_context(struct drm_i915_gem_request *rq) { - struct intel_engine_cs *engine = rq->engine; + struct intel_context *ce = &rq->ctx->engine[rq->engine->id]; struct i915_hw_ppgtt *ppgtt = rq->ctx->ppgtt; - uint32_t *reg_state = rq->ctx->engine[engine->id].lrc_reg_state; + u32 *reg_state = ce->lrc_reg_state; - reg_state[CTX_RING_TAIL+1] = rq->tail; + reg_state[CTX_RING_TAIL+1] = intel_ring_offset(rq->ring, rq->tail); /* True 32b PPGTT with dynamic page allocation: update PDP * registers and point the unallocated PDPs to scratch page. @@ -382,321 +369,236 @@ static void execlists_update_context(struct drm_i915_gem_request *rq) */ if (ppgtt && !USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) execlists_update_context_pdps(ppgtt, reg_state); + + return ce->lrc_desc; } -static void execlists_submit_requests(struct drm_i915_gem_request *rq0, - struct drm_i915_gem_request *rq1) +static void execlists_submit_ports(struct intel_engine_cs *engine) { - struct drm_i915_private *dev_priv = rq0->i915; - unsigned int fw_domains = rq0->engine->fw_domains; - - execlists_update_context(rq0); + struct drm_i915_private *dev_priv = engine->i915; + struct execlist_port *port = engine->execlist_port; + u32 __iomem *elsp = + dev_priv->regs + i915_mmio_reg_offset(RING_ELSP(engine)); + u64 desc[2]; - if (rq1) - execlists_update_context(rq1); + if (!port[0].count) + execlists_context_status_change(port[0].request, + INTEL_CONTEXT_SCHEDULE_IN); + desc[0] = execlists_update_context(port[0].request); + engine->preempt_wa = port[0].count++; /* bdw only? fixed on skl? */ - spin_lock_irq(&dev_priv->uncore.lock); - intel_uncore_forcewake_get__locked(dev_priv, fw_domains); + if (port[1].request) { + GEM_BUG_ON(port[1].count); + execlists_context_status_change(port[1].request, + INTEL_CONTEXT_SCHEDULE_IN); + desc[1] = execlists_update_context(port[1].request); + port[1].count = 1; + } else { + desc[1] = 0; + } + GEM_BUG_ON(desc[0] == desc[1]); - execlists_elsp_write(rq0, rq1); + /* You must always write both descriptors in the order below. */ + writel(upper_32_bits(desc[1]), elsp); + writel(lower_32_bits(desc[1]), elsp); - intel_uncore_forcewake_put__locked(dev_priv, fw_domains); - spin_unlock_irq(&dev_priv->uncore.lock); + writel(upper_32_bits(desc[0]), elsp); + /* The context is automatically loaded after the following */ + writel(lower_32_bits(desc[0]), elsp); } -static inline void execlists_context_status_change( - struct drm_i915_gem_request *rq, - unsigned long status) +static bool ctx_single_port_submission(const struct i915_gem_context *ctx) { - /* - * Only used when GVT-g is enabled now. When GVT-g is disabled, - * The compiler should eliminate this function as dead-code. - */ - if (!IS_ENABLED(CONFIG_DRM_I915_GVT)) - return; - - atomic_notifier_call_chain(&rq->ctx->status_notifier, status, rq); + return (IS_ENABLED(CONFIG_DRM_I915_GVT) && + ctx->execlists_force_single_submission); } -static void execlists_context_unqueue(struct intel_engine_cs *engine) +static bool can_merge_ctx(const struct i915_gem_context *prev, + const struct i915_gem_context *next) { - struct drm_i915_gem_request *req0 = NULL, *req1 = NULL; - struct drm_i915_gem_request *cursor, *tmp; + if (prev != next) + return false; - assert_spin_locked(&engine->execlist_lock); + if (ctx_single_port_submission(prev)) + return false; - /* - * If irqs are not active generate a warning as batches that finish - * without the irqs may get lost and a GPU Hang may occur. - */ - WARN_ON(!intel_irqs_enabled(engine->i915)); - - /* Try to read in pairs */ - list_for_each_entry_safe(cursor, tmp, &engine->execlist_queue, - execlist_link) { - if (!req0) { - req0 = cursor; - } else if (req0->ctx == cursor->ctx) { - /* Same ctx: ignore first request, as second request - * will update tail past first request's workload */ - cursor->elsp_submitted = req0->elsp_submitted; - list_del(&req0->execlist_link); - i915_gem_request_unreference(req0); - req0 = cursor; - } else { - if (IS_ENABLED(CONFIG_DRM_I915_GVT)) { - /* - * req0 (after merged) ctx requires single - * submission, stop picking - */ - if (req0->ctx->execlists_force_single_submission) - break; - /* - * req0 ctx doesn't require single submission, - * but next req ctx requires, stop picking - */ - if (cursor->ctx->execlists_force_single_submission) - break; - } - req1 = cursor; - WARN_ON(req1->elsp_submitted); - break; - } - } + return true; +} - if (unlikely(!req0)) - return; +static void execlists_dequeue(struct intel_engine_cs *engine) +{ + struct drm_i915_gem_request *cursor, *last; + struct execlist_port *port = engine->execlist_port; + bool submit = false; + + last = port->request; + if (last) + /* WaIdleLiteRestore:bdw,skl + * Apply the wa NOOPs to prevent ring:HEAD == req:TAIL + * as we resubmit the request. See gen8_emit_request() + * for where we prepare the padding after the end of the + * request. + */ + last->tail = last->wa_tail; - execlists_context_status_change(req0, INTEL_CONTEXT_SCHEDULE_IN); + GEM_BUG_ON(port[1].request); - if (req1) - execlists_context_status_change(req1, - INTEL_CONTEXT_SCHEDULE_IN); + /* Hardware submission is through 2 ports. Conceptually each port + * has a (RING_START, RING_HEAD, RING_TAIL) tuple. RING_START is + * static for a context, and unique to each, so we only execute + * requests belonging to a single context from each ring. RING_HEAD + * is maintained by the CS in the context image, it marks the place + * where it got up to last time, and through RING_TAIL we tell the CS + * where we want to execute up to this time. + * + * In this list the requests are in order of execution. Consecutive + * requests from the same context are adjacent in the ringbuffer. We + * can combine these requests into a single RING_TAIL update: + * + * RING_HEAD...req1...req2 + * ^- RING_TAIL + * since to execute req2 the CS must first execute req1. + * + * Our goal then is to point each port to the end of a consecutive + * sequence of requests as being the most optimal (fewest wake ups + * and context switches) submission. + */ - if (req0->elsp_submitted & engine->idle_lite_restore_wa) { - /* - * WaIdleLiteRestore: make sure we never cause a lite restore - * with HEAD==TAIL. + spin_lock(&engine->execlist_lock); + list_for_each_entry(cursor, &engine->execlist_queue, execlist_link) { + /* Can we combine this request with the current port? It has to + * be the same context/ringbuffer and not have any exceptions + * (e.g. GVT saying never to combine contexts). * - * Apply the wa NOOPS to prevent ring:HEAD == req:TAIL as we - * resubmit the request. See gen8_emit_request() for where we - * prepare the padding after the end of the request. + * If we can combine the requests, we can execute both by + * updating the RING_TAIL to point to the end of the second + * request, and so we never need to tell the hardware about + * the first. */ - struct intel_ringbuffer *ringbuf; + if (last && !can_merge_ctx(cursor->ctx, last->ctx)) { + /* If we are on the second port and cannot combine + * this request with the last, then we are done. + */ + if (port != engine->execlist_port) + break; + + /* If GVT overrides us we only ever submit port[0], + * leaving port[1] empty. Note that we also have + * to be careful that we don't queue the same + * context (even though a different request) to + * the second port. + */ + if (ctx_single_port_submission(cursor->ctx)) + break; + + GEM_BUG_ON(last->ctx == cursor->ctx); + + i915_gem_request_assign(&port->request, last); + port++; + } + last = cursor; + submit = true; + } + if (submit) { + /* Decouple all the requests submitted from the queue */ + engine->execlist_queue.next = &cursor->execlist_link; + cursor->execlist_link.prev = &engine->execlist_queue; - ringbuf = req0->ctx->engine[engine->id].ringbuf; - req0->tail += 8; - req0->tail &= ringbuf->size - 1; + i915_gem_request_assign(&port->request, last); } + spin_unlock(&engine->execlist_lock); - execlists_submit_requests(req0, req1); + if (submit) + execlists_submit_ports(engine); } -static unsigned int -execlists_check_remove_request(struct intel_engine_cs *engine, u32 ctx_id) +static bool execlists_elsp_idle(struct intel_engine_cs *engine) { - struct drm_i915_gem_request *head_req; - - assert_spin_locked(&engine->execlist_lock); - - head_req = list_first_entry_or_null(&engine->execlist_queue, - struct drm_i915_gem_request, - execlist_link); - - if (WARN_ON(!head_req || (head_req->ctx_hw_id != ctx_id))) - return 0; - - WARN(head_req->elsp_submitted == 0, "Never submitted head request\n"); - - if (--head_req->elsp_submitted > 0) - return 0; - - execlists_context_status_change(head_req, INTEL_CONTEXT_SCHEDULE_OUT); - - list_del(&head_req->execlist_link); - i915_gem_request_unreference(head_req); - - return 1; + return !engine->execlist_port[0].request; } -static u32 -get_context_status(struct intel_engine_cs *engine, unsigned int read_pointer, - u32 *context_id) +static bool execlists_elsp_ready(struct intel_engine_cs *engine) { - struct drm_i915_private *dev_priv = engine->i915; - u32 status; + int port; - read_pointer %= GEN8_CSB_ENTRIES; - - status = I915_READ_FW(RING_CONTEXT_STATUS_BUF_LO(engine, read_pointer)); - - if (status & GEN8_CTX_STATUS_IDLE_ACTIVE) - return 0; + port = 1; /* wait for a free slot */ + if (engine->disable_lite_restore_wa || engine->preempt_wa) + port = 0; /* wait for GPU to be idle before continuing */ - *context_id = I915_READ_FW(RING_CONTEXT_STATUS_BUF_HI(engine, - read_pointer)); - - return status; + return !engine->execlist_port[port].request; } -/** - * intel_lrc_irq_handler() - handle Context Switch interrupts - * @data: tasklet handler passed in unsigned long - * +/* * Check the unread Context Status Buffers and manage the submission of new * contexts to the ELSP accordingly. */ static void intel_lrc_irq_handler(unsigned long data) { struct intel_engine_cs *engine = (struct intel_engine_cs *)data; + struct execlist_port *port = engine->execlist_port; struct drm_i915_private *dev_priv = engine->i915; - u32 status_pointer; - unsigned int read_pointer, write_pointer; - u32 csb[GEN8_CSB_ENTRIES][2]; - unsigned int csb_read = 0, i; - unsigned int submit_contexts = 0; intel_uncore_forcewake_get(dev_priv, engine->fw_domains); - status_pointer = I915_READ_FW(RING_CONTEXT_STATUS_PTR(engine)); - - read_pointer = engine->next_context_status_buffer; - write_pointer = GEN8_CSB_WRITE_PTR(status_pointer); - if (read_pointer > write_pointer) - write_pointer += GEN8_CSB_ENTRIES; - - while (read_pointer < write_pointer) { - if (WARN_ON_ONCE(csb_read == GEN8_CSB_ENTRIES)) - break; - csb[csb_read][0] = get_context_status(engine, ++read_pointer, - &csb[csb_read][1]); - csb_read++; - } - - engine->next_context_status_buffer = write_pointer % GEN8_CSB_ENTRIES; - - /* Update the read pointer to the old write pointer. Manual ringbuffer - * management ftw </sarcasm> */ - I915_WRITE_FW(RING_CONTEXT_STATUS_PTR(engine), - _MASKED_FIELD(GEN8_CSB_READ_PTR_MASK, - engine->next_context_status_buffer << 8)); - - intel_uncore_forcewake_put(dev_priv, engine->fw_domains); - - spin_lock(&engine->execlist_lock); + if (!execlists_elsp_idle(engine)) { + u32 __iomem *csb_mmio = + dev_priv->regs + i915_mmio_reg_offset(RING_CONTEXT_STATUS_PTR(engine)); + u32 __iomem *buf = + dev_priv->regs + i915_mmio_reg_offset(RING_CONTEXT_STATUS_BUF_LO(engine, 0)); + unsigned int csb, head, tail; + + csb = readl(csb_mmio); + head = GEN8_CSB_READ_PTR(csb); + tail = GEN8_CSB_WRITE_PTR(csb); + if (tail < head) + tail += GEN8_CSB_ENTRIES; + while (head < tail) { + unsigned int idx = ++head % GEN8_CSB_ENTRIES; + unsigned int status = readl(buf + 2 * idx); + + if (!(status & GEN8_CTX_STATUS_COMPLETED_MASK)) + continue; + + GEM_BUG_ON(port[0].count == 0); + if (--port[0].count == 0) { + GEM_BUG_ON(status & GEN8_CTX_STATUS_PREEMPTED); + execlists_context_status_change(port[0].request, + INTEL_CONTEXT_SCHEDULE_OUT); + + i915_gem_request_put(port[0].request); + port[0] = port[1]; + memset(&port[1], 0, sizeof(port[1])); + + engine->preempt_wa = false; + } - for (i = 0; i < csb_read; i++) { - if (unlikely(csb[i][0] & GEN8_CTX_STATUS_PREEMPTED)) { - if (csb[i][0] & GEN8_CTX_STATUS_LITE_RESTORE) { - if (execlists_check_remove_request(engine, csb[i][1])) - WARN(1, "Lite Restored request removed from queue\n"); - } else - WARN(1, "Preemption without Lite Restore\n"); + GEM_BUG_ON(port[0].count == 0 && + !(status & GEN8_CTX_STATUS_ACTIVE_IDLE)); } - if (csb[i][0] & (GEN8_CTX_STATUS_ACTIVE_IDLE | - GEN8_CTX_STATUS_ELEMENT_SWITCH)) - submit_contexts += - execlists_check_remove_request(engine, csb[i][1]); - } - - if (submit_contexts) { - if (!engine->disable_lite_restore_wa || - (csb[i][0] & GEN8_CTX_STATUS_ACTIVE_IDLE)) - execlists_context_unqueue(engine); + writel(_MASKED_FIELD(GEN8_CSB_READ_PTR_MASK, + GEN8_CSB_WRITE_PTR(csb) << 8), + csb_mmio); } - spin_unlock(&engine->execlist_lock); + if (execlists_elsp_ready(engine)) + execlists_dequeue(engine); - if (unlikely(submit_contexts > 2)) - DRM_ERROR("More than two context complete events?\n"); + intel_uncore_forcewake_put(dev_priv, engine->fw_domains); } -static void execlists_context_queue(struct drm_i915_gem_request *request) +static void execlists_submit_request(struct drm_i915_gem_request *request) { struct intel_engine_cs *engine = request->engine; - struct drm_i915_gem_request *cursor; - int num_elements = 0; + unsigned long flags; - spin_lock_bh(&engine->execlist_lock); - - list_for_each_entry(cursor, &engine->execlist_queue, execlist_link) - if (++num_elements > 2) - break; - - if (num_elements > 2) { - struct drm_i915_gem_request *tail_req; - - tail_req = list_last_entry(&engine->execlist_queue, - struct drm_i915_gem_request, - execlist_link); - - if (request->ctx == tail_req->ctx) { - WARN(tail_req->elsp_submitted != 0, - "More than 2 already-submitted reqs queued\n"); - list_del(&tail_req->execlist_link); - i915_gem_request_unreference(tail_req); - } - } + spin_lock_irqsave(&engine->execlist_lock, flags); - i915_gem_request_reference(request); list_add_tail(&request->execlist_link, &engine->execlist_queue); - request->ctx_hw_id = request->ctx->hw_id; - if (num_elements == 0) - execlists_context_unqueue(engine); + if (execlists_elsp_idle(engine)) + tasklet_hi_schedule(&engine->irq_tasklet); - spin_unlock_bh(&engine->execlist_lock); -} - -static int logical_ring_invalidate_all_caches(struct drm_i915_gem_request *req) -{ - struct intel_engine_cs *engine = req->engine; - uint32_t flush_domains; - int ret; - - flush_domains = 0; - if (engine->gpu_caches_dirty) - flush_domains = I915_GEM_GPU_DOMAINS; - - ret = engine->emit_flush(req, I915_GEM_GPU_DOMAINS, flush_domains); - if (ret) - return ret; - - engine->gpu_caches_dirty = false; - return 0; -} - -static int execlists_move_to_gpu(struct drm_i915_gem_request *req, - struct list_head *vmas) -{ - const unsigned other_rings = ~intel_engine_flag(req->engine); - struct i915_vma *vma; - uint32_t flush_domains = 0; - bool flush_chipset = false; - int ret; - - list_for_each_entry(vma, vmas, exec_list) { - struct drm_i915_gem_object *obj = vma->obj; - - if (obj->active & other_rings) { - ret = i915_gem_object_sync(obj, req->engine, &req); - if (ret) - return ret; - } - - if (obj->base.write_domain & I915_GEM_DOMAIN_CPU) - flush_chipset |= i915_gem_clflush_object(obj, false); - - flush_domains |= obj->base.write_domain; - } - - if (flush_domains & I915_GEM_DOMAIN_GTT) - wmb(); - - /* Unconditionally invalidate gpu caches and ensure that we do flush - * any residual writes from the previous batch. - */ - return logical_ring_invalidate_all_caches(req); + spin_unlock_irqrestore(&engine->execlist_lock, flags); } int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request) @@ -717,7 +619,7 @@ int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request return ret; } - request->ringbuf = ce->ringbuf; + request->ring = ce->ring; if (i915.enable_guc_submission) { /* @@ -725,7 +627,7 @@ int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request * going any further, as the i915_add_request() call * later on mustn't fail ... */ - ret = i915_guc_wq_check_space(request); + ret = i915_guc_wq_reserve(request); if (ret) return ret; } @@ -762,7 +664,7 @@ err_unpin: } /* - * intel_logical_ring_advance_and_submit() - advance the tail and submit the workload + * intel_logical_ring_advance() - advance the tail and prepare for submission * @request: Request to advance the logical ringbuffer of. * * The tail is updated in our logical ringbuffer struct, not in the actual context. What @@ -771,13 +673,13 @@ err_unpin: * point, the tail *inside* the context is updated and the ELSP written to. */ static int -intel_logical_ring_advance_and_submit(struct drm_i915_gem_request *request) +intel_logical_ring_advance(struct drm_i915_gem_request *request) { - struct intel_ringbuffer *ringbuf = request->ringbuf; + struct intel_ring *ring = request->ring; struct intel_engine_cs *engine = request->engine; - intel_logical_ring_advance(ringbuf); - request->tail = ringbuf->tail; + intel_ring_advance(ring); + request->tail = ring->tail; /* * Here we add two extra NOOPs as padding to avoid @@ -785,9 +687,10 @@ intel_logical_ring_advance_and_submit(struct drm_i915_gem_request *request) * * Caller must reserve WA_TAIL_DWORDS for us! */ - intel_logical_ring_emit(ringbuf, MI_NOOP); - intel_logical_ring_emit(ringbuf, MI_NOOP); - intel_logical_ring_advance(ringbuf); + intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); + request->wa_tail = ring->tail; /* We keep the previous context alive until we retire the following * request. This ensures that any the context object is still pinned @@ -797,165 +700,12 @@ intel_logical_ring_advance_and_submit(struct drm_i915_gem_request *request) */ request->previous_context = engine->last_context; engine->last_context = request->ctx; - - if (i915.enable_guc_submission) - i915_guc_submit(request); - else - execlists_context_queue(request); - - return 0; -} - -/** - * execlists_submission() - submit a batchbuffer for execution, Execlists style - * @params: execbuffer call parameters. - * @args: execbuffer call arguments. - * @vmas: list of vmas. - * - * This is the evil twin version of i915_gem_ringbuffer_submission. It abstracts - * away the submission details of the execbuffer ioctl call. - * - * Return: non-zero if the submission fails. - */ -int intel_execlists_submission(struct i915_execbuffer_params *params, - struct drm_i915_gem_execbuffer2 *args, - struct list_head *vmas) -{ - struct drm_device *dev = params->dev; - struct intel_engine_cs *engine = params->engine; - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_ringbuffer *ringbuf = params->ctx->engine[engine->id].ringbuf; - u64 exec_start; - int instp_mode; - u32 instp_mask; - int ret; - - instp_mode = args->flags & I915_EXEC_CONSTANTS_MASK; - instp_mask = I915_EXEC_CONSTANTS_MASK; - switch (instp_mode) { - case I915_EXEC_CONSTANTS_REL_GENERAL: - case I915_EXEC_CONSTANTS_ABSOLUTE: - case I915_EXEC_CONSTANTS_REL_SURFACE: - if (instp_mode != 0 && engine != &dev_priv->engine[RCS]) { - DRM_DEBUG("non-0 rel constants mode on non-RCS\n"); - return -EINVAL; - } - - if (instp_mode != dev_priv->relative_constants_mode) { - if (instp_mode == I915_EXEC_CONSTANTS_REL_SURFACE) { - DRM_DEBUG("rel surface constants mode invalid on gen5+\n"); - return -EINVAL; - } - - /* The HW changed the meaning on this bit on gen6 */ - instp_mask &= ~I915_EXEC_CONSTANTS_REL_SURFACE; - } - break; - default: - DRM_DEBUG("execbuf with unknown constants: %d\n", instp_mode); - return -EINVAL; - } - - if (args->flags & I915_EXEC_GEN7_SOL_RESET) { - DRM_DEBUG("sol reset is gen7 only\n"); - return -EINVAL; - } - - ret = execlists_move_to_gpu(params->request, vmas); - if (ret) - return ret; - - if (engine == &dev_priv->engine[RCS] && - instp_mode != dev_priv->relative_constants_mode) { - ret = intel_ring_begin(params->request, 4); - if (ret) - return ret; - - intel_logical_ring_emit(ringbuf, MI_NOOP); - intel_logical_ring_emit(ringbuf, MI_LOAD_REGISTER_IMM(1)); - intel_logical_ring_emit_reg(ringbuf, INSTPM); - intel_logical_ring_emit(ringbuf, instp_mask << 16 | instp_mode); - intel_logical_ring_advance(ringbuf); - - dev_priv->relative_constants_mode = instp_mode; - } - - exec_start = params->batch_obj_vm_offset + - args->batch_start_offset; - - ret = engine->emit_bb_start(params->request, exec_start, params->dispatch_flags); - if (ret) - return ret; - - trace_i915_gem_ring_dispatch(params->request, params->dispatch_flags); - - i915_gem_execbuffer_move_to_active(vmas, params->request); - - return 0; -} - -void intel_execlists_cancel_requests(struct intel_engine_cs *engine) -{ - struct drm_i915_gem_request *req, *tmp; - LIST_HEAD(cancel_list); - - WARN_ON(!mutex_is_locked(&engine->i915->drm.struct_mutex)); - - spin_lock_bh(&engine->execlist_lock); - list_replace_init(&engine->execlist_queue, &cancel_list); - spin_unlock_bh(&engine->execlist_lock); - - list_for_each_entry_safe(req, tmp, &cancel_list, execlist_link) { - list_del(&req->execlist_link); - i915_gem_request_unreference(req); - } -} - -void intel_logical_ring_stop(struct intel_engine_cs *engine) -{ - struct drm_i915_private *dev_priv = engine->i915; - int ret; - - if (!intel_engine_initialized(engine)) - return; - - ret = intel_engine_idle(engine); - if (ret) - DRM_ERROR("failed to quiesce %s whilst cleaning up: %d\n", - engine->name, ret); - - /* TODO: Is this correct with Execlists enabled? */ - I915_WRITE_MODE(engine, _MASKED_BIT_ENABLE(STOP_RING)); - if (intel_wait_for_register(dev_priv, - RING_MI_MODE(engine->mmio_base), - MODE_IDLE, MODE_IDLE, - 1000)) { - DRM_ERROR("%s :timed out trying to stop ring\n", engine->name); - return; - } - I915_WRITE_MODE(engine, _MASKED_BIT_DISABLE(STOP_RING)); -} - -int logical_ring_flush_all_caches(struct drm_i915_gem_request *req) -{ - struct intel_engine_cs *engine = req->engine; - int ret; - - if (!engine->gpu_caches_dirty) - return 0; - - ret = engine->emit_flush(req, 0, I915_GEM_GPU_DOMAINS); - if (ret) - return ret; - - engine->gpu_caches_dirty = false; return 0; } static int intel_lr_context_pin(struct i915_gem_context *ctx, struct intel_engine_cs *engine) { - struct drm_i915_private *dev_priv = ctx->i915; struct intel_context *ce = &ctx->engine[engine->id]; void *vaddr; u32 *lrc_reg_state; @@ -966,41 +716,43 @@ static int intel_lr_context_pin(struct i915_gem_context *ctx, if (ce->pin_count++) return 0; - ret = i915_gem_obj_ggtt_pin(ce->state, GEN8_LR_CONTEXT_ALIGN, - PIN_OFFSET_BIAS | GUC_WOPCM_TOP); + ret = i915_vma_pin(ce->state, 0, GEN8_LR_CONTEXT_ALIGN, + PIN_OFFSET_BIAS | GUC_WOPCM_TOP | PIN_GLOBAL); if (ret) goto err; - vaddr = i915_gem_object_pin_map(ce->state); + vaddr = i915_gem_object_pin_map(ce->state->obj, I915_MAP_WB); if (IS_ERR(vaddr)) { ret = PTR_ERR(vaddr); - goto unpin_ctx_obj; + goto unpin_vma; } lrc_reg_state = vaddr + LRC_STATE_PN * PAGE_SIZE; - ret = intel_pin_and_map_ringbuffer_obj(dev_priv, ce->ringbuf); + ret = intel_ring_pin(ce->ring); if (ret) goto unpin_map; - i915_gem_context_reference(ctx); - ce->lrc_vma = i915_gem_obj_to_ggtt(ce->state); intel_lr_context_descriptor_update(ctx, engine); - lrc_reg_state[CTX_RING_BUFFER_START+1] = ce->ringbuf->vma->node.start; + lrc_reg_state[CTX_RING_BUFFER_START+1] = + i915_ggtt_offset(ce->ring->vma); ce->lrc_reg_state = lrc_reg_state; - ce->state->dirty = true; + ce->state->obj->dirty = true; /* Invalidate GuC TLB. */ - if (i915.enable_guc_submission) + if (i915.enable_guc_submission) { + struct drm_i915_private *dev_priv = ctx->i915; I915_WRITE(GEN8_GTCR, GEN8_GTCR_INVALIDATE); + } + i915_gem_context_get(ctx); return 0; unpin_map: - i915_gem_object_unpin_map(ce->state); -unpin_ctx_obj: - i915_gem_object_ggtt_unpin(ce->state); + i915_gem_object_unpin_map(ce->state->obj); +unpin_vma: + __i915_vma_unpin(ce->state); err: ce->pin_count = 0; return ret; @@ -1017,30 +769,24 @@ void intel_lr_context_unpin(struct i915_gem_context *ctx, if (--ce->pin_count) return; - intel_unpin_ringbuffer_obj(ce->ringbuf); - - i915_gem_object_unpin_map(ce->state); - i915_gem_object_ggtt_unpin(ce->state); + intel_ring_unpin(ce->ring); - ce->lrc_vma = NULL; - ce->lrc_desc = 0; - ce->lrc_reg_state = NULL; + i915_gem_object_unpin_map(ce->state->obj); + i915_vma_unpin(ce->state); - i915_gem_context_unreference(ctx); + i915_gem_context_put(ctx); } static int intel_logical_ring_workarounds_emit(struct drm_i915_gem_request *req) { int ret, i; - struct intel_engine_cs *engine = req->engine; - struct intel_ringbuffer *ringbuf = req->ringbuf; + struct intel_ring *ring = req->ring; struct i915_workarounds *w = &req->i915->workarounds; if (w->count == 0) return 0; - engine->gpu_caches_dirty = true; - ret = logical_ring_flush_all_caches(req); + ret = req->engine->emit_flush(req, EMIT_BARRIER); if (ret) return ret; @@ -1048,17 +794,16 @@ static int intel_logical_ring_workarounds_emit(struct drm_i915_gem_request *req) if (ret) return ret; - intel_logical_ring_emit(ringbuf, MI_LOAD_REGISTER_IMM(w->count)); + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(w->count)); for (i = 0; i < w->count; i++) { - intel_logical_ring_emit_reg(ringbuf, w->reg[i].addr); - intel_logical_ring_emit(ringbuf, w->reg[i].value); + intel_ring_emit_reg(ring, w->reg[i].addr); + intel_ring_emit(ring, w->reg[i].value); } - intel_logical_ring_emit(ringbuf, MI_NOOP); + intel_ring_emit(ring, MI_NOOP); - intel_logical_ring_advance(ringbuf); + intel_ring_advance(ring); - engine->gpu_caches_dirty = true; - ret = logical_ring_flush_all_caches(req); + ret = req->engine->emit_flush(req, EMIT_BARRIER); if (ret) return ret; @@ -1094,7 +839,7 @@ static int intel_logical_ring_workarounds_emit(struct drm_i915_gem_request *req) * code duplication. */ static inline int gen8_emit_flush_coherentl3_wa(struct intel_engine_cs *engine, - uint32_t *const batch, + uint32_t *batch, uint32_t index) { struct drm_i915_private *dev_priv = engine->i915; @@ -1113,7 +858,7 @@ static inline int gen8_emit_flush_coherentl3_wa(struct intel_engine_cs *engine, wa_ctx_emit(batch, index, (MI_STORE_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT)); wa_ctx_emit_reg(batch, index, GEN8_L3SQCREG4); - wa_ctx_emit(batch, index, engine->scratch.gtt_offset + 256); + wa_ctx_emit(batch, index, i915_ggtt_offset(engine->scratch) + 256); wa_ctx_emit(batch, index, 0); wa_ctx_emit(batch, index, MI_LOAD_REGISTER_IMM(1)); @@ -1131,7 +876,7 @@ static inline int gen8_emit_flush_coherentl3_wa(struct intel_engine_cs *engine, wa_ctx_emit(batch, index, (MI_LOAD_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT)); wa_ctx_emit_reg(batch, index, GEN8_L3SQCREG4); - wa_ctx_emit(batch, index, engine->scratch.gtt_offset + 256); + wa_ctx_emit(batch, index, i915_ggtt_offset(engine->scratch) + 256); wa_ctx_emit(batch, index, 0); return index; @@ -1156,37 +901,24 @@ static inline int wa_ctx_end(struct i915_wa_ctx_bb *wa_ctx, return 0; } -/** - * gen8_init_indirectctx_bb() - initialize indirect ctx batch with WA - * - * @engine: only applicable for RCS - * @wa_ctx: structure representing wa_ctx - * offset: specifies start of the batch, should be cache-aligned. This is updated - * with the offset value received as input. - * size: size of the batch in DWORDS but HW expects in terms of cachelines - * @batch: page in which WA are loaded - * @offset: This field specifies the start of the batch, it should be - * cache-aligned otherwise it is adjusted accordingly. - * Typically we only have one indirect_ctx and per_ctx batch buffer which are - * initialized at the beginning and shared across all contexts but this field - * helps us to have multiple batches at different offsets and select them based - * on a criteria. At the moment this batch always start at the beginning of the page - * and at this point we don't have multiple wa_ctx batch buffers. - * - * The number of WA applied are not known at the beginning; we use this field - * to return the no of DWORDS written. +/* + * Typically we only have one indirect_ctx and per_ctx batch buffer which are + * initialized at the beginning and shared across all contexts but this field + * helps us to have multiple batches at different offsets and select them based + * on a criteria. At the moment this batch always start at the beginning of the page + * and at this point we don't have multiple wa_ctx batch buffers. * - * It is to be noted that this batch does not contain MI_BATCH_BUFFER_END - * so it adds NOOPs as padding to make it cacheline aligned. - * MI_BATCH_BUFFER_END will be added to perctx batch and both of them together - * makes a complete batch buffer. + * The number of WA applied are not known at the beginning; we use this field + * to return the no of DWORDS written. * - * Return: non-zero if we exceed the PAGE_SIZE limit. + * It is to be noted that this batch does not contain MI_BATCH_BUFFER_END + * so it adds NOOPs as padding to make it cacheline aligned. + * MI_BATCH_BUFFER_END will be added to perctx batch and both of them together + * makes a complete batch buffer. */ - static int gen8_init_indirectctx_bb(struct intel_engine_cs *engine, struct i915_wa_ctx_bb *wa_ctx, - uint32_t *const batch, + uint32_t *batch, uint32_t *offset) { uint32_t scratch_addr; @@ -1205,7 +937,7 @@ static int gen8_init_indirectctx_bb(struct intel_engine_cs *engine, /* WaClearSlmSpaceAtContextSwitch:bdw,chv */ /* Actual scratch location is at 128 bytes offset */ - scratch_addr = engine->scratch.gtt_offset + 2*CACHELINE_BYTES; + scratch_addr = i915_ggtt_offset(engine->scratch) + 2 * CACHELINE_BYTES; wa_ctx_emit(batch, index, GFX_OP_PIPE_CONTROL(6)); wa_ctx_emit(batch, index, (PIPE_CONTROL_FLUSH_L3 | @@ -1230,26 +962,18 @@ static int gen8_init_indirectctx_bb(struct intel_engine_cs *engine, return wa_ctx_end(wa_ctx, *offset = index, CACHELINE_DWORDS); } -/** - * gen8_init_perctx_bb() - initialize per ctx batch with WA - * - * @engine: only applicable for RCS - * @wa_ctx: structure representing wa_ctx - * offset: specifies start of the batch, should be cache-aligned. - * size: size of the batch in DWORDS but HW expects in terms of cachelines - * @batch: page in which WA are loaded - * @offset: This field specifies the start of this batch. - * This batch is started immediately after indirect_ctx batch. Since we ensure - * that indirect_ctx ends on a cacheline this batch is aligned automatically. +/* + * This batch is started immediately after indirect_ctx batch. Since we ensure + * that indirect_ctx ends on a cacheline this batch is aligned automatically. * - * The number of DWORDS written are returned using this field. + * The number of DWORDS written are returned using this field. * * This batch is terminated with MI_BATCH_BUFFER_END and so we need not add padding * to align it with cacheline as padding after MI_BATCH_BUFFER_END is redundant. */ static int gen8_init_perctx_bb(struct intel_engine_cs *engine, struct i915_wa_ctx_bb *wa_ctx, - uint32_t *const batch, + uint32_t *batch, uint32_t *offset) { uint32_t index = wa_ctx_start(wa_ctx, *offset, CACHELINE_DWORDS); @@ -1264,7 +988,7 @@ static int gen8_init_perctx_bb(struct intel_engine_cs *engine, static int gen9_init_indirectctx_bb(struct intel_engine_cs *engine, struct i915_wa_ctx_bb *wa_ctx, - uint32_t *const batch, + uint32_t *batch, uint32_t *offset) { int ret; @@ -1282,11 +1006,18 @@ static int gen9_init_indirectctx_bb(struct intel_engine_cs *engine, return ret; index = ret; + /* WaDisableGatherAtSetShaderCommonSlice:skl,bxt,kbl */ + wa_ctx_emit(batch, index, MI_LOAD_REGISTER_IMM(1)); + wa_ctx_emit_reg(batch, index, COMMON_SLICE_CHICKEN2); + wa_ctx_emit(batch, index, _MASKED_BIT_DISABLE( + GEN9_DISABLE_GATHER_AT_SET_SHADER_COMMON_SLICE)); + wa_ctx_emit(batch, index, MI_NOOP); + /* WaClearSlmSpaceAtContextSwitch:kbl */ /* Actual scratch location is at 128 bytes offset */ if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_A0)) { - uint32_t scratch_addr - = engine->scratch.gtt_offset + 2*CACHELINE_BYTES; + u32 scratch_addr = + i915_ggtt_offset(engine->scratch) + 2 * CACHELINE_BYTES; wa_ctx_emit(batch, index, GFX_OP_PIPE_CONTROL(6)); wa_ctx_emit(batch, index, (PIPE_CONTROL_FLUSH_L3 | @@ -1332,7 +1063,7 @@ static int gen9_init_indirectctx_bb(struct intel_engine_cs *engine, static int gen9_init_perctx_bb(struct intel_engine_cs *engine, struct i915_wa_ctx_bb *wa_ctx, - uint32_t *const batch, + uint32_t *batch, uint32_t *offset) { uint32_t index = wa_ctx_start(wa_ctx, *offset, CACHELINE_DWORDS); @@ -1378,44 +1109,44 @@ static int gen9_init_perctx_bb(struct intel_engine_cs *engine, static int lrc_setup_wa_ctx_obj(struct intel_engine_cs *engine, u32 size) { - int ret; + struct drm_i915_gem_object *obj; + struct i915_vma *vma; + int err; - engine->wa_ctx.obj = i915_gem_object_create(&engine->i915->drm, - PAGE_ALIGN(size)); - if (IS_ERR(engine->wa_ctx.obj)) { - DRM_DEBUG_DRIVER("alloc LRC WA ctx backing obj failed.\n"); - ret = PTR_ERR(engine->wa_ctx.obj); - engine->wa_ctx.obj = NULL; - return ret; - } + obj = i915_gem_object_create(&engine->i915->drm, PAGE_ALIGN(size)); + if (IS_ERR(obj)) + return PTR_ERR(obj); - ret = i915_gem_obj_ggtt_pin(engine->wa_ctx.obj, PAGE_SIZE, 0); - if (ret) { - DRM_DEBUG_DRIVER("pin LRC WA ctx backing obj failed: %d\n", - ret); - drm_gem_object_unreference(&engine->wa_ctx.obj->base); - return ret; + vma = i915_vma_create(obj, &engine->i915->ggtt.base, NULL); + if (IS_ERR(vma)) { + err = PTR_ERR(vma); + goto err; } + err = i915_vma_pin(vma, 0, PAGE_SIZE, PIN_GLOBAL | PIN_HIGH); + if (err) + goto err; + + engine->wa_ctx.vma = vma; return 0; + +err: + i915_gem_object_put(obj); + return err; } static void lrc_destroy_wa_ctx_obj(struct intel_engine_cs *engine) { - if (engine->wa_ctx.obj) { - i915_gem_object_ggtt_unpin(engine->wa_ctx.obj); - drm_gem_object_unreference(&engine->wa_ctx.obj->base); - engine->wa_ctx.obj = NULL; - } + i915_vma_unpin_and_release(&engine->wa_ctx.vma); } static int intel_init_workaround_bb(struct intel_engine_cs *engine) { - int ret; + struct i915_ctx_workarounds *wa_ctx = &engine->wa_ctx; uint32_t *batch; uint32_t offset; struct page *page; - struct i915_ctx_workarounds *wa_ctx = &engine->wa_ctx; + int ret; WARN_ON(engine->id != RCS); @@ -1427,7 +1158,7 @@ static int intel_init_workaround_bb(struct intel_engine_cs *engine) } /* some WA perform writes to scratch page, ensure it is valid */ - if (engine->scratch.obj == NULL) { + if (!engine->scratch) { DRM_ERROR("scratch page not allocated for %s\n", engine->name); return -EINVAL; } @@ -1438,7 +1169,7 @@ static int intel_init_workaround_bb(struct intel_engine_cs *engine) return ret; } - page = i915_gem_object_get_dirty_page(wa_ctx->obj, 0); + page = i915_gem_object_get_dirty_page(wa_ctx->vma->obj, 0); batch = kmap_atomic(page); offset = 0; @@ -1485,55 +1216,37 @@ static void lrc_init_hws(struct intel_engine_cs *engine) struct drm_i915_private *dev_priv = engine->i915; I915_WRITE(RING_HWS_PGA(engine->mmio_base), - (u32)engine->status_page.gfx_addr); + engine->status_page.ggtt_offset); POSTING_READ(RING_HWS_PGA(engine->mmio_base)); } static int gen8_init_common_ring(struct intel_engine_cs *engine) { struct drm_i915_private *dev_priv = engine->i915; - unsigned int next_context_status_buffer_hw; + int ret; + + ret = intel_mocs_init_engine(engine); + if (ret) + return ret; lrc_init_hws(engine); - I915_WRITE_IMR(engine, - ~(engine->irq_enable_mask | engine->irq_keep_mask)); + intel_engine_reset_irq(engine); + I915_WRITE(RING_HWSTAM(engine->mmio_base), 0xffffffff); I915_WRITE(RING_MODE_GEN7(engine), _MASKED_BIT_DISABLE(GFX_REPLAY_MODE) | _MASKED_BIT_ENABLE(GFX_RUN_LIST_ENABLE)); - POSTING_READ(RING_MODE_GEN7(engine)); - - /* - * Instead of resetting the Context Status Buffer (CSB) read pointer to - * zero, we need to read the write pointer from hardware and use its - * value because "this register is power context save restored". - * Effectively, these states have been observed: - * - * | Suspend-to-idle (freeze) | Suspend-to-RAM (mem) | - * BDW | CSB regs not reset | CSB regs reset | - * CHT | CSB regs not reset | CSB regs not reset | - * SKL | ? | ? | - * BXT | ? | ? | - */ - next_context_status_buffer_hw = - GEN8_CSB_WRITE_PTR(I915_READ(RING_CONTEXT_STATUS_PTR(engine))); - - /* - * When the CSB registers are reset (also after power-up / gpu reset), - * CSB write pointer is set to all 1's, which is not valid, use '5' in - * this special case, so the first element read is CSB[0]. - */ - if (next_context_status_buffer_hw == GEN8_CSB_PTR_MASK) - next_context_status_buffer_hw = (GEN8_CSB_ENTRIES - 1); - engine->next_context_status_buffer = next_context_status_buffer_hw; DRM_DEBUG_DRIVER("Execlists enabled for %s\n", engine->name); intel_engine_init_hangcheck(engine); - return intel_mocs_init_engine(engine); + if (!execlists_elsp_idle(engine)) + execlists_submit_ports(engine); + + return 0; } static int gen8_init_render_ring(struct intel_engine_cs *engine) @@ -1569,11 +1282,41 @@ static int gen9_init_render_ring(struct intel_engine_cs *engine) return init_workarounds_ring(engine); } +static void reset_common_ring(struct intel_engine_cs *engine, + struct drm_i915_gem_request *request) +{ + struct drm_i915_private *dev_priv = engine->i915; + struct execlist_port *port = engine->execlist_port; + struct intel_context *ce = &request->ctx->engine[engine->id]; + + /* Move the RING_HEAD onto the breadcrumb, past the hanging batch */ + ce->lrc_reg_state[CTX_RING_HEAD+1] = request->postfix; + request->ring->head = request->postfix; + request->ring->last_retired_head = -1; + intel_ring_update_space(request->ring); + + if (i915.enable_guc_submission) + return; + + /* Catch up with any missed context-switch interrupts */ + I915_WRITE(RING_CONTEXT_STATUS_PTR(engine), _MASKED_FIELD(0xffff, 0)); + if (request->ctx != port[0].request->ctx) { + i915_gem_request_put(port[0].request); + port[0] = port[1]; + memset(&port[1], 0, sizeof(port[1])); + } + + /* CS is stopped, and we will resubmit both ports on resume */ + GEM_BUG_ON(request->ctx != port[0].request->ctx); + port[0].count = 0; + port[1].count = 0; +} + static int intel_logical_ring_emit_pdps(struct drm_i915_gem_request *req) { struct i915_hw_ppgtt *ppgtt = req->ctx->ppgtt; + struct intel_ring *ring = req->ring; struct intel_engine_cs *engine = req->engine; - struct intel_ringbuffer *ringbuf = req->ringbuf; const int num_lri_cmds = GEN8_LEGACY_PDPES * 2; int i, ret; @@ -1581,28 +1324,27 @@ static int intel_logical_ring_emit_pdps(struct drm_i915_gem_request *req) if (ret) return ret; - intel_logical_ring_emit(ringbuf, MI_LOAD_REGISTER_IMM(num_lri_cmds)); + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(num_lri_cmds)); for (i = GEN8_LEGACY_PDPES - 1; i >= 0; i--) { const dma_addr_t pd_daddr = i915_page_dir_dma_addr(ppgtt, i); - intel_logical_ring_emit_reg(ringbuf, - GEN8_RING_PDP_UDW(engine, i)); - intel_logical_ring_emit(ringbuf, upper_32_bits(pd_daddr)); - intel_logical_ring_emit_reg(ringbuf, - GEN8_RING_PDP_LDW(engine, i)); - intel_logical_ring_emit(ringbuf, lower_32_bits(pd_daddr)); + intel_ring_emit_reg(ring, GEN8_RING_PDP_UDW(engine, i)); + intel_ring_emit(ring, upper_32_bits(pd_daddr)); + intel_ring_emit_reg(ring, GEN8_RING_PDP_LDW(engine, i)); + intel_ring_emit(ring, lower_32_bits(pd_daddr)); } - intel_logical_ring_emit(ringbuf, MI_NOOP); - intel_logical_ring_advance(ringbuf); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); return 0; } static int gen8_emit_bb_start(struct drm_i915_gem_request *req, - u64 offset, unsigned dispatch_flags) + u64 offset, u32 len, + unsigned int dispatch_flags) { - struct intel_ringbuffer *ringbuf = req->ringbuf; + struct intel_ring *ring = req->ring; bool ppgtt = !(dispatch_flags & I915_DISPATCH_SECURE); int ret; @@ -1629,14 +1371,14 @@ static int gen8_emit_bb_start(struct drm_i915_gem_request *req, return ret; /* FIXME(BDW): Address space and security selectors. */ - intel_logical_ring_emit(ringbuf, MI_BATCH_BUFFER_START_GEN8 | - (ppgtt<<8) | - (dispatch_flags & I915_DISPATCH_RS ? - MI_BATCH_RESOURCE_STREAMER : 0)); - intel_logical_ring_emit(ringbuf, lower_32_bits(offset)); - intel_logical_ring_emit(ringbuf, upper_32_bits(offset)); - intel_logical_ring_emit(ringbuf, MI_NOOP); - intel_logical_ring_advance(ringbuf); + intel_ring_emit(ring, MI_BATCH_BUFFER_START_GEN8 | + (ppgtt<<8) | + (dispatch_flags & I915_DISPATCH_RS ? + MI_BATCH_RESOURCE_STREAMER : 0)); + intel_ring_emit(ring, lower_32_bits(offset)); + intel_ring_emit(ring, upper_32_bits(offset)); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); return 0; } @@ -1655,14 +1397,10 @@ static void gen8_logical_ring_disable_irq(struct intel_engine_cs *engine) I915_WRITE_IMR(engine, ~engine->irq_keep_mask); } -static int gen8_emit_flush(struct drm_i915_gem_request *request, - u32 invalidate_domains, - u32 unused) +static int gen8_emit_flush(struct drm_i915_gem_request *request, u32 mode) { - struct intel_ringbuffer *ringbuf = request->ringbuf; - struct intel_engine_cs *engine = ringbuf->engine; - struct drm_i915_private *dev_priv = request->i915; - uint32_t cmd; + struct intel_ring *ring = request->ring; + u32 cmd; int ret; ret = intel_ring_begin(request, 4); @@ -1678,30 +1416,30 @@ static int gen8_emit_flush(struct drm_i915_gem_request *request, */ cmd |= MI_FLUSH_DW_STORE_INDEX | MI_FLUSH_DW_OP_STOREDW; - if (invalidate_domains & I915_GEM_GPU_DOMAINS) { + if (mode & EMIT_INVALIDATE) { cmd |= MI_INVALIDATE_TLB; - if (engine == &dev_priv->engine[VCS]) + if (request->engine->id == VCS) cmd |= MI_INVALIDATE_BSD; } - intel_logical_ring_emit(ringbuf, cmd); - intel_logical_ring_emit(ringbuf, - I915_GEM_HWS_SCRATCH_ADDR | - MI_FLUSH_DW_USE_GTT); - intel_logical_ring_emit(ringbuf, 0); /* upper addr */ - intel_logical_ring_emit(ringbuf, 0); /* value */ - intel_logical_ring_advance(ringbuf); + intel_ring_emit(ring, cmd); + intel_ring_emit(ring, + I915_GEM_HWS_SCRATCH_ADDR | + MI_FLUSH_DW_USE_GTT); + intel_ring_emit(ring, 0); /* upper addr */ + intel_ring_emit(ring, 0); /* value */ + intel_ring_advance(ring); return 0; } static int gen8_emit_flush_render(struct drm_i915_gem_request *request, - u32 invalidate_domains, - u32 flush_domains) + u32 mode) { - struct intel_ringbuffer *ringbuf = request->ringbuf; - struct intel_engine_cs *engine = ringbuf->engine; - u32 scratch_addr = engine->scratch.gtt_offset + 2 * CACHELINE_BYTES; + struct intel_ring *ring = request->ring; + struct intel_engine_cs *engine = request->engine; + u32 scratch_addr = + i915_ggtt_offset(engine->scratch) + 2 * CACHELINE_BYTES; bool vf_flush_wa = false, dc_flush_wa = false; u32 flags = 0; int ret; @@ -1709,14 +1447,14 @@ static int gen8_emit_flush_render(struct drm_i915_gem_request *request, flags |= PIPE_CONTROL_CS_STALL; - if (flush_domains) { + if (mode & EMIT_FLUSH) { flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; flags |= PIPE_CONTROL_DC_FLUSH_ENABLE; flags |= PIPE_CONTROL_FLUSH_ENABLE; } - if (invalidate_domains) { + if (mode & EMIT_INVALIDATE) { flags |= PIPE_CONTROL_TLB_INVALIDATE; flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE; flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE; @@ -1751,40 +1489,40 @@ static int gen8_emit_flush_render(struct drm_i915_gem_request *request, return ret; if (vf_flush_wa) { - intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6)); - intel_logical_ring_emit(ringbuf, 0); - intel_logical_ring_emit(ringbuf, 0); - intel_logical_ring_emit(ringbuf, 0); - intel_logical_ring_emit(ringbuf, 0); - intel_logical_ring_emit(ringbuf, 0); + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6)); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); } if (dc_flush_wa) { - intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6)); - intel_logical_ring_emit(ringbuf, PIPE_CONTROL_DC_FLUSH_ENABLE); - intel_logical_ring_emit(ringbuf, 0); - intel_logical_ring_emit(ringbuf, 0); - intel_logical_ring_emit(ringbuf, 0); - intel_logical_ring_emit(ringbuf, 0); + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6)); + intel_ring_emit(ring, PIPE_CONTROL_DC_FLUSH_ENABLE); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); } - intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6)); - intel_logical_ring_emit(ringbuf, flags); - intel_logical_ring_emit(ringbuf, scratch_addr); - intel_logical_ring_emit(ringbuf, 0); - intel_logical_ring_emit(ringbuf, 0); - intel_logical_ring_emit(ringbuf, 0); + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6)); + intel_ring_emit(ring, flags); + intel_ring_emit(ring, scratch_addr); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); if (dc_flush_wa) { - intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6)); - intel_logical_ring_emit(ringbuf, PIPE_CONTROL_CS_STALL); - intel_logical_ring_emit(ringbuf, 0); - intel_logical_ring_emit(ringbuf, 0); - intel_logical_ring_emit(ringbuf, 0); - intel_logical_ring_emit(ringbuf, 0); + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6)); + intel_ring_emit(ring, PIPE_CONTROL_CS_STALL); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); } - intel_logical_ring_advance(ringbuf); + intel_ring_advance(ring); return 0; } @@ -1813,7 +1551,7 @@ static void bxt_a_seqno_barrier(struct intel_engine_cs *engine) static int gen8_emit_request(struct drm_i915_gem_request *request) { - struct intel_ringbuffer *ringbuf = request->ringbuf; + struct intel_ring *ring = request->ring; int ret; ret = intel_ring_begin(request, 6 + WA_TAIL_DWORDS); @@ -1823,21 +1561,20 @@ static int gen8_emit_request(struct drm_i915_gem_request *request) /* w/a: bit 5 needs to be zero for MI_FLUSH_DW address. */ BUILD_BUG_ON(I915_GEM_HWS_INDEX_ADDR & (1 << 5)); - intel_logical_ring_emit(ringbuf, - (MI_FLUSH_DW + 1) | MI_FLUSH_DW_OP_STOREDW); - intel_logical_ring_emit(ringbuf, - intel_hws_seqno_address(request->engine) | - MI_FLUSH_DW_USE_GTT); - intel_logical_ring_emit(ringbuf, 0); - intel_logical_ring_emit(ringbuf, request->seqno); - intel_logical_ring_emit(ringbuf, MI_USER_INTERRUPT); - intel_logical_ring_emit(ringbuf, MI_NOOP); - return intel_logical_ring_advance_and_submit(request); + intel_ring_emit(ring, (MI_FLUSH_DW + 1) | MI_FLUSH_DW_OP_STOREDW); + intel_ring_emit(ring, + intel_hws_seqno_address(request->engine) | + MI_FLUSH_DW_USE_GTT); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, request->fence.seqno); + intel_ring_emit(ring, MI_USER_INTERRUPT); + intel_ring_emit(ring, MI_NOOP); + return intel_logical_ring_advance(request); } static int gen8_emit_request_render(struct drm_i915_gem_request *request) { - struct intel_ringbuffer *ringbuf = request->ringbuf; + struct intel_ring *ring = request->ring; int ret; ret = intel_ring_begin(request, 8 + WA_TAIL_DWORDS); @@ -1851,50 +1588,19 @@ static int gen8_emit_request_render(struct drm_i915_gem_request *request) * need a prior CS_STALL, which is emitted by the flush * following the batch. */ - intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6)); - intel_logical_ring_emit(ringbuf, - (PIPE_CONTROL_GLOBAL_GTT_IVB | - PIPE_CONTROL_CS_STALL | - PIPE_CONTROL_QW_WRITE)); - intel_logical_ring_emit(ringbuf, - intel_hws_seqno_address(request->engine)); - intel_logical_ring_emit(ringbuf, 0); - intel_logical_ring_emit(ringbuf, i915_gem_request_get_seqno(request)); + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6)); + intel_ring_emit(ring, + (PIPE_CONTROL_GLOBAL_GTT_IVB | + PIPE_CONTROL_CS_STALL | + PIPE_CONTROL_QW_WRITE)); + intel_ring_emit(ring, intel_hws_seqno_address(request->engine)); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, i915_gem_request_get_seqno(request)); /* We're thrashing one dword of HWS. */ - intel_logical_ring_emit(ringbuf, 0); - intel_logical_ring_emit(ringbuf, MI_USER_INTERRUPT); - intel_logical_ring_emit(ringbuf, MI_NOOP); - return intel_logical_ring_advance_and_submit(request); -} - -static int intel_lr_context_render_state_init(struct drm_i915_gem_request *req) -{ - struct render_state so; - int ret; - - ret = i915_gem_render_state_prepare(req->engine, &so); - if (ret) - return ret; - - if (so.rodata == NULL) - return 0; - - ret = req->engine->emit_bb_start(req, so.ggtt_offset, - I915_DISPATCH_SECURE); - if (ret) - goto out; - - ret = req->engine->emit_bb_start(req, - (so.ggtt_offset + so.aux_batch_offset), - I915_DISPATCH_SECURE); - if (ret) - goto out; - - i915_vma_move_to_active(i915_gem_obj_to_ggtt(so.obj), req); - -out: - i915_gem_render_state_fini(&so); - return ret; + intel_ring_emit(ring, 0); + intel_ring_emit(ring, MI_USER_INTERRUPT); + intel_ring_emit(ring, MI_NOOP); + return intel_logical_ring_advance(request); } static int gen8_init_rcs_context(struct drm_i915_gem_request *req) @@ -1913,14 +1619,12 @@ static int gen8_init_rcs_context(struct drm_i915_gem_request *req) if (ret) DRM_ERROR("MOCS failed to program: expect performance issues.\n"); - return intel_lr_context_render_state_init(req); + return i915_gem_render_state_init(req); } /** * intel_logical_ring_cleanup() - deallocate the Engine Command Streamer - * * @engine: Engine Command Streamer. - * */ void intel_logical_ring_cleanup(struct intel_engine_cs *engine) { @@ -1939,39 +1643,42 @@ void intel_logical_ring_cleanup(struct intel_engine_cs *engine) dev_priv = engine->i915; if (engine->buffer) { - intel_logical_ring_stop(engine); WARN_ON((I915_READ_MODE(engine) & MODE_IDLE) == 0); } if (engine->cleanup) engine->cleanup(engine); - i915_cmd_parser_fini_ring(engine); - i915_gem_batch_pool_fini(&engine->batch_pool); - - intel_engine_fini_breadcrumbs(engine); + intel_engine_cleanup_common(engine); - if (engine->status_page.obj) { - i915_gem_object_unpin_map(engine->status_page.obj); - engine->status_page.obj = NULL; + if (engine->status_page.vma) { + i915_gem_object_unpin_map(engine->status_page.vma->obj); + engine->status_page.vma = NULL; } intel_lr_context_unpin(dev_priv->kernel_context, engine); - engine->idle_lite_restore_wa = 0; - engine->disable_lite_restore_wa = false; - engine->ctx_desc_template = 0; - lrc_destroy_wa_ctx_obj(engine); engine->i915 = NULL; } +void intel_execlists_enable_submission(struct drm_i915_private *dev_priv) +{ + struct intel_engine_cs *engine; + + for_each_engine(engine, dev_priv) + engine->submit_request = execlists_submit_request; +} + static void logical_ring_default_vfuncs(struct intel_engine_cs *engine) { /* Default vfuncs which can be overriden by each engine. */ engine->init_hw = gen8_init_common_ring; - engine->emit_request = gen8_emit_request; + engine->reset_hw = reset_common_ring; engine->emit_flush = gen8_emit_flush; + engine->emit_request = gen8_emit_request; + engine->submit_request = execlists_submit_request; + engine->irq_enable = gen8_logical_ring_enable_irq; engine->irq_disable = gen8_logical_ring_disable_irq; engine->emit_bb_start = gen8_emit_bb_start; @@ -1980,41 +1687,71 @@ logical_ring_default_vfuncs(struct intel_engine_cs *engine) } static inline void -logical_ring_default_irqs(struct intel_engine_cs *engine, unsigned shift) +logical_ring_default_irqs(struct intel_engine_cs *engine) { + unsigned shift = engine->irq_shift; engine->irq_enable_mask = GT_RENDER_USER_INTERRUPT << shift; engine->irq_keep_mask = GT_CONTEXT_SWITCH_INTERRUPT << shift; } static int -lrc_setup_hws(struct intel_engine_cs *engine, - struct drm_i915_gem_object *dctx_obj) +lrc_setup_hws(struct intel_engine_cs *engine, struct i915_vma *vma) { + const int hws_offset = LRC_PPHWSP_PN * PAGE_SIZE; void *hws; /* The HWSP is part of the default context object in LRC mode. */ - engine->status_page.gfx_addr = i915_gem_obj_ggtt_offset(dctx_obj) + - LRC_PPHWSP_PN * PAGE_SIZE; - hws = i915_gem_object_pin_map(dctx_obj); + hws = i915_gem_object_pin_map(vma->obj, I915_MAP_WB); if (IS_ERR(hws)) return PTR_ERR(hws); - engine->status_page.page_addr = hws + LRC_PPHWSP_PN * PAGE_SIZE; - engine->status_page.obj = dctx_obj; + + engine->status_page.page_addr = hws + hws_offset; + engine->status_page.ggtt_offset = i915_ggtt_offset(vma) + hws_offset; + engine->status_page.vma = vma; return 0; } +static void +logical_ring_setup(struct intel_engine_cs *engine) +{ + struct drm_i915_private *dev_priv = engine->i915; + enum forcewake_domains fw_domains; + + intel_engine_setup_common(engine); + + /* Intentionally left blank. */ + engine->buffer = NULL; + + fw_domains = intel_uncore_forcewake_for_reg(dev_priv, + RING_ELSP(engine), + FW_REG_WRITE); + + fw_domains |= intel_uncore_forcewake_for_reg(dev_priv, + RING_CONTEXT_STATUS_PTR(engine), + FW_REG_READ | FW_REG_WRITE); + + fw_domains |= intel_uncore_forcewake_for_reg(dev_priv, + RING_CONTEXT_STATUS_BUF_BASE(engine), + FW_REG_READ); + + engine->fw_domains = fw_domains; + + tasklet_init(&engine->irq_tasklet, + intel_lrc_irq_handler, (unsigned long)engine); + + logical_ring_init_platform_invariants(engine); + logical_ring_default_vfuncs(engine); + logical_ring_default_irqs(engine); +} + static int logical_ring_init(struct intel_engine_cs *engine) { struct i915_gem_context *dctx = engine->i915->kernel_context; int ret; - ret = intel_engine_init_breadcrumbs(engine); - if (ret) - goto error; - - ret = i915_cmd_parser_init_ring(engine); + ret = intel_engine_init_common(engine); if (ret) goto error; @@ -2044,11 +1781,13 @@ error: return ret; } -static int logical_render_ring_init(struct intel_engine_cs *engine) +int logical_render_ring_init(struct intel_engine_cs *engine) { struct drm_i915_private *dev_priv = engine->i915; int ret; + logical_ring_setup(engine); + if (HAS_L3_DPF(dev_priv)) engine->irq_keep_mask |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT; @@ -2058,11 +1797,10 @@ static int logical_render_ring_init(struct intel_engine_cs *engine) else engine->init_hw = gen8_init_render_ring; engine->init_context = gen8_init_rcs_context; - engine->cleanup = intel_fini_pipe_control; engine->emit_flush = gen8_emit_flush_render; engine->emit_request = gen8_emit_request_render; - ret = intel_init_pipe_control(engine, 4096); + ret = intel_engine_create_scratch(engine, 4096); if (ret) return ret; @@ -2085,160 +1823,11 @@ static int logical_render_ring_init(struct intel_engine_cs *engine) return ret; } -static const struct logical_ring_info { - const char *name; - unsigned exec_id; - unsigned guc_id; - u32 mmio_base; - unsigned irq_shift; - int (*init)(struct intel_engine_cs *engine); -} logical_rings[] = { - [RCS] = { - .name = "render ring", - .exec_id = I915_EXEC_RENDER, - .guc_id = GUC_RENDER_ENGINE, - .mmio_base = RENDER_RING_BASE, - .irq_shift = GEN8_RCS_IRQ_SHIFT, - .init = logical_render_ring_init, - }, - [BCS] = { - .name = "blitter ring", - .exec_id = I915_EXEC_BLT, - .guc_id = GUC_BLITTER_ENGINE, - .mmio_base = BLT_RING_BASE, - .irq_shift = GEN8_BCS_IRQ_SHIFT, - .init = logical_ring_init, - }, - [VCS] = { - .name = "bsd ring", - .exec_id = I915_EXEC_BSD, - .guc_id = GUC_VIDEO_ENGINE, - .mmio_base = GEN6_BSD_RING_BASE, - .irq_shift = GEN8_VCS1_IRQ_SHIFT, - .init = logical_ring_init, - }, - [VCS2] = { - .name = "bsd2 ring", - .exec_id = I915_EXEC_BSD, - .guc_id = GUC_VIDEO_ENGINE2, - .mmio_base = GEN8_BSD2_RING_BASE, - .irq_shift = GEN8_VCS2_IRQ_SHIFT, - .init = logical_ring_init, - }, - [VECS] = { - .name = "video enhancement ring", - .exec_id = I915_EXEC_VEBOX, - .guc_id = GUC_VIDEOENHANCE_ENGINE, - .mmio_base = VEBOX_RING_BASE, - .irq_shift = GEN8_VECS_IRQ_SHIFT, - .init = logical_ring_init, - }, -}; - -static struct intel_engine_cs * -logical_ring_setup(struct drm_i915_private *dev_priv, enum intel_engine_id id) +int logical_xcs_ring_init(struct intel_engine_cs *engine) { - const struct logical_ring_info *info = &logical_rings[id]; - struct intel_engine_cs *engine = &dev_priv->engine[id]; - enum forcewake_domains fw_domains; - - engine->id = id; - engine->name = info->name; - engine->exec_id = info->exec_id; - engine->guc_id = info->guc_id; - engine->mmio_base = info->mmio_base; - - engine->i915 = dev_priv; + logical_ring_setup(engine); - /* Intentionally left blank. */ - engine->buffer = NULL; - - fw_domains = intel_uncore_forcewake_for_reg(dev_priv, - RING_ELSP(engine), - FW_REG_WRITE); - - fw_domains |= intel_uncore_forcewake_for_reg(dev_priv, - RING_CONTEXT_STATUS_PTR(engine), - FW_REG_READ | FW_REG_WRITE); - - fw_domains |= intel_uncore_forcewake_for_reg(dev_priv, - RING_CONTEXT_STATUS_BUF_BASE(engine), - FW_REG_READ); - - engine->fw_domains = fw_domains; - - INIT_LIST_HEAD(&engine->active_list); - INIT_LIST_HEAD(&engine->request_list); - INIT_LIST_HEAD(&engine->buffers); - INIT_LIST_HEAD(&engine->execlist_queue); - spin_lock_init(&engine->execlist_lock); - - tasklet_init(&engine->irq_tasklet, - intel_lrc_irq_handler, (unsigned long)engine); - - logical_ring_init_platform_invariants(engine); - logical_ring_default_vfuncs(engine); - logical_ring_default_irqs(engine, info->irq_shift); - - intel_engine_init_hangcheck(engine); - i915_gem_batch_pool_init(&dev_priv->drm, &engine->batch_pool); - - return engine; -} - -/** - * intel_logical_rings_init() - allocate, populate and init the Engine Command Streamers - * @dev: DRM device. - * - * This function inits the engines for an Execlists submission style (the - * equivalent in the legacy ringbuffer submission world would be - * i915_gem_init_engines). It does it only for those engines that are present in - * the hardware. - * - * Return: non-zero if the initialization failed. - */ -int intel_logical_rings_init(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = to_i915(dev); - unsigned int mask = 0; - unsigned int i; - int ret; - - WARN_ON(INTEL_INFO(dev_priv)->ring_mask & - GENMASK(sizeof(mask) * BITS_PER_BYTE - 1, I915_NUM_ENGINES)); - - for (i = 0; i < ARRAY_SIZE(logical_rings); i++) { - if (!HAS_ENGINE(dev_priv, i)) - continue; - - if (!logical_rings[i].init) - continue; - - ret = logical_rings[i].init(logical_ring_setup(dev_priv, i)); - if (ret) - goto cleanup; - - mask |= ENGINE_MASK(i); - } - - /* - * Catch failures to update logical_rings table when the new engines - * are added to the driver by a warning and disabling the forgotten - * engines. - */ - if (WARN_ON(mask != INTEL_INFO(dev_priv)->ring_mask)) { - struct intel_device_info *info = - (struct intel_device_info *)&dev_priv->info; - info->ring_mask = mask; - } - - return 0; - -cleanup: - for (i = 0; i < I915_NUM_ENGINES; i++) - intel_logical_ring_cleanup(&dev_priv->engine[i]); - - return ret; + return logical_ring_init(engine); } static u32 @@ -2259,24 +1848,24 @@ make_rpcs(struct drm_i915_private *dev_priv) * must make an explicit request through RPCS for full * enablement. */ - if (INTEL_INFO(dev_priv)->has_slice_pg) { + if (INTEL_INFO(dev_priv)->sseu.has_slice_pg) { rpcs |= GEN8_RPCS_S_CNT_ENABLE; - rpcs |= INTEL_INFO(dev_priv)->slice_total << + rpcs |= hweight8(INTEL_INFO(dev_priv)->sseu.slice_mask) << GEN8_RPCS_S_CNT_SHIFT; rpcs |= GEN8_RPCS_ENABLE; } - if (INTEL_INFO(dev_priv)->has_subslice_pg) { + if (INTEL_INFO(dev_priv)->sseu.has_subslice_pg) { rpcs |= GEN8_RPCS_SS_CNT_ENABLE; - rpcs |= INTEL_INFO(dev_priv)->subslice_per_slice << + rpcs |= hweight8(INTEL_INFO(dev_priv)->sseu.subslice_mask) << GEN8_RPCS_SS_CNT_SHIFT; rpcs |= GEN8_RPCS_ENABLE; } - if (INTEL_INFO(dev_priv)->has_eu_pg) { - rpcs |= INTEL_INFO(dev_priv)->eu_per_subslice << + if (INTEL_INFO(dev_priv)->sseu.has_eu_pg) { + rpcs |= INTEL_INFO(dev_priv)->sseu.eu_per_subslice << GEN8_RPCS_EU_MIN_SHIFT; - rpcs |= INTEL_INFO(dev_priv)->eu_per_subslice << + rpcs |= INTEL_INFO(dev_priv)->sseu.eu_per_subslice << GEN8_RPCS_EU_MAX_SHIFT; rpcs |= GEN8_RPCS_ENABLE; } @@ -2309,7 +1898,7 @@ static int populate_lr_context(struct i915_gem_context *ctx, struct drm_i915_gem_object *ctx_obj, struct intel_engine_cs *engine, - struct intel_ringbuffer *ringbuf) + struct intel_ring *ring) { struct drm_i915_private *dev_priv = ctx->i915; struct i915_hw_ppgtt *ppgtt = ctx->ppgtt; @@ -2326,7 +1915,7 @@ populate_lr_context(struct i915_gem_context *ctx, return ret; } - vaddr = i915_gem_object_pin_map(ctx_obj); + vaddr = i915_gem_object_pin_map(ctx_obj, I915_MAP_WB); if (IS_ERR(vaddr)) { ret = PTR_ERR(vaddr); DRM_DEBUG_DRIVER("Could not map object pages! (%d)\n", ret); @@ -2362,7 +1951,7 @@ populate_lr_context(struct i915_gem_context *ctx, RING_START(engine->mmio_base), 0); ASSIGN_CTX_REG(reg_state, CTX_RING_BUFFER_CONTROL, RING_CTL(engine->mmio_base), - ((ringbuf->size - PAGE_SIZE) & RING_NR_PAGES) | RING_VALID); + ((ring->size - PAGE_SIZE) & RING_NR_PAGES) | RING_VALID); ASSIGN_CTX_REG(reg_state, CTX_BB_HEAD_U, RING_BBADDR_UDW(engine->mmio_base), 0); ASSIGN_CTX_REG(reg_state, CTX_BB_HEAD_L, @@ -2383,9 +1972,9 @@ populate_lr_context(struct i915_gem_context *ctx, RING_INDIRECT_CTX(engine->mmio_base), 0); ASSIGN_CTX_REG(reg_state, CTX_RCS_INDIRECT_CTX_OFFSET, RING_INDIRECT_CTX_OFFSET(engine->mmio_base), 0); - if (engine->wa_ctx.obj) { + if (engine->wa_ctx.vma) { struct i915_ctx_workarounds *wa_ctx = &engine->wa_ctx; - uint32_t ggtt_offset = i915_gem_obj_ggtt_offset(wa_ctx->obj); + u32 ggtt_offset = i915_ggtt_offset(wa_ctx->vma); reg_state[CTX_RCS_INDIRECT_CTX+1] = (ggtt_offset + wa_ctx->indirect_ctx.offset * sizeof(uint32_t)) | @@ -2484,26 +2073,14 @@ uint32_t intel_lr_context_size(struct intel_engine_cs *engine) return ret; } -/** - * execlists_context_deferred_alloc() - create the LRC specific bits of a context - * @ctx: LR context to create. - * @engine: engine to be used with the context. - * - * This function can be called more than once, with different engines, if we plan - * to use the context with them. The context backing objects and the ringbuffers - * (specially the ringbuffer backing objects) suck a lot of memory up, and that's why - * the creation is a deferred call: it's better to make sure first that we need to use - * a given ring with the context. - * - * Return: non-zero on error. - */ static int execlists_context_deferred_alloc(struct i915_gem_context *ctx, struct intel_engine_cs *engine) { struct drm_i915_gem_object *ctx_obj; struct intel_context *ce = &ctx->engine[engine->id]; + struct i915_vma *vma; uint32_t context_size; - struct intel_ringbuffer *ringbuf; + struct intel_ring *ring; int ret; WARN_ON(ce->state); @@ -2519,60 +2096,63 @@ static int execlists_context_deferred_alloc(struct i915_gem_context *ctx, return PTR_ERR(ctx_obj); } - ringbuf = intel_engine_create_ringbuffer(engine, ctx->ring_size); - if (IS_ERR(ringbuf)) { - ret = PTR_ERR(ringbuf); + vma = i915_vma_create(ctx_obj, &ctx->i915->ggtt.base, NULL); + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); + goto error_deref_obj; + } + + ring = intel_engine_create_ring(engine, ctx->ring_size); + if (IS_ERR(ring)) { + ret = PTR_ERR(ring); goto error_deref_obj; } - ret = populate_lr_context(ctx, ctx_obj, engine, ringbuf); + ret = populate_lr_context(ctx, ctx_obj, engine, ring); if (ret) { DRM_DEBUG_DRIVER("Failed to populate LRC: %d\n", ret); - goto error_ringbuf; + goto error_ring_free; } - ce->ringbuf = ringbuf; - ce->state = ctx_obj; + ce->ring = ring; + ce->state = vma; ce->initialised = engine->init_context == NULL; return 0; -error_ringbuf: - intel_ringbuffer_free(ringbuf); +error_ring_free: + intel_ring_free(ring); error_deref_obj: - drm_gem_object_unreference(&ctx_obj->base); - ce->ringbuf = NULL; - ce->state = NULL; + i915_gem_object_put(ctx_obj); return ret; } -void intel_lr_context_reset(struct drm_i915_private *dev_priv, - struct i915_gem_context *ctx) +void intel_lr_context_resume(struct drm_i915_private *dev_priv) { + struct i915_gem_context *ctx = dev_priv->kernel_context; struct intel_engine_cs *engine; for_each_engine(engine, dev_priv) { struct intel_context *ce = &ctx->engine[engine->id]; - struct drm_i915_gem_object *ctx_obj = ce->state; void *vaddr; uint32_t *reg_state; - if (!ctx_obj) + if (!ce->state) continue; - vaddr = i915_gem_object_pin_map(ctx_obj); + vaddr = i915_gem_object_pin_map(ce->state->obj, I915_MAP_WB); if (WARN_ON(IS_ERR(vaddr))) continue; reg_state = vaddr + LRC_STATE_PN * PAGE_SIZE; - ctx_obj->dirty = true; reg_state[CTX_RING_HEAD+1] = 0; reg_state[CTX_RING_TAIL+1] = 0; - i915_gem_object_unpin_map(ctx_obj); + ce->state->obj->dirty = true; + i915_gem_object_unpin_map(ce->state->obj); - ce->ringbuf->head = 0; - ce->ringbuf->tail = 0; + ce->ring->head = 0; + ce->ring->tail = 0; } } diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h index 2b8255c..4fed816 100644 --- a/drivers/gpu/drm/i915/intel_lrc.h +++ b/drivers/gpu/drm/i915/intel_lrc.h @@ -29,17 +29,17 @@ #define GEN8_LR_CONTEXT_ALIGN 4096 /* Execlists regs */ -#define RING_ELSP(ring) _MMIO((ring)->mmio_base + 0x230) -#define RING_EXECLIST_STATUS_LO(ring) _MMIO((ring)->mmio_base + 0x234) -#define RING_EXECLIST_STATUS_HI(ring) _MMIO((ring)->mmio_base + 0x234 + 4) -#define RING_CONTEXT_CONTROL(ring) _MMIO((ring)->mmio_base + 0x244) +#define RING_ELSP(engine) _MMIO((engine)->mmio_base + 0x230) +#define RING_EXECLIST_STATUS_LO(engine) _MMIO((engine)->mmio_base + 0x234) +#define RING_EXECLIST_STATUS_HI(engine) _MMIO((engine)->mmio_base + 0x234 + 4) +#define RING_CONTEXT_CONTROL(engine) _MMIO((engine)->mmio_base + 0x244) #define CTX_CTRL_INHIBIT_SYN_CTX_SWITCH (1 << 3) #define CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT (1 << 0) #define CTX_CTRL_RS_CTX_ENABLE (1 << 1) -#define RING_CONTEXT_STATUS_BUF_BASE(ring) _MMIO((ring)->mmio_base + 0x370) -#define RING_CONTEXT_STATUS_BUF_LO(ring, i) _MMIO((ring)->mmio_base + 0x370 + (i) * 8) -#define RING_CONTEXT_STATUS_BUF_HI(ring, i) _MMIO((ring)->mmio_base + 0x370 + (i) * 8 + 4) -#define RING_CONTEXT_STATUS_PTR(ring) _MMIO((ring)->mmio_base + 0x3a0) +#define RING_CONTEXT_STATUS_BUF_BASE(engine) _MMIO((engine)->mmio_base + 0x370) +#define RING_CONTEXT_STATUS_BUF_LO(engine, i) _MMIO((engine)->mmio_base + 0x370 + (i) * 8) +#define RING_CONTEXT_STATUS_BUF_HI(engine, i) _MMIO((engine)->mmio_base + 0x370 + (i) * 8 + 4) +#define RING_CONTEXT_STATUS_PTR(engine) _MMIO((engine)->mmio_base + 0x3a0) /* The docs specify that the write pointer wraps around after 5h, "After status * is written out to the last available status QW at offset 5h, this pointer @@ -67,35 +67,10 @@ int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request int intel_logical_ring_reserve_space(struct drm_i915_gem_request *request); void intel_logical_ring_stop(struct intel_engine_cs *engine); void intel_logical_ring_cleanup(struct intel_engine_cs *engine); -int intel_logical_rings_init(struct drm_device *dev); +int logical_render_ring_init(struct intel_engine_cs *engine); +int logical_xcs_ring_init(struct intel_engine_cs *engine); -int logical_ring_flush_all_caches(struct drm_i915_gem_request *req); -/** - * intel_logical_ring_advance() - advance the ringbuffer tail - * @ringbuf: Ringbuffer to advance. - * - * The tail is only updated in our logical ringbuffer struct. - */ -static inline void intel_logical_ring_advance(struct intel_ringbuffer *ringbuf) -{ - ringbuf->tail &= ringbuf->size - 1; -} -/** - * intel_logical_ring_emit() - write a DWORD to the ringbuffer. - * @ringbuf: Ringbuffer to write to. - * @data: DWORD to write. - */ -static inline void intel_logical_ring_emit(struct intel_ringbuffer *ringbuf, - u32 data) -{ - iowrite32(data, ringbuf->virtual_start + ringbuf->tail); - ringbuf->tail += 4; -} -static inline void intel_logical_ring_emit_reg(struct intel_ringbuffer *ringbuf, - i915_reg_t reg) -{ - intel_logical_ring_emit(ringbuf, i915_mmio_reg_offset(reg)); -} +int intel_engines_init(struct drm_device *dev); /* Logical Ring Contexts */ @@ -112,19 +87,13 @@ void intel_lr_context_unpin(struct i915_gem_context *ctx, struct drm_i915_private; -void intel_lr_context_reset(struct drm_i915_private *dev_priv, - struct i915_gem_context *ctx); +void intel_lr_context_resume(struct drm_i915_private *dev_priv); uint64_t intel_lr_context_descriptor(struct i915_gem_context *ctx, struct intel_engine_cs *engine); /* Execlists */ int intel_sanitize_enable_execlists(struct drm_i915_private *dev_priv, int enable_execlists); -struct i915_execbuffer_params; -int intel_execlists_submission(struct i915_execbuffer_params *params, - struct drm_i915_gem_execbuffer2 *args, - struct list_head *vmas); - -void intel_execlists_cancel_requests(struct intel_engine_cs *engine); +void intel_execlists_enable_submission(struct drm_i915_private *dev_priv); #endif /* _INTEL_LRC_H_ */ diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 4955047..e1d47d5 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -48,6 +48,20 @@ struct intel_lvds_connector { struct notifier_block lid_notifier; }; +struct intel_lvds_pps { + /* 100us units */ + int t1_t2; + int t3; + int t4; + int t5; + int tx; + + int divider; + + int port; + bool powerdown_on_reset; +}; + struct intel_lvds_encoder { struct intel_encoder base; @@ -55,6 +69,9 @@ struct intel_lvds_encoder { i915_reg_t reg; u32 a3_power; + struct intel_lvds_pps init_pps; + u32 init_lvds_val; + struct intel_lvds_connector *attached_connector; }; @@ -136,28 +153,108 @@ static void intel_lvds_get_config(struct intel_encoder *encoder, pipe_config->base.adjusted_mode.crtc_clock = pipe_config->port_clock; } -static void intel_pre_enable_lvds(struct intel_encoder *encoder) +static void intel_lvds_pps_get_hw_state(struct drm_i915_private *dev_priv, + struct intel_lvds_pps *pps) +{ + u32 val; + + pps->powerdown_on_reset = I915_READ(PP_CONTROL(0)) & PANEL_POWER_RESET; + + val = I915_READ(PP_ON_DELAYS(0)); + pps->port = (val & PANEL_PORT_SELECT_MASK) >> + PANEL_PORT_SELECT_SHIFT; + pps->t1_t2 = (val & PANEL_POWER_UP_DELAY_MASK) >> + PANEL_POWER_UP_DELAY_SHIFT; + pps->t5 = (val & PANEL_LIGHT_ON_DELAY_MASK) >> + PANEL_LIGHT_ON_DELAY_SHIFT; + + val = I915_READ(PP_OFF_DELAYS(0)); + pps->t3 = (val & PANEL_POWER_DOWN_DELAY_MASK) >> + PANEL_POWER_DOWN_DELAY_SHIFT; + pps->tx = (val & PANEL_LIGHT_OFF_DELAY_MASK) >> + PANEL_LIGHT_OFF_DELAY_SHIFT; + + val = I915_READ(PP_DIVISOR(0)); + pps->divider = (val & PP_REFERENCE_DIVIDER_MASK) >> + PP_REFERENCE_DIVIDER_SHIFT; + val = (val & PANEL_POWER_CYCLE_DELAY_MASK) >> + PANEL_POWER_CYCLE_DELAY_SHIFT; + /* + * Remove the BSpec specified +1 (100ms) offset that accounts for a + * too short power-cycle delay due to the asynchronous programming of + * the register. + */ + if (val) + val--; + /* Convert from 100ms to 100us units */ + pps->t4 = val * 1000; + + if (INTEL_INFO(dev_priv)->gen <= 4 && + pps->t1_t2 == 0 && pps->t5 == 0 && pps->t3 == 0 && pps->tx == 0) { + DRM_DEBUG_KMS("Panel power timings uninitialized, " + "setting defaults\n"); + /* Set T2 to 40ms and T5 to 200ms in 100 usec units */ + pps->t1_t2 = 40 * 10; + pps->t5 = 200 * 10; + /* Set T3 to 35ms and Tx to 200ms in 100 usec units */ + pps->t3 = 35 * 10; + pps->tx = 200 * 10; + } + + DRM_DEBUG_DRIVER("LVDS PPS:t1+t2 %d t3 %d t4 %d t5 %d tx %d " + "divider %d port %d powerdown_on_reset %d\n", + pps->t1_t2, pps->t3, pps->t4, pps->t5, pps->tx, + pps->divider, pps->port, pps->powerdown_on_reset); +} + +static void intel_lvds_pps_init_hw(struct drm_i915_private *dev_priv, + struct intel_lvds_pps *pps) +{ + u32 val; + + val = I915_READ(PP_CONTROL(0)); + WARN_ON((val & PANEL_UNLOCK_MASK) != PANEL_UNLOCK_REGS); + if (pps->powerdown_on_reset) + val |= PANEL_POWER_RESET; + I915_WRITE(PP_CONTROL(0), val); + + I915_WRITE(PP_ON_DELAYS(0), (pps->port << PANEL_PORT_SELECT_SHIFT) | + (pps->t1_t2 << PANEL_POWER_UP_DELAY_SHIFT) | + (pps->t5 << PANEL_LIGHT_ON_DELAY_SHIFT)); + I915_WRITE(PP_OFF_DELAYS(0), (pps->t3 << PANEL_POWER_DOWN_DELAY_SHIFT) | + (pps->tx << PANEL_LIGHT_OFF_DELAY_SHIFT)); + + val = pps->divider << PP_REFERENCE_DIVIDER_SHIFT; + val |= (DIV_ROUND_UP(pps->t4, 1000) + 1) << + PANEL_POWER_CYCLE_DELAY_SHIFT; + I915_WRITE(PP_DIVISOR(0), val); +} + +static void intel_pre_enable_lvds(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); - struct drm_device *dev = encoder->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); - const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode; + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct intel_crtc *crtc = to_intel_crtc(pipe_config->base.crtc); + const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; int pipe = crtc->pipe; u32 temp; - if (HAS_PCH_SPLIT(dev)) { + if (HAS_PCH_SPLIT(dev_priv)) { assert_fdi_rx_pll_disabled(dev_priv, pipe); assert_shared_dpll_disabled(dev_priv, - crtc->config->shared_dpll); + pipe_config->shared_dpll); } else { assert_pll_disabled(dev_priv, pipe); } - temp = I915_READ(lvds_encoder->reg); + intel_lvds_pps_init_hw(dev_priv, &lvds_encoder->init_pps); + + temp = lvds_encoder->init_lvds_val; temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (HAS_PCH_CPT(dev)) { + if (HAS_PCH_CPT(dev_priv)) { temp &= ~PORT_TRANS_SEL_MASK; temp |= PORT_TRANS_SEL_CPT(pipe); } else { @@ -170,7 +267,7 @@ static void intel_pre_enable_lvds(struct intel_encoder *encoder) /* set the corresponsding LVDS_BORDER bit */ temp &= ~LVDS_BORDER_ENABLE; - temp |= crtc->config->gmch_pfit.lvds_border_bits; + temp |= pipe_config->gmch_pfit.lvds_border_bits; /* Set the B0-B3 data pairs corresponding to whether we're going to * set the DPLLs for dual-channel mode or not. */ @@ -193,7 +290,7 @@ static void intel_pre_enable_lvds(struct intel_encoder *encoder) if (IS_GEN4(dev_priv)) { /* Bspec wording suggests that LVDS port dithering only exists * for 18bpp panels. */ - if (crtc->config->dither && crtc->config->pipe_bpp == 18) + if (pipe_config->dither && pipe_config->pipe_bpp == 18) temp |= LVDS_ENABLE_DITHER; else temp &= ~LVDS_ENABLE_DITHER; @@ -210,57 +307,45 @@ static void intel_pre_enable_lvds(struct intel_encoder *encoder) /** * Sets the power state for the panel. */ -static void intel_enable_lvds(struct intel_encoder *encoder) +static void intel_enable_lvds(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct drm_device *dev = encoder->base.dev; struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); struct intel_connector *intel_connector = &lvds_encoder->attached_connector->base; struct drm_i915_private *dev_priv = to_i915(dev); - i915_reg_t ctl_reg, stat_reg; - - if (HAS_PCH_SPLIT(dev)) { - ctl_reg = PCH_PP_CONTROL; - stat_reg = PCH_PP_STATUS; - } else { - ctl_reg = PP_CONTROL; - stat_reg = PP_STATUS; - } I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) | LVDS_PORT_EN); - I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); + I915_WRITE(PP_CONTROL(0), I915_READ(PP_CONTROL(0)) | PANEL_POWER_ON); POSTING_READ(lvds_encoder->reg); - if (intel_wait_for_register(dev_priv, stat_reg, PP_ON, PP_ON, 1000)) + if (intel_wait_for_register(dev_priv, PP_STATUS(0), PP_ON, PP_ON, 1000)) DRM_ERROR("timed out waiting for panel to power on\n"); intel_panel_enable_backlight(intel_connector); } -static void intel_disable_lvds(struct intel_encoder *encoder) +static void intel_disable_lvds(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { - struct drm_device *dev = encoder->base.dev; struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); - struct drm_i915_private *dev_priv = to_i915(dev); - i915_reg_t ctl_reg, stat_reg; - - if (HAS_PCH_SPLIT(dev)) { - ctl_reg = PCH_PP_CONTROL; - stat_reg = PCH_PP_STATUS; - } else { - ctl_reg = PP_CONTROL; - stat_reg = PP_STATUS; - } + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); - I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON); - if (intel_wait_for_register(dev_priv, stat_reg, PP_ON, 0, 1000)) + I915_WRITE(PP_CONTROL(0), I915_READ(PP_CONTROL(0)) & ~PANEL_POWER_ON); + if (intel_wait_for_register(dev_priv, PP_STATUS(0), PP_ON, 0, 1000)) DRM_ERROR("timed out waiting for panel to power off\n"); I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) & ~LVDS_PORT_EN); POSTING_READ(lvds_encoder->reg); } -static void gmch_disable_lvds(struct intel_encoder *encoder) +static void gmch_disable_lvds(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) + { struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); struct intel_connector *intel_connector = @@ -268,10 +353,12 @@ static void gmch_disable_lvds(struct intel_encoder *encoder) intel_panel_disable_backlight(intel_connector); - intel_disable_lvds(encoder); + intel_disable_lvds(encoder, old_crtc_state, old_conn_state); } -static void pch_disable_lvds(struct intel_encoder *encoder) +static void pch_disable_lvds(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); struct intel_connector *intel_connector = @@ -280,9 +367,11 @@ static void pch_disable_lvds(struct intel_encoder *encoder) intel_panel_disable_backlight(intel_connector); } -static void pch_post_disable_lvds(struct intel_encoder *encoder) +static void pch_post_disable_lvds(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { - intel_disable_lvds(encoder); + intel_disable_lvds(encoder, old_crtc_state, old_conn_state); } static enum drm_mode_status @@ -304,7 +393,8 @@ intel_lvds_mode_valid(struct drm_connector *connector, } static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, - struct intel_crtc_state *pipe_config) + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct drm_device *dev = intel_encoder->base.dev; struct intel_lvds_encoder *lvds_encoder = @@ -900,17 +990,6 @@ void intel_lvds_init(struct drm_device *dev) int pipe; u8 pin; - /* - * Unlock registers and just leave them unlocked. Do this before - * checking quirk lists to avoid bogus WARNINGs. - */ - if (HAS_PCH_SPLIT(dev)) { - I915_WRITE(PCH_PP_CONTROL, - I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); - } else if (INTEL_INFO(dev_priv)->gen < 5) { - I915_WRITE(PP_CONTROL, - I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); - } if (!intel_lvds_supported(dev)) return; @@ -943,18 +1022,6 @@ void intel_lvds_init(struct drm_device *dev) DRM_DEBUG_KMS("LVDS is not present in VBT, but enabled anyway\n"); } - /* Set the Panel Power On/Off timings if uninitialized. */ - if (INTEL_INFO(dev_priv)->gen < 5 && - I915_READ(PP_ON_DELAYS) == 0 && I915_READ(PP_OFF_DELAYS) == 0) { - /* Set T2 to 40ms and T5 to 200ms */ - I915_WRITE(PP_ON_DELAYS, 0x019007d0); - - /* Set T3 to 35ms and Tx to 200ms */ - I915_WRITE(PP_OFF_DELAYS, 0x015e07d0); - - DRM_DEBUG_KMS("Panel power timings uninitialized, setting defaults\n"); - } - lvds_encoder = kzalloc(sizeof(*lvds_encoder), GFP_KERNEL); if (!lvds_encoder) return; @@ -1020,6 +1087,10 @@ void intel_lvds_init(struct drm_device *dev) dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_ASPECT); intel_connector->panel.fitting_mode = DRM_MODE_SCALE_ASPECT; + + intel_lvds_pps_get_hw_state(dev_priv, &lvds_encoder->init_pps); + lvds_encoder->init_lvds_val = lvds; + /* * LVDS discovery: * 1) check for EDID on DDC @@ -1054,17 +1125,6 @@ void intel_lvds_init(struct drm_device *dev) } lvds_connector->base.edid = edid; - if (IS_ERR_OR_NULL(edid)) { - /* Didn't get an EDID, so - * Set wide sync ranges so we get all modes - * handed to valid_mode for checking - */ - connector->display_info.min_vfreq = 0; - connector->display_info.max_vfreq = 200; - connector->display_info.min_hfreq = 0; - connector->display_info.max_hfreq = 200; - } - list_for_each_entry(scan, &connector->probed_modes, head) { if (scan->type & DRM_MODE_TYPE_PREFERRED) { DRM_DEBUG_KMS("using preferred mode from EDID: "); diff --git a/drivers/gpu/drm/i915/intel_mocs.c b/drivers/gpu/drm/i915/intel_mocs.c index 927825f..80bb924 100644 --- a/drivers/gpu/drm/i915/intel_mocs.c +++ b/drivers/gpu/drm/i915/intel_mocs.c @@ -97,7 +97,8 @@ struct drm_i915_mocs_table { * end. */ static const struct drm_i915_mocs_entry skylake_mocs_table[] = { - { /* 0x00000009 */ + [I915_MOCS_UNCACHED] = { + /* 0x00000009 */ .control_value = LE_CACHEABILITY(LE_UC) | LE_TGT_CACHE(LE_TC_LLC_ELLC) | LE_LRUM(0) | LE_AOM(0) | LE_RSC(0) | LE_SCC(0) | @@ -106,7 +107,7 @@ static const struct drm_i915_mocs_entry skylake_mocs_table[] = { /* 0x0010 */ .l3cc_value = L3_ESC(0) | L3_SCC(0) | L3_CACHEABILITY(L3_UC), }, - { + [I915_MOCS_PTE] = { /* 0x00000038 */ .control_value = LE_CACHEABILITY(LE_PAGETABLE) | LE_TGT_CACHE(LE_TC_LLC_ELLC) | @@ -115,7 +116,7 @@ static const struct drm_i915_mocs_entry skylake_mocs_table[] = { /* 0x0030 */ .l3cc_value = L3_ESC(0) | L3_SCC(0) | L3_CACHEABILITY(L3_WB), }, - { + [I915_MOCS_CACHED] = { /* 0x0000003b */ .control_value = LE_CACHEABILITY(LE_WB) | LE_TGT_CACHE(LE_TC_LLC_ELLC) | @@ -128,7 +129,7 @@ static const struct drm_i915_mocs_entry skylake_mocs_table[] = { /* NOTE: the LE_TGT_CACHE is not used on Broxton */ static const struct drm_i915_mocs_entry broxton_mocs_table[] = { - { + [I915_MOCS_UNCACHED] = { /* 0x00000009 */ .control_value = LE_CACHEABILITY(LE_UC) | LE_TGT_CACHE(LE_TC_LLC_ELLC) | @@ -138,7 +139,7 @@ static const struct drm_i915_mocs_entry broxton_mocs_table[] = { /* 0x0010 */ .l3cc_value = L3_ESC(0) | L3_SCC(0) | L3_CACHEABILITY(L3_UC), }, - { + [I915_MOCS_PTE] = { /* 0x00000038 */ .control_value = LE_CACHEABILITY(LE_PAGETABLE) | LE_TGT_CACHE(LE_TC_LLC_ELLC) | @@ -148,7 +149,7 @@ static const struct drm_i915_mocs_entry broxton_mocs_table[] = { /* 0x0030 */ .l3cc_value = L3_ESC(0) | L3_SCC(0) | L3_CACHEABILITY(L3_WB), }, - { + [I915_MOCS_CACHED] = { /* 0x00000039 */ .control_value = LE_CACHEABILITY(LE_UC) | LE_TGT_CACHE(LE_TC_LLC_ELLC) | @@ -203,9 +204,9 @@ static bool get_mocs_settings(struct drm_i915_private *dev_priv, return result; } -static i915_reg_t mocs_register(enum intel_engine_id ring, int index) +static i915_reg_t mocs_register(enum intel_engine_id engine_id, int index) { - switch (ring) { + switch (engine_id) { case RCS: return GEN9_GFX_MOCS(index); case VCS: @@ -217,7 +218,7 @@ static i915_reg_t mocs_register(enum intel_engine_id ring, int index) case VCS2: return GEN9_MFX1_MOCS(index); default: - MISSING_CASE(ring); + MISSING_CASE(engine_id); return INVALID_MMIO_REG; } } @@ -275,7 +276,7 @@ int intel_mocs_init_engine(struct intel_engine_cs *engine) static int emit_mocs_control_table(struct drm_i915_gem_request *req, const struct drm_i915_mocs_table *table) { - struct intel_ringbuffer *ringbuf = req->ringbuf; + struct intel_ring *ring = req->ring; enum intel_engine_id engine = req->engine->id; unsigned int index; int ret; @@ -287,14 +288,11 @@ static int emit_mocs_control_table(struct drm_i915_gem_request *req, if (ret) return ret; - intel_logical_ring_emit(ringbuf, - MI_LOAD_REGISTER_IMM(GEN9_NUM_MOCS_ENTRIES)); + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(GEN9_NUM_MOCS_ENTRIES)); for (index = 0; index < table->size; index++) { - intel_logical_ring_emit_reg(ringbuf, - mocs_register(engine, index)); - intel_logical_ring_emit(ringbuf, - table->table[index].control_value); + intel_ring_emit_reg(ring, mocs_register(engine, index)); + intel_ring_emit(ring, table->table[index].control_value); } /* @@ -306,14 +304,12 @@ static int emit_mocs_control_table(struct drm_i915_gem_request *req, * that value to all the used entries. */ for (; index < GEN9_NUM_MOCS_ENTRIES; index++) { - intel_logical_ring_emit_reg(ringbuf, - mocs_register(engine, index)); - intel_logical_ring_emit(ringbuf, - table->table[0].control_value); + intel_ring_emit_reg(ring, mocs_register(engine, index)); + intel_ring_emit(ring, table->table[0].control_value); } - intel_logical_ring_emit(ringbuf, MI_NOOP); - intel_logical_ring_advance(ringbuf); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); return 0; } @@ -340,7 +336,7 @@ static inline u32 l3cc_combine(const struct drm_i915_mocs_table *table, static int emit_mocs_l3cc_table(struct drm_i915_gem_request *req, const struct drm_i915_mocs_table *table) { - struct intel_ringbuffer *ringbuf = req->ringbuf; + struct intel_ring *ring = req->ring; unsigned int i; int ret; @@ -351,19 +347,18 @@ static int emit_mocs_l3cc_table(struct drm_i915_gem_request *req, if (ret) return ret; - intel_logical_ring_emit(ringbuf, + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(GEN9_NUM_MOCS_ENTRIES / 2)); for (i = 0; i < table->size/2; i++) { - intel_logical_ring_emit_reg(ringbuf, GEN9_LNCFCMOCS(i)); - intel_logical_ring_emit(ringbuf, - l3cc_combine(table, 2*i, 2*i+1)); + intel_ring_emit_reg(ring, GEN9_LNCFCMOCS(i)); + intel_ring_emit(ring, l3cc_combine(table, 2*i, 2*i+1)); } if (table->size & 0x01) { /* Odd table size - 1 left over */ - intel_logical_ring_emit_reg(ringbuf, GEN9_LNCFCMOCS(i)); - intel_logical_ring_emit(ringbuf, l3cc_combine(table, 2*i, 0)); + intel_ring_emit_reg(ring, GEN9_LNCFCMOCS(i)); + intel_ring_emit(ring, l3cc_combine(table, 2*i, 0)); i++; } @@ -373,12 +368,12 @@ static int emit_mocs_l3cc_table(struct drm_i915_gem_request *req, * they are reserved by the hardware. */ for (; i < GEN9_NUM_MOCS_ENTRIES / 2; i++) { - intel_logical_ring_emit_reg(ringbuf, GEN9_LNCFCMOCS(i)); - intel_logical_ring_emit(ringbuf, l3cc_combine(table, 0, 0)); + intel_ring_emit_reg(ring, GEN9_LNCFCMOCS(i)); + intel_ring_emit(ring, l3cc_combine(table, 0, 0)); } - intel_logical_ring_emit(ringbuf, MI_NOOP); - intel_logical_ring_advance(ringbuf); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); return 0; } diff --git a/drivers/gpu/drm/i915/intel_mocs.h b/drivers/gpu/drm/i915/intel_mocs.h index 4640299..a8bd9f7 100644 --- a/drivers/gpu/drm/i915/intel_mocs.h +++ b/drivers/gpu/drm/i915/intel_mocs.h @@ -54,6 +54,6 @@ int intel_rcs_context_init_mocs(struct drm_i915_gem_request *req); void intel_mocs_init_l3cc_table(struct drm_device *dev); -int intel_mocs_init_engine(struct intel_engine_cs *ring); +int intel_mocs_init_engine(struct intel_engine_cs *engine); #endif diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c index f2584d0..951e834 100644 --- a/drivers/gpu/drm/i915/intel_modes.c +++ b/drivers/gpu/drm/i915/intel_modes.c @@ -25,7 +25,6 @@ #include <linux/slab.h> #include <linux/i2c.h> -#include <linux/fb.h> #include <drm/drm_edid.h> #include <drm/drmP.h> #include "intel_drv.h" diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index adca262..7acbbbf 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -1047,6 +1047,23 @@ err_out: return err; } +static int intel_use_opregion_panel_type_callback(const struct dmi_system_id *id) +{ + DRM_INFO("Using panel type from OpRegion on %s\n", id->ident); + return 1; +} + +static const struct dmi_system_id intel_use_opregion_panel_type[] = { + { + .callback = intel_use_opregion_panel_type_callback, + .ident = "Conrac GmbH IX45GM2", + .matches = {DMI_MATCH(DMI_SYS_VENDOR, "Conrac GmbH"), + DMI_MATCH(DMI_PRODUCT_NAME, "IX45GM2"), + }, + }, + { } +}; + int intel_opregion_get_panel_type(struct drm_i915_private *dev_priv) { @@ -1073,6 +1090,16 @@ intel_opregion_get_panel_type(struct drm_i915_private *dev_priv) } /* + * So far we know that some machined must use it, others must not use it. + * There doesn't seem to be any way to determine which way to go, except + * via a quirk list :( + */ + if (!dmi_check_system(intel_use_opregion_panel_type)) { + DRM_DEBUG_KMS("Ignoring OpRegion panel type (%d)\n", ret - 1); + return -ENODEV; + } + + /* * FIXME On Dell XPS 13 9350 the OpRegion panel type (0) gives us * low vswing for eDP, whereas the VBT panel type (2) gives us normal * vswing instead. Low vswing results in some display flickers, so diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 3212d88..a24bc8c 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -30,6 +30,7 @@ #include "i915_drv.h" #include "i915_reg.h" #include "intel_drv.h" +#include "intel_frontbuffer.h" /* Limits for overlay size. According to intel doc, the real limits are: * Y width: 4095, UV width (planar): 2047, Y height: 2047, @@ -170,8 +171,8 @@ struct overlay_registers { struct intel_overlay { struct drm_i915_private *i915; struct intel_crtc *crtc; - struct drm_i915_gem_object *vid_bo; - struct drm_i915_gem_object *old_vid_bo; + struct i915_vma *vma; + struct i915_vma *old_vma; bool active; bool pfit_active; u32 pfit_vscale_ratio; /* shifted-point number, (1<<12) == 1.0 */ @@ -183,8 +184,7 @@ struct intel_overlay { u32 flip_addr; struct drm_i915_gem_object *reg_bo; /* flip handling */ - struct drm_i915_gem_request *last_flip_req; - void (*flip_tail)(struct intel_overlay *); + struct i915_gem_active last_flip; }; static struct overlay_registers __iomem * @@ -196,7 +196,7 @@ intel_overlay_map_regs(struct intel_overlay *overlay) if (OVERLAY_NEEDS_PHYSICAL(dev_priv)) regs = (struct overlay_registers __iomem *)overlay->reg_bo->phys_handle->vaddr; else - regs = io_mapping_map_wc(dev_priv->ggtt.mappable, + regs = io_mapping_map_wc(&dev_priv->ggtt.mappable, overlay->flip_addr, PAGE_SIZE); @@ -210,37 +210,46 @@ static void intel_overlay_unmap_regs(struct intel_overlay *overlay, io_mapping_unmap(regs); } -static int intel_overlay_do_wait_request(struct intel_overlay *overlay, +static void intel_overlay_submit_request(struct intel_overlay *overlay, struct drm_i915_gem_request *req, - void (*tail)(struct intel_overlay *)) + i915_gem_retire_fn retire) { - int ret; - - WARN_ON(overlay->last_flip_req); - i915_gem_request_assign(&overlay->last_flip_req, req); + GEM_BUG_ON(i915_gem_active_peek(&overlay->last_flip, + &overlay->i915->drm.struct_mutex)); + overlay->last_flip.retire = retire; + i915_gem_active_set(&overlay->last_flip, req); i915_add_request(req); +} - overlay->flip_tail = tail; - ret = i915_wait_request(overlay->last_flip_req); - if (ret) - return ret; +static int intel_overlay_do_wait_request(struct intel_overlay *overlay, + struct drm_i915_gem_request *req, + i915_gem_retire_fn retire) +{ + intel_overlay_submit_request(overlay, req, retire); + return i915_gem_active_retire(&overlay->last_flip, + &overlay->i915->drm.struct_mutex); +} - i915_gem_request_assign(&overlay->last_flip_req, NULL); - return 0; +static struct drm_i915_gem_request *alloc_request(struct intel_overlay *overlay) +{ + struct drm_i915_private *dev_priv = overlay->i915; + struct intel_engine_cs *engine = &dev_priv->engine[RCS]; + + return i915_gem_request_alloc(engine, dev_priv->kernel_context); } /* overlay needs to be disable in OCMD reg */ static int intel_overlay_on(struct intel_overlay *overlay) { struct drm_i915_private *dev_priv = overlay->i915; - struct intel_engine_cs *engine = &dev_priv->engine[RCS]; struct drm_i915_gem_request *req; + struct intel_ring *ring; int ret; WARN_ON(overlay->active); WARN_ON(IS_I830(dev_priv) && !(dev_priv->quirks & QUIRK_PIPEA_FORCE)); - req = i915_gem_request_alloc(engine, NULL); + req = alloc_request(overlay); if (IS_ERR(req)) return PTR_ERR(req); @@ -252,11 +261,12 @@ static int intel_overlay_on(struct intel_overlay *overlay) overlay->active = true; - intel_ring_emit(engine, MI_OVERLAY_FLIP | MI_OVERLAY_ON); - intel_ring_emit(engine, overlay->flip_addr | OFC_UPDATE); - intel_ring_emit(engine, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); - intel_ring_emit(engine, MI_NOOP); - intel_ring_advance(engine); + ring = req->ring; + intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_ON); + intel_ring_emit(ring, overlay->flip_addr | OFC_UPDATE); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); return intel_overlay_do_wait_request(overlay, req, NULL); } @@ -266,8 +276,8 @@ static int intel_overlay_continue(struct intel_overlay *overlay, bool load_polyphase_filter) { struct drm_i915_private *dev_priv = overlay->i915; - struct intel_engine_cs *engine = &dev_priv->engine[RCS]; struct drm_i915_gem_request *req; + struct intel_ring *ring; u32 flip_addr = overlay->flip_addr; u32 tmp; int ret; @@ -282,7 +292,7 @@ static int intel_overlay_continue(struct intel_overlay *overlay, if (tmp & (1 << 17)) DRM_DEBUG("overlay underrun, DOVSTA: %x\n", tmp); - req = i915_gem_request_alloc(engine, NULL); + req = alloc_request(overlay); if (IS_ERR(req)) return PTR_ERR(req); @@ -292,38 +302,48 @@ static int intel_overlay_continue(struct intel_overlay *overlay, return ret; } - intel_ring_emit(engine, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); - intel_ring_emit(engine, flip_addr); - intel_ring_advance(engine); + ring = req->ring; + intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); + intel_ring_emit(ring, flip_addr); + intel_ring_advance(ring); - WARN_ON(overlay->last_flip_req); - i915_gem_request_assign(&overlay->last_flip_req, req); - i915_add_request(req); + intel_overlay_submit_request(overlay, req, NULL); return 0; } -static void intel_overlay_release_old_vid_tail(struct intel_overlay *overlay) +static void intel_overlay_release_old_vid_tail(struct i915_gem_active *active, + struct drm_i915_gem_request *req) { - struct drm_i915_gem_object *obj = overlay->old_vid_bo; + struct intel_overlay *overlay = + container_of(active, typeof(*overlay), last_flip); + struct i915_vma *vma; - i915_gem_object_ggtt_unpin(obj); - drm_gem_object_unreference(&obj->base); + vma = fetch_and_zero(&overlay->old_vma); + if (WARN_ON(!vma)) + return; - overlay->old_vid_bo = NULL; + i915_gem_track_fb(vma->obj, NULL, + INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe)); + + i915_gem_object_unpin_from_display_plane(vma); + i915_vma_put(vma); } -static void intel_overlay_off_tail(struct intel_overlay *overlay) +static void intel_overlay_off_tail(struct i915_gem_active *active, + struct drm_i915_gem_request *req) { - struct drm_i915_gem_object *obj = overlay->vid_bo; + struct intel_overlay *overlay = + container_of(active, typeof(*overlay), last_flip); + struct i915_vma *vma; /* never have the overlay hw on without showing a frame */ - if (WARN_ON(!obj)) + vma = fetch_and_zero(&overlay->vma); + if (WARN_ON(!vma)) return; - i915_gem_object_ggtt_unpin(obj); - drm_gem_object_unreference(&obj->base); - overlay->vid_bo = NULL; + i915_gem_object_unpin_from_display_plane(vma); + i915_vma_put(vma); overlay->crtc->overlay = NULL; overlay->crtc = NULL; @@ -334,8 +354,8 @@ static void intel_overlay_off_tail(struct intel_overlay *overlay) static int intel_overlay_off(struct intel_overlay *overlay) { struct drm_i915_private *dev_priv = overlay->i915; - struct intel_engine_cs *engine = &dev_priv->engine[RCS]; struct drm_i915_gem_request *req; + struct intel_ring *ring; u32 flip_addr = overlay->flip_addr; int ret; @@ -347,7 +367,7 @@ static int intel_overlay_off(struct intel_overlay *overlay) * of the hw. Do it in both cases */ flip_addr |= OFC_UPDATE; - req = i915_gem_request_alloc(engine, NULL); + req = alloc_request(overlay); if (IS_ERR(req)) return PTR_ERR(req); @@ -357,46 +377,36 @@ static int intel_overlay_off(struct intel_overlay *overlay) return ret; } + ring = req->ring; /* wait for overlay to go idle */ - intel_ring_emit(engine, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); - intel_ring_emit(engine, flip_addr); - intel_ring_emit(engine, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); + intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); + intel_ring_emit(ring, flip_addr); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); /* turn overlay off */ if (IS_I830(dev_priv)) { /* Workaround: Don't disable the overlay fully, since otherwise * it dies on the next OVERLAY_ON cmd. */ - intel_ring_emit(engine, MI_NOOP); - intel_ring_emit(engine, MI_NOOP); - intel_ring_emit(engine, MI_NOOP); + intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, MI_NOOP); } else { - intel_ring_emit(engine, MI_OVERLAY_FLIP | MI_OVERLAY_OFF); - intel_ring_emit(engine, flip_addr); - intel_ring_emit(engine, + intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_OFF); + intel_ring_emit(ring, flip_addr); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); } - intel_ring_advance(engine); + intel_ring_advance(ring); - return intel_overlay_do_wait_request(overlay, req, intel_overlay_off_tail); + return intel_overlay_do_wait_request(overlay, req, + intel_overlay_off_tail); } /* recover from an interruption due to a signal * We have to be careful not to repeat work forever an make forward progess. */ static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay) { - int ret; - - if (overlay->last_flip_req == NULL) - return 0; - - ret = i915_wait_request(overlay->last_flip_req); - if (ret) - return ret; - - if (overlay->flip_tail) - overlay->flip_tail(overlay); - - i915_gem_request_assign(&overlay->last_flip_req, NULL); - return 0; + return i915_gem_active_retire(&overlay->last_flip, + &overlay->i915->drm.struct_mutex); } /* Wait for pending overlay flip and release old frame. @@ -406,7 +416,6 @@ static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay) static int intel_overlay_release_old_vid(struct intel_overlay *overlay) { struct drm_i915_private *dev_priv = overlay->i915; - struct intel_engine_cs *engine = &dev_priv->engine[RCS]; int ret; lockdep_assert_held(&dev_priv->drm.struct_mutex); @@ -414,14 +423,15 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay) /* Only wait if there is actually an old frame to release to * guarantee forward progress. */ - if (!overlay->old_vid_bo) + if (!overlay->old_vma) return 0; if (I915_READ(ISR) & I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT) { /* synchronous slowpath */ struct drm_i915_gem_request *req; + struct intel_ring *ring; - req = i915_gem_request_alloc(engine, NULL); + req = alloc_request(overlay); if (IS_ERR(req)) return PTR_ERR(req); @@ -431,22 +441,19 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay) return ret; } - intel_ring_emit(engine, + ring = req->ring; + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); - intel_ring_emit(engine, MI_NOOP); - intel_ring_advance(engine); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); ret = intel_overlay_do_wait_request(overlay, req, intel_overlay_release_old_vid_tail); if (ret) return ret; - } + } else + intel_overlay_release_old_vid_tail(&overlay->last_flip, NULL); - intel_overlay_release_old_vid_tail(overlay); - - - i915_gem_track_fb(overlay->old_vid_bo, NULL, - INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe)); return 0; } @@ -459,7 +466,6 @@ void intel_overlay_reset(struct drm_i915_private *dev_priv) intel_overlay_release_old_vid(overlay); - overlay->last_flip_req = NULL; overlay->old_xscale = 0; overlay->old_yscale = 0; overlay->crtc = NULL; @@ -740,6 +746,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, struct drm_i915_private *dev_priv = overlay->i915; u32 swidth, swidthsw, sheight, ostride; enum pipe pipe = overlay->crtc->pipe; + struct i915_vma *vma; lockdep_assert_held(&dev_priv->drm.struct_mutex); WARN_ON(!drm_modeset_is_locked(&dev_priv->drm.mode_config.connection_mutex)); @@ -748,12 +755,12 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, if (ret != 0) return ret; - ret = i915_gem_object_pin_to_display_plane(new_bo, 0, + vma = i915_gem_object_pin_to_display_plane(new_bo, 0, &i915_ggtt_view_normal); - if (ret != 0) - return ret; + if (IS_ERR(vma)) + return PTR_ERR(vma); - ret = i915_gem_object_put_fence(new_bo); + ret = i915_vma_put_fence(vma); if (ret) goto out_unpin; @@ -794,7 +801,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, swidth = params->src_w; swidthsw = calc_swidthsw(dev_priv, params->offset_Y, tmp_width); sheight = params->src_h; - iowrite32(i915_gem_obj_ggtt_offset(new_bo) + params->offset_Y, ®s->OBUF_0Y); + iowrite32(i915_ggtt_offset(vma) + params->offset_Y, ®s->OBUF_0Y); ostride = params->stride_Y; if (params->format & I915_OVERLAY_YUV_PLANAR) { @@ -808,8 +815,10 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, params->src_w/uv_hscale); swidthsw |= max_t(u32, tmp_U, tmp_V) << 16; sheight |= (params->src_h/uv_vscale) << 16; - iowrite32(i915_gem_obj_ggtt_offset(new_bo) + params->offset_U, ®s->OBUF_0U); - iowrite32(i915_gem_obj_ggtt_offset(new_bo) + params->offset_V, ®s->OBUF_0V); + iowrite32(i915_ggtt_offset(vma) + params->offset_U, + ®s->OBUF_0U); + iowrite32(i915_ggtt_offset(vma) + params->offset_V, + ®s->OBUF_0V); ostride |= params->stride_UV << 16; } @@ -830,19 +839,18 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, if (ret) goto out_unpin; - i915_gem_track_fb(overlay->vid_bo, new_bo, + i915_gem_track_fb(overlay->vma->obj, new_bo, INTEL_FRONTBUFFER_OVERLAY(pipe)); - overlay->old_vid_bo = overlay->vid_bo; - overlay->vid_bo = new_bo; + overlay->old_vma = overlay->vma; + overlay->vma = vma; - intel_frontbuffer_flip(&dev_priv->drm, - INTEL_FRONTBUFFER_OVERLAY(pipe)); + intel_frontbuffer_flip(dev_priv, INTEL_FRONTBUFFER_OVERLAY(pipe)); return 0; out_unpin: - i915_gem_object_ggtt_unpin(new_bo); + i915_gem_object_unpin_from_display_plane(vma); return ret; } @@ -870,12 +878,7 @@ int intel_overlay_switch_off(struct intel_overlay *overlay) iowrite32(0, ®s->OCMD); intel_overlay_unmap_regs(overlay, regs); - ret = intel_overlay_off(overlay); - if (ret != 0) - return ret; - - intel_overlay_off_tail(overlay); - return 0; + return intel_overlay_off(overlay); } static int check_overlay_possible_on_crtc(struct intel_overlay *overlay, @@ -1122,9 +1125,8 @@ int intel_overlay_put_image_ioctl(struct drm_device *dev, void *data, } crtc = to_intel_crtc(drmmode_crtc); - new_bo = to_intel_bo(drm_gem_object_lookup(file_priv, - put_image_rec->bo_handle)); - if (&new_bo->base == NULL) { + new_bo = i915_gem_object_lookup(file_priv, put_image_rec->bo_handle); + if (!new_bo) { ret = -ENOENT; goto out_free; } @@ -1132,7 +1134,7 @@ int intel_overlay_put_image_ioctl(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); mutex_lock(&dev->struct_mutex); - if (new_bo->tiling_mode) { + if (i915_gem_object_is_tiled(new_bo)) { DRM_DEBUG_KMS("buffer used for overlay image can not be tiled\n"); ret = -EINVAL; goto out_unlock; @@ -1220,7 +1222,7 @@ int intel_overlay_put_image_ioctl(struct drm_device *dev, void *data, out_unlock: mutex_unlock(&dev->struct_mutex); drm_modeset_unlock_all(dev); - drm_gem_object_unreference_unlocked(&new_bo->base); + i915_gem_object_put_unlocked(new_bo); out_free: kfree(params); @@ -1371,6 +1373,7 @@ void intel_setup_overlay(struct drm_i915_private *dev_priv) struct intel_overlay *overlay; struct drm_i915_gem_object *reg_bo; struct overlay_registers __iomem *regs; + struct i915_vma *vma = NULL; int ret; if (!HAS_OVERLAY(dev_priv)) @@ -1404,12 +1407,14 @@ void intel_setup_overlay(struct drm_i915_private *dev_priv) } overlay->flip_addr = reg_bo->phys_handle->busaddr; } else { - ret = i915_gem_obj_ggtt_pin(reg_bo, PAGE_SIZE, PIN_MAPPABLE); - if (ret) { + vma = i915_gem_object_ggtt_pin(reg_bo, NULL, + 0, PAGE_SIZE, PIN_MAPPABLE); + if (IS_ERR(vma)) { DRM_ERROR("failed to pin overlay register bo\n"); + ret = PTR_ERR(vma); goto out_free_bo; } - overlay->flip_addr = i915_gem_obj_ggtt_offset(reg_bo); + overlay->flip_addr = i915_ggtt_offset(vma); ret = i915_gem_object_set_to_gtt_domain(reg_bo, true); if (ret) { @@ -1441,10 +1446,10 @@ void intel_setup_overlay(struct drm_i915_private *dev_priv) return; out_unpin_bo: - if (!OVERLAY_NEEDS_PHYSICAL(dev_priv)) - i915_gem_object_ggtt_unpin(reg_bo); + if (vma) + i915_vma_unpin(vma); out_free_bo: - drm_gem_object_unreference(®_bo->base); + i915_gem_object_put(reg_bo); out_free: mutex_unlock(&dev_priv->drm.struct_mutex); kfree(overlay); @@ -1461,7 +1466,7 @@ void intel_cleanup_overlay(struct drm_i915_private *dev_priv) * hardware should be off already */ WARN_ON(dev_priv->overlay->active); - drm_gem_object_unreference_unlocked(&dev_priv->overlay->reg_bo->base); + i915_gem_object_put_unlocked(dev_priv->overlay->reg_bo); kfree(dev_priv->overlay); } @@ -1484,7 +1489,7 @@ intel_overlay_map_regs_atomic(struct intel_overlay *overlay) regs = (struct overlay_registers __iomem *) overlay->reg_bo->phys_handle->vaddr; else - regs = io_mapping_map_atomic_wc(dev_priv->ggtt.mappable, + regs = io_mapping_map_atomic_wc(&dev_priv->ggtt.mappable, overlay->flip_addr); return regs; diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 96c65d7..c10e9b0 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -1430,10 +1430,11 @@ static int lpt_setup_backlight(struct intel_connector *connector, enum pipe unus panel->backlight.min = get_backlight_min_vbt(connector); val = lpt_get_backlight(connector); - panel->backlight.level = intel_panel_compute_brightness(connector, val); + val = intel_panel_compute_brightness(connector, val); + panel->backlight.level = clamp(val, panel->backlight.min, + panel->backlight.max); - panel->backlight.enabled = (pch_ctl1 & BLM_PCH_PWM_ENABLE) && - panel->backlight.level != 0; + panel->backlight.enabled = pch_ctl1 & BLM_PCH_PWM_ENABLE; return 0; } @@ -1459,11 +1460,13 @@ static int pch_setup_backlight(struct intel_connector *connector, enum pipe unus panel->backlight.min = get_backlight_min_vbt(connector); val = pch_get_backlight(connector); - panel->backlight.level = intel_panel_compute_brightness(connector, val); + val = intel_panel_compute_brightness(connector, val); + panel->backlight.level = clamp(val, panel->backlight.min, + panel->backlight.max); cpu_ctl2 = I915_READ(BLC_PWM_CPU_CTL2); panel->backlight.enabled = (cpu_ctl2 & BLM_PWM_ENABLE) && - (pch_ctl1 & BLM_PCH_PWM_ENABLE) && panel->backlight.level != 0; + (pch_ctl1 & BLM_PCH_PWM_ENABLE); return 0; } @@ -1498,9 +1501,11 @@ static int i9xx_setup_backlight(struct intel_connector *connector, enum pipe unu panel->backlight.min = get_backlight_min_vbt(connector); val = i9xx_get_backlight(connector); - panel->backlight.level = intel_panel_compute_brightness(connector, val); + val = intel_panel_compute_brightness(connector, val); + panel->backlight.level = clamp(val, panel->backlight.min, + panel->backlight.max); - panel->backlight.enabled = panel->backlight.level != 0; + panel->backlight.enabled = val != 0; return 0; } @@ -1530,10 +1535,11 @@ static int i965_setup_backlight(struct intel_connector *connector, enum pipe unu panel->backlight.min = get_backlight_min_vbt(connector); val = i9xx_get_backlight(connector); - panel->backlight.level = intel_panel_compute_brightness(connector, val); + val = intel_panel_compute_brightness(connector, val); + panel->backlight.level = clamp(val, panel->backlight.min, + panel->backlight.max); - panel->backlight.enabled = (ctl2 & BLM_PWM_ENABLE) && - panel->backlight.level != 0; + panel->backlight.enabled = ctl2 & BLM_PWM_ENABLE; return 0; } @@ -1562,10 +1568,11 @@ static int vlv_setup_backlight(struct intel_connector *connector, enum pipe pipe panel->backlight.min = get_backlight_min_vbt(connector); val = _vlv_get_backlight(dev_priv, pipe); - panel->backlight.level = intel_panel_compute_brightness(connector, val); + val = intel_panel_compute_brightness(connector, val); + panel->backlight.level = clamp(val, panel->backlight.min, + panel->backlight.max); - panel->backlight.enabled = (ctl2 & BLM_PWM_ENABLE) && - panel->backlight.level != 0; + panel->backlight.enabled = ctl2 & BLM_PWM_ENABLE; return 0; } @@ -1607,10 +1614,11 @@ bxt_setup_backlight(struct intel_connector *connector, enum pipe unused) return -ENODEV; val = bxt_get_backlight(connector); - panel->backlight.level = intel_panel_compute_brightness(connector, val); + val = intel_panel_compute_brightness(connector, val); + panel->backlight.level = clamp(val, panel->backlight.min, + panel->backlight.max); - panel->backlight.enabled = (pwm_ctl & BXT_BLC_PWM_ENABLE) && - panel->backlight.level != 0; + panel->backlight.enabled = pwm_ctl & BXT_BLC_PWM_ENABLE; return 0; } diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index f4f3fcc..2df06b7 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -340,6 +340,11 @@ void intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable) I915_WRITE(FW_BLC_SELF, val); POSTING_READ(FW_BLC_SELF); } else if (IS_I915GM(dev)) { + /* + * FIXME can't find a bit like this for 915G, and + * and yet it does have the related watermark in + * FW_BLC_SELF. What's going on? + */ val = enable ? _MASKED_BIT_ENABLE(INSTPM_SELF_EN) : _MASKED_BIT_DISABLE(INSTPM_SELF_EN); I915_WRITE(INSTPM, val); @@ -960,7 +965,7 @@ static uint16_t vlv_compute_wm_level(struct intel_plane *plane, if (dev_priv->wm.pri_latency[level] == 0) return USHRT_MAX; - if (!state->visible) + if (!state->base.visible) return 0; cpp = drm_format_plane_cpp(state->base.fb->pixel_format, 0); @@ -1002,7 +1007,7 @@ static void vlv_compute_fifo(struct intel_crtc *crtc) if (plane->base.type == DRM_PLANE_TYPE_CURSOR) continue; - if (state->visible) { + if (state->base.visible) { wm_state->num_active_planes++; total_rate += drm_format_plane_cpp(state->base.fb->pixel_format, 0); } @@ -1018,7 +1023,7 @@ static void vlv_compute_fifo(struct intel_crtc *crtc) continue; } - if (!state->visible) { + if (!state->base.visible) { plane->wm.fifo_size = 0; continue; } @@ -1118,7 +1123,7 @@ static void vlv_compute_wm(struct intel_crtc *crtc) struct intel_plane_state *state = to_intel_plane_state(plane->base.state); - if (!state->visible) + if (!state->base.visible) continue; /* normal watermarks */ @@ -1580,7 +1585,7 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) obj = intel_fb_obj(enabled->primary->state->fb); /* self-refresh seems busted with untiled */ - if (obj->tiling_mode == I915_TILING_NONE) + if (!i915_gem_object_is_tiled(obj)) enabled = NULL; } @@ -1604,6 +1609,9 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) unsigned long line_time_us; int entries; + if (IS_I915GM(dev) || IS_I945GM(dev)) + cpp = 4; + line_time_us = max(htotal * 1000 / clock, 1); /* Use ns/us then divide to preserve precision */ @@ -1618,7 +1626,7 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) if (IS_I945G(dev) || IS_I945GM(dev)) I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_FIFO_MASK | (srwm & 0xff)); - else if (IS_I915GM(dev)) + else I915_WRITE(FW_BLC_SELF, srwm & 0x3f); } @@ -1767,7 +1775,7 @@ static uint32_t ilk_compute_pri_wm(const struct intel_crtc_state *cstate, drm_format_plane_cpp(pstate->base.fb->pixel_format, 0) : 0; uint32_t method1, method2; - if (!cstate->base.active || !pstate->visible) + if (!cstate->base.active || !pstate->base.visible) return 0; method1 = ilk_wm_method1(ilk_pipe_pixel_rate(cstate), cpp, mem_value); @@ -1777,7 +1785,7 @@ static uint32_t ilk_compute_pri_wm(const struct intel_crtc_state *cstate, method2 = ilk_wm_method2(ilk_pipe_pixel_rate(cstate), cstate->base.adjusted_mode.crtc_htotal, - drm_rect_width(&pstate->dst), + drm_rect_width(&pstate->base.dst), cpp, mem_value); return min(method1, method2); @@ -1795,13 +1803,13 @@ static uint32_t ilk_compute_spr_wm(const struct intel_crtc_state *cstate, drm_format_plane_cpp(pstate->base.fb->pixel_format, 0) : 0; uint32_t method1, method2; - if (!cstate->base.active || !pstate->visible) + if (!cstate->base.active || !pstate->base.visible) return 0; method1 = ilk_wm_method1(ilk_pipe_pixel_rate(cstate), cpp, mem_value); method2 = ilk_wm_method2(ilk_pipe_pixel_rate(cstate), cstate->base.adjusted_mode.crtc_htotal, - drm_rect_width(&pstate->dst), + drm_rect_width(&pstate->base.dst), cpp, mem_value); return min(method1, method2); } @@ -1820,7 +1828,7 @@ static uint32_t ilk_compute_cur_wm(const struct intel_crtc_state *cstate, * this is necessary to avoid flickering. */ int cpp = 4; - int width = pstate->visible ? pstate->base.crtc_w : 64; + int width = pstate->base.visible ? pstate->base.crtc_w : 64; if (!cstate->base.active) return 0; @@ -1838,10 +1846,10 @@ static uint32_t ilk_compute_fbc_wm(const struct intel_crtc_state *cstate, int cpp = pstate->base.fb ? drm_format_plane_cpp(pstate->base.fb->pixel_format, 0) : 0; - if (!cstate->base.active || !pstate->visible) + if (!cstate->base.active || !pstate->base.visible) return 0; - return ilk_wm_fbc(pri_val, drm_rect_width(&pstate->dst), cpp); + return ilk_wm_fbc(pri_val, drm_rect_width(&pstate->base.dst), cpp); } static unsigned int ilk_display_fifo_size(const struct drm_device *dev) @@ -2358,10 +2366,10 @@ static int ilk_compute_pipe_wm(struct intel_crtc_state *cstate) pipe_wm->pipe_enabled = cstate->base.active; if (sprstate) { - pipe_wm->sprites_enabled = sprstate->visible; - pipe_wm->sprites_scaled = sprstate->visible && - (drm_rect_width(&sprstate->dst) != drm_rect_width(&sprstate->src) >> 16 || - drm_rect_height(&sprstate->dst) != drm_rect_height(&sprstate->src) >> 16); + pipe_wm->sprites_enabled = sprstate->base.visible; + pipe_wm->sprites_scaled = sprstate->base.visible && + (drm_rect_width(&sprstate->base.dst) != drm_rect_width(&sprstate->base.src) >> 16 || + drm_rect_height(&sprstate->base.dst) != drm_rect_height(&sprstate->base.src) >> 16); } usable_level = max_level; @@ -2845,13 +2853,7 @@ bool ilk_disable_lp_wm(struct drm_device *dev) return _ilk_disable_lp_wm(dev_priv, WM_DIRTY_LP_ALL); } -/* - * On gen9, we need to allocate Display Data Buffer (DDB) portions to the - * different active planes. - */ - -#define SKL_DDB_SIZE 896 /* in blocks */ -#define BXT_DDB_SIZE 512 +#define SKL_SAGV_BLOCK_TIME 30 /* µs */ /* * Return the index of a plane in the SKL DDB and wm result arrays. Primary @@ -2875,6 +2877,153 @@ skl_wm_plane_id(const struct intel_plane *plane) } } +/* + * SAGV dynamically adjusts the system agent voltage and clock frequencies + * depending on power and performance requirements. The display engine access + * to system memory is blocked during the adjustment time. Because of the + * blocking time, having this enabled can cause full system hangs and/or pipe + * underruns if we don't meet all of the following requirements: + * + * - <= 1 pipe enabled + * - All planes can enable watermarks for latencies >= SAGV engine block time + * - We're not using an interlaced display configuration + */ +int +skl_enable_sagv(struct drm_i915_private *dev_priv) +{ + int ret; + + if (dev_priv->skl_sagv_status == I915_SKL_SAGV_NOT_CONTROLLED || + dev_priv->skl_sagv_status == I915_SKL_SAGV_ENABLED) + return 0; + + DRM_DEBUG_KMS("Enabling the SAGV\n"); + mutex_lock(&dev_priv->rps.hw_lock); + + ret = sandybridge_pcode_write(dev_priv, GEN9_PCODE_SAGV_CONTROL, + GEN9_SAGV_ENABLE); + + /* We don't need to wait for the SAGV when enabling */ + mutex_unlock(&dev_priv->rps.hw_lock); + + /* + * Some skl systems, pre-release machines in particular, + * don't actually have an SAGV. + */ + if (ret == -ENXIO) { + DRM_DEBUG_DRIVER("No SAGV found on system, ignoring\n"); + dev_priv->skl_sagv_status = I915_SKL_SAGV_NOT_CONTROLLED; + return 0; + } else if (ret < 0) { + DRM_ERROR("Failed to enable the SAGV\n"); + return ret; + } + + dev_priv->skl_sagv_status = I915_SKL_SAGV_ENABLED; + return 0; +} + +static int +skl_do_sagv_disable(struct drm_i915_private *dev_priv) +{ + int ret; + uint32_t temp = GEN9_SAGV_DISABLE; + + ret = sandybridge_pcode_read(dev_priv, GEN9_PCODE_SAGV_CONTROL, + &temp); + if (ret) + return ret; + else + return temp & GEN9_SAGV_IS_DISABLED; +} + +int +skl_disable_sagv(struct drm_i915_private *dev_priv) +{ + int ret, result; + + if (dev_priv->skl_sagv_status == I915_SKL_SAGV_NOT_CONTROLLED || + dev_priv->skl_sagv_status == I915_SKL_SAGV_DISABLED) + return 0; + + DRM_DEBUG_KMS("Disabling the SAGV\n"); + mutex_lock(&dev_priv->rps.hw_lock); + + /* bspec says to keep retrying for at least 1 ms */ + ret = wait_for(result = skl_do_sagv_disable(dev_priv), 1); + mutex_unlock(&dev_priv->rps.hw_lock); + + if (ret == -ETIMEDOUT) { + DRM_ERROR("Request to disable SAGV timed out\n"); + return -ETIMEDOUT; + } + + /* + * Some skl systems, pre-release machines in particular, + * don't actually have an SAGV. + */ + if (result == -ENXIO) { + DRM_DEBUG_DRIVER("No SAGV found on system, ignoring\n"); + dev_priv->skl_sagv_status = I915_SKL_SAGV_NOT_CONTROLLED; + return 0; + } else if (result < 0) { + DRM_ERROR("Failed to disable the SAGV\n"); + return result; + } + + dev_priv->skl_sagv_status = I915_SKL_SAGV_DISABLED; + return 0; +} + +bool skl_can_enable_sagv(struct drm_atomic_state *state) +{ + struct drm_device *dev = state->dev; + struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_atomic_state *intel_state = to_intel_atomic_state(state); + struct drm_crtc *crtc; + enum pipe pipe; + int level, plane; + + /* + * SKL workaround: bspec recommends we disable the SAGV when we have + * more then one pipe enabled + * + * If there are no active CRTCs, no additional checks need be performed + */ + if (hweight32(intel_state->active_crtcs) == 0) + return true; + else if (hweight32(intel_state->active_crtcs) > 1) + return false; + + /* Since we're now guaranteed to only have one active CRTC... */ + pipe = ffs(intel_state->active_crtcs) - 1; + crtc = dev_priv->pipe_to_crtc_mapping[pipe]; + + if (crtc->state->mode.flags & DRM_MODE_FLAG_INTERLACE) + return false; + + for_each_plane(dev_priv, pipe, plane) { + /* Skip this plane if it's not enabled */ + if (intel_state->wm_results.plane[pipe][plane][0] == 0) + continue; + + /* Find the highest enabled wm level for this plane */ + for (level = ilk_wm_max_level(dev); + intel_state->wm_results.plane[pipe][plane][level] == 0; --level) + { } + + /* + * If any of the planes on this pipe don't enable wm levels + * that incur memory latencies higher then 30µs we can't enable + * the SAGV + */ + if (dev_priv->wm.skl_latency[level] < SKL_SAGV_BLOCK_TIME) + return false; + } + + return true; +} + static void skl_ddb_get_pipe_allocation_limits(struct drm_device *dev, const struct intel_crtc_state *cstate, @@ -2901,10 +3050,8 @@ skl_ddb_get_pipe_allocation_limits(struct drm_device *dev, else *num_active = hweight32(dev_priv->active_crtcs); - if (IS_BROXTON(dev)) - ddb_size = BXT_DDB_SIZE; - else - ddb_size = SKL_DDB_SIZE; + ddb_size = INTEL_INFO(dev_priv)->ddb_size; + WARN_ON(ddb_size == 0); ddb_size -= 4; /* 4 blocks for bypass path allocation */ @@ -2996,14 +3143,14 @@ skl_plane_downscale_amount(const struct intel_plane_state *pstate) uint32_t downscale_h, downscale_w; uint32_t src_w, src_h, dst_w, dst_h; - if (WARN_ON(!pstate->visible)) + if (WARN_ON(!pstate->base.visible)) return DRM_PLANE_HELPER_NO_SCALING; /* n.b., src is 16.16 fixed point, dst is whole integer */ - src_w = drm_rect_width(&pstate->src); - src_h = drm_rect_height(&pstate->src); - dst_w = drm_rect_width(&pstate->dst); - dst_h = drm_rect_height(&pstate->dst); + src_w = drm_rect_width(&pstate->base.src); + src_h = drm_rect_height(&pstate->base.src); + dst_w = drm_rect_width(&pstate->base.dst); + dst_h = drm_rect_height(&pstate->base.dst); if (intel_rotation_90_or_270(pstate->base.rotation)) swap(dst_w, dst_h); @@ -3025,15 +3172,15 @@ skl_plane_relative_data_rate(const struct intel_crtc_state *cstate, uint32_t width = 0, height = 0; unsigned format = fb ? fb->pixel_format : DRM_FORMAT_XRGB8888; - if (!intel_pstate->visible) + if (!intel_pstate->base.visible) return 0; if (pstate->plane->type == DRM_PLANE_TYPE_CURSOR) return 0; if (y && format != DRM_FORMAT_NV12) return 0; - width = drm_rect_width(&intel_pstate->src) >> 16; - height = drm_rect_height(&intel_pstate->src) >> 16; + width = drm_rect_width(&intel_pstate->base.src) >> 16; + height = drm_rect_height(&intel_pstate->base.src) >> 16; if (intel_rotation_90_or_270(pstate->rotation)) swap(width, height); @@ -3107,8 +3254,6 @@ skl_get_total_relative_data_rate(struct intel_crtc_state *intel_cstate) total_data_rate += intel_cstate->wm.skl.plane_y_data_rate[id]; } - WARN_ON(cstate->plane_mask && total_data_rate == 0); - return total_data_rate; } @@ -3134,8 +3279,8 @@ skl_ddb_min_alloc(const struct drm_plane_state *pstate, fb->modifier[0] != I915_FORMAT_MOD_Yf_TILED) return 8; - src_w = drm_rect_width(&intel_pstate->src) >> 16; - src_h = drm_rect_height(&intel_pstate->src) >> 16; + src_w = drm_rect_width(&intel_pstate->base.src) >> 16; + src_h = drm_rect_height(&intel_pstate->base.src) >> 16; if (intel_rotation_90_or_270(pstate->rotation)) swap(src_w, src_h); @@ -3226,7 +3371,7 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, if (intel_plane->pipe != pipe) continue; - if (!to_intel_plane_state(pstate)->visible) { + if (!to_intel_plane_state(pstate)->base.visible) { minimum[id] = 0; y_minimum[id] = 0; continue; @@ -3344,6 +3489,8 @@ static uint32_t skl_wm_method2(uint32_t pixel_rate, uint32_t pipe_htotal, plane_bytes_per_line *= 4; plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512); plane_blocks_per_line /= 4; + } else if (tiling == DRM_FORMAT_MOD_NONE) { + plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512) + 1; } else { plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512); } @@ -3363,7 +3510,7 @@ static uint32_t skl_adjusted_plane_pixel_rate(const struct intel_crtc_state *cst uint64_t pixel_rate; /* Shouldn't reach here on disabled planes... */ - if (WARN_ON(!pstate->visible)) + if (WARN_ON(!pstate->base.visible)) return 0; /* @@ -3399,13 +3546,13 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, uint32_t width = 0, height = 0; uint32_t plane_pixel_rate; - if (latency == 0 || !cstate->base.active || !intel_pstate->visible) { + if (latency == 0 || !cstate->base.active || !intel_pstate->base.visible) { *enabled = false; return 0; } - width = drm_rect_width(&intel_pstate->src) >> 16; - height = drm_rect_height(&intel_pstate->src) >> 16; + width = drm_rect_width(&intel_pstate->base.src) >> 16; + height = drm_rect_height(&intel_pstate->base.src) >> 16; if (intel_rotation_90_or_270(pstate->rotation)) swap(width, height); @@ -3680,183 +3827,82 @@ static void skl_ddb_entry_write(struct drm_i915_private *dev_priv, I915_WRITE(reg, 0); } -static void skl_write_wm_values(struct drm_i915_private *dev_priv, - const struct skl_wm_values *new) +void skl_write_plane_wm(struct intel_crtc *intel_crtc, + const struct skl_wm_values *wm, + int plane) { - struct drm_device *dev = &dev_priv->drm; - struct intel_crtc *crtc; - - for_each_intel_crtc(dev, crtc) { - int i, level, max_level = ilk_wm_max_level(dev); - enum pipe pipe = crtc->pipe; - - if ((new->dirty_pipes & drm_crtc_mask(&crtc->base)) == 0) - continue; - if (!crtc->active) - continue; - - I915_WRITE(PIPE_WM_LINETIME(pipe), new->wm_linetime[pipe]); - - for (level = 0; level <= max_level; level++) { - for (i = 0; i < intel_num_planes(crtc); i++) - I915_WRITE(PLANE_WM(pipe, i, level), - new->plane[pipe][i][level]); - I915_WRITE(CUR_WM(pipe, level), - new->plane[pipe][PLANE_CURSOR][level]); - } - for (i = 0; i < intel_num_planes(crtc); i++) - I915_WRITE(PLANE_WM_TRANS(pipe, i), - new->plane_trans[pipe][i]); - I915_WRITE(CUR_WM_TRANS(pipe), - new->plane_trans[pipe][PLANE_CURSOR]); - - for (i = 0; i < intel_num_planes(crtc); i++) { - skl_ddb_entry_write(dev_priv, - PLANE_BUF_CFG(pipe, i), - &new->ddb.plane[pipe][i]); - skl_ddb_entry_write(dev_priv, - PLANE_NV12_BUF_CFG(pipe, i), - &new->ddb.y_plane[pipe][i]); - } + struct drm_crtc *crtc = &intel_crtc->base; + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = to_i915(dev); + int level, max_level = ilk_wm_max_level(dev); + enum pipe pipe = intel_crtc->pipe; - skl_ddb_entry_write(dev_priv, CUR_BUF_CFG(pipe), - &new->ddb.plane[pipe][PLANE_CURSOR]); + for (level = 0; level <= max_level; level++) { + I915_WRITE(PLANE_WM(pipe, plane, level), + wm->plane[pipe][plane][level]); } -} + I915_WRITE(PLANE_WM_TRANS(pipe, plane), wm->plane_trans[pipe][plane]); -/* - * When setting up a new DDB allocation arrangement, we need to correctly - * sequence the times at which the new allocations for the pipes are taken into - * account or we'll have pipes fetching from space previously allocated to - * another pipe. - * - * Roughly the sequence looks like: - * 1. re-allocate the pipe(s) with the allocation being reduced and not - * overlapping with a previous light-up pipe (another way to put it is: - * pipes with their new allocation strickly included into their old ones). - * 2. re-allocate the other pipes that get their allocation reduced - * 3. allocate the pipes having their allocation increased - * - * Steps 1. and 2. are here to take care of the following case: - * - Initially DDB looks like this: - * | B | C | - * - enable pipe A. - * - pipe B has a reduced DDB allocation that overlaps with the old pipe C - * allocation - * | A | B | C | - * - * We need to sequence the re-allocation: C, B, A (and not B, C, A). - */ + skl_ddb_entry_write(dev_priv, PLANE_BUF_CFG(pipe, plane), + &wm->ddb.plane[pipe][plane]); + skl_ddb_entry_write(dev_priv, PLANE_NV12_BUF_CFG(pipe, plane), + &wm->ddb.y_plane[pipe][plane]); +} -static void -skl_wm_flush_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, int pass) +void skl_write_cursor_wm(struct intel_crtc *intel_crtc, + const struct skl_wm_values *wm) { - int plane; - - DRM_DEBUG_KMS("flush pipe %c (pass %d)\n", pipe_name(pipe), pass); + struct drm_crtc *crtc = &intel_crtc->base; + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = to_i915(dev); + int level, max_level = ilk_wm_max_level(dev); + enum pipe pipe = intel_crtc->pipe; - for_each_plane(dev_priv, pipe, plane) { - I915_WRITE(PLANE_SURF(pipe, plane), - I915_READ(PLANE_SURF(pipe, plane))); + for (level = 0; level <= max_level; level++) { + I915_WRITE(CUR_WM(pipe, level), + wm->plane[pipe][PLANE_CURSOR][level]); } - I915_WRITE(CURBASE(pipe), I915_READ(CURBASE(pipe))); + I915_WRITE(CUR_WM_TRANS(pipe), wm->plane_trans[pipe][PLANE_CURSOR]); + + skl_ddb_entry_write(dev_priv, CUR_BUF_CFG(pipe), + &wm->ddb.plane[pipe][PLANE_CURSOR]); } -static bool -skl_ddb_allocation_included(const struct skl_ddb_allocation *old, - const struct skl_ddb_allocation *new, - enum pipe pipe) +bool skl_ddb_allocation_equals(const struct skl_ddb_allocation *old, + const struct skl_ddb_allocation *new, + enum pipe pipe) { - uint16_t old_size, new_size; - - old_size = skl_ddb_entry_size(&old->pipe[pipe]); - new_size = skl_ddb_entry_size(&new->pipe[pipe]); - - return old_size != new_size && - new->pipe[pipe].start >= old->pipe[pipe].start && - new->pipe[pipe].end <= old->pipe[pipe].end; + return new->pipe[pipe].start == old->pipe[pipe].start && + new->pipe[pipe].end == old->pipe[pipe].end; } -static void skl_flush_wm_values(struct drm_i915_private *dev_priv, - struct skl_wm_values *new_values) +static inline bool skl_ddb_entries_overlap(const struct skl_ddb_entry *a, + const struct skl_ddb_entry *b) { - struct drm_device *dev = &dev_priv->drm; - struct skl_ddb_allocation *cur_ddb, *new_ddb; - bool reallocated[I915_MAX_PIPES] = {}; - struct intel_crtc *crtc; - enum pipe pipe; - - new_ddb = &new_values->ddb; - cur_ddb = &dev_priv->wm.skl_hw.ddb; - - /* - * First pass: flush the pipes with the new allocation contained into - * the old space. - * - * We'll wait for the vblank on those pipes to ensure we can safely - * re-allocate the freed space without this pipe fetching from it. - */ - for_each_intel_crtc(dev, crtc) { - if (!crtc->active) - continue; - - pipe = crtc->pipe; - - if (!skl_ddb_allocation_included(cur_ddb, new_ddb, pipe)) - continue; - - skl_wm_flush_pipe(dev_priv, pipe, 1); - intel_wait_for_vblank(dev, pipe); - - reallocated[pipe] = true; - } - + return a->start < b->end && b->start < a->end; +} - /* - * Second pass: flush the pipes that are having their allocation - * reduced, but overlapping with a previous allocation. - * - * Here as well we need to wait for the vblank to make sure the freed - * space is not used anymore. - */ - for_each_intel_crtc(dev, crtc) { - if (!crtc->active) - continue; +bool skl_ddb_allocation_overlaps(struct drm_atomic_state *state, + const struct skl_ddb_allocation *old, + const struct skl_ddb_allocation *new, + enum pipe pipe) +{ + struct drm_device *dev = state->dev; + struct intel_crtc *intel_crtc; + enum pipe otherp; - pipe = crtc->pipe; + for_each_intel_crtc(dev, intel_crtc) { + otherp = intel_crtc->pipe; - if (reallocated[pipe]) + if (otherp == pipe) continue; - if (skl_ddb_entry_size(&new_ddb->pipe[pipe]) < - skl_ddb_entry_size(&cur_ddb->pipe[pipe])) { - skl_wm_flush_pipe(dev_priv, pipe, 2); - intel_wait_for_vblank(dev, pipe); - reallocated[pipe] = true; - } + if (skl_ddb_entries_overlap(&new->pipe[pipe], + &old->pipe[otherp])) + return true; } - /* - * Third pass: flush the pipes that got more space allocated. - * - * We don't need to actively wait for the update here, next vblank - * will just get more DDB space with the correct WM values. - */ - for_each_intel_crtc(dev, crtc) { - if (!crtc->active) - continue; - - pipe = crtc->pipe; - - /* - * At this point, only the pipes more space than before are - * left to re-allocate. - */ - if (reallocated[pipe]) - continue; - - skl_wm_flush_pipe(dev_priv, pipe, 3); - } + return false; } static int skl_update_pipe_wm(struct drm_crtc_state *cstate, @@ -3910,9 +3956,24 @@ skl_compute_ddb(struct drm_atomic_state *state) * pretend that all pipes switched active status so that we'll * ensure a full DDB recompute. */ - if (dev_priv->wm.distrust_bios_wm) + if (dev_priv->wm.distrust_bios_wm) { + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, + state->acquire_ctx); + if (ret) + return ret; + intel_state->active_pipe_changes = ~0; + /* + * We usually only initialize intel_state->active_crtcs if we + * we're doing a modeset; make sure this field is always + * initialized during the sanitization process that happens + * on the first commit too. + */ + if (!intel_state->modeset) + intel_state->active_crtcs = dev_priv->active_crtcs; + } + /* * If the modeset changes which CRTC's are active, we need to * recompute the DDB allocation for *all* active pipes, even @@ -3941,11 +4002,33 @@ skl_compute_ddb(struct drm_atomic_state *state) ret = skl_allocate_pipe_ddb(cstate, ddb); if (ret) return ret; + + ret = drm_atomic_add_affected_planes(state, &intel_crtc->base); + if (ret) + return ret; } return 0; } +static void +skl_copy_wm_for_pipe(struct skl_wm_values *dst, + struct skl_wm_values *src, + enum pipe pipe) +{ + dst->wm_linetime[pipe] = src->wm_linetime[pipe]; + memcpy(dst->plane[pipe], src->plane[pipe], + sizeof(dst->plane[pipe])); + memcpy(dst->plane_trans[pipe], src->plane_trans[pipe], + sizeof(dst->plane_trans[pipe])); + + dst->ddb.pipe[pipe] = src->ddb.pipe[pipe]; + memcpy(dst->ddb.y_plane[pipe], src->ddb.y_plane[pipe], + sizeof(dst->ddb.y_plane[pipe])); + memcpy(dst->ddb.plane[pipe], src->ddb.plane[pipe], + sizeof(dst->ddb.plane[pipe])); +} + static int skl_compute_wm(struct drm_atomic_state *state) { @@ -4018,8 +4101,10 @@ static void skl_update_wm(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = to_i915(dev); struct skl_wm_values *results = &dev_priv->wm.skl_results; + struct skl_wm_values *hw_vals = &dev_priv->wm.skl_hw; struct intel_crtc_state *cstate = to_intel_crtc_state(crtc->state); struct skl_pipe_wm *pipe_wm = &cstate->wm.skl.optimal; + enum pipe pipe = intel_crtc->pipe; if ((results->dirty_pipes & drm_crtc_mask(crtc)) == 0) return; @@ -4028,11 +4113,22 @@ static void skl_update_wm(struct drm_crtc *crtc) mutex_lock(&dev_priv->wm.wm_mutex); - skl_write_wm_values(dev_priv, results); - skl_flush_wm_values(dev_priv, results); + /* + * If this pipe isn't active already, we're going to be enabling it + * very soon. Since it's safe to update a pipe's ddb allocation while + * the pipe's shut off, just do so here. Already active pipes will have + * their watermarks updated once we update their planes. + */ + if (crtc->state->active_changed) { + int plane; + + for (plane = 0; plane < intel_num_planes(intel_crtc); plane++) + skl_write_plane_wm(intel_crtc, results, plane); + + skl_write_cursor_wm(intel_crtc, results); + } - /* store the new configuration */ - dev_priv->wm.skl_hw = *results; + skl_copy_wm_for_pipe(hw_vals, results, pipe); mutex_unlock(&dev_priv->wm.wm_mutex); } @@ -4892,7 +4988,8 @@ void gen6_rps_idle(struct drm_i915_private *dev_priv) else gen6_set_rps(dev_priv, dev_priv->rps.idle_freq); dev_priv->rps.last_adj = 0; - I915_WRITE(GEN6_PMINTRMSK, 0xffffffff); + I915_WRITE(GEN6_PMINTRMSK, + gen6_sanitize_rps_pm_mask(dev_priv, ~0)); } mutex_unlock(&dev_priv->rps.hw_lock); @@ -4911,7 +5008,7 @@ void gen6_rps_boost(struct drm_i915_private *dev_priv, */ if (!(dev_priv->gt.awake && dev_priv->rps.enabled && - dev_priv->rps.cur_freq < dev_priv->rps.max_freq_softlimit)) + dev_priv->rps.cur_freq < dev_priv->rps.boost_freq)) return; /* Force a RPS boost (and don't count it against the client) if @@ -5102,35 +5199,31 @@ int sanitize_rc6_option(struct drm_i915_private *dev_priv, int enable_rc6) static void gen6_init_rps_frequencies(struct drm_i915_private *dev_priv) { - uint32_t rp_state_cap; - u32 ddcc_status = 0; - int ret; - /* All of these values are in units of 50MHz */ - dev_priv->rps.cur_freq = 0; + /* static values from HW: RP0 > RP1 > RPn (min_freq) */ if (IS_BROXTON(dev_priv)) { - rp_state_cap = I915_READ(BXT_RP_STATE_CAP); + u32 rp_state_cap = I915_READ(BXT_RP_STATE_CAP); dev_priv->rps.rp0_freq = (rp_state_cap >> 16) & 0xff; dev_priv->rps.rp1_freq = (rp_state_cap >> 8) & 0xff; dev_priv->rps.min_freq = (rp_state_cap >> 0) & 0xff; } else { - rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); + u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); dev_priv->rps.rp0_freq = (rp_state_cap >> 0) & 0xff; dev_priv->rps.rp1_freq = (rp_state_cap >> 8) & 0xff; dev_priv->rps.min_freq = (rp_state_cap >> 16) & 0xff; } - /* hw_max = RP0 until we check for overclocking */ - dev_priv->rps.max_freq = dev_priv->rps.rp0_freq; + dev_priv->rps.max_freq = dev_priv->rps.rp0_freq; dev_priv->rps.efficient_freq = dev_priv->rps.rp1_freq; if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv) || IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { - ret = sandybridge_pcode_read(dev_priv, - HSW_PCODE_DYNAMIC_DUTY_CYCLE_CONTROL, - &ddcc_status); - if (0 == ret) + u32 ddcc_status = 0; + + if (sandybridge_pcode_read(dev_priv, + HSW_PCODE_DYNAMIC_DUTY_CYCLE_CONTROL, + &ddcc_status) == 0) dev_priv->rps.efficient_freq = clamp_t(u8, ((ddcc_status >> 8) & 0xff), @@ -5140,29 +5233,26 @@ static void gen6_init_rps_frequencies(struct drm_i915_private *dev_priv) if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { /* Store the frequency values in 16.66 MHZ units, which is - the natural hardware unit for SKL */ + * the natural hardware unit for SKL + */ dev_priv->rps.rp0_freq *= GEN9_FREQ_SCALER; dev_priv->rps.rp1_freq *= GEN9_FREQ_SCALER; dev_priv->rps.min_freq *= GEN9_FREQ_SCALER; dev_priv->rps.max_freq *= GEN9_FREQ_SCALER; dev_priv->rps.efficient_freq *= GEN9_FREQ_SCALER; } +} - dev_priv->rps.idle_freq = dev_priv->rps.min_freq; +static void reset_rps(struct drm_i915_private *dev_priv, + void (*set)(struct drm_i915_private *, u8)) +{ + u8 freq = dev_priv->rps.cur_freq; - /* Preserve min/max settings in case of re-init */ - if (dev_priv->rps.max_freq_softlimit == 0) - dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq; + /* force a reset */ + dev_priv->rps.power = -1; + dev_priv->rps.cur_freq = -1; - if (dev_priv->rps.min_freq_softlimit == 0) { - if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) - dev_priv->rps.min_freq_softlimit = - max_t(int, dev_priv->rps.efficient_freq, - intel_freq_opcode(dev_priv, 450)); - else - dev_priv->rps.min_freq_softlimit = - dev_priv->rps.min_freq; - } + set(dev_priv, freq); } /* See the Gen9_GT_PM_Programming_Guide doc for the below */ @@ -5170,8 +5260,6 @@ static void gen9_enable_rps(struct drm_i915_private *dev_priv) { intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); - gen6_init_rps_frequencies(dev_priv); - /* WaGsvDisableTurbo: Workaround to disable turbo on BXT A* */ if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_A1)) { /* @@ -5201,8 +5289,7 @@ static void gen9_enable_rps(struct drm_i915_private *dev_priv) /* Leaning on the below call to gen6_set_rps to program/setup the * Up/Down EI & threshold registers, as well as the RP_CONTROL, * RP_INTERRUPT_LIMITS & RPNSWREQ registers */ - dev_priv->rps.power = HIGH_POWER; /* force a reset */ - gen6_set_rps(dev_priv, dev_priv->rps.idle_freq); + reset_rps(dev_priv, gen6_set_rps); intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); } @@ -5289,9 +5376,6 @@ static void gen8_enable_rps(struct drm_i915_private *dev_priv) /* 2a: Disable RC states. */ I915_WRITE(GEN6_RC_CONTROL, 0); - /* Initialize rps frequencies */ - gen6_init_rps_frequencies(dev_priv); - /* 2b: Program RC6 thresholds.*/ I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 40 << 16); I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000); /* 12500 * 1280ns */ @@ -5348,8 +5432,7 @@ static void gen8_enable_rps(struct drm_i915_private *dev_priv) /* 6: Ring frequency + overclocking (our driver does this later */ - dev_priv->rps.power = HIGH_POWER; /* force a reset */ - gen6_set_rps(dev_priv, dev_priv->rps.idle_freq); + reset_rps(dev_priv, gen6_set_rps); intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); } @@ -5357,7 +5440,7 @@ static void gen8_enable_rps(struct drm_i915_private *dev_priv) static void gen6_enable_rps(struct drm_i915_private *dev_priv) { struct intel_engine_cs *engine; - u32 rc6vids, pcu_mbox = 0, rc6_mask = 0; + u32 rc6vids, rc6_mask = 0; u32 gtfifodbg; int rc6_mode; int ret; @@ -5381,9 +5464,6 @@ static void gen6_enable_rps(struct drm_i915_private *dev_priv) intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); - /* Initialize rps frequencies */ - gen6_init_rps_frequencies(dev_priv); - /* disable the counters and set deterministic thresholds */ I915_WRITE(GEN6_RC_CONTROL, 0); @@ -5434,16 +5514,7 @@ static void gen6_enable_rps(struct drm_i915_private *dev_priv) if (ret) DRM_DEBUG_DRIVER("Failed to set the min frequency\n"); - ret = sandybridge_pcode_read(dev_priv, GEN6_READ_OC_PARAMS, &pcu_mbox); - if (!ret && (pcu_mbox & (1<<31))) { /* OC supported */ - DRM_DEBUG_DRIVER("Overclocking supported. Max: %dMHz, Overclock max: %dMHz\n", - (dev_priv->rps.max_freq_softlimit & 0xff) * 50, - (pcu_mbox & 0xff) * 50); - dev_priv->rps.max_freq = pcu_mbox & 0xff; - } - - dev_priv->rps.power = HIGH_POWER; /* force a reset */ - gen6_set_rps(dev_priv, dev_priv->rps.idle_freq); + reset_rps(dev_priv, gen6_set_rps); rc6vids = 0; ret = sandybridge_pcode_read(dev_priv, GEN6_PCODE_READ_RC6VIDS, &rc6vids); @@ -5462,7 +5533,7 @@ static void gen6_enable_rps(struct drm_i915_private *dev_priv) intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); } -static void __gen6_update_ring_freq(struct drm_i915_private *dev_priv) +static void gen6_update_ring_freq(struct drm_i915_private *dev_priv) { int min_freq = 15; unsigned int gpu_freq; @@ -5546,23 +5617,13 @@ static void __gen6_update_ring_freq(struct drm_i915_private *dev_priv) } } -void gen6_update_ring_freq(struct drm_i915_private *dev_priv) -{ - if (!HAS_CORE_RING_FREQ(dev_priv)) - return; - - mutex_lock(&dev_priv->rps.hw_lock); - __gen6_update_ring_freq(dev_priv); - mutex_unlock(&dev_priv->rps.hw_lock); -} - static int cherryview_rps_max_freq(struct drm_i915_private *dev_priv) { u32 val, rp0; val = vlv_punit_read(dev_priv, FB_GFX_FMAX_AT_VMAX_FUSE); - switch (INTEL_INFO(dev_priv)->eu_total) { + switch (INTEL_INFO(dev_priv)->sseu.eu_total) { case 8: /* (2 * 4) config */ rp0 = (val >> FB_GFX_FMAX_AT_VMAX_2SS4EU_FUSE_SHIFT); @@ -5700,8 +5761,6 @@ static void valleyview_setup_pctx(struct drm_i915_private *dev_priv) u32 pcbr; int pctx_size = 24*1024; - mutex_lock(&dev_priv->drm.struct_mutex); - pcbr = I915_READ(VLV_PCBR); if (pcbr) { /* BIOS set it up already, grab the pre-alloc'd space */ @@ -5737,7 +5796,6 @@ static void valleyview_setup_pctx(struct drm_i915_private *dev_priv) out: DRM_DEBUG_DRIVER("PCBR: 0x%08x\n", I915_READ(VLV_PCBR)); dev_priv->vlv_pctx = pctx; - mutex_unlock(&dev_priv->drm.struct_mutex); } static void valleyview_cleanup_pctx(struct drm_i915_private *dev_priv) @@ -5745,7 +5803,7 @@ static void valleyview_cleanup_pctx(struct drm_i915_private *dev_priv) if (WARN_ON(!dev_priv->vlv_pctx)) return; - drm_gem_object_unreference_unlocked(&dev_priv->vlv_pctx->base); + i915_gem_object_put_unlocked(dev_priv->vlv_pctx); dev_priv->vlv_pctx = NULL; } @@ -5768,8 +5826,6 @@ static void valleyview_init_gt_powersave(struct drm_i915_private *dev_priv) vlv_init_gpll_ref_freq(dev_priv); - mutex_lock(&dev_priv->rps.hw_lock); - val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); switch ((val >> 6) & 3) { case 0: @@ -5805,17 +5861,6 @@ static void valleyview_init_gt_powersave(struct drm_i915_private *dev_priv) DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n", intel_gpu_freq(dev_priv, dev_priv->rps.min_freq), dev_priv->rps.min_freq); - - dev_priv->rps.idle_freq = dev_priv->rps.min_freq; - - /* Preserve min/max settings in case of re-init */ - if (dev_priv->rps.max_freq_softlimit == 0) - dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq; - - if (dev_priv->rps.min_freq_softlimit == 0) - dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq; - - mutex_unlock(&dev_priv->rps.hw_lock); } static void cherryview_init_gt_powersave(struct drm_i915_private *dev_priv) @@ -5826,8 +5871,6 @@ static void cherryview_init_gt_powersave(struct drm_i915_private *dev_priv) vlv_init_gpll_ref_freq(dev_priv); - mutex_lock(&dev_priv->rps.hw_lock); - mutex_lock(&dev_priv->sb_lock); val = vlv_cck_read(dev_priv, CCK_FUSE_REG); mutex_unlock(&dev_priv->sb_lock); @@ -5869,17 +5912,6 @@ static void cherryview_init_gt_powersave(struct drm_i915_private *dev_priv) dev_priv->rps.rp1_freq | dev_priv->rps.min_freq) & 1, "Odd GPU freq values\n"); - - dev_priv->rps.idle_freq = dev_priv->rps.min_freq; - - /* Preserve min/max settings in case of re-init */ - if (dev_priv->rps.max_freq_softlimit == 0) - dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq; - - if (dev_priv->rps.min_freq_softlimit == 0) - dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq; - - mutex_unlock(&dev_priv->rps.hw_lock); } static void valleyview_cleanup_gt_powersave(struct drm_i915_private *dev_priv) @@ -5970,16 +6002,7 @@ static void cherryview_enable_rps(struct drm_i915_private *dev_priv) DRM_DEBUG_DRIVER("GPLL enabled? %s\n", yesno(val & GPLLENABLE)); DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val); - dev_priv->rps.cur_freq = (val >> 8) & 0xff; - DRM_DEBUG_DRIVER("current GPU freq: %d MHz (%u)\n", - intel_gpu_freq(dev_priv, dev_priv->rps.cur_freq), - dev_priv->rps.cur_freq); - - DRM_DEBUG_DRIVER("setting GPU freq to %d MHz (%u)\n", - intel_gpu_freq(dev_priv, dev_priv->rps.idle_freq), - dev_priv->rps.idle_freq); - - valleyview_set_rps(dev_priv, dev_priv->rps.idle_freq); + reset_rps(dev_priv, valleyview_set_rps); intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); } @@ -6059,16 +6082,7 @@ static void valleyview_enable_rps(struct drm_i915_private *dev_priv) DRM_DEBUG_DRIVER("GPLL enabled? %s\n", yesno(val & GPLLENABLE)); DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val); - dev_priv->rps.cur_freq = (val >> 8) & 0xff; - DRM_DEBUG_DRIVER("current GPU freq: %d MHz (%u)\n", - intel_gpu_freq(dev_priv, dev_priv->rps.cur_freq), - dev_priv->rps.cur_freq); - - DRM_DEBUG_DRIVER("setting GPU freq to %d MHz (%u)\n", - intel_gpu_freq(dev_priv, dev_priv->rps.idle_freq), - dev_priv->rps.idle_freq); - - valleyview_set_rps(dev_priv, dev_priv->rps.idle_freq); + reset_rps(dev_priv, valleyview_set_rps); intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); } @@ -6397,19 +6411,11 @@ EXPORT_SYMBOL_GPL(i915_gpu_lower); */ bool i915_gpu_busy(void) { - struct drm_i915_private *dev_priv; - struct intel_engine_cs *engine; bool ret = false; spin_lock_irq(&mchdev_lock); - if (!i915_mch_dev) - goto out_unlock; - dev_priv = i915_mch_dev; - - for_each_engine(engine, dev_priv) - ret |= !list_empty(&engine->request_list); - -out_unlock: + if (i915_mch_dev) + ret = i915_mch_dev->gt.awake; spin_unlock_irq(&mchdev_lock); return ret; @@ -6565,30 +6571,62 @@ void intel_init_gt_powersave(struct drm_i915_private *dev_priv) intel_runtime_pm_get(dev_priv); } + mutex_lock(&dev_priv->drm.struct_mutex); + mutex_lock(&dev_priv->rps.hw_lock); + + /* Initialize RPS limits (for userspace) */ if (IS_CHERRYVIEW(dev_priv)) cherryview_init_gt_powersave(dev_priv); else if (IS_VALLEYVIEW(dev_priv)) valleyview_init_gt_powersave(dev_priv); + else if (INTEL_GEN(dev_priv) >= 6) + gen6_init_rps_frequencies(dev_priv); + + /* Derive initial user preferences/limits from the hardware limits */ + dev_priv->rps.idle_freq = dev_priv->rps.min_freq; + dev_priv->rps.cur_freq = dev_priv->rps.idle_freq; + + dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq; + dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq; + + if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) + dev_priv->rps.min_freq_softlimit = + max_t(int, + dev_priv->rps.efficient_freq, + intel_freq_opcode(dev_priv, 450)); + + /* After setting max-softlimit, find the overclock max freq */ + if (IS_GEN6(dev_priv) || + IS_IVYBRIDGE(dev_priv) || IS_HASWELL(dev_priv)) { + u32 params = 0; + + sandybridge_pcode_read(dev_priv, GEN6_READ_OC_PARAMS, ¶ms); + if (params & BIT(31)) { /* OC supported */ + DRM_DEBUG_DRIVER("Overclocking supported, max: %dMHz, overclock: %dMHz\n", + (dev_priv->rps.max_freq & 0xff) * 50, + (params & 0xff) * 50); + dev_priv->rps.max_freq = params & 0xff; + } + } + + /* Finally allow us to boost to max by default */ + dev_priv->rps.boost_freq = dev_priv->rps.max_freq; + + mutex_unlock(&dev_priv->rps.hw_lock); + mutex_unlock(&dev_priv->drm.struct_mutex); + + intel_autoenable_gt_powersave(dev_priv); } void intel_cleanup_gt_powersave(struct drm_i915_private *dev_priv) { - if (IS_CHERRYVIEW(dev_priv)) - return; - else if (IS_VALLEYVIEW(dev_priv)) + if (IS_VALLEYVIEW(dev_priv)) valleyview_cleanup_gt_powersave(dev_priv); if (!i915.enable_rc6) intel_runtime_pm_put(dev_priv); } -static void gen6_suspend_rps(struct drm_i915_private *dev_priv) -{ - flush_delayed_work(&dev_priv->rps.delayed_resume_work); - - gen6_disable_rps_interrupts(dev_priv); -} - /** * intel_suspend_gt_powersave - suspend PM work and helper threads * @dev_priv: i915 device @@ -6602,60 +6640,76 @@ void intel_suspend_gt_powersave(struct drm_i915_private *dev_priv) if (INTEL_GEN(dev_priv) < 6) return; - gen6_suspend_rps(dev_priv); + if (cancel_delayed_work_sync(&dev_priv->rps.autoenable_work)) + intel_runtime_pm_put(dev_priv); - /* Force GPU to min freq during suspend */ - gen6_rps_idle(dev_priv); + /* gen6_rps_idle() will be called later to disable interrupts */ +} + +void intel_sanitize_gt_powersave(struct drm_i915_private *dev_priv) +{ + dev_priv->rps.enabled = true; /* force disabling */ + intel_disable_gt_powersave(dev_priv); + + gen6_reset_rps_interrupts(dev_priv); } void intel_disable_gt_powersave(struct drm_i915_private *dev_priv) { - if (IS_IRONLAKE_M(dev_priv)) { - ironlake_disable_drps(dev_priv); - } else if (INTEL_INFO(dev_priv)->gen >= 6) { - intel_suspend_gt_powersave(dev_priv); + if (!READ_ONCE(dev_priv->rps.enabled)) + return; - mutex_lock(&dev_priv->rps.hw_lock); - if (INTEL_INFO(dev_priv)->gen >= 9) { - gen9_disable_rc6(dev_priv); - gen9_disable_rps(dev_priv); - } else if (IS_CHERRYVIEW(dev_priv)) - cherryview_disable_rps(dev_priv); - else if (IS_VALLEYVIEW(dev_priv)) - valleyview_disable_rps(dev_priv); - else - gen6_disable_rps(dev_priv); + mutex_lock(&dev_priv->rps.hw_lock); - dev_priv->rps.enabled = false; - mutex_unlock(&dev_priv->rps.hw_lock); + if (INTEL_GEN(dev_priv) >= 9) { + gen9_disable_rc6(dev_priv); + gen9_disable_rps(dev_priv); + } else if (IS_CHERRYVIEW(dev_priv)) { + cherryview_disable_rps(dev_priv); + } else if (IS_VALLEYVIEW(dev_priv)) { + valleyview_disable_rps(dev_priv); + } else if (INTEL_GEN(dev_priv) >= 6) { + gen6_disable_rps(dev_priv); + } else if (IS_IRONLAKE_M(dev_priv)) { + ironlake_disable_drps(dev_priv); } + + dev_priv->rps.enabled = false; + mutex_unlock(&dev_priv->rps.hw_lock); } -static void intel_gen6_powersave_work(struct work_struct *work) +void intel_enable_gt_powersave(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = - container_of(work, struct drm_i915_private, - rps.delayed_resume_work.work); + /* We shouldn't be disabling as we submit, so this should be less + * racy than it appears! + */ + if (READ_ONCE(dev_priv->rps.enabled)) + return; - mutex_lock(&dev_priv->rps.hw_lock); + /* Powersaving is controlled by the host when inside a VM */ + if (intel_vgpu_active(dev_priv)) + return; - gen6_reset_rps_interrupts(dev_priv); + mutex_lock(&dev_priv->rps.hw_lock); if (IS_CHERRYVIEW(dev_priv)) { cherryview_enable_rps(dev_priv); } else if (IS_VALLEYVIEW(dev_priv)) { valleyview_enable_rps(dev_priv); - } else if (INTEL_INFO(dev_priv)->gen >= 9) { + } else if (INTEL_GEN(dev_priv) >= 9) { gen9_enable_rc6(dev_priv); gen9_enable_rps(dev_priv); if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) - __gen6_update_ring_freq(dev_priv); + gen6_update_ring_freq(dev_priv); } else if (IS_BROADWELL(dev_priv)) { gen8_enable_rps(dev_priv); - __gen6_update_ring_freq(dev_priv); - } else { + gen6_update_ring_freq(dev_priv); + } else if (INTEL_GEN(dev_priv) >= 6) { gen6_enable_rps(dev_priv); - __gen6_update_ring_freq(dev_priv); + gen6_update_ring_freq(dev_priv); + } else if (IS_IRONLAKE_M(dev_priv)) { + ironlake_enable_drps(dev_priv); + intel_init_emon(dev_priv); } WARN_ON(dev_priv->rps.max_freq < dev_priv->rps.min_freq); @@ -6665,25 +6719,52 @@ static void intel_gen6_powersave_work(struct work_struct *work) WARN_ON(dev_priv->rps.efficient_freq > dev_priv->rps.max_freq); dev_priv->rps.enabled = true; + mutex_unlock(&dev_priv->rps.hw_lock); +} - gen6_enable_rps_interrupts(dev_priv); +static void __intel_autoenable_gt_powersave(struct work_struct *work) +{ + struct drm_i915_private *dev_priv = + container_of(work, typeof(*dev_priv), rps.autoenable_work.work); + struct intel_engine_cs *rcs; + struct drm_i915_gem_request *req; - mutex_unlock(&dev_priv->rps.hw_lock); + if (READ_ONCE(dev_priv->rps.enabled)) + goto out; + + rcs = &dev_priv->engine[RCS]; + if (rcs->last_context) + goto out; + + if (!rcs->init_context) + goto out; + + mutex_lock(&dev_priv->drm.struct_mutex); + req = i915_gem_request_alloc(rcs, dev_priv->kernel_context); + if (IS_ERR(req)) + goto unlock; + + if (!i915.enable_execlists && i915_switch_context(req) == 0) + rcs->init_context(req); + + /* Mark the device busy, calling intel_enable_gt_powersave() */ + i915_add_request_no_flush(req); + +unlock: + mutex_unlock(&dev_priv->drm.struct_mutex); +out: intel_runtime_pm_put(dev_priv); } -void intel_enable_gt_powersave(struct drm_i915_private *dev_priv) +void intel_autoenable_gt_powersave(struct drm_i915_private *dev_priv) { - /* Powersaving is controlled by the host when inside a VM */ - if (intel_vgpu_active(dev_priv)) + if (READ_ONCE(dev_priv->rps.enabled)) return; if (IS_IRONLAKE_M(dev_priv)) { ironlake_enable_drps(dev_priv); - mutex_lock(&dev_priv->drm.struct_mutex); intel_init_emon(dev_priv); - mutex_unlock(&dev_priv->drm.struct_mutex); } else if (INTEL_INFO(dev_priv)->gen >= 6) { /* * PCU communication is slow and this doesn't need to be @@ -6697,21 +6778,13 @@ void intel_enable_gt_powersave(struct drm_i915_private *dev_priv) * paths, so the _noresume version is enough (and in case of * runtime resume it's necessary). */ - if (schedule_delayed_work(&dev_priv->rps.delayed_resume_work, - round_jiffies_up_relative(HZ))) + if (queue_delayed_work(dev_priv->wq, + &dev_priv->rps.autoenable_work, + round_jiffies_up_relative(HZ))) intel_runtime_pm_get_noresume(dev_priv); } } -void intel_reset_gt_powersave(struct drm_i915_private *dev_priv) -{ - if (INTEL_INFO(dev_priv)->gen < 6) - return; - - gen6_suspend_rps(dev_priv); - dev_priv->rps.enabled = false; -} - static void ibx_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); @@ -7657,8 +7730,54 @@ void intel_init_pm(struct drm_device *dev) } } +static inline int gen6_check_mailbox_status(struct drm_i915_private *dev_priv) +{ + uint32_t flags = + I915_READ_FW(GEN6_PCODE_MAILBOX) & GEN6_PCODE_ERROR_MASK; + + switch (flags) { + case GEN6_PCODE_SUCCESS: + return 0; + case GEN6_PCODE_UNIMPLEMENTED_CMD: + case GEN6_PCODE_ILLEGAL_CMD: + return -ENXIO; + case GEN6_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE: + case GEN7_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE: + return -EOVERFLOW; + case GEN6_PCODE_TIMEOUT: + return -ETIMEDOUT; + default: + MISSING_CASE(flags) + return 0; + } +} + +static inline int gen7_check_mailbox_status(struct drm_i915_private *dev_priv) +{ + uint32_t flags = + I915_READ_FW(GEN6_PCODE_MAILBOX) & GEN6_PCODE_ERROR_MASK; + + switch (flags) { + case GEN6_PCODE_SUCCESS: + return 0; + case GEN6_PCODE_ILLEGAL_CMD: + return -ENXIO; + case GEN7_PCODE_TIMEOUT: + return -ETIMEDOUT; + case GEN7_PCODE_ILLEGAL_DATA: + return -EINVAL; + case GEN7_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE: + return -EOVERFLOW; + default: + MISSING_CASE(flags); + return 0; + } +} + int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u32 mbox, u32 *val) { + int status; + WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); /* GEN6_PCODE_* are outside of the forcewake domain, we can @@ -7685,12 +7804,25 @@ int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u32 mbox, u32 *val *val = I915_READ_FW(GEN6_PCODE_DATA); I915_WRITE_FW(GEN6_PCODE_DATA, 0); + if (INTEL_GEN(dev_priv) > 6) + status = gen7_check_mailbox_status(dev_priv); + else + status = gen6_check_mailbox_status(dev_priv); + + if (status) { + DRM_DEBUG_DRIVER("warning: pcode (read) mailbox access failed: %d\n", + status); + return status; + } + return 0; } int sandybridge_pcode_write(struct drm_i915_private *dev_priv, - u32 mbox, u32 val) + u32 mbox, u32 val) { + int status; + WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); /* GEN6_PCODE_* are outside of the forcewake domain, we can @@ -7715,6 +7847,17 @@ int sandybridge_pcode_write(struct drm_i915_private *dev_priv, I915_WRITE_FW(GEN6_PCODE_DATA, 0); + if (INTEL_GEN(dev_priv) > 6) + status = gen7_check_mailbox_status(dev_priv); + else + status = gen6_check_mailbox_status(dev_priv); + + if (status) { + DRM_DEBUG_DRIVER("warning: pcode (write) mailbox access failed: %d\n", + status); + return status; + } + return 0; } @@ -7786,7 +7929,7 @@ static void __intel_rps_boost_work(struct work_struct *work) if (!i915_gem_request_completed(req)) gen6_rps_boost(req->i915, NULL, req->emitted_jiffies); - i915_gem_request_unreference(req); + i915_gem_request_put(req); kfree(boost); } @@ -7804,8 +7947,7 @@ void intel_queue_rps_boost_for_request(struct drm_i915_gem_request *req) if (boost == NULL) return; - i915_gem_request_reference(req); - boost->req = req; + boost->req = i915_gem_request_get(req); INIT_WORK(&boost->work, __intel_rps_boost_work); queue_work(req->i915->wq, &boost->work); @@ -7818,11 +7960,9 @@ void intel_pm_setup(struct drm_device *dev) mutex_init(&dev_priv->rps.hw_lock); spin_lock_init(&dev_priv->rps.client_lock); - INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work, - intel_gen6_powersave_work); + INIT_DELAYED_WORK(&dev_priv->rps.autoenable_work, + __intel_autoenable_gt_powersave); INIT_LIST_HEAD(&dev_priv->rps.clients); - INIT_LIST_HEAD(&dev_priv->rps.semaphores.link); - INIT_LIST_HEAD(&dev_priv->rps.mmioflips.link); dev_priv->pm.suspended = false; atomic_set(&dev_priv->pm.wakeref_count, 0); diff --git a/drivers/gpu/drm/i915/intel_psr.c b/drivers/gpu/drm/i915/intel_psr.c index 2b0d1ba..108ba1e 100644 --- a/drivers/gpu/drm/i915/intel_psr.c +++ b/drivers/gpu/drm/i915/intel_psr.c @@ -255,14 +255,14 @@ static void hsw_psr_enable_source(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = to_i915(dev); uint32_t max_sleep_time = 0x1f; - /* Lately it was identified that depending on panel idle frame count - * calculated at HW can be off by 1. So let's use what came - * from VBT + 1. - * There are also other cases where panel demands at least 4 - * but VBT is not being set. To cover these 2 cases lets use - * at least 5 when VBT isn't set to be on the safest side. + /* + * Let's respect VBT in case VBT asks a higher idle_frame value. + * Let's use 6 as the minimum to cover all known cases including + * the off-by-one issue that HW has in some cases. Also there are + * cases where sink should be able to train + * with the 5 or 6 idle patterns. */ - uint32_t idle_frames = dev_priv->vbt.psr.idle_frames + 1; + uint32_t idle_frames = max(6, dev_priv->vbt.psr.idle_frames); uint32_t val = EDP_PSR_ENABLE; val |= max_sleep_time << EDP_PSR_MAX_SLEEP_TIME_SHIFT; @@ -645,9 +645,8 @@ unlock: mutex_unlock(&dev_priv->psr.lock); } -static void intel_psr_exit(struct drm_device *dev) +static void intel_psr_exit(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_dp *intel_dp = dev_priv->psr.enabled; struct drm_crtc *crtc = dp_to_dig_port(intel_dp)->base.base.crtc; enum pipe pipe = to_intel_crtc(crtc)->pipe; @@ -656,7 +655,7 @@ static void intel_psr_exit(struct drm_device *dev) if (!dev_priv->psr.active) return; - if (HAS_DDI(dev)) { + if (HAS_DDI(dev_priv)) { val = I915_READ(EDP_PSR_CTL); WARN_ON(!(val & EDP_PSR_ENABLE)); @@ -691,7 +690,7 @@ static void intel_psr_exit(struct drm_device *dev) /** * intel_psr_single_frame_update - Single Frame Update - * @dev: DRM device + * @dev_priv: i915 device * @frontbuffer_bits: frontbuffer plane tracking bits * * Some platforms support a single frame update feature that is used to @@ -699,10 +698,9 @@ static void intel_psr_exit(struct drm_device *dev) * So far it is only implemented for Valleyview and Cherryview because * hardware requires this to be done before a page flip. */ -void intel_psr_single_frame_update(struct drm_device *dev, +void intel_psr_single_frame_update(struct drm_i915_private *dev_priv, unsigned frontbuffer_bits) { - struct drm_i915_private *dev_priv = to_i915(dev); struct drm_crtc *crtc; enum pipe pipe; u32 val; @@ -711,7 +709,7 @@ void intel_psr_single_frame_update(struct drm_device *dev, * Single frame update is already supported on BDW+ but it requires * many W/A and it isn't really needed. */ - if (!IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev)) + if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv)) return; mutex_lock(&dev_priv->psr.lock); @@ -737,7 +735,7 @@ void intel_psr_single_frame_update(struct drm_device *dev, /** * intel_psr_invalidate - Invalidade PSR - * @dev: DRM device + * @dev_priv: i915 device * @frontbuffer_bits: frontbuffer plane tracking bits * * Since the hardware frontbuffer tracking has gaps we need to integrate @@ -747,10 +745,9 @@ void intel_psr_single_frame_update(struct drm_device *dev, * * Dirty frontbuffers relevant to PSR are tracked in busy_frontbuffer_bits." */ -void intel_psr_invalidate(struct drm_device *dev, +void intel_psr_invalidate(struct drm_i915_private *dev_priv, unsigned frontbuffer_bits) { - struct drm_i915_private *dev_priv = to_i915(dev); struct drm_crtc *crtc; enum pipe pipe; @@ -767,14 +764,14 @@ void intel_psr_invalidate(struct drm_device *dev, dev_priv->psr.busy_frontbuffer_bits |= frontbuffer_bits; if (frontbuffer_bits) - intel_psr_exit(dev); + intel_psr_exit(dev_priv); mutex_unlock(&dev_priv->psr.lock); } /** * intel_psr_flush - Flush PSR - * @dev: DRM device + * @dev_priv: i915 device * @frontbuffer_bits: frontbuffer plane tracking bits * @origin: which operation caused the flush * @@ -785,10 +782,9 @@ void intel_psr_invalidate(struct drm_device *dev, * * Dirty frontbuffers relevant to PSR are tracked in busy_frontbuffer_bits. */ -void intel_psr_flush(struct drm_device *dev, +void intel_psr_flush(struct drm_i915_private *dev_priv, unsigned frontbuffer_bits, enum fb_op_origin origin) { - struct drm_i915_private *dev_priv = to_i915(dev); struct drm_crtc *crtc; enum pipe pipe; @@ -806,7 +802,7 @@ void intel_psr_flush(struct drm_device *dev, /* By definition flush = invalidate + flush */ if (frontbuffer_bits) - intel_psr_exit(dev); + intel_psr_exit(dev_priv); if (!dev_priv->psr.active && !dev_priv->psr.busy_frontbuffer_bits) if (!work_busy(&dev_priv->psr.work.work)) diff --git a/drivers/gpu/drm/i915/intel_renderstate.h b/drivers/gpu/drm/i915/intel_renderstate.h index 5bd6985..08f6fea 100644 --- a/drivers/gpu/drm/i915/intel_renderstate.h +++ b/drivers/gpu/drm/i915/intel_renderstate.h @@ -24,12 +24,13 @@ #ifndef _INTEL_RENDERSTATE_H #define _INTEL_RENDERSTATE_H -#include "i915_drv.h" +#include <linux/types.h> -extern const struct intel_renderstate_rodata gen6_null_state; -extern const struct intel_renderstate_rodata gen7_null_state; -extern const struct intel_renderstate_rodata gen8_null_state; -extern const struct intel_renderstate_rodata gen9_null_state; +struct intel_renderstate_rodata { + const u32 *reloc; + const u32 *batch; + const u32 batch_items; +}; #define RO_RENDERSTATE(_g) \ const struct intel_renderstate_rodata gen ## _g ## _null_state = { \ @@ -38,4 +39,9 @@ extern const struct intel_renderstate_rodata gen9_null_state; .batch_items = sizeof(gen ## _g ## _null_state_batch)/4, \ } +extern const struct intel_renderstate_rodata gen6_null_state; +extern const struct intel_renderstate_rodata gen7_null_state; +extern const struct intel_renderstate_rodata gen8_null_state; +extern const struct intel_renderstate_rodata gen9_null_state; + #endif /* INTEL_RENDERSTATE_H */ diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index cca7792..7a74750 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -47,57 +47,44 @@ int __intel_ring_space(int head, int tail, int size) return space - I915_RING_FREE_SPACE; } -void intel_ring_update_space(struct intel_ringbuffer *ringbuf) +void intel_ring_update_space(struct intel_ring *ring) { - if (ringbuf->last_retired_head != -1) { - ringbuf->head = ringbuf->last_retired_head; - ringbuf->last_retired_head = -1; + if (ring->last_retired_head != -1) { + ring->head = ring->last_retired_head; + ring->last_retired_head = -1; } - ringbuf->space = __intel_ring_space(ringbuf->head & HEAD_ADDR, - ringbuf->tail, ringbuf->size); -} - -static void __intel_ring_advance(struct intel_engine_cs *engine) -{ - struct intel_ringbuffer *ringbuf = engine->buffer; - ringbuf->tail &= ringbuf->size - 1; - engine->write_tail(engine, ringbuf->tail); + ring->space = __intel_ring_space(ring->head & HEAD_ADDR, + ring->tail, ring->size); } static int -gen2_render_ring_flush(struct drm_i915_gem_request *req, - u32 invalidate_domains, - u32 flush_domains) +gen2_render_ring_flush(struct drm_i915_gem_request *req, u32 mode) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; u32 cmd; int ret; cmd = MI_FLUSH; - if (((invalidate_domains|flush_domains) & I915_GEM_DOMAIN_RENDER) == 0) - cmd |= MI_NO_WRITE_FLUSH; - if (invalidate_domains & I915_GEM_DOMAIN_SAMPLER) + if (mode & EMIT_INVALIDATE) cmd |= MI_READ_FLUSH; ret = intel_ring_begin(req, 2); if (ret) return ret; - intel_ring_emit(engine, cmd); - intel_ring_emit(engine, MI_NOOP); - intel_ring_advance(engine); + intel_ring_emit(ring, cmd); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); return 0; } static int -gen4_render_ring_flush(struct drm_i915_gem_request *req, - u32 invalidate_domains, - u32 flush_domains) +gen4_render_ring_flush(struct drm_i915_gem_request *req, u32 mode) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; u32 cmd; int ret; @@ -129,23 +116,20 @@ gen4_render_ring_flush(struct drm_i915_gem_request *req, * are flushed at any MI_FLUSH. */ - cmd = MI_FLUSH | MI_NO_WRITE_FLUSH; - if ((invalidate_domains|flush_domains) & I915_GEM_DOMAIN_RENDER) - cmd &= ~MI_NO_WRITE_FLUSH; - if (invalidate_domains & I915_GEM_DOMAIN_INSTRUCTION) + cmd = MI_FLUSH; + if (mode & EMIT_INVALIDATE) { cmd |= MI_EXE_FLUSH; - - if (invalidate_domains & I915_GEM_DOMAIN_COMMAND && - (IS_G4X(req->i915) || IS_GEN5(req->i915))) - cmd |= MI_INVALIDATE_ISP; + if (IS_G4X(req->i915) || IS_GEN5(req->i915)) + cmd |= MI_INVALIDATE_ISP; + } ret = intel_ring_begin(req, 2); if (ret) return ret; - intel_ring_emit(engine, cmd); - intel_ring_emit(engine, MI_NOOP); - intel_ring_advance(engine); + intel_ring_emit(ring, cmd); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); return 0; } @@ -190,45 +174,46 @@ gen4_render_ring_flush(struct drm_i915_gem_request *req, static int intel_emit_post_sync_nonzero_flush(struct drm_i915_gem_request *req) { - struct intel_engine_cs *engine = req->engine; - u32 scratch_addr = engine->scratch.gtt_offset + 2 * CACHELINE_BYTES; + struct intel_ring *ring = req->ring; + u32 scratch_addr = + i915_ggtt_offset(req->engine->scratch) + 2 * CACHELINE_BYTES; int ret; ret = intel_ring_begin(req, 6); if (ret) return ret; - intel_ring_emit(engine, GFX_OP_PIPE_CONTROL(5)); - intel_ring_emit(engine, PIPE_CONTROL_CS_STALL | + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5)); + intel_ring_emit(ring, PIPE_CONTROL_CS_STALL | PIPE_CONTROL_STALL_AT_SCOREBOARD); - intel_ring_emit(engine, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); /* address */ - intel_ring_emit(engine, 0); /* low dword */ - intel_ring_emit(engine, 0); /* high dword */ - intel_ring_emit(engine, MI_NOOP); - intel_ring_advance(engine); + intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); + intel_ring_emit(ring, 0); /* low dword */ + intel_ring_emit(ring, 0); /* high dword */ + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); ret = intel_ring_begin(req, 6); if (ret) return ret; - intel_ring_emit(engine, GFX_OP_PIPE_CONTROL(5)); - intel_ring_emit(engine, PIPE_CONTROL_QW_WRITE); - intel_ring_emit(engine, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); /* address */ - intel_ring_emit(engine, 0); - intel_ring_emit(engine, 0); - intel_ring_emit(engine, MI_NOOP); - intel_ring_advance(engine); + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5)); + intel_ring_emit(ring, PIPE_CONTROL_QW_WRITE); + intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); return 0; } static int -gen6_render_ring_flush(struct drm_i915_gem_request *req, - u32 invalidate_domains, u32 flush_domains) +gen6_render_ring_flush(struct drm_i915_gem_request *req, u32 mode) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; + u32 scratch_addr = + i915_ggtt_offset(req->engine->scratch) + 2 * CACHELINE_BYTES; u32 flags = 0; - u32 scratch_addr = engine->scratch.gtt_offset + 2 * CACHELINE_BYTES; int ret; /* Force SNB workarounds for PIPE_CONTROL flushes */ @@ -240,7 +225,7 @@ gen6_render_ring_flush(struct drm_i915_gem_request *req, * number of bits based on the write domains has little performance * impact. */ - if (flush_domains) { + if (mode & EMIT_FLUSH) { flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; /* @@ -249,7 +234,7 @@ gen6_render_ring_flush(struct drm_i915_gem_request *req, */ flags |= PIPE_CONTROL_CS_STALL; } - if (invalidate_domains) { + if (mode & EMIT_INVALIDATE) { flags |= PIPE_CONTROL_TLB_INVALIDATE; flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE; flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE; @@ -266,11 +251,11 @@ gen6_render_ring_flush(struct drm_i915_gem_request *req, if (ret) return ret; - intel_ring_emit(engine, GFX_OP_PIPE_CONTROL(4)); - intel_ring_emit(engine, flags); - intel_ring_emit(engine, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); - intel_ring_emit(engine, 0); - intel_ring_advance(engine); + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4)); + intel_ring_emit(ring, flags); + intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); + intel_ring_emit(ring, 0); + intel_ring_advance(ring); return 0; } @@ -278,30 +263,31 @@ gen6_render_ring_flush(struct drm_i915_gem_request *req, static int gen7_render_ring_cs_stall_wa(struct drm_i915_gem_request *req) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; int ret; ret = intel_ring_begin(req, 4); if (ret) return ret; - intel_ring_emit(engine, GFX_OP_PIPE_CONTROL(4)); - intel_ring_emit(engine, PIPE_CONTROL_CS_STALL | - PIPE_CONTROL_STALL_AT_SCOREBOARD); - intel_ring_emit(engine, 0); - intel_ring_emit(engine, 0); - intel_ring_advance(engine); + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4)); + intel_ring_emit(ring, + PIPE_CONTROL_CS_STALL | + PIPE_CONTROL_STALL_AT_SCOREBOARD); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_advance(ring); return 0; } static int -gen7_render_ring_flush(struct drm_i915_gem_request *req, - u32 invalidate_domains, u32 flush_domains) +gen7_render_ring_flush(struct drm_i915_gem_request *req, u32 mode) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; + u32 scratch_addr = + i915_ggtt_offset(req->engine->scratch) + 2 * CACHELINE_BYTES; u32 flags = 0; - u32 scratch_addr = engine->scratch.gtt_offset + 2 * CACHELINE_BYTES; int ret; /* @@ -318,13 +304,13 @@ gen7_render_ring_flush(struct drm_i915_gem_request *req, * number of bits based on the write domains has little performance * impact. */ - if (flush_domains) { + if (mode & EMIT_FLUSH) { flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; flags |= PIPE_CONTROL_DC_FLUSH_ENABLE; flags |= PIPE_CONTROL_FLUSH_ENABLE; } - if (invalidate_domains) { + if (mode & EMIT_INVALIDATE) { flags |= PIPE_CONTROL_TLB_INVALIDATE; flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE; flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE; @@ -350,11 +336,11 @@ gen7_render_ring_flush(struct drm_i915_gem_request *req, if (ret) return ret; - intel_ring_emit(engine, GFX_OP_PIPE_CONTROL(4)); - intel_ring_emit(engine, flags); - intel_ring_emit(engine, scratch_addr); - intel_ring_emit(engine, 0); - intel_ring_advance(engine); + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4)); + intel_ring_emit(ring, flags); + intel_ring_emit(ring, scratch_addr); + intel_ring_emit(ring, 0); + intel_ring_advance(ring); return 0; } @@ -363,41 +349,41 @@ static int gen8_emit_pipe_control(struct drm_i915_gem_request *req, u32 flags, u32 scratch_addr) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; int ret; ret = intel_ring_begin(req, 6); if (ret) return ret; - intel_ring_emit(engine, GFX_OP_PIPE_CONTROL(6)); - intel_ring_emit(engine, flags); - intel_ring_emit(engine, scratch_addr); - intel_ring_emit(engine, 0); - intel_ring_emit(engine, 0); - intel_ring_emit(engine, 0); - intel_ring_advance(engine); + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6)); + intel_ring_emit(ring, flags); + intel_ring_emit(ring, scratch_addr); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_advance(ring); return 0; } static int -gen8_render_ring_flush(struct drm_i915_gem_request *req, - u32 invalidate_domains, u32 flush_domains) +gen8_render_ring_flush(struct drm_i915_gem_request *req, u32 mode) { + u32 scratch_addr = + i915_ggtt_offset(req->engine->scratch) + 2 * CACHELINE_BYTES; u32 flags = 0; - u32 scratch_addr = req->engine->scratch.gtt_offset + 2 * CACHELINE_BYTES; int ret; flags |= PIPE_CONTROL_CS_STALL; - if (flush_domains) { + if (mode & EMIT_FLUSH) { flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; flags |= PIPE_CONTROL_DC_FLUSH_ENABLE; flags |= PIPE_CONTROL_FLUSH_ENABLE; } - if (invalidate_domains) { + if (mode & EMIT_INVALIDATE) { flags |= PIPE_CONTROL_TLB_INVALIDATE; flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE; flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE; @@ -419,14 +405,7 @@ gen8_render_ring_flush(struct drm_i915_gem_request *req, return gen8_emit_pipe_control(req, flags, scratch_addr); } -static void ring_write_tail(struct intel_engine_cs *engine, - u32 value) -{ - struct drm_i915_private *dev_priv = engine->i915; - I915_WRITE_TAIL(engine, value); -} - -u64 intel_ring_get_active_head(struct intel_engine_cs *engine) +u64 intel_engine_get_active_head(struct intel_engine_cs *engine) { struct drm_i915_private *dev_priv = engine->i915; u64 acthd; @@ -488,7 +467,7 @@ static void intel_ring_setup_status_page(struct intel_engine_cs *engine) mmio = RING_HWS_PGA(engine->mmio_base); } - I915_WRITE(mmio, (u32)engine->status_page.gfx_addr); + I915_WRITE(mmio, engine->status_page.ggtt_offset); POSTING_READ(mmio); /* @@ -519,7 +498,7 @@ static bool stop_ring(struct intel_engine_cs *engine) { struct drm_i915_private *dev_priv = engine->i915; - if (!IS_GEN2(dev_priv)) { + if (INTEL_GEN(dev_priv) > 2) { I915_WRITE_MODE(engine, _MASKED_BIT_ENABLE(STOP_RING)); if (intel_wait_for_register(dev_priv, RING_MI_MODE(engine->mmio_base), @@ -539,9 +518,9 @@ static bool stop_ring(struct intel_engine_cs *engine) I915_WRITE_CTL(engine, 0); I915_WRITE_HEAD(engine, 0); - engine->write_tail(engine, 0); + I915_WRITE_TAIL(engine, 0); - if (!IS_GEN2(dev_priv)) { + if (INTEL_GEN(dev_priv) > 2) { (void)I915_READ_CTL(engine); I915_WRITE_MODE(engine, _MASKED_BIT_DISABLE(STOP_RING)); } @@ -549,16 +528,10 @@ static bool stop_ring(struct intel_engine_cs *engine) return (I915_READ_HEAD(engine) & HEAD_ADDR) == 0; } -void intel_engine_init_hangcheck(struct intel_engine_cs *engine) -{ - memset(&engine->hangcheck, 0, sizeof(engine->hangcheck)); -} - static int init_ring_common(struct intel_engine_cs *engine) { struct drm_i915_private *dev_priv = engine->i915; - struct intel_ringbuffer *ringbuf = engine->buffer; - struct drm_i915_gem_object *obj = ringbuf->obj; + struct intel_ring *ring = engine->buffer; int ret = 0; intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); @@ -586,10 +559,12 @@ static int init_ring_common(struct intel_engine_cs *engine) } } - if (I915_NEED_GFX_HWS(dev_priv)) - intel_ring_setup_status_page(engine); - else + if (HWS_NEEDS_PHYSICAL(dev_priv)) ring_setup_phys_status_page(engine); + else + intel_ring_setup_status_page(engine); + + intel_engine_reset_irq(engine); /* Enforce ordering by reading HEAD register back */ I915_READ_HEAD(engine); @@ -598,40 +573,39 @@ static int init_ring_common(struct intel_engine_cs *engine) * registers with the above sequence (the readback of the HEAD registers * also enforces ordering), otherwise the hw might lose the new ring * register values. */ - I915_WRITE_START(engine, i915_gem_obj_ggtt_offset(obj)); + I915_WRITE_START(engine, i915_ggtt_offset(ring->vma)); /* WaClearRingBufHeadRegAtInit:ctg,elk */ if (I915_READ_HEAD(engine)) DRM_DEBUG("%s initialization failed [head=%08x], fudging\n", engine->name, I915_READ_HEAD(engine)); - I915_WRITE_HEAD(engine, 0); - (void)I915_READ_HEAD(engine); + + intel_ring_update_space(ring); + I915_WRITE_HEAD(engine, ring->head); + I915_WRITE_TAIL(engine, ring->tail); + (void)I915_READ_TAIL(engine); I915_WRITE_CTL(engine, - ((ringbuf->size - PAGE_SIZE) & RING_NR_PAGES) + ((ring->size - PAGE_SIZE) & RING_NR_PAGES) | RING_VALID); /* If the head is still not zero, the ring is dead */ - if (wait_for((I915_READ_CTL(engine) & RING_VALID) != 0 && - I915_READ_START(engine) == i915_gem_obj_ggtt_offset(obj) && - (I915_READ_HEAD(engine) & HEAD_ADDR) == 0, 50)) { + if (intel_wait_for_register_fw(dev_priv, RING_CTL(engine->mmio_base), + RING_VALID, RING_VALID, + 50)) { DRM_ERROR("%s initialization failed " - "ctl %08x (valid? %d) head %08x tail %08x start %08x [expected %08lx]\n", + "ctl %08x (valid? %d) head %08x [%08x] tail %08x [%08x] start %08x [expected %08x]\n", engine->name, I915_READ_CTL(engine), I915_READ_CTL(engine) & RING_VALID, - I915_READ_HEAD(engine), I915_READ_TAIL(engine), + I915_READ_HEAD(engine), ring->head, + I915_READ_TAIL(engine), ring->tail, I915_READ_START(engine), - (unsigned long)i915_gem_obj_ggtt_offset(obj)); + i915_ggtt_offset(ring->vma)); ret = -EIO; goto out; } - ringbuf->last_retired_head = -1; - ringbuf->head = I915_READ_HEAD(engine); - ringbuf->tail = I915_READ_TAIL(engine) & TAIL_ADDR; - intel_ring_update_space(ringbuf); - intel_engine_init_hangcheck(engine); out: @@ -640,59 +614,25 @@ out: return ret; } -void intel_fini_pipe_control(struct intel_engine_cs *engine) -{ - if (engine->scratch.obj == NULL) - return; - - i915_gem_object_ggtt_unpin(engine->scratch.obj); - drm_gem_object_unreference(&engine->scratch.obj->base); - engine->scratch.obj = NULL; -} - -int intel_init_pipe_control(struct intel_engine_cs *engine, int size) +static void reset_ring_common(struct intel_engine_cs *engine, + struct drm_i915_gem_request *request) { - struct drm_i915_gem_object *obj; - int ret; - - WARN_ON(engine->scratch.obj); + struct intel_ring *ring = request->ring; - obj = i915_gem_object_create_stolen(&engine->i915->drm, size); - if (!obj) - obj = i915_gem_object_create(&engine->i915->drm, size); - if (IS_ERR(obj)) { - DRM_ERROR("Failed to allocate scratch page\n"); - ret = PTR_ERR(obj); - goto err; - } - - ret = i915_gem_obj_ggtt_pin(obj, 4096, PIN_HIGH); - if (ret) - goto err_unref; - - engine->scratch.obj = obj; - engine->scratch.gtt_offset = i915_gem_obj_ggtt_offset(obj); - DRM_DEBUG_DRIVER("%s pipe control offset: 0x%08x\n", - engine->name, engine->scratch.gtt_offset); - return 0; - -err_unref: - drm_gem_object_unreference(&engine->scratch.obj->base); -err: - return ret; + ring->head = request->postfix; + ring->last_retired_head = -1; } static int intel_ring_workarounds_emit(struct drm_i915_gem_request *req) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; struct i915_workarounds *w = &req->i915->workarounds; int ret, i; if (w->count == 0) return 0; - engine->gpu_caches_dirty = true; - ret = intel_ring_flush_all_caches(req); + ret = req->engine->emit_flush(req, EMIT_BARRIER); if (ret) return ret; @@ -700,17 +640,16 @@ static int intel_ring_workarounds_emit(struct drm_i915_gem_request *req) if (ret) return ret; - intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(w->count)); + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(w->count)); for (i = 0; i < w->count; i++) { - intel_ring_emit_reg(engine, w->reg[i].addr); - intel_ring_emit(engine, w->reg[i].value); + intel_ring_emit_reg(ring, w->reg[i].addr); + intel_ring_emit(ring, w->reg[i].value); } - intel_ring_emit(engine, MI_NOOP); + intel_ring_emit(ring, MI_NOOP); - intel_ring_advance(engine); + intel_ring_advance(ring); - engine->gpu_caches_dirty = true; - ret = intel_ring_flush_all_caches(req); + ret = req->engine->emit_flush(req, EMIT_BARRIER); if (ret) return ret; @@ -1022,7 +961,7 @@ static int skl_tune_iz_hashing(struct intel_engine_cs *engine) * Only consider slices where one, and only one, subslice has 7 * EUs */ - if (!is_power_of_2(dev_priv->info.subslice_7eu[i])) + if (!is_power_of_2(INTEL_INFO(dev_priv)->sseu.subslice_7eu[i])) continue; /* @@ -1031,7 +970,7 @@ static int skl_tune_iz_hashing(struct intel_engine_cs *engine) * * -> 0 <= ss <= 3; */ - ss = ffs(dev_priv->info.subslice_7eu[i]) - 1; + ss = ffs(INTEL_INFO(dev_priv)->sseu.subslice_7eu[i]) - 1; vals[i] = 3 - ss; } @@ -1178,8 +1117,8 @@ static int bxt_init_workarounds(struct intel_engine_cs *engine) I915_WRITE(GEN8_L3SQCREG1, L3_GENERAL_PRIO_CREDITS(62) | L3_HIGH_PRIO_CREDITS(2)); - /* WaInsertDummyPushConstPs:bxt */ - if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_B0)) + /* WaToEnableHwFixForPushConstHWBug:bxt */ + if (IS_BXT_REVID(dev_priv, BXT_REVID_C0, REVID_FOREVER)) WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2, GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION); @@ -1222,8 +1161,8 @@ static int kbl_init_workarounds(struct intel_engine_cs *engine) I915_WRITE(GEN8_L3SQCREG4, I915_READ(GEN8_L3SQCREG4) | GEN8_LQSC_RO_PERF_DIS); - /* WaInsertDummyPushConstPs:kbl */ - if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0)) + /* WaToEnableHwFixForPushConstHWBug:kbl */ + if (IS_KBL_REVID(dev_priv, KBL_REVID_C0, REVID_FOREVER)) WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2, GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION); @@ -1329,191 +1268,194 @@ static void render_ring_cleanup(struct intel_engine_cs *engine) { struct drm_i915_private *dev_priv = engine->i915; - if (dev_priv->semaphore_obj) { - i915_gem_object_ggtt_unpin(dev_priv->semaphore_obj); - drm_gem_object_unreference(&dev_priv->semaphore_obj->base); - dev_priv->semaphore_obj = NULL; - } - - intel_fini_pipe_control(engine); + i915_vma_unpin_and_release(&dev_priv->semaphore); } -static int gen8_rcs_signal(struct drm_i915_gem_request *signaller_req, - unsigned int num_dwords) +static int gen8_rcs_signal(struct drm_i915_gem_request *req) { -#define MBOX_UPDATE_DWORDS 8 - struct intel_engine_cs *signaller = signaller_req->engine; - struct drm_i915_private *dev_priv = signaller_req->i915; + struct intel_ring *ring = req->ring; + struct drm_i915_private *dev_priv = req->i915; struct intel_engine_cs *waiter; enum intel_engine_id id; int ret, num_rings; - num_rings = hweight32(INTEL_INFO(dev_priv)->ring_mask); - num_dwords += (num_rings-1) * MBOX_UPDATE_DWORDS; -#undef MBOX_UPDATE_DWORDS - - ret = intel_ring_begin(signaller_req, num_dwords); + num_rings = INTEL_INFO(dev_priv)->num_rings; + ret = intel_ring_begin(req, (num_rings-1) * 8); if (ret) return ret; for_each_engine_id(waiter, dev_priv, id) { - u64 gtt_offset = signaller->semaphore.signal_ggtt[id]; + u64 gtt_offset = req->engine->semaphore.signal_ggtt[id]; if (gtt_offset == MI_SEMAPHORE_SYNC_INVALID) continue; - intel_ring_emit(signaller, GFX_OP_PIPE_CONTROL(6)); - intel_ring_emit(signaller, PIPE_CONTROL_GLOBAL_GTT_IVB | - PIPE_CONTROL_QW_WRITE | - PIPE_CONTROL_CS_STALL); - intel_ring_emit(signaller, lower_32_bits(gtt_offset)); - intel_ring_emit(signaller, upper_32_bits(gtt_offset)); - intel_ring_emit(signaller, signaller_req->seqno); - intel_ring_emit(signaller, 0); - intel_ring_emit(signaller, MI_SEMAPHORE_SIGNAL | - MI_SEMAPHORE_TARGET(waiter->hw_id)); - intel_ring_emit(signaller, 0); + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6)); + intel_ring_emit(ring, + PIPE_CONTROL_GLOBAL_GTT_IVB | + PIPE_CONTROL_QW_WRITE | + PIPE_CONTROL_CS_STALL); + intel_ring_emit(ring, lower_32_bits(gtt_offset)); + intel_ring_emit(ring, upper_32_bits(gtt_offset)); + intel_ring_emit(ring, req->fence.seqno); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, + MI_SEMAPHORE_SIGNAL | + MI_SEMAPHORE_TARGET(waiter->hw_id)); + intel_ring_emit(ring, 0); } + intel_ring_advance(ring); return 0; } -static int gen8_xcs_signal(struct drm_i915_gem_request *signaller_req, - unsigned int num_dwords) +static int gen8_xcs_signal(struct drm_i915_gem_request *req) { -#define MBOX_UPDATE_DWORDS 6 - struct intel_engine_cs *signaller = signaller_req->engine; - struct drm_i915_private *dev_priv = signaller_req->i915; + struct intel_ring *ring = req->ring; + struct drm_i915_private *dev_priv = req->i915; struct intel_engine_cs *waiter; enum intel_engine_id id; int ret, num_rings; - num_rings = hweight32(INTEL_INFO(dev_priv)->ring_mask); - num_dwords += (num_rings-1) * MBOX_UPDATE_DWORDS; -#undef MBOX_UPDATE_DWORDS - - ret = intel_ring_begin(signaller_req, num_dwords); + num_rings = INTEL_INFO(dev_priv)->num_rings; + ret = intel_ring_begin(req, (num_rings-1) * 6); if (ret) return ret; for_each_engine_id(waiter, dev_priv, id) { - u64 gtt_offset = signaller->semaphore.signal_ggtt[id]; + u64 gtt_offset = req->engine->semaphore.signal_ggtt[id]; if (gtt_offset == MI_SEMAPHORE_SYNC_INVALID) continue; - intel_ring_emit(signaller, (MI_FLUSH_DW + 1) | - MI_FLUSH_DW_OP_STOREDW); - intel_ring_emit(signaller, lower_32_bits(gtt_offset) | - MI_FLUSH_DW_USE_GTT); - intel_ring_emit(signaller, upper_32_bits(gtt_offset)); - intel_ring_emit(signaller, signaller_req->seqno); - intel_ring_emit(signaller, MI_SEMAPHORE_SIGNAL | - MI_SEMAPHORE_TARGET(waiter->hw_id)); - intel_ring_emit(signaller, 0); + intel_ring_emit(ring, + (MI_FLUSH_DW + 1) | MI_FLUSH_DW_OP_STOREDW); + intel_ring_emit(ring, + lower_32_bits(gtt_offset) | + MI_FLUSH_DW_USE_GTT); + intel_ring_emit(ring, upper_32_bits(gtt_offset)); + intel_ring_emit(ring, req->fence.seqno); + intel_ring_emit(ring, + MI_SEMAPHORE_SIGNAL | + MI_SEMAPHORE_TARGET(waiter->hw_id)); + intel_ring_emit(ring, 0); } + intel_ring_advance(ring); return 0; } -static int gen6_signal(struct drm_i915_gem_request *signaller_req, - unsigned int num_dwords) +static int gen6_signal(struct drm_i915_gem_request *req) { - struct intel_engine_cs *signaller = signaller_req->engine; - struct drm_i915_private *dev_priv = signaller_req->i915; - struct intel_engine_cs *useless; - enum intel_engine_id id; + struct intel_ring *ring = req->ring; + struct drm_i915_private *dev_priv = req->i915; + struct intel_engine_cs *engine; int ret, num_rings; -#define MBOX_UPDATE_DWORDS 3 - num_rings = hweight32(INTEL_INFO(dev_priv)->ring_mask); - num_dwords += round_up((num_rings-1) * MBOX_UPDATE_DWORDS, 2); -#undef MBOX_UPDATE_DWORDS - - ret = intel_ring_begin(signaller_req, num_dwords); + num_rings = INTEL_INFO(dev_priv)->num_rings; + ret = intel_ring_begin(req, round_up((num_rings-1) * 3, 2)); if (ret) return ret; - for_each_engine_id(useless, dev_priv, id) { - i915_reg_t mbox_reg = signaller->semaphore.mbox.signal[id]; + for_each_engine(engine, dev_priv) { + i915_reg_t mbox_reg; + + if (!(BIT(engine->hw_id) & GEN6_SEMAPHORES_MASK)) + continue; + mbox_reg = req->engine->semaphore.mbox.signal[engine->hw_id]; if (i915_mmio_reg_valid(mbox_reg)) { - intel_ring_emit(signaller, MI_LOAD_REGISTER_IMM(1)); - intel_ring_emit_reg(signaller, mbox_reg); - intel_ring_emit(signaller, signaller_req->seqno); + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1)); + intel_ring_emit_reg(ring, mbox_reg); + intel_ring_emit(ring, req->fence.seqno); } } /* If num_dwords was rounded, make sure the tail pointer is correct */ if (num_rings % 2 == 0) - intel_ring_emit(signaller, MI_NOOP); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); + + return 0; +} + +static void i9xx_submit_request(struct drm_i915_gem_request *request) +{ + struct drm_i915_private *dev_priv = request->i915; + + I915_WRITE_TAIL(request->engine, + intel_ring_offset(request->ring, request->tail)); +} + +static int i9xx_emit_request(struct drm_i915_gem_request *req) +{ + struct intel_ring *ring = req->ring; + int ret; + + ret = intel_ring_begin(req, 4); + if (ret) + return ret; + + intel_ring_emit(ring, MI_STORE_DWORD_INDEX); + intel_ring_emit(ring, I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT); + intel_ring_emit(ring, req->fence.seqno); + intel_ring_emit(ring, MI_USER_INTERRUPT); + intel_ring_advance(ring); + + req->tail = ring->tail; return 0; } /** - * gen6_add_request - Update the semaphore mailbox registers + * gen6_sema_emit_request - Update the semaphore mailbox registers * * @request - request to write to the ring * * Update the mailbox registers in the *other* rings with the current seqno. * This acts like a signal in the canonical semaphore. */ -static int -gen6_add_request(struct drm_i915_gem_request *req) +static int gen6_sema_emit_request(struct drm_i915_gem_request *req) { - struct intel_engine_cs *engine = req->engine; int ret; - if (engine->semaphore.signal) - ret = engine->semaphore.signal(req, 4); - else - ret = intel_ring_begin(req, 4); - + ret = req->engine->semaphore.signal(req); if (ret) return ret; - intel_ring_emit(engine, MI_STORE_DWORD_INDEX); - intel_ring_emit(engine, - I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT); - intel_ring_emit(engine, req->seqno); - intel_ring_emit(engine, MI_USER_INTERRUPT); - __intel_ring_advance(engine); - - return 0; + return i9xx_emit_request(req); } -static int -gen8_render_add_request(struct drm_i915_gem_request *req) +static int gen8_render_emit_request(struct drm_i915_gem_request *req) { struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; int ret; - if (engine->semaphore.signal) - ret = engine->semaphore.signal(req, 8); - else - ret = intel_ring_begin(req, 8); + if (engine->semaphore.signal) { + ret = engine->semaphore.signal(req); + if (ret) + return ret; + } + + ret = intel_ring_begin(req, 8); if (ret) return ret; - intel_ring_emit(engine, GFX_OP_PIPE_CONTROL(6)); - intel_ring_emit(engine, (PIPE_CONTROL_GLOBAL_GTT_IVB | - PIPE_CONTROL_CS_STALL | - PIPE_CONTROL_QW_WRITE)); - intel_ring_emit(engine, intel_hws_seqno_address(req->engine)); - intel_ring_emit(engine, 0); - intel_ring_emit(engine, i915_gem_request_get_seqno(req)); + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6)); + intel_ring_emit(ring, (PIPE_CONTROL_GLOBAL_GTT_IVB | + PIPE_CONTROL_CS_STALL | + PIPE_CONTROL_QW_WRITE)); + intel_ring_emit(ring, intel_hws_seqno_address(engine)); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, i915_gem_request_get_seqno(req)); /* We're thrashing one dword of HWS. */ - intel_ring_emit(engine, 0); - intel_ring_emit(engine, MI_USER_INTERRUPT); - intel_ring_emit(engine, MI_NOOP); - __intel_ring_advance(engine); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, MI_USER_INTERRUPT); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); - return 0; -} + req->tail = ring->tail; -static inline bool i915_gem_has_seqno_wrapped(struct drm_i915_private *dev_priv, - u32 seqno) -{ - return dev_priv->last_seqno < seqno; + return 0; } /** @@ -1525,82 +1467,71 @@ static inline bool i915_gem_has_seqno_wrapped(struct drm_i915_private *dev_priv, */ static int -gen8_ring_sync(struct drm_i915_gem_request *waiter_req, - struct intel_engine_cs *signaller, - u32 seqno) +gen8_ring_sync_to(struct drm_i915_gem_request *req, + struct drm_i915_gem_request *signal) { - struct intel_engine_cs *waiter = waiter_req->engine; - struct drm_i915_private *dev_priv = waiter_req->i915; - u64 offset = GEN8_WAIT_OFFSET(waiter, signaller->id); + struct intel_ring *ring = req->ring; + struct drm_i915_private *dev_priv = req->i915; + u64 offset = GEN8_WAIT_OFFSET(req->engine, signal->engine->id); struct i915_hw_ppgtt *ppgtt; int ret; - ret = intel_ring_begin(waiter_req, 4); + ret = intel_ring_begin(req, 4); if (ret) return ret; - intel_ring_emit(waiter, MI_SEMAPHORE_WAIT | - MI_SEMAPHORE_GLOBAL_GTT | - MI_SEMAPHORE_SAD_GTE_SDD); - intel_ring_emit(waiter, seqno); - intel_ring_emit(waiter, lower_32_bits(offset)); - intel_ring_emit(waiter, upper_32_bits(offset)); - intel_ring_advance(waiter); + intel_ring_emit(ring, + MI_SEMAPHORE_WAIT | + MI_SEMAPHORE_GLOBAL_GTT | + MI_SEMAPHORE_SAD_GTE_SDD); + intel_ring_emit(ring, signal->fence.seqno); + intel_ring_emit(ring, lower_32_bits(offset)); + intel_ring_emit(ring, upper_32_bits(offset)); + intel_ring_advance(ring); /* When the !RCS engines idle waiting upon a semaphore, they lose their * pagetables and we must reload them before executing the batch. * We do this on the i915_switch_context() following the wait and * before the dispatch. */ - ppgtt = waiter_req->ctx->ppgtt; - if (ppgtt && waiter_req->engine->id != RCS) - ppgtt->pd_dirty_rings |= intel_engine_flag(waiter_req->engine); + ppgtt = req->ctx->ppgtt; + if (ppgtt && req->engine->id != RCS) + ppgtt->pd_dirty_rings |= intel_engine_flag(req->engine); return 0; } static int -gen6_ring_sync(struct drm_i915_gem_request *waiter_req, - struct intel_engine_cs *signaller, - u32 seqno) +gen6_ring_sync_to(struct drm_i915_gem_request *req, + struct drm_i915_gem_request *signal) { - struct intel_engine_cs *waiter = waiter_req->engine; + struct intel_ring *ring = req->ring; u32 dw1 = MI_SEMAPHORE_MBOX | MI_SEMAPHORE_COMPARE | MI_SEMAPHORE_REGISTER; - u32 wait_mbox = signaller->semaphore.mbox.wait[waiter->id]; + u32 wait_mbox = signal->engine->semaphore.mbox.wait[req->engine->hw_id]; int ret; - /* Throughout all of the GEM code, seqno passed implies our current - * seqno is >= the last seqno executed. However for hardware the - * comparison is strictly greater than. - */ - seqno -= 1; - WARN_ON(wait_mbox == MI_SEMAPHORE_SYNC_INVALID); - ret = intel_ring_begin(waiter_req, 4); + ret = intel_ring_begin(req, 4); if (ret) return ret; - /* If seqno wrap happened, omit the wait with no-ops */ - if (likely(!i915_gem_has_seqno_wrapped(waiter_req->i915, seqno))) { - intel_ring_emit(waiter, dw1 | wait_mbox); - intel_ring_emit(waiter, seqno); - intel_ring_emit(waiter, 0); - intel_ring_emit(waiter, MI_NOOP); - } else { - intel_ring_emit(waiter, MI_NOOP); - intel_ring_emit(waiter, MI_NOOP); - intel_ring_emit(waiter, MI_NOOP); - intel_ring_emit(waiter, MI_NOOP); - } - intel_ring_advance(waiter); + intel_ring_emit(ring, dw1 | wait_mbox); + /* Throughout all of the GEM code, seqno passed implies our current + * seqno is >= the last seqno executed. However for hardware the + * comparison is strictly greater than. + */ + intel_ring_emit(ring, signal->fence.seqno - 1); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); return 0; } static void -gen5_seqno_barrier(struct intel_engine_cs *ring) +gen5_seqno_barrier(struct intel_engine_cs *engine) { /* MI_STORE are internally buffered by the GPU and not flushed * either by MI_FLUSH or SyncFlush or any other combination of @@ -1693,40 +1624,18 @@ i8xx_irq_disable(struct intel_engine_cs *engine) } static int -bsd_ring_flush(struct drm_i915_gem_request *req, - u32 invalidate_domains, - u32 flush_domains) +bsd_ring_flush(struct drm_i915_gem_request *req, u32 mode) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; int ret; ret = intel_ring_begin(req, 2); if (ret) return ret; - intel_ring_emit(engine, MI_FLUSH); - intel_ring_emit(engine, MI_NOOP); - intel_ring_advance(engine); - return 0; -} - -static int -i9xx_add_request(struct drm_i915_gem_request *req) -{ - struct intel_engine_cs *engine = req->engine; - int ret; - - ret = intel_ring_begin(req, 4); - if (ret) - return ret; - - intel_ring_emit(engine, MI_STORE_DWORD_INDEX); - intel_ring_emit(engine, - I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT); - intel_ring_emit(engine, req->seqno); - intel_ring_emit(engine, MI_USER_INTERRUPT); - __intel_ring_advance(engine); - + intel_ring_emit(ring, MI_FLUSH); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); return 0; } @@ -1788,24 +1697,24 @@ gen8_irq_disable(struct intel_engine_cs *engine) } static int -i965_dispatch_execbuffer(struct drm_i915_gem_request *req, - u64 offset, u32 length, - unsigned dispatch_flags) +i965_emit_bb_start(struct drm_i915_gem_request *req, + u64 offset, u32 length, + unsigned int dispatch_flags) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; int ret; ret = intel_ring_begin(req, 2); if (ret) return ret; - intel_ring_emit(engine, + intel_ring_emit(ring, MI_BATCH_BUFFER_START | MI_BATCH_GTT | (dispatch_flags & I915_DISPATCH_SECURE ? 0 : MI_BATCH_NON_SECURE_I965)); - intel_ring_emit(engine, offset); - intel_ring_advance(engine); + intel_ring_emit(ring, offset); + intel_ring_advance(ring); return 0; } @@ -1815,12 +1724,12 @@ i965_dispatch_execbuffer(struct drm_i915_gem_request *req, #define I830_TLB_ENTRIES (2) #define I830_WA_SIZE max(I830_TLB_ENTRIES*4096, I830_BATCH_LIMIT) static int -i830_dispatch_execbuffer(struct drm_i915_gem_request *req, - u64 offset, u32 len, - unsigned dispatch_flags) +i830_emit_bb_start(struct drm_i915_gem_request *req, + u64 offset, u32 len, + unsigned int dispatch_flags) { - struct intel_engine_cs *engine = req->engine; - u32 cs_offset = engine->scratch.gtt_offset; + struct intel_ring *ring = req->ring; + u32 cs_offset = i915_ggtt_offset(req->engine->scratch); int ret; ret = intel_ring_begin(req, 6); @@ -1828,13 +1737,13 @@ i830_dispatch_execbuffer(struct drm_i915_gem_request *req, return ret; /* Evict the invalid PTE TLBs */ - intel_ring_emit(engine, COLOR_BLT_CMD | BLT_WRITE_RGBA); - intel_ring_emit(engine, BLT_DEPTH_32 | BLT_ROP_COLOR_COPY | 4096); - intel_ring_emit(engine, I830_TLB_ENTRIES << 16 | 4); /* load each page */ - intel_ring_emit(engine, cs_offset); - intel_ring_emit(engine, 0xdeadbeef); - intel_ring_emit(engine, MI_NOOP); - intel_ring_advance(engine); + intel_ring_emit(ring, COLOR_BLT_CMD | BLT_WRITE_RGBA); + intel_ring_emit(ring, BLT_DEPTH_32 | BLT_ROP_COLOR_COPY | 4096); + intel_ring_emit(ring, I830_TLB_ENTRIES << 16 | 4); /* load each page */ + intel_ring_emit(ring, cs_offset); + intel_ring_emit(ring, 0xdeadbeef); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); if ((dispatch_flags & I915_DISPATCH_PINNED) == 0) { if (len > I830_BATCH_LIMIT) @@ -1848,17 +1757,17 @@ i830_dispatch_execbuffer(struct drm_i915_gem_request *req, * stable batch scratch bo area (so that the CS never * stumbles over its tlb invalidation bug) ... */ - intel_ring_emit(engine, SRC_COPY_BLT_CMD | BLT_WRITE_RGBA); - intel_ring_emit(engine, + intel_ring_emit(ring, SRC_COPY_BLT_CMD | BLT_WRITE_RGBA); + intel_ring_emit(ring, BLT_DEPTH_32 | BLT_ROP_SRC_COPY | 4096); - intel_ring_emit(engine, DIV_ROUND_UP(len, 4096) << 16 | 4096); - intel_ring_emit(engine, cs_offset); - intel_ring_emit(engine, 4096); - intel_ring_emit(engine, offset); + intel_ring_emit(ring, DIV_ROUND_UP(len, 4096) << 16 | 4096); + intel_ring_emit(ring, cs_offset); + intel_ring_emit(ring, 4096); + intel_ring_emit(ring, offset); - intel_ring_emit(engine, MI_FLUSH); - intel_ring_emit(engine, MI_NOOP); - intel_ring_advance(engine); + intel_ring_emit(ring, MI_FLUSH); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); /* ... and execute it. */ offset = cs_offset; @@ -1868,30 +1777,30 @@ i830_dispatch_execbuffer(struct drm_i915_gem_request *req, if (ret) return ret; - intel_ring_emit(engine, MI_BATCH_BUFFER_START | MI_BATCH_GTT); - intel_ring_emit(engine, offset | (dispatch_flags & I915_DISPATCH_SECURE ? - 0 : MI_BATCH_NON_SECURE)); - intel_ring_advance(engine); + intel_ring_emit(ring, MI_BATCH_BUFFER_START | MI_BATCH_GTT); + intel_ring_emit(ring, offset | (dispatch_flags & I915_DISPATCH_SECURE ? + 0 : MI_BATCH_NON_SECURE)); + intel_ring_advance(ring); return 0; } static int -i915_dispatch_execbuffer(struct drm_i915_gem_request *req, - u64 offset, u32 len, - unsigned dispatch_flags) +i915_emit_bb_start(struct drm_i915_gem_request *req, + u64 offset, u32 len, + unsigned int dispatch_flags) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; int ret; ret = intel_ring_begin(req, 2); if (ret) return ret; - intel_ring_emit(engine, MI_BATCH_BUFFER_START | MI_BATCH_GTT); - intel_ring_emit(engine, offset | (dispatch_flags & I915_DISPATCH_SECURE ? - 0 : MI_BATCH_NON_SECURE)); - intel_ring_advance(engine); + intel_ring_emit(ring, MI_BATCH_BUFFER_START | MI_BATCH_GTT); + intel_ring_emit(ring, offset | (dispatch_flags & I915_DISPATCH_SECURE ? + 0 : MI_BATCH_NON_SECURE)); + intel_ring_advance(ring); return 0; } @@ -1909,79 +1818,79 @@ static void cleanup_phys_status_page(struct intel_engine_cs *engine) static void cleanup_status_page(struct intel_engine_cs *engine) { - struct drm_i915_gem_object *obj; + struct i915_vma *vma; - obj = engine->status_page.obj; - if (obj == NULL) + vma = fetch_and_zero(&engine->status_page.vma); + if (!vma) return; - kunmap(sg_page(obj->pages->sgl)); - i915_gem_object_ggtt_unpin(obj); - drm_gem_object_unreference(&obj->base); - engine->status_page.obj = NULL; + i915_vma_unpin(vma); + i915_gem_object_unpin_map(vma->obj); + i915_vma_put(vma); } static int init_status_page(struct intel_engine_cs *engine) { - struct drm_i915_gem_object *obj = engine->status_page.obj; - - if (obj == NULL) { - unsigned flags; - int ret; + struct drm_i915_gem_object *obj; + struct i915_vma *vma; + unsigned int flags; + int ret; - obj = i915_gem_object_create(&engine->i915->drm, 4096); - if (IS_ERR(obj)) { - DRM_ERROR("Failed to allocate status page\n"); - return PTR_ERR(obj); - } + obj = i915_gem_object_create(&engine->i915->drm, 4096); + if (IS_ERR(obj)) { + DRM_ERROR("Failed to allocate status page\n"); + return PTR_ERR(obj); + } - ret = i915_gem_object_set_cache_level(obj, I915_CACHE_LLC); - if (ret) - goto err_unref; - - flags = 0; - if (!HAS_LLC(engine->i915)) - /* On g33, we cannot place HWS above 256MiB, so - * restrict its pinning to the low mappable arena. - * Though this restriction is not documented for - * gen4, gen5, or byt, they also behave similarly - * and hang if the HWS is placed at the top of the - * GTT. To generalise, it appears that all !llc - * platforms have issues with us placing the HWS - * above the mappable region (even though we never - * actualy map it). - */ - flags |= PIN_MAPPABLE; - ret = i915_gem_obj_ggtt_pin(obj, 4096, flags); - if (ret) { -err_unref: - drm_gem_object_unreference(&obj->base); - return ret; - } + ret = i915_gem_object_set_cache_level(obj, I915_CACHE_LLC); + if (ret) + goto err; - engine->status_page.obj = obj; + vma = i915_vma_create(obj, &engine->i915->ggtt.base, NULL); + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); + goto err; } - engine->status_page.gfx_addr = i915_gem_obj_ggtt_offset(obj); - engine->status_page.page_addr = kmap(sg_page(obj->pages->sgl)); - memset(engine->status_page.page_addr, 0, PAGE_SIZE); + flags = PIN_GLOBAL; + if (!HAS_LLC(engine->i915)) + /* On g33, we cannot place HWS above 256MiB, so + * restrict its pinning to the low mappable arena. + * Though this restriction is not documented for + * gen4, gen5, or byt, they also behave similarly + * and hang if the HWS is placed at the top of the + * GTT. To generalise, it appears that all !llc + * platforms have issues with us placing the HWS + * above the mappable region (even though we never + * actualy map it). + */ + flags |= PIN_MAPPABLE; + ret = i915_vma_pin(vma, 0, 4096, flags); + if (ret) + goto err; - DRM_DEBUG_DRIVER("%s hws offset: 0x%08x\n", - engine->name, engine->status_page.gfx_addr); + engine->status_page.vma = vma; + engine->status_page.ggtt_offset = i915_ggtt_offset(vma); + engine->status_page.page_addr = + i915_gem_object_pin_map(obj, I915_MAP_WB); + DRM_DEBUG_DRIVER("%s hws offset: 0x%08x\n", + engine->name, i915_ggtt_offset(vma)); return 0; + +err: + i915_gem_object_put(obj); + return ret; } static int init_phys_status_page(struct intel_engine_cs *engine) { struct drm_i915_private *dev_priv = engine->i915; - if (!dev_priv->status_page_dmah) { - dev_priv->status_page_dmah = - drm_pci_alloc(&dev_priv->drm, PAGE_SIZE, PAGE_SIZE); - if (!dev_priv->status_page_dmah) - return -ENOMEM; - } + dev_priv->status_page_dmah = + drm_pci_alloc(&dev_priv->drm, PAGE_SIZE, PAGE_SIZE); + if (!dev_priv->status_page_dmah) + return -ENOMEM; engine->status_page.page_addr = dev_priv->status_page_dmah->vaddr; memset(engine->status_page.page_addr, 0, PAGE_SIZE); @@ -1989,115 +1898,105 @@ static int init_phys_status_page(struct intel_engine_cs *engine) return 0; } -void intel_unpin_ringbuffer_obj(struct intel_ringbuffer *ringbuf) -{ - GEM_BUG_ON(ringbuf->vma == NULL); - GEM_BUG_ON(ringbuf->virtual_start == NULL); - - if (HAS_LLC(ringbuf->obj->base.dev) && !ringbuf->obj->stolen) - i915_gem_object_unpin_map(ringbuf->obj); - else - i915_vma_unpin_iomap(ringbuf->vma); - ringbuf->virtual_start = NULL; - - i915_gem_object_ggtt_unpin(ringbuf->obj); - ringbuf->vma = NULL; -} - -int intel_pin_and_map_ringbuffer_obj(struct drm_i915_private *dev_priv, - struct intel_ringbuffer *ringbuf) +int intel_ring_pin(struct intel_ring *ring) { - struct drm_i915_gem_object *obj = ringbuf->obj; /* Ring wraparound at offset 0 sometimes hangs. No idea why. */ - unsigned flags = PIN_OFFSET_BIAS | 4096; + unsigned int flags = PIN_GLOBAL | PIN_OFFSET_BIAS | 4096; + enum i915_map_type map; + struct i915_vma *vma = ring->vma; void *addr; int ret; - if (HAS_LLC(dev_priv) && !obj->stolen) { - ret = i915_gem_obj_ggtt_pin(obj, PAGE_SIZE, flags); - if (ret) - return ret; + GEM_BUG_ON(ring->vaddr); - ret = i915_gem_object_set_to_cpu_domain(obj, true); - if (ret) - goto err_unpin; + map = HAS_LLC(ring->engine->i915) ? I915_MAP_WB : I915_MAP_WC; - addr = i915_gem_object_pin_map(obj); - if (IS_ERR(addr)) { - ret = PTR_ERR(addr); - goto err_unpin; - } - } else { - ret = i915_gem_obj_ggtt_pin(obj, PAGE_SIZE, - flags | PIN_MAPPABLE); - if (ret) - return ret; + if (vma->obj->stolen) + flags |= PIN_MAPPABLE; - ret = i915_gem_object_set_to_gtt_domain(obj, true); - if (ret) - goto err_unpin; + if (!(vma->flags & I915_VMA_GLOBAL_BIND)) { + if (flags & PIN_MAPPABLE || map == I915_MAP_WC) + ret = i915_gem_object_set_to_gtt_domain(vma->obj, true); + else + ret = i915_gem_object_set_to_cpu_domain(vma->obj, true); + if (unlikely(ret)) + return ret; + } - /* Access through the GTT requires the device to be awake. */ - assert_rpm_wakelock_held(dev_priv); + ret = i915_vma_pin(vma, 0, PAGE_SIZE, flags); + if (unlikely(ret)) + return ret; - addr = i915_vma_pin_iomap(i915_gem_obj_to_ggtt(obj)); - if (IS_ERR(addr)) { - ret = PTR_ERR(addr); - goto err_unpin; - } - } + if (i915_vma_is_map_and_fenceable(vma)) + addr = (void __force *)i915_vma_pin_iomap(vma); + else + addr = i915_gem_object_pin_map(vma->obj, map); + if (IS_ERR(addr)) + goto err; - ringbuf->virtual_start = addr; - ringbuf->vma = i915_gem_obj_to_ggtt(obj); + ring->vaddr = addr; return 0; -err_unpin: - i915_gem_object_ggtt_unpin(obj); - return ret; +err: + i915_vma_unpin(vma); + return PTR_ERR(addr); } -static void intel_destroy_ringbuffer_obj(struct intel_ringbuffer *ringbuf) +void intel_ring_unpin(struct intel_ring *ring) { - drm_gem_object_unreference(&ringbuf->obj->base); - ringbuf->obj = NULL; + GEM_BUG_ON(!ring->vma); + GEM_BUG_ON(!ring->vaddr); + + if (i915_vma_is_map_and_fenceable(ring->vma)) + i915_vma_unpin_iomap(ring->vma); + else + i915_gem_object_unpin_map(ring->vma->obj); + ring->vaddr = NULL; + + i915_vma_unpin(ring->vma); } -static int intel_alloc_ringbuffer_obj(struct drm_device *dev, - struct intel_ringbuffer *ringbuf) +static struct i915_vma * +intel_ring_create_vma(struct drm_i915_private *dev_priv, int size) { struct drm_i915_gem_object *obj; + struct i915_vma *vma; - obj = NULL; - if (!HAS_LLC(dev)) - obj = i915_gem_object_create_stolen(dev, ringbuf->size); - if (obj == NULL) - obj = i915_gem_object_create(dev, ringbuf->size); + obj = i915_gem_object_create_stolen(&dev_priv->drm, size); + if (!obj) + obj = i915_gem_object_create(&dev_priv->drm, size); if (IS_ERR(obj)) - return PTR_ERR(obj); + return ERR_CAST(obj); /* mark ring buffers as read-only from GPU side by default */ obj->gt_ro = 1; - ringbuf->obj = obj; + vma = i915_vma_create(obj, &dev_priv->ggtt.base, NULL); + if (IS_ERR(vma)) + goto err; - return 0; + return vma; + +err: + i915_gem_object_put(obj); + return vma; } -struct intel_ringbuffer * -intel_engine_create_ringbuffer(struct intel_engine_cs *engine, int size) +struct intel_ring * +intel_engine_create_ring(struct intel_engine_cs *engine, int size) { - struct intel_ringbuffer *ring; - int ret; + struct intel_ring *ring; + struct i915_vma *vma; + + GEM_BUG_ON(!is_power_of_2(size)); ring = kzalloc(sizeof(*ring), GFP_KERNEL); - if (ring == NULL) { - DRM_DEBUG_DRIVER("Failed to allocate ringbuffer %s\n", - engine->name); + if (!ring) return ERR_PTR(-ENOMEM); - } ring->engine = engine; - list_add(&ring->link, &engine->buffers); + + INIT_LIST_HEAD(&ring->request_list); ring->size = size; /* Workaround an erratum on the i830 which causes a hang if @@ -2111,23 +2010,20 @@ intel_engine_create_ringbuffer(struct intel_engine_cs *engine, int size) ring->last_retired_head = -1; intel_ring_update_space(ring); - ret = intel_alloc_ringbuffer_obj(&engine->i915->drm, ring); - if (ret) { - DRM_DEBUG_DRIVER("Failed to allocate ringbuffer %s: %d\n", - engine->name, ret); - list_del(&ring->link); + vma = intel_ring_create_vma(engine->i915, size); + if (IS_ERR(vma)) { kfree(ring); - return ERR_PTR(ret); + return ERR_CAST(vma); } + ring->vma = vma; return ring; } void -intel_ringbuffer_free(struct intel_ringbuffer *ring) +intel_ring_free(struct intel_ring *ring) { - intel_destroy_ringbuffer_obj(ring); - list_del(&ring->link); + i915_vma_put(ring->vma); kfree(ring); } @@ -2143,7 +2039,12 @@ static int intel_ring_context_pin(struct i915_gem_context *ctx, return 0; if (ce->state) { - ret = i915_gem_obj_ggtt_pin(ce->state, ctx->ggtt_alignment, 0); + ret = i915_gem_object_set_to_gtt_domain(ce->state->obj, false); + if (ret) + goto error; + + ret = i915_vma_pin(ce->state, 0, ctx->ggtt_alignment, + PIN_GLOBAL | PIN_HIGH); if (ret) goto error; } @@ -2158,7 +2059,7 @@ static int intel_ring_context_pin(struct i915_gem_context *ctx, if (ctx == ctx->i915->kernel_context) ce->initialised = true; - i915_gem_context_reference(ctx); + i915_gem_context_get(ctx); return 0; error: @@ -2177,30 +2078,25 @@ static void intel_ring_context_unpin(struct i915_gem_context *ctx, return; if (ce->state) - i915_gem_object_ggtt_unpin(ce->state); + i915_vma_unpin(ce->state); - i915_gem_context_unreference(ctx); + i915_gem_context_put(ctx); } -static int intel_init_ring_buffer(struct drm_device *dev, - struct intel_engine_cs *engine) +static int intel_init_ring_buffer(struct intel_engine_cs *engine) { - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_ringbuffer *ringbuf; + struct drm_i915_private *dev_priv = engine->i915; + struct intel_ring *ring; int ret; WARN_ON(engine->buffer); - engine->i915 = dev_priv; - INIT_LIST_HEAD(&engine->active_list); - INIT_LIST_HEAD(&engine->request_list); - INIT_LIST_HEAD(&engine->execlist_queue); - INIT_LIST_HEAD(&engine->buffers); - i915_gem_batch_pool_init(dev, &engine->batch_pool); + intel_engine_setup_common(engine); + memset(engine->semaphore.sync_seqno, 0, sizeof(engine->semaphore.sync_seqno)); - ret = intel_engine_init_breadcrumbs(engine); + ret = intel_engine_init_common(engine); if (ret) goto error; @@ -2215,44 +2111,38 @@ static int intel_init_ring_buffer(struct drm_device *dev, if (ret) goto error; - ringbuf = intel_engine_create_ringbuffer(engine, 32 * PAGE_SIZE); - if (IS_ERR(ringbuf)) { - ret = PTR_ERR(ringbuf); + ring = intel_engine_create_ring(engine, 32 * PAGE_SIZE); + if (IS_ERR(ring)) { + ret = PTR_ERR(ring); goto error; } - engine->buffer = ringbuf; - if (I915_NEED_GFX_HWS(dev_priv)) { - ret = init_status_page(engine); + if (HWS_NEEDS_PHYSICAL(dev_priv)) { + WARN_ON(engine->id != RCS); + ret = init_phys_status_page(engine); if (ret) goto error; } else { - WARN_ON(engine->id != RCS); - ret = init_phys_status_page(engine); + ret = init_status_page(engine); if (ret) goto error; } - ret = intel_pin_and_map_ringbuffer_obj(dev_priv, ringbuf); + ret = intel_ring_pin(ring); if (ret) { - DRM_ERROR("Failed to pin and map ringbuffer %s: %d\n", - engine->name, ret); - intel_destroy_ringbuffer_obj(ringbuf); + intel_ring_free(ring); goto error; } - - ret = i915_cmd_parser_init_ring(engine); - if (ret) - goto error; + engine->buffer = ring; return 0; error: - intel_cleanup_engine(engine); + intel_engine_cleanup(engine); return ret; } -void intel_cleanup_engine(struct intel_engine_cs *engine) +void intel_engine_cleanup(struct intel_engine_cs *engine) { struct drm_i915_private *dev_priv; @@ -2262,49 +2152,39 @@ void intel_cleanup_engine(struct intel_engine_cs *engine) dev_priv = engine->i915; if (engine->buffer) { - intel_stop_engine(engine); - WARN_ON(!IS_GEN2(dev_priv) && (I915_READ_MODE(engine) & MODE_IDLE) == 0); + WARN_ON(INTEL_GEN(dev_priv) > 2 && + (I915_READ_MODE(engine) & MODE_IDLE) == 0); - intel_unpin_ringbuffer_obj(engine->buffer); - intel_ringbuffer_free(engine->buffer); + intel_ring_unpin(engine->buffer); + intel_ring_free(engine->buffer); engine->buffer = NULL; } if (engine->cleanup) engine->cleanup(engine); - if (I915_NEED_GFX_HWS(dev_priv)) { - cleanup_status_page(engine); - } else { + if (HWS_NEEDS_PHYSICAL(dev_priv)) { WARN_ON(engine->id != RCS); cleanup_phys_status_page(engine); + } else { + cleanup_status_page(engine); } - i915_cmd_parser_fini_ring(engine); - i915_gem_batch_pool_fini(&engine->batch_pool); - intel_engine_fini_breadcrumbs(engine); + intel_engine_cleanup_common(engine); intel_ring_context_unpin(dev_priv->kernel_context, engine); engine->i915 = NULL; } -int intel_engine_idle(struct intel_engine_cs *engine) +void intel_legacy_submission_resume(struct drm_i915_private *dev_priv) { - struct drm_i915_gem_request *req; - - /* Wait upon the last request to be completed */ - if (list_empty(&engine->request_list)) - return 0; + struct intel_engine_cs *engine; - req = list_entry(engine->request_list.prev, - struct drm_i915_gem_request, - list); - - /* Make sure we do not trigger any retires */ - return __i915_wait_request(req, - req->i915->mm.interruptible, - NULL, NULL); + for_each_engine(engine, dev_priv) { + engine->buffer->head = engine->buffer->tail; + engine->buffer->last_retired_head = -1; + } } int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request) @@ -2317,7 +2197,7 @@ int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request) */ request->reserved_space += LEGACY_REQUEST_SIZE; - request->ringbuf = request->engine->buffer; + request->ring = request->engine->buffer; ret = intel_ring_begin(request, 0); if (ret) @@ -2329,12 +2209,12 @@ int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request) static int wait_for_space(struct drm_i915_gem_request *req, int bytes) { - struct intel_ringbuffer *ringbuf = req->ringbuf; - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; struct drm_i915_gem_request *target; + int ret; - intel_ring_update_space(ringbuf); - if (ringbuf->space >= bytes) + intel_ring_update_space(ring); + if (ring->space >= bytes) return 0; /* @@ -2348,35 +2228,37 @@ static int wait_for_space(struct drm_i915_gem_request *req, int bytes) */ GEM_BUG_ON(!req->reserved_space); - list_for_each_entry(target, &engine->request_list, list) { + list_for_each_entry(target, &ring->request_list, ring_link) { unsigned space; - /* - * The request queue is per-engine, so can contain requests - * from multiple ringbuffers. Here, we must ignore any that - * aren't from the ringbuffer we're considering. - */ - if (target->ringbuf != ringbuf) - continue; - /* Would completion of this request free enough space? */ - space = __intel_ring_space(target->postfix, ringbuf->tail, - ringbuf->size); + space = __intel_ring_space(target->postfix, ring->tail, + ring->size); if (space >= bytes) break; } - if (WARN_ON(&target->list == &engine->request_list)) + if (WARN_ON(&target->ring_link == &ring->request_list)) return -ENOSPC; - return i915_wait_request(target); + ret = i915_wait_request(target, + I915_WAIT_INTERRUPTIBLE | I915_WAIT_LOCKED, + NULL, NO_WAITBOOST); + if (ret) + return ret; + + i915_gem_request_retire_upto(target); + + intel_ring_update_space(ring); + GEM_BUG_ON(ring->space < bytes); + return 0; } int intel_ring_begin(struct drm_i915_gem_request *req, int num_dwords) { - struct intel_ringbuffer *ringbuf = req->ringbuf; - int remain_actual = ringbuf->size - ringbuf->tail; - int remain_usable = ringbuf->effective_size - ringbuf->tail; + struct intel_ring *ring = req->ring; + int remain_actual = ring->size - ring->tail; + int remain_usable = ring->effective_size - ring->tail; int bytes = num_dwords * sizeof(u32); int total_bytes, wait_bytes; bool need_wrap = false; @@ -2403,37 +2285,33 @@ int intel_ring_begin(struct drm_i915_gem_request *req, int num_dwords) wait_bytes = total_bytes; } - if (wait_bytes > ringbuf->space) { + if (wait_bytes > ring->space) { int ret = wait_for_space(req, wait_bytes); if (unlikely(ret)) return ret; - - intel_ring_update_space(ringbuf); - if (unlikely(ringbuf->space < wait_bytes)) - return -EAGAIN; } if (unlikely(need_wrap)) { - GEM_BUG_ON(remain_actual > ringbuf->space); - GEM_BUG_ON(ringbuf->tail + remain_actual > ringbuf->size); + GEM_BUG_ON(remain_actual > ring->space); + GEM_BUG_ON(ring->tail + remain_actual > ring->size); /* Fill the tail with MI_NOOP */ - memset(ringbuf->virtual_start + ringbuf->tail, - 0, remain_actual); - ringbuf->tail = 0; - ringbuf->space -= remain_actual; + memset(ring->vaddr + ring->tail, 0, remain_actual); + ring->tail = 0; + ring->space -= remain_actual; } - ringbuf->space -= bytes; - GEM_BUG_ON(ringbuf->space < 0); + ring->space -= bytes; + GEM_BUG_ON(ring->space < 0); return 0; } /* Align the ring tail to a cacheline boundary */ int intel_ring_cacheline_align(struct drm_i915_gem_request *req) { - struct intel_engine_cs *engine = req->engine; - int num_dwords = (engine->buffer->tail & (CACHELINE_BYTES - 1)) / sizeof(uint32_t); + struct intel_ring *ring = req->ring; + int num_dwords = + (ring->tail & (CACHELINE_BYTES - 1)) / sizeof(uint32_t); int ret; if (num_dwords == 0) @@ -2445,61 +2323,16 @@ int intel_ring_cacheline_align(struct drm_i915_gem_request *req) return ret; while (num_dwords--) - intel_ring_emit(engine, MI_NOOP); + intel_ring_emit(ring, MI_NOOP); - intel_ring_advance(engine); + intel_ring_advance(ring); return 0; } -void intel_ring_init_seqno(struct intel_engine_cs *engine, u32 seqno) -{ - struct drm_i915_private *dev_priv = engine->i915; - - /* Our semaphore implementation is strictly monotonic (i.e. we proceed - * so long as the semaphore value in the register/page is greater - * than the sync value), so whenever we reset the seqno, - * so long as we reset the tracking semaphore value to 0, it will - * always be before the next request's seqno. If we don't reset - * the semaphore value, then when the seqno moves backwards all - * future waits will complete instantly (causing rendering corruption). - */ - if (IS_GEN6(dev_priv) || IS_GEN7(dev_priv)) { - I915_WRITE(RING_SYNC_0(engine->mmio_base), 0); - I915_WRITE(RING_SYNC_1(engine->mmio_base), 0); - if (HAS_VEBOX(dev_priv)) - I915_WRITE(RING_SYNC_2(engine->mmio_base), 0); - } - if (dev_priv->semaphore_obj) { - struct drm_i915_gem_object *obj = dev_priv->semaphore_obj; - struct page *page = i915_gem_object_get_dirty_page(obj, 0); - void *semaphores = kmap(page); - memset(semaphores + GEN8_SEMAPHORE_OFFSET(engine->id, 0), - 0, I915_NUM_ENGINES * gen8_semaphore_seqno_size); - kunmap(page); - } - memset(engine->semaphore.sync_seqno, 0, - sizeof(engine->semaphore.sync_seqno)); - - intel_write_status_page(engine, I915_GEM_HWS_INDEX, seqno); - if (engine->irq_seqno_barrier) - engine->irq_seqno_barrier(engine); - engine->last_submitted_seqno = seqno; - - engine->hangcheck.seqno = seqno; - - /* After manually advancing the seqno, fake the interrupt in case - * there are any waiters for that seqno. - */ - rcu_read_lock(); - intel_engine_wakeup(engine); - rcu_read_unlock(); -} - -static void gen6_bsd_ring_write_tail(struct intel_engine_cs *engine, - u32 value) +static void gen6_bsd_submit_request(struct drm_i915_gem_request *request) { - struct drm_i915_private *dev_priv = engine->i915; + struct drm_i915_private *dev_priv = request->i915; intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); @@ -2523,8 +2356,7 @@ static void gen6_bsd_ring_write_tail(struct intel_engine_cs *engine, DRM_ERROR("timed out waiting for the BSD ring to wake up\n"); /* Now that the ring is fully powered up, update the tail */ - I915_WRITE_FW(RING_TAIL(engine->mmio_base), value); - POSTING_READ_FW(RING_TAIL(engine->mmio_base)); + i9xx_submit_request(request); /* Let the ring send IDLE messages to the GT again, * and so let it sleep to conserve power when idle. @@ -2535,10 +2367,9 @@ static void gen6_bsd_ring_write_tail(struct intel_engine_cs *engine, intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); } -static int gen6_bsd_ring_flush(struct drm_i915_gem_request *req, - u32 invalidate, u32 flush) +static int gen6_bsd_ring_flush(struct drm_i915_gem_request *req, u32 mode) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; uint32_t cmd; int ret; @@ -2563,30 +2394,29 @@ static int gen6_bsd_ring_flush(struct drm_i915_gem_request *req, * operation is complete. This bit is only valid when the * Post-Sync Operation field is a value of 1h or 3h." */ - if (invalidate & I915_GEM_GPU_DOMAINS) + if (mode & EMIT_INVALIDATE) cmd |= MI_INVALIDATE_TLB | MI_INVALIDATE_BSD; - intel_ring_emit(engine, cmd); - intel_ring_emit(engine, - I915_GEM_HWS_SCRATCH_ADDR | MI_FLUSH_DW_USE_GTT); + intel_ring_emit(ring, cmd); + intel_ring_emit(ring, I915_GEM_HWS_SCRATCH_ADDR | MI_FLUSH_DW_USE_GTT); if (INTEL_GEN(req->i915) >= 8) { - intel_ring_emit(engine, 0); /* upper addr */ - intel_ring_emit(engine, 0); /* value */ + intel_ring_emit(ring, 0); /* upper addr */ + intel_ring_emit(ring, 0); /* value */ } else { - intel_ring_emit(engine, 0); - intel_ring_emit(engine, MI_NOOP); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, MI_NOOP); } - intel_ring_advance(engine); + intel_ring_advance(ring); return 0; } static int -gen8_ring_dispatch_execbuffer(struct drm_i915_gem_request *req, - u64 offset, u32 len, - unsigned dispatch_flags) +gen8_emit_bb_start(struct drm_i915_gem_request *req, + u64 offset, u32 len, + unsigned int dispatch_flags) { - struct intel_engine_cs *engine = req->engine; - bool ppgtt = USES_PPGTT(engine->dev) && + struct intel_ring *ring = req->ring; + bool ppgtt = USES_PPGTT(req->i915) && !(dispatch_flags & I915_DISPATCH_SECURE); int ret; @@ -2595,71 +2425,70 @@ gen8_ring_dispatch_execbuffer(struct drm_i915_gem_request *req, return ret; /* FIXME(BDW): Address space and security selectors. */ - intel_ring_emit(engine, MI_BATCH_BUFFER_START_GEN8 | (ppgtt<<8) | + intel_ring_emit(ring, MI_BATCH_BUFFER_START_GEN8 | (ppgtt<<8) | (dispatch_flags & I915_DISPATCH_RS ? MI_BATCH_RESOURCE_STREAMER : 0)); - intel_ring_emit(engine, lower_32_bits(offset)); - intel_ring_emit(engine, upper_32_bits(offset)); - intel_ring_emit(engine, MI_NOOP); - intel_ring_advance(engine); + intel_ring_emit(ring, lower_32_bits(offset)); + intel_ring_emit(ring, upper_32_bits(offset)); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); return 0; } static int -hsw_ring_dispatch_execbuffer(struct drm_i915_gem_request *req, - u64 offset, u32 len, - unsigned dispatch_flags) +hsw_emit_bb_start(struct drm_i915_gem_request *req, + u64 offset, u32 len, + unsigned int dispatch_flags) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; int ret; ret = intel_ring_begin(req, 2); if (ret) return ret; - intel_ring_emit(engine, + intel_ring_emit(ring, MI_BATCH_BUFFER_START | (dispatch_flags & I915_DISPATCH_SECURE ? 0 : MI_BATCH_PPGTT_HSW | MI_BATCH_NON_SECURE_HSW) | (dispatch_flags & I915_DISPATCH_RS ? MI_BATCH_RESOURCE_STREAMER : 0)); /* bit0-7 is the length on GEN6+ */ - intel_ring_emit(engine, offset); - intel_ring_advance(engine); + intel_ring_emit(ring, offset); + intel_ring_advance(ring); return 0; } static int -gen6_ring_dispatch_execbuffer(struct drm_i915_gem_request *req, - u64 offset, u32 len, - unsigned dispatch_flags) +gen6_emit_bb_start(struct drm_i915_gem_request *req, + u64 offset, u32 len, + unsigned int dispatch_flags) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; int ret; ret = intel_ring_begin(req, 2); if (ret) return ret; - intel_ring_emit(engine, + intel_ring_emit(ring, MI_BATCH_BUFFER_START | (dispatch_flags & I915_DISPATCH_SECURE ? 0 : MI_BATCH_NON_SECURE_I965)); /* bit0-7 is the length on GEN6+ */ - intel_ring_emit(engine, offset); - intel_ring_advance(engine); + intel_ring_emit(ring, offset); + intel_ring_advance(ring); return 0; } /* Blitter support (SandyBridge+) */ -static int gen6_ring_flush(struct drm_i915_gem_request *req, - u32 invalidate, u32 flush) +static int gen6_ring_flush(struct drm_i915_gem_request *req, u32 mode) { - struct intel_engine_cs *engine = req->engine; + struct intel_ring *ring = req->ring; uint32_t cmd; int ret; @@ -2684,19 +2513,19 @@ static int gen6_ring_flush(struct drm_i915_gem_request *req, * operation is complete. This bit is only valid when the * Post-Sync Operation field is a value of 1h or 3h." */ - if (invalidate & I915_GEM_DOMAIN_RENDER) + if (mode & EMIT_INVALIDATE) cmd |= MI_INVALIDATE_TLB; - intel_ring_emit(engine, cmd); - intel_ring_emit(engine, + intel_ring_emit(ring, cmd); + intel_ring_emit(ring, I915_GEM_HWS_SCRATCH_ADDR | MI_FLUSH_DW_USE_GTT); if (INTEL_GEN(req->i915) >= 8) { - intel_ring_emit(engine, 0); /* upper addr */ - intel_ring_emit(engine, 0); /* value */ + intel_ring_emit(ring, 0); /* upper addr */ + intel_ring_emit(ring, 0); /* value */ } else { - intel_ring_emit(engine, 0); - intel_ring_emit(engine, MI_NOOP); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, MI_NOOP); } - intel_ring_advance(engine); + intel_ring_advance(ring); return 0; } @@ -2707,38 +2536,39 @@ static void intel_ring_init_semaphores(struct drm_i915_private *dev_priv, struct drm_i915_gem_object *obj; int ret, i; - if (!i915_semaphore_is_enabled(dev_priv)) + if (!i915.semaphores) return; - if (INTEL_GEN(dev_priv) >= 8 && !dev_priv->semaphore_obj) { + if (INTEL_GEN(dev_priv) >= 8 && !dev_priv->semaphore) { + struct i915_vma *vma; + obj = i915_gem_object_create(&dev_priv->drm, 4096); - if (IS_ERR(obj)) { - DRM_ERROR("Failed to allocate semaphore bo. Disabling semaphores\n"); - i915.semaphores = 0; - } else { - i915_gem_object_set_cache_level(obj, I915_CACHE_LLC); - ret = i915_gem_obj_ggtt_pin(obj, 0, PIN_NONBLOCK); - if (ret != 0) { - drm_gem_object_unreference(&obj->base); - DRM_ERROR("Failed to pin semaphore bo. Disabling semaphores\n"); - i915.semaphores = 0; - } else { - dev_priv->semaphore_obj = obj; - } - } - } + if (IS_ERR(obj)) + goto err; - if (!i915_semaphore_is_enabled(dev_priv)) - return; + vma = i915_vma_create(obj, &dev_priv->ggtt.base, NULL); + if (IS_ERR(vma)) + goto err_obj; + + ret = i915_gem_object_set_to_gtt_domain(obj, false); + if (ret) + goto err_obj; + + ret = i915_vma_pin(vma, 0, 0, PIN_GLOBAL | PIN_HIGH); + if (ret) + goto err_obj; + + dev_priv->semaphore = vma; + } if (INTEL_GEN(dev_priv) >= 8) { - u64 offset = i915_gem_obj_ggtt_offset(dev_priv->semaphore_obj); + u32 offset = i915_ggtt_offset(dev_priv->semaphore); - engine->semaphore.sync_to = gen8_ring_sync; + engine->semaphore.sync_to = gen8_ring_sync_to; engine->semaphore.signal = gen8_xcs_signal; for (i = 0; i < I915_NUM_ENGINES; i++) { - u64 ring_offset; + u32 ring_offset; if (i != engine->id) ring_offset = offset + GEN8_SEMAPHORE_OFFSET(engine->id, i); @@ -2748,7 +2578,7 @@ static void intel_ring_init_semaphores(struct drm_i915_private *dev_priv, engine->semaphore.signal_ggtt[i] = ring_offset; } } else if (INTEL_GEN(dev_priv) >= 6) { - engine->semaphore.sync_to = gen6_ring_sync; + engine->semaphore.sync_to = gen6_ring_sync_to; engine->semaphore.signal = gen6_signal; /* @@ -2758,52 +2588,62 @@ static void intel_ring_init_semaphores(struct drm_i915_private *dev_priv, * initialized as INVALID. Gen8 will initialize the * sema between VCS2 and RCS later. */ - for (i = 0; i < I915_NUM_ENGINES; i++) { + for (i = 0; i < GEN6_NUM_SEMAPHORES; i++) { static const struct { u32 wait_mbox; i915_reg_t mbox_reg; - } sem_data[I915_NUM_ENGINES][I915_NUM_ENGINES] = { - [RCS] = { - [VCS] = { .wait_mbox = MI_SEMAPHORE_SYNC_RV, .mbox_reg = GEN6_VRSYNC }, - [BCS] = { .wait_mbox = MI_SEMAPHORE_SYNC_RB, .mbox_reg = GEN6_BRSYNC }, - [VECS] = { .wait_mbox = MI_SEMAPHORE_SYNC_RVE, .mbox_reg = GEN6_VERSYNC }, + } sem_data[GEN6_NUM_SEMAPHORES][GEN6_NUM_SEMAPHORES] = { + [RCS_HW] = { + [VCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_RV, .mbox_reg = GEN6_VRSYNC }, + [BCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_RB, .mbox_reg = GEN6_BRSYNC }, + [VECS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_RVE, .mbox_reg = GEN6_VERSYNC }, }, - [VCS] = { - [RCS] = { .wait_mbox = MI_SEMAPHORE_SYNC_VR, .mbox_reg = GEN6_RVSYNC }, - [BCS] = { .wait_mbox = MI_SEMAPHORE_SYNC_VB, .mbox_reg = GEN6_BVSYNC }, - [VECS] = { .wait_mbox = MI_SEMAPHORE_SYNC_VVE, .mbox_reg = GEN6_VEVSYNC }, + [VCS_HW] = { + [RCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VR, .mbox_reg = GEN6_RVSYNC }, + [BCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VB, .mbox_reg = GEN6_BVSYNC }, + [VECS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VVE, .mbox_reg = GEN6_VEVSYNC }, }, - [BCS] = { - [RCS] = { .wait_mbox = MI_SEMAPHORE_SYNC_BR, .mbox_reg = GEN6_RBSYNC }, - [VCS] = { .wait_mbox = MI_SEMAPHORE_SYNC_BV, .mbox_reg = GEN6_VBSYNC }, - [VECS] = { .wait_mbox = MI_SEMAPHORE_SYNC_BVE, .mbox_reg = GEN6_VEBSYNC }, + [BCS_HW] = { + [RCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_BR, .mbox_reg = GEN6_RBSYNC }, + [VCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_BV, .mbox_reg = GEN6_VBSYNC }, + [VECS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_BVE, .mbox_reg = GEN6_VEBSYNC }, }, - [VECS] = { - [RCS] = { .wait_mbox = MI_SEMAPHORE_SYNC_VER, .mbox_reg = GEN6_RVESYNC }, - [VCS] = { .wait_mbox = MI_SEMAPHORE_SYNC_VEV, .mbox_reg = GEN6_VVESYNC }, - [BCS] = { .wait_mbox = MI_SEMAPHORE_SYNC_VEB, .mbox_reg = GEN6_BVESYNC }, + [VECS_HW] = { + [RCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VER, .mbox_reg = GEN6_RVESYNC }, + [VCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VEV, .mbox_reg = GEN6_VVESYNC }, + [BCS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VEB, .mbox_reg = GEN6_BVESYNC }, }, }; u32 wait_mbox; i915_reg_t mbox_reg; - if (i == engine->id || i == VCS2) { + if (i == engine->hw_id) { wait_mbox = MI_SEMAPHORE_SYNC_INVALID; mbox_reg = GEN6_NOSYNC; } else { - wait_mbox = sem_data[engine->id][i].wait_mbox; - mbox_reg = sem_data[engine->id][i].mbox_reg; + wait_mbox = sem_data[engine->hw_id][i].wait_mbox; + mbox_reg = sem_data[engine->hw_id][i].mbox_reg; } engine->semaphore.mbox.wait[i] = wait_mbox; engine->semaphore.mbox.signal[i] = mbox_reg; } } + + return; + +err_obj: + i915_gem_object_put(obj); +err: + DRM_DEBUG_DRIVER("Failed to allocate space for semaphores, disabling\n"); + i915.semaphores = 0; } static void intel_ring_init_irq(struct drm_i915_private *dev_priv, struct intel_engine_cs *engine) { + engine->irq_enable_mask = GT_RENDER_USER_INTERRUPT << engine->irq_shift; + if (INTEL_GEN(dev_priv) >= 8) { engine->irq_enable = gen8_irq_enable; engine->irq_disable = gen8_irq_disable; @@ -2828,83 +2668,76 @@ static void intel_ring_init_irq(struct drm_i915_private *dev_priv, static void intel_ring_default_vfuncs(struct drm_i915_private *dev_priv, struct intel_engine_cs *engine) { + intel_ring_init_irq(dev_priv, engine); + intel_ring_init_semaphores(dev_priv, engine); + engine->init_hw = init_ring_common; - engine->write_tail = ring_write_tail; + engine->reset_hw = reset_ring_common; - engine->add_request = i9xx_add_request; - if (INTEL_GEN(dev_priv) >= 6) - engine->add_request = gen6_add_request; + engine->emit_request = i9xx_emit_request; + if (i915.semaphores) + engine->emit_request = gen6_sema_emit_request; + engine->submit_request = i9xx_submit_request; if (INTEL_GEN(dev_priv) >= 8) - engine->dispatch_execbuffer = gen8_ring_dispatch_execbuffer; + engine->emit_bb_start = gen8_emit_bb_start; else if (INTEL_GEN(dev_priv) >= 6) - engine->dispatch_execbuffer = gen6_ring_dispatch_execbuffer; + engine->emit_bb_start = gen6_emit_bb_start; else if (INTEL_GEN(dev_priv) >= 4) - engine->dispatch_execbuffer = i965_dispatch_execbuffer; + engine->emit_bb_start = i965_emit_bb_start; else if (IS_I830(dev_priv) || IS_845G(dev_priv)) - engine->dispatch_execbuffer = i830_dispatch_execbuffer; + engine->emit_bb_start = i830_emit_bb_start; else - engine->dispatch_execbuffer = i915_dispatch_execbuffer; - - intel_ring_init_irq(dev_priv, engine); - intel_ring_init_semaphores(dev_priv, engine); + engine->emit_bb_start = i915_emit_bb_start; } -int intel_init_render_ring_buffer(struct drm_device *dev) +int intel_init_render_ring_buffer(struct intel_engine_cs *engine) { - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_engine_cs *engine = &dev_priv->engine[RCS]; + struct drm_i915_private *dev_priv = engine->i915; int ret; - engine->name = "render ring"; - engine->id = RCS; - engine->exec_id = I915_EXEC_RENDER; - engine->hw_id = 0; - engine->mmio_base = RENDER_RING_BASE; - intel_ring_default_vfuncs(dev_priv, engine); - engine->irq_enable_mask = GT_RENDER_USER_INTERRUPT; if (HAS_L3_DPF(dev_priv)) engine->irq_keep_mask = GT_RENDER_L3_PARITY_ERROR_INTERRUPT; if (INTEL_GEN(dev_priv) >= 8) { engine->init_context = intel_rcs_ctx_init; - engine->add_request = gen8_render_add_request; - engine->flush = gen8_render_ring_flush; - if (i915_semaphore_is_enabled(dev_priv)) + engine->emit_request = gen8_render_emit_request; + engine->emit_flush = gen8_render_ring_flush; + if (i915.semaphores) engine->semaphore.signal = gen8_rcs_signal; } else if (INTEL_GEN(dev_priv) >= 6) { engine->init_context = intel_rcs_ctx_init; - engine->flush = gen7_render_ring_flush; + engine->emit_flush = gen7_render_ring_flush; if (IS_GEN6(dev_priv)) - engine->flush = gen6_render_ring_flush; + engine->emit_flush = gen6_render_ring_flush; } else if (IS_GEN5(dev_priv)) { - engine->flush = gen4_render_ring_flush; + engine->emit_flush = gen4_render_ring_flush; } else { if (INTEL_GEN(dev_priv) < 4) - engine->flush = gen2_render_ring_flush; + engine->emit_flush = gen2_render_ring_flush; else - engine->flush = gen4_render_ring_flush; + engine->emit_flush = gen4_render_ring_flush; engine->irq_enable_mask = I915_USER_INTERRUPT; } if (IS_HASWELL(dev_priv)) - engine->dispatch_execbuffer = hsw_ring_dispatch_execbuffer; + engine->emit_bb_start = hsw_emit_bb_start; engine->init_hw = init_render_ring; engine->cleanup = render_ring_cleanup; - ret = intel_init_ring_buffer(dev, engine); + ret = intel_init_ring_buffer(engine); if (ret) return ret; if (INTEL_GEN(dev_priv) >= 6) { - ret = intel_init_pipe_control(engine, 4096); + ret = intel_engine_create_scratch(engine, 4096); if (ret) return ret; } else if (HAS_BROKEN_CS_TLB(dev_priv)) { - ret = intel_init_pipe_control(engine, I830_WA_SIZE); + ret = intel_engine_create_scratch(engine, I830_WA_SIZE); if (ret) return ret; } @@ -2912,166 +2745,71 @@ int intel_init_render_ring_buffer(struct drm_device *dev) return 0; } -int intel_init_bsd_ring_buffer(struct drm_device *dev) +int intel_init_bsd_ring_buffer(struct intel_engine_cs *engine) { - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_engine_cs *engine = &dev_priv->engine[VCS]; - - engine->name = "bsd ring"; - engine->id = VCS; - engine->exec_id = I915_EXEC_BSD; - engine->hw_id = 1; + struct drm_i915_private *dev_priv = engine->i915; intel_ring_default_vfuncs(dev_priv, engine); if (INTEL_GEN(dev_priv) >= 6) { - engine->mmio_base = GEN6_BSD_RING_BASE; /* gen6 bsd needs a special wa for tail updates */ if (IS_GEN6(dev_priv)) - engine->write_tail = gen6_bsd_ring_write_tail; - engine->flush = gen6_bsd_ring_flush; - if (INTEL_GEN(dev_priv) >= 8) - engine->irq_enable_mask = - GT_RENDER_USER_INTERRUPT << GEN8_VCS1_IRQ_SHIFT; - else + engine->submit_request = gen6_bsd_submit_request; + engine->emit_flush = gen6_bsd_ring_flush; + if (INTEL_GEN(dev_priv) < 8) engine->irq_enable_mask = GT_BSD_USER_INTERRUPT; } else { engine->mmio_base = BSD_RING_BASE; - engine->flush = bsd_ring_flush; + engine->emit_flush = bsd_ring_flush; if (IS_GEN5(dev_priv)) engine->irq_enable_mask = ILK_BSD_USER_INTERRUPT; else engine->irq_enable_mask = I915_BSD_USER_INTERRUPT; } - return intel_init_ring_buffer(dev, engine); + return intel_init_ring_buffer(engine); } /** * Initialize the second BSD ring (eg. Broadwell GT3, Skylake GT3) */ -int intel_init_bsd2_ring_buffer(struct drm_device *dev) +int intel_init_bsd2_ring_buffer(struct intel_engine_cs *engine) { - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_engine_cs *engine = &dev_priv->engine[VCS2]; - - engine->name = "bsd2 ring"; - engine->id = VCS2; - engine->exec_id = I915_EXEC_BSD; - engine->hw_id = 4; - engine->mmio_base = GEN8_BSD2_RING_BASE; + struct drm_i915_private *dev_priv = engine->i915; intel_ring_default_vfuncs(dev_priv, engine); - engine->flush = gen6_bsd_ring_flush; - engine->irq_enable_mask = - GT_RENDER_USER_INTERRUPT << GEN8_VCS2_IRQ_SHIFT; + engine->emit_flush = gen6_bsd_ring_flush; - return intel_init_ring_buffer(dev, engine); + return intel_init_ring_buffer(engine); } -int intel_init_blt_ring_buffer(struct drm_device *dev) +int intel_init_blt_ring_buffer(struct intel_engine_cs *engine) { - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_engine_cs *engine = &dev_priv->engine[BCS]; - - engine->name = "blitter ring"; - engine->id = BCS; - engine->exec_id = I915_EXEC_BLT; - engine->hw_id = 2; - engine->mmio_base = BLT_RING_BASE; + struct drm_i915_private *dev_priv = engine->i915; intel_ring_default_vfuncs(dev_priv, engine); - engine->flush = gen6_ring_flush; - if (INTEL_GEN(dev_priv) >= 8) - engine->irq_enable_mask = - GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT; - else + engine->emit_flush = gen6_ring_flush; + if (INTEL_GEN(dev_priv) < 8) engine->irq_enable_mask = GT_BLT_USER_INTERRUPT; - return intel_init_ring_buffer(dev, engine); + return intel_init_ring_buffer(engine); } -int intel_init_vebox_ring_buffer(struct drm_device *dev) +int intel_init_vebox_ring_buffer(struct intel_engine_cs *engine) { - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_engine_cs *engine = &dev_priv->engine[VECS]; - - engine->name = "video enhancement ring"; - engine->id = VECS; - engine->exec_id = I915_EXEC_VEBOX; - engine->hw_id = 3; - engine->mmio_base = VEBOX_RING_BASE; + struct drm_i915_private *dev_priv = engine->i915; intel_ring_default_vfuncs(dev_priv, engine); - engine->flush = gen6_ring_flush; + engine->emit_flush = gen6_ring_flush; - if (INTEL_GEN(dev_priv) >= 8) { - engine->irq_enable_mask = - GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT; - } else { + if (INTEL_GEN(dev_priv) < 8) { engine->irq_enable_mask = PM_VEBOX_USER_INTERRUPT; engine->irq_enable = hsw_vebox_irq_enable; engine->irq_disable = hsw_vebox_irq_disable; } - return intel_init_ring_buffer(dev, engine); -} - -int -intel_ring_flush_all_caches(struct drm_i915_gem_request *req) -{ - struct intel_engine_cs *engine = req->engine; - int ret; - - if (!engine->gpu_caches_dirty) - return 0; - - ret = engine->flush(req, 0, I915_GEM_GPU_DOMAINS); - if (ret) - return ret; - - trace_i915_gem_ring_flush(req, 0, I915_GEM_GPU_DOMAINS); - - engine->gpu_caches_dirty = false; - return 0; -} - -int -intel_ring_invalidate_all_caches(struct drm_i915_gem_request *req) -{ - struct intel_engine_cs *engine = req->engine; - uint32_t flush_domains; - int ret; - - flush_domains = 0; - if (engine->gpu_caches_dirty) - flush_domains = I915_GEM_GPU_DOMAINS; - - ret = engine->flush(req, I915_GEM_GPU_DOMAINS, flush_domains); - if (ret) - return ret; - - trace_i915_gem_ring_flush(req, I915_GEM_GPU_DOMAINS, flush_domains); - - engine->gpu_caches_dirty = false; - return 0; -} - -void -intel_stop_engine(struct intel_engine_cs *engine) -{ - int ret; - - if (!intel_engine_initialized(engine)) - return; - - ret = intel_engine_idle(engine); - if (ret) - DRM_ERROR("failed to quiesce %s whilst cleaning up: %d\n", - engine->name, ret); - - stop_ring(engine); + return intel_init_ring_buffer(engine); } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 12cb7ed..7f64d61 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -3,6 +3,7 @@ #include <linux/hashtable.h> #include "i915_gem_batch_pool.h" +#include "i915_gem_request.h" #define I915_CMD_HASH_ORDER 9 @@ -25,29 +26,29 @@ */ #define I915_RING_FREE_SPACE 64 -struct intel_hw_status_page { - u32 *page_addr; - unsigned int gfx_addr; - struct drm_i915_gem_object *obj; +struct intel_hw_status_page { + struct i915_vma *vma; + u32 *page_addr; + u32 ggtt_offset; }; -#define I915_READ_TAIL(ring) I915_READ(RING_TAIL((ring)->mmio_base)) -#define I915_WRITE_TAIL(ring, val) I915_WRITE(RING_TAIL((ring)->mmio_base), val) +#define I915_READ_TAIL(engine) I915_READ(RING_TAIL((engine)->mmio_base)) +#define I915_WRITE_TAIL(engine, val) I915_WRITE(RING_TAIL((engine)->mmio_base), val) -#define I915_READ_START(ring) I915_READ(RING_START((ring)->mmio_base)) -#define I915_WRITE_START(ring, val) I915_WRITE(RING_START((ring)->mmio_base), val) +#define I915_READ_START(engine) I915_READ(RING_START((engine)->mmio_base)) +#define I915_WRITE_START(engine, val) I915_WRITE(RING_START((engine)->mmio_base), val) -#define I915_READ_HEAD(ring) I915_READ(RING_HEAD((ring)->mmio_base)) -#define I915_WRITE_HEAD(ring, val) I915_WRITE(RING_HEAD((ring)->mmio_base), val) +#define I915_READ_HEAD(engine) I915_READ(RING_HEAD((engine)->mmio_base)) +#define I915_WRITE_HEAD(engine, val) I915_WRITE(RING_HEAD((engine)->mmio_base), val) -#define I915_READ_CTL(ring) I915_READ(RING_CTL((ring)->mmio_base)) -#define I915_WRITE_CTL(ring, val) I915_WRITE(RING_CTL((ring)->mmio_base), val) +#define I915_READ_CTL(engine) I915_READ(RING_CTL((engine)->mmio_base)) +#define I915_WRITE_CTL(engine, val) I915_WRITE(RING_CTL((engine)->mmio_base), val) -#define I915_READ_IMR(ring) I915_READ(RING_IMR((ring)->mmio_base)) -#define I915_WRITE_IMR(ring, val) I915_WRITE(RING_IMR((ring)->mmio_base), val) +#define I915_READ_IMR(engine) I915_READ(RING_IMR((engine)->mmio_base)) +#define I915_WRITE_IMR(engine, val) I915_WRITE(RING_IMR((engine)->mmio_base), val) -#define I915_READ_MODE(ring) I915_READ(RING_MI_MODE((ring)->mmio_base)) -#define I915_WRITE_MODE(ring, val) I915_WRITE(RING_MI_MODE((ring)->mmio_base), val) +#define I915_READ_MODE(engine) I915_READ(RING_MI_MODE((engine)->mmio_base)) +#define I915_WRITE_MODE(engine, val) I915_WRITE(RING_MI_MODE((engine)->mmio_base), val) /* seqno size is actually only a uint32, but since we plan to use MI_FLUSH_DW to * do the writes, and that must have qw aligned offsets, simply pretend it's 8b. @@ -56,13 +57,13 @@ struct intel_hw_status_page { #define GEN8_SEMAPHORE_OFFSET(__from, __to) \ (((__from) * I915_NUM_ENGINES + (__to)) * gen8_semaphore_seqno_size) #define GEN8_SIGNAL_OFFSET(__ring, to) \ - (i915_gem_obj_ggtt_offset(dev_priv->semaphore_obj) + \ + (dev_priv->semaphore->node.start + \ GEN8_SEMAPHORE_OFFSET((__ring)->id, (to))) #define GEN8_WAIT_OFFSET(__ring, from) \ - (i915_gem_obj_ggtt_offset(dev_priv->semaphore_obj) + \ + (dev_priv->semaphore->node.start + \ GEN8_SEMAPHORE_OFFSET(from, (__ring)->id)) -enum intel_ring_hangcheck_action { +enum intel_engine_hangcheck_action { HANGCHECK_IDLE = 0, HANGCHECK_WAIT, HANGCHECK_ACTIVE, @@ -72,23 +73,22 @@ enum intel_ring_hangcheck_action { #define HANGCHECK_SCORE_RING_HUNG 31 -struct intel_ring_hangcheck { +struct intel_engine_hangcheck { u64 acthd; - unsigned long user_interrupts; u32 seqno; int score; - enum intel_ring_hangcheck_action action; + enum intel_engine_hangcheck_action action; int deadlock; u32 instdone[I915_NUM_INSTDONE_REG]; }; -struct intel_ringbuffer { - struct drm_i915_gem_object *obj; - void __iomem *virtual_start; +struct intel_ring { struct i915_vma *vma; + void *vaddr; struct intel_engine_cs *engine; - struct list_head link; + + struct list_head request_list; u32 head; u32 tail; @@ -121,12 +121,12 @@ struct drm_i915_reg_table; * an option for future use. * size: size of the batch in DWORDS */ -struct i915_ctx_workarounds { +struct i915_ctx_workarounds { struct i915_wa_ctx_bb { u32 offset; u32 size; } indirect_ctx, per_ctx; - struct drm_i915_gem_object *obj; + struct i915_vma *vma; }; struct drm_i915_gem_request; @@ -144,11 +144,18 @@ struct intel_engine_cs { #define I915_NUM_ENGINES 5 #define _VCS(n) (VCS + (n)) unsigned int exec_id; - unsigned int hw_id; - unsigned int guc_id; /* XXX same as hw_id? */ + enum intel_engine_hw_id { + RCS_HW = 0, + VCS_HW, + BCS_HW, + VECS_HW, + VCS2_HW + } hw_id; + enum intel_engine_hw_id guc_id; /* XXX same as hw_id? */ + u64 fence_context; u32 mmio_base; - struct intel_ringbuffer *buffer; - struct list_head buffers; + unsigned int irq_shift; + struct intel_ring *buffer; /* Rather than have every client wait upon all user interrupts, * with the herd waking after every interrupt and each doing the @@ -167,8 +174,7 @@ struct intel_engine_cs { * the overhead of waking that client is much preferred. */ struct intel_breadcrumbs { - struct task_struct *irq_seqno_bh; /* bh for user interrupts */ - unsigned long irq_wakeups; + struct task_struct __rcu *irq_seqno_bh; /* bh for interrupts */ bool irq_posted; spinlock_t lock; /* protects the lists of requests */ @@ -178,6 +184,9 @@ struct intel_engine_cs { struct task_struct *signaler; /* used for fence signalling */ struct drm_i915_gem_request *first_signal; struct timer_list fake_irq; /* used after a missed interrupt */ + struct timer_list hangcheck; /* detect missed interrupts */ + + unsigned long timeout; bool irq_enabled : 1; bool rpm_wakelock : 1; @@ -192,36 +201,48 @@ struct intel_engine_cs { struct intel_hw_status_page status_page; struct i915_ctx_workarounds wa_ctx; + struct i915_vma *scratch; u32 irq_keep_mask; /* always keep these interrupts */ u32 irq_enable_mask; /* bitmask to enable ring interrupt */ - void (*irq_enable)(struct intel_engine_cs *ring); - void (*irq_disable)(struct intel_engine_cs *ring); + void (*irq_enable)(struct intel_engine_cs *engine); + void (*irq_disable)(struct intel_engine_cs *engine); - int (*init_hw)(struct intel_engine_cs *ring); + int (*init_hw)(struct intel_engine_cs *engine); + void (*reset_hw)(struct intel_engine_cs *engine, + struct drm_i915_gem_request *req); int (*init_context)(struct drm_i915_gem_request *req); - void (*write_tail)(struct intel_engine_cs *ring, - u32 value); - int __must_check (*flush)(struct drm_i915_gem_request *req, - u32 invalidate_domains, - u32 flush_domains); - int (*add_request)(struct drm_i915_gem_request *req); + int (*emit_flush)(struct drm_i915_gem_request *request, + u32 mode); +#define EMIT_INVALIDATE BIT(0) +#define EMIT_FLUSH BIT(1) +#define EMIT_BARRIER (EMIT_INVALIDATE | EMIT_FLUSH) + int (*emit_bb_start)(struct drm_i915_gem_request *req, + u64 offset, u32 length, + unsigned int dispatch_flags); +#define I915_DISPATCH_SECURE BIT(0) +#define I915_DISPATCH_PINNED BIT(1) +#define I915_DISPATCH_RS BIT(2) + int (*emit_request)(struct drm_i915_gem_request *req); + + /* Pass the request to the hardware queue (e.g. directly into + * the legacy ringbuffer or to the end of an execlist). + * + * This is called from an atomic context with irqs disabled; must + * be irq safe. + */ + void (*submit_request)(struct drm_i915_gem_request *req); + /* Some chipsets are not quite as coherent as advertised and need * an expensive kick to force a true read of the up-to-date seqno. * However, the up-to-date seqno is not always required and the last * seen value is good enough. Note that the seqno will always be * monotonic, even if not coherent. */ - void (*irq_seqno_barrier)(struct intel_engine_cs *ring); - int (*dispatch_execbuffer)(struct drm_i915_gem_request *req, - u64 offset, u32 length, - unsigned dispatch_flags); -#define I915_DISPATCH_SECURE 0x1 -#define I915_DISPATCH_PINNED 0x2 -#define I915_DISPATCH_RS 0x4 - void (*cleanup)(struct intel_engine_cs *ring); + void (*irq_seqno_barrier)(struct intel_engine_cs *engine); + void (*cleanup)(struct intel_engine_cs *engine); /* GEN8 signal/wait table - never trust comments! * signal to signal to signal to signal to signal to @@ -264,51 +285,36 @@ struct intel_engine_cs { u32 sync_seqno[I915_NUM_ENGINES-1]; union { +#define GEN6_SEMAPHORE_LAST VECS_HW +#define GEN6_NUM_SEMAPHORES (GEN6_SEMAPHORE_LAST + 1) +#define GEN6_SEMAPHORES_MASK GENMASK(GEN6_SEMAPHORE_LAST, 0) struct { /* our mbox written by others */ - u32 wait[I915_NUM_ENGINES]; + u32 wait[GEN6_NUM_SEMAPHORES]; /* mboxes this ring signals to */ - i915_reg_t signal[I915_NUM_ENGINES]; + i915_reg_t signal[GEN6_NUM_SEMAPHORES]; } mbox; u64 signal_ggtt[I915_NUM_ENGINES]; }; /* AKA wait() */ - int (*sync_to)(struct drm_i915_gem_request *to_req, - struct intel_engine_cs *from, - u32 seqno); - int (*signal)(struct drm_i915_gem_request *signaller_req, - /* num_dwords needed by caller */ - unsigned int num_dwords); + int (*sync_to)(struct drm_i915_gem_request *req, + struct drm_i915_gem_request *signal); + int (*signal)(struct drm_i915_gem_request *req); } semaphore; /* Execlists */ struct tasklet_struct irq_tasklet; spinlock_t execlist_lock; /* used inside tasklet, use spin_lock_bh */ + struct execlist_port { + struct drm_i915_gem_request *request; + unsigned int count; + } execlist_port[2]; struct list_head execlist_queue; unsigned int fw_domains; - unsigned int next_context_status_buffer; - unsigned int idle_lite_restore_wa; bool disable_lite_restore_wa; + bool preempt_wa; u32 ctx_desc_template; - int (*emit_request)(struct drm_i915_gem_request *request); - int (*emit_flush)(struct drm_i915_gem_request *request, - u32 invalidate_domains, - u32 flush_domains); - int (*emit_bb_start)(struct drm_i915_gem_request *req, - u64 offset, unsigned dispatch_flags); - - /** - * List of objects currently involved in rendering from the - * ringbuffer. - * - * Includes buffers having the contents of their GPU caches - * flushed, not necessarily primitives. last_read_req - * represents when the rendering involved will be completed. - * - * A reference is held on the buffer while on this list. - */ - struct list_head active_list; /** * List of breadcrumbs associated with GPU requests currently @@ -323,22 +329,22 @@ struct intel_engine_cs { */ u32 last_submitted_seqno; - bool gpu_caches_dirty; + /* An RCU guarded pointer to the last request. No reference is + * held to the request, users must carefully acquire a reference to + * the request using i915_gem_active_get_rcu(), or hold the + * struct_mutex. + */ + struct i915_gem_active last_request; struct i915_gem_context *last_context; - struct intel_ring_hangcheck hangcheck; - - struct { - struct drm_i915_gem_object *obj; - u32 gtt_offset; - } scratch; + struct intel_engine_hangcheck hangcheck; bool needs_cmd_parser; /* * Table of commands the command parser needs to know about - * for this ring. + * for this engine. */ DECLARE_HASHTABLE(cmd_hash, I915_CMD_HASH_ORDER); @@ -352,11 +358,11 @@ struct intel_engine_cs { * Returns the bitmask for the length field of the specified command. * Return 0 for an unrecognized/invalid command. * - * If the command parser finds an entry for a command in the ring's + * If the command parser finds an entry for a command in the engine's * cmd_tables, it gets the command's length based on the table entry. - * If not, it calls this function to determine the per-ring length field - * encoding for the command (i.e. certain opcode ranges use certain bits - * to encode the command length in the header). + * If not, it calls this function to determine the per-engine length + * field encoding for the command (i.e. different opcode ranges use + * certain bits to encode the command length in the header). */ u32 (*get_cmd_length_mask)(u32 cmd_header); }; @@ -374,8 +380,8 @@ intel_engine_flag(const struct intel_engine_cs *engine) } static inline u32 -intel_ring_sync_index(struct intel_engine_cs *engine, - struct intel_engine_cs *other) +intel_engine_sync_index(struct intel_engine_cs *engine, + struct intel_engine_cs *other) { int idx; @@ -437,55 +443,77 @@ intel_write_status_page(struct intel_engine_cs *engine, #define I915_GEM_HWS_SCRATCH_INDEX 0x40 #define I915_GEM_HWS_SCRATCH_ADDR (I915_GEM_HWS_SCRATCH_INDEX << MI_STORE_DWORD_INDEX_SHIFT) -struct intel_ringbuffer * -intel_engine_create_ringbuffer(struct intel_engine_cs *engine, int size); -int intel_pin_and_map_ringbuffer_obj(struct drm_i915_private *dev_priv, - struct intel_ringbuffer *ringbuf); -void intel_unpin_ringbuffer_obj(struct intel_ringbuffer *ringbuf); -void intel_ringbuffer_free(struct intel_ringbuffer *ring); +struct intel_ring * +intel_engine_create_ring(struct intel_engine_cs *engine, int size); +int intel_ring_pin(struct intel_ring *ring); +void intel_ring_unpin(struct intel_ring *ring); +void intel_ring_free(struct intel_ring *ring); + +void intel_engine_stop(struct intel_engine_cs *engine); +void intel_engine_cleanup(struct intel_engine_cs *engine); -void intel_stop_engine(struct intel_engine_cs *engine); -void intel_cleanup_engine(struct intel_engine_cs *engine); +void intel_legacy_submission_resume(struct drm_i915_private *dev_priv); int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request); int __must_check intel_ring_begin(struct drm_i915_gem_request *req, int n); int __must_check intel_ring_cacheline_align(struct drm_i915_gem_request *req); -static inline void intel_ring_emit(struct intel_engine_cs *engine, - u32 data) + +static inline void intel_ring_emit(struct intel_ring *ring, u32 data) { - struct intel_ringbuffer *ringbuf = engine->buffer; - iowrite32(data, ringbuf->virtual_start + ringbuf->tail); - ringbuf->tail += 4; + *(uint32_t *)(ring->vaddr + ring->tail) = data; + ring->tail += 4; } -static inline void intel_ring_emit_reg(struct intel_engine_cs *engine, - i915_reg_t reg) + +static inline void intel_ring_emit_reg(struct intel_ring *ring, i915_reg_t reg) { - intel_ring_emit(engine, i915_mmio_reg_offset(reg)); + intel_ring_emit(ring, i915_mmio_reg_offset(reg)); } -static inline void intel_ring_advance(struct intel_engine_cs *engine) + +static inline void intel_ring_advance(struct intel_ring *ring) { - struct intel_ringbuffer *ringbuf = engine->buffer; - ringbuf->tail &= ringbuf->size - 1; + /* Dummy function. + * + * This serves as a placeholder in the code so that the reader + * can compare against the preceding intel_ring_begin() and + * check that the number of dwords emitted matches the space + * reserved for the command packet (i.e. the value passed to + * intel_ring_begin()). + */ +} + +static inline u32 intel_ring_offset(struct intel_ring *ring, u32 value) +{ + /* Don't write ring->size (equivalent to 0) as that hangs some GPUs. */ + return value & (ring->size - 1); } + int __intel_ring_space(int head, int tail, int size); -void intel_ring_update_space(struct intel_ringbuffer *ringbuf); +void intel_ring_update_space(struct intel_ring *ring); -int __must_check intel_engine_idle(struct intel_engine_cs *engine); -void intel_ring_init_seqno(struct intel_engine_cs *engine, u32 seqno); -int intel_ring_flush_all_caches(struct drm_i915_gem_request *req); -int intel_ring_invalidate_all_caches(struct drm_i915_gem_request *req); +void intel_engine_init_seqno(struct intel_engine_cs *engine, u32 seqno); +void intel_engine_reset_irq(struct intel_engine_cs *engine); -int intel_init_pipe_control(struct intel_engine_cs *engine, int size); -void intel_fini_pipe_control(struct intel_engine_cs *engine); +void intel_engine_setup_common(struct intel_engine_cs *engine); +int intel_engine_init_common(struct intel_engine_cs *engine); +int intel_engine_create_scratch(struct intel_engine_cs *engine, int size); +void intel_engine_cleanup_common(struct intel_engine_cs *engine); -int intel_init_render_ring_buffer(struct drm_device *dev); -int intel_init_bsd_ring_buffer(struct drm_device *dev); -int intel_init_bsd2_ring_buffer(struct drm_device *dev); -int intel_init_blt_ring_buffer(struct drm_device *dev); -int intel_init_vebox_ring_buffer(struct drm_device *dev); +static inline int intel_engine_idle(struct intel_engine_cs *engine, + unsigned int flags) +{ + /* Wait upon the last request to be completed */ + return i915_gem_active_wait_unlocked(&engine->last_request, + flags, NULL, NULL); +} + +int intel_init_render_ring_buffer(struct intel_engine_cs *engine); +int intel_init_bsd_ring_buffer(struct intel_engine_cs *engine); +int intel_init_bsd2_ring_buffer(struct intel_engine_cs *engine); +int intel_init_blt_ring_buffer(struct intel_engine_cs *engine); +int intel_init_vebox_ring_buffer(struct intel_engine_cs *engine); -u64 intel_ring_get_active_head(struct intel_engine_cs *engine); +u64 intel_engine_get_active_head(struct intel_engine_cs *engine); static inline u32 intel_engine_get_seqno(struct intel_engine_cs *engine) { return intel_read_status_page(engine, I915_GEM_HWS_INDEX); @@ -493,11 +521,6 @@ static inline u32 intel_engine_get_seqno(struct intel_engine_cs *engine) int init_workarounds_ring(struct intel_engine_cs *engine); -static inline u32 intel_ring_get_tail(struct intel_ringbuffer *ringbuf) -{ - return ringbuf->tail; -} - /* * Arbitrary size for largest possible 'add request' sequence. The code paths * are complex and variable. Empirical measurement shows that the worst case @@ -509,21 +532,10 @@ static inline u32 intel_ring_get_tail(struct intel_ringbuffer *ringbuf) static inline u32 intel_hws_seqno_address(struct intel_engine_cs *engine) { - return engine->status_page.gfx_addr + I915_GEM_HWS_INDEX_ADDR; + return engine->status_page.ggtt_offset + I915_GEM_HWS_INDEX_ADDR; } /* intel_breadcrumbs.c -- user interrupt bottom-half for waiters */ -struct intel_wait { - struct rb_node node; - struct task_struct *tsk; - u32 seqno; -}; - -struct intel_signal_node { - struct rb_node node; - struct intel_wait wait; -}; - int intel_engine_init_breadcrumbs(struct intel_engine_cs *engine); static inline void intel_wait_init(struct intel_wait *wait, u32 seqno) @@ -543,31 +555,42 @@ void intel_engine_remove_wait(struct intel_engine_cs *engine, struct intel_wait *wait); void intel_engine_enable_signaling(struct drm_i915_gem_request *request); -static inline bool intel_engine_has_waiter(struct intel_engine_cs *engine) +static inline bool intel_engine_has_waiter(const struct intel_engine_cs *engine) { - return READ_ONCE(engine->breadcrumbs.irq_seqno_bh); + return rcu_access_pointer(engine->breadcrumbs.irq_seqno_bh); } -static inline bool intel_engine_wakeup(struct intel_engine_cs *engine) +static inline bool intel_engine_wakeup(const struct intel_engine_cs *engine) { bool wakeup = false; - struct task_struct *tsk = READ_ONCE(engine->breadcrumbs.irq_seqno_bh); + /* Note that for this not to dangerously chase a dangling pointer, - * the caller is responsible for ensure that the task remain valid for - * wake_up_process() i.e. that the RCU grace period cannot expire. + * we must hold the rcu_read_lock here. * * Also note that tsk is likely to be in !TASK_RUNNING state so an * early test for tsk->state != TASK_RUNNING before wake_up_process() * is unlikely to be beneficial. */ - if (tsk) - wakeup = wake_up_process(tsk); + if (intel_engine_has_waiter(engine)) { + struct task_struct *tsk; + + rcu_read_lock(); + tsk = rcu_dereference(engine->breadcrumbs.irq_seqno_bh); + if (tsk) + wakeup = wake_up_process(tsk); + rcu_read_unlock(); + } + return wakeup; } -void intel_engine_enable_fake_irq(struct intel_engine_cs *engine); void intel_engine_fini_breadcrumbs(struct intel_engine_cs *engine); unsigned int intel_kick_waiters(struct drm_i915_private *i915); unsigned int intel_kick_signalers(struct drm_i915_private *i915); +static inline bool intel_engine_is_active(struct intel_engine_cs *engine) +{ + return i915_gem_active_isset(&engine->last_request); +} + #endif /* _INTEL_RINGBUFFER_H_ */ diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index 1c603bb..6c11168 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -287,6 +287,7 @@ void intel_display_set_init_power(struct drm_i915_private *dev_priv, */ static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv) { + struct pci_dev *pdev = dev_priv->drm.pdev; struct drm_device *dev = &dev_priv->drm; /* @@ -299,9 +300,9 @@ static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv) * sure vgacon can keep working normally without triggering interrupts * and error messages. */ - vga_get_uninterruptible(dev->pdev, VGA_RSRC_LEGACY_IO); + vga_get_uninterruptible(pdev, VGA_RSRC_LEGACY_IO); outb(inb(VGA_MSR_READ), VGA_MSR_WRITE); - vga_put(dev->pdev, VGA_RSRC_LEGACY_IO); + vga_put(pdev, VGA_RSRC_LEGACY_IO); if (IS_BROADWELL(dev)) gen8_irq_power_well_post_enable(dev_priv, @@ -318,7 +319,7 @@ static void hsw_power_well_pre_disable(struct drm_i915_private *dev_priv) static void skl_power_well_post_enable(struct drm_i915_private *dev_priv, struct i915_power_well *power_well) { - struct drm_device *dev = &dev_priv->drm; + struct pci_dev *pdev = dev_priv->drm.pdev; /* * After we re-enable the power well, if we touch VGA register 0x3d5 @@ -331,9 +332,9 @@ static void skl_power_well_post_enable(struct drm_i915_private *dev_priv, * and error messages. */ if (power_well->data == SKL_DISP_PW_2) { - vga_get_uninterruptible(dev->pdev, VGA_RSRC_LEGACY_IO); + vga_get_uninterruptible(pdev, VGA_RSRC_LEGACY_IO); outb(inb(VGA_MSR_READ), VGA_MSR_WRITE); - vga_put(dev->pdev, VGA_RSRC_LEGACY_IO); + vga_put(pdev, VGA_RSRC_LEGACY_IO); gen8_irq_power_well_post_enable(dev_priv, 1 << PIPE_C | 1 << PIPE_B); @@ -592,6 +593,8 @@ void bxt_disable_dc9(struct drm_i915_private *dev_priv) DRM_DEBUG_KMS("Disabling DC9\n"); gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); + + intel_pps_unlock_regs_wa(dev_priv); } static void assert_csr_loaded(struct drm_i915_private *dev_priv) @@ -854,7 +857,7 @@ static void bxt_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, struct i915_power_well *power_well) { enum skl_disp_power_wells power_well_id = power_well->data; - struct i915_power_well *cmn_a_well; + struct i915_power_well *cmn_a_well = NULL; if (power_well_id == BXT_DPIO_CMN_BC) { /* @@ -867,7 +870,7 @@ static void bxt_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, bxt_ddi_phy_init(dev_priv, bxt_power_well_to_phy(power_well)); - if (power_well_id == BXT_DPIO_CMN_BC) + if (cmn_a_well) intel_power_well_put(dev_priv, cmn_a_well); } @@ -1121,6 +1124,8 @@ static void vlv_display_power_well_init(struct drm_i915_private *dev_priv) } i915_redisable_vga_power_on(&dev_priv->drm); + + intel_pps_unlock_regs_wa(dev_priv); } static void vlv_display_power_well_deinit(struct drm_i915_private *dev_priv) @@ -2284,7 +2289,7 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv) */ void intel_power_domains_fini(struct drm_i915_private *dev_priv) { - struct device *device = &dev_priv->drm.pdev->dev; + struct device *kdev = &dev_priv->drm.pdev->dev; /* * The i915.ko module is still not prepared to be loaded when @@ -2306,7 +2311,7 @@ void intel_power_domains_fini(struct drm_i915_private *dev_priv) * the platform doesn't support runtime PM. */ if (!HAS_RUNTIME_PM(dev_priv)) - pm_runtime_put(device); + pm_runtime_put(kdev); } static void intel_power_domains_sync_hw(struct drm_i915_private *dev_priv) @@ -2647,10 +2652,10 @@ void intel_power_domains_suspend(struct drm_i915_private *dev_priv) */ void intel_runtime_pm_get(struct drm_i915_private *dev_priv) { - struct drm_device *dev = &dev_priv->drm; - struct device *device = &dev->pdev->dev; + struct pci_dev *pdev = dev_priv->drm.pdev; + struct device *kdev = &pdev->dev; - pm_runtime_get_sync(device); + pm_runtime_get_sync(kdev); atomic_inc(&dev_priv->pm.wakeref_count); assert_rpm_wakelock_held(dev_priv); @@ -2668,11 +2673,11 @@ void intel_runtime_pm_get(struct drm_i915_private *dev_priv) */ bool intel_runtime_pm_get_if_in_use(struct drm_i915_private *dev_priv) { - struct drm_device *dev = &dev_priv->drm; - struct device *device = &dev->pdev->dev; + struct pci_dev *pdev = dev_priv->drm.pdev; + struct device *kdev = &pdev->dev; if (IS_ENABLED(CONFIG_PM)) { - int ret = pm_runtime_get_if_in_use(device); + int ret = pm_runtime_get_if_in_use(kdev); /* * In cases runtime PM is disabled by the RPM core and we get @@ -2710,11 +2715,11 @@ bool intel_runtime_pm_get_if_in_use(struct drm_i915_private *dev_priv) */ void intel_runtime_pm_get_noresume(struct drm_i915_private *dev_priv) { - struct drm_device *dev = &dev_priv->drm; - struct device *device = &dev->pdev->dev; + struct pci_dev *pdev = dev_priv->drm.pdev; + struct device *kdev = &pdev->dev; assert_rpm_wakelock_held(dev_priv); - pm_runtime_get_noresume(device); + pm_runtime_get_noresume(kdev); atomic_inc(&dev_priv->pm.wakeref_count); } @@ -2729,15 +2734,15 @@ void intel_runtime_pm_get_noresume(struct drm_i915_private *dev_priv) */ void intel_runtime_pm_put(struct drm_i915_private *dev_priv) { - struct drm_device *dev = &dev_priv->drm; - struct device *device = &dev->pdev->dev; + struct pci_dev *pdev = dev_priv->drm.pdev; + struct device *kdev = &pdev->dev; assert_rpm_wakelock_held(dev_priv); if (atomic_dec_and_test(&dev_priv->pm.wakeref_count)) atomic_inc(&dev_priv->pm.atomic_seq); - pm_runtime_mark_last_busy(device); - pm_runtime_put_autosuspend(device); + pm_runtime_mark_last_busy(kdev); + pm_runtime_put_autosuspend(kdev); } /** @@ -2752,11 +2757,12 @@ void intel_runtime_pm_put(struct drm_i915_private *dev_priv) */ void intel_runtime_pm_enable(struct drm_i915_private *dev_priv) { + struct pci_dev *pdev = dev_priv->drm.pdev; struct drm_device *dev = &dev_priv->drm; - struct device *device = &dev->pdev->dev; + struct device *kdev = &pdev->dev; - pm_runtime_set_autosuspend_delay(device, 10000); /* 10s */ - pm_runtime_mark_last_busy(device); + pm_runtime_set_autosuspend_delay(kdev, 10000); /* 10s */ + pm_runtime_mark_last_busy(kdev); /* * Take a permanent reference to disable the RPM functionality and drop @@ -2765,10 +2771,10 @@ void intel_runtime_pm_enable(struct drm_i915_private *dev_priv) * platforms without RPM support. */ if (!HAS_RUNTIME_PM(dev)) { - pm_runtime_dont_use_autosuspend(device); - pm_runtime_get_sync(device); + pm_runtime_dont_use_autosuspend(kdev); + pm_runtime_get_sync(kdev); } else { - pm_runtime_use_autosuspend(device); + pm_runtime_use_autosuspend(kdev); } /* @@ -2776,6 +2782,5 @@ void intel_runtime_pm_enable(struct drm_i915_private *dev_priv) * We drop that here and will reacquire it during unloading in * intel_power_domains_fini(). */ - pm_runtime_put_autosuspend(device); + pm_runtime_put_autosuspend(kdev); } - diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index e378f35..c551024 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1003,24 +1003,22 @@ static bool intel_sdvo_write_infoframe(struct intel_sdvo *intel_sdvo, } static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo, - const struct drm_display_mode *adjusted_mode) + struct intel_crtc_state *pipe_config) { uint8_t sdvo_data[HDMI_INFOFRAME_SIZE(AVI)]; - struct drm_crtc *crtc = intel_sdvo->base.base.crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); union hdmi_infoframe frame; int ret; ssize_t len; ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, - adjusted_mode); + &pipe_config->base.adjusted_mode); if (ret < 0) { DRM_ERROR("couldn't fill AVI infoframe\n"); return false; } if (intel_sdvo->rgb_quant_range_selectable) { - if (intel_crtc->config->limited_color_range) + if (pipe_config->limited_color_range) frame.avi.quantization_range = HDMI_QUANTIZATION_RANGE_LIMITED; else @@ -1125,7 +1123,8 @@ static void i9xx_adjust_sdvo_tv_clock(struct intel_crtc_state *pipe_config) } static bool intel_sdvo_compute_config(struct intel_encoder *encoder, - struct intel_crtc_state *pipe_config) + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct intel_sdvo *intel_sdvo = to_sdvo(encoder); struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; @@ -1192,22 +1191,21 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder, return true; } -static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder) +static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder, + struct intel_crtc_state *crtc_state, + struct drm_connector_state *conn_state) { struct drm_device *dev = intel_encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *crtc = to_intel_crtc(intel_encoder->base.crtc); - const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode; - struct drm_display_mode *mode = &crtc->config->base.mode; + struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc); + const struct drm_display_mode *adjusted_mode = &crtc_state->base.adjusted_mode; + struct drm_display_mode *mode = &crtc_state->base.mode; struct intel_sdvo *intel_sdvo = to_sdvo(intel_encoder); u32 sdvox; struct intel_sdvo_in_out_map in_out; struct intel_sdvo_dtd input_dtd, output_dtd; int rate; - if (!mode) - return; - /* First, set the input mapping for the first input to our controlled * output. This is only correct if we're a single-input device, in * which case the first input is the output from the appropriate SDVO @@ -1240,11 +1238,11 @@ static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder) if (!intel_sdvo_set_target_input(intel_sdvo)) return; - if (crtc->config->has_hdmi_sink) { + if (crtc_state->has_hdmi_sink) { intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_HDMI); intel_sdvo_set_colorimetry(intel_sdvo, SDVO_COLORIMETRY_RGB256); - intel_sdvo_set_avi_infoframe(intel_sdvo, adjusted_mode); + intel_sdvo_set_avi_infoframe(intel_sdvo, crtc_state); } else intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_DVI); @@ -1260,7 +1258,7 @@ static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder) DRM_INFO("Setting input timings on %s failed\n", SDVO_NAME(intel_sdvo)); - switch (crtc->config->pixel_multiplier) { + switch (crtc_state->pixel_multiplier) { default: WARN(1, "unknown pixel multiplier specified\n"); case 1: rate = SDVO_CLOCK_RATE_MULT_1X; break; @@ -1275,7 +1273,7 @@ static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder) /* The real mode polarity is set by the SDVO commands, using * struct intel_sdvo_dtd. */ sdvox = SDVO_VSYNC_ACTIVE_HIGH | SDVO_HSYNC_ACTIVE_HIGH; - if (!HAS_PCH_SPLIT(dev) && crtc->config->limited_color_range) + if (!HAS_PCH_SPLIT(dev) && crtc_state->limited_color_range) sdvox |= HDMI_COLOR_RANGE_16_235; if (INTEL_INFO(dev)->gen < 5) sdvox |= SDVO_BORDER_ENABLE; @@ -1301,7 +1299,7 @@ static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder) } else if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) { /* done in crtc_mode_set as it lives inside the dpll register */ } else { - sdvox |= (crtc->config->pixel_multiplier - 1) + sdvox |= (crtc_state->pixel_multiplier - 1) << SDVO_PORT_MULTIPLY_SHIFT; } @@ -1434,7 +1432,9 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder, pipe_config->pixel_multiplier, encoder_pixel_multiplier); } -static void intel_disable_sdvo(struct intel_encoder *encoder) +static void intel_disable_sdvo(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *conn_state) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_sdvo *intel_sdvo = to_sdvo(encoder); @@ -1477,16 +1477,22 @@ static void intel_disable_sdvo(struct intel_encoder *encoder) } } -static void pch_disable_sdvo(struct intel_encoder *encoder) +static void pch_disable_sdvo(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { } -static void pch_post_disable_sdvo(struct intel_encoder *encoder) +static void pch_post_disable_sdvo(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { - intel_disable_sdvo(encoder); + intel_disable_sdvo(encoder, old_crtc_state, old_conn_state); } -static void intel_enable_sdvo(struct intel_encoder *encoder) +static void intel_enable_sdvo(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); @@ -2930,10 +2936,12 @@ static bool intel_sdvo_init_ddc_proxy(struct intel_sdvo *sdvo, struct drm_device *dev) { + struct pci_dev *pdev = dev->pdev; + sdvo->ddc.owner = THIS_MODULE; sdvo->ddc.class = I2C_CLASS_DDC; snprintf(sdvo->ddc.name, I2C_NAME_SIZE, "SDVO DDC proxy"); - sdvo->ddc.dev.parent = &dev->pdev->dev; + sdvo->ddc.dev.parent = &pdev->dev; sdvo->ddc.algo_data = sdvo; sdvo->ddc.algo = &intel_sdvo_ddc_proxy; diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 7c08e4f..73a521f 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -36,6 +36,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_plane_helper.h> #include "intel_drv.h" +#include "intel_frontbuffer.h" #include <drm/i915_drm.h> #include "i915_drv.h" @@ -202,23 +203,24 @@ skl_update_plane(struct drm_plane *drm_plane, struct drm_i915_private *dev_priv = to_i915(dev); struct intel_plane *intel_plane = to_intel_plane(drm_plane); struct drm_framebuffer *fb = plane_state->base.fb; - struct drm_i915_gem_object *obj = intel_fb_obj(fb); + const struct skl_wm_values *wm = &dev_priv->wm.skl_results; + struct drm_crtc *crtc = crtc_state->base.crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); const int pipe = intel_plane->pipe; const int plane = intel_plane->plane + 1; - u32 plane_ctl, stride_div, stride; + u32 plane_ctl; const struct drm_intel_sprite_colorkey *key = &plane_state->ckey; - u32 surf_addr; - u32 tile_height, plane_offset, plane_size; + u32 surf_addr = plane_state->main.offset; unsigned int rotation = plane_state->base.rotation; - int x_offset, y_offset; - int crtc_x = plane_state->dst.x1; - int crtc_y = plane_state->dst.y1; - uint32_t crtc_w = drm_rect_width(&plane_state->dst); - uint32_t crtc_h = drm_rect_height(&plane_state->dst); - uint32_t x = plane_state->src.x1 >> 16; - uint32_t y = plane_state->src.y1 >> 16; - uint32_t src_w = drm_rect_width(&plane_state->src) >> 16; - uint32_t src_h = drm_rect_height(&plane_state->src) >> 16; + u32 stride = skl_plane_stride(fb, 0, rotation); + int crtc_x = plane_state->base.dst.x1; + int crtc_y = plane_state->base.dst.y1; + uint32_t crtc_w = drm_rect_width(&plane_state->base.dst); + uint32_t crtc_h = drm_rect_height(&plane_state->base.dst); + uint32_t x = plane_state->main.x; + uint32_t y = plane_state->main.y; + uint32_t src_w = drm_rect_width(&plane_state->base.src) >> 16; + uint32_t src_h = drm_rect_height(&plane_state->base.src) >> 16; plane_ctl = PLANE_CTL_ENABLE | PLANE_CTL_PIPE_GAMMA_ENABLE | @@ -229,14 +231,8 @@ skl_update_plane(struct drm_plane *drm_plane, plane_ctl |= skl_plane_ctl_rotation(rotation); - stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0], - fb->pixel_format); - - /* Sizes are 0 based */ - src_w--; - src_h--; - crtc_w--; - crtc_h--; + if (wm->dirty_pipes & drm_crtc_mask(crtc)) + skl_write_plane_wm(intel_crtc, wm, plane); if (key->flags) { I915_WRITE(PLANE_KEYVAL(pipe, plane), key->min_value); @@ -249,28 +245,15 @@ skl_update_plane(struct drm_plane *drm_plane, else if (key->flags & I915_SET_COLORKEY_SOURCE) plane_ctl |= PLANE_CTL_KEY_ENABLE_SOURCE; - surf_addr = intel_plane_obj_offset(intel_plane, obj, 0); - - if (intel_rotation_90_or_270(rotation)) { - int cpp = drm_format_plane_cpp(fb->pixel_format, 0); - - /* stride: Surface height in tiles */ - tile_height = intel_tile_height(dev_priv, fb->modifier[0], cpp); - stride = DIV_ROUND_UP(fb->height, tile_height); - plane_size = (src_w << 16) | src_h; - x_offset = stride * tile_height - y - (src_h + 1); - y_offset = x; - } else { - stride = fb->pitches[0] / stride_div; - plane_size = (src_h << 16) | src_w; - x_offset = x; - y_offset = y; - } - plane_offset = y_offset << 16 | x_offset; + /* Sizes are 0 based */ + src_w--; + src_h--; + crtc_w--; + crtc_h--; - I915_WRITE(PLANE_OFFSET(pipe, plane), plane_offset); + I915_WRITE(PLANE_OFFSET(pipe, plane), (y << 16) | x); I915_WRITE(PLANE_STRIDE(pipe, plane), stride); - I915_WRITE(PLANE_SIZE(pipe, plane), plane_size); + I915_WRITE(PLANE_SIZE(pipe, plane), (src_h << 16) | src_w); /* program plane scaler */ if (plane_state->scaler_id >= 0) { @@ -295,7 +278,8 @@ skl_update_plane(struct drm_plane *drm_plane, } I915_WRITE(PLANE_CTL(pipe, plane), plane_ctl); - I915_WRITE(PLANE_SURF(pipe, plane), surf_addr); + I915_WRITE(PLANE_SURF(pipe, plane), + intel_fb_gtt_offset(fb, rotation) + surf_addr); POSTING_READ(PLANE_SURF(pipe, plane)); } @@ -308,6 +292,14 @@ skl_disable_plane(struct drm_plane *dplane, struct drm_crtc *crtc) const int pipe = intel_plane->pipe; const int plane = intel_plane->plane + 1; + /* + * We only populate skl_results on watermark updates, and if the + * plane's visiblity isn't actually changing neither is its watermarks. + */ + if (!dplane->state->visible) + skl_write_plane_wm(to_intel_crtc(crtc), + &dev_priv->wm.skl_results, plane); + I915_WRITE(PLANE_CTL(pipe, plane), 0); I915_WRITE(PLANE_SURF(pipe, plane), 0); @@ -362,22 +354,20 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_i915_private *dev_priv = to_i915(dev); struct intel_plane *intel_plane = to_intel_plane(dplane); struct drm_framebuffer *fb = plane_state->base.fb; - struct drm_i915_gem_object *obj = intel_fb_obj(fb); int pipe = intel_plane->pipe; int plane = intel_plane->plane; u32 sprctl; u32 sprsurf_offset, linear_offset; unsigned int rotation = dplane->state->rotation; - int cpp = drm_format_plane_cpp(fb->pixel_format, 0); const struct drm_intel_sprite_colorkey *key = &plane_state->ckey; - int crtc_x = plane_state->dst.x1; - int crtc_y = plane_state->dst.y1; - uint32_t crtc_w = drm_rect_width(&plane_state->dst); - uint32_t crtc_h = drm_rect_height(&plane_state->dst); - uint32_t x = plane_state->src.x1 >> 16; - uint32_t y = plane_state->src.y1 >> 16; - uint32_t src_w = drm_rect_width(&plane_state->src) >> 16; - uint32_t src_h = drm_rect_height(&plane_state->src) >> 16; + int crtc_x = plane_state->base.dst.x1; + int crtc_y = plane_state->base.dst.y1; + uint32_t crtc_w = drm_rect_width(&plane_state->base.dst); + uint32_t crtc_h = drm_rect_height(&plane_state->base.dst); + uint32_t x = plane_state->base.src.x1 >> 16; + uint32_t y = plane_state->base.src.y1 >> 16; + uint32_t src_w = drm_rect_width(&plane_state->base.src) >> 16; + uint32_t src_h = drm_rect_height(&plane_state->base.src) >> 16; sprctl = SP_ENABLE; @@ -430,7 +420,7 @@ vlv_update_plane(struct drm_plane *dplane, */ sprctl |= SP_GAMMA_ENABLE; - if (obj->tiling_mode != I915_TILING_NONE) + if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) sprctl |= SP_TILED; /* Sizes are 0 based */ @@ -439,19 +429,18 @@ vlv_update_plane(struct drm_plane *dplane, crtc_w--; crtc_h--; - linear_offset = y * fb->pitches[0] + x * cpp; - sprsurf_offset = intel_compute_tile_offset(&x, &y, fb, 0, - fb->pitches[0], rotation); - linear_offset -= sprsurf_offset; + intel_add_fb_offsets(&x, &y, plane_state, 0); + sprsurf_offset = intel_compute_tile_offset(&x, &y, plane_state, 0); - if (rotation == BIT(DRM_ROTATE_180)) { + if (rotation == DRM_ROTATE_180) { sprctl |= SP_ROTATE_180; x += src_w; y += src_h; - linear_offset += src_h * fb->pitches[0] + src_w * cpp; } + linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0); + if (key->flags) { I915_WRITE(SPKEYMINVAL(pipe, plane), key->min_value); I915_WRITE(SPKEYMAXVAL(pipe, plane), key->max_value); @@ -467,7 +456,7 @@ vlv_update_plane(struct drm_plane *dplane, I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]); I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x); - if (obj->tiling_mode != I915_TILING_NONE) + if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) I915_WRITE(SPTILEOFF(pipe, plane), (y << 16) | x); else I915_WRITE(SPLINOFF(pipe, plane), linear_offset); @@ -476,8 +465,8 @@ vlv_update_plane(struct drm_plane *dplane, I915_WRITE(SPSIZE(pipe, plane), (crtc_h << 16) | crtc_w); I915_WRITE(SPCNTR(pipe, plane), sprctl); - I915_WRITE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) + - sprsurf_offset); + I915_WRITE(SPSURF(pipe, plane), + intel_fb_gtt_offset(fb, rotation) + sprsurf_offset); POSTING_READ(SPSURF(pipe, plane)); } @@ -505,21 +494,19 @@ ivb_update_plane(struct drm_plane *plane, struct drm_i915_private *dev_priv = to_i915(dev); struct intel_plane *intel_plane = to_intel_plane(plane); struct drm_framebuffer *fb = plane_state->base.fb; - struct drm_i915_gem_object *obj = intel_fb_obj(fb); enum pipe pipe = intel_plane->pipe; u32 sprctl, sprscale = 0; u32 sprsurf_offset, linear_offset; unsigned int rotation = plane_state->base.rotation; - int cpp = drm_format_plane_cpp(fb->pixel_format, 0); const struct drm_intel_sprite_colorkey *key = &plane_state->ckey; - int crtc_x = plane_state->dst.x1; - int crtc_y = plane_state->dst.y1; - uint32_t crtc_w = drm_rect_width(&plane_state->dst); - uint32_t crtc_h = drm_rect_height(&plane_state->dst); - uint32_t x = plane_state->src.x1 >> 16; - uint32_t y = plane_state->src.y1 >> 16; - uint32_t src_w = drm_rect_width(&plane_state->src) >> 16; - uint32_t src_h = drm_rect_height(&plane_state->src) >> 16; + int crtc_x = plane_state->base.dst.x1; + int crtc_y = plane_state->base.dst.y1; + uint32_t crtc_w = drm_rect_width(&plane_state->base.dst); + uint32_t crtc_h = drm_rect_height(&plane_state->base.dst); + uint32_t x = plane_state->base.src.x1 >> 16; + uint32_t y = plane_state->base.src.y1 >> 16; + uint32_t src_w = drm_rect_width(&plane_state->base.src) >> 16; + uint32_t src_h = drm_rect_height(&plane_state->base.src) >> 16; sprctl = SPRITE_ENABLE; @@ -552,7 +539,7 @@ ivb_update_plane(struct drm_plane *plane, */ sprctl |= SPRITE_GAMMA_ENABLE; - if (obj->tiling_mode != I915_TILING_NONE) + if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) sprctl |= SPRITE_TILED; if (IS_HASWELL(dev) || IS_BROADWELL(dev)) @@ -572,22 +559,21 @@ ivb_update_plane(struct drm_plane *plane, if (crtc_w != src_w || crtc_h != src_h) sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h; - linear_offset = y * fb->pitches[0] + x * cpp; - sprsurf_offset = intel_compute_tile_offset(&x, &y, fb, 0, - fb->pitches[0], rotation); - linear_offset -= sprsurf_offset; + intel_add_fb_offsets(&x, &y, plane_state, 0); + sprsurf_offset = intel_compute_tile_offset(&x, &y, plane_state, 0); - if (rotation == BIT(DRM_ROTATE_180)) { + if (rotation == DRM_ROTATE_180) { sprctl |= SPRITE_ROTATE_180; /* HSW and BDW does this automagically in hardware */ if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) { x += src_w; y += src_h; - linear_offset += src_h * fb->pitches[0] + src_w * cpp; } } + linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0); + if (key->flags) { I915_WRITE(SPRKEYVAL(pipe), key->min_value); I915_WRITE(SPRKEYMAX(pipe), key->max_value); @@ -606,7 +592,7 @@ ivb_update_plane(struct drm_plane *plane, * register */ if (IS_HASWELL(dev) || IS_BROADWELL(dev)) I915_WRITE(SPROFFSET(pipe), (y << 16) | x); - else if (obj->tiling_mode != I915_TILING_NONE) + else if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) I915_WRITE(SPRTILEOFF(pipe), (y << 16) | x); else I915_WRITE(SPRLINOFF(pipe), linear_offset); @@ -616,7 +602,7 @@ ivb_update_plane(struct drm_plane *plane, I915_WRITE(SPRSCALE(pipe), sprscale); I915_WRITE(SPRCTL(pipe), sprctl); I915_WRITE(SPRSURF(pipe), - i915_gem_obj_ggtt_offset(obj) + sprsurf_offset); + intel_fb_gtt_offset(fb, rotation) + sprsurf_offset); POSTING_READ(SPRSURF(pipe)); } @@ -646,21 +632,19 @@ ilk_update_plane(struct drm_plane *plane, struct drm_i915_private *dev_priv = to_i915(dev); struct intel_plane *intel_plane = to_intel_plane(plane); struct drm_framebuffer *fb = plane_state->base.fb; - struct drm_i915_gem_object *obj = intel_fb_obj(fb); int pipe = intel_plane->pipe; u32 dvscntr, dvsscale; u32 dvssurf_offset, linear_offset; unsigned int rotation = plane_state->base.rotation; - int cpp = drm_format_plane_cpp(fb->pixel_format, 0); const struct drm_intel_sprite_colorkey *key = &plane_state->ckey; - int crtc_x = plane_state->dst.x1; - int crtc_y = plane_state->dst.y1; - uint32_t crtc_w = drm_rect_width(&plane_state->dst); - uint32_t crtc_h = drm_rect_height(&plane_state->dst); - uint32_t x = plane_state->src.x1 >> 16; - uint32_t y = plane_state->src.y1 >> 16; - uint32_t src_w = drm_rect_width(&plane_state->src) >> 16; - uint32_t src_h = drm_rect_height(&plane_state->src) >> 16; + int crtc_x = plane_state->base.dst.x1; + int crtc_y = plane_state->base.dst.y1; + uint32_t crtc_w = drm_rect_width(&plane_state->base.dst); + uint32_t crtc_h = drm_rect_height(&plane_state->base.dst); + uint32_t x = plane_state->base.src.x1 >> 16; + uint32_t y = plane_state->base.src.y1 >> 16; + uint32_t src_w = drm_rect_width(&plane_state->base.src) >> 16; + uint32_t src_h = drm_rect_height(&plane_state->base.src) >> 16; dvscntr = DVS_ENABLE; @@ -693,7 +677,7 @@ ilk_update_plane(struct drm_plane *plane, */ dvscntr |= DVS_GAMMA_ENABLE; - if (obj->tiling_mode != I915_TILING_NONE) + if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) dvscntr |= DVS_TILED; if (IS_GEN6(dev)) @@ -709,19 +693,18 @@ ilk_update_plane(struct drm_plane *plane, if (crtc_w != src_w || crtc_h != src_h) dvsscale = DVS_SCALE_ENABLE | (src_w << 16) | src_h; - linear_offset = y * fb->pitches[0] + x * cpp; - dvssurf_offset = intel_compute_tile_offset(&x, &y, fb, 0, - fb->pitches[0], rotation); - linear_offset -= dvssurf_offset; + intel_add_fb_offsets(&x, &y, plane_state, 0); + dvssurf_offset = intel_compute_tile_offset(&x, &y, plane_state, 0); - if (rotation == BIT(DRM_ROTATE_180)) { + if (rotation == DRM_ROTATE_180) { dvscntr |= DVS_ROTATE_180; x += src_w; y += src_h; - linear_offset += src_h * fb->pitches[0] + src_w * cpp; } + linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0); + if (key->flags) { I915_WRITE(DVSKEYVAL(pipe), key->min_value); I915_WRITE(DVSKEYMAX(pipe), key->max_value); @@ -736,7 +719,7 @@ ilk_update_plane(struct drm_plane *plane, I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]); I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x); - if (obj->tiling_mode != I915_TILING_NONE) + if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) I915_WRITE(DVSTILEOFF(pipe), (y << 16) | x); else I915_WRITE(DVSLINOFF(pipe), linear_offset); @@ -745,7 +728,7 @@ ilk_update_plane(struct drm_plane *plane, I915_WRITE(DVSSCALE(pipe), dvsscale); I915_WRITE(DVSCNTR(pipe), dvscntr); I915_WRITE(DVSSURF(pipe), - i915_gem_obj_ggtt_offset(obj) + dvssurf_offset); + intel_fb_gtt_offset(fb, rotation) + dvssurf_offset); POSTING_READ(DVSSURF(pipe)); } @@ -778,15 +761,26 @@ intel_check_sprite_plane(struct drm_plane *plane, int crtc_x, crtc_y; unsigned int crtc_w, crtc_h; uint32_t src_x, src_y, src_w, src_h; - struct drm_rect *src = &state->src; - struct drm_rect *dst = &state->dst; + struct drm_rect *src = &state->base.src; + struct drm_rect *dst = &state->base.dst; const struct drm_rect *clip = &state->clip; int hscale, vscale; int max_scale, min_scale; bool can_scale; + int ret; + + src->x1 = state->base.src_x; + src->y1 = state->base.src_y; + src->x2 = state->base.src_x + state->base.src_w; + src->y2 = state->base.src_y + state->base.src_h; + + dst->x1 = state->base.crtc_x; + dst->y1 = state->base.crtc_y; + dst->x2 = state->base.crtc_x + state->base.crtc_w; + dst->y2 = state->base.crtc_y + state->base.crtc_h; if (!fb) { - state->visible = false; + state->base.visible = false; return 0; } @@ -834,14 +828,14 @@ intel_check_sprite_plane(struct drm_plane *plane, vscale = drm_rect_calc_vscale_relaxed(src, dst, min_scale, max_scale); BUG_ON(vscale < 0); - state->visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale); + state->base.visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale); crtc_x = dst->x1; crtc_y = dst->y1; crtc_w = drm_rect_width(dst); crtc_h = drm_rect_height(dst); - if (state->visible) { + if (state->base.visible) { /* check again in case clipping clamped the results */ hscale = drm_rect_calc_hscale(src, dst, min_scale, max_scale); if (hscale < 0) { @@ -898,12 +892,12 @@ intel_check_sprite_plane(struct drm_plane *plane, crtc_w &= ~1; if (crtc_w == 0) - state->visible = false; + state->base.visible = false; } } /* Check size restrictions when scaling */ - if (state->visible && (src_w != crtc_w || src_h != crtc_h)) { + if (state->base.visible && (src_w != crtc_w || src_h != crtc_h)) { unsigned int width_bytes; int cpp = drm_format_plane_cpp(fb->pixel_format, 0); @@ -912,10 +906,10 @@ intel_check_sprite_plane(struct drm_plane *plane, /* FIXME interlacing min height is 6 */ if (crtc_w < 3 || crtc_h < 3) - state->visible = false; + state->base.visible = false; if (src_w < 3 || src_h < 3) - state->visible = false; + state->base.visible = false; width_bytes = ((src_x * cpp) & 63) + src_w * cpp; @@ -926,7 +920,7 @@ intel_check_sprite_plane(struct drm_plane *plane, } } - if (state->visible) { + if (state->base.visible) { src->x1 = src_x << 16; src->x2 = (src_x + src_w) << 16; src->y1 = src_y << 16; @@ -938,6 +932,12 @@ intel_check_sprite_plane(struct drm_plane *plane, dst->y1 = crtc_y; dst->y2 = crtc_y + crtc_h; + if (INTEL_GEN(dev) >= 9) { + ret = skl_check_plane_surface(state); + if (ret) + return ret; + } + return 0; } diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 49136ad..d960e48 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -838,7 +838,9 @@ intel_tv_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe) } static void -intel_enable_tv(struct intel_encoder *encoder) +intel_enable_tv(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); @@ -851,7 +853,9 @@ intel_enable_tv(struct intel_encoder *encoder) } static void -intel_disable_tv(struct intel_encoder *encoder) +intel_disable_tv(struct intel_encoder *encoder, + struct intel_crtc_state *old_crtc_state, + struct drm_connector_state *old_conn_state) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); @@ -908,7 +912,8 @@ intel_tv_get_config(struct intel_encoder *encoder, static bool intel_tv_compute_config(struct intel_encoder *encoder, - struct intel_crtc_state *pipe_config) + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct intel_tv *intel_tv = enc_to_tv(encoder); const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); @@ -1010,7 +1015,9 @@ static void set_color_conversion(struct drm_i915_private *dev_priv, color_conversion->av); } -static void intel_tv_pre_enable(struct intel_encoder *encoder) +static void intel_tv_pre_enable(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, + struct drm_connector_state *conn_state) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index ff80a81..a9b6c93 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -435,7 +435,7 @@ void intel_uncore_sanitize(struct drm_i915_private *dev_priv) i915.enable_rc6 = sanitize_rc6_option(dev_priv, i915.enable_rc6); /* BIOS often leaves RC6 enabled, but disable it for hw init */ - intel_disable_gt_powersave(dev_priv); + intel_sanitize_gt_powersave(dev_priv); } static void __intel_uncore_forcewake_get(struct drm_i915_private *dev_priv, @@ -1018,11 +1018,9 @@ gen5_write##x(struct drm_i915_private *dev_priv, i915_reg_t reg, u##x val, bool __gen5_write(8) __gen5_write(16) __gen5_write(32) -__gen5_write(64) __gen2_write(8) __gen2_write(16) __gen2_write(32) -__gen2_write(64) #undef __gen5_write #undef __gen2_write @@ -1112,23 +1110,18 @@ gen9_write##x(struct drm_i915_private *dev_priv, i915_reg_t reg, u##x val, \ __gen9_write(8) __gen9_write(16) __gen9_write(32) -__gen9_write(64) __chv_write(8) __chv_write(16) __chv_write(32) -__chv_write(64) __gen8_write(8) __gen8_write(16) __gen8_write(32) -__gen8_write(64) __hsw_write(8) __hsw_write(16) __hsw_write(32) -__hsw_write(64) __gen6_write(8) __gen6_write(16) __gen6_write(32) -__gen6_write(64) #undef __gen9_write #undef __chv_write @@ -1158,7 +1151,6 @@ static void vgpu_write##x(struct drm_i915_private *dev_priv, \ __vgpu_write(8) __vgpu_write(16) __vgpu_write(32) -__vgpu_write(64) #undef __vgpu_write #undef VGPU_WRITE_FOOTER @@ -1169,7 +1161,6 @@ do { \ dev_priv->uncore.funcs.mmio_writeb = x##_write8; \ dev_priv->uncore.funcs.mmio_writew = x##_write16; \ dev_priv->uncore.funcs.mmio_writel = x##_write32; \ - dev_priv->uncore.funcs.mmio_writeq = x##_write64; \ } while (0) #define ASSIGN_READ_MMIO_VFUNCS(x) \ @@ -1597,8 +1588,10 @@ static int gen6_reset_engines(struct drm_i915_private *dev_priv, if (engine_mask == ALL_ENGINES) { hw_mask = GEN6_GRDOM_FULL; } else { + unsigned int tmp; + hw_mask = 0; - for_each_engine_masked(engine, dev_priv, engine_mask) + for_each_engine_masked(engine, dev_priv, engine_mask, tmp) hw_mask |= hw_engine_mask[engine->id]; } @@ -1618,8 +1611,10 @@ static int gen6_reset_engines(struct drm_i915_private *dev_priv, * @timeout_ms: timeout in millisecond * * This routine waits until the target register @reg contains the expected - * @value after applying the @mask, i.e. it waits until - * (I915_READ_FW(@reg) & @mask) == @value + * @value after applying the @mask, i.e. it waits until :: + * + * (I915_READ_FW(reg) & mask) == value + * * Otherwise, the wait will timeout after @timeout_ms milliseconds. * * Note that this routine assumes the caller holds forcewake asserted, it is @@ -1652,8 +1647,10 @@ int intel_wait_for_register_fw(struct drm_i915_private *dev_priv, * @timeout_ms: timeout in millisecond * * This routine waits until the target register @reg contains the expected - * @value after applying the @mask, i.e. it waits until - * (I915_READ(@reg) & @mask) == @value + * @value after applying the @mask, i.e. it waits until :: + * + * (I915_READ(reg) & mask) == value + * * Otherwise, the wait will timeout after @timeout_ms milliseconds. * * Returns 0 if the register matches the desired condition, or -ETIMEOUT. @@ -1710,15 +1707,16 @@ static int gen8_reset_engines(struct drm_i915_private *dev_priv, unsigned engine_mask) { struct intel_engine_cs *engine; + unsigned int tmp; - for_each_engine_masked(engine, dev_priv, engine_mask) + for_each_engine_masked(engine, dev_priv, engine_mask, tmp) if (gen8_request_engine_reset(engine)) goto not_ready; return gen6_reset_engines(dev_priv, engine_mask); not_ready: - for_each_engine_masked(engine, dev_priv, engine_mask) + for_each_engine_masked(engine, dev_priv, engine_mask, tmp) gen8_unrequest_engine_reset(engine); return -EIO; diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 9f7dafc..98df09c 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -16,7 +16,6 @@ #include <linux/component.h> #include <linux/device.h> #include <linux/dma-buf.h> -#include <linux/fb.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/reservation.h> @@ -58,12 +57,6 @@ static int legacyfb_depth = 16; module_param(legacyfb_depth, int, 0444); #endif -unsigned int imx_drm_crtc_id(struct imx_drm_crtc *crtc) -{ - return drm_crtc_index(crtc->crtc); -} -EXPORT_SYMBOL_GPL(imx_drm_crtc_id); - static void imx_drm_driver_lastclose(struct drm_device *drm) { struct imx_drm_device *imxdrm = drm->dev_private; @@ -71,43 +64,6 @@ static void imx_drm_driver_lastclose(struct drm_device *drm) drm_fbdev_cma_restore_mode(imxdrm->fbhelper); } -static int imx_drm_driver_unload(struct drm_device *drm) -{ - struct imx_drm_device *imxdrm = drm->dev_private; - - drm_kms_helper_poll_fini(drm); - - if (imxdrm->fbhelper) - drm_fbdev_cma_fini(imxdrm->fbhelper); - - component_unbind_all(drm->dev, drm); - - drm_vblank_cleanup(drm); - drm_mode_config_cleanup(drm); - - platform_set_drvdata(drm->platformdev, NULL); - - return 0; -} - -int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc) -{ - return drm_crtc_vblank_get(imx_drm_crtc->crtc); -} -EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_get); - -void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc) -{ - drm_crtc_vblank_put(imx_drm_crtc->crtc); -} -EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_put); - -void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc) -{ - drm_crtc_handle_vblank(imx_drm_crtc->crtc); -} -EXPORT_SYMBOL_GPL(imx_drm_handle_vblank); - static int imx_drm_enable_vblank(struct drm_device *drm, unsigned int pipe) { struct imx_drm_device *imxdrm = drm->dev_private; @@ -171,54 +127,73 @@ static void imx_drm_output_poll_changed(struct drm_device *drm) drm_fbdev_cma_hotplug_event(imxdrm->fbhelper); } +static int imx_drm_atomic_check(struct drm_device *dev, + struct drm_atomic_state *state) +{ + int ret; + + ret = drm_atomic_helper_check_modeset(dev, state); + if (ret) + return ret; + + ret = drm_atomic_helper_check_planes(dev, state); + if (ret) + return ret; + + /* + * Check modeset again in case crtc_state->mode_changed is + * updated in plane's ->atomic_check callback. + */ + ret = drm_atomic_helper_check_modeset(dev, state); + if (ret) + return ret; + + return ret; +} + +static int imx_drm_atomic_commit(struct drm_device *dev, + struct drm_atomic_state *state, + bool nonblock) +{ + struct drm_plane_state *plane_state; + struct drm_plane *plane; + struct dma_buf *dma_buf; + int i; + + /* + * If the plane fb has an dma-buf attached, fish out the exclusive + * fence for the atomic helper to wait on. + */ + for_each_plane_in_state(state, plane, plane_state, i) { + if ((plane->state->fb != plane_state->fb) && plane_state->fb) { + dma_buf = drm_fb_cma_get_gem_obj(plane_state->fb, + 0)->base.dma_buf; + if (!dma_buf) + continue; + plane_state->fence = + reservation_object_get_excl_rcu(dma_buf->resv); + } + } + + return drm_atomic_helper_commit(dev, state, nonblock); +} + static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = { .fb_create = drm_fb_cma_create, .output_poll_changed = imx_drm_output_poll_changed, - .atomic_check = drm_atomic_helper_check, - .atomic_commit = drm_atomic_helper_commit, + .atomic_check = imx_drm_atomic_check, + .atomic_commit = imx_drm_atomic_commit, }; static void imx_drm_atomic_commit_tail(struct drm_atomic_state *state) { struct drm_device *dev = state->dev; - struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; - struct drm_plane_state *plane_state; - struct drm_gem_cma_object *cma_obj; - struct fence *excl; - unsigned shared_count; - struct fence **shared; - unsigned int i, j; - int ret; - - /* Wait for fences. */ - for_each_crtc_in_state(state, crtc, crtc_state, i) { - plane_state = crtc->primary->state; - if (plane_state->fb) { - cma_obj = drm_fb_cma_get_gem_obj(plane_state->fb, 0); - if (cma_obj->base.dma_buf) { - ret = reservation_object_get_fences_rcu( - cma_obj->base.dma_buf->resv, &excl, - &shared_count, &shared); - if (unlikely(ret)) - DRM_ERROR("failed to get fences " - "for buffer\n"); - - if (excl) { - fence_wait(excl, false); - fence_put(excl); - } - for (j = 0; j < shared_count; i++) { - fence_wait(shared[j], false); - fence_put(shared[j]); - } - } - } - } drm_atomic_helper_commit_modeset_disables(dev, state); - drm_atomic_helper_commit_planes(dev, state, true); + drm_atomic_helper_commit_planes(dev, state, + DRM_PLANE_COMMIT_ACTIVE_ONLY | + DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET); drm_atomic_helper_commit_modeset_enables(dev, state); @@ -234,111 +209,6 @@ static struct drm_mode_config_helper_funcs imx_drm_mode_config_helpers = { }; /* - * Main DRM initialisation. This binds, initialises and registers - * with DRM the subcomponents of the driver. - */ -static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) -{ - struct imx_drm_device *imxdrm; - struct drm_connector *connector; - int ret; - - imxdrm = devm_kzalloc(drm->dev, sizeof(*imxdrm), GFP_KERNEL); - if (!imxdrm) - return -ENOMEM; - - imxdrm->drm = drm; - - drm->dev_private = imxdrm; - - /* - * enable drm irq mode. - * - with irq_enabled = true, we can use the vblank feature. - * - * P.S. note that we wouldn't use drm irq handler but - * just specific driver own one instead because - * drm framework supports only one irq handler and - * drivers can well take care of their interrupts - */ - drm->irq_enabled = true; - - /* - * set max width and height as default value(4096x4096). - * this value would be used to check framebuffer size limitation - * at drm_mode_addfb(). - */ - drm->mode_config.min_width = 64; - drm->mode_config.min_height = 64; - drm->mode_config.max_width = 4096; - drm->mode_config.max_height = 4096; - drm->mode_config.funcs = &imx_drm_mode_config_funcs; - drm->mode_config.helper_private = &imx_drm_mode_config_helpers; - - drm_mode_config_init(drm); - - ret = drm_vblank_init(drm, MAX_CRTC); - if (ret) - goto err_kms; - - platform_set_drvdata(drm->platformdev, drm); - - /* Now try and bind all our sub-components */ - ret = component_bind_all(drm->dev, drm); - if (ret) - goto err_vblank; - - /* - * All components are now added, we can publish the connector sysfs - * entries to userspace. This will generate hotplug events and so - * userspace will expect to be able to access DRM at this point. - */ - list_for_each_entry(connector, &drm->mode_config.connector_list, head) { - ret = drm_connector_register(connector); - if (ret) { - dev_err(drm->dev, - "[CONNECTOR:%d:%s] drm_connector_register failed: %d\n", - connector->base.id, - connector->name, ret); - goto err_unbind; - } - } - - drm_mode_config_reset(drm); - - /* - * All components are now initialised, so setup the fb helper. - * The fb helper takes copies of key hardware information, so the - * crtcs/connectors/encoders must not change after this point. - */ -#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) - if (legacyfb_depth != 16 && legacyfb_depth != 32) { - dev_warn(drm->dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n"); - legacyfb_depth = 16; - } - imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, - drm->mode_config.num_crtc, MAX_CRTC); - if (IS_ERR(imxdrm->fbhelper)) { - ret = PTR_ERR(imxdrm->fbhelper); - imxdrm->fbhelper = NULL; - goto err_unbind; - } -#endif - - drm_kms_helper_poll_init(drm); - - return 0; - -err_unbind: - component_unbind_all(drm->dev, drm); -err_vblank: - drm_vblank_cleanup(drm); -err_kms: - drm_mode_config_cleanup(drm); - - return ret; -} - -/* * imx_drm_add_crtc - add a new crtc */ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, @@ -430,8 +300,6 @@ static const struct drm_ioctl_desc imx_drm_ioctls[] = { static struct drm_driver imx_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, - .load = imx_drm_driver_load, - .unload = imx_drm_driver_unload, .lastclose = imx_drm_driver_lastclose, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, @@ -484,12 +352,122 @@ static int compare_of(struct device *dev, void *data) static int imx_drm_bind(struct device *dev) { - return drm_platform_init(&imx_drm_driver, to_platform_device(dev)); + struct drm_device *drm; + struct imx_drm_device *imxdrm; + int ret; + + drm = drm_dev_alloc(&imx_drm_driver, dev); + if (!drm) + return -ENOMEM; + + imxdrm = devm_kzalloc(dev, sizeof(*imxdrm), GFP_KERNEL); + if (!imxdrm) { + ret = -ENOMEM; + goto err_unref; + } + + imxdrm->drm = drm; + drm->dev_private = imxdrm; + + /* + * enable drm irq mode. + * - with irq_enabled = true, we can use the vblank feature. + * + * P.S. note that we wouldn't use drm irq handler but + * just specific driver own one instead because + * drm framework supports only one irq handler and + * drivers can well take care of their interrupts + */ + drm->irq_enabled = true; + + /* + * set max width and height as default value(4096x4096). + * this value would be used to check framebuffer size limitation + * at drm_mode_addfb(). + */ + drm->mode_config.min_width = 64; + drm->mode_config.min_height = 64; + drm->mode_config.max_width = 4096; + drm->mode_config.max_height = 4096; + drm->mode_config.funcs = &imx_drm_mode_config_funcs; + drm->mode_config.helper_private = &imx_drm_mode_config_helpers; + + drm_mode_config_init(drm); + + ret = drm_vblank_init(drm, MAX_CRTC); + if (ret) + goto err_kms; + + dev_set_drvdata(dev, drm); + + /* Now try and bind all our sub-components */ + ret = component_bind_all(dev, drm); + if (ret) + goto err_vblank; + + drm_mode_config_reset(drm); + + /* + * All components are now initialised, so setup the fb helper. + * The fb helper takes copies of key hardware information, so the + * crtcs/connectors/encoders must not change after this point. + */ +#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) + if (legacyfb_depth != 16 && legacyfb_depth != 32) { + dev_warn(dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n"); + legacyfb_depth = 16; + } + imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, + drm->mode_config.num_crtc, MAX_CRTC); + if (IS_ERR(imxdrm->fbhelper)) { + ret = PTR_ERR(imxdrm->fbhelper); + imxdrm->fbhelper = NULL; + goto err_unbind; + } +#endif + + drm_kms_helper_poll_init(drm); + + ret = drm_dev_register(drm, 0); + if (ret) + goto err_fbhelper; + + return 0; + +err_fbhelper: + drm_kms_helper_poll_fini(drm); + if (imxdrm->fbhelper) + drm_fbdev_cma_fini(imxdrm->fbhelper); +err_unbind: + component_unbind_all(drm->dev, drm); +err_vblank: + drm_vblank_cleanup(drm); +err_kms: + drm_mode_config_cleanup(drm); +err_unref: + drm_dev_unref(drm); + + return ret; } static void imx_drm_unbind(struct device *dev) { - drm_put_dev(dev_get_drvdata(dev)); + struct drm_device *drm = dev_get_drvdata(dev); + struct imx_drm_device *imxdrm = drm->dev_private; + + drm_dev_unregister(drm); + + drm_kms_helper_poll_fini(drm); + + if (imxdrm->fbhelper) + drm_fbdev_cma_fini(imxdrm->fbhelper); + + drm_mode_config_cleanup(drm); + + component_unbind_all(drm->dev, drm); + dev_set_drvdata(dev, NULL); + + drm_dev_unref(drm); } static const struct component_master_ops imx_drm_ops = { diff --git a/drivers/gpu/drm/imx/imx-drm.h b/drivers/gpu/drm/imx/imx-drm.h index 07d33e4..5a91cb1 100644 --- a/drivers/gpu/drm/imx/imx-drm.h +++ b/drivers/gpu/drm/imx/imx-drm.h @@ -13,8 +13,6 @@ struct drm_plane; struct imx_drm_crtc; struct platform_device; -unsigned int imx_drm_crtc_id(struct imx_drm_crtc *crtc); - struct imx_crtc_state { struct drm_crtc_state base; u32 bus_format; @@ -44,10 +42,6 @@ int imx_drm_init_drm(struct platform_device *pdev, int preferred_bpp); int imx_drm_exit_drm(void); -int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc); -void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc); -void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc); - void imx_drm_mode_config_init(struct drm_device *drm); struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb); diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c index b03919e..3ce391c2 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/imx-ldb.c @@ -57,7 +57,11 @@ struct imx_ldb_channel { struct imx_ldb *ldb; struct drm_connector connector; struct drm_encoder encoder; + + /* Defines what is connected to the ldb, only one at a time */ struct drm_panel *panel; + struct drm_bridge *bridge; + struct device_node *child; struct i2c_adapter *ddc; int chno; @@ -66,6 +70,7 @@ struct imx_ldb_channel { struct drm_display_mode mode; int mode_valid; u32 bus_format; + u32 bus_flags; }; static inline struct imx_ldb_channel *con_to_imx_ldb_ch(struct drm_connector *c) @@ -251,11 +256,13 @@ static void imx_ldb_encoder_enable(struct drm_encoder *encoder) drm_panel_enable(imx_ldb_ch->panel); } -static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *orig_mode, - struct drm_display_mode *mode) +static void +imx_ldb_encoder_atomic_mode_set(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *connector_state) { struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); + struct drm_display_mode *mode = &crtc_state->adjusted_mode; struct imx_ldb *ldb = imx_ldb_ch->ldb; int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; unsigned long serial_clk; @@ -297,17 +304,11 @@ static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder, } if (!bus_format) { - struct drm_connector *connector; - - drm_for_each_connector(connector, encoder->dev) { - struct drm_display_info *di = &connector->display_info; + struct drm_connector *connector = connector_state->connector; + struct drm_display_info *di = &connector->display_info; - if (connector->encoder == encoder && - di->num_bus_formats) { - bus_format = di->bus_formats[0]; - break; - } - } + if (di->num_bus_formats) + bus_format = di->bus_formats[0]; } imx_ldb_ch_set_bus_format(imx_ldb_ch, bus_format); } @@ -379,8 +380,13 @@ static int imx_ldb_encoder_atomic_check(struct drm_encoder *encoder, u32 bus_format = imx_ldb_ch->bus_format; /* Bus format description in DT overrides connector display info. */ - if (!bus_format && di->num_bus_formats) + if (!bus_format && di->num_bus_formats) { bus_format = di->bus_formats[0]; + imx_crtc_state->bus_flags = di->bus_flags; + } else { + bus_format = imx_ldb_ch->bus_format; + imx_crtc_state->bus_flags = imx_ldb_ch->bus_flags; + } switch (bus_format) { case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB666_1X18; @@ -420,7 +426,7 @@ static const struct drm_encoder_funcs imx_ldb_encoder_funcs = { }; static const struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = { - .mode_set = imx_ldb_encoder_mode_set, + .atomic_mode_set = imx_ldb_encoder_atomic_mode_set, .enable = imx_ldb_encoder_enable, .disable = imx_ldb_encoder_disable, .atomic_check = imx_ldb_encoder_atomic_check, @@ -466,10 +472,30 @@ static int imx_ldb_register(struct drm_device *drm, drm_encoder_init(drm, encoder, &imx_ldb_encoder_funcs, DRM_MODE_ENCODER_LVDS, NULL); - drm_connector_helper_add(&imx_ldb_ch->connector, - &imx_ldb_connector_helper_funcs); - drm_connector_init(drm, &imx_ldb_ch->connector, - &imx_ldb_connector_funcs, DRM_MODE_CONNECTOR_LVDS); + if (imx_ldb_ch->bridge) { + imx_ldb_ch->bridge->encoder = encoder; + + imx_ldb_ch->encoder.bridge = imx_ldb_ch->bridge; + ret = drm_bridge_attach(drm, imx_ldb_ch->bridge); + if (ret) { + DRM_ERROR("Failed to initialize bridge with drm\n"); + return ret; + } + } else { + /* + * We want to add the connector whenever there is no bridge + * that brings its own, not only when there is a panel. For + * historical reasons, the ldb driver can also work without + * a panel. + */ + drm_connector_helper_add(&imx_ldb_ch->connector, + &imx_ldb_connector_helper_funcs); + drm_connector_init(drm, &imx_ldb_ch->connector, + &imx_ldb_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + drm_mode_connector_attach_encoder(&imx_ldb_ch->connector, + encoder); + } if (imx_ldb_ch->panel) { ret = drm_panel_attach(imx_ldb_ch->panel, @@ -478,8 +504,6 @@ static int imx_ldb_register(struct drm_device *drm, return ret; } - drm_mode_connector_attach_encoder(&imx_ldb_ch->connector, encoder); - return 0; } @@ -548,6 +572,46 @@ static const struct of_device_id imx_ldb_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, imx_ldb_dt_ids); +static int imx_ldb_panel_ddc(struct device *dev, + struct imx_ldb_channel *channel, struct device_node *child) +{ + struct device_node *ddc_node; + const u8 *edidp; + int ret; + + ddc_node = of_parse_phandle(child, "ddc-i2c-bus", 0); + if (ddc_node) { + channel->ddc = of_find_i2c_adapter_by_node(ddc_node); + of_node_put(ddc_node); + if (!channel->ddc) { + dev_warn(dev, "failed to get ddc i2c adapter\n"); + return -EPROBE_DEFER; + } + } + + if (!channel->ddc) { + /* if no DDC available, fallback to hardcoded EDID */ + dev_dbg(dev, "no ddc available\n"); + + edidp = of_get_property(child, "edid", + &channel->edid_len); + if (edidp) { + channel->edid = kmemdup(edidp, + channel->edid_len, + GFP_KERNEL); + } else if (!channel->panel) { + /* fallback to display-timings node */ + ret = of_get_drm_display_mode(child, + &channel->mode, + &channel->bus_flags, + OF_USE_NATIVE_MODE); + if (!ret) + channel->mode_valid = 1; + } + } + return 0; +} + static int imx_ldb_bind(struct device *dev, struct device *master, void *data) { struct drm_device *drm = data; @@ -555,7 +619,6 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) const struct of_device_id *of_id = of_match_device(imx_ldb_dt_ids, dev); struct device_node *child; - const u8 *edidp; struct imx_ldb *imx_ldb; int dual; int ret; @@ -605,7 +668,6 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) for_each_child_of_node(np, child) { struct imx_ldb_channel *channel; - struct device_node *ddc_node; struct device_node *ep; int bus_format; @@ -638,46 +700,25 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) remote = of_graph_get_remote_port_parent(ep); of_node_put(ep); - if (remote) + if (remote) { channel->panel = of_drm_find_panel(remote); - else + channel->bridge = of_drm_find_bridge(remote); + } else return -EPROBE_DEFER; of_node_put(remote); - if (!channel->panel) { - dev_err(dev, "panel not found: %s\n", + + if (!channel->panel && !channel->bridge) { + dev_err(dev, "panel/bridge not found: %s\n", remote->full_name); return -EPROBE_DEFER; } } - ddc_node = of_parse_phandle(child, "ddc-i2c-bus", 0); - if (ddc_node) { - channel->ddc = of_find_i2c_adapter_by_node(ddc_node); - of_node_put(ddc_node); - if (!channel->ddc) { - dev_warn(dev, "failed to get ddc i2c adapter\n"); - return -EPROBE_DEFER; - } - } - - if (!channel->ddc) { - /* if no DDC available, fallback to hardcoded EDID */ - dev_dbg(dev, "no ddc available\n"); - - edidp = of_get_property(child, "edid", - &channel->edid_len); - if (edidp) { - channel->edid = kmemdup(edidp, - channel->edid_len, - GFP_KERNEL); - } else if (!channel->panel) { - /* fallback to display-timings node */ - ret = of_get_drm_display_mode(child, - &channel->mode, - OF_USE_NATIVE_MODE); - if (!ret) - channel->mode_valid = 1; - } + /* panel ddc only if there is no bridge */ + if (!channel->bridge) { + ret = imx_ldb_panel_ddc(dev, channel, child); + if (ret) + return ret; } bus_format = of_get_bus_format(dev, child); @@ -716,11 +757,10 @@ static void imx_ldb_unbind(struct device *dev, struct device *master, for (i = 0; i < 2; i++) { struct imx_ldb_channel *channel = &imx_ldb->channel[i]; - if (!channel->connector.funcs) - continue; - - channel->connector.funcs->destroy(&channel->connector); - channel->encoder.funcs->destroy(&channel->encoder); + if (channel->bridge) + drm_bridge_detach(channel->bridge); + if (channel->panel) + drm_panel_detach(channel->panel); kfree(channel->edid); i2c_put_adapter(channel->ddc); diff --git a/drivers/gpu/drm/imx/imx-tve.c b/drivers/gpu/drm/imx/imx-tve.c index 5e87594..8fc0888 100644 --- a/drivers/gpu/drm/imx/imx-tve.c +++ b/drivers/gpu/drm/imx/imx-tve.c @@ -685,9 +685,6 @@ static void imx_tve_unbind(struct device *dev, struct device *master, { struct imx_tve *tve = dev_get_drvdata(dev); - tve->connector.funcs->destroy(&tve->connector); - tve->encoder.funcs->destroy(&tve->encoder); - if (!IS_ERR(tve->dac_reg)) regulator_disable(tve->dac_reg); } diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c index 08e188b..4e1ae3f 100644 --- a/drivers/gpu/drm/imx/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c @@ -21,7 +21,6 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> -#include <linux/fb.h> #include <linux/clk.h> #include <linux/errno.h> #include <drm/drm_gem_cma_helper.h> @@ -61,7 +60,8 @@ static void ipu_crtc_enable(struct drm_crtc *crtc) ipu_di_enable(ipu_crtc->di); } -static void ipu_crtc_disable(struct drm_crtc *crtc) +static void ipu_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); @@ -76,6 +76,11 @@ static void ipu_crtc_disable(struct drm_crtc *crtc) crtc->state->event = NULL; } spin_unlock_irq(&crtc->dev->event_lock); + + /* always disable planes on the CRTC */ + drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, true); + + drm_crtc_vblank_off(crtc); } static void imx_drm_crtc_reset(struct drm_crtc *crtc) @@ -121,9 +126,14 @@ static void imx_drm_crtc_destroy_state(struct drm_crtc *crtc, kfree(to_imx_crtc_state(state)); } +static void imx_drm_crtc_destroy(struct drm_crtc *crtc) +{ + imx_drm_remove_crtc(to_ipu_crtc(crtc)->imx_crtc); +} + static const struct drm_crtc_funcs ipu_crtc_funcs = { .set_config = drm_atomic_helper_set_config, - .destroy = drm_crtc_cleanup, + .destroy = imx_drm_crtc_destroy, .page_flip = drm_atomic_helper_page_flip, .reset = imx_drm_crtc_reset, .atomic_duplicate_state = imx_drm_crtc_duplicate_state, @@ -134,7 +144,7 @@ static irqreturn_t ipu_irq_handler(int irq, void *dev_id) { struct ipu_crtc *ipu_crtc = dev_id; - imx_drm_handle_vblank(ipu_crtc->imx_crtc); + drm_crtc_handle_vblank(&ipu_crtc->base); return IRQ_HANDLED; } @@ -175,6 +185,8 @@ static int ipu_crtc_atomic_check(struct drm_crtc *crtc, static void ipu_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { + drm_crtc_vblank_on(crtc); + spin_lock_irq(&crtc->dev->event_lock); if (crtc->state->event) { WARN_ON(drm_crtc_vblank_get(crtc)); @@ -242,7 +254,7 @@ static const struct drm_crtc_helper_funcs ipu_helper_funcs = { .mode_set_nofb = ipu_crtc_mode_set_nofb, .atomic_check = ipu_crtc_atomic_check, .atomic_begin = ipu_crtc_atomic_begin, - .disable = ipu_crtc_disable, + .atomic_disable = ipu_crtc_atomic_disable, .enable = ipu_crtc_enable, }; @@ -410,8 +422,6 @@ static void ipu_drm_unbind(struct device *dev, struct device *master, { struct ipu_crtc *ipu_crtc = dev_get_drvdata(dev); - imx_drm_remove_crtc(ipu_crtc->imx_crtc); - ipu_put_resources(ipu_crtc); if (ipu_crtc->plane[1]) ipu_plane_put_resources(ipu_crtc->plane[1]); diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c index 4ad67d0..ce22d0a 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3-plane.c @@ -213,8 +213,12 @@ static void ipu_plane_enable(struct ipu_plane *ipu_plane) ipu_dp_enable_channel(ipu_plane->dp); } -static void ipu_plane_disable(struct ipu_plane *ipu_plane) +static int ipu_disable_plane(struct drm_plane *plane) { + struct ipu_plane *ipu_plane = to_ipu_plane(plane); + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50); if (ipu_plane->dp) @@ -223,15 +227,6 @@ static void ipu_plane_disable(struct ipu_plane *ipu_plane) ipu_dmfc_disable_channel(ipu_plane->dmfc); if (ipu_plane->dp) ipu_dp_disable(ipu_plane->ipu); -} - -static int ipu_disable_plane(struct drm_plane *plane) -{ - struct ipu_plane *ipu_plane = to_ipu_plane(plane); - - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - ipu_plane_disable(ipu_plane); return 0; } @@ -242,7 +237,6 @@ static void ipu_plane_destroy(struct drm_plane *plane) DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - ipu_disable_plane(plane); drm_plane_cleanup(plane); kfree(ipu_plane); } @@ -319,13 +313,16 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, return -EINVAL; /* - * since we cannot touch active IDMAC channels, we do not support - * resizing the enabled plane or changing its format + * We support resizing active plane or changing its format by + * forcing CRTC mode change in plane's ->atomic_check callback + * and disabling all affected active planes in CRTC's ->atomic_disable + * callback. The planes will be reenabled in plane's ->atomic_update + * callback. */ if (old_fb && (state->src_w != old_state->src_w || state->src_h != old_state->src_h || fb->pixel_format != old_fb->pixel_format)) - return -EINVAL; + crtc_state->mode_changed = true; eba = drm_plane_state_to_eba(state); @@ -336,7 +333,7 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, return -EINVAL; if (old_fb && fb->pitches[0] != old_fb->pitches[0]) - return -EINVAL; + crtc_state->mode_changed = true; switch (fb->pixel_format) { case DRM_FORMAT_YUV420: @@ -372,7 +369,7 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, return -EINVAL; if (old_fb && old_fb->pitches[1] != fb->pitches[1]) - return -EINVAL; + crtc_state->mode_changed = true; } return 0; @@ -392,8 +389,12 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, enum ipu_color_space ics; if (old_state->fb) { - ipu_plane_atomic_set_base(ipu_plane, old_state); - return; + struct drm_crtc_state *crtc_state = state->crtc->state; + + if (!drm_atomic_crtc_needs_modeset(crtc_state)) { + ipu_plane_atomic_set_base(ipu_plane, old_state); + return; + } } switch (ipu_plane->dp_flow) { diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 1dad297..d796ada 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -33,6 +33,7 @@ struct imx_parallel_display { void *edid; int edid_len; u32 bus_format; + u32 bus_flags; struct drm_display_mode mode; struct drm_panel *panel; struct drm_bridge *bridge; @@ -80,6 +81,7 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector) return -EINVAL; ret = of_get_drm_display_mode(np, &imxpd->mode, + &imxpd->bus_flags, OF_USE_NATIVE_MODE); if (ret) return ret; @@ -125,11 +127,13 @@ static int imx_pd_encoder_atomic_check(struct drm_encoder *encoder, struct drm_display_info *di = &conn_state->connector->display_info; struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); - imx_crtc_state->bus_flags = di->bus_flags; - if (!imxpd->bus_format && di->num_bus_formats) + if (!imxpd->bus_format && di->num_bus_formats) { + imx_crtc_state->bus_flags = di->bus_flags; imx_crtc_state->bus_format = di->bus_formats[0]; - else + } else { + imx_crtc_state->bus_flags = imxpd->bus_flags; imx_crtc_state->bus_format = imxpd->bus_format; + } imx_crtc_state->di_hsync_pin = 2; imx_crtc_state->di_vsync_pin = 3; @@ -289,8 +293,10 @@ static void imx_pd_unbind(struct device *dev, struct device *master, { struct imx_parallel_display *imxpd = dev_get_drvdata(dev); - imxpd->encoder.funcs->destroy(&imxpd->encoder); - imxpd->connector.funcs->destroy(&imxpd->connector); + if (imxpd->bridge) + drm_bridge_detach(imxpd->bridge); + if (imxpd->panel) + drm_panel_detach(imxpd->panel); kfree(imxpd->edid); } diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig index 23ac804..294de45 100644 --- a/drivers/gpu/drm/mediatek/Kconfig +++ b/drivers/gpu/drm/mediatek/Kconfig @@ -2,6 +2,9 @@ config DRM_MEDIATEK tristate "DRM Support for Mediatek SoCs" depends on DRM depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST) + depends on COMMON_CLK + depends on HAVE_ARM_SMCCC + depends on OF select DRM_GEM_CMA_HELPER select DRM_KMS_HELPER select DRM_MIPI_DSI diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c index 8f62671f..019b7ca 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c @@ -103,7 +103,8 @@ static void mtk_ovl_stop(struct mtk_ddp_comp *comp) } static void mtk_ovl_config(struct mtk_ddp_comp *comp, unsigned int w, - unsigned int h, unsigned int vrefresh) + unsigned int h, unsigned int vrefresh, + unsigned int bpc) { if (w != 0 && h != 0) writel_relaxed(h << 16 | w, comp->regs + DISP_REG_OVL_ROI_SIZE); diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c index 5fb80cb..0df05f9 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c @@ -106,7 +106,8 @@ static void mtk_rdma_stop(struct mtk_ddp_comp *comp) } static void mtk_rdma_config(struct mtk_ddp_comp *comp, unsigned int width, - unsigned int height, unsigned int vrefresh) + unsigned int height, unsigned int vrefresh, + unsigned int bpc) { unsigned int threshold; unsigned int reg; diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c index 24aa3ba..01a21dd 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c @@ -31,7 +31,7 @@ * struct mtk_drm_crtc - MediaTek specific crtc structure. * @base: crtc object. * @enabled: records whether crtc_enable succeeded - * @planes: array of 4 mtk_drm_plane structures, one for each overlay plane + * @planes: array of 4 drm_plane structures, one for each overlay plane * @pending_planes: whether any plane has pending changes to be applied * @config_regs: memory mapped mmsys configuration register space * @mutex: handle to one of the ten disp_mutex streams @@ -45,7 +45,7 @@ struct mtk_drm_crtc { bool pending_needs_vblank; struct drm_pending_vblank_event *event; - struct mtk_drm_plane planes[OVL_LAYER_NR]; + struct drm_plane planes[OVL_LAYER_NR]; bool pending_planes; void __iomem *config_regs; @@ -112,8 +112,7 @@ static void mtk_drm_crtc_reset(struct drm_crtc *crtc) struct mtk_crtc_state *state; if (crtc->state) { - if (crtc->state->mode_blob) - drm_property_unreference_blob(crtc->state->mode_blob); + __drm_atomic_helper_crtc_destroy_state(crtc->state); state = to_mtk_crtc_state(crtc->state); memset(state, 0, sizeof(*state)); @@ -222,7 +221,9 @@ static void mtk_crtc_ddp_clk_disable(struct mtk_drm_crtc *mtk_crtc) static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc) { struct drm_crtc *crtc = &mtk_crtc->base; - unsigned int width, height, vrefresh; + struct drm_connector *connector; + struct drm_encoder *encoder; + unsigned int width, height, vrefresh, bpc = MTK_MAX_BPC; int ret; int i; @@ -234,6 +235,19 @@ static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc) height = crtc->state->adjusted_mode.vdisplay; vrefresh = crtc->state->adjusted_mode.vrefresh; + drm_for_each_encoder(encoder, crtc->dev) { + if (encoder->crtc != crtc) + continue; + + drm_for_each_connector(connector, crtc->dev) { + if (connector->encoder != encoder) + continue; + if (connector->display_info.bpc != 0 && + bpc > connector->display_info.bpc) + bpc = connector->display_info.bpc; + } + } + ret = pm_runtime_get_sync(crtc->dev->dev); if (ret < 0) { DRM_ERROR("Failed to enable power domain: %d\n", ret); @@ -266,13 +280,13 @@ static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc) for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) { struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i]; - mtk_ddp_comp_config(comp, width, height, vrefresh); + mtk_ddp_comp_config(comp, width, height, vrefresh, bpc); mtk_ddp_comp_start(comp); } /* Initially configure all planes */ for (i = 0; i < OVL_LAYER_NR; i++) { - struct drm_plane *plane = &mtk_crtc->planes[i].base; + struct drm_plane *plane = &mtk_crtc->planes[i]; struct mtk_plane_state *plane_state; plane_state = to_mtk_plane_state(plane->state); @@ -351,7 +365,7 @@ static void mtk_drm_crtc_disable(struct drm_crtc *crtc) /* Set all pending plane state to disabled */ for (i = 0; i < OVL_LAYER_NR; i++) { - struct drm_plane *plane = &mtk_crtc->planes[i].base; + struct drm_plane *plane = &mtk_crtc->planes[i]; struct mtk_plane_state *plane_state; plane_state = to_mtk_plane_state(plane->state); @@ -397,7 +411,7 @@ static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc, if (mtk_crtc->event) mtk_crtc->pending_needs_vblank = true; for (i = 0; i < OVL_LAYER_NR; i++) { - struct drm_plane *plane = &mtk_crtc->planes[i].base; + struct drm_plane *plane = &mtk_crtc->planes[i]; struct mtk_plane_state *plane_state; plane_state = to_mtk_plane_state(plane->state); @@ -409,6 +423,9 @@ static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc, } if (pending_planes) mtk_crtc->pending_planes = true; + if (crtc->state->color_mgmt_changed) + for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) + mtk_ddp_gamma_set(mtk_crtc->ddp_comp[i], crtc->state); } static const struct drm_crtc_funcs mtk_crtc_funcs = { @@ -418,6 +435,7 @@ static const struct drm_crtc_funcs mtk_crtc_funcs = { .reset = mtk_drm_crtc_reset, .atomic_duplicate_state = mtk_drm_crtc_duplicate_state, .atomic_destroy_state = mtk_drm_crtc_destroy_state, + .gamma_set = drm_atomic_helper_legacy_gamma_set, }; static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = { @@ -464,14 +482,14 @@ void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl) if (state->pending_config) { mtk_ddp_comp_config(ovl, state->pending_width, state->pending_height, - state->pending_vrefresh); + state->pending_vrefresh, 0); state->pending_config = false; } if (mtk_crtc->pending_planes) { for (i = 0; i < OVL_LAYER_NR; i++) { - struct drm_plane *plane = &mtk_crtc->planes[i].base; + struct drm_plane *plane = &mtk_crtc->planes[i]; struct mtk_plane_state *plane_state; plane_state = to_mtk_plane_state(plane->state); @@ -559,16 +577,17 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev, (zpos == 1) ? DRM_PLANE_TYPE_CURSOR : DRM_PLANE_TYPE_OVERLAY; ret = mtk_plane_init(drm_dev, &mtk_crtc->planes[zpos], - BIT(pipe), type, zpos); + BIT(pipe), type); if (ret) goto unprepare; } - ret = mtk_drm_crtc_init(drm_dev, mtk_crtc, &mtk_crtc->planes[0].base, - &mtk_crtc->planes[1].base, pipe); + ret = mtk_drm_crtc_init(drm_dev, mtk_crtc, &mtk_crtc->planes[0], + &mtk_crtc->planes[1], pipe); if (ret < 0) goto unprepare; - + drm_mode_crtc_set_gamma_size(&mtk_crtc->base, MTK_LUT_SIZE); + drm_crtc_enable_color_mgmt(&mtk_crtc->base, 0, false, MTK_LUT_SIZE); priv->crtc[pipe] = &mtk_crtc->base; priv->num_pipes++; diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h index 81e5566..a1550fa 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h @@ -19,10 +19,12 @@ #include "mtk_drm_plane.h" #define OVL_LAYER_NR 4 +#define MTK_LUT_SIZE 512 +#define MTK_MAX_BPC 10 +#define MTK_MIN_BPC 3 int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe); void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe); -void mtk_drm_crtc_check_flush(struct drm_crtc *crtc); void mtk_drm_crtc_commit(struct drm_crtc *crtc); void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl); int mtk_drm_crtc_create(struct drm_device *drm_dev, diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c index 3970fcf..df33b3c 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c @@ -24,12 +24,17 @@ #include "mtk_drm_drv.h" #include "mtk_drm_plane.h" #include "mtk_drm_ddp_comp.h" +#include "mtk_drm_crtc.h" #define DISP_OD_EN 0x0000 #define DISP_OD_INTEN 0x0008 #define DISP_OD_INTSTA 0x000c #define DISP_OD_CFG 0x0020 #define DISP_OD_SIZE 0x0030 +#define DISP_DITHER_5 0x0114 +#define DISP_DITHER_7 0x011c +#define DISP_DITHER_15 0x013c +#define DISP_DITHER_16 0x0140 #define DISP_REG_UFO_START 0x0000 @@ -38,15 +43,69 @@ #define DISP_COLOR_WIDTH 0x0c50 #define DISP_COLOR_HEIGHT 0x0c54 -#define OD_RELAY_MODE BIT(0) +#define DISP_AAL_EN 0x0000 +#define DISP_AAL_SIZE 0x0030 -#define UFO_BYPASS BIT(2) +#define DISP_GAMMA_EN 0x0000 +#define DISP_GAMMA_CFG 0x0020 +#define DISP_GAMMA_SIZE 0x0030 +#define DISP_GAMMA_LUT 0x0700 -#define COLOR_BYPASS_ALL BIT(7) -#define COLOR_SEQ_SEL BIT(13) +#define LUT_10BIT_MASK 0x03ff + +#define COLOR_BYPASS_ALL BIT(7) +#define COLOR_SEQ_SEL BIT(13) + +#define OD_RELAYMODE BIT(0) + +#define UFO_BYPASS BIT(2) + +#define AAL_EN BIT(0) + +#define GAMMA_EN BIT(0) +#define GAMMA_LUT_EN BIT(1) + +#define DISP_DITHERING BIT(2) +#define DITHER_LSB_ERR_SHIFT_R(x) (((x) & 0x7) << 28) +#define DITHER_OVFLW_BIT_R(x) (((x) & 0x7) << 24) +#define DITHER_ADD_LSHIFT_R(x) (((x) & 0x7) << 20) +#define DITHER_ADD_RSHIFT_R(x) (((x) & 0x7) << 16) +#define DITHER_NEW_BIT_MODE BIT(0) +#define DITHER_LSB_ERR_SHIFT_B(x) (((x) & 0x7) << 28) +#define DITHER_OVFLW_BIT_B(x) (((x) & 0x7) << 24) +#define DITHER_ADD_LSHIFT_B(x) (((x) & 0x7) << 20) +#define DITHER_ADD_RSHIFT_B(x) (((x) & 0x7) << 16) +#define DITHER_LSB_ERR_SHIFT_G(x) (((x) & 0x7) << 12) +#define DITHER_OVFLW_BIT_G(x) (((x) & 0x7) << 8) +#define DITHER_ADD_LSHIFT_G(x) (((x) & 0x7) << 4) +#define DITHER_ADD_RSHIFT_G(x) (((x) & 0x7) << 0) + +void mtk_dither_set(struct mtk_ddp_comp *comp, unsigned int bpc, + unsigned int CFG) +{ + /* If bpc equal to 0, the dithering function didn't be enabled */ + if (bpc == 0) + return; + + if (bpc >= MTK_MIN_BPC) { + writel(0, comp->regs + DISP_DITHER_5); + writel(0, comp->regs + DISP_DITHER_7); + writel(DITHER_LSB_ERR_SHIFT_R(MTK_MAX_BPC - bpc) | + DITHER_ADD_LSHIFT_R(MTK_MAX_BPC - bpc) | + DITHER_NEW_BIT_MODE, + comp->regs + DISP_DITHER_15); + writel(DITHER_LSB_ERR_SHIFT_B(MTK_MAX_BPC - bpc) | + DITHER_ADD_LSHIFT_B(MTK_MAX_BPC - bpc) | + DITHER_LSB_ERR_SHIFT_G(MTK_MAX_BPC - bpc) | + DITHER_ADD_LSHIFT_G(MTK_MAX_BPC - bpc), + comp->regs + DISP_DITHER_16); + writel(DISP_DITHERING, comp->regs + CFG); + } +} static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w, - unsigned int h, unsigned int vrefresh) + unsigned int h, unsigned int vrefresh, + unsigned int bpc) { writel(w, comp->regs + DISP_COLOR_WIDTH); writel(h, comp->regs + DISP_COLOR_HEIGHT); @@ -60,14 +119,16 @@ static void mtk_color_start(struct mtk_ddp_comp *comp) } static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w, - unsigned int h, unsigned int vrefresh) + unsigned int h, unsigned int vrefresh, + unsigned int bpc) { writel(w << 16 | h, comp->regs + DISP_OD_SIZE); + writel(OD_RELAYMODE, comp->regs + OD_RELAYMODE); + mtk_dither_set(comp, bpc, DISP_OD_CFG); } static void mtk_od_start(struct mtk_ddp_comp *comp) { - writel(OD_RELAY_MODE, comp->regs + DISP_OD_CFG); writel(1, comp->regs + DISP_OD_EN); } @@ -76,6 +137,78 @@ static void mtk_ufoe_start(struct mtk_ddp_comp *comp) writel(UFO_BYPASS, comp->regs + DISP_REG_UFO_START); } +static void mtk_aal_config(struct mtk_ddp_comp *comp, unsigned int w, + unsigned int h, unsigned int vrefresh, + unsigned int bpc) +{ + writel(h << 16 | w, comp->regs + DISP_AAL_SIZE); +} + +static void mtk_aal_start(struct mtk_ddp_comp *comp) +{ + writel(AAL_EN, comp->regs + DISP_AAL_EN); +} + +static void mtk_aal_stop(struct mtk_ddp_comp *comp) +{ + writel_relaxed(0x0, comp->regs + DISP_AAL_EN); +} + +static void mtk_gamma_config(struct mtk_ddp_comp *comp, unsigned int w, + unsigned int h, unsigned int vrefresh, + unsigned int bpc) +{ + writel(h << 16 | w, comp->regs + DISP_GAMMA_SIZE); + mtk_dither_set(comp, bpc, DISP_GAMMA_CFG); +} + +static void mtk_gamma_start(struct mtk_ddp_comp *comp) +{ + writel(GAMMA_EN, comp->regs + DISP_GAMMA_EN); +} + +static void mtk_gamma_stop(struct mtk_ddp_comp *comp) +{ + writel_relaxed(0x0, comp->regs + DISP_GAMMA_EN); +} + +static void mtk_gamma_set(struct mtk_ddp_comp *comp, + struct drm_crtc_state *state) +{ + unsigned int i, reg; + struct drm_color_lut *lut; + void __iomem *lut_base; + u32 word; + + if (state->gamma_lut) { + reg = readl(comp->regs + DISP_GAMMA_CFG); + reg = reg | GAMMA_LUT_EN; + writel(reg, comp->regs + DISP_GAMMA_CFG); + lut_base = comp->regs + DISP_GAMMA_LUT; + lut = (struct drm_color_lut *)state->gamma_lut->data; + for (i = 0; i < MTK_LUT_SIZE; i++) { + word = (((lut[i].red >> 6) & LUT_10BIT_MASK) << 20) + + (((lut[i].green >> 6) & LUT_10BIT_MASK) << 10) + + ((lut[i].blue >> 6) & LUT_10BIT_MASK); + writel(word, (lut_base + i * 4)); + } + } +} + +static const struct mtk_ddp_comp_funcs ddp_aal = { + .gamma_set = mtk_gamma_set, + .config = mtk_aal_config, + .start = mtk_aal_start, + .stop = mtk_aal_stop, +}; + +static const struct mtk_ddp_comp_funcs ddp_gamma = { + .gamma_set = mtk_gamma_set, + .config = mtk_gamma_config, + .start = mtk_gamma_start, + .stop = mtk_gamma_stop, +}; + static const struct mtk_ddp_comp_funcs ddp_color = { .config = mtk_color_config, .start = mtk_color_start, @@ -112,13 +245,13 @@ struct mtk_ddp_comp_match { }; static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = { - [DDP_COMPONENT_AAL] = { MTK_DISP_AAL, 0, NULL }, + [DDP_COMPONENT_AAL] = { MTK_DISP_AAL, 0, &ddp_aal }, [DDP_COMPONENT_COLOR0] = { MTK_DISP_COLOR, 0, &ddp_color }, [DDP_COMPONENT_COLOR1] = { MTK_DISP_COLOR, 1, &ddp_color }, [DDP_COMPONENT_DPI0] = { MTK_DPI, 0, NULL }, [DDP_COMPONENT_DSI0] = { MTK_DSI, 0, NULL }, [DDP_COMPONENT_DSI1] = { MTK_DSI, 1, NULL }, - [DDP_COMPONENT_GAMMA] = { MTK_DISP_GAMMA, 0, NULL }, + [DDP_COMPONENT_GAMMA] = { MTK_DISP_GAMMA, 0, &ddp_gamma }, [DDP_COMPONENT_OD] = { MTK_DISP_OD, 0, &ddp_od }, [DDP_COMPONENT_OVL0] = { MTK_DISP_OVL, 0, NULL }, [DDP_COMPONENT_OVL1] = { MTK_DISP_OVL, 1, NULL }, diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h index 6b13ba9..22a33ee 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h @@ -21,6 +21,7 @@ struct device_node; struct drm_crtc; struct drm_device; struct mtk_plane_state; +struct drm_crtc_state; enum mtk_ddp_comp_type { MTK_DISP_OVL, @@ -64,7 +65,7 @@ struct mtk_ddp_comp; struct mtk_ddp_comp_funcs { void (*config)(struct mtk_ddp_comp *comp, unsigned int w, - unsigned int h, unsigned int vrefresh); + unsigned int h, unsigned int vrefresh, unsigned int bpc); void (*start)(struct mtk_ddp_comp *comp); void (*stop)(struct mtk_ddp_comp *comp); void (*enable_vblank)(struct mtk_ddp_comp *comp, struct drm_crtc *crtc); @@ -73,6 +74,8 @@ struct mtk_ddp_comp_funcs { void (*layer_off)(struct mtk_ddp_comp *comp, unsigned int idx); void (*layer_config)(struct mtk_ddp_comp *comp, unsigned int idx, struct mtk_plane_state *state); + void (*gamma_set)(struct mtk_ddp_comp *comp, + struct drm_crtc_state *state); }; struct mtk_ddp_comp { @@ -86,10 +89,10 @@ struct mtk_ddp_comp { static inline void mtk_ddp_comp_config(struct mtk_ddp_comp *comp, unsigned int w, unsigned int h, - unsigned int vrefresh) + unsigned int vrefresh, unsigned int bpc) { if (comp->funcs && comp->funcs->config) - comp->funcs->config(comp, w, h, vrefresh); + comp->funcs->config(comp, w, h, vrefresh, bpc); } static inline void mtk_ddp_comp_start(struct mtk_ddp_comp *comp) @@ -139,6 +142,13 @@ static inline void mtk_ddp_comp_layer_config(struct mtk_ddp_comp *comp, comp->funcs->layer_config(comp, idx, state); } +static inline void mtk_ddp_gamma_set(struct mtk_ddp_comp *comp, + struct drm_crtc_state *state) +{ + if (comp->funcs && comp->funcs->gamma_set) + comp->funcs->gamma_set(comp, state); +} + int mtk_ddp_comp_get_id(struct device_node *node, enum mtk_ddp_comp_type comp_type); int mtk_ddp_comp_init(struct device *dev, struct device_node *comp_node, @@ -146,5 +156,7 @@ int mtk_ddp_comp_init(struct device *dev, struct device_node *comp_node, const struct mtk_ddp_comp_funcs *funcs); int mtk_ddp_comp_register(struct drm_device *drm, struct mtk_ddp_comp *comp); void mtk_ddp_comp_unregister(struct drm_device *drm, struct mtk_ddp_comp *comp); +void mtk_dither_set(struct mtk_ddp_comp *comp, unsigned int bpc, + unsigned int CFG); #endif /* MTK_DRM_DDP_COMP_H */ diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index eebb7d8..cf83f65 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -61,10 +61,27 @@ static void mtk_atomic_complete(struct mtk_drm_private *private, mtk_atomic_wait_for_fences(state); + /* + * Mediatek drm supports runtime PM, so plane registers cannot be + * written when their crtc is disabled. + * + * The comment for drm_atomic_helper_commit states: + * For drivers supporting runtime PM the recommended sequence is + * + * drm_atomic_helper_commit_modeset_disables(dev, state); + * drm_atomic_helper_commit_modeset_enables(dev, state); + * drm_atomic_helper_commit_planes(dev, state, + * DRM_PLANE_COMMIT_ACTIVE_ONLY); + * + * See the kerneldoc entries for these three functions for more details. + */ drm_atomic_helper_commit_modeset_disables(drm, state); - drm_atomic_helper_commit_planes(drm, state, false); drm_atomic_helper_commit_modeset_enables(drm, state); + drm_atomic_helper_commit_planes(drm, state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); + drm_atomic_helper_wait_for_vblanks(drm, state); + drm_atomic_helper_cleanup_planes(drm, state); drm_atomic_state_free(state); } @@ -277,8 +294,8 @@ static int mtk_drm_bind(struct device *dev) int ret; drm = drm_dev_alloc(&mtk_drm_driver, dev); - if (!drm) - return -ENOMEM; + if (IS_ERR(drm)) + return PTR_ERR(drm); drm->dev_private = private; private->drm = drm; diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.c b/drivers/gpu/drm/mediatek/mtk_drm_plane.c index 3995765..c461a23 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_plane.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c @@ -30,57 +30,12 @@ static const u32 formats[] = { DRM_FORMAT_RGB565, }; -static void mtk_plane_enable(struct mtk_drm_plane *mtk_plane, bool enable, - dma_addr_t addr, struct drm_rect *dest) -{ - struct drm_plane *plane = &mtk_plane->base; - struct mtk_plane_state *state = to_mtk_plane_state(plane->state); - unsigned int pitch, format; - int x, y; - - if (WARN_ON(!plane->state || (enable && !plane->state->fb))) - return; - - if (plane->state->fb) { - pitch = plane->state->fb->pitches[0]; - format = plane->state->fb->pixel_format; - } else { - pitch = 0; - format = DRM_FORMAT_RGBA8888; - } - - x = plane->state->crtc_x; - y = plane->state->crtc_y; - - if (x < 0) { - addr -= x * 4; - x = 0; - } - - if (y < 0) { - addr -= y * pitch; - y = 0; - } - - state->pending.enable = enable; - state->pending.pitch = pitch; - state->pending.format = format; - state->pending.addr = addr; - state->pending.x = x; - state->pending.y = y; - state->pending.width = dest->x2 - dest->x1; - state->pending.height = dest->y2 - dest->y1; - wmb(); /* Make sure the above parameters are set before update */ - state->pending.dirty = true; -} - static void mtk_plane_reset(struct drm_plane *plane) { struct mtk_plane_state *state; if (plane->state) { - if (plane->state->fb) - drm_framebuffer_unreference(plane->state->fb); + __drm_atomic_helper_plane_destroy_state(plane->state); state = to_mtk_plane_state(plane->state); memset(state, 0, sizeof(*state)); @@ -134,20 +89,6 @@ static int mtk_plane_atomic_check(struct drm_plane *plane, { struct drm_framebuffer *fb = state->fb; struct drm_crtc_state *crtc_state; - bool visible; - struct drm_rect dest = { - .x1 = state->crtc_x, - .y1 = state->crtc_y, - .x2 = state->crtc_x + state->crtc_w, - .y2 = state->crtc_y + state->crtc_h, - }; - struct drm_rect src = { - /* 16.16 fixed point */ - .x1 = state->src_x, - .y1 = state->src_y, - .x2 = state->src_x + state->src_w, - .y2 = state->src_y + state->src_h, - }; struct drm_rect clip = { 0, }; if (!fb) @@ -168,40 +109,45 @@ static int mtk_plane_atomic_check(struct drm_plane *plane, clip.x2 = crtc_state->mode.hdisplay; clip.y2 = crtc_state->mode.vdisplay; - return drm_plane_helper_check_update(plane, state->crtc, fb, - &src, &dest, &clip, - state->rotation, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - true, true, &visible); + return drm_plane_helper_check_state(state, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true); } static void mtk_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { struct mtk_plane_state *state = to_mtk_plane_state(plane->state); - struct drm_crtc *crtc = state->base.crtc; + struct drm_crtc *crtc = plane->state->crtc; + struct drm_framebuffer *fb = plane->state->fb; struct drm_gem_object *gem; struct mtk_drm_gem_obj *mtk_gem; - struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane); - struct drm_rect dest = { - .x1 = state->base.crtc_x, - .y1 = state->base.crtc_y, - .x2 = state->base.crtc_x + state->base.crtc_w, - .y2 = state->base.crtc_y + state->base.crtc_h, - }; - struct drm_rect clip = { 0, }; + unsigned int pitch, format; + dma_addr_t addr; - if (!crtc) + if (!crtc || WARN_ON(!fb)) return; - clip.x2 = state->base.crtc->state->mode.hdisplay; - clip.y2 = state->base.crtc->state->mode.vdisplay; - drm_rect_intersect(&dest, &clip); - - gem = mtk_fb_get_gem_obj(state->base.fb); + gem = mtk_fb_get_gem_obj(fb); mtk_gem = to_mtk_gem_obj(gem); - mtk_plane_enable(mtk_plane, true, mtk_gem->dma_addr, &dest); + addr = mtk_gem->dma_addr; + pitch = fb->pitches[0]; + format = fb->pixel_format; + + addr += (plane->state->src.x1 >> 16) * drm_format_plane_cpp(format, 0); + addr += (plane->state->src.y1 >> 16) * pitch; + + state->pending.enable = true; + state->pending.pitch = pitch; + state->pending.format = format; + state->pending.addr = addr; + state->pending.x = plane->state->dst.x1; + state->pending.y = plane->state->dst.y1; + state->pending.width = drm_rect_width(&plane->state->dst); + state->pending.height = drm_rect_height(&plane->state->dst); + wmb(); /* Make sure the above parameters are set before update */ + state->pending.dirty = true; } static void mtk_plane_atomic_disable(struct drm_plane *plane, @@ -220,13 +166,12 @@ static const struct drm_plane_helper_funcs mtk_plane_helper_funcs = { .atomic_disable = mtk_plane_atomic_disable, }; -int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane, - unsigned long possible_crtcs, enum drm_plane_type type, - unsigned int zpos) +int mtk_plane_init(struct drm_device *dev, struct drm_plane *plane, + unsigned long possible_crtcs, enum drm_plane_type type) { int err; - err = drm_universal_plane_init(dev, &mtk_plane->base, possible_crtcs, + err = drm_universal_plane_init(dev, plane, possible_crtcs, &mtk_plane_funcs, formats, ARRAY_SIZE(formats), type, NULL); if (err) { @@ -234,8 +179,7 @@ int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane, return err; } - drm_plane_helper_add(&mtk_plane->base, &mtk_plane_helper_funcs); - mtk_plane->idx = zpos; + drm_plane_helper_add(plane, &mtk_plane_helper_funcs); return 0; } diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.h b/drivers/gpu/drm/mediatek/mtk_drm_plane.h index 72a7b3e..6a20b49 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_plane.h +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.h @@ -18,11 +18,6 @@ #include <drm/drm_crtc.h> #include <linux/types.h> -struct mtk_drm_plane { - struct drm_plane base; - unsigned int idx; -}; - struct mtk_plane_pending_state { bool config; bool enable; @@ -41,19 +36,13 @@ struct mtk_plane_state { struct mtk_plane_pending_state pending; }; -static inline struct mtk_drm_plane *to_mtk_plane(struct drm_plane *plane) -{ - return container_of(plane, struct mtk_drm_plane, base); -} - static inline struct mtk_plane_state * to_mtk_plane_state(struct drm_plane_state *state) { return container_of(state, struct mtk_plane_state, base); } -int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane, - unsigned long possible_crtcs, enum drm_plane_type type, - unsigned int zpos); +int mtk_plane_init(struct drm_device *dev, struct drm_plane *plane, + unsigned long possible_crtcs, enum drm_plane_type type); #endif diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c index 5e2f131..25b2a1a 100644 --- a/drivers/gpu/drm/mga/mga_drv.c +++ b/drivers/gpu/drm/mga/mga_drv.c @@ -58,7 +58,7 @@ static const struct file_operations mga_driver_fops = { static struct drm_driver driver = { .driver_features = - DRIVER_USE_AGP | DRIVER_PCI_DMA | + DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_LEGACY | DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, .dev_priv_size = sizeof(drm_mga_buf_priv_t), .load = mga_driver_load, diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 2b4b125..1443b3a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -56,7 +56,7 @@ static void mgag200_kick_out_firmware_fb(struct pci_dev *pdev) #ifdef CONFIG_X86 primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; #endif - remove_conflicting_framebuffers(ap, "mgag200drmfb", primary); + drm_fb_helper_remove_conflicting_framebuffers(ap, "mgag200drmfb", primary); kfree(ap); } diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index d9b04b0..88dd221 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -15,8 +15,6 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_crtc_helper.h> -#include <linux/fb.h> - #include "mgag200_drv.h" static void mga_dirty_update(struct mga_fbdev *mfbdev, @@ -185,8 +183,10 @@ static int mgag200fb_create(struct drm_fb_helper *helper, } sysram = vmalloc(size); - if (!sysram) + if (!sysram) { + ret = -ENOMEM; goto err_sysram; + } info = drm_fb_helper_alloc_fbi(helper); if (IS_ERR(info)) { diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index 13798b3..e79cbc2 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -135,7 +135,7 @@ static int mga_vram_init(struct mga_device *mdev) aper->ranges[0].base = mdev->mc.vram_base; aper->ranges[0].size = mdev->mc.vram_window; - remove_conflicting_framebuffers(aper, "mgafb", true); + drm_fb_helper_remove_conflicting_framebuffers(aper, "mgafb", true); kfree(aper); if (!devm_request_mem_region(mdev->dev->dev, mdev->mc.vram_base, mdev->mc.vram_window, diff --git a/drivers/gpu/drm/mgag200/mgag200_ttm.c b/drivers/gpu/drm/mgag200/mgag200_ttm.c index 68268e5..919b35f 100644 --- a/drivers/gpu/drm/mgag200/mgag200_ttm.c +++ b/drivers/gpu/drm/mgag200/mgag200_ttm.c @@ -150,7 +150,8 @@ static int mgag200_bo_verify_access(struct ttm_buffer_object *bo, struct file *f { struct mgag200_bo *mgabo = mgag200_bo(bo); - return drm_vma_node_verify_access(&mgabo->gem.vma_node, filp); + return drm_vma_node_verify_access(&mgabo->gem.vma_node, + filp->private_data); } static int mgag200_ttm_io_mem_reserve(struct ttm_bo_device *bdev, diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 7c7a031..d96b2b6 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -11,6 +11,7 @@ config DRM_MSM select TMPFS select QCOM_SCM select SND_SOC_HDMI_CODEC if SND_SOC + select SYNC_FILE default y help DRM/KMS driver for MSM/snapdragon. diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 9737207..a968cad 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -422,12 +422,29 @@ static const struct { static int msm_hdmi_get_gpio(struct device_node *of_node, const char *name) { - int gpio = of_get_named_gpio(of_node, name, 0); + int gpio; + + /* try with the gpio names as in the table (downstream bindings) */ + gpio = of_get_named_gpio(of_node, name, 0); if (gpio < 0) { char name2[32]; - snprintf(name2, sizeof(name2), "%s-gpio", name); + + /* try with the gpio names as in the upstream bindings */ + snprintf(name2, sizeof(name2), "%s-gpios", name); gpio = of_get_named_gpio(of_node, name2, 0); if (gpio < 0) { + char name3[32]; + + /* + * try again after stripping out the "qcom,hdmi-tx" + * prefix. This is mainly to match "hpd-gpios" used + * in the upstream bindings + */ + if (sscanf(name2, "qcom,hdmi-tx-%s", name3)) + gpio = of_get_named_gpio(of_node, name3, 0); + } + + if (gpio < 0) { DBG("failed to get gpio: %s (%d)", name, gpio); gpio = -1; } diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c index de9007e..73e2021 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c @@ -243,7 +243,6 @@ void msm_hdmi_i2c_destroy(struct i2c_adapter *i2c) struct i2c_adapter *msm_hdmi_i2c_init(struct hdmi *hdmi) { - struct drm_device *dev = hdmi->dev; struct hdmi_i2c_adapter *hdmi_i2c; struct i2c_adapter *i2c = NULL; int ret; @@ -267,10 +266,8 @@ struct i2c_adapter *msm_hdmi_i2c_init(struct hdmi *hdmi) i2c->algo = &msm_hdmi_i2c_algorithm; ret = i2c_add_adapter(i2c); - if (ret) { - dev_err(dev->dev, "failed to register hdmi i2c: %d\n", ret); + if (ret) goto fail; - } return i2c; diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index 7b39e89..571a91e 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -228,18 +228,21 @@ static struct device_node *mdp4_detect_lcdc_panel(struct drm_device *dev) struct device_node *endpoint, *panel_node; struct device_node *np = dev->dev->of_node; - endpoint = of_graph_get_next_endpoint(np, NULL); + /* + * LVDS/LCDC is the first port described in the list of ports in the + * MDP4 DT node. + */ + endpoint = of_graph_get_endpoint_by_regs(np, 0, -1); if (!endpoint) { - DBG("no endpoint in MDP4 to fetch LVDS panel\n"); + DBG("no LVDS remote endpoint\n"); return NULL; } - /* don't proceed if we have an endpoint but no panel_node tied to it */ panel_node = of_graph_get_remote_port_parent(endpoint); if (!panel_node) { - dev_err(dev->dev, "no valid panel node\n"); + DBG("no valid panel node in LVDS endpoint\n"); of_node_put(endpoint); - return ERR_PTR(-ENODEV); + return NULL; } of_node_put(endpoint); @@ -262,14 +265,12 @@ static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms, switch (intf_type) { case DRM_MODE_ENCODER_LVDS: /* - * bail out early if: - * - there is no panel node (no need to initialize lcdc - * encoder and lvds connector), or - * - panel node is a bad pointer + * bail out early if there is no panel node (no need to + * initialize LCDC encoder and LVDS connector) */ panel_node = mdp4_detect_lcdc_panel(dev); - if (IS_ERR_OR_NULL(panel_node)) - return PTR_ERR(panel_node); + if (!panel_node) + return 0; encoder = mdp4_lcdc_encoder_init(dev, panel_node); if (IS_ERR(encoder)) { diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c index bc3d8e7..a06b064 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c @@ -93,7 +93,7 @@ static const struct drm_encoder_funcs mdp4_lcdc_encoder_funcs = { }; /* this should probably be a helper: */ -struct drm_connector *get_connector(struct drm_encoder *encoder) +static struct drm_connector *get_connector(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_connector *connector; diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 9f96dfe..3903dbc 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -81,7 +81,7 @@ static void mdp4_plane_install_properties(struct drm_plane *plane, // XXX } -int mdp4_plane_set_property(struct drm_plane *plane, +static int mdp4_plane_set_property(struct drm_plane *plane, struct drm_property *property, uint64_t val) { // XXX @@ -99,7 +99,7 @@ static const struct drm_plane_funcs mdp4_plane_funcs = { }; static int mdp4_plane_prepare_fb(struct drm_plane *plane, - const struct drm_plane_state *new_state) + struct drm_plane_state *new_state) { struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); struct mdp4_kms *mdp4_kms = get_kms(plane); @@ -113,7 +113,7 @@ static int mdp4_plane_prepare_fb(struct drm_plane *plane, } static void mdp4_plane_cleanup_fb(struct drm_plane *plane, - const struct drm_plane_state *old_state) + struct drm_plane_state *old_state) { struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); struct mdp4_kms *mdp4_kms = get_kms(plane); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 432c098..951c002 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -78,12 +78,12 @@ static void mdp5_plane_install_rotation_property(struct drm_device *dev, if (!dev->mode_config.rotation_property) dev->mode_config.rotation_property = drm_mode_create_rotation_property(dev, - BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y)); + DRM_ROTATE_0 | DRM_REFLECT_X | DRM_REFLECT_Y); if (dev->mode_config.rotation_property) drm_object_attach_property(&plane->base, dev->mode_config.rotation_property, - 0); + DRM_ROTATE_0); } /* helper to install properties which are common to planes and crtcs */ @@ -250,7 +250,7 @@ static const struct drm_plane_funcs mdp5_plane_funcs = { }; static int mdp5_plane_prepare_fb(struct drm_plane *plane, - const struct drm_plane_state *new_state) + struct drm_plane_state *new_state) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct mdp5_kms *mdp5_kms = get_kms(plane); @@ -264,7 +264,7 @@ static int mdp5_plane_prepare_fb(struct drm_plane *plane, } static void mdp5_plane_cleanup_fb(struct drm_plane *plane, - const struct drm_plane_state *old_state) + struct drm_plane_state *old_state) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct mdp5_kms *mdp5_kms = get_kms(plane); @@ -309,8 +309,8 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane, return -EINVAL; } - hflip = !!(state->rotation & BIT(DRM_REFLECT_X)); - vflip = !!(state->rotation & BIT(DRM_REFLECT_Y)); + hflip = !!(state->rotation & DRM_REFLECT_X); + vflip = !!(state->rotation & DRM_REFLECT_Y); if ((vflip && !(mdp5_plane->caps & MDP_PIPE_CAP_VFLIP)) || (hflip && !(mdp5_plane->caps & MDP_PIPE_CAP_HFLIP))) { dev_err(plane->dev->dev, @@ -743,8 +743,8 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, config |= get_scale_config(format, src_h, crtc_h, false); DBG("scale config = %x", config); - hflip = !!(pstate->rotation & BIT(DRM_REFLECT_X)); - vflip = !!(pstate->rotation & BIT(DRM_REFLECT_Y)); + hflip = !!(pstate->rotation & DRM_REFLECT_X); + vflip = !!(pstate->rotation & DRM_REFLECT_Y); spin_lock_irqsave(&mdp5_plane->pipe_lock, flags); diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 4a8a6f1..73bae38 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -112,13 +112,13 @@ static void complete_commit(struct msm_commit *c, bool async) struct msm_drm_private *priv = dev->dev_private; struct msm_kms *kms = priv->kms; - drm_atomic_helper_wait_for_fences(dev, state); + drm_atomic_helper_wait_for_fences(dev, state, false); kms->funcs->prepare_commit(kms, state); drm_atomic_helper_commit_modeset_disables(dev, state); - drm_atomic_helper_commit_planes(dev, state, false); + drm_atomic_helper_commit_planes(dev, state, 0); drm_atomic_helper_commit_modeset_enables(dev, state); diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 8a02370..fb5c0b0 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -26,9 +26,10 @@ * MSM driver version: * - 1.0.0 - initial interface * - 1.1.0 - adds madvise, and support for submits with > 4 cmd buffers + * - 1.2.0 - adds explicit fence support for submit ioctl */ #define MSM_VERSION_MAJOR 1 -#define MSM_VERSION_MINOR 1 +#define MSM_VERSION_MINOR 2 #define MSM_VERSION_PATCHLEVEL 0 static void msm_fb_output_poll_changed(struct drm_device *dev) @@ -347,9 +348,9 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv) int ret; ddev = drm_dev_alloc(drv, dev); - if (!ddev) { + if (IS_ERR(ddev)) { dev_err(dev, "failed to allocate drm_device\n"); - return -ENOMEM; + return PTR_ERR(ddev); } platform_set_drvdata(pdev, ddev); diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index b4bc7f1..d0da52f 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -157,6 +157,12 @@ struct msm_drm_private { struct shrinker shrinker; struct msm_vblank_ctrl vblank_ctrl; + + /* task holding struct_mutex.. currently only used in submit path + * to detect and reject faults from copy_from_user() for submit + * ioctl. + */ + struct task_struct *struct_mutex_task; }; struct msm_format { diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 6cd4af4..b6ac27e 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -196,11 +196,20 @@ int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct drm_gem_object *obj = vma->vm_private_data; struct drm_device *dev = obj->dev; + struct msm_drm_private *priv = dev->dev_private; struct page **pages; unsigned long pfn; pgoff_t pgoff; int ret; + /* This should only happen if userspace tries to pass a mmap'd + * but unfaulted gem bo vaddr into submit ioctl, triggering + * a page fault while struct_mutex is already held. This is + * not a valid use-case so just bail. + */ + if (priv->struct_mutex_task == current) + return VM_FAULT_SIGBUS; + /* Make sure we don't parallel update on a fault, nor move or remove * something from beneath our feet */ @@ -584,18 +593,16 @@ int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, ktime_t *timeout) { struct msm_gem_object *msm_obj = to_msm_bo(obj); bool write = !!(op & MSM_PREP_WRITE); - - if (op & MSM_PREP_NOSYNC) { - if (!reservation_object_test_signaled_rcu(msm_obj->resv, write)) - return -EBUSY; - } else { - int ret; - - ret = reservation_object_wait_timeout_rcu(msm_obj->resv, write, - true, timeout_to_jiffies(timeout)); - if (ret <= 0) - return ret == 0 ? -ETIMEDOUT : ret; - } + unsigned long remain = + op & MSM_PREP_NOSYNC ? 0 : timeout_to_jiffies(timeout); + long ret; + + ret = reservation_object_wait_timeout_rcu(msm_obj->resv, write, + true, remain); + if (ret == 0) + return remain == 0 ? -EBUSY : -ETIMEDOUT; + else if (ret < 0) + return ret; /* TODO cache maintenance */ diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 9766f9a..b6a0f37 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -15,6 +15,8 @@ * this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <linux/sync_file.h> + #include "msm_drv.h" #include "msm_gpu.h" #include "msm_gem.h" @@ -64,6 +66,14 @@ void msm_gem_submit_free(struct msm_gem_submit *submit) kfree(submit); } +static inline unsigned long __must_check +copy_from_user_inatomic(void *to, const void __user *from, unsigned long n) +{ + if (access_ok(VERIFY_READ, from, n)) + return __copy_from_user_inatomic(to, from, n); + return -EFAULT; +} + static int submit_lookup_objects(struct msm_gem_submit *submit, struct drm_msm_gem_submit *args, struct drm_file *file) { @@ -71,6 +81,7 @@ static int submit_lookup_objects(struct msm_gem_submit *submit, int ret = 0; spin_lock(&file->table_lock); + pagefault_disable(); for (i = 0; i < args->nr_bos; i++) { struct drm_msm_gem_submit_bo submit_bo; @@ -84,10 +95,15 @@ static int submit_lookup_objects(struct msm_gem_submit *submit, */ submit->bos[i].flags = 0; - ret = copy_from_user(&submit_bo, userptr, sizeof(submit_bo)); - if (ret) { - ret = -EFAULT; - goto out_unlock; + ret = copy_from_user_inatomic(&submit_bo, userptr, sizeof(submit_bo)); + if (unlikely(ret)) { + pagefault_enable(); + spin_unlock(&file->table_lock); + ret = copy_from_user(&submit_bo, userptr, sizeof(submit_bo)); + if (ret) + goto out; + spin_lock(&file->table_lock); + pagefault_disable(); } if (submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS) { @@ -127,9 +143,12 @@ static int submit_lookup_objects(struct msm_gem_submit *submit, } out_unlock: - submit->nr_bos = i; + pagefault_enable(); spin_unlock(&file->table_lock); +out: + submit->nr_bos = i; + return ret; } @@ -361,6 +380,9 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, struct msm_file_private *ctx = file->driver_priv; struct msm_gem_submit *submit; struct msm_gpu *gpu = priv->gpu; + struct fence *in_fence = NULL; + struct sync_file *sync_file = NULL; + int out_fence_fd = -1; unsigned i; int ret; @@ -370,13 +392,25 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, /* for now, we just have 3d pipe.. eventually this would need to * be more clever to dispatch to appropriate gpu module: */ - if (args->pipe != MSM_PIPE_3D0) + if (MSM_PIPE_ID(args->flags) != MSM_PIPE_3D0) + return -EINVAL; + + if (MSM_PIPE_FLAGS(args->flags) & ~MSM_SUBMIT_FLAGS) return -EINVAL; ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) return ret; + if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) { + out_fence_fd = get_unused_fd_flags(O_CLOEXEC); + if (out_fence_fd < 0) { + ret = out_fence_fd; + goto out_unlock; + } + } + priv->struct_mutex_task = current; + submit = submit_create(dev, gpu, args->nr_bos, args->nr_cmds); if (!submit) { ret = -ENOMEM; @@ -391,9 +425,32 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, if (ret) goto out; - ret = submit_fence_sync(submit); - if (ret) - goto out; + if (args->flags & MSM_SUBMIT_FENCE_FD_IN) { + in_fence = sync_file_get_fence(args->fence_fd); + + if (!in_fence) { + ret = -EINVAL; + goto out; + } + + /* TODO if we get an array-fence due to userspace merging multiple + * fences, we need a way to determine if all the backing fences + * are from our own context.. + */ + + if (in_fence->context != gpu->fctx->context) { + ret = fence_wait(in_fence, true); + if (ret) + goto out; + } + + } + + if (!(args->fence & MSM_SUBMIT_NO_IMPLICIT)) { + ret = submit_fence_sync(submit); + if (ret) + goto out; + } ret = submit_pin_objects(submit); if (ret) @@ -459,15 +516,40 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, submit->nr_cmds = i; - ret = msm_gpu_submit(gpu, submit, ctx); + submit->fence = msm_fence_alloc(gpu->fctx); + if (IS_ERR(submit->fence)) { + ret = PTR_ERR(submit->fence); + submit->fence = NULL; + goto out; + } + + if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) { + sync_file = sync_file_create(submit->fence); + if (!sync_file) { + ret = -ENOMEM; + goto out; + } + } + + msm_gpu_submit(gpu, submit, ctx); args->fence = submit->fence->seqno; + if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) { + fd_install(out_fence_fd, sync_file->file); + args->fence_fd = out_fence_fd; + } + out: + if (in_fence) + fence_put(in_fence); submit_cleanup(submit); if (ret) msm_gem_submit_free(submit); out_unlock: + if (ret && (out_fence_fd >= 0)) + put_unused_fd(out_fence_fd); + priv->struct_mutex_task = NULL; mutex_unlock(&dev->struct_mutex); return ret; } diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 36ed53e..5bb0983 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -509,22 +509,15 @@ void msm_gpu_retire(struct msm_gpu *gpu) } /* add bo's to gpu's ring, and kick gpu: */ -int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, +void msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, struct msm_file_private *ctx) { struct drm_device *dev = gpu->dev; struct msm_drm_private *priv = dev->dev_private; - int i, ret; + int i; WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - submit->fence = msm_fence_alloc(gpu->fctx); - if (IS_ERR(submit->fence)) { - ret = PTR_ERR(submit->fence); - submit->fence = NULL; - return ret; - } - inactive_cancel(gpu); list_add_tail(&submit->node, &gpu->submit_list); @@ -557,8 +550,6 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, priv->lastctx = ctx; hangcheck_timer_reset(gpu); - - return 0; } /* diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index c902283..d61d98a 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -163,7 +163,7 @@ int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime, uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs); void msm_gpu_retire(struct msm_gpu *gpu); -int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, +void msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, struct msm_file_private *ctx); int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index f2ad17a..dc57b62 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -225,6 +225,17 @@ static bool nouveau_pr3_present(struct pci_dev *pdev) if (!parent_pdev) return false; + if (!parent_pdev->bridge_d3) { + /* + * Parent PCI bridge is currently not power managed. + * Since userspace can change these afterwards to be on + * the safe side we stick with _DSM and prevent usage of + * _PR3 from the bridge. + */ + pci_d3cold_disable(pdev); + return false; + } + parent_adev = ACPI_COMPANION(&parent_pdev->dev); if (!parent_adev) return false; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 8ab9ce5..66f31c3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -1315,7 +1315,8 @@ nouveau_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp) { struct nouveau_bo *nvbo = nouveau_bo(bo); - return drm_vma_node_verify_access(&nvbo->gem.vma_node, filp); + return drm_vma_node_verify_access(&nvbo->gem.vma_node, + filp->private_data); } static int diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 66c1280..3100fd88 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -351,7 +351,7 @@ static int nouveau_drm_probe(struct pci_dev *pdev, boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; #endif if (nouveau_modeset != 2) - remove_conflicting_framebuffers(aper, "nouveaufb", boot); + drm_fb_helper_remove_conflicting_framebuffers(aper, "nouveaufb", boot); kfree(aper); ret = nvkm_device_pci_new(pdev, nouveau_config, nouveau_debug, @@ -1067,8 +1067,8 @@ nouveau_platform_device_create(const struct nvkm_device_tegra_func *func, goto err_free; drm = drm_dev_alloc(&driver_platform, &pdev->dev); - if (!drm) { - err = -ENOMEM; + if (IS_ERR(drm)) { + err = PTR_ERR(drm); goto err_free; } diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index d1f248f..9f56927 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -32,7 +32,6 @@ #include <linux/tty.h> #include <linux/sysrq.h> #include <linux/delay.h> -#include <linux/fb.h> #include <linux/init.h> #include <linux/screen_info.h> #include <linux/vga_switcheroo.h> diff --git a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c index 0eae8af..b1f3b81 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c @@ -13,7 +13,6 @@ #include <linux/backlight.h> #include <linux/delay.h> -#include <linux/fb.h> #include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/jiffies.h> diff --git a/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c b/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c index fc4c238..9f3d6f4 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c @@ -14,7 +14,6 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/spi/spi.h> -#include <linux/fb.h> #include <linux/gpio/consumer.h> #include <linux/of_gpio.h> diff --git a/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c b/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c index 157c512..3557a4c 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c @@ -28,7 +28,6 @@ #include <linux/jiffies.h> #include <linux/sched.h> #include <linux/backlight.h> -#include <linux/fb.h> #include <linux/gpio/consumer.h> #include <linux/of.h> #include <linux/of_gpio.h> diff --git a/drivers/gpu/drm/omapdrm/dss/dss-of.c b/drivers/gpu/drm/omapdrm/dss/dss-of.c index e256d87..dfd4e96 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss-of.c +++ b/drivers/gpu/drm/omapdrm/dss/dss-of.c @@ -125,16 +125,15 @@ u32 dss_of_port_get_port_number(struct device_node *port) static struct device_node *omapdss_of_get_remote_port(const struct device_node *node) { - struct device_node *np, *np_parent; + struct device_node *np; np = of_parse_phandle(node, "remote-endpoint", 0); if (!np) return NULL; - np_parent = of_get_next_parent(np); - of_node_put(np); + np = of_get_next_parent(np); - return np_parent; + return np; } struct device_node * diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 26c6134..e1cfba5 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -96,7 +96,7 @@ static void omap_atomic_complete(struct omap_atomic_state_commit *commit) dispc_runtime_get(); drm_atomic_helper_commit_modeset_disables(dev, old_state); - drm_atomic_helper_commit_planes(dev, old_state, false); + drm_atomic_helper_commit_planes(dev, old_state, 0); drm_atomic_helper_commit_modeset_enables(dev, old_state); omap_atomic_wait_for_completion(dev, old_state); @@ -295,9 +295,9 @@ static int omap_modeset_init_properties(struct drm_device *dev) if (priv->has_dmm) { dev->mode_config.rotation_property = drm_mode_create_rotation_property(dev, - BIT(DRM_ROTATE_0) | BIT(DRM_ROTATE_90) | - BIT(DRM_ROTATE_180) | BIT(DRM_ROTATE_270) | - BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y)); + DRM_ROTATE_0 | DRM_ROTATE_90 | + DRM_ROTATE_180 | DRM_ROTATE_270 | + DRM_REFLECT_X | DRM_REFLECT_Y); if (!dev->mode_config.rotation_property) return -ENOMEM; } diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c index 31f5178..5f3337f 100644 --- a/drivers/gpu/drm/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -179,24 +179,24 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, (uint32_t)win->rotation); /* fallthru to default to no rotation */ case 0: - case BIT(DRM_ROTATE_0): + case DRM_ROTATE_0: orient = 0; break; - case BIT(DRM_ROTATE_90): + case DRM_ROTATE_90: orient = MASK_XY_FLIP | MASK_X_INVERT; break; - case BIT(DRM_ROTATE_180): + case DRM_ROTATE_180: orient = MASK_X_INVERT | MASK_Y_INVERT; break; - case BIT(DRM_ROTATE_270): + case DRM_ROTATE_270: orient = MASK_XY_FLIP | MASK_Y_INVERT; break; } - if (win->rotation & BIT(DRM_REFLECT_X)) + if (win->rotation & DRM_REFLECT_X) orient ^= MASK_X_INVERT; - if (win->rotation & BIT(DRM_REFLECT_Y)) + if (win->rotation & DRM_REFLECT_Y) orient ^= MASK_Y_INVERT; /* adjust x,y offset for flip/invert: */ @@ -213,7 +213,7 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, } else { switch (win->rotation & DRM_ROTATE_MASK) { case 0: - case BIT(DRM_ROTATE_0): + case DRM_ROTATE_0: /* OK */ break; diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 5252ab7..66ac8c4 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -60,7 +60,7 @@ to_omap_plane_state(struct drm_plane_state *state) } static int omap_plane_prepare_fb(struct drm_plane *plane, - const struct drm_plane_state *new_state) + struct drm_plane_state *new_state) { if (!new_state->fb) return 0; @@ -69,7 +69,7 @@ static int omap_plane_prepare_fb(struct drm_plane *plane, } static void omap_plane_cleanup_fb(struct drm_plane *plane, - const struct drm_plane_state *old_state) + struct drm_plane_state *old_state) { if (old_state->fb) omap_framebuffer_unpin(old_state->fb); @@ -109,8 +109,8 @@ static void omap_plane_atomic_update(struct drm_plane *plane, win.src_y = state->src_y >> 16; switch (state->rotation & DRM_ROTATE_MASK) { - case BIT(DRM_ROTATE_90): - case BIT(DRM_ROTATE_270): + case DRM_ROTATE_90: + case DRM_ROTATE_270: win.src_w = state->src_h >> 16; win.src_h = state->src_w >> 16; break; @@ -149,7 +149,7 @@ static void omap_plane_atomic_disable(struct drm_plane *plane, struct omap_plane_state *omap_state = to_omap_plane_state(plane->state); struct omap_plane *omap_plane = to_omap_plane(plane); - plane->state->rotation = BIT(DRM_ROTATE_0); + plane->state->rotation = DRM_ROTATE_0; omap_state->zorder = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id; @@ -178,7 +178,7 @@ static int omap_plane_atomic_check(struct drm_plane *plane, return -EINVAL; if (state->fb) { - if (state->rotation != BIT(DRM_ROTATE_0) && + if (state->rotation != DRM_ROTATE_0 && !omap_framebuffer_supports_rotation(state->fb)) return -EINVAL; } @@ -269,7 +269,7 @@ static void omap_plane_reset(struct drm_plane *plane) */ omap_state->zorder = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id; - omap_state->base.rotation = BIT(DRM_ROTATE_0); + omap_state->base.rotation = DRM_ROTATE_0; plane->state = &omap_state->base; plane->state->plane = plane; diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 1500ab9..62aba97 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -18,6 +18,17 @@ config DRM_PANEL_SIMPLE that it can be automatically turned off when the panel goes into a low power state. +config DRM_PANEL_JDI_LT070ME05000 + tristate "JDI LT070ME05000 WUXGA DSI panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for JDI DSI video mode + panel as found in Google Nexus 7 (2013) devices. + The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses + 24 bit per pixel. + config DRM_PANEL_SAMSUNG_LD9040 tristate "Samsung LD9040 RGB/SPI panel" depends on OF && SPI diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index f277eed..a5c7ec0 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o +obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c new file mode 100644 index 0000000..5b2340e --- /dev/null +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2016 InforceComputing + * Author: Vinay Simha BN <simhavcs@gmail.com> + * + * Copyright (C) 2016 Linaro Ltd + * Author: Sumit Semwal <sumit.semwal@linaro.org> + * + * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a + * JDI model LT070ME05000, and its data sheet is at: + * http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/backlight.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_panel.h> + +#include <video/mipi_display.h> + +static const char * const regulator_names[] = { + "vddp", + "iovcc" +}; + +struct jdi_panel { + struct drm_panel base; + struct mipi_dsi_device *dsi; + + struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)]; + + struct gpio_desc *enable_gpio; + struct gpio_desc *reset_gpio; + struct gpio_desc *dcdc_en_gpio; + struct backlight_device *backlight; + + bool prepared; + bool enabled; + + const struct drm_display_mode *mode; +}; + +static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel) +{ + return container_of(panel, struct jdi_panel, base); +} + +static int jdi_panel_init(struct jdi_panel *jdi) +{ + struct mipi_dsi_device *dsi = jdi->dsi; + struct device *dev = &jdi->dsi->dev; + int ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_soft_reset(dsi); + if (ret < 0) + return ret; + + usleep_range(10000, 20000); + + ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT << 4); + if (ret < 0) { + dev_err(dev, "failed to set pixel format: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_set_column_address(dsi, 0, jdi->mode->hdisplay - 1); + if (ret < 0) { + dev_err(dev, "failed to set column address: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_set_page_address(dsi, 0, jdi->mode->vdisplay - 1); + if (ret < 0) { + dev_err(dev, "failed to set page address: %d\n", ret); + return ret; + } + + /* + * BIT(5) BCTRL = 1 Backlight Control Block On, Brightness registers + * are active + * BIT(3) BL = 1 Backlight Control On + * BIT(2) DD = 0 Display Dimming is Off + */ + ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, + (u8[]){ 0x24 }, 1); + if (ret < 0) { + dev_err(dev, "failed to write control display: %d\n", ret); + return ret; + } + + /* CABC off */ + ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_POWER_SAVE, + (u8[]){ 0x00 }, 1); + if (ret < 0) { + dev_err(dev, "failed to set cabc off: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "failed to set exit sleep mode: %d\n", ret); + return ret; + } + + msleep(120); + + ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x00}, 2); + if (ret < 0) { + dev_err(dev, "failed to set mcap: %d\n", ret); + return ret; + } + + mdelay(10); + + /* Interface setting, video mode */ + ret = mipi_dsi_generic_write(dsi, (u8[]) + {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00}, 6); + if (ret < 0) { + dev_err(dev, "failed to set display interface setting: %d\n" + , ret); + return ret; + } + + mdelay(20); + + ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x03}, 2); + if (ret < 0) { + dev_err(dev, "failed to set default values for mcap: %d\n" + , ret); + return ret; + } + + return 0; +} + +static int jdi_panel_on(struct jdi_panel *jdi) +{ + struct mipi_dsi_device *dsi = jdi->dsi; + struct device *dev = &jdi->dsi->dev; + int ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) + dev_err(dev, "failed to set display on: %d\n", ret); + + return ret; +} + +static void jdi_panel_off(struct jdi_panel *jdi) +{ + struct mipi_dsi_device *dsi = jdi->dsi; + struct device *dev = &jdi->dsi->dev; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) + dev_err(dev, "failed to set display off: %d\n", ret); + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) + dev_err(dev, "failed to enter sleep mode: %d\n", ret); + + msleep(100); +} + +static int jdi_panel_disable(struct drm_panel *panel) +{ + struct jdi_panel *jdi = to_jdi_panel(panel); + + if (!jdi->enabled) + return 0; + + jdi->backlight->props.power = FB_BLANK_POWERDOWN; + backlight_update_status(jdi->backlight); + + jdi->enabled = false; + + return 0; +} + +static int jdi_panel_unprepare(struct drm_panel *panel) +{ + struct jdi_panel *jdi = to_jdi_panel(panel); + struct device *dev = &jdi->dsi->dev; + int ret; + + if (!jdi->prepared) + return 0; + + jdi_panel_off(jdi); + + ret = regulator_bulk_disable(ARRAY_SIZE(jdi->supplies), jdi->supplies); + if (ret < 0) + dev_err(dev, "regulator disable failed, %d\n", ret); + + gpiod_set_value(jdi->enable_gpio, 0); + + gpiod_set_value(jdi->reset_gpio, 1); + + gpiod_set_value(jdi->dcdc_en_gpio, 0); + + jdi->prepared = false; + + return 0; +} + +static int jdi_panel_prepare(struct drm_panel *panel) +{ + struct jdi_panel *jdi = to_jdi_panel(panel); + struct device *dev = &jdi->dsi->dev; + int ret; + + if (jdi->prepared) + return 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(jdi->supplies), jdi->supplies); + if (ret < 0) { + dev_err(dev, "regulator enable failed, %d\n", ret); + return ret; + } + + msleep(20); + + gpiod_set_value(jdi->dcdc_en_gpio, 1); + usleep_range(10, 20); + + gpiod_set_value(jdi->reset_gpio, 0); + usleep_range(10, 20); + + gpiod_set_value(jdi->enable_gpio, 1); + usleep_range(10, 20); + + ret = jdi_panel_init(jdi); + if (ret < 0) { + dev_err(dev, "failed to init panel: %d\n", ret); + goto poweroff; + } + + ret = jdi_panel_on(jdi); + if (ret < 0) { + dev_err(dev, "failed to set panel on: %d\n", ret); + goto poweroff; + } + + jdi->prepared = true; + + return 0; + +poweroff: + ret = regulator_bulk_disable(ARRAY_SIZE(jdi->supplies), jdi->supplies); + if (ret < 0) + dev_err(dev, "regulator disable failed, %d\n", ret); + + gpiod_set_value(jdi->enable_gpio, 0); + + gpiod_set_value(jdi->reset_gpio, 1); + + gpiod_set_value(jdi->dcdc_en_gpio, 0); + + return ret; +} + +static int jdi_panel_enable(struct drm_panel *panel) +{ + struct jdi_panel *jdi = to_jdi_panel(panel); + + if (jdi->enabled) + return 0; + + jdi->backlight->props.power = FB_BLANK_UNBLANK; + backlight_update_status(jdi->backlight); + + jdi->enabled = true; + + return 0; +} + +static const struct drm_display_mode default_mode = { + .clock = 155493, + .hdisplay = 1200, + .hsync_start = 1200 + 48, + .hsync_end = 1200 + 48 + 32, + .htotal = 1200 + 48 + 32 + 60, + .vdisplay = 1920, + .vsync_start = 1920 + 3, + .vsync_end = 1920 + 3 + 5, + .vtotal = 1920 + 3 + 5 + 6, + .vrefresh = 60, + .flags = 0, +}; + +static int jdi_panel_get_modes(struct drm_panel *panel) +{ + struct drm_display_mode *mode; + struct jdi_panel *jdi = to_jdi_panel(panel); + struct device *dev = &jdi->dsi->dev; + + mode = drm_mode_duplicate(panel->drm, &default_mode); + if (!mode) { + dev_err(dev, "failed to add mode %ux%ux@%u\n", + default_mode.hdisplay, default_mode.vdisplay, + default_mode.vrefresh); + return -ENOMEM; + } + + drm_mode_set_name(mode); + + drm_mode_probed_add(panel->connector, mode); + + panel->connector->display_info.width_mm = 95; + panel->connector->display_info.height_mm = 151; + + return 1; +} + +static int dsi_dcs_bl_get_brightness(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + int ret; + u16 brightness = bl->props.brightness; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness); + if (ret < 0) + return ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + return brightness & 0xff; +} + +static int dsi_dcs_bl_update_status(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness); + if (ret < 0) + return ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + return 0; +} + +static const struct backlight_ops dsi_bl_ops = { + .update_status = dsi_dcs_bl_update_status, + .get_brightness = dsi_dcs_bl_get_brightness, +}; + +static struct backlight_device * +drm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct backlight_properties props; + + memset(&props, 0, sizeof(props)); + props.type = BACKLIGHT_RAW; + props.brightness = 255; + props.max_brightness = 255; + + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, + &dsi_bl_ops, &props); +} + +static const struct drm_panel_funcs jdi_panel_funcs = { + .disable = jdi_panel_disable, + .unprepare = jdi_panel_unprepare, + .prepare = jdi_panel_prepare, + .enable = jdi_panel_enable, + .get_modes = jdi_panel_get_modes, +}; + +static const struct of_device_id jdi_of_match[] = { + { .compatible = "jdi,lt070me05000", }, + { } +}; +MODULE_DEVICE_TABLE(of, jdi_of_match); + +static int jdi_panel_add(struct jdi_panel *jdi) +{ + struct device *dev = &jdi->dsi->dev; + int ret; + unsigned int i; + + jdi->mode = &default_mode; + + for (i = 0; i < ARRAY_SIZE(jdi->supplies); i++) + jdi->supplies[i].supply = regulator_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(jdi->supplies), + jdi->supplies); + if (ret < 0) { + dev_err(dev, "failed to init regulator, ret=%d\n", ret); + return ret; + } + + jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(jdi->enable_gpio)) { + ret = PTR_ERR(jdi->enable_gpio); + dev_err(dev, "cannot get enable-gpio %d\n", ret); + return ret; + } + + jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(jdi->reset_gpio)) { + ret = PTR_ERR(jdi->reset_gpio); + dev_err(dev, "cannot get reset-gpios %d\n", ret); + return ret; + } + + jdi->dcdc_en_gpio = devm_gpiod_get(dev, "dcdc-en", GPIOD_OUT_LOW); + if (IS_ERR(jdi->dcdc_en_gpio)) { + ret = PTR_ERR(jdi->dcdc_en_gpio); + dev_err(dev, "cannot get dcdc-en-gpio %d\n", ret); + return ret; + } + + jdi->backlight = drm_panel_create_dsi_backlight(jdi->dsi); + if (IS_ERR(jdi->backlight)) { + ret = PTR_ERR(jdi->backlight); + dev_err(dev, "failed to register backlight %d\n", ret); + return ret; + } + + drm_panel_init(&jdi->base); + jdi->base.funcs = &jdi_panel_funcs; + jdi->base.dev = &jdi->dsi->dev; + + ret = drm_panel_add(&jdi->base); + + return ret; +} + +static void jdi_panel_del(struct jdi_panel *jdi) +{ + if (jdi->base.dev) + drm_panel_remove(&jdi->base); +} + +static int jdi_panel_probe(struct mipi_dsi_device *dsi) +{ + struct jdi_panel *jdi; + int ret; + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | + MIPI_DSI_CLOCK_NON_CONTINUOUS; + + jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL); + if (!jdi) + return -ENOMEM; + + mipi_dsi_set_drvdata(dsi, jdi); + + jdi->dsi = dsi; + + ret = jdi_panel_add(jdi); + if (ret < 0) + return ret; + + return mipi_dsi_attach(dsi); +} + +static int jdi_panel_remove(struct mipi_dsi_device *dsi) +{ + struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = jdi_panel_disable(&jdi->base); + if (ret < 0) + dev_err(&dsi->dev, "failed to disable panel: %d\n", ret); + + ret = mipi_dsi_detach(dsi); + if (ret < 0) + dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", + ret); + + drm_panel_detach(&jdi->base); + jdi_panel_del(jdi); + + return 0; +} + +static void jdi_panel_shutdown(struct mipi_dsi_device *dsi) +{ + struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi); + + jdi_panel_disable(&jdi->base); +} + +static struct mipi_dsi_driver jdi_panel_driver = { + .driver = { + .name = "panel-jdi-lt070me05000", + .of_match_table = jdi_of_match, + }, + .probe = jdi_panel_probe, + .remove = jdi_panel_remove, + .shutdown = jdi_panel_shutdown, +}; +module_mipi_dsi_driver(jdi_panel_driver); + +MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>"); +MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>"); +MODULE_DESCRIPTION("JDI LT070ME05000 WUXGA"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 85143d1..113db3c 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -849,6 +849,34 @@ static const struct panel_desc innolux_at070tn92 = { .bus_format = MEDIA_BUS_FMT_RGB888_1X24, }; +static const struct display_timing innolux_g101ice_l01_timing = { + .pixelclock = { 60400000, 71100000, 74700000 }, + .hactive = { 1280, 1280, 1280 }, + .hfront_porch = { 41, 80, 100 }, + .hback_porch = { 40, 79, 99 }, + .hsync_len = { 1, 1, 1 }, + .vactive = { 800, 800, 800 }, + .vfront_porch = { 5, 11, 14 }, + .vback_porch = { 4, 11, 14 }, + .vsync_len = { 1, 1, 1 }, + .flags = DISPLAY_FLAGS_DE_HIGH, +}; + +static const struct panel_desc innolux_g101ice_l01 = { + .timings = &innolux_g101ice_l01_timing, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 217, + .height = 135, + }, + .delay = { + .enable = 200, + .disable = 200, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, +}; + static const struct drm_display_mode innolux_g121i1_l01_mode = { .clock = 71000, .hdisplay = 1280, @@ -1186,7 +1214,7 @@ static const struct panel_desc olimex_lcd_olinuxino_43ts = { .width = 105, .height = 67, }, - .bus_format = MEDIA_BUS_FMT_RGB666_1X18, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, }; /* @@ -1245,6 +1273,7 @@ static const struct panel_desc ortustech_com43h4m85ulc = { .height = 93, }, .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE, }; static const struct drm_display_mode qd43003c0_40_mode = { @@ -1384,6 +1413,11 @@ static const struct panel_desc sharp_lq123p1jx31 = { .width = 259, .height = 173, }, + .delay = { + .prepare = 110, + .enable = 50, + .unprepare = 550, + }, }; static const struct drm_display_mode shelly_sca07010_bfn_lnn_mode = { @@ -1430,6 +1464,11 @@ static const struct panel_desc starry_kr122ea0sra = { .width = 263, .height = 164, }, + .delay = { + .prepare = 10 + 200, + .enable = 50, + .unprepare = 10 + 500, + }, }; static const struct drm_display_mode tpk_f07a_0102_mode = { @@ -1575,6 +1614,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "innolux,at070tn92", .data = &innolux_at070tn92, }, { + .compatible ="innolux,g101ice-l01", + .data = &innolux_g101ice_l01 + }, { .compatible ="innolux,g121i1-l01", .data = &innolux_g121i1_l01 }, { diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 3aef127..a61c0d4 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -211,6 +211,7 @@ static void qxl_crtc_destroy(struct drm_crtc *crtc) struct qxl_crtc *qxl_crtc = to_qxl_crtc(crtc); drm_crtc_cleanup(crtc); + qxl_bo_unref(&qxl_crtc->cursor_bo); kfree(qxl_crtc); } @@ -296,6 +297,52 @@ qxl_hide_cursor(struct qxl_device *qdev) return 0; } +static int qxl_crtc_apply_cursor(struct drm_crtc *crtc) +{ + struct qxl_crtc *qcrtc = to_qxl_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct qxl_device *qdev = dev->dev_private; + struct qxl_cursor_cmd *cmd; + struct qxl_release *release; + int ret = 0; + + if (!qcrtc->cursor_bo) + return 0; + + ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), + QXL_RELEASE_CURSOR_CMD, + &release, NULL); + if (ret) + return ret; + + ret = qxl_release_list_add(release, qcrtc->cursor_bo); + if (ret) + goto out_free_release; + + ret = qxl_release_reserve_list(release, false); + if (ret) + goto out_free_release; + + cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release); + cmd->type = QXL_CURSOR_SET; + cmd->u.set.position.x = qcrtc->cur_x + qcrtc->hot_spot_x; + cmd->u.set.position.y = qcrtc->cur_y + qcrtc->hot_spot_y; + + cmd->u.set.shape = qxl_bo_physical_address(qdev, qcrtc->cursor_bo, 0); + + cmd->u.set.visible = 1; + qxl_release_unmap(qdev, release, &cmd->release_info); + + qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false); + qxl_release_fence_buffer_objects(release); + + return ret; + +out_free_release: + qxl_release_free(qdev, release); + return ret; +} + static int qxl_crtc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv, uint32_t handle, @@ -400,7 +447,8 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc, } drm_gem_object_unreference_unlocked(obj); - qxl_bo_unref(&cursor_bo); + qxl_bo_unref (&qcrtc->cursor_bo); + qcrtc->cursor_bo = cursor_bo; return ret; @@ -655,6 +703,12 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc, bo->surf.stride, bo->surf.format); qxl_io_create_primary(qdev, 0, bo); bo->is_primary = true; + + ret = qxl_crtc_apply_cursor(crtc); + if (ret) { + DRM_ERROR("could not set cursor after modeset"); + ret = 0; + } } if (bo->is_primary) { diff --git a/drivers/gpu/drm/qxl/qxl_draw.c b/drivers/gpu/drm/qxl/qxl_draw.c index ffe8853..9b728ed 100644 --- a/drivers/gpu/drm/qxl/qxl_draw.c +++ b/drivers/gpu/drm/qxl/qxl_draw.c @@ -57,11 +57,8 @@ static struct qxl_rect *drawable_set_clipping(struct qxl_device *qdev, static int alloc_drawable(struct qxl_device *qdev, struct qxl_release **release) { - int ret; - ret = qxl_alloc_release_reserved(qdev, sizeof(struct qxl_drawable), - QXL_RELEASE_DRAWABLE, release, - NULL); - return ret; + return qxl_alloc_release_reserved(qdev, sizeof(struct qxl_drawable), + QXL_RELEASE_DRAWABLE, release, NULL); } static void diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 8e633ca..5f3e5ad 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -137,6 +137,7 @@ struct qxl_crtc { int cur_y; int hot_spot_x; int hot_spot_y; + struct qxl_bo *cursor_bo; }; struct qxl_output { diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index df26570..2cd879a 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -24,7 +24,6 @@ * David Airlie */ #include <linux/module.h> -#include <linux/fb.h> #include "drmP.h" #include "drm/drm.h" @@ -73,10 +72,12 @@ static void qxl_fb_image_init(struct qxl_fb_image *qxl_fb_image, } } +#ifdef CONFIG_DRM_FBDEV_EMULATION static struct fb_deferred_io qxl_defio = { .delay = QXL_DIRTY_DELAY, .deferred_io = drm_fb_helper_deferred_io, }; +#endif static struct fb_ops qxlfb_ops = { .owner = THIS_MODULE, @@ -313,8 +314,10 @@ static int qxlfb_create(struct qxl_fbdev *qfbdev, goto out_destroy_fbi; } +#ifdef CONFIG_DRM_FBDEV_EMULATION info->fbdefio = &qxl_defio; fb_deferred_io_init(info); +#endif qdev->fbdev_info = info; qdev->fbdev_qfb = &qfbdev->qfb; diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c index f599cd0..cd83f05 100644 --- a/drivers/gpu/drm/qxl/qxl_release.c +++ b/drivers/gpu/drm/qxl/qxl_release.c @@ -203,12 +203,9 @@ qxl_release_free(struct qxl_device *qdev, static int qxl_release_bo_alloc(struct qxl_device *qdev, struct qxl_bo **bo) { - int ret; /* pin releases bo's they are too messy to evict */ - ret = qxl_bo_create(qdev, PAGE_SIZE, false, true, - QXL_GEM_DOMAIN_VRAM, NULL, - bo); - return ret; + return qxl_bo_create(qdev, PAGE_SIZE, false, true, + QXL_GEM_DOMAIN_VRAM, NULL, bo); } int qxl_release_list_add(struct qxl_release *release, struct qxl_bo *bo) diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c index a257ad2..e26c82d 100644 --- a/drivers/gpu/drm/qxl/qxl_ttm.c +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -210,7 +210,8 @@ static int qxl_verify_access(struct ttm_buffer_object *bo, struct file *filp) { struct qxl_bo *qbo = to_qxl_bo(bo); - return drm_vma_node_verify_access(&qbo->gem_base.vma_node, filp); + return drm_vma_node_verify_access(&qbo->gem_base.vma_node, + filp->private_data); } static int qxl_ttm_io_mem_reserve(struct ttm_bo_device *bdev, diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c index c57b4de..a982be5 100644 --- a/drivers/gpu/drm/r128/r128_drv.c +++ b/drivers/gpu/drm/r128/r128_drv.c @@ -56,7 +56,7 @@ static const struct file_operations r128_driver_fops = { static struct drm_driver driver = { .driver_features = - DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG | + DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG | DRIVER_LEGACY | DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, .dev_priv_size = sizeof(drm_r128_buf_priv_t), .load = r128_driver_load, diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 2029e35..a4e9f35 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -627,7 +627,9 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, if (radeon_crtc->ss.refdiv) { radeon_crtc->pll_flags |= RADEON_PLL_USE_REF_DIV; radeon_crtc->pll_reference_div = radeon_crtc->ss.refdiv; - if (rdev->family >= CHIP_RV770) + if (ASIC_IS_AVIVO(rdev) && + rdev->family != CHIP_RS780 && + rdev->family != CHIP_RS880) radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV; } } @@ -1154,6 +1156,7 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, u32 tmp, viewport_w, viewport_h; int r; bool bypass_lut = false; + char *format_name; /* no fb bound */ if (!atomic && !crtc->primary->fb) { @@ -1257,8 +1260,9 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, bypass_lut = true; break; default: - DRM_ERROR("Unsupported screen format %s\n", - drm_get_format_name(target_fb->pixel_format)); + format_name = drm_get_format_name(target_fb->pixel_format); + DRM_ERROR("Unsupported screen format %s\n", format_name); + kfree(format_name); return -EINVAL; } @@ -1469,6 +1473,7 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, u32 viewport_w, viewport_h; int r; bool bypass_lut = false; + char *format_name; /* no fb bound */ if (!atomic && !crtc->primary->fb) { @@ -1558,8 +1563,9 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, bypass_lut = true; break; default: - DRM_ERROR("Unsupported screen format %s\n", - drm_get_format_name(target_fb->pixel_format)); + format_name = drm_get_format_name(target_fb->pixel_format); + DRM_ERROR("Unsupported screen format %s\n", format_name); + kfree(format_name); return -EINVAL; } diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index b1784a1..f6ff41a 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -4193,11 +4193,7 @@ u32 cik_gfx_get_rptr(struct radeon_device *rdev, u32 cik_gfx_get_wptr(struct radeon_device *rdev, struct radeon_ring *ring) { - u32 wptr; - - wptr = RREG32(CP_RB0_WPTR); - - return wptr; + return RREG32(CP_RB0_WPTR); } void cik_gfx_set_wptr(struct radeon_device *rdev, diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index f25994b..f5e84f4 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -1071,11 +1071,7 @@ u32 r100_gfx_get_rptr(struct radeon_device *rdev, u32 r100_gfx_get_wptr(struct radeon_device *rdev, struct radeon_ring *ring) { - u32 wptr; - - wptr = RREG32(RADEON_CP_RB_WPTR); - - return wptr; + return RREG32(RADEON_CP_RB_WPTR); } void r100_gfx_set_wptr(struct radeon_device *rdev, diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 6406536..a951881 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -2631,11 +2631,7 @@ u32 r600_gfx_get_rptr(struct radeon_device *rdev, u32 r600_gfx_get_wptr(struct radeon_device *rdev, struct radeon_ring *ring) { - u32 wptr; - - wptr = RREG32(R600_CP_RB_WPTR); - - return wptr; + return RREG32(R600_CP_RB_WPTR); } void r600_gfx_set_wptr(struct radeon_device *rdev, diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c index a1321b2..2fdcd04 100644 --- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c +++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c @@ -203,16 +203,7 @@ static int radeon_atpx_validate(struct radeon_atpx *atpx) atpx->is_hybrid = false; if (valid_bits & ATPX_MS_HYBRID_GFX_SUPPORTED) { printk("ATPX Hybrid Graphics\n"); -#if 1 - /* This is a temporary hack until the D3 cold support - * makes it upstream. The ATPX power_control method seems - * to still work on even if the system should be using - * the new standardized hybrid D3 cold ACPI interface. - */ - atpx->functions.power_cntl = true; -#else atpx->functions.power_cntl = false; -#endif atpx->is_hybrid = true; } diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index d406378..91c8f43 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -39,6 +39,7 @@ #include <linux/pm_runtime.h> #include <linux/vga_switcheroo.h> #include <drm/drm_gem.h> +#include <drm/drm_fb_helper.h> #include "drm_crtc_helper.h" #include "radeon_kfd.h" @@ -322,7 +323,7 @@ static int radeon_kick_out_firmware_fb(struct pci_dev *pdev) #ifdef CONFIG_X86 primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; #endif - remove_conflicting_framebuffers(ap, "radeondrmfb", primary); + drm_fb_helper_remove_conflicting_framebuffers(ap, "radeondrmfb", primary); kfree(ap); return 0; diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 6b2537d..0daad44 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -25,7 +25,6 @@ */ #include <linux/module.h> #include <linux/slab.h> -#include <linux/fb.h> #include <linux/pm_runtime.h> #include <drm/drmP.h> @@ -411,7 +410,7 @@ void radeon_fbdev_fini(struct radeon_device *rdev) void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state) { if (rdev->mode_info.rfbdev) - fb_set_suspend(rdev->mode_info.rfbdev->helper.fbdev, state); + drm_fb_helper_set_suspend(&rdev->mode_info.rfbdev->helper, state); } bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj) diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 93414ac..4552682 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -237,7 +237,8 @@ static int radeon_verify_access(struct ttm_buffer_object *bo, struct file *filp) if (radeon_ttm_tt_has_userptr(bo->ttm)) return -EPERM; - return drm_vma_node_verify_access(&rbo->gem_base.vma_node, filp); + return drm_vma_node_verify_access(&rbo->gem_base.vma_node, + filp->private_data); } static void radeon_move_null(struct ttm_buffer_object *bo, @@ -263,8 +264,8 @@ static int radeon_move_blit(struct ttm_buffer_object *bo, rdev = radeon_get_rdev(bo->bdev); ridx = radeon_copy_ring_index(rdev); - old_start = old_mem->start << PAGE_SHIFT; - new_start = new_mem->start << PAGE_SHIFT; + old_start = (u64)old_mem->start << PAGE_SHIFT; + new_start = (u64)new_mem->start << PAGE_SHIFT; switch (old_mem->mem_type) { case TTM_PL_VRAM: diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 899ef7a..73c971e 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -316,8 +316,8 @@ static int rcar_du_probe(struct platform_device *pdev) rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data; ddev = drm_dev_alloc(&rcar_du_driver, &pdev->dev); - if (!ddev) - return -ENOMEM; + if (IS_ERR(ddev)) + return PTR_ERR(ddev); rcdu->ddev = ddev; ddev->dev_private = rcdu; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index f03eb55..bd9c3bb 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -257,7 +257,8 @@ static void rcar_du_atomic_complete(struct rcar_du_commit *commit) /* Apply the atomic update. */ drm_atomic_helper_commit_modeset_disables(dev, old_state); drm_atomic_helper_commit_modeset_enables(dev, old_state); - drm_atomic_helper_commit_planes(dev, old_state, true); + drm_atomic_helper_commit_planes(dev, old_state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); drm_atomic_helper_wait_for_vblanks(dev, old_state); diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index 05d0713..9746365 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -3,7 +3,7 @@ # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \ - rockchip_drm_gem.o rockchip_drm_vop.o + rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index 89aadbf..e83be15 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -32,6 +32,7 @@ #include <drm/bridge/analogix_dp.h> #include "rockchip_drm_drv.h" +#include "rockchip_drm_psr.h" #include "rockchip_drm_vop.h" #define RK3288_GRF_SOC_CON6 0x25c @@ -41,6 +42,8 @@ #define HIWORD_UPDATE(val, mask) (val | (mask) << 16) +#define PSR_WAIT_LINE_FLAG_TIMEOUT_MS 100 + #define to_dp(nm) container_of(nm, struct rockchip_dp_device, nm) /** @@ -68,11 +71,62 @@ struct rockchip_dp_device { struct regmap *grf; struct reset_control *rst; + struct work_struct psr_work; + spinlock_t psr_lock; + unsigned int psr_state; + const struct rockchip_dp_chip_data *data; struct analogix_dp_plat_data plat_data; }; +static void analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled) +{ + struct rockchip_dp_device *dp = to_dp(encoder); + unsigned long flags; + + dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit"); + + spin_lock_irqsave(&dp->psr_lock, flags); + if (enabled) + dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE; + else + dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE; + + schedule_work(&dp->psr_work); + spin_unlock_irqrestore(&dp->psr_lock, flags); +} + +static void analogix_dp_psr_work(struct work_struct *work) +{ + struct rockchip_dp_device *dp = + container_of(work, typeof(*dp), psr_work); + struct drm_crtc *crtc = dp->encoder.crtc; + int psr_state = dp->psr_state; + int vact_end; + int ret; + unsigned long flags; + + if (!crtc) + return; + + vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay; + + ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end, + PSR_WAIT_LINE_FLAG_TIMEOUT_MS); + if (ret) { + dev_err(dp->dev, "line flag interrupt did not arrive\n"); + return; + } + + spin_lock_irqsave(&dp->psr_lock, flags); + if (psr_state == EDP_VSC_PSR_STATE_ACTIVE) + analogix_dp_enable_psr(dp->dev); + else + analogix_dp_disable_psr(dp->dev); + spin_unlock_irqrestore(&dp->psr_lock, flags); +} + static int rockchip_dp_pre_init(struct rockchip_dp_device *dp) { reset_control_assert(dp->rst); @@ -87,6 +141,8 @@ static int rockchip_dp_poweron(struct analogix_dp_plat_data *plat_data) struct rockchip_dp_device *dp = to_dp(plat_data); int ret; + cancel_work_sync(&dp->psr_work); + ret = clk_prepare_enable(dp->pclk); if (ret < 0) { dev_err(dp->dev, "failed to enable pclk %d\n", ret); @@ -342,12 +398,22 @@ static int rockchip_dp_bind(struct device *dev, struct device *master, dp->plat_data.power_off = rockchip_dp_powerdown; dp->plat_data.get_modes = rockchip_dp_get_modes; + spin_lock_init(&dp->psr_lock); + dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE; + INIT_WORK(&dp->psr_work, analogix_dp_psr_work); + + rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set); + return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data); } static void rockchip_dp_unbind(struct device *dev, struct device *master, void *data) { + struct rockchip_dp_device *dp = dev_get_drvdata(dev); + + rockchip_drm_psr_unregister(&dp->encoder); + return analogix_dp_unbind(dev, master, data); } @@ -381,10 +447,8 @@ static int rockchip_dp_probe(struct platform_device *pdev) panel = of_drm_find_panel(panel_node); of_node_put(panel_node); - if (!panel) { - DRM_ERROR("failed to find panel\n"); + if (!panel) return -EPROBE_DEFER; - } } dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL); @@ -445,7 +509,6 @@ static struct platform_driver rockchip_dp_driver = { .remove = rockchip_dp_remove, .driver = { .name = "rockchip-dp", - .owner = THIS_MODULE, .pm = &rockchip_dp_pm_ops, .of_match_table = of_match_ptr(rockchip_dp_dt_ids), }, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index a822d49..446b5d7 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -143,8 +143,8 @@ static int rockchip_drm_bind(struct device *dev) int ret; drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev); - if (!drm_dev) - return -ENOMEM; + if (IS_ERR(drm_dev)) + return PTR_ERR(drm_dev); dev_set_drvdata(dev, drm_dev); @@ -156,6 +156,9 @@ static int rockchip_drm_bind(struct device *dev) drm_dev->dev_private = private; + INIT_LIST_HEAD(&private->psr_list); + spin_lock_init(&private->psr_list_lock); + drm_mode_config_init(drm_dev); rockchip_drm_mode_config_init(drm_dev); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index ea39329..fb6226c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -39,7 +39,6 @@ struct drm_connector; struct rockchip_crtc_funcs { int (*enable_vblank)(struct drm_crtc *crtc); void (*disable_vblank)(struct drm_crtc *crtc); - void (*wait_for_update)(struct drm_crtc *crtc); }; struct rockchip_crtc_state { @@ -61,6 +60,9 @@ struct rockchip_drm_private { struct drm_gem_object *fbdev_bo; const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC]; struct drm_atomic_state *state; + + struct list_head psr_list; + spinlock_t psr_list_lock; }; int rockchip_register_crtc_funcs(struct drm_crtc *crtc, @@ -70,4 +72,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev, struct device *dev); void rockchip_drm_dma_detach_device(struct drm_device *drm_dev, struct device *dev); +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num, + unsigned int mstimeout); + #endif /* _ROCKCHIP_DRM_DRV_H_ */ diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 55c5273..0f6eda0 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -22,6 +22,7 @@ #include "rockchip_drm_drv.h" #include "rockchip_drm_fb.h" #include "rockchip_drm_gem.h" +#include "rockchip_drm_psr.h" #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb) @@ -63,9 +64,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb, rockchip_fb->obj[0], handle); } +static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb, + struct drm_file *file, + unsigned int flags, unsigned int color, + struct drm_clip_rect *clips, + unsigned int num_clips) +{ + rockchip_drm_psr_flush_all(fb->dev); + return 0; +} + static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = { .destroy = rockchip_drm_fb_destroy, .create_handle = rockchip_drm_fb_create_handle, + .dirty = rockchip_drm_fb_dirty, }; static struct rockchip_drm_fb * @@ -162,68 +174,6 @@ static void rockchip_drm_output_poll_changed(struct drm_device *dev) drm_fb_helper_hotplug_event(fb_helper); } -static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc) -{ - struct rockchip_drm_private *priv = crtc->dev->dev_private; - int pipe = drm_crtc_index(crtc); - const struct rockchip_crtc_funcs *crtc_funcs = priv->crtc_funcs[pipe]; - - if (crtc_funcs && crtc_funcs->wait_for_update) - crtc_funcs->wait_for_update(crtc); -} - -/* - * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066 - * have hardware counters for neither vblanks nor scanlines, which results in - * a race where: - * | <-- HW vsync irq and reg take effect - * plane_commit --> | - * get_vblank and wait --> | - * | <-- handle_vblank, vblank->count + 1 - * cleanup_fb --> | - * iommu crash --> | - * | <-- HW vsync irq and reg take effect - * - * This function is equivalent but uses rockchip_crtc_wait_for_update() instead - * of waiting for vblank_count to change. - */ -static void -rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state) -{ - struct drm_crtc_state *old_crtc_state; - struct drm_crtc *crtc; - int i, ret; - - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { - /* No one cares about the old state, so abuse it for tracking - * and store whether we hold a vblank reference (and should do a - * vblank wait) in the ->enable boolean. - */ - old_crtc_state->enable = false; - - if (!crtc->state->active) - continue; - - if (!drm_atomic_helper_framebuffer_changed(dev, - old_state, crtc)) - continue; - - ret = drm_crtc_vblank_get(crtc); - if (ret != 0) - continue; - - old_crtc_state->enable = true; - } - - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { - if (!old_crtc_state->enable) - continue; - - rockchip_crtc_wait_for_update(crtc); - drm_crtc_vblank_put(crtc); - } -} - static void rockchip_atomic_commit_tail(struct drm_atomic_state *state) { @@ -233,11 +183,12 @@ rockchip_atomic_commit_tail(struct drm_atomic_state *state) drm_atomic_helper_commit_modeset_enables(dev, state); - drm_atomic_helper_commit_planes(dev, state, true); + drm_atomic_helper_commit_planes(dev, state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); drm_atomic_helper_commit_hw_done(state); - rockchip_atomic_wait_for_complete(dev, state); + drm_atomic_helper_wait_for_vblanks(dev, state); drm_atomic_helper_cleanup_planes(dev, state); } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c new file mode 100644 index 0000000..a553e18 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c @@ -0,0 +1,275 @@ +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Yakir Yang <ykk@rock-chips.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <drm/drmP.h> +#include <drm/drm_crtc_helper.h> + +#include "rockchip_drm_drv.h" +#include "rockchip_drm_psr.h" + +#define PSR_FLUSH_TIMEOUT msecs_to_jiffies(100) + +enum psr_state { + PSR_FLUSH, + PSR_ENABLE, + PSR_DISABLE, +}; + +struct psr_drv { + struct list_head list; + struct drm_encoder *encoder; + + spinlock_t lock; + bool active; + enum psr_state state; + + struct timer_list flush_timer; + + void (*set)(struct drm_encoder *encoder, bool enable); +}; + +static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc) +{ + struct rockchip_drm_private *drm_drv = crtc->dev->dev_private; + struct psr_drv *psr; + unsigned long flags; + + spin_lock_irqsave(&drm_drv->psr_list_lock, flags); + list_for_each_entry(psr, &drm_drv->psr_list, list) { + if (psr->encoder->crtc == crtc) + goto out; + } + psr = ERR_PTR(-ENODEV); + +out: + spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); + return psr; +} + +static void psr_set_state_locked(struct psr_drv *psr, enum psr_state state) +{ + /* + * Allowed finite state machine: + * + * PSR_ENABLE < = = = = = > PSR_FLUSH + * | ^ | + * | | | + * v | | + * PSR_DISABLE < - - - - - - - - - + */ + if (state == psr->state || !psr->active) + return; + + /* Already disabled in flush, change the state, but not the hardware */ + if (state == PSR_DISABLE && psr->state == PSR_FLUSH) { + psr->state = state; + return; + } + + psr->state = state; + + /* Actually commit the state change to hardware */ + switch (psr->state) { + case PSR_ENABLE: + psr->set(psr->encoder, true); + break; + + case PSR_DISABLE: + case PSR_FLUSH: + psr->set(psr->encoder, false); + break; + } +} + +static void psr_set_state(struct psr_drv *psr, enum psr_state state) +{ + unsigned long flags; + + spin_lock_irqsave(&psr->lock, flags); + psr_set_state_locked(psr, state); + spin_unlock_irqrestore(&psr->lock, flags); +} + +static void psr_flush_handler(unsigned long data) +{ + struct psr_drv *psr = (struct psr_drv *)data; + unsigned long flags; + + /* If the state has changed since we initiated the flush, do nothing */ + spin_lock_irqsave(&psr->lock, flags); + if (psr->state == PSR_FLUSH) + psr_set_state_locked(psr, PSR_ENABLE); + spin_unlock_irqrestore(&psr->lock, flags); +} + +/** + * rockchip_drm_psr_activate - activate PSR on the given pipe + * @crtc: CRTC to obtain the PSR encoder + * + * Returns: + * Zero on success, negative errno on failure. + */ +int rockchip_drm_psr_activate(struct drm_crtc *crtc) +{ + struct psr_drv *psr = find_psr_by_crtc(crtc); + unsigned long flags; + + if (IS_ERR(psr)) + return PTR_ERR(psr); + + spin_lock_irqsave(&psr->lock, flags); + psr->active = true; + spin_unlock_irqrestore(&psr->lock, flags); + + return 0; +} +EXPORT_SYMBOL(rockchip_drm_psr_activate); + +/** + * rockchip_drm_psr_deactivate - deactivate PSR on the given pipe + * @crtc: CRTC to obtain the PSR encoder + * + * Returns: + * Zero on success, negative errno on failure. + */ +int rockchip_drm_psr_deactivate(struct drm_crtc *crtc) +{ + struct psr_drv *psr = find_psr_by_crtc(crtc); + unsigned long flags; + + if (IS_ERR(psr)) + return PTR_ERR(psr); + + spin_lock_irqsave(&psr->lock, flags); + psr->active = false; + spin_unlock_irqrestore(&psr->lock, flags); + del_timer_sync(&psr->flush_timer); + + return 0; +} +EXPORT_SYMBOL(rockchip_drm_psr_deactivate); + +static void rockchip_drm_do_flush(struct psr_drv *psr) +{ + mod_timer(&psr->flush_timer, + round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT)); + psr_set_state(psr, PSR_FLUSH); +} + +/** + * rockchip_drm_psr_flush - flush a single pipe + * @crtc: CRTC of the pipe to flush + * + * Returns: + * 0 on success, -errno on fail + */ +int rockchip_drm_psr_flush(struct drm_crtc *crtc) +{ + struct psr_drv *psr = find_psr_by_crtc(crtc); + if (IS_ERR(psr)) + return PTR_ERR(psr); + + rockchip_drm_do_flush(psr); + return 0; +} +EXPORT_SYMBOL(rockchip_drm_psr_flush); + +/** + * rockchip_drm_psr_flush_all - force to flush all registered PSR encoders + * @dev: drm device + * + * Disable the PSR function for all registered encoders, and then enable the + * PSR function back after PSR_FLUSH_TIMEOUT. If encoder PSR state have been + * changed during flush time, then keep the state no change after flush + * timeout. + * + * Returns: + * Zero on success, negative errno on failure. + */ +void rockchip_drm_psr_flush_all(struct drm_device *dev) +{ + struct rockchip_drm_private *drm_drv = dev->dev_private; + struct psr_drv *psr; + unsigned long flags; + + spin_lock_irqsave(&drm_drv->psr_list_lock, flags); + list_for_each_entry(psr, &drm_drv->psr_list, list) + rockchip_drm_do_flush(psr); + spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); +} +EXPORT_SYMBOL(rockchip_drm_psr_flush_all); + +/** + * rockchip_drm_psr_register - register encoder to psr driver + * @encoder: encoder that obtain the PSR function + * @psr_set: call back to set PSR state + * + * Returns: + * Zero on success, negative errno on failure. + */ +int rockchip_drm_psr_register(struct drm_encoder *encoder, + void (*psr_set)(struct drm_encoder *, bool enable)) +{ + struct rockchip_drm_private *drm_drv = encoder->dev->dev_private; + struct psr_drv *psr; + unsigned long flags; + + if (!encoder || !psr_set) + return -EINVAL; + + psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL); + if (!psr) + return -ENOMEM; + + setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr); + spin_lock_init(&psr->lock); + + psr->active = true; + psr->state = PSR_DISABLE; + psr->encoder = encoder; + psr->set = psr_set; + + spin_lock_irqsave(&drm_drv->psr_list_lock, flags); + list_add_tail(&psr->list, &drm_drv->psr_list); + spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); + + return 0; +} +EXPORT_SYMBOL(rockchip_drm_psr_register); + +/** + * rockchip_drm_psr_unregister - unregister encoder to psr driver + * @encoder: encoder that obtain the PSR function + * @psr_set: call back to set PSR state + * + * Returns: + * Zero on success, negative errno on failure. + */ +void rockchip_drm_psr_unregister(struct drm_encoder *encoder) +{ + struct rockchip_drm_private *drm_drv = encoder->dev->dev_private; + struct psr_drv *psr, *n; + unsigned long flags; + + spin_lock_irqsave(&drm_drv->psr_list_lock, flags); + list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) { + if (psr->encoder == encoder) { + del_timer(&psr->flush_timer); + list_del(&psr->list); + kfree(psr); + } + } + spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); +} +EXPORT_SYMBOL(rockchip_drm_psr_unregister); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h new file mode 100644 index 0000000..b420cf1 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Yakir Yang <ykk@rock-chips.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __ROCKCHIP_DRM_PSR___ +#define __ROCKCHIP_DRM_PSR___ + +void rockchip_drm_psr_flush_all(struct drm_device *dev); +int rockchip_drm_psr_flush(struct drm_crtc *crtc); + +int rockchip_drm_psr_activate(struct drm_crtc *crtc); +int rockchip_drm_psr_deactivate(struct drm_crtc *crtc); + +int rockchip_drm_psr_register(struct drm_encoder *encoder, + void (*psr_set)(struct drm_encoder *, bool enable)); +void rockchip_drm_psr_unregister(struct drm_encoder *encoder); + +#endif /* __ROCKCHIP_DRM_PSR__ */ diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 91305eb..c7eba30 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -17,12 +17,14 @@ #include <drm/drm_atomic.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_flip_work.h> #include <drm/drm_plane_helper.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/iopoll.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/pm_runtime.h> @@ -34,17 +36,21 @@ #include "rockchip_drm_drv.h" #include "rockchip_drm_gem.h" #include "rockchip_drm_fb.h" +#include "rockchip_drm_psr.h" #include "rockchip_drm_vop.h" -#define __REG_SET_RELAXED(x, off, mask, shift, v) \ - vop_mask_write_relaxed(x, off, (mask) << shift, (v) << shift) -#define __REG_SET_NORMAL(x, off, mask, shift, v) \ - vop_mask_write(x, off, (mask) << shift, (v) << shift) +#define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \ + vop_mask_write(x, off, mask, shift, v, write_mask, true) + +#define __REG_SET_NORMAL(x, off, mask, shift, v, write_mask) \ + vop_mask_write(x, off, mask, shift, v, write_mask, false) #define REG_SET(x, base, reg, v, mode) \ - __REG_SET_##mode(x, base + reg.offset, reg.mask, reg.shift, v) + __REG_SET_##mode(x, base + reg.offset, \ + reg.mask, reg.shift, v, reg.write_mask) #define REG_SET_MASK(x, base, reg, mask, v, mode) \ - __REG_SET_##mode(x, base + reg.offset, mask, reg.shift, v) + __REG_SET_##mode(x, base + reg.offset, \ + mask, reg.shift, v, reg.write_mask) #define VOP_WIN_SET(x, win, name, v) \ REG_SET(x, win->base, win->phy->name, v, RELAXED) @@ -82,25 +88,15 @@ #define to_vop(x) container_of(x, struct vop, crtc) #define to_vop_win(x) container_of(x, struct vop_win, base) -#define to_vop_plane_state(x) container_of(x, struct vop_plane_state, base) -struct vop_plane_state { - struct drm_plane_state base; - int format; - struct drm_rect src; - struct drm_rect dest; - dma_addr_t yrgb_mst; - bool enable; +enum vop_pending { + VOP_PENDING_FB_UNREF, }; struct vop_win { struct drm_plane base; const struct vop_win_data *data; struct vop *vop; - - /* protected by dev->event_lock */ - bool enable; - dma_addr_t yrgb_mst; }; struct vop { @@ -113,11 +109,15 @@ struct vop { struct mutex vsync_mutex; bool vsync_work_pending; struct completion dsp_hold_completion; - struct completion wait_update_complete; /* protected by dev->event_lock */ struct drm_pending_vblank_event *event; + struct drm_flip_work fb_unref_work; + unsigned long pending; + + struct completion line_flag_completion; + const struct vop_data *data; uint32_t *regsbak; @@ -164,27 +164,25 @@ static inline uint32_t vop_read_reg(struct vop *vop, uint32_t base, } static inline void vop_mask_write(struct vop *vop, uint32_t offset, - uint32_t mask, uint32_t v) + uint32_t mask, uint32_t shift, uint32_t v, + bool write_mask, bool relaxed) { - if (mask) { - uint32_t cached_val = vop->regsbak[offset >> 2]; - - cached_val = (cached_val & ~mask) | v; - writel(cached_val, vop->regs + offset); - vop->regsbak[offset >> 2] = cached_val; - } -} + if (!mask) + return; -static inline void vop_mask_write_relaxed(struct vop *vop, uint32_t offset, - uint32_t mask, uint32_t v) -{ - if (mask) { + if (write_mask) { + v = ((v << shift) & 0xffff) | (mask << (shift + 16)); + } else { uint32_t cached_val = vop->regsbak[offset >> 2]; - cached_val = (cached_val & ~mask) | v; - writel_relaxed(cached_val, vop->regs + offset); - vop->regsbak[offset >> 2] = cached_val; + v = (cached_val & ~(mask << shift)) | ((v & mask) << shift); + vop->regsbak[offset >> 2] = v; } + + if (relaxed) + writel_relaxed(v, vop->regs + offset); + else + writel(v, vop->regs + offset); } static inline uint32_t vop_get_intr_type(struct vop *vop, @@ -240,7 +238,7 @@ static enum vop_data_format vop_convert_format(uint32_t format) case DRM_FORMAT_NV24: return VOP_FMT_YUV444SP; default: - DRM_ERROR("unsupport format[%08x]\n", format); + DRM_ERROR("unsupported format[%08x]\n", format); return -EINVAL; } } @@ -317,7 +315,7 @@ static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win, int vskiplines = 0; if (dst_w > 3840) { - DRM_ERROR("Maximum destination width (3840) exceeded\n"); + DRM_DEV_ERROR(vop->dev, "Maximum dst width (3840) exceeded\n"); return; } @@ -355,11 +353,11 @@ static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win, VOP_SCL_SET_EXT(vop, win, lb_mode, lb_mode); if (lb_mode == LB_RGB_3840X2) { if (yrgb_ver_scl_mode != SCALE_NONE) { - DRM_ERROR("ERROR : not allow yrgb ver scale\n"); + DRM_DEV_ERROR(vop->dev, "not allow yrgb ver scale\n"); return; } if (cbcr_ver_scl_mode != SCALE_NONE) { - DRM_ERROR("ERROR : not allow cbcr ver scale\n"); + DRM_DEV_ERROR(vop->dev, "not allow cbcr ver scale\n"); return; } vsu_mode = SCALE_UP_BIL; @@ -411,6 +409,7 @@ static void vop_dsp_hold_valid_irq_enable(struct vop *vop) spin_lock_irqsave(&vop->irq_lock, flags); + VOP_INTR_SET_TYPE(vop, clear, DSP_HOLD_VALID_INTR, 1); VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 1); spin_unlock_irqrestore(&vop->irq_lock, flags); @@ -430,7 +429,73 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop) spin_unlock_irqrestore(&vop->irq_lock, flags); } -static void vop_enable(struct drm_crtc *crtc) +/* + * (1) each frame starts at the start of the Vsync pulse which is signaled by + * the "FRAME_SYNC" interrupt. + * (2) the active data region of each frame ends at dsp_vact_end + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num, + * to get "LINE_FLAG" interrupt at the end of the active on screen data. + * + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end + * Interrupts + * LINE_FLAG -------------------------------+ + * FRAME_SYNC ----+ | + * | | + * v v + * | Vsync | Vbp | Vactive | Vfp | + * ^ ^ ^ ^ + * | | | | + * | | | | + * dsp_vs_end ------------+ | | | VOP_DSP_VTOTAL_VS_END + * dsp_vact_start --------------+ | | VOP_DSP_VACT_ST_END + * dsp_vact_end ----------------------------+ | VOP_DSP_VACT_ST_END + * dsp_total -------------------------------------+ VOP_DSP_VTOTAL_VS_END + */ +static bool vop_line_flag_irq_is_enabled(struct vop *vop) +{ + uint32_t line_flag_irq; + unsigned long flags; + + spin_lock_irqsave(&vop->irq_lock, flags); + + line_flag_irq = VOP_INTR_GET_TYPE(vop, enable, LINE_FLAG_INTR); + + spin_unlock_irqrestore(&vop->irq_lock, flags); + + return !!line_flag_irq; +} + +static void vop_line_flag_irq_enable(struct vop *vop, int line_num) +{ + unsigned long flags; + + if (WARN_ON(!vop->is_enabled)) + return; + + spin_lock_irqsave(&vop->irq_lock, flags); + + VOP_CTRL_SET(vop, line_flag_num[0], line_num); + VOP_INTR_SET_TYPE(vop, clear, LINE_FLAG_INTR, 1); + VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1); + + spin_unlock_irqrestore(&vop->irq_lock, flags); +} + +static void vop_line_flag_irq_disable(struct vop *vop) +{ + unsigned long flags; + + if (WARN_ON(!vop->is_enabled)) + return; + + spin_lock_irqsave(&vop->irq_lock, flags); + + VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0); + + spin_unlock_irqrestore(&vop->irq_lock, flags); +} + +static int vop_enable(struct drm_crtc *crtc) { struct vop *vop = to_vop(crtc); int ret; @@ -438,26 +503,20 @@ static void vop_enable(struct drm_crtc *crtc) ret = pm_runtime_get_sync(vop->dev); if (ret < 0) { dev_err(vop->dev, "failed to get pm runtime: %d\n", ret); - return; + goto err_put_pm_runtime; } ret = clk_enable(vop->hclk); - if (ret < 0) { - dev_err(vop->dev, "failed to enable hclk - %d\n", ret); - return; - } + if (WARN_ON(ret < 0)) + goto err_put_pm_runtime; ret = clk_enable(vop->dclk); - if (ret < 0) { - dev_err(vop->dev, "failed to enable dclk - %d\n", ret); + if (WARN_ON(ret < 0)) goto err_disable_hclk; - } ret = clk_enable(vop->aclk); - if (ret < 0) { - dev_err(vop->dev, "failed to enable aclk - %d\n", ret); + if (WARN_ON(ret < 0)) goto err_disable_dclk; - } /* * Slave iommu shares power, irq and clock with vop. It was associated @@ -487,7 +546,7 @@ static void vop_enable(struct drm_crtc *crtc) drm_crtc_vblank_on(crtc); - return; + return 0; err_disable_aclk: clk_disable(vop->aclk); @@ -495,6 +554,9 @@ err_disable_dclk: clk_disable(vop->dclk); err_disable_hclk: clk_disable(vop->hclk); +err_put_pm_runtime: + pm_runtime_put_sync(vop->dev); + return ret; } static void vop_crtc_disable(struct drm_crtc *crtc) @@ -504,6 +566,8 @@ static void vop_crtc_disable(struct drm_crtc *crtc) WARN_ON(vop->event); + rockchip_drm_psr_deactivate(&vop->crtc); + /* * We need to make sure that all windows are disabled before we * disable that crtc. Otherwise we might try to scan from a destroyed @@ -568,22 +632,6 @@ static void vop_plane_destroy(struct drm_plane *plane) drm_plane_cleanup(plane); } -static int vop_plane_prepare_fb(struct drm_plane *plane, - const struct drm_plane_state *new_state) -{ - if (plane->state->fb) - drm_framebuffer_reference(plane->state->fb); - - return 0; -} - -static void vop_plane_cleanup_fb(struct drm_plane *plane, - const struct drm_plane_state *old_state) -{ - if (old_state->fb) - drm_framebuffer_unreference(old_state->fb); -} - static int vop_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) { @@ -591,12 +639,8 @@ static int vop_plane_atomic_check(struct drm_plane *plane, struct drm_crtc_state *crtc_state; struct drm_framebuffer *fb = state->fb; struct vop_win *vop_win = to_vop_win(plane); - struct vop_plane_state *vop_plane_state = to_vop_plane_state(state); const struct vop_win_data *win = vop_win->data; - bool visible; int ret; - struct drm_rect *dest = &vop_plane_state->dest; - struct drm_rect *src = &vop_plane_state->src; struct drm_rect clip; int min_scale = win->phy->scl ? FRAC_16_16(1, 8) : DRM_PLANE_HELPER_NO_SCALING; @@ -604,62 +648,43 @@ static int vop_plane_atomic_check(struct drm_plane *plane, DRM_PLANE_HELPER_NO_SCALING; if (!crtc || !fb) - goto out_disable; + return 0; crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); if (WARN_ON(!crtc_state)) return -EINVAL; - src->x1 = state->src_x; - src->y1 = state->src_y; - src->x2 = state->src_x + state->src_w; - src->y2 = state->src_y + state->src_h; - dest->x1 = state->crtc_x; - dest->y1 = state->crtc_y; - dest->x2 = state->crtc_x + state->crtc_w; - dest->y2 = state->crtc_y + state->crtc_h; - clip.x1 = 0; clip.y1 = 0; clip.x2 = crtc_state->adjusted_mode.hdisplay; clip.y2 = crtc_state->adjusted_mode.vdisplay; - ret = drm_plane_helper_check_update(plane, crtc, state->fb, - src, dest, &clip, - state->rotation, - min_scale, - max_scale, - true, true, &visible); + ret = drm_plane_helper_check_state(state, &clip, + min_scale, max_scale, + true, true); if (ret) return ret; - if (!visible) - goto out_disable; + if (!state->visible) + return 0; - vop_plane_state->format = vop_convert_format(fb->pixel_format); - if (vop_plane_state->format < 0) - return vop_plane_state->format; + ret = vop_convert_format(fb->pixel_format); + if (ret < 0) + return ret; /* * Src.x1 can be odd when do clip, but yuv plane start point * need align with 2 pixel. */ - if (is_yuv_support(fb->pixel_format) && ((src->x1 >> 16) % 2)) + if (is_yuv_support(fb->pixel_format) && ((state->src.x1 >> 16) % 2)) return -EINVAL; - vop_plane_state->enable = true; - - return 0; - -out_disable: - vop_plane_state->enable = false; return 0; } static void vop_plane_atomic_disable(struct drm_plane *plane, struct drm_plane_state *old_state) { - struct vop_plane_state *vop_plane_state = to_vop_plane_state(old_state); struct vop_win *vop_win = to_vop_win(plane); const struct vop_win_data *win = vop_win->data; struct vop *vop = to_vop(old_state->crtc); @@ -667,18 +692,11 @@ static void vop_plane_atomic_disable(struct drm_plane *plane, if (!old_state->crtc) return; - spin_lock_irq(&plane->dev->event_lock); - vop_win->enable = false; - vop_win->yrgb_mst = 0; - spin_unlock_irq(&plane->dev->event_lock); - spin_lock(&vop->reg_lock); VOP_WIN_SET(vop, win, enable, 0); spin_unlock(&vop->reg_lock); - - vop_plane_state->enable = false; } static void vop_plane_atomic_update(struct drm_plane *plane, @@ -687,21 +705,21 @@ static void vop_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *state = plane->state; struct drm_crtc *crtc = state->crtc; struct vop_win *vop_win = to_vop_win(plane); - struct vop_plane_state *vop_plane_state = to_vop_plane_state(state); const struct vop_win_data *win = vop_win->data; struct vop *vop = to_vop(state->crtc); struct drm_framebuffer *fb = state->fb; unsigned int actual_w, actual_h; unsigned int dsp_stx, dsp_sty; uint32_t act_info, dsp_info, dsp_st; - struct drm_rect *src = &vop_plane_state->src; - struct drm_rect *dest = &vop_plane_state->dest; + struct drm_rect *src = &state->src; + struct drm_rect *dest = &state->dst; struct drm_gem_object *obj, *uv_obj; struct rockchip_gem_object *rk_obj, *rk_uv_obj; unsigned long offset; dma_addr_t dma_addr; uint32_t val; bool rb_swap; + int format; /* * can't update plane when vop is disabled. @@ -712,7 +730,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, if (WARN_ON(!vop->is_enabled)) return; - if (!vop_plane_state->enable) { + if (!state->visible) { vop_plane_atomic_disable(plane, old_state); return; } @@ -733,18 +751,15 @@ static void vop_plane_atomic_update(struct drm_plane *plane, offset = (src->x1 >> 16) * drm_format_plane_cpp(fb->pixel_format, 0); offset += (src->y1 >> 16) * fb->pitches[0]; - vop_plane_state->yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0]; + dma_addr = rk_obj->dma_addr + offset + fb->offsets[0]; - spin_lock_irq(&plane->dev->event_lock); - vop_win->enable = true; - vop_win->yrgb_mst = vop_plane_state->yrgb_mst; - spin_unlock_irq(&plane->dev->event_lock); + format = vop_convert_format(fb->pixel_format); spin_lock(&vop->reg_lock); - VOP_WIN_SET(vop, win, format, vop_plane_state->format); + VOP_WIN_SET(vop, win, format, format); VOP_WIN_SET(vop, win, yrgb_vir, fb->pitches[0] >> 2); - VOP_WIN_SET(vop, win, yrgb_mst, vop_plane_state->yrgb_mst); + VOP_WIN_SET(vop, win, yrgb_mst, dma_addr); if (is_yuv_support(fb->pixel_format)) { int hsub = drm_format_horz_chroma_subsampling(fb->pixel_format); int vsub = drm_format_vert_chroma_subsampling(fb->pixel_format); @@ -791,68 +806,18 @@ static void vop_plane_atomic_update(struct drm_plane *plane, } static const struct drm_plane_helper_funcs plane_helper_funcs = { - .prepare_fb = vop_plane_prepare_fb, - .cleanup_fb = vop_plane_cleanup_fb, .atomic_check = vop_plane_atomic_check, .atomic_update = vop_plane_atomic_update, .atomic_disable = vop_plane_atomic_disable, }; -static void vop_atomic_plane_reset(struct drm_plane *plane) -{ - struct vop_plane_state *vop_plane_state = - to_vop_plane_state(plane->state); - - if (plane->state && plane->state->fb) - drm_framebuffer_unreference(plane->state->fb); - - kfree(vop_plane_state); - vop_plane_state = kzalloc(sizeof(*vop_plane_state), GFP_KERNEL); - if (!vop_plane_state) - return; - - plane->state = &vop_plane_state->base; - plane->state->plane = plane; -} - -static struct drm_plane_state * -vop_atomic_plane_duplicate_state(struct drm_plane *plane) -{ - struct vop_plane_state *old_vop_plane_state; - struct vop_plane_state *vop_plane_state; - - if (WARN_ON(!plane->state)) - return NULL; - - old_vop_plane_state = to_vop_plane_state(plane->state); - vop_plane_state = kmemdup(old_vop_plane_state, - sizeof(*vop_plane_state), GFP_KERNEL); - if (!vop_plane_state) - return NULL; - - __drm_atomic_helper_plane_duplicate_state(plane, - &vop_plane_state->base); - - return &vop_plane_state->base; -} - -static void vop_atomic_plane_destroy_state(struct drm_plane *plane, - struct drm_plane_state *state) -{ - struct vop_plane_state *vop_state = to_vop_plane_state(state); - - __drm_atomic_helper_plane_destroy_state(state); - - kfree(vop_state); -} - static const struct drm_plane_funcs vop_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = vop_plane_destroy, - .reset = vop_atomic_plane_reset, - .atomic_duplicate_state = vop_atomic_plane_duplicate_state, - .atomic_destroy_state = vop_atomic_plane_destroy_state, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, }; static int vop_crtc_enable_vblank(struct drm_crtc *crtc) @@ -865,6 +830,7 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc) spin_lock_irqsave(&vop->irq_lock, flags); + VOP_INTR_SET_TYPE(vop, clear, FS_INTR, 1); VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 1); spin_unlock_irqrestore(&vop->irq_lock, flags); @@ -887,18 +853,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc) spin_unlock_irqrestore(&vop->irq_lock, flags); } -static void vop_crtc_wait_for_update(struct drm_crtc *crtc) -{ - struct vop *vop = to_vop(crtc); - - reinit_completion(&vop->wait_update_complete); - WARN_ON(!wait_for_completion_timeout(&vop->wait_update_complete, 100)); -} - static const struct rockchip_crtc_funcs private_crtc_funcs = { .enable_vblank = vop_crtc_enable_vblank, .disable_vblank = vop_crtc_disable_vblank, - .wait_for_update = vop_crtc_wait_for_update, }; static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, @@ -928,11 +885,17 @@ static void vop_crtc_enable(struct drm_crtc *crtc) u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start; u16 vact_st = adjusted_mode->vtotal - adjusted_mode->vsync_start; u16 vact_end = vact_st + vdisplay; - uint32_t val; + uint32_t pin_pol, val; + int ret; WARN_ON(vop->event); - vop_enable(crtc); + ret = vop_enable(crtc); + if (ret) { + DRM_DEV_ERROR(vop->dev, "Failed to enable vop (%d)\n", ret); + return; + } + /* * If dclk rate is zero, mean that scanout is stop, * we don't need wait any more. @@ -969,25 +932,31 @@ static void vop_crtc_enable(struct drm_crtc *crtc) vop_dsp_hold_valid_irq_disable(vop); } - val = 0x8; - val |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1; - val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1); - VOP_CTRL_SET(vop, pin_pol, val); + pin_pol = 0x8; + pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1; + pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1); + VOP_CTRL_SET(vop, pin_pol, pin_pol); + switch (s->output_type) { case DRM_MODE_CONNECTOR_LVDS: VOP_CTRL_SET(vop, rgb_en, 1); + VOP_CTRL_SET(vop, rgb_pin_pol, pin_pol); break; case DRM_MODE_CONNECTOR_eDP: + VOP_CTRL_SET(vop, edp_pin_pol, pin_pol); VOP_CTRL_SET(vop, edp_en, 1); break; case DRM_MODE_CONNECTOR_HDMIA: + VOP_CTRL_SET(vop, hdmi_pin_pol, pin_pol); VOP_CTRL_SET(vop, hdmi_en, 1); break; case DRM_MODE_CONNECTOR_DSI: + VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol); VOP_CTRL_SET(vop, mipi_en, 1); break; default: - DRM_ERROR("unsupport connector_type[%d]\n", s->output_type); + DRM_DEV_ERROR(vop->dev, "unsupported connector_type [%d]\n", + s->output_type); } VOP_CTRL_SET(vop, out_mode, s->output_mode); @@ -1006,12 +975,44 @@ static void vop_crtc_enable(struct drm_crtc *crtc) clk_set_rate(vop->dclk, adjusted_mode->clock * 1000); VOP_CTRL_SET(vop, standby, 0); + + rockchip_drm_psr_activate(&vop->crtc); +} + +static bool vop_fs_irq_is_pending(struct vop *vop) +{ + return VOP_INTR_GET_TYPE(vop, status, FS_INTR); +} + +static void vop_wait_for_irq_handler(struct vop *vop) +{ + bool pending; + int ret; + + /* + * Spin until frame start interrupt status bit goes low, which means + * that interrupt handler was invoked and cleared it. The timeout of + * 10 msecs is really too long, but it is just a safety measure if + * something goes really wrong. The wait will only happen in the very + * unlikely case of a vblank happening exactly at the same time and + * shouldn't exceed microseconds range. + */ + ret = readx_poll_timeout_atomic(vop_fs_irq_is_pending, vop, pending, + !pending, 0, 10 * 1000); + if (ret) + DRM_DEV_ERROR(vop->dev, "VOP vblank IRQ stuck for 10 ms\n"); + + synchronize_irq(vop->irq); } static void vop_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { + struct drm_atomic_state *old_state = old_crtc_state->state; + struct drm_plane_state *old_plane_state; struct vop *vop = to_vop(crtc); + struct drm_plane *plane; + int i; if (WARN_ON(!vop->is_enabled)) return; @@ -1021,12 +1022,13 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc, vop_cfg_done(vop); spin_unlock(&vop->reg_lock); -} -static void vop_crtc_atomic_begin(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) -{ - struct vop *vop = to_vop(crtc); + /* + * There is a (rather unlikely) possiblity that a vblank interrupt + * fired before we set the cfg_done bit. To avoid spuriously + * signalling flip completion we need to wait for it to finish. + */ + vop_wait_for_irq_handler(vop); spin_lock_irq(&crtc->dev->event_lock); if (crtc->state->event) { @@ -1037,6 +1039,25 @@ static void vop_crtc_atomic_begin(struct drm_crtc *crtc, crtc->state->event = NULL; } spin_unlock_irq(&crtc->dev->event_lock); + + for_each_plane_in_state(old_state, plane, old_plane_state, i) { + if (!old_plane_state->fb) + continue; + + if (old_plane_state->fb == plane->state->fb) + continue; + + drm_framebuffer_reference(old_plane_state->fb); + drm_flip_work_queue(&vop->fb_unref_work, old_plane_state->fb); + set_bit(VOP_PENDING_FB_UNREF, &vop->pending); + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + } +} + +static void vop_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + rockchip_drm_psr_flush(crtc); } static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = { @@ -1093,16 +1114,13 @@ static const struct drm_crtc_funcs vop_crtc_funcs = { .atomic_destroy_state = vop_crtc_destroy_state, }; -static bool vop_win_pending_is_complete(struct vop_win *vop_win) +static void vop_fb_unref_worker(struct drm_flip_work *work, void *val) { - dma_addr_t yrgb_mst; - - if (!vop_win->enable) - return VOP_WIN_GET(vop_win->vop, vop_win->data, enable) == 0; - - yrgb_mst = VOP_WIN_GET_YRGBADDR(vop_win->vop, vop_win->data); + struct vop *vop = container_of(work, struct vop, fb_unref_work); + struct drm_framebuffer *fb = val; - return yrgb_mst == vop_win->yrgb_mst; + drm_crtc_vblank_put(&vop->crtc); + drm_framebuffer_unreference(fb); } static void vop_handle_vblank(struct vop *vop) @@ -1110,25 +1128,17 @@ static void vop_handle_vblank(struct vop *vop) struct drm_device *drm = vop->drm_dev; struct drm_crtc *crtc = &vop->crtc; unsigned long flags; - int i; - - for (i = 0; i < vop->data->win_size; i++) { - if (!vop_win_pending_is_complete(&vop->win[i])) - return; - } spin_lock_irqsave(&drm->event_lock, flags); if (vop->event) { - drm_crtc_send_vblank_event(crtc, vop->event); drm_crtc_vblank_put(crtc); vop->event = NULL; - } spin_unlock_irqrestore(&drm->event_lock, flags); - if (!completion_done(&vop->wait_update_complete)) - complete(&vop->wait_update_complete); + if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vop->pending)) + drm_flip_work_commit(&vop->fb_unref_work, system_unbound_wq); } static irqreturn_t vop_isr(int irq, void *data) @@ -1162,6 +1172,12 @@ static irqreturn_t vop_isr(int irq, void *data) ret = IRQ_HANDLED; } + if (active_irqs & LINE_FLAG_INTR) { + complete(&vop->line_flag_completion); + active_irqs &= ~LINE_FLAG_INTR; + ret = IRQ_HANDLED; + } + if (active_irqs & FS_INTR) { drm_crtc_handle_vblank(crtc); vop_handle_vblank(vop); @@ -1171,7 +1187,8 @@ static irqreturn_t vop_isr(int irq, void *data) /* Unhandled irqs are spurious. */ if (active_irqs) - DRM_ERROR("Unknown VOP IRQs: %#02x\n", active_irqs); + DRM_DEV_ERROR(vop->dev, "Unknown VOP IRQs: %#02x\n", + active_irqs); return ret; } @@ -1206,7 +1223,8 @@ static int vop_create_crtc(struct vop *vop) win_data->phy->nformats, win_data->type, NULL); if (ret) { - DRM_ERROR("failed to initialize plane\n"); + DRM_DEV_ERROR(vop->dev, "failed to init plane %d\n", + ret); goto err_cleanup_planes; } @@ -1244,7 +1262,8 @@ static int vop_create_crtc(struct vop *vop) win_data->phy->nformats, win_data->type, NULL); if (ret) { - DRM_ERROR("failed to initialize overlay plane\n"); + DRM_DEV_ERROR(vop->dev, "failed to init overlay %d\n", + ret); goto err_cleanup_crtc; } drm_plane_helper_add(&vop_win->base, &plane_helper_funcs); @@ -1252,14 +1271,17 @@ static int vop_create_crtc(struct vop *vop) port = of_get_child_by_name(dev->of_node, "port"); if (!port) { - DRM_ERROR("no port node found in %s\n", - dev->of_node->full_name); + DRM_DEV_ERROR(vop->dev, "no port node found in %s\n", + dev->of_node->full_name); ret = -ENOENT; goto err_cleanup_crtc; } + drm_flip_work_init(&vop->fb_unref_work, "fb_unref", + vop_fb_unref_worker); + init_completion(&vop->dsp_hold_completion); - init_completion(&vop->wait_update_complete); + init_completion(&vop->line_flag_completion); crtc->port = port; rockchip_register_crtc_funcs(crtc, &private_crtc_funcs); @@ -1300,6 +1322,7 @@ static void vop_destroy_crtc(struct vop *vop) * references the CRTC. */ drm_crtc_cleanup(crtc); + drm_flip_work_cleanup(&vop->fb_unref_work); } static int vop_initial(struct vop *vop) @@ -1416,6 +1439,49 @@ static void vop_win_init(struct vop *vop) } } +/** + * rockchip_drm_wait_line_flag - acqiure the give line flag event + * @crtc: CRTC to enable line flag + * @line_num: interested line number + * @mstimeout: millisecond for timeout + * + * Driver would hold here until the interested line flag interrupt have + * happened or timeout to wait. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num, + unsigned int mstimeout) +{ + struct vop *vop = to_vop(crtc); + unsigned long jiffies_left; + + if (!crtc || !vop->is_enabled) + return -ENODEV; + + if (line_num > crtc->mode.vtotal || mstimeout <= 0) + return -EINVAL; + + if (vop_line_flag_irq_is_enabled(vop)) + return -EBUSY; + + reinit_completion(&vop->line_flag_completion); + vop_line_flag_irq_enable(vop, line_num); + + jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion, + msecs_to_jiffies(mstimeout)); + vop_line_flag_irq_disable(vop); + + if (jiffies_left == 0) { + dev_err(vop->dev, "Timeout waiting for IRQ\n"); + return -ETIMEDOUT; + } + + return 0; +} +EXPORT_SYMBOL(rockchip_drm_wait_line_flag); + static int vop_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); @@ -1481,10 +1547,15 @@ static int vop_bind(struct device *dev, struct device *master, void *data) ret = vop_create_crtc(vop); if (ret) - return ret; + goto err_enable_irq; pm_runtime_enable(&pdev->dev); + return 0; + +err_enable_irq: + enable_irq(vop->irq); /* To balance out the disable_irq above */ + return ret; } static void vop_unbind(struct device *dev, struct device *master, void *data) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index 071ff0b..1dbc526 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -33,6 +33,7 @@ struct vop_reg { uint32_t offset; uint32_t shift; uint32_t mask; + bool write_mask; }; struct vop_ctrl { @@ -48,6 +49,10 @@ struct vop_ctrl { struct vop_reg dither_down; struct vop_reg dither_up; struct vop_reg pin_pol; + struct vop_reg rgb_pin_pol; + struct vop_reg hdmi_pin_pol; + struct vop_reg edp_pin_pol; + struct vop_reg mipi_pin_pol; struct vop_reg htotal_pw; struct vop_reg hact_st_end; @@ -56,6 +61,8 @@ struct vop_ctrl { struct vop_reg hpost_st_end; struct vop_reg vpost_st_end; + struct vop_reg line_flag_num[2]; + struct vop_reg cfg_done; }; diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index 919992c..35c51f3 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -23,7 +23,14 @@ #define VOP_REG(off, _mask, s) \ {.offset = off, \ .mask = _mask, \ - .shift = s,} + .shift = s, \ + .write_mask = false,} + +#define VOP_REG_MASK(off, _mask, s) \ + {.offset = off, \ + .mask = _mask, \ + .shift = s, \ + .write_mask = true,} static const uint32_t formats_win_full[] = { DRM_FORMAT_XRGB8888, @@ -50,6 +57,89 @@ static const uint32_t formats_win_lite[] = { DRM_FORMAT_BGR565, }; +static const struct vop_scl_regs rk3036_win_scl = { + .scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), + .scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), + .scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), + .scale_cbcr_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 16), +}; + +static const struct vop_win_phy rk3036_win0_data = { + .scl = &rk3036_win_scl, + .data_formats = formats_win_full, + .nformats = ARRAY_SIZE(formats_win_full), + .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0), + .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 3), + .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 15), + .act_info = VOP_REG(RK3036_WIN0_ACT_INFO, 0x1fff1fff, 0), + .dsp_info = VOP_REG(RK3036_WIN0_DSP_INFO, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3036_WIN0_DSP_ST, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3036_WIN0_YRGB_MST, 0xffffffff, 0), + .uv_mst = VOP_REG(RK3036_WIN0_CBR_MST, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3036_WIN0_VIR, 0xffff, 0), + .uv_vir = VOP_REG(RK3036_WIN0_VIR, 0x1fff, 16), +}; + +static const struct vop_win_phy rk3036_win1_data = { + .data_formats = formats_win_lite, + .nformats = ARRAY_SIZE(formats_win_lite), + .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1), + .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6), + .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19), + .act_info = VOP_REG(RK3036_WIN1_ACT_INFO, 0x1fff1fff, 0), + .dsp_info = VOP_REG(RK3036_WIN1_DSP_INFO, 0x0fff0fff, 0), + .dsp_st = VOP_REG(RK3036_WIN1_DSP_ST, 0x1fff1fff, 0), + .yrgb_mst = VOP_REG(RK3036_WIN1_MST, 0xffffffff, 0), + .yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0), +}; + +static const struct vop_win_data rk3036_vop_win_data[] = { + { .base = 0x00, .phy = &rk3036_win0_data, + .type = DRM_PLANE_TYPE_PRIMARY }, + { .base = 0x00, .phy = &rk3036_win1_data, + .type = DRM_PLANE_TYPE_CURSOR }, +}; + +static const int rk3036_vop_intrs[] = { + DSP_HOLD_VALID_INTR, + FS_INTR, + LINE_FLAG_INTR, + BUS_ERROR_INTR, +}; + +static const struct vop_intr rk3036_intr = { + .intrs = rk3036_vop_intrs, + .nintrs = ARRAY_SIZE(rk3036_vop_intrs), + .status = VOP_REG(RK3036_INT_STATUS, 0xf, 0), + .enable = VOP_REG(RK3036_INT_STATUS, 0xf, 4), + .clear = VOP_REG(RK3036_INT_STATUS, 0xf, 8), +}; + +static const struct vop_ctrl rk3036_ctrl_data = { + .standby = VOP_REG(RK3036_SYS_CTRL, 0x1, 30), + .out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0), + .pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4), + .htotal_pw = VOP_REG(RK3036_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), + .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0), + .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), + .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0), + .line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12), + .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0), +}; + +static const struct vop_reg_data rk3036_vop_init_reg_table[] = { + {RK3036_DSP_CTRL1, 0x00000000}, +}; + +static const struct vop_data rk3036_vop = { + .init_table = rk3036_vop_init_reg_table, + .table_size = ARRAY_SIZE(rk3036_vop_init_reg_table), + .ctrl = &rk3036_ctrl_data, + .intr = &rk3036_intr, + .win = rk3036_vop_win_data, + .win_size = ARRAY_SIZE(rk3036_vop_win_data), +}; + static const struct vop_scl_extension rk3288_win_full_scl_ext = { .cbcr_vsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 31), .cbcr_vsu_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 30), @@ -133,6 +223,7 @@ static const struct vop_ctrl rk3288_ctrl_data = { .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0), .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0), .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0), + .line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12), .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0), }; @@ -190,93 +281,104 @@ static const struct vop_data rk3288_vop = { .win_size = ARRAY_SIZE(rk3288_vop_win_data), }; -static const struct vop_scl_regs rk3036_win_scl = { - .scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), - .scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), - .scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), - .scale_cbcr_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 16), -}; - -static const struct vop_win_phy rk3036_win0_data = { - .scl = &rk3036_win_scl, - .data_formats = formats_win_full, - .nformats = ARRAY_SIZE(formats_win_full), - .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0), - .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 3), - .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 15), - .act_info = VOP_REG(RK3036_WIN0_ACT_INFO, 0x1fff1fff, 0), - .dsp_info = VOP_REG(RK3036_WIN0_DSP_INFO, 0x0fff0fff, 0), - .dsp_st = VOP_REG(RK3036_WIN0_DSP_ST, 0x1fff1fff, 0), - .yrgb_mst = VOP_REG(RK3036_WIN0_YRGB_MST, 0xffffffff, 0), - .uv_mst = VOP_REG(RK3036_WIN0_CBR_MST, 0xffffffff, 0), - .yrgb_vir = VOP_REG(RK3036_WIN0_VIR, 0xffff, 0), - .uv_vir = VOP_REG(RK3036_WIN0_VIR, 0x1fff, 16), +static const struct vop_ctrl rk3399_ctrl_data = { + .standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22), + .gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23), + .rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12), + .hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13), + .edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14), + .mipi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 15), + .dither_down = VOP_REG(RK3399_DSP_CTRL1, 0xf, 1), + .dither_up = VOP_REG(RK3399_DSP_CTRL1, 0x1, 6), + .data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19), + .out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0), + .rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16), + .hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20), + .edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24), + .mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28), + .htotal_pw = VOP_REG(RK3399_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), + .hact_st_end = VOP_REG(RK3399_DSP_HACT_ST_END, 0x1fff1fff, 0), + .vtotal_pw = VOP_REG(RK3399_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), + .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0), + .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0), + .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0), + .line_flag_num[0] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0), + .line_flag_num[1] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16), + .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0), }; -static const struct vop_win_phy rk3036_win1_data = { - .data_formats = formats_win_lite, - .nformats = ARRAY_SIZE(formats_win_lite), - .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1), - .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6), - .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19), - .act_info = VOP_REG(RK3036_WIN1_ACT_INFO, 0x1fff1fff, 0), - .dsp_info = VOP_REG(RK3036_WIN1_DSP_INFO, 0x0fff0fff, 0), - .dsp_st = VOP_REG(RK3036_WIN1_DSP_ST, 0x1fff1fff, 0), - .yrgb_mst = VOP_REG(RK3036_WIN1_MST, 0xffffffff, 0), - .yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0), -}; - -static const struct vop_win_data rk3036_vop_win_data[] = { - { .base = 0x00, .phy = &rk3036_win0_data, - .type = DRM_PLANE_TYPE_PRIMARY }, - { .base = 0x00, .phy = &rk3036_win1_data, - .type = DRM_PLANE_TYPE_CURSOR }, -}; - -static const int rk3036_vop_intrs[] = { - DSP_HOLD_VALID_INTR, +static const int rk3399_vop_intrs[] = { FS_INTR, + 0, 0, LINE_FLAG_INTR, + 0, BUS_ERROR_INTR, + 0, 0, 0, 0, 0, 0, 0, + DSP_HOLD_VALID_INTR, }; -static const struct vop_intr rk3036_intr = { - .intrs = rk3036_vop_intrs, - .nintrs = ARRAY_SIZE(rk3036_vop_intrs), - .status = VOP_REG(RK3036_INT_STATUS, 0xf, 0), - .enable = VOP_REG(RK3036_INT_STATUS, 0xf, 4), - .clear = VOP_REG(RK3036_INT_STATUS, 0xf, 8), +static const struct vop_intr rk3399_vop_intr = { + .intrs = rk3399_vop_intrs, + .nintrs = ARRAY_SIZE(rk3399_vop_intrs), + .status = VOP_REG_MASK(RK3399_INTR_STATUS0, 0xffff, 0), + .enable = VOP_REG_MASK(RK3399_INTR_EN0, 0xffff, 0), + .clear = VOP_REG_MASK(RK3399_INTR_CLEAR0, 0xffff, 0), }; -static const struct vop_ctrl rk3036_ctrl_data = { - .standby = VOP_REG(RK3036_SYS_CTRL, 0x1, 30), - .out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0), - .pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4), - .htotal_pw = VOP_REG(RK3036_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), - .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0), - .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), - .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0), - .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0), +static const struct vop_reg_data rk3399_init_reg_table[] = { + {RK3399_SYS_CTRL, 0x2000f800}, + {RK3399_DSP_CTRL0, 0x00000000}, + {RK3399_WIN0_CTRL0, 0x00000080}, + {RK3399_WIN1_CTRL0, 0x00000080}, + /* TODO: Win2/3 support multiple area function, but we haven't found + * a suitable way to use it yet, so let's just use them as other windows + * with only area 0 enabled. + */ + {RK3399_WIN2_CTRL0, 0x00000010}, + {RK3399_WIN3_CTRL0, 0x00000010}, }; -static const struct vop_reg_data rk3036_vop_init_reg_table[] = { - {RK3036_DSP_CTRL1, 0x00000000}, +static const struct vop_data rk3399_vop_big = { + .init_table = rk3399_init_reg_table, + .table_size = ARRAY_SIZE(rk3399_init_reg_table), + .intr = &rk3399_vop_intr, + .ctrl = &rk3399_ctrl_data, + /* + * rk3399 vop big windows register layout is same as rk3288. + */ + .win = rk3288_vop_win_data, + .win_size = ARRAY_SIZE(rk3288_vop_win_data), }; -static const struct vop_data rk3036_vop = { - .init_table = rk3036_vop_init_reg_table, - .table_size = ARRAY_SIZE(rk3036_vop_init_reg_table), - .ctrl = &rk3036_ctrl_data, - .intr = &rk3036_intr, - .win = rk3036_vop_win_data, - .win_size = ARRAY_SIZE(rk3036_vop_win_data), +static const struct vop_win_data rk3399_vop_lit_win_data[] = { + { .base = 0x00, .phy = &rk3288_win01_data, + .type = DRM_PLANE_TYPE_PRIMARY }, + { .base = 0x00, .phy = &rk3288_win23_data, + .type = DRM_PLANE_TYPE_CURSOR}, +}; + +static const struct vop_data rk3399_vop_lit = { + .init_table = rk3399_init_reg_table, + .table_size = ARRAY_SIZE(rk3399_init_reg_table), + .intr = &rk3399_vop_intr, + .ctrl = &rk3399_ctrl_data, + /* + * rk3399 vop lit windows register layout is same as rk3288, + * but cut off the win1 and win3 windows. + */ + .win = rk3399_vop_lit_win_data, + .win_size = ARRAY_SIZE(rk3399_vop_lit_win_data), }; static const struct of_device_id vop_driver_dt_match[] = { - { .compatible = "rockchip,rk3288-vop", - .data = &rk3288_vop }, { .compatible = "rockchip,rk3036-vop", .data = &rk3036_vop }, + { .compatible = "rockchip,rk3288-vop", + .data = &rk3288_vop }, + { .compatible = "rockchip,rk3399-vop-big", + .data = &rk3399_vop_big }, + { .compatible = "rockchip,rk3399-vop-lit", + .data = &rk3399_vop_lit }, {}, }; MODULE_DEVICE_TABLE(of, vop_driver_dt_match); @@ -305,7 +407,6 @@ static struct platform_driver vop_platform_driver = { .remove = vop_remove, .driver = { .name = "rockchip-vop", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(vop_driver_dt_match), }, }; diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h index d4b46cb..cd19726 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h @@ -166,4 +166,197 @@ #define RK3036_HWC_LUT_ADDR 0x800 /* rk3036 register definition end */ +/* rk3399 register definition */ +#define RK3399_REG_CFG_DONE 0x00000 +#define RK3399_VERSION_INFO 0x00004 +#define RK3399_SYS_CTRL 0x00008 +#define RK3399_SYS_CTRL1 0x0000c +#define RK3399_DSP_CTRL0 0x00010 +#define RK3399_DSP_CTRL1 0x00014 +#define RK3399_DSP_BG 0x00018 +#define RK3399_MCU_CTRL 0x0001c +#define RK3399_WB_CTRL0 0x00020 +#define RK3399_WB_CTRL1 0x00024 +#define RK3399_WB_YRGB_MST 0x00028 +#define RK3399_WB_CBR_MST 0x0002c +#define RK3399_WIN0_CTRL0 0x00030 +#define RK3399_WIN0_CTRL1 0x00034 +#define RK3399_WIN0_COLOR_KEY 0x00038 +#define RK3399_WIN0_VIR 0x0003c +#define RK3399_WIN0_YRGB_MST 0x00040 +#define RK3399_WIN0_CBR_MST 0x00044 +#define RK3399_WIN0_ACT_INFO 0x00048 +#define RK3399_WIN0_DSP_INFO 0x0004c +#define RK3399_WIN0_DSP_ST 0x00050 +#define RK3399_WIN0_SCL_FACTOR_YRGB 0x00054 +#define RK3399_WIN0_SCL_FACTOR_CBR 0x00058 +#define RK3399_WIN0_SCL_OFFSET 0x0005c +#define RK3399_WIN0_SRC_ALPHA_CTRL 0x00060 +#define RK3399_WIN0_DST_ALPHA_CTRL 0x00064 +#define RK3399_WIN0_FADING_CTRL 0x00068 +#define RK3399_WIN0_CTRL2 0x0006c +#define RK3399_WIN1_CTRL0 0x00070 +#define RK3399_WIN1_CTRL1 0x00074 +#define RK3399_WIN1_COLOR_KEY 0x00078 +#define RK3399_WIN1_VIR 0x0007c +#define RK3399_WIN1_YRGB_MST 0x00080 +#define RK3399_WIN1_CBR_MST 0x00084 +#define RK3399_WIN1_ACT_INFO 0x00088 +#define RK3399_WIN1_DSP_INFO 0x0008c +#define RK3399_WIN1_DSP_ST 0x00090 +#define RK3399_WIN1_SCL_FACTOR_YRGB 0x00094 +#define RK3399_WIN1_SCL_FACTOR_CBR 0x00098 +#define RK3399_WIN1_SCL_OFFSET 0x0009c +#define RK3399_WIN1_SRC_ALPHA_CTRL 0x000a0 +#define RK3399_WIN1_DST_ALPHA_CTRL 0x000a4 +#define RK3399_WIN1_FADING_CTRL 0x000a8 +#define RK3399_WIN1_CTRL2 0x000ac +#define RK3399_WIN2_CTRL0 0x000b0 +#define RK3399_WIN2_CTRL1 0x000b4 +#define RK3399_WIN2_VIR0_1 0x000b8 +#define RK3399_WIN2_VIR2_3 0x000bc +#define RK3399_WIN2_MST0 0x000c0 +#define RK3399_WIN2_DSP_INFO0 0x000c4 +#define RK3399_WIN2_DSP_ST0 0x000c8 +#define RK3399_WIN2_COLOR_KEY 0x000cc +#define RK3399_WIN2_MST1 0x000d0 +#define RK3399_WIN2_DSP_INFO1 0x000d4 +#define RK3399_WIN2_DSP_ST1 0x000d8 +#define RK3399_WIN2_SRC_ALPHA_CTRL 0x000dc +#define RK3399_WIN2_MST2 0x000e0 +#define RK3399_WIN2_DSP_INFO2 0x000e4 +#define RK3399_WIN2_DSP_ST2 0x000e8 +#define RK3399_WIN2_DST_ALPHA_CTRL 0x000ec +#define RK3399_WIN2_MST3 0x000f0 +#define RK3399_WIN2_DSP_INFO3 0x000f4 +#define RK3399_WIN2_DSP_ST3 0x000f8 +#define RK3399_WIN2_FADING_CTRL 0x000fc +#define RK3399_WIN3_CTRL0 0x00100 +#define RK3399_WIN3_CTRL1 0x00104 +#define RK3399_WIN3_VIR0_1 0x00108 +#define RK3399_WIN3_VIR2_3 0x0010c +#define RK3399_WIN3_MST0 0x00110 +#define RK3399_WIN3_DSP_INFO0 0x00114 +#define RK3399_WIN3_DSP_ST0 0x00118 +#define RK3399_WIN3_COLOR_KEY 0x0011c +#define RK3399_WIN3_MST1 0x00120 +#define RK3399_WIN3_DSP_INFO1 0x00124 +#define RK3399_WIN3_DSP_ST1 0x00128 +#define RK3399_WIN3_SRC_ALPHA_CTRL 0x0012c +#define RK3399_WIN3_MST2 0x00130 +#define RK3399_WIN3_DSP_INFO2 0x00134 +#define RK3399_WIN3_DSP_ST2 0x00138 +#define RK3399_WIN3_DST_ALPHA_CTRL 0x0013c +#define RK3399_WIN3_MST3 0x00140 +#define RK3399_WIN3_DSP_INFO3 0x00144 +#define RK3399_WIN3_DSP_ST3 0x00148 +#define RK3399_WIN3_FADING_CTRL 0x0014c +#define RK3399_HWC_CTRL0 0x00150 +#define RK3399_HWC_CTRL1 0x00154 +#define RK3399_HWC_MST 0x00158 +#define RK3399_HWC_DSP_ST 0x0015c +#define RK3399_HWC_SRC_ALPHA_CTRL 0x00160 +#define RK3399_HWC_DST_ALPHA_CTRL 0x00164 +#define RK3399_HWC_FADING_CTRL 0x00168 +#define RK3399_HWC_RESERVED1 0x0016c +#define RK3399_POST_DSP_HACT_INFO 0x00170 +#define RK3399_POST_DSP_VACT_INFO 0x00174 +#define RK3399_POST_SCL_FACTOR_YRGB 0x00178 +#define RK3399_POST_RESERVED 0x0017c +#define RK3399_POST_SCL_CTRL 0x00180 +#define RK3399_POST_DSP_VACT_INFO_F1 0x00184 +#define RK3399_DSP_HTOTAL_HS_END 0x00188 +#define RK3399_DSP_HACT_ST_END 0x0018c +#define RK3399_DSP_VTOTAL_VS_END 0x00190 +#define RK3399_DSP_VACT_ST_END 0x00194 +#define RK3399_DSP_VS_ST_END_F1 0x00198 +#define RK3399_DSP_VACT_ST_END_F1 0x0019c +#define RK3399_PWM_CTRL 0x001a0 +#define RK3399_PWM_PERIOD_HPR 0x001a4 +#define RK3399_PWM_DUTY_LPR 0x001a8 +#define RK3399_PWM_CNT 0x001ac +#define RK3399_BCSH_COLOR_BAR 0x001b0 +#define RK3399_BCSH_BCS 0x001b4 +#define RK3399_BCSH_H 0x001b8 +#define RK3399_BCSH_CTRL 0x001bc +#define RK3399_CABC_CTRL0 0x001c0 +#define RK3399_CABC_CTRL1 0x001c4 +#define RK3399_CABC_CTRL2 0x001c8 +#define RK3399_CABC_CTRL3 0x001cc +#define RK3399_CABC_GAUSS_LINE0_0 0x001d0 +#define RK3399_CABC_GAUSS_LINE0_1 0x001d4 +#define RK3399_CABC_GAUSS_LINE1_0 0x001d8 +#define RK3399_CABC_GAUSS_LINE1_1 0x001dc +#define RK3399_CABC_GAUSS_LINE2_0 0x001e0 +#define RK3399_CABC_GAUSS_LINE2_1 0x001e4 +#define RK3399_FRC_LOWER01_0 0x001e8 +#define RK3399_FRC_LOWER01_1 0x001ec +#define RK3399_FRC_LOWER10_0 0x001f0 +#define RK3399_FRC_LOWER10_1 0x001f4 +#define RK3399_FRC_LOWER11_0 0x001f8 +#define RK3399_FRC_LOWER11_1 0x001fc +#define RK3399_AFBCD0_CTRL 0x00200 +#define RK3399_AFBCD0_HDR_PTR 0x00204 +#define RK3399_AFBCD0_PIC_SIZE 0x00208 +#define RK3399_AFBCD0_STATUS 0x0020c +#define RK3399_AFBCD1_CTRL 0x00220 +#define RK3399_AFBCD1_HDR_PTR 0x00224 +#define RK3399_AFBCD1_PIC_SIZE 0x00228 +#define RK3399_AFBCD1_STATUS 0x0022c +#define RK3399_AFBCD2_CTRL 0x00240 +#define RK3399_AFBCD2_HDR_PTR 0x00244 +#define RK3399_AFBCD2_PIC_SIZE 0x00248 +#define RK3399_AFBCD2_STATUS 0x0024c +#define RK3399_AFBCD3_CTRL 0x00260 +#define RK3399_AFBCD3_HDR_PTR 0x00264 +#define RK3399_AFBCD3_PIC_SIZE 0x00268 +#define RK3399_AFBCD3_STATUS 0x0026c +#define RK3399_INTR_EN0 0x00280 +#define RK3399_INTR_CLEAR0 0x00284 +#define RK3399_INTR_STATUS0 0x00288 +#define RK3399_INTR_RAW_STATUS0 0x0028c +#define RK3399_INTR_EN1 0x00290 +#define RK3399_INTR_CLEAR1 0x00294 +#define RK3399_INTR_STATUS1 0x00298 +#define RK3399_INTR_RAW_STATUS1 0x0029c +#define RK3399_LINE_FLAG 0x002a0 +#define RK3399_VOP_STATUS 0x002a4 +#define RK3399_BLANKING_VALUE 0x002a8 +#define RK3399_MCU_BYPASS_PORT 0x002ac +#define RK3399_WIN0_DSP_BG 0x002b0 +#define RK3399_WIN1_DSP_BG 0x002b4 +#define RK3399_WIN2_DSP_BG 0x002b8 +#define RK3399_WIN3_DSP_BG 0x002bc +#define RK3399_YUV2YUV_WIN 0x002c0 +#define RK3399_YUV2YUV_POST 0x002c4 +#define RK3399_AUTO_GATING_EN 0x002cc +#define RK3399_WIN0_CSC_COE 0x003a0 +#define RK3399_WIN1_CSC_COE 0x003c0 +#define RK3399_WIN2_CSC_COE 0x003e0 +#define RK3399_WIN3_CSC_COE 0x00400 +#define RK3399_HWC_CSC_COE 0x00420 +#define RK3399_BCSH_R2Y_CSC_COE 0x00440 +#define RK3399_BCSH_Y2R_CSC_COE 0x00460 +#define RK3399_POST_YUV2YUV_Y2R_COE 0x00480 +#define RK3399_POST_YUV2YUV_3X3_COE 0x004a0 +#define RK3399_POST_YUV2YUV_R2Y_COE 0x004c0 +#define RK3399_WIN0_YUV2YUV_Y2R 0x004e0 +#define RK3399_WIN0_YUV2YUV_3X3 0x00500 +#define RK3399_WIN0_YUV2YUV_R2Y 0x00520 +#define RK3399_WIN1_YUV2YUV_Y2R 0x00540 +#define RK3399_WIN1_YUV2YUV_3X3 0x00560 +#define RK3399_WIN1_YUV2YUV_R2Y 0x00580 +#define RK3399_WIN2_YUV2YUV_Y2R 0x005a0 +#define RK3399_WIN2_YUV2YUV_3X3 0x005c0 +#define RK3399_WIN2_YUV2YUV_R2Y 0x005e0 +#define RK3399_WIN3_YUV2YUV_Y2R 0x00600 +#define RK3399_WIN3_YUV2YUV_3X3 0x00620 +#define RK3399_WIN3_YUV2YUV_R2Y 0x00640 +#define RK3399_WIN2_LUT_ADDR 0x01000 +#define RK3399_WIN3_LUT_ADDR 0x01400 +#define RK3399_HWC_LUT_ADDR 0x01800 +#define RK3399_CABC_GAMMA_LUT_ADDR 0x01c00 +#define RK3399_GAMMA_LUT_ADDR 0x02000 +/* rk3399 register definition end */ + #endif /* _ROCKCHIP_VOP_REG_H */ diff --git a/drivers/gpu/drm/savage/savage_drv.c b/drivers/gpu/drm/savage/savage_drv.c index 21aed1f..3b80713 100644 --- a/drivers/gpu/drm/savage/savage_drv.c +++ b/drivers/gpu/drm/savage/savage_drv.c @@ -50,7 +50,7 @@ static const struct file_operations savage_driver_fops = { static struct drm_driver driver = { .driver_features = - DRIVER_USE_AGP | DRIVER_HAVE_DMA | DRIVER_PCI_DMA, + DRIVER_USE_AGP | DRIVER_HAVE_DMA | DRIVER_PCI_DMA | DRIVER_LEGACY, .dev_priv_size = sizeof(drm_savage_buf_priv_t), .load = savage_driver_load, .firstopen = savage_driver_firstopen, diff --git a/drivers/gpu/drm/savage/savage_state.c b/drivers/gpu/drm/savage/savage_state.c index c01ad0a..3dc0d8f 100644 --- a/drivers/gpu/drm/savage/savage_state.c +++ b/drivers/gpu/drm/savage/savage_state.c @@ -1001,15 +1001,9 @@ int savage_bci_cmdbuf(struct drm_device *dev, void *data, struct drm_file *file_ cmdbuf->cmd_addr = kcmd_addr; } if (cmdbuf->vb_size) { - kvb_addr = kmalloc(cmdbuf->vb_size, GFP_KERNEL); - if (kvb_addr == NULL) { - ret = -ENOMEM; - goto done; - } - - if (copy_from_user(kvb_addr, cmdbuf->vb_addr, - cmdbuf->vb_size)) { - ret = -EFAULT; + kvb_addr = memdup_user(cmdbuf->vb_addr, cmdbuf->vb_size); + if (IS_ERR(kvb_addr)) { + ret = PTR_ERR(kvb_addr); goto done; } cmdbuf->vb_addr = kvb_addr; diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c index 79bce76..ae98398 100644 --- a/drivers/gpu/drm/sis/sis_drv.c +++ b/drivers/gpu/drm/sis/sis_drv.c @@ -102,7 +102,7 @@ static void sis_driver_postclose(struct drm_device *dev, struct drm_file *file) } static struct drm_driver driver = { - .driver_features = DRIVER_USE_AGP, + .driver_features = DRIVER_USE_AGP | DRIVER_LEGACY, .load = sis_driver_load, .unload = sis_driver_unload, .open = sis_driver_open, diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig index 494ab25..acd7286 100644 --- a/drivers/gpu/drm/sti/Kconfig +++ b/drivers/gpu/drm/sti/Kconfig @@ -1,6 +1,6 @@ config DRM_STI - tristate "DRM Support for STMicroelectronics SoC stiH41x Series" - depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM) + tristate "DRM Support for STMicroelectronics SoC stiH4xx Series" + depends on DRM && (ARCH_STI || ARCH_MULTIPLATFORM) select RESET_CONTROLLER select DRM_KMS_HELPER select DRM_GEM_CMA_HELPER @@ -9,4 +9,4 @@ config DRM_STI select FW_LOADER select SND_SOC_HDMI_CODEC if SND_SOC help - Choose this option to enable DRM on STM stiH41x chipset + Choose this option to enable DRM on STM stiH4xx chipset diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile index b805762..d20f7c0 100644 --- a/drivers/gpu/drm/sti/Makefile +++ b/drivers/gpu/drm/sti/Makefile @@ -9,7 +9,6 @@ sti-drm-y := \ sti_crtc.o \ sti_plane.o \ sti_hdmi.o \ - sti_hdmi_tx3g0c55phy.o \ sti_hdmi_tx3g4c28phy.o \ sti_dvo.o \ sti_awg_utils.o \ diff --git a/drivers/gpu/drm/sti/sti_compositor.c b/drivers/gpu/drm/sti/sti_compositor.c index 134201e..f62041f 100644 --- a/drivers/gpu/drm/sti/sti_compositor.c +++ b/drivers/gpu/drm/sti/sti_compositor.c @@ -25,7 +25,7 @@ /* * stiH407 compositor properties */ -struct sti_compositor_data stih407_compositor_data = { +static const struct sti_compositor_data stih407_compositor_data = { .nb_subdev = 8, .subdev_desc = { {STI_CURSOR_SUBDEV, (int)STI_CURSOR, 0x000}, @@ -39,38 +39,18 @@ struct sti_compositor_data stih407_compositor_data = { }, }; -/* - * stiH416 compositor properties - * Note: - * on stih416 MIXER_AUX has a different base address from MIXER_MAIN - * Moreover, GDPx is different for Main and Aux Mixer. So this subdev map does - * not fit for stiH416 if we want to enable the MIXER_AUX. - */ -struct sti_compositor_data stih416_compositor_data = { - .nb_subdev = 3, - .subdev_desc = { - {STI_GPD_SUBDEV, (int)STI_GDP_0, 0x100}, - {STI_GPD_SUBDEV, (int)STI_GDP_1, 0x200}, - {STI_MIXER_MAIN_SUBDEV, STI_MIXER_MAIN, 0xC00} - }, -}; - -int sti_compositor_debufs_init(struct sti_compositor *compo, - struct drm_minor *minor) +int sti_compositor_debugfs_init(struct sti_compositor *compo, + struct drm_minor *minor) { - int ret = 0, i; + unsigned int i; - for (i = 0; compo->vid[i]; i++) { - ret = vid_debugfs_init(compo->vid[i], minor); - if (ret) - return ret; - } + for (i = 0; i < STI_MAX_VID; i++) + if (compo->vid[i]) + vid_debugfs_init(compo->vid[i], minor); - for (i = 0; compo->mixer[i]; i++) { - ret = sti_mixer_debugfs_init(compo->mixer[i], minor); - if (ret) - return ret; - } + for (i = 0; i < STI_MAX_MIXER; i++) + if (compo->mixer[i]) + sti_mixer_debugfs_init(compo->mixer[i], minor); return 0; } @@ -183,9 +163,6 @@ static const struct component_ops sti_compositor_ops = { static const struct of_device_id compositor_of_match[] = { { - .compatible = "st,stih416-compositor", - .data = &stih416_compositor_data, - }, { .compatible = "st,stih407-compositor", .data = &stih407_compositor_data, }, { @@ -201,6 +178,7 @@ static int sti_compositor_probe(struct platform_device *pdev) struct device_node *vtg_np; struct sti_compositor *compo; struct resource *res; + unsigned int i; compo = devm_kzalloc(dev, sizeof(*compo), GFP_KERNEL); if (!compo) { @@ -208,7 +186,8 @@ static int sti_compositor_probe(struct platform_device *pdev) return -ENOMEM; } compo->dev = dev; - compo->vtg_vblank_nb.notifier_call = sti_crtc_vblank_cb; + for (i = 0; i < STI_MAX_MIXER; i++) + compo->vtg_vblank_nb[i].notifier_call = sti_crtc_vblank_cb; /* populate data structure depending on compatibility */ BUG_ON(!of_match_node(compositor_of_match, np)->data); @@ -266,12 +245,12 @@ static int sti_compositor_probe(struct platform_device *pdev) vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 0); if (vtg_np) - compo->vtg_main = of_vtg_find(vtg_np); + compo->vtg[STI_MIXER_MAIN] = of_vtg_find(vtg_np); of_node_put(vtg_np); vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 1); if (vtg_np) - compo->vtg_aux = of_vtg_find(vtg_np); + compo->vtg[STI_MIXER_AUX] = of_vtg_find(vtg_np); of_node_put(vtg_np); platform_set_drvdata(pdev, compo); diff --git a/drivers/gpu/drm/sti/sti_compositor.h b/drivers/gpu/drm/sti/sti_compositor.h index 24444ef..2952a2d 100644 --- a/drivers/gpu/drm/sti/sti_compositor.h +++ b/drivers/gpu/drm/sti/sti_compositor.h @@ -60,9 +60,8 @@ struct sti_compositor_data { * @rst_aux: reset control of the aux path * @mixer: array of mixers * @vid: array of vids - * @vtg_main: vtg for main data path - * @vtg_aux: vtg for auxillary data path - * @vtg_vblank_nb: callback for VTG VSYNC notification + * @vtg: array of vtgs + * @vtg_vblank_nb: array of callbacks for VTG VSYNC notification */ struct sti_compositor { struct device *dev; @@ -76,12 +75,11 @@ struct sti_compositor { struct reset_control *rst_aux; struct sti_mixer *mixer[STI_MAX_MIXER]; struct sti_vid *vid[STI_MAX_VID]; - struct sti_vtg *vtg_main; - struct sti_vtg *vtg_aux; - struct notifier_block vtg_vblank_nb; + struct sti_vtg *vtg[STI_MAX_MIXER]; + struct notifier_block vtg_vblank_nb[STI_MAX_MIXER]; }; -int sti_compositor_debufs_init(struct sti_compositor *compo, - struct drm_minor *minor); +int sti_compositor_debugfs_init(struct sti_compositor *compo, + struct drm_minor *minor); #endif diff --git a/drivers/gpu/drm/sti/sti_crtc.c b/drivers/gpu/drm/sti/sti_crtc.c index c7d734d..e992bed 100644 --- a/drivers/gpu/drm/sti/sti_crtc.c +++ b/drivers/gpu/drm/sti/sti_crtc.c @@ -86,8 +86,7 @@ sti_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode) goto pix_error; } - sti_vtg_set_config(mixer->id == STI_MIXER_MAIN ? - compo->vtg_main : compo->vtg_aux, &crtc->mode); + sti_vtg_set_config(compo->vtg[mixer->id], &crtc->mode); if (sti_mixer_active_video_area(mixer, &crtc->mode)) { DRM_ERROR("Can't set active video area\n"); @@ -166,6 +165,10 @@ static void sti_crtc_atomic_flush(struct drm_crtc *crtc, switch (plane->status) { case STI_PLANE_UPDATED: + /* ignore update for other CRTC */ + if (p->state->crtc != crtc) + continue; + /* update planes tag as updated */ DRM_DEBUG_DRIVER("update plane %s\n", sti_plane_to_str(plane)); @@ -244,8 +247,7 @@ static int sti_crtc_set_property(struct drm_crtc *crtc, int sti_crtc_vblank_cb(struct notifier_block *nb, unsigned long event, void *data) { - struct sti_compositor *compo = - container_of(nb, struct sti_compositor, vtg_vblank_nb); + struct sti_compositor *compo; struct drm_crtc *crtc = data; struct sti_mixer *mixer; unsigned long flags; @@ -254,6 +256,7 @@ int sti_crtc_vblank_cb(struct notifier_block *nb, priv = crtc->dev->dev_private; pipe = drm_crtc_index(crtc); + compo = container_of(nb, struct sti_compositor, vtg_vblank_nb[pipe]); mixer = compo->mixer[pipe]; if ((event != VTG_TOP_FIELD_EVENT) && @@ -295,14 +298,13 @@ int sti_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe) { struct sti_private *dev_priv = dev->dev_private; struct sti_compositor *compo = dev_priv->compo; - struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb; + struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb[pipe]; struct drm_crtc *crtc = &compo->mixer[pipe]->drm_crtc; + struct sti_vtg *vtg = compo->vtg[pipe]; DRM_DEBUG_DRIVER("\n"); - if (sti_vtg_register_client(pipe == STI_MIXER_MAIN ? - compo->vtg_main : compo->vtg_aux, - vtg_vblank_nb, crtc)) { + if (sti_vtg_register_client(vtg, vtg_vblank_nb, crtc)) { DRM_ERROR("Cannot register VTG notifier\n"); return -EINVAL; } @@ -314,13 +316,13 @@ void sti_crtc_disable_vblank(struct drm_device *drm_dev, unsigned int pipe) { struct sti_private *priv = drm_dev->dev_private; struct sti_compositor *compo = priv->compo; - struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb; + struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb[pipe]; struct drm_crtc *crtc = &compo->mixer[pipe]->drm_crtc; + struct sti_vtg *vtg = compo->vtg[pipe]; DRM_DEBUG_DRIVER("\n"); - if (sti_vtg_unregister_client(pipe == STI_MIXER_MAIN ? - compo->vtg_main : compo->vtg_aux, vtg_vblank_nb)) + if (sti_vtg_unregister_client(vtg, vtg_vblank_nb)) DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n"); /* free the resources of the pending requests */ @@ -336,7 +338,7 @@ static int sti_crtc_late_register(struct drm_crtc *crtc) struct sti_compositor *compo = dev_get_drvdata(mixer->dev); if (drm_crtc_index(crtc) == 0) - return sti_compositor_debufs_init(compo, crtc->dev->primary); + return sti_compositor_debugfs_init(compo, crtc->dev->primary); return 0; } diff --git a/drivers/gpu/drm/sti/sti_cursor.c b/drivers/gpu/drm/sti/sti_cursor.c index 3b53f7f..cca75bd 100644 --- a/drivers/gpu/drm/sti/sti_cursor.c +++ b/drivers/gpu/drm/sti/sti_cursor.c @@ -309,15 +309,15 @@ static void sti_cursor_atomic_disable(struct drm_plane *drm_plane, { struct sti_plane *plane = to_sti_plane(drm_plane); - if (!drm_plane->crtc) { + if (!oldstate->crtc) { DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", drm_plane->base.id); return; } DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", - drm_plane->crtc->base.id, - sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)), + oldstate->crtc->base.id, + sti_mixer_to_str(to_sti_mixer(oldstate->crtc)), drm_plane->base.id, sti_plane_to_str(plane)); plane->status = STI_PLANE_DISABLING; @@ -345,7 +345,7 @@ static int sti_cursor_late_register(struct drm_plane *drm_plane) return cursor_debugfs_init(cursor, drm_plane->dev->primary); } -struct drm_plane_funcs sti_cursor_plane_helpers_funcs = { +static const struct drm_plane_funcs sti_cursor_plane_helpers_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = sti_cursor_destroy, diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index 96bd3d0..2784919 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -140,7 +140,7 @@ err: return ret; } -void sti_drm_dbg_cleanup(struct drm_minor *minor) +static void sti_drm_dbg_cleanup(struct drm_minor *minor) { drm_debugfs_remove_files(sti_drm_dbg_list, ARRAY_SIZE(sti_drm_dbg_list), minor); @@ -178,7 +178,7 @@ static void sti_atomic_complete(struct sti_private *private, */ drm_atomic_helper_commit_modeset_disables(drm, state); - drm_atomic_helper_commit_planes(drm, state, false); + drm_atomic_helper_commit_planes(drm, state, 0); drm_atomic_helper_commit_modeset_enables(drm, state); drm_atomic_helper_wait_for_vblanks(drm, state); @@ -282,7 +282,7 @@ static const struct file_operations sti_driver_fops = { }; static struct drm_driver sti_driver = { - .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, @@ -365,8 +365,8 @@ static int sti_bind(struct device *dev) int ret; ddev = drm_dev_alloc(&sti_driver, dev); - if (!ddev) - return -ENOMEM; + if (IS_ERR(ddev)) + return PTR_ERR(ddev); ddev->platformdev = to_platform_device(dev); diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c index 00881eb..e8c1ed0 100644 --- a/drivers/gpu/drm/sti/sti_dvo.c +++ b/drivers/gpu/drm/sti/sti_dvo.c @@ -17,6 +17,7 @@ #include <drm/drm_panel.h> #include "sti_awg_utils.h" +#include "sti_drv.h" #include "sti_mixer.h" /* DVO registers */ @@ -106,7 +107,7 @@ struct sti_dvo_connector { container_of(x, struct sti_dvo_connector, drm_connector) #define BLANKING_LEVEL 16 -int dvo_awg_generate_code(struct sti_dvo *dvo, u8 *ram_size, u32 *ram_code) +static int dvo_awg_generate_code(struct sti_dvo *dvo, u8 *ram_size, u32 *ram_code) { struct drm_display_mode *mode = &dvo->mode; struct dvo_config *config = dvo->config; diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c index b8d942c..81df309 100644 --- a/drivers/gpu/drm/sti/sti_gdp.c +++ b/drivers/gpu/drm/sti/sti_gdp.c @@ -460,6 +460,7 @@ static void sti_gdp_disable(struct sti_gdp *gdp) clk_disable_unprepare(gdp->clk_pix); gdp->plane.status = STI_PLANE_DISABLED; + gdp->vtg = NULL; } /** @@ -473,8 +474,8 @@ static void sti_gdp_disable(struct sti_gdp *gdp) * RETURNS: * 0 on success. */ -int sti_gdp_field_cb(struct notifier_block *nb, - unsigned long event, void *data) +static int sti_gdp_field_cb(struct notifier_block *nb, + unsigned long event, void *data) { struct sti_gdp *gdp = container_of(nb, struct sti_gdp, vtg_field_nb); @@ -611,7 +612,6 @@ static int sti_gdp_atomic_check(struct drm_plane *drm_plane, struct drm_crtc *crtc = state->crtc; struct sti_compositor *compo = dev_get_drvdata(gdp->dev); struct drm_framebuffer *fb = state->fb; - bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false; struct drm_crtc_state *crtc_state; struct sti_mixer *mixer; struct drm_display_mode *mode; @@ -628,8 +628,8 @@ static int sti_gdp_atomic_check(struct drm_plane *drm_plane, mode = &crtc_state->mode; dst_x = state->crtc_x; dst_y = state->crtc_y; - dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); - dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); + dst_w = clamp_val(state->crtc_w, 0, mode->hdisplay - dst_x); + dst_h = clamp_val(state->crtc_h, 0, mode->vdisplay - dst_y); /* src_x are in 16.16 format */ src_x = state->src_x >> 16; src_y = state->src_y >> 16; @@ -648,10 +648,9 @@ static int sti_gdp_atomic_check(struct drm_plane *drm_plane, return -EINVAL; } - if (first_prepare) { + if (!gdp->vtg) { /* Register gdp callback */ - gdp->vtg = mixer->id == STI_MIXER_MAIN ? - compo->vtg_main : compo->vtg_aux; + gdp->vtg = compo->vtg[mixer->id]; if (sti_vtg_register_client(gdp->vtg, &gdp->vtg_field_nb, crtc)) { DRM_ERROR("Cannot register VTG notifier\n"); @@ -719,7 +718,7 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, u32 dma_updated_top; u32 dma_updated_btm; int format; - unsigned int depth, bpp; + unsigned int bpp; u32 ydo, xdo, yds, xds; if (!crtc || !fb) @@ -728,8 +727,8 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, mode = &crtc->mode; dst_x = state->crtc_x; dst_y = state->crtc_y; - dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); - dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); + dst_w = clamp_val(state->crtc_w, 0, mode->hdisplay - dst_x); + dst_h = clamp_val(state->crtc_h, 0, mode->vdisplay - dst_y); /* src_x are in 16.16 format */ src_x = state->src_x >> 16; src_y = state->src_y >> 16; @@ -758,9 +757,9 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, (unsigned long)cma_obj->paddr); /* pixel memory location */ - drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); + bpp = drm_format_plane_cpp(fb->pixel_format, 0); top_field->gam_gdp_pml = (u32)cma_obj->paddr + fb->offsets[0]; - top_field->gam_gdp_pml += src_x * (bpp >> 3); + top_field->gam_gdp_pml += src_x * bpp; top_field->gam_gdp_pml += src_y * fb->pitches[0]; /* output parameters (clamped / cropped) */ @@ -810,7 +809,7 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, if (!curr_list) { /* First update or invalid node should directly write in the * hw register */ - DRM_DEBUG_DRIVER("%s first update (or invalid node)", + DRM_DEBUG_DRIVER("%s first update (or invalid node)\n", sti_plane_to_str(plane)); writel(gdp->is_curr_top ? @@ -846,15 +845,15 @@ static void sti_gdp_atomic_disable(struct drm_plane *drm_plane, { struct sti_plane *plane = to_sti_plane(drm_plane); - if (!drm_plane->crtc) { + if (!oldstate->crtc) { DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", drm_plane->base.id); return; } DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", - drm_plane->crtc->base.id, - sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)), + oldstate->crtc->base.id, + sti_mixer_to_str(to_sti_mixer(oldstate->crtc)), drm_plane->base.id, sti_plane_to_str(plane)); plane->status = STI_PLANE_DISABLING; @@ -882,7 +881,7 @@ static int sti_gdp_late_register(struct drm_plane *drm_plane) return gdp_debugfs_init(gdp, drm_plane->dev->primary); } -struct drm_plane_funcs sti_gdp_plane_helpers_funcs = { +static const struct drm_plane_funcs sti_gdp_plane_helpers_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = sti_gdp_destroy, diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c index 8505569..e7c243f 100644 --- a/drivers/gpu/drm/sti/sti_hda.c +++ b/drivers/gpu/drm/sti/sti_hda.c @@ -62,14 +62,8 @@ #define SCALE_CTRL_CR_DFLT 0x00DB0249 /* Video DACs control */ -#define VIDEO_DACS_CONTROL_MASK 0x0FFF -#define VIDEO_DACS_CONTROL_SYSCFG2535 0x085C /* for stih416 */ -#define DAC_CFG_HD_OFF_SHIFT 5 -#define DAC_CFG_HD_OFF_MASK (0x7 << DAC_CFG_HD_OFF_SHIFT) -#define VIDEO_DACS_CONTROL_SYSCFG5072 0x0120 /* for stih407 */ #define DAC_CFG_HD_HZUVW_OFF_MASK BIT(1) - /* Upsampler values for the alternative 2X Filter */ #define SAMPLER_COEF_NB 8 #define HDA_ANA_SRC_Y_CFG_ALT_2X 0x01130000 @@ -300,28 +294,14 @@ static bool hda_get_mode_idx(struct drm_display_mode mode, int *idx) */ static void hda_enable_hd_dacs(struct sti_hda *hda, bool enable) { - u32 mask; - if (hda->video_dacs_ctrl) { u32 val; - switch ((u32)hda->video_dacs_ctrl & VIDEO_DACS_CONTROL_MASK) { - case VIDEO_DACS_CONTROL_SYSCFG2535: - mask = DAC_CFG_HD_OFF_MASK; - break; - case VIDEO_DACS_CONTROL_SYSCFG5072: - mask = DAC_CFG_HD_HZUVW_OFF_MASK; - break; - default: - DRM_INFO("Video DACS control register not supported!"); - return; - } - val = readl(hda->video_dacs_ctrl); if (enable) - val &= ~mask; + val &= ~DAC_CFG_HD_HZUVW_OFF_MASK; else - val |= mask; + val |= DAC_CFG_HD_HZUVW_OFF_MASK; writel(val, hda->video_dacs_ctrl); } @@ -352,24 +332,11 @@ static void hda_dbg_awg_microcode(struct seq_file *s, void __iomem *reg) static void hda_dbg_video_dacs_ctrl(struct seq_file *s, void __iomem *reg) { u32 val = readl(reg); - u32 mask; - - switch ((u32)reg & VIDEO_DACS_CONTROL_MASK) { - case VIDEO_DACS_CONTROL_SYSCFG2535: - mask = DAC_CFG_HD_OFF_MASK; - break; - case VIDEO_DACS_CONTROL_SYSCFG5072: - mask = DAC_CFG_HD_HZUVW_OFF_MASK; - break; - default: - DRM_DEBUG_DRIVER("Warning: DACS ctrl register not supported!"); - return; - } seq_puts(s, "\n"); seq_printf(s, "\n %-25s 0x%08X", "VIDEO_DACS_CONTROL", val); seq_puts(s, "\tHD DACs "); - seq_puts(s, val & mask ? "disabled" : "enabled"); + seq_puts(s, val & DAC_CFG_HD_HZUVW_OFF_MASK ? "disabled" : "enabled"); } static int hda_dbg_show(struct seq_file *s, void *data) diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index fedc17f..376b076 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -22,7 +22,6 @@ #include "sti_hdmi.h" #include "sti_hdmi_tx3g4c28phy.h" -#include "sti_hdmi_tx3g0c55phy.h" #include "sti_vtg.h" #define HDMI_CFG 0x0000 @@ -203,7 +202,7 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg) /* Audio FIFO underrun IRQ */ if (hdmi->irq_status & HDMI_INT_AUDIO_FIFO_XRUN) - DRM_INFO("Warning: audio FIFO underrun occurs!"); + DRM_INFO("Warning: audio FIFO underrun occurs!\n"); return IRQ_HANDLED; } @@ -569,7 +568,7 @@ static void hdmi_swreset(struct sti_hdmi *hdmi) /* Wait reset completed */ wait_event_interruptible_timeout(hdmi->wait_event, - hdmi->event_received == true, + hdmi->event_received, msecs_to_jiffies (HDMI_TIMEOUT_SWRESET)); @@ -1054,6 +1053,7 @@ static int sti_hdmi_late_register(struct drm_connector *connector) } static const struct drm_connector_funcs sti_hdmi_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .detect = sti_hdmi_connector_detect, .destroy = drm_connector_cleanup, @@ -1181,7 +1181,7 @@ static void hdmi_audio_shutdown(struct device *dev, void *data) HDMI_AUD_CFG_ONE_BIT_INVALID; hdmi_write(hdmi, audio_cfg, HDMI_AUDIO_CFG); - hdmi->audio.enabled = 0; + hdmi->audio.enabled = false; hdmi_audio_infoframe_config(hdmi); } @@ -1213,7 +1213,7 @@ static int hdmi_audio_hw_params(struct device *dev, return -EINVAL; } - audio.enabled = 1; + audio.enabled = true; ret = hdmi_audio_configure(hdmi, &audio); if (ret < 0) @@ -1265,7 +1265,7 @@ static int sti_hdmi_register_audio_driver(struct device *dev, DRM_DEBUG_DRIVER("\n"); - hdmi->audio.enabled = 0; + hdmi->audio.enabled = false; hdmi->audio_pdev = platform_device_register_data( dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO, @@ -1373,9 +1373,6 @@ static const struct component_ops sti_hdmi_ops = { static const struct of_device_id hdmi_of_match[] = { { - .compatible = "st,stih416-hdmi", - .data = &tx3g0c55phy_ops, - }, { .compatible = "st,stih407-hdmi", .data = &tx3g4c28phy_ops, }, { @@ -1422,22 +1419,6 @@ static int sti_hdmi_probe(struct platform_device *pdev) goto release_adapter; } - if (of_device_is_compatible(np, "st,stih416-hdmi")) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "syscfg"); - if (!res) { - DRM_ERROR("Invalid syscfg resource\n"); - ret = -ENOMEM; - goto release_adapter; - } - hdmi->syscfg = devm_ioremap_nocache(dev, res->start, - resource_size(res)); - if (!hdmi->syscfg) { - ret = -ENOMEM; - goto release_adapter; - } - } - hdmi->phy_ops = (struct hdmi_phy_ops *) of_match_node(hdmi_of_match, np)->data; diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c deleted file mode 100644 index 49ae8e4..0000000 --- a/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) STMicroelectronics SA 2014 - * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 - */ - -#include "sti_hdmi_tx3g0c55phy.h" - -#define HDMI_SRZ_PLL_CFG 0x0504 -#define HDMI_SRZ_TAP_1 0x0508 -#define HDMI_SRZ_TAP_2 0x050C -#define HDMI_SRZ_TAP_3 0x0510 -#define HDMI_SRZ_CTRL 0x0514 - -#define HDMI_SRZ_PLL_CFG_POWER_DOWN BIT(0) -#define HDMI_SRZ_PLL_CFG_VCOR_SHIFT 1 -#define HDMI_SRZ_PLL_CFG_VCOR_425MHZ 0 -#define HDMI_SRZ_PLL_CFG_VCOR_850MHZ 1 -#define HDMI_SRZ_PLL_CFG_VCOR_1700MHZ 2 -#define HDMI_SRZ_PLL_CFG_VCOR_3000MHZ 3 -#define HDMI_SRZ_PLL_CFG_VCOR_MASK 3 -#define HDMI_SRZ_PLL_CFG_VCOR(x) (x << HDMI_SRZ_PLL_CFG_VCOR_SHIFT) -#define HDMI_SRZ_PLL_CFG_NDIV_SHIFT 8 -#define HDMI_SRZ_PLL_CFG_NDIV_MASK (0x1F << HDMI_SRZ_PLL_CFG_NDIV_SHIFT) -#define HDMI_SRZ_PLL_CFG_MODE_SHIFT 16 -#define HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ 0x1 -#define HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ 0x4 -#define HDMI_SRZ_PLL_CFG_MODE_27_MHZ 0x5 -#define HDMI_SRZ_PLL_CFG_MODE_33_75_MHZ 0x6 -#define HDMI_SRZ_PLL_CFG_MODE_40_5_MHZ 0x7 -#define HDMI_SRZ_PLL_CFG_MODE_54_MHZ 0x8 -#define HDMI_SRZ_PLL_CFG_MODE_67_5_MHZ 0x9 -#define HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ 0xA -#define HDMI_SRZ_PLL_CFG_MODE_81_MHZ 0xB -#define HDMI_SRZ_PLL_CFG_MODE_82_5_MHZ 0xC -#define HDMI_SRZ_PLL_CFG_MODE_108_MHZ 0xD -#define HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ 0xE -#define HDMI_SRZ_PLL_CFG_MODE_165_MHZ 0xF -#define HDMI_SRZ_PLL_CFG_MODE_MASK 0xF -#define HDMI_SRZ_PLL_CFG_MODE(x) (x << HDMI_SRZ_PLL_CFG_MODE_SHIFT) - -#define HDMI_SRZ_CTRL_POWER_DOWN (1 << 0) -#define HDMI_SRZ_CTRL_EXTERNAL_DATA_EN (1 << 1) - -/* sysconf registers */ -#define HDMI_REJECTION_PLL_CONFIGURATION 0x0858 /* SYSTEM_CONFIG2534 */ -#define HDMI_REJECTION_PLL_STATUS 0x0948 /* SYSTEM_CONFIG2594 */ - -#define REJECTION_PLL_HDMI_ENABLE_SHIFT 0 -#define REJECTION_PLL_HDMI_ENABLE_MASK (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT) -#define REJECTION_PLL_HDMI_PDIV_SHIFT 24 -#define REJECTION_PLL_HDMI_PDIV_MASK (0x7 << REJECTION_PLL_HDMI_PDIV_SHIFT) -#define REJECTION_PLL_HDMI_NDIV_SHIFT 16 -#define REJECTION_PLL_HDMI_NDIV_MASK (0xFF << REJECTION_PLL_HDMI_NDIV_SHIFT) -#define REJECTION_PLL_HDMI_MDIV_SHIFT 8 -#define REJECTION_PLL_HDMI_MDIV_MASK (0xFF << REJECTION_PLL_HDMI_MDIV_SHIFT) - -#define REJECTION_PLL_HDMI_REJ_PLL_LOCK BIT(0) - -#define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */ - -/** - * pll mode structure - * - * A pointer to an array of these structures is passed to a TMDS (HDMI) output - * via the control interface to provide board and SoC specific - * configurations of the HDMI PHY. Each entry in the array specifies a hardware - * specific configuration for a given TMDS clock frequency range. The array - * should be terminated with an entry that has all fields set to zero. - * - * @min: Lower bound of TMDS clock frequency this entry applies to - * @max: Upper bound of TMDS clock frequency this entry applies to - * @mode: SoC specific register configuration - */ -struct pllmode { - u32 min; - u32 max; - u32 mode; -}; - -#define NB_PLL_MODE 7 -static struct pllmode pllmodes[NB_PLL_MODE] = { - {13500000, 13513500, HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ}, - {25174800, 25200000, HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ}, - {27000000, 27027000, HDMI_SRZ_PLL_CFG_MODE_27_MHZ}, - {54000000, 54054000, HDMI_SRZ_PLL_CFG_MODE_54_MHZ}, - {72000000, 74250000, HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ}, - {108000000, 108108000, HDMI_SRZ_PLL_CFG_MODE_108_MHZ}, - {148351648, 297000000, HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ} -}; - -#define NB_HDMI_PHY_CONFIG 5 -static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = { - {0, 40000000, {0x00101010, 0x00101010, 0x00101010, 0x02} }, - {40000000, 140000000, {0x00111111, 0x00111111, 0x00111111, 0x02} }, - {140000000, 160000000, {0x00131313, 0x00101010, 0x00101010, 0x02} }, - {160000000, 250000000, {0x00131313, 0x00111111, 0x00111111, 0x03FE} }, - {250000000, 300000000, {0x00151515, 0x00101010, 0x00101010, 0x03FE} }, -}; - -#define PLL_CHANGE_DELAY 1 /* ms */ - -/** - * Disable the pll rejection - * - * @hdmi: pointer on the hdmi internal structure - * - * return true if the pll has been disabled - */ -static bool disable_pll_rejection(struct sti_hdmi *hdmi) -{ - u32 val; - - DRM_DEBUG_DRIVER("\n"); - - val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION); - val &= ~REJECTION_PLL_HDMI_ENABLE_MASK; - writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION); - - msleep(PLL_CHANGE_DELAY); - val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS); - - return !(val & REJECTION_PLL_HDMI_REJ_PLL_LOCK); -} - -/** - * Enable the old BCH/rejection PLL is now reused to provide the CLKPXPLL - * clock input to the new PHY PLL that generates the serializer clock - * (TMDS*10) and the TMDS clock which is now fed back into the HDMI - * formatter instead of the TMDS clock line from ClockGenB. - * - * @hdmi: pointer on the hdmi internal structure - * - * return true if pll has been correctly set - */ -static bool enable_pll_rejection(struct sti_hdmi *hdmi) -{ - unsigned int inputclock; - u32 mdiv, ndiv, pdiv, val; - - DRM_DEBUG_DRIVER("\n"); - - if (!disable_pll_rejection(hdmi)) - return false; - - inputclock = hdmi->mode.clock * 1000; - - DRM_DEBUG_DRIVER("hdmi rejection pll input clock = %dHz\n", inputclock); - - - /* Power up the HDMI rejection PLL - * Note: On this SoC (stiH416) we are forced to have the input clock - * be equal to the HDMI pixel clock. - * - * The values here have been suggested by validation however they are - * still provisional and subject to change. - * - * PLLout = (Fin*Mdiv) / ((2 * Ndiv) / 2^Pdiv) - */ - if (inputclock < 50000000) { - /* - * For slower clocks we need to multiply more to keep the - * internal VCO frequency within the physical specification - * of the PLL. - */ - pdiv = 4; - ndiv = 240; - mdiv = 30; - } else { - pdiv = 2; - ndiv = 60; - mdiv = 30; - } - - val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION); - - val &= ~(REJECTION_PLL_HDMI_PDIV_MASK | - REJECTION_PLL_HDMI_NDIV_MASK | - REJECTION_PLL_HDMI_MDIV_MASK | - REJECTION_PLL_HDMI_ENABLE_MASK); - - val |= (pdiv << REJECTION_PLL_HDMI_PDIV_SHIFT) | - (ndiv << REJECTION_PLL_HDMI_NDIV_SHIFT) | - (mdiv << REJECTION_PLL_HDMI_MDIV_SHIFT) | - (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT); - - writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION); - - msleep(PLL_CHANGE_DELAY); - val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS); - - return (val & REJECTION_PLL_HDMI_REJ_PLL_LOCK); -} - -/** - * Start hdmi phy macro cell tx3g0c55 - * - * @hdmi: pointer on the hdmi internal structure - * - * Return false if an error occur - */ -static bool sti_hdmi_tx3g0c55phy_start(struct sti_hdmi *hdmi) -{ - u32 ckpxpll = hdmi->mode.clock * 1000; - u32 val, tmdsck, freqvco, pllctrl = 0; - unsigned int i; - - if (!enable_pll_rejection(hdmi)) - return false; - - DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll); - - /* Assuming no pixel repetition and 24bits color */ - tmdsck = ckpxpll; - pllctrl = 2 << HDMI_SRZ_PLL_CFG_NDIV_SHIFT; - - /* - * Setup the PLL mode parameter based on the ckpxpll. If we haven't got - * a clock frequency supported by one of the specific PLL modes then we - * will end up using the generic mode (0) which only supports a 10x - * multiplier, hence only 24bit color. - */ - for (i = 0; i < NB_PLL_MODE; i++) { - if (ckpxpll >= pllmodes[i].min && ckpxpll <= pllmodes[i].max) - pllctrl |= HDMI_SRZ_PLL_CFG_MODE(pllmodes[i].mode); - } - - freqvco = tmdsck * 10; - if (freqvco <= 425000000UL) - pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_425MHZ); - else if (freqvco <= 850000000UL) - pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_850MHZ); - else if (freqvco <= 1700000000UL) - pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_1700MHZ); - else if (freqvco <= 2970000000UL) - pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_3000MHZ); - else { - DRM_ERROR("PHY serializer clock out of range\n"); - goto err; - } - - /* - * Configure and power up the PHY PLL - */ - hdmi->event_received = false; - DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl); - hdmi_write(hdmi, pllctrl, HDMI_SRZ_PLL_CFG); - - /* wait PLL interrupt */ - wait_event_interruptible_timeout(hdmi->wait_event, - hdmi->event_received == true, - msecs_to_jiffies - (HDMI_TIMEOUT_PLL_LOCK)); - - if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) { - DRM_ERROR("hdmi phy pll not locked\n"); - goto err; - } - - DRM_DEBUG_DRIVER("got PHY PLL Lock\n"); - - /* - * To configure the source termination and pre-emphasis appropriately - * for different high speed TMDS clock frequencies a phy configuration - * table must be provided, tailored to the SoC and board combination. - */ - for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) { - if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) && - (hdmiphy_config[i].max_tmds_freq >= tmdsck)) { - val = hdmiphy_config[i].config[0]; - hdmi_write(hdmi, val, HDMI_SRZ_TAP_1); - val = hdmiphy_config[i].config[1]; - hdmi_write(hdmi, val, HDMI_SRZ_TAP_2); - val = hdmiphy_config[i].config[2]; - hdmi_write(hdmi, val, HDMI_SRZ_TAP_3); - val = hdmiphy_config[i].config[3]; - val |= HDMI_SRZ_CTRL_EXTERNAL_DATA_EN; - val &= ~HDMI_SRZ_CTRL_POWER_DOWN; - hdmi_write(hdmi, val, HDMI_SRZ_CTRL); - - DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x 0x%x\n", - hdmiphy_config[i].config[0], - hdmiphy_config[i].config[1], - hdmiphy_config[i].config[2], - hdmiphy_config[i].config[3]); - return true; - } - } - - /* - * Default, power up the serializer with no pre-emphasis or source - * termination. - */ - hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_1); - hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_2); - hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_3); - hdmi_write(hdmi, HDMI_SRZ_CTRL_EXTERNAL_DATA_EN, HDMI_SRZ_CTRL); - - return true; - -err: - disable_pll_rejection(hdmi); - - return false; -} - -/** - * Stop hdmi phy macro cell tx3g0c55 - * - * @hdmi: pointer on the hdmi internal structure - */ -static void sti_hdmi_tx3g0c55phy_stop(struct sti_hdmi *hdmi) -{ - DRM_DEBUG_DRIVER("\n"); - - hdmi->event_received = false; - - hdmi_write(hdmi, HDMI_SRZ_CTRL_POWER_DOWN, HDMI_SRZ_CTRL); - hdmi_write(hdmi, HDMI_SRZ_PLL_CFG_POWER_DOWN, HDMI_SRZ_PLL_CFG); - - /* wait PLL interrupt */ - wait_event_interruptible_timeout(hdmi->wait_event, - hdmi->event_received == true, - msecs_to_jiffies - (HDMI_TIMEOUT_PLL_LOCK)); - - if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) - DRM_ERROR("hdmi phy pll not well disabled\n"); - - disable_pll_rejection(hdmi); -} - -struct hdmi_phy_ops tx3g0c55phy_ops = { - .start = sti_hdmi_tx3g0c55phy_start, - .stop = sti_hdmi_tx3g0c55phy_stop, -}; diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h deleted file mode 100644 index 068237b..0000000 --- a/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (C) STMicroelectronics SA 2014 - * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 - */ - -#ifndef _STI_HDMI_TX3G0C55PHY_H_ -#define _STI_HDMI_TX3G0C55PHY_H_ - -#include "sti_hdmi.h" - -extern struct hdmi_phy_ops tx3g0c55phy_ops; - -#endif diff --git a/drivers/gpu/drm/sti/sti_hqvdp.c b/drivers/gpu/drm/sti/sti_hqvdp.c index b5ee783..f88130f 100644 --- a/drivers/gpu/drm/sti/sti_hqvdp.c +++ b/drivers/gpu/drm/sti/sti_hqvdp.c @@ -17,6 +17,7 @@ #include "sti_hqvdp_lut.h" #include "sti_plane.h" #include "sti_vtg.h" +#include "sti_drv.h" /* Firmware name */ #define HQVDP_FMW_NAME "hqvdp-stih407.bin" @@ -770,6 +771,7 @@ static void sti_hqvdp_disable(struct sti_hqvdp *hqvdp) DRM_ERROR("XP70 could not revert to idle\n"); hqvdp->plane.status = STI_PLANE_DISABLED; + hqvdp->xp70_initialized = false; } /** @@ -783,7 +785,7 @@ static void sti_hqvdp_disable(struct sti_hqvdp *hqvdp) * RETURNS: * 0 on success. */ -int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data) +static int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data) { struct sti_hqvdp *hqvdp = container_of(nb, struct sti_hqvdp, vtg_nb); int btm_cmd_offset, top_cmd_offest; @@ -1012,7 +1014,6 @@ static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane, struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane); struct drm_crtc *crtc = state->crtc; struct drm_framebuffer *fb = state->fb; - bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false; struct drm_crtc_state *crtc_state; struct drm_display_mode *mode; int dst_x, dst_y, dst_w, dst_h; @@ -1026,8 +1027,8 @@ static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane, mode = &crtc_state->mode; dst_x = state->crtc_x; dst_y = state->crtc_y; - dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); - dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); + dst_w = clamp_val(state->crtc_w, 0, mode->hdisplay - dst_x); + dst_h = clamp_val(state->crtc_h, 0, mode->vdisplay - dst_y); /* src_x are in 16.16 format */ src_x = state->src_x >> 16; src_y = state->src_y >> 16; @@ -1063,7 +1064,7 @@ static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane, return -EINVAL; } - if (first_prepare) { + if (!hqvdp->xp70_initialized) { /* Start HQVDP XP70 coprocessor */ sti_hqvdp_start_xp70(hqvdp); @@ -1115,8 +1116,8 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, mode = &crtc->mode; dst_x = state->crtc_x; dst_y = state->crtc_y; - dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); - dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); + dst_w = clamp_val(state->crtc_w, 0, mode->hdisplay - dst_x); + dst_h = clamp_val(state->crtc_h, 0, mode->vdisplay - dst_y); /* src_x are in 16.16 format */ src_x = state->src_x >> 16; src_y = state->src_y >> 16; @@ -1214,15 +1215,15 @@ static void sti_hqvdp_atomic_disable(struct drm_plane *drm_plane, { struct sti_plane *plane = to_sti_plane(drm_plane); - if (!drm_plane->crtc) { + if (!oldstate->crtc) { DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", drm_plane->base.id); return; } DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", - drm_plane->crtc->base.id, - sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)), + oldstate->crtc->base.id, + sti_mixer_to_str(to_sti_mixer(oldstate->crtc)), drm_plane->base.id, sti_plane_to_str(plane)); plane->status = STI_PLANE_DISABLING; @@ -1250,7 +1251,7 @@ static int sti_hqvdp_late_register(struct drm_plane *drm_plane) return hqvdp_debugfs_init(hqvdp, drm_plane->dev->primary); } -struct drm_plane_funcs sti_hqvdp_plane_helpers_funcs = { +static const struct drm_plane_funcs sti_hqvdp_plane_helpers_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = sti_hqvdp_destroy, @@ -1289,7 +1290,7 @@ static struct drm_plane *sti_hqvdp_create(struct drm_device *drm_dev, return &hqvdp->plane.drm_plane; } -int sti_hqvdp_bind(struct device *dev, struct device *master, void *data) +static int sti_hqvdp_bind(struct device *dev, struct device *master, void *data) { struct sti_hqvdp *hqvdp = dev_get_drvdata(dev); struct drm_device *drm_dev = data; diff --git a/drivers/gpu/drm/sti/sti_mixer.c b/drivers/gpu/drm/sti/sti_mixer.c index 7d9aea8..4ddc58f 100644 --- a/drivers/gpu/drm/sti/sti_mixer.c +++ b/drivers/gpu/drm/sti/sti_mixer.c @@ -16,12 +16,6 @@ static unsigned int bkg_color = 0x000000; MODULE_PARM_DESC(bkgcolor, "Value of the background color 0xRRGGBB"); module_param_named(bkgcolor, bkg_color, int, 0644); -/* Identity: G=Y , B=Cb , R=Cr */ -static const u32 mixerColorSpaceMatIdentity[] = { - 0x10000000, 0x00000000, 0x10000000, 0x00001000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000 -}; - /* regs offset */ #define GAM_MIXER_CTL 0x00 #define GAM_MIXER_BKC 0x04 @@ -358,22 +352,12 @@ int sti_mixer_set_plane_status(struct sti_mixer *mixer, return 0; } -void sti_mixer_set_matrix(struct sti_mixer *mixer) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(mixerColorSpaceMatIdentity); i++) - sti_mixer_reg_write(mixer, GAM_MIXER_MX0 + (i * 4), - mixerColorSpaceMatIdentity[i]); -} - struct sti_mixer *sti_mixer_create(struct device *dev, struct drm_device *drm_dev, int id, void __iomem *baseaddr) { struct sti_mixer *mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL); - struct device_node *np = dev->of_node; dev_dbg(dev, "%s\n", __func__); if (!mixer) { @@ -384,9 +368,6 @@ struct sti_mixer *sti_mixer_create(struct device *dev, mixer->dev = dev; mixer->id = id; - if (of_device_is_compatible(np, "st,stih416-compositor")) - sti_mixer_set_matrix(mixer); - DRM_DEBUG_DRIVER("%s created. Regs=%p\n", sti_mixer_to_str(mixer), mixer->regs); diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c index e25995b..ad46d35 100644 --- a/drivers/gpu/drm/sti/sti_tvout.c +++ b/drivers/gpu/drm/sti/sti_tvout.c @@ -18,6 +18,7 @@ #include <drm/drm_crtc_helper.h> #include "sti_crtc.h" +#include "sti_drv.h" #include "sti_vtg.h" /* glue registers */ @@ -209,13 +210,11 @@ static void tvout_vip_set_rnd(struct sti_tvout *tvout, int reg, u32 rnd) * @tvout: tvout structure * @reg: register to set * @main_path: main or auxiliary path - * @sel_input_logic_inverted: need to invert the logic * @sel_input: selected_input (main/aux + conv) */ static void tvout_vip_set_sel_input(struct sti_tvout *tvout, int reg, bool main_path, - bool sel_input_logic_inverted, enum sti_tvout_video_out_type video_out) { u32 sel_input; @@ -236,8 +235,7 @@ static void tvout_vip_set_sel_input(struct sti_tvout *tvout, } /* on stih407 chip the sel_input bypass mode logic is inverted */ - if (sel_input_logic_inverted) - sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK; + sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK; val &= ~TVO_VIP_SEL_INPUT_MASK; val |= sel_input; @@ -295,8 +293,6 @@ static void tvout_preformatter_set_matrix(struct sti_tvout *tvout, */ static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path) { - struct device_node *node = tvout->dev->of_node; - bool sel_input_logic_inverted = false; u32 tvo_in_vid_format; int val, tmp; @@ -334,16 +330,11 @@ static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path) /* Set round mode (rounded to 8-bit per component) */ tvout_vip_set_rnd(tvout, TVO_VIP_DVO, TVO_VIP_RND_8BIT_ROUNDED); - if (of_device_is_compatible(node, "st,stih407-tvout")) { - /* Set input video format */ - tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, - TVO_IN_FMT_SIGNED); - sel_input_logic_inverted = true; - } + /* Set input video format */ + tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED); /* Input selection */ tvout_vip_set_sel_input(tvout, TVO_VIP_DVO, main_path, - sel_input_logic_inverted, STI_TVOUT_VIDEO_OUT_RGB); } @@ -356,8 +347,6 @@ static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path) */ static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path) { - struct device_node *node = tvout->dev->of_node; - bool sel_input_logic_inverted = false; u32 tvo_in_vid_format; dev_dbg(tvout->dev, "%s\n", __func__); @@ -390,16 +379,12 @@ static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path) /* set round mode (rounded to 8-bit per component) */ tvout_vip_set_rnd(tvout, TVO_VIP_HDMI, TVO_VIP_RND_8BIT_ROUNDED); - if (of_device_is_compatible(node, "st,stih407-tvout")) { - /* set input video format */ - tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, - TVO_IN_FMT_SIGNED); - sel_input_logic_inverted = true; - } + /* set input video format */ + tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED); /* input selection */ tvout_vip_set_sel_input(tvout, TVO_VIP_HDMI, main_path, - sel_input_logic_inverted, STI_TVOUT_VIDEO_OUT_RGB); + STI_TVOUT_VIDEO_OUT_RGB); } /** @@ -411,8 +396,6 @@ static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path) */ static void tvout_hda_start(struct sti_tvout *tvout, bool main_path) { - struct device_node *node = tvout->dev->of_node; - bool sel_input_logic_inverted = false; u32 tvo_in_vid_format; int val; @@ -448,16 +431,11 @@ static void tvout_hda_start(struct sti_tvout *tvout, bool main_path) /* set round mode (rounded to 10-bit per component) */ tvout_vip_set_rnd(tvout, TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED); - if (of_device_is_compatible(node, "st,stih407-tvout")) { - /* set input video format */ - tvout_vip_set_in_vid_fmt(tvout, - tvo_in_vid_format, TVO_IN_FMT_SIGNED); - sel_input_logic_inverted = true; - } + /* Set input video format */ + tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED); /* Input selection */ tvout_vip_set_sel_input(tvout, TVO_VIP_HDF, main_path, - sel_input_logic_inverted, STI_TVOUT_VIDEO_OUT_YUV); /* power up HD DAC */ @@ -905,7 +883,6 @@ static int sti_tvout_remove(struct platform_device *pdev) } static const struct of_device_id tvout_of_match[] = { - { .compatible = "st,stih416-tvout", }, { .compatible = "st,stih407-tvout", }, { /* end node */ } }; diff --git a/drivers/gpu/drm/sti/sti_vid.c b/drivers/gpu/drm/sti/sti_vid.c index 47634a0..2ad5989 100644 --- a/drivers/gpu/drm/sti/sti_vid.c +++ b/drivers/gpu/drm/sti/sti_vid.c @@ -142,8 +142,8 @@ void sti_vid_commit(struct sti_vid *vid, struct drm_display_mode *mode = &crtc->mode; int dst_x = state->crtc_x; int dst_y = state->crtc_y; - int dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); - int dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); + int dst_w = clamp_val(state->crtc_w, 0, mode->hdisplay - dst_x); + int dst_h = clamp_val(state->crtc_h, 0, mode->vdisplay - dst_y); int src_h = state->src_h >> 16; u32 val, ydo, xdo, yds, xds; diff --git a/drivers/gpu/drm/sti/sti_vtac.c b/drivers/gpu/drm/sti/sti_vtac.c index b1eb0d7..cf7fe8a 100644 --- a/drivers/gpu/drm/sti/sti_vtac.c +++ b/drivers/gpu/drm/sti/sti_vtac.c @@ -12,6 +12,8 @@ #include <drm/drmP.h> +#include "sti_drv.h" + /* registers offset */ #define VTAC_CONFIG 0x00 #define VTAC_RX_FIFO_CONFIG 0x04 diff --git a/drivers/gpu/drm/sti/sti_vtg.c b/drivers/gpu/drm/sti/sti_vtg.c index 0bdc385..a8882bd 100644 --- a/drivers/gpu/drm/sti/sti_vtg.c +++ b/drivers/gpu/drm/sti/sti_vtg.c @@ -13,6 +13,7 @@ #include <drm/drmP.h> +#include "sti_drv.h" #include "sti_vtg.h" #define VTG_MODE_MASTER 0 @@ -72,7 +73,7 @@ #define AWG_DELAY_ED (-8) #define AWG_DELAY_SD (-7) -LIST_HEAD(vtg_lookup); +static LIST_HEAD(vtg_lookup); /* * STI VTG register offset structure diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile index 58cd551..d625a82 100644 --- a/drivers/gpu/drm/sun4i/Makefile +++ b/drivers/gpu/drm/sun4i/Makefile @@ -9,5 +9,5 @@ sun4i-tcon-y += sun4i_dotclock.o obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o obj-$(CONFIG_DRM_SUN4I) += sun4i_backend.o - +obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index 3ab5604..32c0584 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -83,8 +83,13 @@ void sun4i_backend_layer_enable(struct sun4i_backend *backend, } EXPORT_SYMBOL(sun4i_backend_layer_enable); -static int sun4i_backend_drm_format_to_layer(u32 format, u32 *mode) +static int sun4i_backend_drm_format_to_layer(struct drm_plane *plane, + u32 format, u32 *mode) { + if ((plane->type == DRM_PLANE_TYPE_PRIMARY) && + (format == DRM_FORMAT_ARGB8888)) + format = DRM_FORMAT_XRGB8888; + switch (format) { case DRM_FORMAT_ARGB8888: *mode = SUN4I_BACKEND_LAY_FBFMT_ARGB8888; @@ -164,7 +169,7 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n", interlaced ? "on" : "off"); - ret = sun4i_backend_drm_format_to_layer(fb->pixel_format, &val); + ret = sun4i_backend_drm_format_to_layer(plane, fb->pixel_format, &val); if (ret) { DRM_DEBUG_DRIVER("Invalid format\n"); return val; @@ -217,6 +222,51 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, } EXPORT_SYMBOL(sun4i_backend_update_layer_buffer); +static int sun4i_backend_init_sat(struct device *dev) { + struct sun4i_backend *backend = dev_get_drvdata(dev); + int ret; + + backend->sat_reset = devm_reset_control_get(dev, "sat"); + if (IS_ERR(backend->sat_reset)) { + dev_err(dev, "Couldn't get the SAT reset line\n"); + return PTR_ERR(backend->sat_reset); + } + + ret = reset_control_deassert(backend->sat_reset); + if (ret) { + dev_err(dev, "Couldn't deassert the SAT reset line\n"); + return ret; + } + + backend->sat_clk = devm_clk_get(dev, "sat"); + if (IS_ERR(backend->sat_clk)) { + dev_err(dev, "Couldn't get our SAT clock\n"); + ret = PTR_ERR(backend->sat_clk); + goto err_assert_reset; + } + + ret = clk_prepare_enable(backend->sat_clk); + if (ret) { + dev_err(dev, "Couldn't enable the SAT clock\n"); + return ret; + } + + return 0; + +err_assert_reset: + reset_control_assert(backend->sat_reset); + return ret; +} + +static int sun4i_backend_free_sat(struct device *dev) { + struct sun4i_backend *backend = dev_get_drvdata(dev); + + clk_disable_unprepare(backend->sat_clk); + reset_control_assert(backend->sat_reset); + + return 0; +} + static struct regmap_config sun4i_backend_regmap_config = { .reg_bits = 32, .val_bits = 32, @@ -243,10 +293,8 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(dev, res); - if (IS_ERR(regs)) { - dev_err(dev, "Couldn't map the backend registers\n"); + if (IS_ERR(regs)) return PTR_ERR(regs); - } backend->regs = devm_regmap_init_mmio(dev, regs, &sun4i_backend_regmap_config); @@ -291,6 +339,15 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, } clk_prepare_enable(backend->ram_clk); + if (of_device_is_compatible(dev->of_node, + "allwinner,sun8i-a33-display-backend")) { + ret = sun4i_backend_init_sat(dev); + if (ret) { + dev_err(dev, "Couldn't init SAT resources\n"); + goto err_disable_ram_clk; + } + } + /* Reset the registers */ for (i = 0x800; i < 0x1000; i += 4) regmap_write(backend->regs, i, 0); @@ -306,6 +363,8 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, return 0; +err_disable_ram_clk: + clk_disable_unprepare(backend->ram_clk); err_disable_mod_clk: clk_disable_unprepare(backend->mod_clk); err_disable_bus_clk: @@ -320,6 +379,10 @@ static void sun4i_backend_unbind(struct device *dev, struct device *master, { struct sun4i_backend *backend = dev_get_drvdata(dev); + if (of_device_is_compatible(dev->of_node, + "allwinner,sun8i-a33-display-backend")) + sun4i_backend_free_sat(dev); + clk_disable_unprepare(backend->ram_clk); clk_disable_unprepare(backend->mod_clk); clk_disable_unprepare(backend->bus_clk); @@ -345,6 +408,7 @@ static int sun4i_backend_remove(struct platform_device *pdev) static const struct of_device_id sun4i_backend_of_table[] = { { .compatible = "allwinner,sun5i-a13-display-backend" }, + { .compatible = "allwinner,sun8i-a33-display-backend" }, { } }; MODULE_DEVICE_TABLE(of, sun4i_backend_of_table); diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h index 7070bb3..83e63cc 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.h +++ b/drivers/gpu/drm/sun4i/sun4i_backend.h @@ -52,8 +52,8 @@ #define SUN4I_BACKEND_LAYFB_L32ADD_REG(l) (0x850 + (0x4 * (l))) #define SUN4I_BACKEND_LAYFB_H4ADD_REG 0x860 -#define SUN4I_BACKEND_LAYFB_H4ADD_MSK(l) GENMASK(3 + ((l) * 8), 0) -#define SUN4I_BACKEND_LAYFB_H4ADD(l, val) ((val) << ((l) * 8)) +#define SUN4I_BACKEND_LAYFB_H4ADD_MSK(l) GENMASK(3 + ((l) * 8), (l) * 8) +#define SUN4I_BACKEND_LAYFB_H4ADD(l, val) ((val) << ((l) * 8)) #define SUN4I_BACKEND_REGBUFFCTL_REG 0x870 #define SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS BIT(1) @@ -146,6 +146,9 @@ struct sun4i_backend { struct clk *bus_clk; struct clk *mod_clk; struct clk *ram_clk; + + struct clk *sat_clk; + struct reset_control *sat_reset; }; void sun4i_backend_apply_color_correction(struct sun4i_backend *backend); diff --git a/drivers/gpu/drm/sun4i/sun4i_dotclock.c b/drivers/gpu/drm/sun4i/sun4i_dotclock.c index 5b34631..d401156 100644 --- a/drivers/gpu/drm/sun4i/sun4i_dotclock.c +++ b/drivers/gpu/drm/sun4i/sun4i_dotclock.c @@ -14,6 +14,7 @@ #include <linux/regmap.h> #include "sun4i_tcon.h" +#include "sun4i_dotclock.h" struct sun4i_dclk { struct clk_hw hw; @@ -61,7 +62,7 @@ static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw, regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val); val >>= SUN4I_TCON0_DCLK_DIV_SHIFT; - val &= SUN4I_TCON0_DCLK_DIV_WIDTH; + val &= (1 << SUN4I_TCON0_DCLK_DIV_WIDTH) - 1; if (!val) val = 1; @@ -76,7 +77,7 @@ static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate, u8 best_div = 1; int i; - for (i = 6; i < 127; i++) { + for (i = 6; i <= 127; i++) { unsigned long ideal = rate * i; unsigned long rounded; @@ -89,7 +90,8 @@ static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate, goto out; } - if ((rounded < ideal) && (rounded > best_parent)) { + if (abs(rate - rounded / i) < + abs(rate - best_parent / best_div)) { best_parent = rounded; best_div = i; } diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 7092daa..0da9862 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -17,6 +17,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> +#include <drm/drm_fb_helper.h> #include "sun4i_crtc.h" #include "sun4i_drv.h" @@ -109,7 +110,7 @@ static void sun4i_remove_framebuffers(void) ap->ranges[0].base = 0; ap->ranges[0].size = ~0; - remove_conflicting_framebuffers(ap, "sun4i-drm-fb", false); + drm_fb_helper_remove_conflicting_framebuffers(ap, "sun4i-drm-fb", false); kfree(ap); } @@ -120,8 +121,8 @@ static int sun4i_drv_bind(struct device *dev) int ret; drm = drm_dev_alloc(&sun4i_drv_driver, dev); - if (!drm) - return -ENOMEM; + if (IS_ERR(drm)) + return PTR_ERR(drm); drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); if (!drv) { @@ -199,13 +200,14 @@ static const struct component_master_ops sun4i_drv_master_ops = { static bool sun4i_drv_node_is_frontend(struct device_node *node) { - return of_device_is_compatible(node, - "allwinner,sun5i-a13-display-frontend"); + return of_device_is_compatible(node, "allwinner,sun5i-a13-display-frontend") || + of_device_is_compatible(node, "allwinner,sun8i-a33-display-frontend"); } static bool sun4i_drv_node_is_tcon(struct device_node *node) { - return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon"); + return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") || + of_device_is_compatible(node, "allwinner,sun8i-a33-tcon"); } static int compare_of(struct device *dev, void *data) @@ -257,8 +259,8 @@ static int sun4i_drv_add_endpoints(struct device *dev, } /* - * If the node is our TCON, the first port is used for our - * panel, and will not be part of the + * If the node is our TCON, the first port is used for + * panel or bridges, and will not be part of the * component framework. */ if (sun4i_drv_node_is_tcon(node)) { @@ -320,6 +322,7 @@ static int sun4i_drv_remove(struct platform_device *pdev) static const struct of_device_id sun4i_drv_of_table[] = { { .compatible = "allwinner,sun5i-a13-display-engine" }, + { .compatible = "allwinner,sun8i-a33-display-engine" }, { } }; MODULE_DEVICE_TABLE(of, sun4i_drv_of_table); diff --git a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c index 70688fe..8b6ce619 100644 --- a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c +++ b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c @@ -15,6 +15,7 @@ #include <drm/drmP.h> #include "sun4i_drv.h" +#include "sun4i_framebuffer.h" static void sun4i_de_output_poll_changed(struct drm_device *drm) { diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c index 068ab80..f0035bf 100644 --- a/drivers/gpu/drm/sun4i/sun4i_layer.c +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c @@ -19,7 +19,12 @@ #include "sun4i_drv.h" #include "sun4i_layer.h" -#define SUN4I_NUM_LAYERS 2 +struct sun4i_plane_desc { + enum drm_plane_type type; + u8 pipe; + const uint32_t *formats; + uint32_t nformats; +}; static int sun4i_backend_layer_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) @@ -65,14 +70,35 @@ static const struct drm_plane_funcs sun4i_backend_layer_funcs = { .update_plane = drm_atomic_helper_update_plane, }; -static const uint32_t sun4i_backend_layer_formats[] = { +static const uint32_t sun4i_backend_layer_formats_primary[] = { DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_XRGB8888, +}; + +static const uint32_t sun4i_backend_layer_formats_overlay[] = { + DRM_FORMAT_ARGB8888, DRM_FORMAT_RGB888, + DRM_FORMAT_XRGB8888, +}; + +static const struct sun4i_plane_desc sun4i_backend_planes[] = { + { + .type = DRM_PLANE_TYPE_PRIMARY, + .pipe = 0, + .formats = sun4i_backend_layer_formats_primary, + .nformats = ARRAY_SIZE(sun4i_backend_layer_formats_primary), + }, + { + .type = DRM_PLANE_TYPE_OVERLAY, + .pipe = 1, + .formats = sun4i_backend_layer_formats_overlay, + .nformats = ARRAY_SIZE(sun4i_backend_layer_formats_overlay), + }, }; static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm, - enum drm_plane_type type) + const struct sun4i_plane_desc *plane) { struct sun4i_drv *drv = drm->dev_private; struct sun4i_layer *layer; @@ -84,10 +110,8 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm, ret = drm_universal_plane_init(drm, &layer->plane, BIT(0), &sun4i_backend_layer_funcs, - sun4i_backend_layer_formats, - ARRAY_SIZE(sun4i_backend_layer_formats), - type, - NULL); + plane->formats, plane->nformats, + plane->type, NULL); if (ret) { dev_err(drm->dev, "Couldn't initialize layer\n"); return ERR_PTR(ret); @@ -97,7 +121,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm, &sun4i_backend_layer_helper_funcs); layer->drv = drv; - if (type == DRM_PLANE_TYPE_PRIMARY) + if (plane->type == DRM_PLANE_TYPE_PRIMARY) drv->primary = &layer->plane; return layer; @@ -109,8 +133,8 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm) struct sun4i_layer **layers; int i; - layers = devm_kcalloc(drm->dev, SUN4I_NUM_LAYERS, sizeof(**layers), - GFP_KERNEL); + layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun4i_backend_planes), + sizeof(**layers), GFP_KERNEL); if (!layers) return ERR_PTR(-ENOMEM); @@ -135,13 +159,11 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm) * SoCs that support it, sprites could fill the need for more * layers. */ - for (i = 0; i < SUN4I_NUM_LAYERS; i++) { - enum drm_plane_type type = (i == 0) - ? DRM_PLANE_TYPE_PRIMARY - : DRM_PLANE_TYPE_OVERLAY; + for (i = 0; i < ARRAY_SIZE(sun4i_backend_planes); i++) { + const struct sun4i_plane_desc *plane = &sun4i_backend_planes[i]; struct sun4i_layer *layer = layers[i]; - layer = sun4i_layer_init_one(drm, type); + layer = sun4i_layer_init_one(drm, plane); if (IS_ERR(layer)) { dev_err(drm->dev, "Couldn't initialize %s plane\n", i ? "overlay" : "primary"); @@ -149,10 +171,10 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm) }; DRM_DEBUG_DRIVER("Assigning %s plane to pipe %d\n", - i ? "overlay" : "primary", i); + i ? "overlay" : "primary", plane->pipe); regmap_update_bits(drv->backend->regs, SUN4I_BACKEND_ATTCTL_REG0(i), SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL_MASK, - SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(i)); + SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(plane->pipe)); layer->id = i; }; diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c index f5bbac6..c3ff10f 100644 --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c @@ -19,6 +19,7 @@ #include "sun4i_drv.h" #include "sun4i_tcon.h" +#include "sun4i_rgb.h" struct sun4i_rgb { struct drm_connector connector; @@ -151,7 +152,14 @@ static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder) DRM_DEBUG_DRIVER("Enabling RGB output\n"); - drm_panel_enable(tcon->panel); + if (!IS_ERR(tcon->panel)) { + drm_panel_prepare(tcon->panel); + drm_panel_enable(tcon->panel); + } + + /* encoder->bridge can be NULL; drm_bridge_enable checks for it */ + drm_bridge_enable(encoder->bridge); + sun4i_tcon_channel_enable(tcon, 0); } @@ -164,7 +172,14 @@ static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder) DRM_DEBUG_DRIVER("Disabling RGB output\n"); sun4i_tcon_channel_disable(tcon, 0); - drm_panel_disable(tcon->panel); + + /* encoder->bridge can be NULL; drm_bridge_disable checks for it */ + drm_bridge_disable(encoder->bridge); + + if (!IS_ERR(tcon->panel)) { + drm_panel_disable(tcon->panel); + drm_panel_unprepare(tcon->panel); + } } static void sun4i_rgb_encoder_mode_set(struct drm_encoder *encoder, @@ -203,17 +218,22 @@ int sun4i_rgb_init(struct drm_device *drm) { struct sun4i_drv *drv = drm->dev_private; struct sun4i_tcon *tcon = drv->tcon; + struct drm_encoder *encoder; struct sun4i_rgb *rgb; int ret; - /* If we don't have a panel, there's no point in going on */ - if (IS_ERR(tcon->panel)) - return -ENODEV; - rgb = devm_kzalloc(drm->dev, sizeof(*rgb), GFP_KERNEL); if (!rgb) return -ENOMEM; rgb->drv = drv; + encoder = &rgb->encoder; + + tcon->panel = sun4i_tcon_find_panel(tcon->dev->of_node); + encoder->bridge = sun4i_tcon_find_bridge(tcon->dev->of_node); + if (IS_ERR(tcon->panel) && IS_ERR(encoder->bridge)) { + dev_info(drm->dev, "No panel or bridge found... RGB output disabled\n"); + return 0; + } drm_encoder_helper_add(&rgb->encoder, &sun4i_rgb_enc_helper_funcs); @@ -230,19 +250,38 @@ int sun4i_rgb_init(struct drm_device *drm) /* The RGB encoder can only work with the TCON channel 0 */ rgb->encoder.possible_crtcs = BIT(0); - drm_connector_helper_add(&rgb->connector, - &sun4i_rgb_con_helper_funcs); - ret = drm_connector_init(drm, &rgb->connector, - &sun4i_rgb_con_funcs, - DRM_MODE_CONNECTOR_Unknown); - if (ret) { - dev_err(drm->dev, "Couldn't initialise the rgb connector\n"); - goto err_cleanup_connector; + if (!IS_ERR(tcon->panel)) { + drm_connector_helper_add(&rgb->connector, + &sun4i_rgb_con_helper_funcs); + ret = drm_connector_init(drm, &rgb->connector, + &sun4i_rgb_con_funcs, + DRM_MODE_CONNECTOR_Unknown); + if (ret) { + dev_err(drm->dev, "Couldn't initialise the rgb connector\n"); + goto err_cleanup_connector; + } + + drm_mode_connector_attach_encoder(&rgb->connector, + &rgb->encoder); + + ret = drm_panel_attach(tcon->panel, &rgb->connector); + if (ret) { + dev_err(drm->dev, "Couldn't attach our panel\n"); + goto err_cleanup_connector; + } } - drm_mode_connector_attach_encoder(&rgb->connector, &rgb->encoder); + if (!IS_ERR(encoder->bridge)) { + encoder->bridge->encoder = &rgb->encoder; - drm_panel_attach(tcon->panel, &rgb->connector); + ret = drm_bridge_attach(drm, encoder->bridge); + if (ret) { + dev_err(drm->dev, "Couldn't attach our bridge\n"); + goto err_cleanup_connector; + } + } else { + encoder->bridge = NULL; + } return 0; diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 652385f..cadacb5 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -59,11 +59,13 @@ void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel) regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, SUN4I_TCON0_CTL_TCON_ENABLE, 0); clk_disable_unprepare(tcon->dclk); - } else if (channel == 1) { - regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, - SUN4I_TCON1_CTL_TCON_ENABLE, 0); - clk_disable_unprepare(tcon->sclk1); + return; } + + WARN_ON(!tcon->has_channel_1); + regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, + SUN4I_TCON1_CTL_TCON_ENABLE, 0); + clk_disable_unprepare(tcon->sclk1); } EXPORT_SYMBOL(sun4i_tcon_channel_disable); @@ -75,12 +77,14 @@ void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel) SUN4I_TCON0_CTL_TCON_ENABLE, SUN4I_TCON0_CTL_TCON_ENABLE); clk_prepare_enable(tcon->dclk); - } else if (channel == 1) { - regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, - SUN4I_TCON1_CTL_TCON_ENABLE, - SUN4I_TCON1_CTL_TCON_ENABLE); - clk_prepare_enable(tcon->sclk1); + return; } + + WARN_ON(!tcon->has_channel_1); + regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, + SUN4I_TCON1_CTL_TCON_ENABLE, + SUN4I_TCON1_CTL_TCON_ENABLE); + clk_prepare_enable(tcon->sclk1); } EXPORT_SYMBOL(sun4i_tcon_channel_enable); @@ -198,6 +202,8 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, u8 clk_delay; u32 val; + WARN_ON(!tcon->has_channel_1); + /* Adjust clock delay */ clk_delay = sun4i_tcon_get_clk_delay(mode, 1); regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, @@ -321,10 +327,12 @@ static int sun4i_tcon_init_clocks(struct device *dev, return PTR_ERR(tcon->sclk0); } - tcon->sclk1 = devm_clk_get(dev, "tcon-ch1"); - if (IS_ERR(tcon->sclk1)) { - dev_err(dev, "Couldn't get the TCON channel 1 clock\n"); - return PTR_ERR(tcon->sclk1); + if (tcon->has_channel_1) { + tcon->sclk1 = devm_clk_get(dev, "tcon-ch1"); + if (IS_ERR(tcon->sclk1)) { + dev_err(dev, "Couldn't get the TCON channel 1 clock\n"); + return PTR_ERR(tcon->sclk1); + } } return sun4i_dclk_create(dev, tcon); @@ -374,10 +382,8 @@ static int sun4i_tcon_init_regmap(struct device *dev, res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(dev, res); - if (IS_ERR(regs)) { - dev_err(dev, "Couldn't map the TCON registers\n"); + if (IS_ERR(regs)) return PTR_ERR(regs); - } tcon->regs = devm_regmap_init_mmio(dev, regs, &sun4i_tcon_regmap_config); @@ -398,7 +404,7 @@ static int sun4i_tcon_init_regmap(struct device *dev, return 0; } -static struct drm_panel *sun4i_tcon_find_panel(struct device_node *node) +struct drm_panel *sun4i_tcon_find_panel(struct device_node *node) { struct device_node *port, *remote, *child; struct device_node *end_node = NULL; @@ -432,6 +438,40 @@ static struct drm_panel *sun4i_tcon_find_panel(struct device_node *node) return of_drm_find_panel(remote) ?: ERR_PTR(-EPROBE_DEFER); } +struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node) +{ + struct device_node *port, *remote, *child; + struct device_node *end_node = NULL; + + /* Inputs are listed first, then outputs */ + port = of_graph_get_port_by_id(node, 1); + + /* + * Our first output is the RGB interface where the panel will + * be connected. + */ + for_each_child_of_node(port, child) { + u32 reg; + + of_property_read_u32(child, "reg", ®); + if (reg == 0) + end_node = child; + } + + if (!end_node) { + DRM_DEBUG_DRIVER("Missing bridge endpoint\n"); + return ERR_PTR(-ENODEV); + } + + remote = of_graph_get_remote_port_parent(end_node); + if (!remote) { + DRM_DEBUG_DRIVER("Enable to parse remote node\n"); + return ERR_PTR(-EINVAL); + } + + return of_drm_find_bridge(remote) ?: ERR_PTR(-EPROBE_DEFER); +} + static int sun4i_tcon_bind(struct device *dev, struct device *master, void *data) { @@ -446,9 +486,15 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, dev_set_drvdata(dev, tcon); drv->tcon = tcon; tcon->drm = drm; + tcon->dev = dev; - if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon")) + if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon")) { tcon->has_mux = true; + tcon->has_channel_1 = true; + } else { + tcon->has_mux = false; + tcon->has_channel_1 = false; + } tcon->lcd_rst = devm_reset_control_get(dev, "lcd"); if (IS_ERR(tcon->lcd_rst)) { @@ -484,12 +530,6 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, goto err_free_clocks; } - tcon->panel = sun4i_tcon_find_panel(dev->of_node); - if (IS_ERR(tcon->panel)) { - dev_info(dev, "No panel found... RGB output disabled\n"); - return 0; - } - ret = sun4i_rgb_init(drm); if (ret < 0) goto err_free_clocks; @@ -519,19 +559,22 @@ static struct component_ops sun4i_tcon_ops = { static int sun4i_tcon_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; + struct drm_bridge *bridge; struct drm_panel *panel; /* - * The panel is not ready. + * Neither the bridge or the panel is ready. * Defer the probe. */ panel = sun4i_tcon_find_panel(node); + bridge = sun4i_tcon_find_bridge(node); /* * If we don't have a panel endpoint, just go on */ - if (PTR_ERR(panel) == -EPROBE_DEFER) { - DRM_DEBUG_DRIVER("Still waiting for our panel. Deferring...\n"); + if ((PTR_ERR(panel) == -EPROBE_DEFER) && + (PTR_ERR(bridge) == -EPROBE_DEFER)) { + DRM_DEBUG_DRIVER("Still waiting for our panel/bridge. Deferring...\n"); return -EPROBE_DEFER; } @@ -547,6 +590,7 @@ static int sun4i_tcon_remove(struct platform_device *pdev) static const struct of_device_id sun4i_tcon_of_table[] = { { .compatible = "allwinner,sun5i-a13-tcon" }, + { .compatible = "allwinner,sun8i-a33-tcon" }, { } }; MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table); diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h index 0e0b11d..12bd489 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.h +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h @@ -143,6 +143,7 @@ #define SUN4I_TCON_MAX_CHANNELS 2 struct sun4i_tcon { + struct device *dev; struct drm_device *drm; struct regmap *regs; @@ -163,8 +164,13 @@ struct sun4i_tcon { bool has_mux; struct drm_panel *panel; + + bool has_channel_1; }; +struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node); +struct drm_panel *sun4i_tcon_find_panel(struct device_node *node); + /* Global Control */ void sun4i_tcon_disable(struct sun4i_tcon *tcon); void sun4i_tcon_enable(struct sun4i_tcon *tcon); diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c index b841478..1dd3d9e 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tv.c +++ b/drivers/gpu/drm/sun4i/sun4i_tv.c @@ -161,10 +161,10 @@ struct tv_mode { bool dac3_en; bool dac_bit25_en; - struct color_gains *color_gains; - struct burst_levels *burst_levels; - struct video_levels *video_levels; - struct resync_parameters *resync_params; + const struct color_gains *color_gains; + const struct burst_levels *burst_levels; + const struct video_levels *video_levels; + const struct resync_parameters *resync_params; }; struct sun4i_tv { @@ -178,39 +178,39 @@ struct sun4i_tv { struct sun4i_drv *drv; }; -struct video_levels ntsc_video_levels = { +static const struct video_levels ntsc_video_levels = { .black = 282, .blank = 240, }; -struct video_levels pal_video_levels = { +static const struct video_levels pal_video_levels = { .black = 252, .blank = 252, }; -struct burst_levels ntsc_burst_levels = { +static const struct burst_levels ntsc_burst_levels = { .cb = 79, .cr = 0, }; -struct burst_levels pal_burst_levels = { +static const struct burst_levels pal_burst_levels = { .cb = 40, .cr = 40, }; -struct color_gains ntsc_color_gains = { +static const struct color_gains ntsc_color_gains = { .cb = 160, .cr = 160, }; -struct color_gains pal_color_gains = { +static const struct color_gains pal_color_gains = { .cb = 224, .cr = 224, }; -struct resync_parameters ntsc_resync_parameters = { +static const struct resync_parameters ntsc_resync_parameters = { .field = false, .line = 14, .pixel = 12, }; -struct resync_parameters pal_resync_parameters = { +static const struct resync_parameters pal_resync_parameters = { .field = true, .line = 13, .pixel = 12, }; -struct tv_mode tv_modes[] = { +static const struct tv_mode tv_modes[] = { { .name = "NTSC", .mode = SUN4I_TVE_CFG0_RES_480i, @@ -289,13 +289,13 @@ drm_connector_to_sun4i_tv(struct drm_connector *connector) * So far, it doesn't seem to be preserved when the mode is passed by * to mode_set for some reason. */ -static struct tv_mode *sun4i_tv_find_tv_by_mode(struct drm_display_mode *mode) +static const struct tv_mode *sun4i_tv_find_tv_by_mode(const struct drm_display_mode *mode) { int i; /* First try to identify the mode by name */ for (i = 0; i < ARRAY_SIZE(tv_modes); i++) { - struct tv_mode *tv_mode = &tv_modes[i]; + const struct tv_mode *tv_mode = &tv_modes[i]; DRM_DEBUG_DRIVER("Comparing mode %s vs %s", mode->name, tv_mode->name); @@ -306,7 +306,7 @@ static struct tv_mode *sun4i_tv_find_tv_by_mode(struct drm_display_mode *mode) /* Then by number of lines */ for (i = 0; i < ARRAY_SIZE(tv_modes); i++) { - struct tv_mode *tv_mode = &tv_modes[i]; + const struct tv_mode *tv_mode = &tv_modes[i]; DRM_DEBUG_DRIVER("Comparing mode %s vs %s (X: %d vs %d)", mode->name, tv_mode->name, @@ -319,7 +319,7 @@ static struct tv_mode *sun4i_tv_find_tv_by_mode(struct drm_display_mode *mode) return NULL; } -static void sun4i_tv_mode_to_drm_mode(struct tv_mode *tv_mode, +static void sun4i_tv_mode_to_drm_mode(const struct tv_mode *tv_mode, struct drm_display_mode *mode) { DRM_DEBUG_DRIVER("Creating mode %s\n", mode->name); @@ -386,7 +386,7 @@ static void sun4i_tv_mode_set(struct drm_encoder *encoder, struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder); struct sun4i_drv *drv = tv->drv; struct sun4i_tcon *tcon = drv->tcon; - struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode); + const struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode); sun4i_tcon1_mode_set(tcon, mode); @@ -507,8 +507,14 @@ static int sun4i_tv_comp_get_modes(struct drm_connector *connector) int i; for (i = 0; i < ARRAY_SIZE(tv_modes); i++) { - struct drm_display_mode *mode = drm_mode_create(connector->dev); - struct tv_mode *tv_mode = &tv_modes[i]; + struct drm_display_mode *mode; + const struct tv_mode *tv_mode = &tv_modes[i]; + + mode = drm_mode_create(connector->dev); + if (!mode) { + DRM_ERROR("Failed to create a new display mode\n"); + return 0; + } strcpy(mode->name, tv_mode->name); diff --git a/drivers/gpu/drm/sun4i/sun6i_drc.c b/drivers/gpu/drm/sun4i/sun6i_drc.c new file mode 100644 index 0000000..bf6d846 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun6i_drc.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016 Free Electrons + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +struct sun6i_drc { + struct clk *bus_clk; + struct clk *mod_clk; + struct reset_control *reset; +}; + +static int sun6i_drc_bind(struct device *dev, struct device *master, + void *data) +{ + struct sun6i_drc *drc; + int ret; + + drc = devm_kzalloc(dev, sizeof(*drc), GFP_KERNEL); + if (!drc) + return -ENOMEM; + dev_set_drvdata(dev, drc); + + drc->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(drc->reset)) { + dev_err(dev, "Couldn't get our reset line\n"); + return PTR_ERR(drc->reset); + } + + ret = reset_control_deassert(drc->reset); + if (ret) { + dev_err(dev, "Couldn't deassert our reset line\n"); + return ret; + } + + drc->bus_clk = devm_clk_get(dev, "ahb"); + if (IS_ERR(drc->bus_clk)) { + dev_err(dev, "Couldn't get our bus clock\n"); + ret = PTR_ERR(drc->bus_clk); + goto err_assert_reset; + } + clk_prepare_enable(drc->bus_clk); + + drc->mod_clk = devm_clk_get(dev, "mod"); + if (IS_ERR(drc->mod_clk)) { + dev_err(dev, "Couldn't get our mod clock\n"); + ret = PTR_ERR(drc->mod_clk); + goto err_disable_bus_clk; + } + clk_prepare_enable(drc->mod_clk); + + return 0; + +err_disable_bus_clk: + clk_disable_unprepare(drc->bus_clk); +err_assert_reset: + reset_control_assert(drc->reset); + return ret; +} + +static void sun6i_drc_unbind(struct device *dev, struct device *master, + void *data) +{ + struct sun6i_drc *drc = dev_get_drvdata(dev); + + clk_disable_unprepare(drc->mod_clk); + clk_disable_unprepare(drc->bus_clk); + reset_control_assert(drc->reset); +} + +static struct component_ops sun6i_drc_ops = { + .bind = sun6i_drc_bind, + .unbind = sun6i_drc_unbind, +}; + +static int sun6i_drc_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &sun6i_drc_ops); +} + +static int sun6i_drc_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &sun6i_drc_ops); + + return 0; +} + +static const struct of_device_id sun6i_drc_of_table[] = { + { .compatible = "allwinner,sun8i-a33-drc" }, + { } +}; +MODULE_DEVICE_TABLE(of, sun6i_drc_of_table); + +static struct platform_driver sun6i_drc_platform_driver = { + .probe = sun6i_drc_probe, + .remove = sun6i_drc_remove, + .driver = { + .name = "sun6i-drc", + .of_match_table = sun6i_drc_of_table, + }, +}; +module_platform_driver(sun6i_drc_platform_driver); + +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); +MODULE_DESCRIPTION("Allwinner A31 Dynamic Range Control (DRC) Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c index fab5ebc..f418892 100644 --- a/drivers/gpu/drm/tdfx/tdfx_drv.c +++ b/drivers/gpu/drm/tdfx/tdfx_drv.c @@ -56,6 +56,7 @@ static const struct file_operations tdfx_driver_fops = { }; static struct drm_driver driver = { + .driver_features = DRIVER_LEGACY, .set_busid = drm_pci_set_busid, .fops = &tdfx_driver_fops, .name = DRIVER_NAME, diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 8495bd0..4010d69 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -480,17 +480,6 @@ static const struct drm_plane_funcs tegra_primary_plane_funcs = { .atomic_destroy_state = tegra_plane_atomic_destroy_state, }; -static int tegra_plane_prepare_fb(struct drm_plane *plane, - const struct drm_plane_state *new_state) -{ - return 0; -} - -static void tegra_plane_cleanup_fb(struct drm_plane *plane, - const struct drm_plane_state *old_fb) -{ -} - static int tegra_plane_state_add(struct tegra_plane *plane, struct drm_plane_state *state) { @@ -591,7 +580,14 @@ static void tegra_plane_atomic_update(struct drm_plane *plane, struct tegra_bo *bo = tegra_fb_get_plane(fb, i); window.base[i] = bo->paddr + fb->offsets[i]; - window.stride[i] = fb->pitches[i]; + + /* + * Tegra uses a shared stride for UV planes. Framebuffers are + * already checked for this in the tegra_plane_atomic_check() + * function, so it's safe to ignore the V-plane pitch here. + */ + if (i < 2) + window.stride[i] = fb->pitches[i]; } tegra_dc_setup_window(dc, p->index, &window); @@ -624,8 +620,6 @@ static void tegra_plane_atomic_disable(struct drm_plane *plane, } static const struct drm_plane_helper_funcs tegra_primary_plane_helper_funcs = { - .prepare_fb = tegra_plane_prepare_fb, - .cleanup_fb = tegra_plane_cleanup_fb, .atomic_check = tegra_plane_atomic_check, .atomic_update = tegra_plane_atomic_update, .atomic_disable = tegra_plane_atomic_disable, @@ -796,8 +790,6 @@ static const struct drm_plane_funcs tegra_cursor_plane_funcs = { }; static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = { - .prepare_fb = tegra_plane_prepare_fb, - .cleanup_fb = tegra_plane_cleanup_fb, .atomic_check = tegra_cursor_atomic_check, .atomic_update = tegra_cursor_atomic_update, .atomic_disable = tegra_cursor_atomic_disable, @@ -866,8 +858,6 @@ static const uint32_t tegra_overlay_plane_formats[] = { }; static const struct drm_plane_helper_funcs tegra_overlay_plane_helper_funcs = { - .prepare_fb = tegra_plane_prepare_fb, - .cleanup_fb = tegra_plane_cleanup_fb, .atomic_check = tegra_plane_atomic_check, .atomic_update = tegra_plane_atomic_update, .atomic_disable = tegra_plane_atomic_disable, diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 755264d..8ab47b5 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -57,7 +57,8 @@ static void tegra_atomic_complete(struct tegra_drm *tegra, drm_atomic_helper_commit_modeset_disables(drm, state); drm_atomic_helper_commit_modeset_enables(drm, state); - drm_atomic_helper_commit_planes(drm, state, true); + drm_atomic_helper_commit_planes(drm, state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); drm_atomic_helper_wait_for_vblanks(drm, state); @@ -982,8 +983,8 @@ static int host1x_drm_probe(struct host1x_device *dev) int err; drm = drm_dev_alloc(driver, &dev->dev); - if (!drm) - return -ENOMEM; + if (IS_ERR(drm)) + return PTR_ERR(drm); dev_set_drvdata(&dev->dev, drm); diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 3d228ad..3dea121 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -840,6 +840,21 @@ static const struct drm_encoder_funcs tegra_dsi_encoder_funcs = { .destroy = tegra_output_encoder_destroy, }; +static void tegra_dsi_unprepare(struct tegra_dsi *dsi) +{ + int err; + + if (dsi->slave) + tegra_dsi_unprepare(dsi->slave); + + err = tegra_mipi_disable(dsi->mipi); + if (err < 0) + dev_err(dsi->dev, "failed to disable MIPI calibration: %d\n", + err); + + pm_runtime_put(dsi->dev); +} + static void tegra_dsi_encoder_disable(struct drm_encoder *encoder) { struct tegra_output *output = encoder_to_output(encoder); @@ -876,7 +891,26 @@ static void tegra_dsi_encoder_disable(struct drm_encoder *encoder) tegra_dsi_disable(dsi); - pm_runtime_put(dsi->dev); + tegra_dsi_unprepare(dsi); +} + +static void tegra_dsi_prepare(struct tegra_dsi *dsi) +{ + int err; + + pm_runtime_get_sync(dsi->dev); + + err = tegra_mipi_enable(dsi->mipi); + if (err < 0) + dev_err(dsi->dev, "failed to enable MIPI calibration: %d\n", + err); + + err = tegra_dsi_pad_calibrate(dsi); + if (err < 0) + dev_err(dsi->dev, "MIPI calibration failed: %d\n", err); + + if (dsi->slave) + tegra_dsi_prepare(dsi->slave); } static void tegra_dsi_encoder_enable(struct drm_encoder *encoder) @@ -887,13 +921,8 @@ static void tegra_dsi_encoder_enable(struct drm_encoder *encoder) struct tegra_dsi *dsi = to_dsi(output); struct tegra_dsi_state *state; u32 value; - int err; - - pm_runtime_get_sync(dsi->dev); - err = tegra_dsi_pad_calibrate(dsi); - if (err < 0) - dev_err(dsi->dev, "MIPI calibration failed: %d\n", err); + tegra_dsi_prepare(dsi); state = tegra_dsi_get_state(dsi); diff --git a/drivers/gpu/drm/tilcdc/Makefile b/drivers/gpu/drm/tilcdc/Makefile index deeca48..6f67517 100644 --- a/drivers/gpu/drm/tilcdc/Makefile +++ b/drivers/gpu/drm/tilcdc/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_DRM_TILCDC_SLAVE_COMPAT) += tilcdc_slave_compat.o \ tilcdc_slave_compat.dtb.o tilcdc-y := \ + tilcdc_plane.o \ tilcdc_crtc.o \ tilcdc_tfp410.o \ tilcdc_panel.o \ diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 107c8bd..52ebe8f 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -15,8 +15,12 @@ * this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "drm_flip_work.h" +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_flip_work.h> #include <drm/drm_plane_helper.h> +#include <linux/workqueue.h> #include "tilcdc_drv.h" #include "tilcdc_regs.h" @@ -26,13 +30,16 @@ struct tilcdc_crtc { struct drm_crtc base; + struct drm_plane primary; const struct tilcdc_panel_info *info; struct drm_pending_vblank_event *event; - int dpms; + bool enabled; wait_queue_head_t frame_done_wq; bool frame_done; spinlock_t irq_lock; + unsigned int lcd_fck_rate; + ktime_t last_vblank; struct drm_framebuffer *curr_fb; @@ -67,6 +74,7 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) struct drm_gem_cma_object *gem; unsigned int depth, bpp; dma_addr_t start, end; + u64 dma_base_and_ceiling; drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); gem = drm_fb_cma_get_gem_obj(fb, 0); @@ -77,8 +85,13 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) end = start + (crtc->mode.vdisplay * fb->pitches[0]); - tilcdc_write(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, start); - tilcdc_write(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG, end); + /* Write LCDC_DMA_FB_BASE_ADDR_0_REG and LCDC_DMA_FB_CEILING_ADDR_0_REG + * with a single insruction, if available. This should make it more + * unlikely that LCDC would fetch the DMA addresses in the middle of + * an update. + */ + dma_base_and_ceiling = (u64)(end - 1) << 32 | start; + tilcdc_write64(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, dma_base_and_ceiling); if (tilcdc_crtc->curr_fb) drm_flip_work_queue(&tilcdc_crtc->unref_work, @@ -87,6 +100,43 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) tilcdc_crtc->curr_fb = fb; } +static void tilcdc_crtc_enable_irqs(struct drm_device *dev) +{ + struct tilcdc_drm_private *priv = dev->dev_private; + + tilcdc_clear_irqstatus(dev, 0xffffffff); + + if (priv->rev == 1) { + tilcdc_set(dev, LCDC_RASTER_CTRL_REG, + LCDC_V1_UNDERFLOW_INT_ENA); + tilcdc_set(dev, LCDC_DMA_CTRL_REG, + LCDC_V1_END_OF_FRAME_INT_ENA); + } else { + tilcdc_write(dev, LCDC_INT_ENABLE_SET_REG, + LCDC_V2_UNDERFLOW_INT_ENA | + LCDC_V2_END_OF_FRAME0_INT_ENA | + LCDC_FRAME_DONE | LCDC_SYNC_LOST); + } +} + +static void tilcdc_crtc_disable_irqs(struct drm_device *dev) +{ + struct tilcdc_drm_private *priv = dev->dev_private; + + /* disable irqs that we might have enabled: */ + if (priv->rev == 1) { + tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, + LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA); + tilcdc_clear(dev, LCDC_DMA_CTRL_REG, + LCDC_V1_END_OF_FRAME_INT_ENA); + } else { + tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, + LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA | + LCDC_V2_END_OF_FRAME0_INT_ENA | + LCDC_FRAME_DONE | LCDC_SYNC_LOST); + } +} + static void reset(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -100,66 +150,112 @@ static void reset(struct drm_crtc *crtc) tilcdc_clear(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); } -static void start(struct drm_crtc *crtc) +static void tilcdc_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); + + if (tilcdc_crtc->enabled) + return; + + pm_runtime_get_sync(dev->dev); reset(crtc); + tilcdc_crtc_enable_irqs(dev); + tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_PALETTE_LOAD_MODE(DATA_ONLY)); tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); + + drm_crtc_vblank_on(crtc); + + tilcdc_crtc->enabled = true; } -static void stop(struct drm_crtc *crtc) +void tilcdc_crtc_disable(struct drm_crtc *crtc) { + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; + struct tilcdc_drm_private *priv = dev->dev_private; + + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); + + if (!tilcdc_crtc->enabled) + return; + tilcdc_crtc->frame_done = false; tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); + + /* + * if necessary wait for framedone irq which will still come + * before putting things to sleep.. + */ + if (priv->rev == 2) { + int ret = wait_event_timeout(tilcdc_crtc->frame_done_wq, + tilcdc_crtc->frame_done, + msecs_to_jiffies(500)); + if (ret == 0) + dev_err(dev->dev, "%s: timeout waiting for framedone\n", + __func__); + } + + drm_crtc_vblank_off(crtc); + + tilcdc_crtc_disable_irqs(dev); + + pm_runtime_put_sync(dev->dev); + + if (tilcdc_crtc->next_fb) { + drm_flip_work_queue(&tilcdc_crtc->unref_work, + tilcdc_crtc->next_fb); + tilcdc_crtc->next_fb = NULL; + } + + if (tilcdc_crtc->curr_fb) { + drm_flip_work_queue(&tilcdc_crtc->unref_work, + tilcdc_crtc->curr_fb); + tilcdc_crtc->curr_fb = NULL; + } + + drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); + tilcdc_crtc->last_vblank = ktime_set(0, 0); + + tilcdc_crtc->enabled = false; +} + +static bool tilcdc_crtc_is_on(struct drm_crtc *crtc) +{ + return crtc->state && crtc->state->enable && crtc->state->active; } static void tilcdc_crtc_destroy(struct drm_crtc *crtc) { struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + struct tilcdc_drm_private *priv = crtc->dev->dev_private; + + drm_modeset_lock_crtc(crtc, NULL); + tilcdc_crtc_disable(crtc); + drm_modeset_unlock_crtc(crtc); - tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + flush_workqueue(priv->wq); of_node_put(crtc->port); drm_crtc_cleanup(crtc); drm_flip_work_cleanup(&tilcdc_crtc->unref_work); } -static int tilcdc_verify_fb(struct drm_crtc *crtc, struct drm_framebuffer *fb) -{ - struct drm_device *dev = crtc->dev; - unsigned int depth, bpp; - - drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); - - if (fb->pitches[0] != crtc->mode.hdisplay * bpp / 8) { - dev_err(dev->dev, - "Invalid pitch: fb and crtc widths must be the same"); - return -EINVAL; - } - - return 0; -} - -static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, +int tilcdc_crtc_update_fb(struct drm_crtc *crtc, struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event, - uint32_t page_flip_flags) + struct drm_pending_vblank_event *event) { struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; - int r; unsigned long flags; - s64 tdiff; - ktime_t next_vblank; - r = tilcdc_verify_fb(crtc, fb); - if (r) - return r; + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); if (tilcdc_crtc->event) { dev_err(dev->dev, "already pending page flip!\n"); @@ -170,82 +266,31 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, crtc->primary->fb = fb; - pm_runtime_get_sync(dev->dev); - spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags); - next_vblank = ktime_add_us(tilcdc_crtc->last_vblank, - 1000000 / crtc->hwmode.vrefresh); + if (crtc->hwmode.vrefresh && ktime_to_ns(tilcdc_crtc->last_vblank)) { + ktime_t next_vblank; + s64 tdiff; + + next_vblank = ktime_add_us(tilcdc_crtc->last_vblank, + 1000000 / crtc->hwmode.vrefresh); - tdiff = ktime_to_us(ktime_sub(next_vblank, ktime_get())); + tdiff = ktime_to_us(ktime_sub(next_vblank, ktime_get())); - if (tdiff >= TILCDC_VBLANK_SAFETY_THRESHOLD_US) + if (tdiff < TILCDC_VBLANK_SAFETY_THRESHOLD_US) + tilcdc_crtc->next_fb = fb; + } + + if (tilcdc_crtc->next_fb != fb) set_scanout(crtc, fb); - else - tilcdc_crtc->next_fb = fb; tilcdc_crtc->event = event; spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags); - pm_runtime_put_sync(dev->dev); - return 0; } -void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); - struct drm_device *dev = crtc->dev; - struct tilcdc_drm_private *priv = dev->dev_private; - - /* we really only care about on or off: */ - if (mode != DRM_MODE_DPMS_ON) - mode = DRM_MODE_DPMS_OFF; - - if (tilcdc_crtc->dpms == mode) - return; - - tilcdc_crtc->dpms = mode; - - if (mode == DRM_MODE_DPMS_ON) { - pm_runtime_get_sync(dev->dev); - start(crtc); - } else { - tilcdc_crtc->frame_done = false; - stop(crtc); - - /* - * if necessary wait for framedone irq which will still come - * before putting things to sleep.. - */ - if (priv->rev == 2) { - int ret = wait_event_timeout( - tilcdc_crtc->frame_done_wq, - tilcdc_crtc->frame_done, - msecs_to_jiffies(50)); - if (ret == 0) - dev_err(dev->dev, "timeout waiting for framedone\n"); - } - - pm_runtime_put_sync(dev->dev); - - if (tilcdc_crtc->next_fb) { - drm_flip_work_queue(&tilcdc_crtc->unref_work, - tilcdc_crtc->next_fb); - tilcdc_crtc->next_fb = NULL; - } - - if (tilcdc_crtc->curr_fb) { - drm_flip_work_queue(&tilcdc_crtc->unref_work, - tilcdc_crtc->curr_fb); - tilcdc_crtc->curr_fb = NULL; - } - - drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); - } -} - static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -275,41 +320,54 @@ static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc, return true; } -static void tilcdc_crtc_prepare(struct drm_crtc *crtc) +static void tilcdc_crtc_set_clk(struct drm_crtc *crtc) { - tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); -} + struct drm_device *dev = crtc->dev; + struct tilcdc_drm_private *priv = dev->dev_private; + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + const unsigned clkdiv = 2; /* using a fixed divider of 2 */ + int ret; -static void tilcdc_crtc_commit(struct drm_crtc *crtc) -{ - tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON); + /* mode.clock is in KHz, set_rate wants parameter in Hz */ + ret = clk_set_rate(priv->clk, crtc->mode.clock * 1000 * clkdiv); + if (ret < 0) { + dev_err(dev->dev, "failed to set display clock rate to: %d\n", + crtc->mode.clock); + return; + } + + tilcdc_crtc->lcd_fck_rate = clk_get_rate(priv->clk); + + DBG("lcd_clk=%u, mode clock=%d, div=%u", + tilcdc_crtc->lcd_fck_rate, crtc->mode.clock, clkdiv); + + /* Configure the LCD clock divisor. */ + tilcdc_write(dev, LCDC_CTRL_REG, LCDC_CLK_DIVISOR(clkdiv) | + LCDC_RASTER_MODE); + + if (priv->rev == 2) + tilcdc_set(dev, LCDC_CLK_ENABLE_REG, + LCDC_V2_DMA_CLK_EN | LCDC_V2_LIDD_CLK_EN | + LCDC_V2_CORE_CLK_EN); } -static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) +static void tilcdc_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; struct tilcdc_drm_private *priv = dev->dev_private; const struct tilcdc_panel_info *info = tilcdc_crtc->info; uint32_t reg, hbp, hfp, hsw, vbp, vfp, vsw; - int ret; + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + struct drm_framebuffer *fb = crtc->primary->state->fb; - ret = tilcdc_crtc_mode_valid(crtc, mode); - if (WARN_ON(ret)) - return ret; + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); if (WARN_ON(!info)) - return -EINVAL; - - ret = tilcdc_verify_fb(crtc, crtc->primary->fb); - if (ret) - return ret; + return; - pm_runtime_get_sync(dev->dev); + if (WARN_ON(!fb)) + return; /* Configure the Burst Size and fifo threshold of DMA: */ reg = tilcdc_read(dev, LCDC_DMA_CTRL_REG) & ~0x00000770; @@ -330,7 +388,8 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_16); break; default: - return -EINVAL; + dev_err(dev->dev, "invalid burst size\n"); + return; } reg |= (info->fifo_th << 8); tilcdc_write(dev, LCDC_DMA_CTRL_REG, reg); @@ -344,9 +403,9 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, vsw = mode->vsync_end - mode->vsync_start; DBG("%dx%d, hbp=%u, hfp=%u, hsw=%u, vbp=%u, vfp=%u, vsw=%u", - mode->hdisplay, mode->vdisplay, hbp, hfp, hsw, vbp, vfp, vsw); + mode->hdisplay, mode->vdisplay, hbp, hfp, hsw, vbp, vfp, vsw); - /* Configure the AC Bias Period and Number of Transitions per Interrupt: */ + /* Set AC Bias Period and Number of Transitions per Interrupt: */ reg = tilcdc_read(dev, LCDC_RASTER_TIMING_2_REG) & ~0x000fff00; reg |= LCDC_AC_BIAS_FREQUENCY(info->ac_bias) | LCDC_AC_BIAS_TRANSITIONS_PER_INT(info->ac_bias_intrpt); @@ -381,7 +440,7 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, /* * be sure to set Bit 10 for the V2 LCDC controller, * otherwise limited to 1024 pixels width, stopping - * 1920x1080 being suppoted. + * 1920x1080 being supported. */ if (priv->rev == 2) { if ((mode->vdisplay - 1) & 0x400) { @@ -396,14 +455,15 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, /* Configure display type: */ reg = tilcdc_read(dev, LCDC_RASTER_CTRL_REG) & ~(LCDC_TFT_MODE | LCDC_MONO_8BIT_MODE | LCDC_MONOCHROME_MODE | - LCDC_V2_TFT_24BPP_MODE | LCDC_V2_TFT_24BPP_UNPACK | 0x000ff000); + LCDC_V2_TFT_24BPP_MODE | LCDC_V2_TFT_24BPP_UNPACK | + 0x000ff000 /* Palette Loading Delay bits */); reg |= LCDC_TFT_MODE; /* no monochrome/passive support */ if (info->tft_alt_mode) reg |= LCDC_TFT_ALT_ENABLE; if (priv->rev == 2) { unsigned int depth, bpp; - drm_fb_get_bpp_depth(crtc->primary->fb->pixel_format, &depth, &bpp); + drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); switch (bpp) { case 16: break; @@ -415,7 +475,7 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, break; default: dev_err(dev->dev, "invalid pixel format\n"); - return -EINVAL; + return; } } reg |= info->fdd < 12; @@ -436,12 +496,7 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, else tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_EDGE); - /* - * use value from adjusted_mode here as this might have been - * changed as part of the fixup for slave encoders to solve the - * issue where tilcdc timings are not VESA compliant - */ - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) + if (mode->flags & DRM_MODE_FLAG_NHSYNC) tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_HSYNC); else tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_HSYNC); @@ -456,51 +511,56 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, else tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ORDER); - drm_framebuffer_reference(crtc->primary->fb); - - set_scanout(crtc, crtc->primary->fb); + drm_framebuffer_reference(fb); - tilcdc_crtc_update_clk(crtc); + set_scanout(crtc, fb); - pm_runtime_put_sync(dev->dev); + tilcdc_crtc_set_clk(crtc); - return 0; + crtc->hwmode = crtc->state->adjusted_mode; } -static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) +static int tilcdc_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) { - struct drm_device *dev = crtc->dev; - int r; - - r = tilcdc_verify_fb(crtc, crtc->primary->fb); - if (r) - return r; - - drm_framebuffer_reference(crtc->primary->fb); + struct drm_display_mode *mode = &state->mode; + int ret; - pm_runtime_get_sync(dev->dev); + /* If we are not active we don't care */ + if (!state->active) + return 0; - set_scanout(crtc, crtc->primary->fb); + if (state->state->planes[0].ptr != crtc->primary || + state->state->planes[0].state == NULL || + state->state->planes[0].state->crtc != crtc) { + dev_dbg(crtc->dev->dev, "CRTC primary plane must be present"); + return -EINVAL; + } - pm_runtime_put_sync(dev->dev); + ret = tilcdc_crtc_mode_valid(crtc, mode); + if (ret) { + dev_dbg(crtc->dev->dev, "Mode \"%s\" not valid", mode->name); + return -EINVAL; + } return 0; } static const struct drm_crtc_funcs tilcdc_crtc_funcs = { - .destroy = tilcdc_crtc_destroy, - .set_config = drm_crtc_helper_set_config, - .page_flip = tilcdc_crtc_page_flip, + .destroy = tilcdc_crtc_destroy, + .set_config = drm_atomic_helper_set_config, + .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, }; static const struct drm_crtc_helper_funcs tilcdc_crtc_helper_funcs = { - .dpms = tilcdc_crtc_dpms, .mode_fixup = tilcdc_crtc_mode_fixup, - .prepare = tilcdc_crtc_prepare, - .commit = tilcdc_crtc_commit, - .mode_set = tilcdc_crtc_mode_set, - .mode_set_base = tilcdc_crtc_mode_set_base, + .enable = tilcdc_crtc_enable, + .disable = tilcdc_crtc_disable, + .atomic_check = tilcdc_crtc_atomic_check, + .mode_set_nofb = tilcdc_crtc_mode_set_nofb, }; int tilcdc_crtc_max_width(struct drm_crtc *crtc) @@ -622,46 +682,23 @@ void tilcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *crtc, void tilcdc_crtc_update_clk(struct drm_crtc *crtc) { - struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; struct tilcdc_drm_private *priv = dev->dev_private; - int dpms = tilcdc_crtc->dpms; - unsigned long lcd_clk; - const unsigned clkdiv = 2; /* using a fixed divider of 2 */ - int ret; + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); - pm_runtime_get_sync(dev->dev); + drm_modeset_lock_crtc(crtc, NULL); + if (tilcdc_crtc->lcd_fck_rate != clk_get_rate(priv->clk)) { + if (tilcdc_crtc_is_on(crtc)) { + pm_runtime_get_sync(dev->dev); + tilcdc_crtc_disable(crtc); - if (dpms == DRM_MODE_DPMS_ON) - tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + tilcdc_crtc_set_clk(crtc); - /* mode.clock is in KHz, set_rate wants parameter in Hz */ - ret = clk_set_rate(priv->clk, crtc->mode.clock * 1000 * clkdiv); - if (ret < 0) { - dev_err(dev->dev, "failed to set display clock rate to: %d\n", - crtc->mode.clock); - goto out; + tilcdc_crtc_enable(crtc); + pm_runtime_put_sync(dev->dev); + } } - - lcd_clk = clk_get_rate(priv->clk); - - DBG("lcd_clk=%lu, mode clock=%d, div=%u", - lcd_clk, crtc->mode.clock, clkdiv); - - /* Configure the LCD clock divisor. */ - tilcdc_write(dev, LCDC_CTRL_REG, LCDC_CLK_DIVISOR(clkdiv) | - LCDC_RASTER_MODE); - - if (priv->rev == 2) - tilcdc_set(dev, LCDC_CLK_ENABLE_REG, - LCDC_V2_DMA_CLK_EN | LCDC_V2_LIDD_CLK_EN | - LCDC_V2_CORE_CLK_EN); - - if (dpms == DRM_MODE_DPMS_ON) - tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON); - -out: - pm_runtime_put_sync(dev->dev); + drm_modeset_unlock_crtc(crtc); } #define SYNC_LOST_COUNT_LIMIT 50 @@ -718,30 +755,34 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) tilcdc_crtc->frame_intact = true; } + if (stat & LCDC_FIFO_UNDERFLOW) + dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underfow", + __func__, stat); + + /* For revision 2 only */ if (priv->rev == 2) { if (stat & LCDC_FRAME_DONE) { tilcdc_crtc->frame_done = true; wake_up(&tilcdc_crtc->frame_done_wq); } - tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0); - } - if (stat & LCDC_SYNC_LOST) { - dev_err_ratelimited(dev->dev, "%s(0x%08x): Sync lost", - __func__, stat); - tilcdc_crtc->frame_intact = false; - if (tilcdc_crtc->sync_lost_count++ > SYNC_LOST_COUNT_LIMIT) { - dev_err(dev->dev, - "%s(0x%08x): Sync lost flood detected, disabling the interrupt", - __func__, stat); - tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, - LCDC_SYNC_LOST); + if (stat & LCDC_SYNC_LOST) { + dev_err_ratelimited(dev->dev, "%s(0x%08x): Sync lost", + __func__, stat); + tilcdc_crtc->frame_intact = false; + if (tilcdc_crtc->sync_lost_count++ > + SYNC_LOST_COUNT_LIMIT) { + dev_err(dev->dev, "%s(0x%08x): Sync lost flood detected, disabling the interrupt", __func__, stat); + tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, + LCDC_SYNC_LOST); + } } - } - if (stat & LCDC_FIFO_UNDERFLOW) - dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underfow", - __func__, stat); + /* Indicate to LCDC that the interrupt service routine has + * completed, see 13.3.6.1.6 in AM335x TRM. + */ + tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0); + } return IRQ_HANDLED; } @@ -761,7 +802,10 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) crtc = &tilcdc_crtc->base; - tilcdc_crtc->dpms = DRM_MODE_DPMS_OFF; + ret = tilcdc_plane_init(dev, &tilcdc_crtc->primary); + if (ret < 0) + goto fail; + init_waitqueue_head(&tilcdc_crtc->frame_done_wq); drm_flip_work_init(&tilcdc_crtc->unref_work, @@ -769,7 +813,11 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) spin_lock_init(&tilcdc_crtc->irq_lock); - ret = drm_crtc_init(dev, crtc, &tilcdc_crtc_funcs); + ret = drm_crtc_init_with_planes(dev, crtc, + &tilcdc_crtc->primary, + NULL, + &tilcdc_crtc_funcs, + "tilcdc crtc"); if (ret < 0) goto fail; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index d278093..a694977 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -20,6 +20,8 @@ #include <linux/component.h> #include <linux/pinctrl/consumer.h> #include <linux/suspend.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> #include "tilcdc_drv.h" #include "tilcdc_regs.h" @@ -31,6 +33,20 @@ static LIST_HEAD(module_list); +static const u32 tilcdc_rev1_formats[] = { DRM_FORMAT_RGB565 }; + +static const u32 tilcdc_straight_formats[] = { DRM_FORMAT_RGB565, + DRM_FORMAT_BGR888, + DRM_FORMAT_XBGR8888 }; + +static const u32 tilcdc_crossed_formats[] = { DRM_FORMAT_BGR565, + DRM_FORMAT_RGB888, + DRM_FORMAT_XRGB8888 }; + +static const u32 tilcdc_legacy_formats[] = { DRM_FORMAT_RGB565, + DRM_FORMAT_RGB888, + DRM_FORMAT_XRGB8888 }; + void tilcdc_module_init(struct tilcdc_module *mod, const char *name, const struct tilcdc_module_ops *funcs) { @@ -59,9 +75,84 @@ static void tilcdc_fb_output_poll_changed(struct drm_device *dev) drm_fbdev_cma_hotplug_event(priv->fbdev); } +static int tilcdc_atomic_check(struct drm_device *dev, + struct drm_atomic_state *state) +{ + int ret; + + ret = drm_atomic_helper_check_modeset(dev, state); + if (ret) + return ret; + + ret = drm_atomic_helper_check_planes(dev, state); + if (ret) + return ret; + + /* + * tilcdc ->atomic_check can update ->mode_changed if pixel format + * changes, hence will we check modeset changes again. + */ + ret = drm_atomic_helper_check_modeset(dev, state); + if (ret) + return ret; + + return ret; +} + +static int tilcdc_commit(struct drm_device *dev, + struct drm_atomic_state *state, + bool async) +{ + int ret; + + ret = drm_atomic_helper_prepare_planes(dev, state); + if (ret) + return ret; + + drm_atomic_helper_swap_state(state, true); + + /* + * Everything below can be run asynchronously without the need to grab + * any modeset locks at all under one condition: It must be guaranteed + * that the asynchronous work has either been cancelled (if the driver + * supports it, which at least requires that the framebuffers get + * cleaned up with drm_atomic_helper_cleanup_planes()) or completed + * before the new state gets committed on the software side with + * drm_atomic_helper_swap_state(). + * + * This scheme allows new atomic state updates to be prepared and + * checked in parallel to the asynchronous completion of the previous + * update. Which is important since compositors need to figure out the + * composition of the next frame right after having submitted the + * current layout. + */ + + /* Keep HW on while we commit the state. */ + pm_runtime_get_sync(dev->dev); + + drm_atomic_helper_commit_modeset_disables(dev, state); + + drm_atomic_helper_commit_planes(dev, state, 0); + + drm_atomic_helper_commit_modeset_enables(dev, state); + + /* Now HW should remain on if need becase the crtc is enabled */ + pm_runtime_put_sync(dev->dev); + + drm_atomic_helper_wait_for_vblanks(dev, state); + + drm_atomic_helper_cleanup_planes(dev, state); + + drm_atomic_state_free(state); + + return 0; +} + static const struct drm_mode_config_funcs mode_config_funcs = { .fb_create = tilcdc_fb_create, .output_poll_changed = tilcdc_fb_output_poll_changed, + .atomic_check = tilcdc_atomic_check, + .atomic_commit = tilcdc_commit, }; static int modeset_init(struct drm_device *dev) @@ -93,12 +184,9 @@ static int cpufreq_transition(struct notifier_block *nb, { struct tilcdc_drm_private *priv = container_of(nb, struct tilcdc_drm_private, freq_transition); - if (val == CPUFREQ_POSTCHANGE) { - if (priv->lcd_fck_rate != clk_get_rate(priv->clk)) { - priv->lcd_fck_rate = clk_get_rate(priv->clk); - tilcdc_crtc_update_clk(priv->crtc); - } - } + + if (val == CPUFREQ_POSTCHANGE) + tilcdc_crtc_update_clk(priv->crtc); return 0; } @@ -112,8 +200,6 @@ static int tilcdc_unload(struct drm_device *dev) { struct tilcdc_drm_private *priv = dev->dev_private; - tilcdc_crtc_dpms(priv->crtc, DRM_MODE_DPMS_OFF); - tilcdc_remove_external_encoders(dev); drm_fbdev_cma_fini(priv->fbdev); @@ -121,9 +207,7 @@ static int tilcdc_unload(struct drm_device *dev) drm_mode_config_cleanup(dev); drm_vblank_cleanup(dev); - pm_runtime_get_sync(dev->dev); drm_irq_uninstall(dev); - pm_runtime_put_sync(dev->dev); #ifdef CONFIG_CPU_FREQ cpufreq_unregister_notifier(&priv->freq_transition, @@ -146,24 +230,17 @@ static int tilcdc_unload(struct drm_device *dev) return 0; } -static size_t tilcdc_num_regs(void); - static int tilcdc_load(struct drm_device *dev, unsigned long flags) { struct platform_device *pdev = dev->platformdev; struct device_node *node = pdev->dev.of_node; struct tilcdc_drm_private *priv; - struct tilcdc_module *mod; struct resource *res; u32 bpp = 0; int ret; priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL); - if (priv) - priv->saved_register = - devm_kcalloc(dev->dev, tilcdc_num_regs(), - sizeof(*priv->saved_register), GFP_KERNEL); - if (!priv || !priv->saved_register) { + if (!priv) { dev_err(dev->dev, "failed to allocate private data\n"); return -ENOMEM; } @@ -201,7 +278,6 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) } #ifdef CONFIG_CPU_FREQ - priv->lcd_fck_rate = clk_get_rate(priv->clk); priv->freq_transition.notifier_call = cpufreq_transition; ret = cpufreq_register_notifier(&priv->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); @@ -249,6 +325,37 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) pm_runtime_put_sync(dev->dev); + if (priv->rev == 1) { + DBG("Revision 1 LCDC supports only RGB565 format"); + priv->pixelformats = tilcdc_rev1_formats; + priv->num_pixelformats = ARRAY_SIZE(tilcdc_rev1_formats); + bpp = 16; + } else { + const char *str = "\0"; + + of_property_read_string(node, "blue-and-red-wiring", &str); + if (0 == strcmp(str, "crossed")) { + DBG("Configured for crossed blue and red wires"); + priv->pixelformats = tilcdc_crossed_formats; + priv->num_pixelformats = + ARRAY_SIZE(tilcdc_crossed_formats); + bpp = 32; /* Choose bpp with RGB support for fbdef */ + } else if (0 == strcmp(str, "straight")) { + DBG("Configured for straight blue and red wires"); + priv->pixelformats = tilcdc_straight_formats; + priv->num_pixelformats = + ARRAY_SIZE(tilcdc_straight_formats); + bpp = 16; /* Choose bpp with RGB support for fbdef */ + } else { + DBG("Blue and red wiring '%s' unknown, use legacy mode", + str); + priv->pixelformats = tilcdc_legacy_formats; + priv->num_pixelformats = + ARRAY_SIZE(tilcdc_legacy_formats); + bpp = 16; /* This is just a guess */ + } + } + ret = modeset_init(dev); if (ret < 0) { dev_err(dev->dev, "failed to initialize mode setting\n"); @@ -262,7 +369,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) if (ret < 0) goto fail_mode_config_cleanup; - ret = tilcdc_add_external_encoders(dev, &bpp); + ret = tilcdc_add_external_encoders(dev); if (ret < 0) goto fail_component_cleanup; } @@ -279,22 +386,14 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) goto fail_external_cleanup; } - pm_runtime_get_sync(dev->dev); ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0)); - pm_runtime_put_sync(dev->dev); if (ret < 0) { dev_err(dev->dev, "failed to install IRQ handler\n"); goto fail_vblank_cleanup; } - list_for_each_entry(mod, &module_list, list) { - DBG("%s: preferred_bpp: %d", mod->name, mod->preferred_bpp); - bpp = mod->preferred_bpp; - if (bpp > 0) - break; - } + drm_mode_config_reset(dev); - drm_helper_disable_unused_functions(dev); priv->fbdev = drm_fbdev_cma_init(dev, bpp, dev->mode_config.num_crtc, dev->mode_config.num_connector); @@ -308,20 +407,18 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) return 0; fail_irq_uninstall: - pm_runtime_get_sync(dev->dev); drm_irq_uninstall(dev); - pm_runtime_put_sync(dev->dev); fail_vblank_cleanup: drm_vblank_cleanup(dev); -fail_mode_config_cleanup: - drm_mode_config_cleanup(dev); - fail_component_cleanup: if (priv->is_componentized) component_unbind_all(dev->dev, dev); +fail_mode_config_cleanup: + drm_mode_config_cleanup(dev); + fail_external_cleanup: tilcdc_remove_external_encoders(dev); @@ -361,45 +458,6 @@ static irqreturn_t tilcdc_irq(int irq, void *arg) return tilcdc_crtc_irq(priv->crtc); } -static void tilcdc_irq_preinstall(struct drm_device *dev) -{ - tilcdc_clear_irqstatus(dev, 0xffffffff); -} - -static int tilcdc_irq_postinstall(struct drm_device *dev) -{ - struct tilcdc_drm_private *priv = dev->dev_private; - - /* enable FIFO underflow irq: */ - if (priv->rev == 1) { - tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_UNDERFLOW_INT_ENA); - } else { - tilcdc_write(dev, LCDC_INT_ENABLE_SET_REG, - LCDC_V2_UNDERFLOW_INT_ENA | - LCDC_V2_END_OF_FRAME0_INT_ENA | - LCDC_FRAME_DONE | LCDC_SYNC_LOST); - } - - return 0; -} - -static void tilcdc_irq_uninstall(struct drm_device *dev) -{ - struct tilcdc_drm_private *priv = dev->dev_private; - - /* disable irqs that we might have enabled: */ - if (priv->rev == 1) { - tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, - LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA); - tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_V1_END_OF_FRAME_INT_ENA); - } else { - tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, - LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA | - LCDC_V2_END_OF_FRAME0_INT_ENA | - LCDC_FRAME_DONE | LCDC_SYNC_LOST); - } -} - static int tilcdc_enable_vblank(struct drm_device *dev, unsigned int pipe) { return 0; @@ -410,7 +468,7 @@ static void tilcdc_disable_vblank(struct drm_device *dev, unsigned int pipe) return; } -#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_PM_SLEEP) +#if defined(CONFIG_DEBUG_FS) static const struct { const char *name; uint8_t rev; @@ -441,15 +499,6 @@ static const struct { #undef REG }; -static size_t tilcdc_num_regs(void) -{ - return ARRAY_SIZE(registers); -} -#else -static size_t tilcdc_num_regs(void) -{ - return 0; -} #endif #ifdef CONFIG_DEBUG_FS @@ -537,14 +586,11 @@ static const struct file_operations fops = { static struct drm_driver tilcdc_driver = { .driver_features = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | - DRIVER_PRIME), + DRIVER_PRIME | DRIVER_ATOMIC), .load = tilcdc_load, .unload = tilcdc_unload, .lastclose = tilcdc_lastclose, .irq_handler = tilcdc_irq, - .irq_preinstall = tilcdc_irq_preinstall, - .irq_postinstall = tilcdc_irq_postinstall, - .irq_uninstall = tilcdc_irq_uninstall, .get_vblank_counter = drm_vblank_no_hw_counter, .enable_vblank = tilcdc_enable_vblank, .disable_vblank = tilcdc_disable_vblank, @@ -584,28 +630,12 @@ static int tilcdc_pm_suspend(struct device *dev) { struct drm_device *ddev = dev_get_drvdata(dev); struct tilcdc_drm_private *priv = ddev->dev_private; - unsigned i, n = 0; - drm_kms_helper_poll_disable(ddev); + priv->saved_state = drm_atomic_helper_suspend(ddev); /* Select sleep pin state */ pinctrl_pm_select_sleep_state(dev); - if (pm_runtime_suspended(dev)) { - priv->ctx_valid = false; - return 0; - } - - /* Disable the LCDC controller, to avoid locking up the PRCM */ - tilcdc_crtc_dpms(priv->crtc, DRM_MODE_DPMS_OFF); - - /* Save register state: */ - for (i = 0; i < ARRAY_SIZE(registers); i++) - if (registers[i].save && (priv->rev >= registers[i].rev)) - priv->saved_register[n++] = tilcdc_read(ddev, registers[i].reg); - - priv->ctx_valid = true; - return 0; } @@ -613,23 +643,15 @@ static int tilcdc_pm_resume(struct device *dev) { struct drm_device *ddev = dev_get_drvdata(dev); struct tilcdc_drm_private *priv = ddev->dev_private; - unsigned i, n = 0; + int ret = 0; /* Select default pin state */ pinctrl_pm_select_default_state(dev); - if (priv->ctx_valid == true) { - /* Restore register state: */ - for (i = 0; i < ARRAY_SIZE(registers); i++) - if (registers[i].save && - (priv->rev >= registers[i].rev)) - tilcdc_write(ddev, registers[i].reg, - priv->saved_register[n++]); - } - - drm_kms_helper_poll_enable(ddev); + if (priv->saved_state) + ret = drm_atomic_helper_resume(ddev, priv->saved_state); - return 0; + return ret; } #endif @@ -648,6 +670,12 @@ static int tilcdc_bind(struct device *dev) static void tilcdc_unbind(struct device *dev) { + struct drm_device *ddev = dev_get_drvdata(dev); + + /* Check if a subcomponent has already triggered the unloading. */ + if (!ddev->dev_private) + return; + drm_put_dev(dev_get_drvdata(dev)); } @@ -680,17 +708,15 @@ static int tilcdc_pdev_probe(struct platform_device *pdev) static int tilcdc_pdev_remove(struct platform_device *pdev) { - struct drm_device *ddev = dev_get_drvdata(&pdev->dev); - struct tilcdc_drm_private *priv = ddev->dev_private; - - /* Check if a subcomponent has already triggered the unloading. */ - if (!priv) - return 0; + int ret; - if (priv->is_componentized) - component_master_del(&pdev->dev, &tilcdc_comp_ops); - else + ret = tilcdc_get_external_components(&pdev->dev, NULL); + if (ret < 0) + return ret; + else if (ret == 0) drm_put_dev(platform_get_drvdata(pdev)); + else + component_master_del(&pdev->dev, &tilcdc_comp_ops); return 0; } diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h index c1de18b..9780c37 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h @@ -65,13 +65,15 @@ struct tilcdc_drm_private { */ uint32_t max_width; - /* register contents saved across suspend/resume: */ - u32 *saved_register; - bool ctx_valid; + /* Supported pixel formats */ + const uint32_t *pixelformats; + uint32_t num_pixelformats; + + /* The context for pm susped/resume cycle is stored here */ + struct drm_atomic_state *saved_state; #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; - unsigned int lcd_fck_rate; #endif struct workqueue_struct *wq; @@ -113,7 +115,6 @@ struct tilcdc_module { const char *name; struct list_head list; const struct tilcdc_module_ops *funcs; - unsigned int preferred_bpp; }; void tilcdc_module_init(struct tilcdc_module *mod, const char *name, @@ -171,6 +172,11 @@ void tilcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *crtc, bool simulate_vesa_sync); int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode); int tilcdc_crtc_max_width(struct drm_crtc *crtc); -void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode); +void tilcdc_crtc_disable(struct drm_crtc *crtc); +int tilcdc_crtc_update_fb(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event); + +int tilcdc_plane_init(struct drm_device *dev, struct drm_plane *plane); #endif /* __TILCDC_DRV_H__ */ diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c index 03acb4f..68e8950 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_external.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c @@ -52,7 +52,7 @@ static int tilcdc_external_mode_valid(struct drm_connector *connector, return MODE_OK; } -static int tilcdc_add_external_encoder(struct drm_device *dev, int *bpp, +static int tilcdc_add_external_encoder(struct drm_device *dev, struct drm_connector *connector) { struct tilcdc_drm_private *priv = dev->dev_private; @@ -64,7 +64,6 @@ static int tilcdc_add_external_encoder(struct drm_device *dev, int *bpp, /* Only tda998x is supported at the moment. */ tilcdc_crtc_set_simulate_vesa_sync(priv->crtc, true); tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_tda998x); - *bpp = panel_info_tda998x.bpp; connector_funcs = devm_kzalloc(dev->dev, sizeof(*connector_funcs), GFP_KERNEL); @@ -94,7 +93,7 @@ static int tilcdc_add_external_encoder(struct drm_device *dev, int *bpp, return 0; } -int tilcdc_add_external_encoders(struct drm_device *dev, int *bpp) +int tilcdc_add_external_encoders(struct drm_device *dev) { struct tilcdc_drm_private *priv = dev->dev_private; struct drm_connector *connector; @@ -108,7 +107,7 @@ int tilcdc_add_external_encoders(struct drm_device *dev, int *bpp) if (connector == priv->connectors[i]) found = true; if (!found) { - ret = tilcdc_add_external_encoder(dev, bpp, connector); + ret = tilcdc_add_external_encoder(dev, connector); if (ret) return ret; } @@ -138,14 +137,23 @@ static int dev_match_of(struct device *dev, void *data) int tilcdc_get_external_components(struct device *dev, struct component_match **match) { + struct device_node *node; struct device_node *ep = NULL; int count = 0; - while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) { - struct device_node *node; + /* Avoid error print by of_graph_get_next_endpoint() if there + * is no ports present. + */ + node = of_get_child_by_name(dev->of_node, "ports"); + if (!node) + node = of_get_child_by_name(dev->of_node, "port"); + if (!node) + return 0; + of_node_put(node); + while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) { node = of_graph_get_remote_port_parent(ep); - if (!node && !of_device_is_available(node)) { + if (!node || !of_device_is_available(node)) { of_node_put(node); continue; } diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.h b/drivers/gpu/drm/tilcdc/tilcdc_external.h index 6aabe27..c700e0c 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_external.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.h @@ -18,7 +18,7 @@ #ifndef __TILCDC_EXTERNAL_H__ #define __TILCDC_EXTERNAL_H__ -int tilcdc_add_external_encoders(struct drm_device *dev, int *bpp); +int tilcdc_add_external_encoders(struct drm_device *dev); void tilcdc_remove_external_encoders(struct drm_device *dev); int tilcdc_get_external_components(struct device *dev, struct component_match **match); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c index ff7774c..2134bb20 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c @@ -22,8 +22,10 @@ #include <video/display_timing.h> #include <video/of_display_timing.h> #include <video/videomode.h> +#include <drm/drm_atomic_helper.h> #include "tilcdc_drv.h" +#include "tilcdc_panel.h" struct panel_module { struct tilcdc_module base; @@ -64,9 +66,7 @@ static void panel_encoder_dpms(struct drm_encoder *encoder, int mode) static void panel_encoder_prepare(struct drm_encoder *encoder) { - struct panel_encoder *panel_encoder = to_panel_encoder(encoder); panel_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); - tilcdc_crtc_set_panel_info(encoder->crtc, panel_encoder->mod->info); } static void panel_encoder_commit(struct drm_encoder *encoder) @@ -196,9 +196,12 @@ static struct drm_encoder *panel_connector_best_encoder( static const struct drm_connector_funcs panel_connector_funcs = { .destroy = panel_connector_destroy, - .dpms = drm_helper_connector_dpms, + .dpms = drm_atomic_helper_connector_dpms, .detect = panel_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, + .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 const struct drm_connector_helper_funcs panel_connector_helper_funcs = { @@ -268,6 +271,9 @@ static int panel_modeset_init(struct tilcdc_module *mod, struct drm_device *dev) priv->encoders[priv->num_encoders++] = encoder; priv->connectors[priv->num_connectors++] = connector; + tilcdc_crtc_set_panel_info(priv->crtc, + to_panel_encoder(encoder)->mod->info); + return 0; } @@ -392,8 +398,6 @@ static int panel_probe(struct platform_device *pdev) goto fail_timings; } - mod->preferred_bpp = panel_mod->info->bpp; - return 0; fail_timings: diff --git a/drivers/gpu/drm/tilcdc/tilcdc_plane.c b/drivers/gpu/drm/tilcdc/tilcdc_plane.c new file mode 100644 index 0000000..74c65fa --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_plane.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2015 Texas Instruments + * Author: Jyri Sarha <jsarha@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <drm/drmP.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_atomic_helper.h> +#include <uapi/drm/drm_fourcc.h> + +#include "tilcdc_drv.h" + +static struct drm_plane_funcs tilcdc_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .set_property = drm_atomic_helper_plane_set_property, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static int tilcdc_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_crtc_state *crtc_state; + struct drm_plane_state *old_state = plane->state; + unsigned int depth, bpp; + + if (!state->crtc) + return 0; + + if (WARN_ON(!state->fb)) + return -EINVAL; + + if (state->crtc_x || state->crtc_y) { + dev_err(plane->dev->dev, "%s: crtc position must be zero.", + __func__); + return -EINVAL; + } + + crtc_state = drm_atomic_get_existing_crtc_state(state->state, + state->crtc); + /* we should have a crtc state if the plane is attached to a crtc */ + if (WARN_ON(!crtc_state)) + return 0; + + if (crtc_state->mode.hdisplay != state->crtc_w || + crtc_state->mode.vdisplay != state->crtc_h) { + dev_err(plane->dev->dev, + "%s: Size must match mode (%dx%d == %dx%d)", __func__, + crtc_state->mode.hdisplay, crtc_state->mode.vdisplay, + state->crtc_w, state->crtc_h); + return -EINVAL; + } + + drm_fb_get_bpp_depth(state->fb->pixel_format, &depth, &bpp); + if (state->fb->pitches[0] != crtc_state->mode.hdisplay * bpp / 8) { + dev_err(plane->dev->dev, + "Invalid pitch: fb and crtc widths must be the same"); + return -EINVAL; + } + + if (state->fb && old_state->fb && + state->fb->pixel_format != old_state->fb->pixel_format) { + dev_dbg(plane->dev->dev, + "%s(): pixel format change requires mode_change\n", + __func__); + crtc_state->mode_changed = true; + } + + return 0; +} + +static void tilcdc_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_plane_state *state = plane->state; + + if (!state->crtc) + return; + + if (WARN_ON(!state->fb || !state->crtc->state)) + return; + + tilcdc_crtc_update_fb(state->crtc, + state->fb, + state->crtc->state->event); +} + +static const struct drm_plane_helper_funcs plane_helper_funcs = { + .atomic_check = tilcdc_plane_atomic_check, + .atomic_update = tilcdc_plane_atomic_update, +}; + +int tilcdc_plane_init(struct drm_device *dev, + struct drm_plane *plane) +{ + struct tilcdc_drm_private *priv = dev->dev_private; + int ret; + + ret = drm_plane_init(dev, plane, 1, + &tilcdc_plane_funcs, + priv->pixelformats, + priv->num_pixelformats, + true); + if (ret) { + dev_err(dev->dev, "Failed to initialize plane: %d\n", ret); + return ret; + } + + drm_plane_helper_add(plane, &plane_helper_funcs); + + return 0; +} diff --git a/drivers/gpu/drm/tilcdc/tilcdc_regs.h b/drivers/gpu/drm/tilcdc/tilcdc_regs.h index 1bf5e25..f57c0d6 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_regs.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_regs.h @@ -119,6 +119,20 @@ static inline void tilcdc_write(struct drm_device *dev, u32 reg, u32 data) iowrite32(data, priv->mmio + reg); } +static inline void tilcdc_write64(struct drm_device *dev, u32 reg, u64 data) +{ + struct tilcdc_drm_private *priv = dev->dev_private; + volatile void __iomem *addr = priv->mmio + reg; + +#ifdef iowrite64 + iowrite64(data, addr); +#else + __iowmb(); + /* This compiles to strd (=64-bit write) on ARM7 */ + *(volatile u64 __force *)addr = __cpu_to_le64(data); +#endif +} + static inline u32 tilcdc_read(struct drm_device *dev, u32 reg) { struct tilcdc_drm_private *priv = dev->dev_private; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c index f9c79da..623a914 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c @@ -139,7 +139,7 @@ static void __init tilcdc_node_disable(struct device_node *node) of_update_property(node, prop); } -struct device_node * __init tilcdc_get_overlay(struct kfree_table *kft) +static struct device_node * __init tilcdc_get_overlay(struct kfree_table *kft) { const int size = __dtb_tilcdc_slave_compat_end - __dtb_tilcdc_slave_compat_begin; @@ -195,7 +195,7 @@ static const char * const tilcdc_slave_props[] __initconst = { NULL }; -void __init tilcdc_convert_slave_node(void) +static void __init tilcdc_convert_slave_node(void) { struct device_node *slave = NULL, *lcdc = NULL; struct device_node *i2c = NULL, *fragment = NULL; @@ -207,7 +207,7 @@ void __init tilcdc_convert_slave_node(void) int ret; if (kfree_table_init(&kft)) - goto out; + return; lcdc = of_find_matching_node(NULL, tilcdc_of_match); slave = of_find_matching_node(NULL, tilcdc_slave_of_match); @@ -261,7 +261,7 @@ out: of_node_put(fragment); } -int __init tilcdc_slave_compat_init(void) +static int __init tilcdc_slave_compat_init(void) { tilcdc_convert_slave_node(); return 0; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c index 6b8c5b3..458043a 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c @@ -20,8 +20,10 @@ #include <linux/of_gpio.h> #include <linux/pinctrl/pinmux.h> #include <linux/pinctrl/consumer.h> +#include <drm/drm_atomic_helper.h> #include "tilcdc_drv.h" +#include "tilcdc_tfp410.h" struct tfp410_module { struct tilcdc_module base; @@ -75,7 +77,6 @@ static void tfp410_encoder_dpms(struct drm_encoder *encoder, int mode) static void tfp410_encoder_prepare(struct drm_encoder *encoder) { tfp410_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); - tilcdc_crtc_set_panel_info(encoder->crtc, &dvi_info); } static void tfp410_encoder_commit(struct drm_encoder *encoder) @@ -201,9 +202,12 @@ static struct drm_encoder *tfp410_connector_best_encoder( static const struct drm_connector_funcs tfp410_connector_funcs = { .destroy = tfp410_connector_destroy, - .dpms = drm_helper_connector_dpms, + .dpms = drm_atomic_helper_connector_dpms, .detect = tfp410_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, + .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 const struct drm_connector_helper_funcs tfp410_connector_helper_funcs = { @@ -276,6 +280,7 @@ static int tfp410_modeset_init(struct tilcdc_module *mod, struct drm_device *dev priv->encoders[priv->num_encoders++] = encoder; priv->connectors[priv->num_connectors++] = connector; + tilcdc_crtc_set_panel_info(priv->crtc, &dvi_info); return 0; } @@ -323,8 +328,6 @@ static int tfp410_probe(struct platform_device *pdev) goto fail; } - mod->preferred_bpp = dvi_info.bpp; - i2c_node = of_find_node_by_phandle(i2c_phandle); if (!i2c_node) { dev_err(&pdev->dev, "could not get i2c bus node\n"); diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index 4709b54..d2f57c5 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -150,8 +150,5 @@ int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder) drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); - drm_object_attach_property(&connector->base, - dev->mode_config.dirty_info_property, - 1); return 0; } diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index 17d34e0..cc45d98 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -16,6 +16,20 @@ static int udl_driver_set_busid(struct drm_device *d, struct drm_master *m) return 0; } +static int udl_usb_suspend(struct usb_interface *interface, + pm_message_t message) +{ + return 0; +} + +static int udl_usb_resume(struct usb_interface *interface) +{ + struct drm_device *dev = usb_get_intfdata(interface); + + udl_modeset_restore(dev); + return 0; +} + static const struct vm_operations_struct udl_gem_vm_ops = { .fault = udl_gem_fault, .open = drm_gem_vm_open, @@ -72,8 +86,8 @@ static int udl_usb_probe(struct usb_interface *interface, int r; dev = drm_dev_alloc(&driver, &interface->dev); - if (!dev) - return -ENOMEM; + if (IS_ERR(dev)) + return PTR_ERR(dev); r = drm_dev_register(dev, (unsigned long)udev); if (r) @@ -122,6 +136,8 @@ static struct usb_driver udl_driver = { .name = "udl", .probe = udl_usb_probe, .disconnect = udl_usb_disconnect, + .suspend = udl_usb_suspend, + .resume = udl_usb_resume, .id_table = id_table, }; module_usb_driver(udl_driver); diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 0b03d34..f338a57 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -52,6 +52,7 @@ struct udl_device { struct device *dev; struct drm_device *ddev; struct usb_device *udev; + struct drm_crtc *crtc; int sku_pixel_limit; @@ -87,6 +88,7 @@ struct udl_framebuffer { /* modeset */ int udl_modeset_init(struct drm_device *dev); +void udl_modeset_restore(struct drm_device *dev); void udl_modeset_cleanup(struct drm_device *dev); int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder); diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index d5df555..9688bfa 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -203,6 +203,7 @@ static int udl_fb_open(struct fb_info *info, int user) ufbdev->fb_count++; +#ifdef CONFIG_DRM_FBDEV_EMULATION if (fb_defio && (info->fbdefio == NULL)) { /* enable defio at last moment if not disabled by client */ @@ -218,6 +219,7 @@ static int udl_fb_open(struct fb_info *info, int user) info->fbdefio = fbdefio; fb_deferred_io_init(info); } +#endif pr_notice("open /dev/fb%d user=%d fb_info=%p count=%d\n", info->node, user, info, ufbdev->fb_count); @@ -235,12 +237,14 @@ static int udl_fb_release(struct fb_info *info, int user) ufbdev->fb_count--; +#ifdef CONFIG_DRM_FBDEV_EMULATION if ((ufbdev->fb_count == 0) && (info->fbdefio)) { fb_deferred_io_cleanup(info); kfree(info->fbdefio); info->fbdefio = NULL; info->fbops->fb_mmap = udl_fb_mmap; } +#endif pr_warn("released /dev/fb%d user=%d count=%d\n", info->node, user, ufbdev->fb_count); diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 33dbfb2..29f0207 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -16,6 +16,8 @@ /* -BULK_SIZE as per usb-skeleton. Can we get full page and avoid overhead? */ #define BULK_SIZE 512 +#define NR_USB_REQUEST_CHANNEL 0x12 + #define MAX_TRANSFER (PAGE_SIZE*16 - BULK_SIZE) #define WRITES_IN_FLIGHT (4) #define MAX_VENDOR_DESCRIPTOR_SIZE 256 @@ -90,6 +92,26 @@ success: return true; } +/* + * Need to ensure a channel is selected before submitting URBs + */ +static int udl_select_std_channel(struct udl_device *udl) +{ + int ret; + u8 set_def_chn[] = {0x57, 0xCD, 0xDC, 0xA7, + 0x1C, 0x88, 0x5E, 0x15, + 0x60, 0xFE, 0xC6, 0x97, + 0x16, 0x3D, 0x47, 0xF2}; + + ret = usb_control_msg(udl->udev, + usb_sndctrlpipe(udl->udev, 0), + NR_USB_REQUEST_CHANNEL, + (USB_DIR_OUT | USB_TYPE_VENDOR), 0, 0, + set_def_chn, sizeof(set_def_chn), + USB_CTRL_SET_TIMEOUT); + return ret < 0 ? ret : 0; +} + static void udl_release_urb_work(struct work_struct *work) { struct urb_node *unode = container_of(work, struct urb_node, @@ -301,6 +323,9 @@ int udl_driver_load(struct drm_device *dev, unsigned long flags) goto err; } + if (udl_select_std_channel(udl)) + DRM_ERROR("Selecting channel failed\n"); + if (!udl_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) { DRM_ERROR("udl_alloc_urb_list failed\n"); goto err; diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index f92ea95..f2b2481 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -309,6 +309,8 @@ static int udl_crtc_mode_set(struct drm_crtc *crtc, char *wrptr; int color_depth = 0; + udl->crtc = crtc; + buf = (char *)udl->mode_buf; /* for now we just clip 24 -> 16 - if we fix that fix this */ @@ -441,8 +443,6 @@ int udl_modeset_init(struct drm_device *dev) dev->mode_config.funcs = &udl_mode_funcs; - drm_mode_create_dirty_info_property(dev); - udl_crtc_init(dev); encoder = udl_encoder_init(dev); @@ -452,6 +452,18 @@ int udl_modeset_init(struct drm_device *dev) return 0; } +void udl_modeset_restore(struct drm_device *dev) +{ + struct udl_device *udl = dev->dev_private; + struct udl_framebuffer *ufb; + + if (!udl->crtc || !udl->crtc->primary->fb) + return; + udl_crtc_commit(udl->crtc); + ufb = to_udl_fb(udl->crtc->primary->fb); + udl_handle_damage(ufb, 0, 0, ufb->base.width, ufb->base.height); +} + void udl_modeset_cleanup(struct drm_device *dev) { drm_mode_config_cleanup(dev); diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 59adcf8..3f6704c 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -144,7 +144,7 @@ static struct list_head *vc4_get_cache_list_for_size(struct drm_device *dev, return &vc4->bo_cache.size_list[page_index]; } -void vc4_bo_cache_purge(struct drm_device *dev) +static void vc4_bo_cache_purge(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 8fc2b73..2682f07 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -163,14 +163,6 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id, int vblank_lines; int ret = 0; - /* - * XXX Doesn't work well in interlaced mode yet, partially due - * to problems in vc4 kms or drm core interlaced mode handling, - * so disable for now in interlaced mode. - */ - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - return ret; - /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */ /* Get optional system timestamp before query. */ @@ -191,10 +183,15 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id, /* Vertical position of hvs composed scanline. */ *vpos = VC4_GET_FIELD(val, SCALER_DISPSTATX_LINE); + *hpos = 0; - /* No hpos info available. */ - if (hpos) - *hpos = 0; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + *vpos /= 2; + + /* Use hpos to correct for field offset in interlaced mode. */ + if (VC4_GET_FIELD(val, SCALER_DISPSTATX_FRAME_COUNT) % 2) + *hpos += mode->crtc_htotal / 2; + } /* This is the offset we need for translating hvs -> pv scanout pos. */ fifo_lines = vc4_crtc->cob_size / mode->crtc_hdisplay; @@ -217,8 +214,6 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id, * position of the PV. */ *vpos -= fifo_lines + 1; - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - *vpos /= 2; ret |= DRM_SCANOUTPOS_ACCURATE; return ret; @@ -480,6 +475,9 @@ static void vc4_crtc_disable(struct drm_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); @@ -530,6 +528,33 @@ static void vc4_crtc_enable(struct drm_crtc *crtc) /* Turn on the pixel valve, which will emit the vstart signal. */ CRTC_WRITE(PV_V_CONTROL, CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN); + + /* Enable vblank irq handling after crtc is started. */ + drm_crtc_vblank_on(crtc); +} + +static bool vc4_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + /* Do not allow doublescan modes from user space */ + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) { + DRM_DEBUG_KMS("[CRTC:%d] Doublescan mode rejected.\n", + crtc->base.id); + return false; + } + + /* + * Interlaced video modes got CRTC_INTERLACE_HALVE_V applied when + * coming from user space. We don't want this, as it screws up + * vblank timestamping, so fix it up. + */ + drm_mode_set_crtcinfo(adjusted_mode, 0); + + DRM_DEBUG_KMS("[CRTC:%d] adjusted_mode :\n", crtc->base.id); + drm_mode_debug_printmodeline(adjusted_mode); + + return true; } static int vc4_crtc_atomic_check(struct drm_crtc *crtc, @@ -819,6 +844,7 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { .mode_set_nofb = vc4_crtc_mode_set_nofb, .disable = vc4_crtc_disable, .enable = vc4_crtc_enable, + .mode_fixup = vc4_crtc_mode_fixup, .atomic_check = vc4_crtc_atomic_check, .atomic_flush = vc4_crtc_atomic_flush, }; diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index 275fedb..1e1f6b8 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -340,9 +340,20 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) } } +static bool vc4_dpi_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) + return false; + + return true; +} + static const struct drm_encoder_helper_funcs vc4_dpi_encoder_helper_funcs = { .disable = vc4_dpi_encoder_disable, .enable = vc4_dpi_encoder_enable, + .mode_fixup = vc4_dpi_encoder_mode_fixup, }; static const struct of_device_id vc4_dpi_dt_match[] = { diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 8b42d31..8703f56 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -16,6 +16,7 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include "drm_fb_cma_helper.h" +#include <drm/drm_fb_helper.h> #include "uapi/drm/vc4_drm.h" #include "vc4_drv.h" @@ -57,21 +58,21 @@ static int vc4_get_param_ioctl(struct drm_device *dev, void *data, switch (args->param) { case DRM_VC4_PARAM_V3D_IDENT0: ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev); - if (ret) + if (ret < 0) return ret; args->value = V3D_READ(V3D_IDENT0); pm_runtime_put(&vc4->v3d->pdev->dev); break; case DRM_VC4_PARAM_V3D_IDENT1: ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev); - if (ret) + if (ret < 0) return ret; args->value = V3D_READ(V3D_IDENT1); pm_runtime_put(&vc4->v3d->pdev->dev); break; case DRM_VC4_PARAM_V3D_IDENT2: ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev); - if (ret) + if (ret < 0) return ret; args->value = V3D_READ(V3D_IDENT2); pm_runtime_put(&vc4->v3d->pdev->dev); @@ -214,7 +215,7 @@ static void vc4_kick_out_firmware_fb(void) ap->ranges[0].base = 0; ap->ranges[0].size = ~0; - remove_conflicting_framebuffers(ap, "vc4drmfb", false); + drm_fb_helper_remove_conflicting_framebuffers(ap, "vc4drmfb", false); kfree(ap); } @@ -232,8 +233,8 @@ static int vc4_drm_bind(struct device *dev) return -ENOMEM; drm = drm_dev_alloc(&vc4_drm_driver, dev); - if (!drm) - return -ENOMEM; + if (IS_ERR(drm)) + return PTR_ERR(drm); platform_set_drvdata(pdev, drm); vc4->dev = drm; drm->dev_private = vc4; diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 489e3de..428e249 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -321,6 +321,15 @@ vc4_first_render_job(struct vc4_dev *vc4) struct vc4_exec_info, head); } +static inline struct vc4_exec_info * +vc4_last_render_job(struct vc4_dev *vc4) +{ + if (list_empty(&vc4->render_job_list)) + return NULL; + return list_last_entry(&vc4->render_job_list, + struct vc4_exec_info, head); +} + /** * struct vc4_texture_sample_info - saves the offsets into the UBO for texture * setup parameters. diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 6155e8a..77daea6 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -419,10 +419,6 @@ again: vc4_flush_caches(dev); - /* Disable the binner's pre-loaded overflow memory address */ - V3D_WRITE(V3D_BPOA, 0); - V3D_WRITE(V3D_BPOS, 0); - /* Either put the job in the binner if it uses the binner, or * immediately move it to the to-be-rendered queue. */ @@ -534,8 +530,8 @@ vc4_cl_lookup_bos(struct drm_device *dev, return -EINVAL; } - exec->bo = kcalloc(exec->bo_count, sizeof(struct drm_gem_cma_object *), - GFP_KERNEL); + exec->bo = drm_calloc_large(exec->bo_count, + sizeof(struct drm_gem_cma_object *)); if (!exec->bo) { DRM_ERROR("Failed to allocate validated BO pointers\n"); return -ENOMEM; @@ -572,8 +568,8 @@ vc4_cl_lookup_bos(struct drm_device *dev, spin_unlock(&file_priv->table_lock); fail: - kfree(handles); - return 0; + drm_free_large(handles); + return ret; } static int @@ -608,7 +604,7 @@ vc4_get_bcl(struct drm_device *dev, struct vc4_exec_info *exec) * read the contents back for validation, and I think the * bo->vaddr is uncached access. */ - temp = kmalloc(temp_size, GFP_KERNEL); + temp = drm_malloc_ab(temp_size, 1); if (!temp) { DRM_ERROR("Failed to allocate storage for copying " "in bin/render CLs.\n"); @@ -675,7 +671,7 @@ vc4_get_bcl(struct drm_device *dev, struct vc4_exec_info *exec) ret = vc4_validate_shader_recs(dev, exec); fail: - kfree(temp); + drm_free_large(temp); return ret; } @@ -688,7 +684,7 @@ vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec) if (exec->bo) { for (i = 0; i < exec->bo_count; i++) drm_gem_object_unreference_unlocked(&exec->bo[i]->base); - kfree(exec->bo); + drm_free_large(exec->bo); } while (!list_empty(&exec->unref_list)) { @@ -942,8 +938,8 @@ vc4_gem_destroy(struct drm_device *dev) vc4->overflow_mem = NULL; } - vc4_bo_cache_destroy(dev); - if (vc4->hang_state) vc4_free_hang_state(dev, vc4->hang_state); + + vc4_bo_cache_destroy(dev); } diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 4452f36..68ad106 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -208,10 +208,35 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) return ret; } +/* + * drm_helper_probe_single_connector_modes() applies drm_mode_set_crtcinfo to + * all modes with flag CRTC_INTERLACE_HALVE_V. We don't want this, as it + * screws up vblank timestamping for interlaced modes, so fix it up. + */ +static int vc4_hdmi_connector_probe_modes(struct drm_connector *connector, + uint32_t maxX, uint32_t maxY) +{ + struct drm_display_mode *mode; + int count; + + count = drm_helper_probe_single_connector_modes(connector, maxX, maxY); + if (count == 0) + return 0; + + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed adapted modes :\n", + connector->base.id, connector->name); + list_for_each_entry(mode, &connector->modes, head) { + drm_mode_set_crtcinfo(mode, 0); + drm_mode_debug_printmodeline(mode); + } + + return count; +} + static const struct drm_connector_funcs vc4_hdmi_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, .detect = vc4_hdmi_connector_detect, - .fill_modes = drm_helper_probe_single_connector_modes, + .fill_modes = vc4_hdmi_connector_probe_modes, .destroy = vc4_hdmi_connector_destroy, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, @@ -246,7 +271,7 @@ static struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev, connector->polled = (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT); - connector->interlace_allowed = 0; + connector->interlace_allowed = 1; connector->doublescan_allowed = 0; drm_mode_connector_attach_encoder(connector, encoder); diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index b0104a34..094bc6a 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -83,8 +83,10 @@ vc4_overflow_mem_work(struct work_struct *work) spin_lock_irqsave(&vc4->job_lock, irqflags); current_exec = vc4_first_bin_job(vc4); + if (!current_exec) + current_exec = vc4_last_render_job(vc4); if (current_exec) { - vc4->overflow_mem->seqno = vc4->finished_seqno + 1; + vc4->overflow_mem->seqno = current_exec->seqno; list_add_tail(&vc4->overflow_mem->unref_head, ¤t_exec->unref_list); vc4->overflow_mem = NULL; diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 4ac894d..c1f65c6 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -44,7 +44,7 @@ vc4_atomic_complete_commit(struct vc4_commit *c) drm_atomic_helper_commit_modeset_disables(dev, state); - drm_atomic_helper_commit_planes(dev, state, false); + drm_atomic_helper_commit_planes(dev, state, 0); drm_atomic_helper_commit_modeset_enables(dev, state); diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 29e4b40..881bf48 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -735,8 +735,6 @@ void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb) } static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { - .prepare_fb = NULL, - .cleanup_fb = NULL, .atomic_check = vc4_plane_atomic_check, .atomic_update = vc4_plane_atomic_update, }; diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index 46527e9..2543cf5 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -309,8 +309,14 @@ validate_uniform_address_write(struct vc4_validated_shader_info *validated_shade * of uniforms on each side. However, this scheme is easy to * validate so it's all we allow for now. */ - - if (QPU_GET_FIELD(inst, QPU_SIG) != QPU_SIG_NONE) { + switch (QPU_GET_FIELD(inst, QPU_SIG)) { + case QPU_SIG_NONE: + case QPU_SIG_SCOREBOARD_UNLOCK: + case QPU_SIG_COLOR_LOAD: + case QPU_SIG_LOAD_TMU0: + case QPU_SIG_LOAD_TMU1: + break; + default: DRM_ERROR("uniforms address change must be " "normal math\n"); return false; diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c index c15bafb..f36c147 100644 --- a/drivers/gpu/drm/vgem/vgem_drv.c +++ b/drivers/gpu/drm/vgem/vgem_drv.c @@ -334,8 +334,8 @@ static int __init vgem_init(void) int ret; vgem_device = drm_dev_alloc(&vgem_driver, NULL); - if (!vgem_device) { - ret = -ENOMEM; + if (IS_ERR(vgem_device)) { + ret = PTR_ERR(vgem_device); goto out; } diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c index ed8aa8f..e5582ba 100644 --- a/drivers/gpu/drm/via/via_drv.c +++ b/drivers/gpu/drm/via/via_drv.c @@ -72,7 +72,7 @@ static const struct file_operations via_driver_fops = { static struct drm_driver driver = { .driver_features = - DRIVER_USE_AGP | DRIVER_HAVE_IRQ | + DRIVER_USE_AGP | DRIVER_HAVE_IRQ | DRIVER_LEGACY | DRIVER_IRQ_SHARED, .load = via_driver_load, .unload = via_driver_unload, diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index 4e192aa..7cf3678 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -338,7 +338,8 @@ static void vgdev_atomic_commit_tail(struct drm_atomic_state *state) drm_atomic_helper_commit_modeset_disables(dev, state); drm_atomic_helper_commit_modeset_enables(dev, state); - drm_atomic_helper_commit_planes(dev, state, true); + drm_atomic_helper_commit_planes(dev, state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); drm_atomic_helper_commit_hw_done(state); diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c index 7f0e93f87..26197dd 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c +++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c @@ -24,6 +24,7 @@ */ #include <linux/pci.h> +#include <drm/drm_fb_helper.h> #include "virtgpu_drv.h" @@ -42,7 +43,7 @@ static void virtio_pci_kick_out_firmware_fb(struct pci_dev *pci_dev) primary = pci_dev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; - remove_conflicting_framebuffers(ap, "virtiodrmfb", primary); + drm_fb_helper_remove_conflicting_framebuffers(ap, "virtiodrmfb", primary); kfree(ap); } @@ -53,8 +54,8 @@ int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev) int ret; dev = drm_dev_alloc(driver, &vdev->dev); - if (!dev) - return -ENOMEM; + if (IS_ERR(dev)) + return PTR_ERR(dev); dev->virtdev = vdev; vdev->priv = dev; diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index b18ef31..06ad923 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -75,6 +75,7 @@ typedef void (*virtio_gpu_resp_cb)(struct virtio_gpu_device *vgdev, struct virtio_gpu_fence_driver { atomic64_t last_seq; uint64_t sync_seq; + uint64_t context; struct list_head fences; spinlock_t lock; }; diff --git a/drivers/gpu/drm/virtio/virtgpu_fence.c b/drivers/gpu/drm/virtio/virtgpu_fence.c index cf44187..f3f70fa 100644 --- a/drivers/gpu/drm/virtio/virtgpu_fence.c +++ b/drivers/gpu/drm/virtio/virtgpu_fence.c @@ -89,7 +89,7 @@ int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev, (*fence)->drv = drv; (*fence)->seq = ++drv->sync_seq; fence_init(&(*fence)->f, &virtio_fence_ops, &drv->lock, - 0, (*fence)->seq); + drv->context, (*fence)->seq); fence_get(&(*fence)->f); list_add_tail(&(*fence)->node, &drv->fences); spin_unlock_irqrestore(&drv->lock, irq_flags); diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c index c046903..818478b 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c @@ -89,10 +89,16 @@ static void virtio_gpu_unref_list(struct list_head *head) } } -static int virtio_gpu_execbuffer(struct drm_device *dev, - struct drm_virtgpu_execbuffer *exbuf, +/* + * Usage of execbuffer: + * Relocations need to take into account the full VIRTIO_GPUDrawable size. + * However, the command as passed from user space must *not* contain the initial + * VIRTIO_GPUReleaseInfo struct (first XXX bytes) + */ +static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data, struct drm_file *drm_file) { + struct drm_virtgpu_execbuffer *exbuf = data; struct virtio_gpu_device *vgdev = dev->dev_private; struct virtio_gpu_fpriv *vfpriv = drm_file->driver_priv; struct drm_gem_object *gobj; @@ -152,15 +158,10 @@ static int virtio_gpu_execbuffer(struct drm_device *dev, if (ret) goto out_free; - buf = kmalloc(exbuf->size, GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - goto out_unresv; - } - if (copy_from_user(buf, (void __user *)(uintptr_t)exbuf->command, - exbuf->size)) { - kfree(buf); - ret = -EFAULT; + buf = memdup_user((void __user *)(uintptr_t)exbuf->command, + exbuf->size); + if (IS_ERR(buf)) { + ret = PTR_ERR(buf); goto out_unresv; } virtio_gpu_cmd_submit(vgdev, buf, exbuf->size, @@ -182,20 +183,6 @@ out_free: return ret; } -/* - * Usage of execbuffer: - * Relocations need to take into account the full VIRTIO_GPUDrawable size. - * However, the command as passed from user space must *not* contain the initial - * VIRTIO_GPUReleaseInfo struct (first XXX bytes) - */ -static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_virtgpu_execbuffer *execbuffer = data; - return virtio_gpu_execbuffer(dev, execbuffer, file_priv); -} - - static int virtio_gpu_getparam_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index 4150873..036b0fb 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -159,6 +159,7 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags) virtio_gpu_init_vq(&vgdev->ctrlq, virtio_gpu_dequeue_ctrl_func); virtio_gpu_init_vq(&vgdev->cursorq, virtio_gpu_dequeue_cursor_func); + vgdev->fence_drv.context = fence_context_alloc(1); spin_lock_init(&vgdev->fence_drv.lock); INIT_LIST_HEAD(&vgdev->fence_drv.fences); INIT_LIST_HEAD(&vgdev->cap_cache); diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c index 925ca25..ba28c0f 100644 --- a/drivers/gpu/drm/virtio/virtgpu_plane.c +++ b/drivers/gpu/drm/virtio/virtgpu_plane.c @@ -76,7 +76,8 @@ static void virtio_gpu_primary_plane_update(struct drm_plane *plane, output = drm_crtc_to_virtio_gpu_output(plane->state->crtc); if (old_state->crtc) output = drm_crtc_to_virtio_gpu_output(old_state->crtc); - WARN_ON(!output); + if (WARN_ON(!output)) + return; if (plane->state->fb) { vgfb = to_virtio_gpu_framebuffer(plane->state->fb); @@ -129,7 +130,8 @@ static void virtio_gpu_cursor_plane_update(struct drm_plane *plane, output = drm_crtc_to_virtio_gpu_output(plane->state->crtc); if (old_state->crtc) output = drm_crtc_to_virtio_gpu_output(old_state->crtc); - WARN_ON(!output); + if (WARN_ON(!output)) + return; if (plane->state->fb) { vgfb = to_virtio_gpu_framebuffer(plane->state->fb); diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig index b49445d..fb7b82a 100644 --- a/drivers/gpu/drm/vmwgfx/Kconfig +++ b/drivers/gpu/drm/vmwgfx/Kconfig @@ -6,6 +6,7 @@ config DRM_VMWGFX select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select DRM_TTM + select FB # Only needed for the transitional use of drm_crtc_init - can be removed # again once vmwgfx sets up the primary plane itself. select DRM_KMS_HELPER diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 63ccd98..23ec673 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -377,9 +377,6 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) drm_mode_crtc_set_gamma_size(crtc, 256); drm_object_attach_property(&connector->base, - dev->mode_config.dirty_info_property, - 1); - drm_object_attach_property(&connector->base, dev_priv->hotplug_mode_update_property, 1); drm_object_attach_property(&connector->base, dev->mode_config.suggested_x_property, 0); @@ -421,10 +418,6 @@ int vmw_kms_ldu_init_display(struct vmw_private *dev_priv) if (ret != 0) goto err_free; - ret = drm_mode_create_dirty_info_property(dev); - if (ret != 0) - goto err_vblank_cleanup; - vmw_kms_create_implicit_placement_property(dev_priv, true); if (dev_priv->capabilities & SVGA_CAP_MULTIMON) @@ -439,8 +432,6 @@ int vmw_kms_ldu_init_display(struct vmw_private *dev_priv) return 0; -err_vblank_cleanup: - drm_vblank_cleanup(dev); err_free: kfree(dev_priv->ldu_priv); dev_priv->ldu_priv = NULL; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index b74eae2..f423590 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -538,9 +538,6 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) drm_mode_crtc_set_gamma_size(crtc, 256); drm_object_attach_property(&connector->base, - dev->mode_config.dirty_info_property, - 1); - drm_object_attach_property(&connector->base, dev_priv->hotplug_mode_update_property, 1); drm_object_attach_property(&connector->base, dev->mode_config.suggested_x_property, 0); @@ -574,10 +571,6 @@ int vmw_kms_sou_init_display(struct vmw_private *dev_priv) if (unlikely(ret != 0)) return ret; - ret = drm_mode_create_dirty_info_property(dev); - if (unlikely(ret != 0)) - goto err_vblank_cleanup; - vmw_kms_create_implicit_placement_property(dev_priv, false); for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) @@ -588,10 +581,6 @@ int vmw_kms_sou_init_display(struct vmw_private *dev_priv) DRM_INFO("Screen Objects Display Unit initialized\n"); return 0; - -err_vblank_cleanup: - drm_vblank_cleanup(dev); - return ret; } int vmw_kms_sou_close_display(struct vmw_private *dev_priv) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 41932a7..94ad8d2 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -1131,9 +1131,6 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) drm_mode_crtc_set_gamma_size(crtc, 256); drm_object_attach_property(&connector->base, - dev->mode_config.dirty_info_property, - 1); - drm_object_attach_property(&connector->base, dev_priv->hotplug_mode_update_property, 1); drm_object_attach_property(&connector->base, dev->mode_config.suggested_x_property, 0); @@ -1202,10 +1199,6 @@ int vmw_kms_stdu_init_display(struct vmw_private *dev_priv) if (unlikely(ret != 0)) return ret; - ret = drm_mode_create_dirty_info_property(dev); - if (unlikely(ret != 0)) - goto err_vblank_cleanup; - dev_priv->active_display_unit = vmw_du_screen_target; vmw_kms_create_implicit_placement_property(dev_priv, false); diff --git a/drivers/gpu/host1x/mipi.c b/drivers/gpu/host1x/mipi.c index 52a6fd2..e00809d 100644 --- a/drivers/gpu/host1x/mipi.c +++ b/drivers/gpu/host1x/mipi.c @@ -242,20 +242,6 @@ struct tegra_mipi_device *tegra_mipi_request(struct device *device) dev->pads = args.args[0]; dev->device = device; - mutex_lock(&dev->mipi->lock); - - if (dev->mipi->usage_count++ == 0) { - err = tegra_mipi_power_up(dev->mipi); - if (err < 0) { - dev_err(dev->mipi->dev, - "failed to power up MIPI bricks: %d\n", - err); - return ERR_PTR(err); - } - } - - mutex_unlock(&dev->mipi->lock); - return dev; put: @@ -270,29 +256,42 @@ EXPORT_SYMBOL(tegra_mipi_request); void tegra_mipi_free(struct tegra_mipi_device *device) { - int err; + platform_device_put(device->pdev); + kfree(device); +} +EXPORT_SYMBOL(tegra_mipi_free); - mutex_lock(&device->mipi->lock); +int tegra_mipi_enable(struct tegra_mipi_device *dev) +{ + int err = 0; - if (--device->mipi->usage_count == 0) { - err = tegra_mipi_power_down(device->mipi); - if (err < 0) { - /* - * Not much that can be done here, so an error message - * will have to do. - */ - dev_err(device->mipi->dev, - "failed to power down MIPI bricks: %d\n", - err); - } - } + mutex_lock(&dev->mipi->lock); - mutex_unlock(&device->mipi->lock); + if (dev->mipi->usage_count++ == 0) + err = tegra_mipi_power_up(dev->mipi); + + mutex_unlock(&dev->mipi->lock); + + return err; - platform_device_put(device->pdev); - kfree(device); } -EXPORT_SYMBOL(tegra_mipi_free); +EXPORT_SYMBOL(tegra_mipi_enable); + +int tegra_mipi_disable(struct tegra_mipi_device *dev) +{ + int err = 0; + + mutex_lock(&dev->mipi->lock); + + if (--dev->mipi->usage_count == 0) + err = tegra_mipi_power_down(dev->mipi); + + mutex_unlock(&dev->mipi->lock); + + return err; + +} +EXPORT_SYMBOL(tegra_mipi_disable); static int tegra_mipi_wait(struct tegra_mipi *mipi) { diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile index 107ec23..5f96141 100644 --- a/drivers/gpu/ipu-v3/Makefile +++ b/drivers/gpu/ipu-v3/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ - ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o + ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-image-convert.o \ + ipu-smfc.o ipu-vdi.o diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index 99dcacf..b9539f7 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -45,6 +45,12 @@ static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) writel(value, ipu->cm_reg + offset); } +int ipu_get_num(struct ipu_soc *ipu) +{ + return ipu->id; +} +EXPORT_SYMBOL_GPL(ipu_get_num); + void ipu_srm_dp_sync_update(struct ipu_soc *ipu) { u32 val; @@ -724,6 +730,137 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) } EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux); + +/* Frame Synchronization Unit Channel Linking */ + +struct fsu_link_reg_info { + int chno; + u32 reg; + u32 mask; + u32 val; +}; + +struct fsu_link_info { + struct fsu_link_reg_info src; + struct fsu_link_reg_info sink; +}; + +static const struct fsu_link_info fsu_link_info[] = { + { + .src = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2, + FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC }, + .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1, + FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC }, + }, { + .src = { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2, + FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF }, + .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1, + FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF }, + }, { + .src = { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2, + FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP }, + .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1, + FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP }, + }, { + .src = { IPUV3_CHANNEL_CSI_DIRECT, 0 }, + .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1, + FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT }, + }, +}; + +static const struct fsu_link_info *find_fsu_link_info(int src, int sink) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) { + if (src == fsu_link_info[i].src.chno && + sink == fsu_link_info[i].sink.chno) + return &fsu_link_info[i]; + } + + return NULL; +} + +/* + * Links a source channel to a sink channel in the FSU. + */ +int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch) +{ + const struct fsu_link_info *link; + u32 src_reg, sink_reg; + unsigned long flags; + + link = find_fsu_link_info(src_ch, sink_ch); + if (!link) + return -EINVAL; + + spin_lock_irqsave(&ipu->lock, flags); + + if (link->src.mask) { + src_reg = ipu_cm_read(ipu, link->src.reg); + src_reg &= ~link->src.mask; + src_reg |= link->src.val; + ipu_cm_write(ipu, src_reg, link->src.reg); + } + + if (link->sink.mask) { + sink_reg = ipu_cm_read(ipu, link->sink.reg); + sink_reg &= ~link->sink.mask; + sink_reg |= link->sink.val; + ipu_cm_write(ipu, sink_reg, link->sink.reg); + } + + spin_unlock_irqrestore(&ipu->lock, flags); + return 0; +} +EXPORT_SYMBOL_GPL(ipu_fsu_link); + +/* + * Unlinks source and sink channels in the FSU. + */ +int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch) +{ + const struct fsu_link_info *link; + u32 src_reg, sink_reg; + unsigned long flags; + + link = find_fsu_link_info(src_ch, sink_ch); + if (!link) + return -EINVAL; + + spin_lock_irqsave(&ipu->lock, flags); + + if (link->src.mask) { + src_reg = ipu_cm_read(ipu, link->src.reg); + src_reg &= ~link->src.mask; + ipu_cm_write(ipu, src_reg, link->src.reg); + } + + if (link->sink.mask) { + sink_reg = ipu_cm_read(ipu, link->sink.reg); + sink_reg &= ~link->sink.mask; + ipu_cm_write(ipu, sink_reg, link->sink.reg); + } + + spin_unlock_irqrestore(&ipu->lock, flags); + return 0; +} +EXPORT_SYMBOL_GPL(ipu_fsu_unlink); + +/* Link IDMAC channels in the FSU */ +int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink) +{ + return ipu_fsu_link(src->ipu, src->num, sink->num); +} +EXPORT_SYMBOL_GPL(ipu_idmac_link); + +/* Unlink IDMAC channels in the FSU */ +int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink) +{ + return ipu_fsu_unlink(src->ipu, src->num, sink->num); +} +EXPORT_SYMBOL_GPL(ipu_idmac_unlink); + struct ipu_devtype { const char *name; unsigned long cm_ofs; @@ -833,6 +970,20 @@ static int ipu_submodules_init(struct ipu_soc *ipu, goto err_ic; } + ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs, + IPU_CONF_VDI_EN | IPU_CONF_ISP_EN | + IPU_CONF_IC_INPUT); + if (ret) { + unit = "vdi"; + goto err_vdi; + } + + ret = ipu_image_convert_init(ipu, dev); + if (ret) { + unit = "image_convert"; + goto err_image_convert; + } + ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, IPU_CONF_DI0_EN, ipu_clk); if (ret) { @@ -887,6 +1038,10 @@ err_dc: err_di_1: ipu_di_exit(ipu, 0); err_di_0: + ipu_image_convert_exit(ipu); +err_image_convert: + ipu_vdi_exit(ipu); +err_vdi: ipu_ic_exit(ipu); err_ic: ipu_csi_exit(ipu, 1); @@ -971,6 +1126,8 @@ static void ipu_submodules_exit(struct ipu_soc *ipu) ipu_dc_exit(ipu); ipu_di_exit(ipu, 1); ipu_di_exit(ipu, 0); + ipu_image_convert_exit(ipu); + ipu_vdi_exit(ipu); ipu_ic_exit(ipu); ipu_csi_exit(ipu, 1); ipu_csi_exit(ipu, 0); @@ -1004,14 +1161,14 @@ static struct ipu_platform_reg client_reg[] = { .dma[0] = IPUV3_CHANNEL_CSI0, .dma[1] = -EINVAL, }, - .name = "imx-ipuv3-camera", + .name = "imx-ipuv3-csi", }, { .pdata = { .csi = 1, .dma[0] = IPUV3_CHANNEL_CSI1, .dma[1] = -EINVAL, }, - .name = "imx-ipuv3-camera", + .name = "imx-ipuv3-csi", }, { .pdata = { .di = 0, @@ -1207,15 +1364,16 @@ EXPORT_SYMBOL_GPL(ipu_dump); static int ipu_probe(struct platform_device *pdev) { - const struct of_device_id *of_id = - of_match_device(imx_ipu_dt_ids, &pdev->dev); + struct device_node *np = pdev->dev.of_node; struct ipu_soc *ipu; struct resource *res; unsigned long ipu_base; int i, ret, irq_sync, irq_err; const struct ipu_devtype *devtype; - devtype = of_id->data; + devtype = of_device_get_match_data(&pdev->dev); + if (!devtype) + return -EINVAL; irq_sync = platform_get_irq(pdev, 0); irq_err = platform_get_irq(pdev, 1); @@ -1237,6 +1395,7 @@ static int ipu_probe(struct platform_device *pdev) ipu->channel[i].ipu = ipu; ipu->devtype = devtype; ipu->ipu_type = devtype->type; + ipu->id = of_alias_get_id(np, "ipu"); spin_lock_init(&ipu->lock); mutex_init(&ipu->channel_lock); diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c index 6494a4d..fcb7dc8 100644 --- a/drivers/gpu/ipu-v3/ipu-cpmem.c +++ b/drivers/gpu/ipu-v3/ipu-cpmem.c @@ -253,6 +253,13 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer); +void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) +{ + ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8); + ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8); +} +EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); + void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride) { ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); @@ -268,6 +275,12 @@ void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id) } EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id); +int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch) +{ + return ipu_ch_param_read_field(ch, IPU_FIELD_NPB) + 1; +} +EXPORT_SYMBOL_GPL(ipu_cpmem_get_burstsize); + void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize) { ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1); diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index 06631ac..d6e5ded 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -258,12 +258,8 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code) cfg->data_width = IPU_CSI_DATA_WIDTH_8; break; case MEDIA_BUS_FMT_UYVY8_1X16: - cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY; - cfg->mipi_dt = MIPI_DT_YUV422; - cfg->data_width = IPU_CSI_DATA_WIDTH_16; - break; case MEDIA_BUS_FMT_YUYV8_1X16: - cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV; + cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; cfg->mipi_dt = MIPI_DT_YUV422; cfg->data_width = IPU_CSI_DATA_WIDTH_16; break; @@ -365,10 +361,14 @@ int ipu_csi_init_interface(struct ipu_csi *csi, { struct ipu_csi_bus_config cfg; unsigned long flags; - u32 data = 0; + u32 width, height, data = 0; fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt); + /* set default sensor frame width and height */ + width = mbus_fmt->width; + height = mbus_fmt->height; + /* Set the CSI_SENS_CONF register remaining fields */ data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT | cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT | @@ -386,11 +386,6 @@ int ipu_csi_init_interface(struct ipu_csi *csi, ipu_csi_write(csi, data, CSI_SENS_CONF); - /* Setup sensor frame size */ - ipu_csi_write(csi, - (mbus_fmt->width - 1) | ((mbus_fmt->height - 1) << 16), - CSI_SENS_FRM_SIZE); - /* Set CCIR registers */ switch (cfg.clk_mode) { @@ -408,11 +403,12 @@ int ipu_csi_init_interface(struct ipu_csi *csi, * Field1BlankEnd = 0x7, Field1BlankStart = 0x3, * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1 */ + height = 625; /* framelines for PAL */ + ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, CSI_CCIR_CODE_1); ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); - } else if (mbus_fmt->width == 720 && mbus_fmt->height == 480) { /* * NTSC case @@ -422,6 +418,8 @@ int ipu_csi_init_interface(struct ipu_csi *csi, * Field1BlankEnd = 0x6, Field1BlankStart = 0x2, * Field1ActiveEnd = 0x4, Field1ActiveStart = 0 */ + height = 525; /* framelines for NTSC */ + ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, CSI_CCIR_CODE_1); ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); @@ -447,6 +445,10 @@ int ipu_csi_init_interface(struct ipu_csi *csi, break; } + /* Setup sensor frame size */ + ipu_csi_write(csi, (width - 1) | ((height - 1) << 16), + CSI_SENS_FRM_SIZE); + dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n", ipu_csi_read(csi, CSI_SENS_CONF)); dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n", diff --git a/drivers/gpu/ipu-v3/ipu-dmfc.c b/drivers/gpu/ipu-v3/ipu-dmfc.c index 42705bb..a40f211 100644 --- a/drivers/gpu/ipu-v3/ipu-dmfc.c +++ b/drivers/gpu/ipu-v3/ipu-dmfc.c @@ -123,20 +123,6 @@ int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc) } EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel); -static void ipu_dmfc_wait_fifos(struct ipu_dmfc_priv *priv) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(1000); - - while ((readl(priv->base + DMFC_STAT) & 0x02fff000) != 0x02fff000) { - if (time_after(jiffies, timeout)) { - dev_warn(priv->dev, - "Timeout waiting for DMFC FIFOs to clear\n"); - break; - } - cpu_relax(); - } -} - void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) { struct ipu_dmfc_priv *priv = dmfc->priv; @@ -145,10 +131,8 @@ void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) priv->use_count--; - if (!priv->use_count) { - ipu_dmfc_wait_fifos(priv); + if (!priv->use_count) ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN); - } if (priv->use_count < 0) priv->use_count = 0; diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c index 1dcb96c..321eb98 100644 --- a/drivers/gpu/ipu-v3/ipu-ic.c +++ b/drivers/gpu/ipu-v3/ipu-ic.c @@ -160,6 +160,7 @@ struct ipu_ic_priv { spinlock_t lock; struct ipu_soc *ipu; int use_count; + int irt_use_count; struct ipu_ic task[IC_NUM_TASKS]; }; @@ -379,8 +380,6 @@ void ipu_ic_task_disable(struct ipu_ic *ic) ipu_ic_write(ic, ic_conf, IC_CONF); - ic->rotation = ic->graphics = false; - spin_unlock_irqrestore(&priv->lock, flags); } EXPORT_SYMBOL_GPL(ipu_ic_task_disable); @@ -620,7 +619,7 @@ int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel, ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2); ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3); - if (rot >= IPU_ROTATE_90_RIGHT) + if (ipu_rot_mode_is_irt(rot)) ic->rotation = true; unlock: @@ -629,22 +628,41 @@ unlock: } EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init); +static void ipu_irt_enable(struct ipu_ic *ic) +{ + struct ipu_ic_priv *priv = ic->priv; + + if (!priv->irt_use_count) + ipu_module_enable(priv->ipu, IPU_CONF_ROT_EN); + + priv->irt_use_count++; +} + +static void ipu_irt_disable(struct ipu_ic *ic) +{ + struct ipu_ic_priv *priv = ic->priv; + + if (priv->irt_use_count) { + if (!--priv->irt_use_count) + ipu_module_disable(priv->ipu, IPU_CONF_ROT_EN); + } +} + int ipu_ic_enable(struct ipu_ic *ic) { struct ipu_ic_priv *priv = ic->priv; unsigned long flags; - u32 module = IPU_CONF_IC_EN; spin_lock_irqsave(&priv->lock, flags); - if (ic->rotation) - module |= IPU_CONF_ROT_EN; - if (!priv->use_count) - ipu_module_enable(priv->ipu, module); + ipu_module_enable(priv->ipu, IPU_CONF_IC_EN); priv->use_count++; + if (ic->rotation) + ipu_irt_enable(ic); + spin_unlock_irqrestore(&priv->lock, flags); return 0; @@ -655,18 +673,22 @@ int ipu_ic_disable(struct ipu_ic *ic) { struct ipu_ic_priv *priv = ic->priv; unsigned long flags; - u32 module = IPU_CONF_IC_EN | IPU_CONF_ROT_EN; spin_lock_irqsave(&priv->lock, flags); priv->use_count--; if (!priv->use_count) - ipu_module_disable(priv->ipu, module); + ipu_module_disable(priv->ipu, IPU_CONF_IC_EN); if (priv->use_count < 0) priv->use_count = 0; + if (ic->rotation) + ipu_irt_disable(ic); + + ic->rotation = ic->graphics = false; + spin_unlock_irqrestore(&priv->lock, flags); return 0; diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c new file mode 100644 index 0000000..2ba7d43 --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -0,0 +1,1709 @@ +/* + * Copyright (C) 2012-2016 Mentor Graphics Inc. + * + * Queued image conversion support, with tiling and rotation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <video/imx-ipu-image-convert.h> +#include "ipu-prv.h" + +/* + * The IC Resizer has a restriction that the output frame from the + * resizer must be 1024 or less in both width (pixels) and height + * (lines). + * + * The image converter attempts to split up a conversion when + * the desired output (converted) frame resolution exceeds the + * IC resizer limit of 1024 in either dimension. + * + * If either dimension of the output frame exceeds the limit, the + * dimension is split into 1, 2, or 4 equal stripes, for a maximum + * of 4*4 or 16 tiles. A conversion is then carried out for each + * tile (but taking care to pass the full frame stride length to + * the DMA channel's parameter memory!). IDMA double-buffering is used + * to convert each tile back-to-back when possible (see note below + * when double_buffering boolean is set). + * + * Note that the input frame must be split up into the same number + * of tiles as the output frame. + * + * FIXME: at this point there is no attempt to deal with visible seams + * at the tile boundaries when upscaling. The seams are caused by a reset + * of the bilinear upscale interpolation when starting a new tile. The + * seams are barely visible for small upscale factors, but become + * increasingly visible as the upscale factor gets larger, since more + * interpolated pixels get thrown out at the tile boundaries. A possilble + * fix might be to overlap tiles of different sizes, but this must be done + * while also maintaining the IDMAC dma buffer address alignment and 8x8 IRT + * alignment restrictions of each tile. + */ + +#define MAX_STRIPES_W 4 +#define MAX_STRIPES_H 4 +#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H) + +#define MIN_W 16 +#define MIN_H 8 +#define MAX_W 4096 +#define MAX_H 4096 + +enum ipu_image_convert_type { + IMAGE_CONVERT_IN = 0, + IMAGE_CONVERT_OUT, +}; + +struct ipu_image_convert_dma_buf { + void *virt; + dma_addr_t phys; + unsigned long len; +}; + +struct ipu_image_convert_dma_chan { + int in; + int out; + int rot_in; + int rot_out; + int vdi_in_p; + int vdi_in; + int vdi_in_n; +}; + +/* dimensions of one tile */ +struct ipu_image_tile { + u32 width; + u32 height; + /* size and strides are in bytes */ + u32 size; + u32 stride; + u32 rot_stride; + /* start Y or packed offset of this tile */ + u32 offset; + /* offset from start to tile in U plane, for planar formats */ + u32 u_off; + /* offset from start to tile in V plane, for planar formats */ + u32 v_off; +}; + +struct ipu_image_convert_image { + struct ipu_image base; + enum ipu_image_convert_type type; + + const struct ipu_image_pixfmt *fmt; + unsigned int stride; + + /* # of rows (horizontal stripes) if dest height is > 1024 */ + unsigned int num_rows; + /* # of columns (vertical stripes) if dest width is > 1024 */ + unsigned int num_cols; + + struct ipu_image_tile tile[MAX_TILES]; +}; + +struct ipu_image_pixfmt { + u32 fourcc; /* V4L2 fourcc */ + int bpp; /* total bpp */ + int uv_width_dec; /* decimation in width for U/V planes */ + int uv_height_dec; /* decimation in height for U/V planes */ + bool planar; /* planar format */ + bool uv_swapped; /* U and V planes are swapped */ + bool uv_packed; /* partial planar (U and V in same plane) */ +}; + +struct ipu_image_convert_ctx; +struct ipu_image_convert_chan; +struct ipu_image_convert_priv; + +struct ipu_image_convert_ctx { + struct ipu_image_convert_chan *chan; + + ipu_image_convert_cb_t complete; + void *complete_context; + + /* Source/destination image data and rotation mode */ + struct ipu_image_convert_image in; + struct ipu_image_convert_image out; + enum ipu_rotate_mode rot_mode; + + /* intermediate buffer for rotation */ + struct ipu_image_convert_dma_buf rot_intermediate[2]; + + /* current buffer number for double buffering */ + int cur_buf_num; + + bool aborting; + struct completion aborted; + + /* can we use double-buffering for this conversion operation? */ + bool double_buffering; + /* num_rows * num_cols */ + unsigned int num_tiles; + /* next tile to process */ + unsigned int next_tile; + /* where to place converted tile in dest image */ + unsigned int out_tile_map[MAX_TILES]; + + struct list_head list; +}; + +struct ipu_image_convert_chan { + struct ipu_image_convert_priv *priv; + + enum ipu_ic_task ic_task; + const struct ipu_image_convert_dma_chan *dma_ch; + + struct ipu_ic *ic; + struct ipuv3_channel *in_chan; + struct ipuv3_channel *out_chan; + struct ipuv3_channel *rotation_in_chan; + struct ipuv3_channel *rotation_out_chan; + + /* the IPU end-of-frame irqs */ + int out_eof_irq; + int rot_out_eof_irq; + + spinlock_t irqlock; + + /* list of convert contexts */ + struct list_head ctx_list; + /* queue of conversion runs */ + struct list_head pending_q; + /* queue of completed runs */ + struct list_head done_q; + + /* the current conversion run */ + struct ipu_image_convert_run *current_run; +}; + +struct ipu_image_convert_priv { + struct ipu_image_convert_chan chan[IC_NUM_TASKS]; + struct ipu_soc *ipu; +}; + +static const struct ipu_image_convert_dma_chan +image_convert_dma_chan[IC_NUM_TASKS] = { + [IC_TASK_VIEWFINDER] = { + .in = IPUV3_CHANNEL_MEM_IC_PRP_VF, + .out = IPUV3_CHANNEL_IC_PRP_VF_MEM, + .rot_in = IPUV3_CHANNEL_MEM_ROT_VF, + .rot_out = IPUV3_CHANNEL_ROT_VF_MEM, + .vdi_in_p = IPUV3_CHANNEL_MEM_VDI_PREV, + .vdi_in = IPUV3_CHANNEL_MEM_VDI_CUR, + .vdi_in_n = IPUV3_CHANNEL_MEM_VDI_NEXT, + }, + [IC_TASK_POST_PROCESSOR] = { + .in = IPUV3_CHANNEL_MEM_IC_PP, + .out = IPUV3_CHANNEL_IC_PP_MEM, + .rot_in = IPUV3_CHANNEL_MEM_ROT_PP, + .rot_out = IPUV3_CHANNEL_ROT_PP_MEM, + }, +}; + +static const struct ipu_image_pixfmt image_convert_formats[] = { + { + .fourcc = V4L2_PIX_FMT_RGB565, + .bpp = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, + .bpp = 24, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, + .bpp = 24, + }, { + .fourcc = V4L2_PIX_FMT_RGB32, + .bpp = 32, + }, { + .fourcc = V4L2_PIX_FMT_BGR32, + .bpp = 32, + }, { + .fourcc = V4L2_PIX_FMT_YUYV, + .bpp = 16, + .uv_width_dec = 2, + .uv_height_dec = 1, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .bpp = 16, + .uv_width_dec = 2, + .uv_height_dec = 1, + }, { + .fourcc = V4L2_PIX_FMT_YUV420, + .bpp = 12, + .planar = true, + .uv_width_dec = 2, + .uv_height_dec = 2, + }, { + .fourcc = V4L2_PIX_FMT_YVU420, + .bpp = 12, + .planar = true, + .uv_width_dec = 2, + .uv_height_dec = 2, + .uv_swapped = true, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .bpp = 12, + .planar = true, + .uv_width_dec = 2, + .uv_height_dec = 2, + .uv_packed = true, + }, { + .fourcc = V4L2_PIX_FMT_YUV422P, + .bpp = 16, + .planar = true, + .uv_width_dec = 2, + .uv_height_dec = 1, + }, { + .fourcc = V4L2_PIX_FMT_NV16, + .bpp = 16, + .planar = true, + .uv_width_dec = 2, + .uv_height_dec = 1, + .uv_packed = true, + }, +}; + +static const struct ipu_image_pixfmt *get_format(u32 fourcc) +{ + const struct ipu_image_pixfmt *ret = NULL; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(image_convert_formats); i++) { + if (image_convert_formats[i].fourcc == fourcc) { + ret = &image_convert_formats[i]; + break; + } + } + + return ret; +} + +static void dump_format(struct ipu_image_convert_ctx *ctx, + struct ipu_image_convert_image *ic_image) +{ + struct ipu_image_convert_chan *chan = ctx->chan; + struct ipu_image_convert_priv *priv = chan->priv; + + dev_dbg(priv->ipu->dev, + "task %u: ctx %p: %s format: %dx%d (%dx%d tiles of size %dx%d), %c%c%c%c\n", + chan->ic_task, ctx, + ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input", + ic_image->base.pix.width, ic_image->base.pix.height, + ic_image->num_cols, ic_image->num_rows, + ic_image->tile[0].width, ic_image->tile[0].height, + ic_image->fmt->fourcc & 0xff, + (ic_image->fmt->fourcc >> 8) & 0xff, + (ic_image->fmt->fourcc >> 16) & 0xff, + (ic_image->fmt->fourcc >> 24) & 0xff); +} + +int ipu_image_convert_enum_format(int index, u32 *fourcc) +{ + const struct ipu_image_pixfmt *fmt; + + if (index >= (int)ARRAY_SIZE(image_convert_formats)) + return -EINVAL; + + /* Format found */ + fmt = &image_convert_formats[index]; + *fourcc = fmt->fourcc; + return 0; +} +EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format); + +static void free_dma_buf(struct ipu_image_convert_priv *priv, + struct ipu_image_convert_dma_buf *buf) +{ + if (buf->virt) + dma_free_coherent(priv->ipu->dev, + buf->len, buf->virt, buf->phys); + buf->virt = NULL; + buf->phys = 0; +} + +static int alloc_dma_buf(struct ipu_image_convert_priv *priv, + struct ipu_image_convert_dma_buf *buf, + int size) +{ + buf->len = PAGE_ALIGN(size); + buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys, + GFP_DMA | GFP_KERNEL); + if (!buf->virt) { + dev_err(priv->ipu->dev, "failed to alloc dma buffer\n"); + return -ENOMEM; + } + + return 0; +} + +static inline int num_stripes(int dim) +{ + if (dim <= 1024) + return 1; + else if (dim <= 2048) + return 2; + else + return 4; +} + +static void calc_tile_dimensions(struct ipu_image_convert_ctx *ctx, + struct ipu_image_convert_image *image) +{ + int i; + + for (i = 0; i < ctx->num_tiles; i++) { + struct ipu_image_tile *tile = &image->tile[i]; + + tile->height = image->base.pix.height / image->num_rows; + tile->width = image->base.pix.width / image->num_cols; + tile->size = ((tile->height * image->fmt->bpp) >> 3) * + tile->width; + + if (image->fmt->planar) { + tile->stride = tile->width; + tile->rot_stride = tile->height; + } else { + tile->stride = + (image->fmt->bpp * tile->width) >> 3; + tile->rot_stride = + (image->fmt->bpp * tile->height) >> 3; + } + } +} + +/* + * Use the rotation transformation to find the tile coordinates + * (row, col) of a tile in the destination frame that corresponds + * to the given tile coordinates of a source frame. The destination + * coordinate is then converted to a tile index. + */ +static int transform_tile_index(struct ipu_image_convert_ctx *ctx, + int src_row, int src_col) +{ + struct ipu_image_convert_chan *chan = ctx->chan; + struct ipu_image_convert_priv *priv = chan->priv; + struct ipu_image_convert_image *s_image = &ctx->in; + struct ipu_image_convert_image *d_image = &ctx->out; + int dst_row, dst_col; + + /* with no rotation it's a 1:1 mapping */ + if (ctx->rot_mode == IPU_ROTATE_NONE) + return src_row * s_image->num_cols + src_col; + + /* + * before doing the transform, first we have to translate + * source row,col for an origin in the center of s_image + */ + src_row = src_row * 2 - (s_image->num_rows - 1); + src_col = src_col * 2 - (s_image->num_cols - 1); + + /* do the rotation transform */ + if (ctx->rot_mode & IPU_ROT_BIT_90) { + dst_col = -src_row; + dst_row = src_col; + } else { + dst_col = src_col; + dst_row = src_row; + } + + /* apply flip */ + if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) + dst_col = -dst_col; + if (ctx->rot_mode & IPU_ROT_BIT_VFLIP) + dst_row = -dst_row; + + dev_dbg(priv->ipu->dev, "task %u: ctx %p: [%d,%d] --> [%d,%d]\n", + chan->ic_task, ctx, src_col, src_row, dst_col, dst_row); + + /* + * finally translate dest row,col using an origin in upper + * left of d_image + */ + dst_row += d_image->num_rows - 1; + dst_col += d_image->num_cols - 1; + dst_row /= 2; + dst_col /= 2; + + return dst_row * d_image->num_cols + dst_col; +} + +/* + * Fill the out_tile_map[] with transformed destination tile indeces. + */ +static void calc_out_tile_map(struct ipu_image_convert_ctx *ctx) +{ + struct ipu_image_convert_image *s_image = &ctx->in; + unsigned int row, col, tile = 0; + + for (row = 0; row < s_image->num_rows; row++) { + for (col = 0; col < s_image->num_cols; col++) { + ctx->out_tile_map[tile] = + transform_tile_index(ctx, row, col); + tile++; + } + } +} + +static void calc_tile_offsets_planar(struct ipu_image_convert_ctx *ctx, + struct ipu_image_convert_image *image) +{ + struct ipu_image_convert_chan *chan = ctx->chan; + struct ipu_image_convert_priv *priv = chan->priv; + const struct ipu_image_pixfmt *fmt = image->fmt; + unsigned int row, col, tile = 0; + u32 H, w, h, y_stride, uv_stride; + u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp; + u32 y_row_off, y_col_off, y_off; + u32 y_size, uv_size; + + /* setup some convenience vars */ + H = image->base.pix.height; + + y_stride = image->stride; + uv_stride = y_stride / fmt->uv_width_dec; + if (fmt->uv_packed) + uv_stride *= 2; + + y_size = H * y_stride; + uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec); + + for (row = 0; row < image->num_rows; row++) { + w = image->tile[tile].width; + h = image->tile[tile].height; + y_row_off = row * h * y_stride; + uv_row_off = (row * h * uv_stride) / fmt->uv_height_dec; + + for (col = 0; col < image->num_cols; col++) { + y_col_off = col * w; + uv_col_off = y_col_off / fmt->uv_width_dec; + if (fmt->uv_packed) + uv_col_off *= 2; + + y_off = y_row_off + y_col_off; + uv_off = uv_row_off + uv_col_off; + + u_off = y_size - y_off + uv_off; + v_off = (fmt->uv_packed) ? 0 : u_off + uv_size; + if (fmt->uv_swapped) { + tmp = u_off; + u_off = v_off; + v_off = tmp; + } + + image->tile[tile].offset = y_off; + image->tile[tile].u_off = u_off; + image->tile[tile++].v_off = v_off; + + dev_dbg(priv->ipu->dev, + "task %u: ctx %p: %s@[%d,%d]: y_off %08x, u_off %08x, v_off %08x\n", + chan->ic_task, ctx, + image->type == IMAGE_CONVERT_IN ? + "Input" : "Output", row, col, + y_off, u_off, v_off); + } + } +} + +static void calc_tile_offsets_packed(struct ipu_image_convert_ctx *ctx, + struct ipu_image_convert_image *image) +{ + struct ipu_image_convert_chan *chan = ctx->chan; + struct ipu_image_convert_priv *priv = chan->priv; + const struct ipu_image_pixfmt *fmt = image->fmt; + unsigned int row, col, tile = 0; + u32 w, h, bpp, stride; + u32 row_off, col_off; + + /* setup some convenience vars */ + stride = image->stride; + bpp = fmt->bpp; + + for (row = 0; row < image->num_rows; row++) { + w = image->tile[tile].width; + h = image->tile[tile].height; + row_off = row * h * stride; + + for (col = 0; col < image->num_cols; col++) { + col_off = (col * w * bpp) >> 3; + + image->tile[tile].offset = row_off + col_off; + image->tile[tile].u_off = 0; + image->tile[tile++].v_off = 0; + + dev_dbg(priv->ipu->dev, + "task %u: ctx %p: %s@[%d,%d]: phys %08x\n", + chan->ic_task, ctx, + image->type == IMAGE_CONVERT_IN ? + "Input" : "Output", row, col, + row_off + col_off); + } + } +} + +static void calc_tile_offsets(struct ipu_image_convert_ctx *ctx, + struct ipu_image_convert_image *image) +{ + if (image->fmt->planar) + calc_tile_offsets_planar(ctx, image); + else + calc_tile_offsets_packed(ctx, image); +} + +/* + * return the number of runs in given queue (pending_q or done_q) + * for this context. hold irqlock when calling. + */ +static int get_run_count(struct ipu_image_convert_ctx *ctx, + struct list_head *q) +{ + struct ipu_image_convert_run *run; + int count = 0; + + lockdep_assert_held(&ctx->chan->irqlock); + + list_for_each_entry(run, q, list) { + if (run->ctx == ctx) + count++; + } + + return count; +} + +static void convert_stop(struct ipu_image_convert_run *run) +{ + struct ipu_image_convert_ctx *ctx = run->ctx; + struct ipu_image_convert_chan *chan = ctx->chan; + struct ipu_image_convert_priv *priv = chan->priv; + + dev_dbg(priv->ipu->dev, "%s: task %u: stopping ctx %p run %p\n", + __func__, chan->ic_task, ctx, run); + + /* disable IC tasks and the channels */ + ipu_ic_task_disable(chan->ic); + ipu_idmac_disable_channel(chan->in_chan); + ipu_idmac_disable_channel(chan->out_chan); + + if (ipu_rot_mode_is_irt(ctx->rot_mode)) { + ipu_idmac_disable_channel(chan->rotation_in_chan); + ipu_idmac_disable_channel(chan->rotation_out_chan); + ipu_idmac_unlink(chan->out_chan, chan->rotation_in_chan); + } + + ipu_ic_disable(chan->ic); +} + +static void init_idmac_channel(struct ipu_image_convert_ctx *ctx, + struct ipuv3_channel *channel, + struct ipu_image_convert_image *image, + enum ipu_rotate_mode rot_mode, + bool rot_swap_width_height) +{ + struct ipu_image_convert_chan *chan = ctx->chan; + unsigned int burst_size; + u32 width, height, stride; + dma_addr_t addr0, addr1 = 0; + struct ipu_image tile_image; + unsigned int tile_idx[2]; + + if (image->type == IMAGE_CONVERT_OUT) { + tile_idx[0] = ctx->out_tile_map[0]; + tile_idx[1] = ctx->out_tile_map[1]; + } else { + tile_idx[0] = 0; + tile_idx[1] = 1; + } + + if (rot_swap_width_height) { + width = image->tile[0].height; + height = image->tile[0].width; + stride = image->tile[0].rot_stride; + addr0 = ctx->rot_intermediate[0].phys; + if (ctx->double_buffering) + addr1 = ctx->rot_intermediate[1].phys; + } else { + width = image->tile[0].width; + height = image->tile[0].height; + stride = image->stride; + addr0 = image->base.phys0 + + image->tile[tile_idx[0]].offset; + if (ctx->double_buffering) + addr1 = image->base.phys0 + + image->tile[tile_idx[1]].offset; + } + + ipu_cpmem_zero(channel); + + memset(&tile_image, 0, sizeof(tile_image)); + tile_image.pix.width = tile_image.rect.width = width; + tile_image.pix.height = tile_image.rect.height = height; + tile_image.pix.bytesperline = stride; + tile_image.pix.pixelformat = image->fmt->fourcc; + tile_image.phys0 = addr0; + tile_image.phys1 = addr1; + ipu_cpmem_set_image(channel, &tile_image); + + if (image->fmt->planar && !rot_swap_width_height) + ipu_cpmem_set_uv_offset(channel, + image->tile[tile_idx[0]].u_off, + image->tile[tile_idx[0]].v_off); + + if (rot_mode) + ipu_cpmem_set_rotation(channel, rot_mode); + + if (channel == chan->rotation_in_chan || + channel == chan->rotation_out_chan) { + burst_size = 8; + ipu_cpmem_set_block_mode(channel); + } else + burst_size = (width % 16) ? 8 : 16; + + ipu_cpmem_set_burstsize(channel, burst_size); + + ipu_ic_task_idma_init(chan->ic, channel, width, height, + burst_size, rot_mode); + + ipu_cpmem_set_axi_id(channel, 1); + + ipu_idmac_set_double_buffer(channel, ctx->double_buffering); +} + +static int convert_start(struct ipu_image_convert_run *run) +{ + struct ipu_image_convert_ctx *ctx = run->ctx; + struct ipu_image_convert_chan *chan = ctx->chan; + struct ipu_image_convert_priv *priv = chan->priv; + struct ipu_image_convert_image *s_image = &ctx->in; + struct ipu_image_convert_image *d_image = &ctx->out; + enum ipu_color_space src_cs, dest_cs; + unsigned int dest_width, dest_height; + int ret; + + dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p\n", + __func__, chan->ic_task, ctx, run); + + src_cs = ipu_pixelformat_to_colorspace(s_image->fmt->fourcc); + dest_cs = ipu_pixelformat_to_colorspace(d_image->fmt->fourcc); + + if (ipu_rot_mode_is_irt(ctx->rot_mode)) { + /* swap width/height for resizer */ + dest_width = d_image->tile[0].height; + dest_height = d_image->tile[0].width; + } else { + dest_width = d_image->tile[0].width; + dest_height = d_image->tile[0].height; + } + + /* setup the IC resizer and CSC */ + ret = ipu_ic_task_init(chan->ic, + s_image->tile[0].width, + s_image->tile[0].height, + dest_width, + dest_height, + src_cs, dest_cs); + if (ret) { + dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret); + return ret; + } + + /* init the source MEM-->IC PP IDMAC channel */ + init_idmac_channel(ctx, chan->in_chan, s_image, + IPU_ROTATE_NONE, false); + + if (ipu_rot_mode_is_irt(ctx->rot_mode)) { + /* init the IC PP-->MEM IDMAC channel */ + init_idmac_channel(ctx, chan->out_chan, d_image, + IPU_ROTATE_NONE, true); + + /* init the MEM-->IC PP ROT IDMAC channel */ + init_idmac_channel(ctx, chan->rotation_in_chan, d_image, + ctx->rot_mode, true); + + /* init the destination IC PP ROT-->MEM IDMAC channel */ + init_idmac_channel(ctx, chan->rotation_out_chan, d_image, + IPU_ROTATE_NONE, false); + + /* now link IC PP-->MEM to MEM-->IC PP ROT */ + ipu_idmac_link(chan->out_chan, chan->rotation_in_chan); + } else { + /* init the destination IC PP-->MEM IDMAC channel */ + init_idmac_channel(ctx, chan->out_chan, d_image, + ctx->rot_mode, false); + } + + /* enable the IC */ + ipu_ic_enable(chan->ic); + + /* set buffers ready */ + ipu_idmac_select_buffer(chan->in_chan, 0); + ipu_idmac_select_buffer(chan->out_chan, 0); + if (ipu_rot_mode_is_irt(ctx->rot_mode)) + ipu_idmac_select_buffer(chan->rotation_out_chan, 0); + if (ctx->double_buffering) { + ipu_idmac_select_buffer(chan->in_chan, 1); + ipu_idmac_select_buffer(chan->out_chan, 1); + if (ipu_rot_mode_is_irt(ctx->rot_mode)) + ipu_idmac_select_buffer(chan->rotation_out_chan, 1); + } + + /* enable the channels! */ + ipu_idmac_enable_channel(chan->in_chan); + ipu_idmac_enable_channel(chan->out_chan); + if (ipu_rot_mode_is_irt(ctx->rot_mode)) { + ipu_idmac_enable_channel(chan->rotation_in_chan); + ipu_idmac_enable_channel(chan->rotation_out_chan); + } + + ipu_ic_task_enable(chan->ic); + + ipu_cpmem_dump(chan->in_chan); + ipu_cpmem_dump(chan->out_chan); + if (ipu_rot_mode_is_irt(ctx->rot_mode)) { + ipu_cpmem_dump(chan->rotation_in_chan); + ipu_cpmem_dump(chan->rotation_out_chan); + } + + ipu_dump(priv->ipu); + + return 0; +} + +/* hold irqlock when calling */ +static int do_run(struct ipu_image_convert_run *run) +{ + struct ipu_image_convert_ctx *ctx = run->ctx; + struct ipu_image_convert_chan *chan = ctx->chan; + + lockdep_assert_held(&chan->irqlock); + + ctx->in.base.phys0 = run->in_phys; + ctx->out.base.phys0 = run->out_phys; + + ctx->cur_buf_num = 0; + ctx->next_tile = 1; + + /* remove run from pending_q and set as current */ + list_del(&run->list); + chan->current_run = run; + + return convert_start(run); +} + +/* hold irqlock when calling */ +static void run_next(struct ipu_image_convert_chan *chan) +{ + struct ipu_image_convert_priv *priv = chan->priv; + struct ipu_image_convert_run *run, *tmp; + int ret; + + lockdep_assert_held(&chan->irqlock); + + list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { + /* skip contexts that are aborting */ + if (run->ctx->aborting) { + dev_dbg(priv->ipu->dev, + "%s: task %u: skipping aborting ctx %p run %p\n", + __func__, chan->ic_task, run->ctx, run); + continue; + } + + ret = do_run(run); + if (!ret) + break; + + /* + * something went wrong with start, add the run + * to done q and continue to the next run in the + * pending q. + */ + run->status = ret; + list_add_tail(&run->list, &chan->done_q); + chan->current_run = NULL; + } +} + +static void empty_done_q(struct ipu_image_convert_chan *chan) +{ + struct ipu_image_convert_priv *priv = chan->priv; + struct ipu_image_convert_run *run; + unsigned long flags; + + spin_lock_irqsave(&chan->irqlock, flags); + + while (!list_empty(&chan->done_q)) { + run = list_entry(chan->done_q.next, + struct ipu_image_convert_run, + list); + + list_del(&run->list); + + dev_dbg(priv->ipu->dev, + "%s: task %u: completing ctx %p run %p with %d\n", + __func__, chan->ic_task, run->ctx, run, run->status); + + /* call the completion callback and free the run */ + spin_unlock_irqrestore(&chan->irqlock, flags); + run->ctx->complete(run, run->ctx->complete_context); + spin_lock_irqsave(&chan->irqlock, flags); + } + + spin_unlock_irqrestore(&chan->irqlock, flags); +} + +/* + * the bottom half thread clears out the done_q, calling the + * completion handler for each. + */ +static irqreturn_t do_bh(int irq, void *dev_id) +{ + struct ipu_image_convert_chan *chan = dev_id; + struct ipu_image_convert_priv *priv = chan->priv; + struct ipu_image_convert_ctx *ctx; + unsigned long flags; + + dev_dbg(priv->ipu->dev, "%s: task %u: enter\n", __func__, + chan->ic_task); + + empty_done_q(chan); + + spin_lock_irqsave(&chan->irqlock, flags); + + /* + * the done_q is cleared out, signal any contexts + * that are aborting that abort can complete. + */ + list_for_each_entry(ctx, &chan->ctx_list, list) { + if (ctx->aborting) { + dev_dbg(priv->ipu->dev, + "%s: task %u: signaling abort for ctx %p\n", + __func__, chan->ic_task, ctx); + complete(&ctx->aborted); + } + } + + spin_unlock_irqrestore(&chan->irqlock, flags); + + dev_dbg(priv->ipu->dev, "%s: task %u: exit\n", __func__, + chan->ic_task); + + return IRQ_HANDLED; +} + +/* hold irqlock when calling */ +static irqreturn_t do_irq(struct ipu_image_convert_run *run) +{ + struct ipu_image_convert_ctx *ctx = run->ctx; + struct ipu_image_convert_chan *chan = ctx->chan; + struct ipu_image_tile *src_tile, *dst_tile; + struct ipu_image_convert_image *s_image = &ctx->in; + struct ipu_image_convert_image *d_image = &ctx->out; + struct ipuv3_channel *outch; + unsigned int dst_idx; + + lockdep_assert_held(&chan->irqlock); + + outch = ipu_rot_mode_is_irt(ctx->rot_mode) ? + chan->rotation_out_chan : chan->out_chan; + + /* + * It is difficult to stop the channel DMA before the channels + * enter the paused state. Without double-buffering the channels + * are always in a paused state when the EOF irq occurs, so it + * is safe to stop the channels now. For double-buffering we + * just ignore the abort until the operation completes, when it + * is safe to shut down. + */ + if (ctx->aborting && !ctx->double_buffering) { + convert_stop(run); + run->status = -EIO; + goto done; + } + + if (ctx->next_tile == ctx->num_tiles) { + /* + * the conversion is complete + */ + convert_stop(run); + run->status = 0; + goto done; + } + + /* + * not done, place the next tile buffers. + */ + if (!ctx->double_buffering) { + + src_tile = &s_image->tile[ctx->next_tile]; + dst_idx = ctx->out_tile_map[ctx->next_tile]; + dst_tile = &d_image->tile[dst_idx]; + + ipu_cpmem_set_buffer(chan->in_chan, 0, + s_image->base.phys0 + src_tile->offset); + ipu_cpmem_set_buffer(outch, 0, + d_image->base.phys0 + dst_tile->offset); + if (s_image->fmt->planar) + ipu_cpmem_set_uv_offset(chan->in_chan, + src_tile->u_off, + src_tile->v_off); + if (d_image->fmt->planar) + ipu_cpmem_set_uv_offset(outch, + dst_tile->u_off, + dst_tile->v_off); + + ipu_idmac_select_buffer(chan->in_chan, 0); + ipu_idmac_select_buffer(outch, 0); + + } else if (ctx->next_tile < ctx->num_tiles - 1) { + + src_tile = &s_image->tile[ctx->next_tile + 1]; + dst_idx = ctx->out_tile_map[ctx->next_tile + 1]; + dst_tile = &d_image->tile[dst_idx]; + + ipu_cpmem_set_buffer(chan->in_chan, ctx->cur_buf_num, + s_image->base.phys0 + src_tile->offset); + ipu_cpmem_set_buffer(outch, ctx->cur_buf_num, + d_image->base.phys0 + dst_tile->offset); + + ipu_idmac_select_buffer(chan->in_chan, ctx->cur_buf_num); + ipu_idmac_select_buffer(outch, ctx->cur_buf_num); + + ctx->cur_buf_num ^= 1; + } + + ctx->next_tile++; + return IRQ_HANDLED; +done: + list_add_tail(&run->list, &chan->done_q); + chan->current_run = NULL; + run_next(chan); + return IRQ_WAKE_THREAD; +} + +static irqreturn_t norotate_irq(int irq, void *data) +{ + struct ipu_image_convert_chan *chan = data; + struct ipu_image_convert_ctx *ctx; + struct ipu_image_convert_run *run; + unsigned long flags; + irqreturn_t ret; + + spin_lock_irqsave(&chan->irqlock, flags); + + /* get current run and its context */ + run = chan->current_run; + if (!run) { + ret = IRQ_NONE; + goto out; + } + + ctx = run->ctx; + + if (ipu_rot_mode_is_irt(ctx->rot_mode)) { + /* this is a rotation operation, just ignore */ + spin_unlock_irqrestore(&chan->irqlock, flags); + return IRQ_HANDLED; + } + + ret = do_irq(run); +out: + spin_unlock_irqrestore(&chan->irqlock, flags); + return ret; +} + +static irqreturn_t rotate_irq(int irq, void *data) +{ + struct ipu_image_convert_chan *chan = data; + struct ipu_image_convert_priv *priv = chan->priv; + struct ipu_image_convert_ctx *ctx; + struct ipu_image_convert_run *run; + unsigned long flags; + irqreturn_t ret; + + spin_lock_irqsave(&chan->irqlock, flags); + + /* get current run and its context */ + run = chan->current_run; + if (!run) { + ret = IRQ_NONE; + goto out; + } + + ctx = run->ctx; + + if (!ipu_rot_mode_is_irt(ctx->rot_mode)) { + /* this was NOT a rotation operation, shouldn't happen */ + dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n"); + spin_unlock_irqrestore(&chan->irqlock, flags); + return IRQ_HANDLED; + } + + ret = do_irq(run); +out: + spin_unlock_irqrestore(&chan->irqlock, flags); + return ret; +} + +/* + * try to force the completion of runs for this ctx. Called when + * abort wait times out in ipu_image_convert_abort(). + */ +static void force_abort(struct ipu_image_convert_ctx *ctx) +{ + struct ipu_image_convert_chan *chan = ctx->chan; + struct ipu_image_convert_run *run; + unsigned long flags; + + spin_lock_irqsave(&chan->irqlock, flags); + + run = chan->current_run; + if (run && run->ctx == ctx) { + convert_stop(run); + run->status = -EIO; + list_add_tail(&run->list, &chan->done_q); + chan->current_run = NULL; + run_next(chan); + } + + spin_unlock_irqrestore(&chan->irqlock, flags); + + empty_done_q(chan); +} + +static void release_ipu_resources(struct ipu_image_convert_chan *chan) +{ + if (chan->out_eof_irq >= 0) + free_irq(chan->out_eof_irq, chan); + if (chan->rot_out_eof_irq >= 0) + free_irq(chan->rot_out_eof_irq, chan); + + if (!IS_ERR_OR_NULL(chan->in_chan)) + ipu_idmac_put(chan->in_chan); + if (!IS_ERR_OR_NULL(chan->out_chan)) + ipu_idmac_put(chan->out_chan); + if (!IS_ERR_OR_NULL(chan->rotation_in_chan)) + ipu_idmac_put(chan->rotation_in_chan); + if (!IS_ERR_OR_NULL(chan->rotation_out_chan)) + ipu_idmac_put(chan->rotation_out_chan); + if (!IS_ERR_OR_NULL(chan->ic)) + ipu_ic_put(chan->ic); + + chan->in_chan = chan->out_chan = chan->rotation_in_chan = + chan->rotation_out_chan = NULL; + chan->out_eof_irq = chan->rot_out_eof_irq = -1; +} + +static int get_ipu_resources(struct ipu_image_convert_chan *chan) +{ + const struct ipu_image_convert_dma_chan *dma = chan->dma_ch; + struct ipu_image_convert_priv *priv = chan->priv; + int ret; + + /* get IC */ + chan->ic = ipu_ic_get(priv->ipu, chan->ic_task); + if (IS_ERR(chan->ic)) { + dev_err(priv->ipu->dev, "could not acquire IC\n"); + ret = PTR_ERR(chan->ic); + goto err; + } + + /* get IDMAC channels */ + chan->in_chan = ipu_idmac_get(priv->ipu, dma->in); + chan->out_chan = ipu_idmac_get(priv->ipu, dma->out); + if (IS_ERR(chan->in_chan) || IS_ERR(chan->out_chan)) { + dev_err(priv->ipu->dev, "could not acquire idmac channels\n"); + ret = -EBUSY; + goto err; + } + + chan->rotation_in_chan = ipu_idmac_get(priv->ipu, dma->rot_in); + chan->rotation_out_chan = ipu_idmac_get(priv->ipu, dma->rot_out); + if (IS_ERR(chan->rotation_in_chan) || IS_ERR(chan->rotation_out_chan)) { + dev_err(priv->ipu->dev, + "could not acquire idmac rotation channels\n"); + ret = -EBUSY; + goto err; + } + + /* acquire the EOF interrupts */ + chan->out_eof_irq = ipu_idmac_channel_irq(priv->ipu, + chan->out_chan, + IPU_IRQ_EOF); + + ret = request_threaded_irq(chan->out_eof_irq, norotate_irq, do_bh, + 0, "ipu-ic", chan); + if (ret < 0) { + dev_err(priv->ipu->dev, "could not acquire irq %d\n", + chan->out_eof_irq); + chan->out_eof_irq = -1; + goto err; + } + + chan->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu, + chan->rotation_out_chan, + IPU_IRQ_EOF); + + ret = request_threaded_irq(chan->rot_out_eof_irq, rotate_irq, do_bh, + 0, "ipu-ic", chan); + if (ret < 0) { + dev_err(priv->ipu->dev, "could not acquire irq %d\n", + chan->rot_out_eof_irq); + chan->rot_out_eof_irq = -1; + goto err; + } + + return 0; +err: + release_ipu_resources(chan); + return ret; +} + +static int fill_image(struct ipu_image_convert_ctx *ctx, + struct ipu_image_convert_image *ic_image, + struct ipu_image *image, + enum ipu_image_convert_type type) +{ + struct ipu_image_convert_priv *priv = ctx->chan->priv; + + ic_image->base = *image; + ic_image->type = type; + + ic_image->fmt = get_format(image->pix.pixelformat); + if (!ic_image->fmt) { + dev_err(priv->ipu->dev, "pixelformat not supported for %s\n", + type == IMAGE_CONVERT_OUT ? "Output" : "Input"); + return -EINVAL; + } + + if (ic_image->fmt->planar) + ic_image->stride = ic_image->base.pix.width; + else + ic_image->stride = ic_image->base.pix.bytesperline; + + calc_tile_dimensions(ctx, ic_image); + calc_tile_offsets(ctx, ic_image); + + return 0; +} + +/* borrowed from drivers/media/v4l2-core/v4l2-common.c */ +static unsigned int clamp_align(unsigned int x, unsigned int min, + unsigned int max, unsigned int align) +{ + /* Bits that must be zero to be aligned */ + unsigned int mask = ~((1 << align) - 1); + + /* Clamp to aligned min and max */ + x = clamp(x, (min + ~mask) & mask, max & mask); + + /* Round to nearest aligned value */ + if (align) + x = (x + (1 << (align - 1))) & mask; + + return x; +} + +/* + * We have to adjust the tile width such that the tile physaddrs and + * U and V plane offsets are multiples of 8 bytes as required by + * the IPU DMA Controller. For the planar formats, this corresponds + * to a pixel alignment of 16 (but use a more formal equation since + * the variables are available). For all the packed formats, 8 is + * good enough. + */ +static inline u32 tile_width_align(const struct ipu_image_pixfmt *fmt) +{ + return fmt->planar ? 8 * fmt->uv_width_dec : 8; +} + +/* + * For tile height alignment, we have to ensure that the output tile + * heights are multiples of 8 lines if the IRT is required by the + * given rotation mode (the IRT performs rotations on 8x8 blocks + * at a time). If the IRT is not used, or for input image tiles, + * 2 lines are good enough. + */ +static inline u32 tile_height_align(enum ipu_image_convert_type type, + enum ipu_rotate_mode rot_mode) +{ + return (type == IMAGE_CONVERT_OUT && + ipu_rot_mode_is_irt(rot_mode)) ? 8 : 2; +} + +/* Adjusts input/output images to IPU restrictions */ +void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out, + enum ipu_rotate_mode rot_mode) +{ + const struct ipu_image_pixfmt *infmt, *outfmt; + unsigned int num_in_rows, num_in_cols; + unsigned int num_out_rows, num_out_cols; + u32 w_align, h_align; + + infmt = get_format(in->pix.pixelformat); + outfmt = get_format(out->pix.pixelformat); + + /* set some default pixel formats if needed */ + if (!infmt) { + in->pix.pixelformat = V4L2_PIX_FMT_RGB24; + infmt = get_format(V4L2_PIX_FMT_RGB24); + } + if (!outfmt) { + out->pix.pixelformat = V4L2_PIX_FMT_RGB24; + outfmt = get_format(V4L2_PIX_FMT_RGB24); + } + + /* image converter does not handle fields */ + in->pix.field = out->pix.field = V4L2_FIELD_NONE; + + /* resizer cannot downsize more than 4:1 */ + if (ipu_rot_mode_is_irt(rot_mode)) { + out->pix.height = max_t(__u32, out->pix.height, + in->pix.width / 4); + out->pix.width = max_t(__u32, out->pix.width, + in->pix.height / 4); + } else { + out->pix.width = max_t(__u32, out->pix.width, + in->pix.width / 4); + out->pix.height = max_t(__u32, out->pix.height, + in->pix.height / 4); + } + + /* get tiling rows/cols from output format */ + num_out_rows = num_stripes(out->pix.height); + num_out_cols = num_stripes(out->pix.width); + if (ipu_rot_mode_is_irt(rot_mode)) { + num_in_rows = num_out_cols; + num_in_cols = num_out_rows; + } else { + num_in_rows = num_out_rows; + num_in_cols = num_out_cols; + } + + /* align input width/height */ + w_align = ilog2(tile_width_align(infmt) * num_in_cols); + h_align = ilog2(tile_height_align(IMAGE_CONVERT_IN, rot_mode) * + num_in_rows); + in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, w_align); + in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, h_align); + + /* align output width/height */ + w_align = ilog2(tile_width_align(outfmt) * num_out_cols); + h_align = ilog2(tile_height_align(IMAGE_CONVERT_OUT, rot_mode) * + num_out_rows); + out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, w_align); + out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, h_align); + + /* set input/output strides and image sizes */ + in->pix.bytesperline = (in->pix.width * infmt->bpp) >> 3; + in->pix.sizeimage = in->pix.height * in->pix.bytesperline; + out->pix.bytesperline = (out->pix.width * outfmt->bpp) >> 3; + out->pix.sizeimage = out->pix.height * out->pix.bytesperline; +} +EXPORT_SYMBOL_GPL(ipu_image_convert_adjust); + +/* + * this is used by ipu_image_convert_prepare() to verify set input and + * output images are valid before starting the conversion. Clients can + * also call it before calling ipu_image_convert_prepare(). + */ +int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out, + enum ipu_rotate_mode rot_mode) +{ + struct ipu_image testin, testout; + + testin = *in; + testout = *out; + + ipu_image_convert_adjust(&testin, &testout, rot_mode); + + if (testin.pix.width != in->pix.width || + testin.pix.height != in->pix.height || + testout.pix.width != out->pix.width || + testout.pix.height != out->pix.height) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_image_convert_verify); + +/* + * Call ipu_image_convert_prepare() to prepare for the conversion of + * given images and rotation mode. Returns a new conversion context. + */ +struct ipu_image_convert_ctx * +ipu_image_convert_prepare(struct ipu_soc *ipu, enum ipu_ic_task ic_task, + struct ipu_image *in, struct ipu_image *out, + enum ipu_rotate_mode rot_mode, + ipu_image_convert_cb_t complete, + void *complete_context) +{ + struct ipu_image_convert_priv *priv = ipu->image_convert_priv; + struct ipu_image_convert_image *s_image, *d_image; + struct ipu_image_convert_chan *chan; + struct ipu_image_convert_ctx *ctx; + unsigned long flags; + bool get_res; + int ret; + + if (!in || !out || !complete || + (ic_task != IC_TASK_VIEWFINDER && + ic_task != IC_TASK_POST_PROCESSOR)) + return ERR_PTR(-EINVAL); + + /* verify the in/out images before continuing */ + ret = ipu_image_convert_verify(in, out, rot_mode); + if (ret) { + dev_err(priv->ipu->dev, "%s: in/out formats invalid\n", + __func__); + return ERR_PTR(ret); + } + + chan = &priv->chan[ic_task]; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); + + dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p\n", __func__, + chan->ic_task, ctx); + + ctx->chan = chan; + init_completion(&ctx->aborted); + + s_image = &ctx->in; + d_image = &ctx->out; + + /* set tiling and rotation */ + d_image->num_rows = num_stripes(out->pix.height); + d_image->num_cols = num_stripes(out->pix.width); + if (ipu_rot_mode_is_irt(rot_mode)) { + s_image->num_rows = d_image->num_cols; + s_image->num_cols = d_image->num_rows; + } else { + s_image->num_rows = d_image->num_rows; + s_image->num_cols = d_image->num_cols; + } + + ctx->num_tiles = d_image->num_cols * d_image->num_rows; + ctx->rot_mode = rot_mode; + + ret = fill_image(ctx, s_image, in, IMAGE_CONVERT_IN); + if (ret) + goto out_free; + ret = fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT); + if (ret) + goto out_free; + + calc_out_tile_map(ctx); + + dump_format(ctx, s_image); + dump_format(ctx, d_image); + + ctx->complete = complete; + ctx->complete_context = complete_context; + + /* + * Can we use double-buffering for this operation? If there is + * only one tile (the whole image can be converted in a single + * operation) there's no point in using double-buffering. Also, + * the IPU's IDMAC channels allow only a single U and V plane + * offset shared between both buffers, but these offsets change + * for every tile, and therefore would have to be updated for + * each buffer which is not possible. So double-buffering is + * impossible when either the source or destination images are + * a planar format (YUV420, YUV422P, etc.). + */ + ctx->double_buffering = (ctx->num_tiles > 1 && + !s_image->fmt->planar && + !d_image->fmt->planar); + + if (ipu_rot_mode_is_irt(ctx->rot_mode)) { + ret = alloc_dma_buf(priv, &ctx->rot_intermediate[0], + d_image->tile[0].size); + if (ret) + goto out_free; + if (ctx->double_buffering) { + ret = alloc_dma_buf(priv, + &ctx->rot_intermediate[1], + d_image->tile[0].size); + if (ret) + goto out_free_dmabuf0; + } + } + + spin_lock_irqsave(&chan->irqlock, flags); + + get_res = list_empty(&chan->ctx_list); + + list_add_tail(&ctx->list, &chan->ctx_list); + + spin_unlock_irqrestore(&chan->irqlock, flags); + + if (get_res) { + ret = get_ipu_resources(chan); + if (ret) + goto out_free_dmabuf1; + } + + return ctx; + +out_free_dmabuf1: + free_dma_buf(priv, &ctx->rot_intermediate[1]); + spin_lock_irqsave(&chan->irqlock, flags); + list_del(&ctx->list); + spin_unlock_irqrestore(&chan->irqlock, flags); +out_free_dmabuf0: + free_dma_buf(priv, &ctx->rot_intermediate[0]); +out_free: + kfree(ctx); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(ipu_image_convert_prepare); + +/* + * Carry out a single image conversion run. Only the physaddr's of the input + * and output image buffers are needed. The conversion context must have + * been created previously with ipu_image_convert_prepare(). + */ +int ipu_image_convert_queue(struct ipu_image_convert_run *run) +{ + struct ipu_image_convert_chan *chan; + struct ipu_image_convert_priv *priv; + struct ipu_image_convert_ctx *ctx; + unsigned long flags; + int ret = 0; + + if (!run || !run->ctx || !run->in_phys || !run->out_phys) + return -EINVAL; + + ctx = run->ctx; + chan = ctx->chan; + priv = chan->priv; + + dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p run %p\n", __func__, + chan->ic_task, ctx, run); + + INIT_LIST_HEAD(&run->list); + + spin_lock_irqsave(&chan->irqlock, flags); + + if (ctx->aborting) { + ret = -EIO; + goto unlock; + } + + list_add_tail(&run->list, &chan->pending_q); + + if (!chan->current_run) { + ret = do_run(run); + if (ret) + chan->current_run = NULL; + } +unlock: + spin_unlock_irqrestore(&chan->irqlock, flags); + return ret; +} +EXPORT_SYMBOL_GPL(ipu_image_convert_queue); + +/* Abort any active or pending conversions for this context */ +void ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) +{ + struct ipu_image_convert_chan *chan = ctx->chan; + struct ipu_image_convert_priv *priv = chan->priv; + struct ipu_image_convert_run *run, *active_run, *tmp; + unsigned long flags; + int run_count, ret; + bool need_abort; + + reinit_completion(&ctx->aborted); + + spin_lock_irqsave(&chan->irqlock, flags); + + /* move all remaining pending runs in this context to done_q */ + list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { + if (run->ctx != ctx) + continue; + run->status = -EIO; + list_move_tail(&run->list, &chan->done_q); + } + + run_count = get_run_count(ctx, &chan->done_q); + active_run = (chan->current_run && chan->current_run->ctx == ctx) ? + chan->current_run : NULL; + + need_abort = (run_count || active_run); + + ctx->aborting = need_abort; + + spin_unlock_irqrestore(&chan->irqlock, flags); + + if (!need_abort) { + dev_dbg(priv->ipu->dev, + "%s: task %u: no abort needed for ctx %p\n", + __func__, chan->ic_task, ctx); + return; + } + + dev_dbg(priv->ipu->dev, + "%s: task %u: wait for completion: %d runs, active run %p\n", + __func__, chan->ic_task, run_count, active_run); + + ret = wait_for_completion_timeout(&ctx->aborted, + msecs_to_jiffies(10000)); + if (ret == 0) { + dev_warn(priv->ipu->dev, "%s: timeout\n", __func__); + force_abort(ctx); + } + + ctx->aborting = false; +} +EXPORT_SYMBOL_GPL(ipu_image_convert_abort); + +/* Unprepare image conversion context */ +void ipu_image_convert_unprepare(struct ipu_image_convert_ctx *ctx) +{ + struct ipu_image_convert_chan *chan = ctx->chan; + struct ipu_image_convert_priv *priv = chan->priv; + unsigned long flags; + bool put_res; + + /* make sure no runs are hanging around */ + ipu_image_convert_abort(ctx); + + dev_dbg(priv->ipu->dev, "%s: task %u: removing ctx %p\n", __func__, + chan->ic_task, ctx); + + spin_lock_irqsave(&chan->irqlock, flags); + + list_del(&ctx->list); + + put_res = list_empty(&chan->ctx_list); + + spin_unlock_irqrestore(&chan->irqlock, flags); + + if (put_res) + release_ipu_resources(chan); + + free_dma_buf(priv, &ctx->rot_intermediate[1]); + free_dma_buf(priv, &ctx->rot_intermediate[0]); + + kfree(ctx); +} +EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare); + +/* + * "Canned" asynchronous single image conversion. Allocates and returns + * a new conversion run. On successful return the caller must free the + * run and call ipu_image_convert_unprepare() after conversion completes. + */ +struct ipu_image_convert_run * +ipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task, + struct ipu_image *in, struct ipu_image *out, + enum ipu_rotate_mode rot_mode, + ipu_image_convert_cb_t complete, + void *complete_context) +{ + struct ipu_image_convert_ctx *ctx; + struct ipu_image_convert_run *run; + int ret; + + ctx = ipu_image_convert_prepare(ipu, ic_task, in, out, rot_mode, + complete, complete_context); + if (IS_ERR(ctx)) + return ERR_PTR(PTR_ERR(ctx)); + + run = kzalloc(sizeof(*run), GFP_KERNEL); + if (!run) { + ipu_image_convert_unprepare(ctx); + return ERR_PTR(-ENOMEM); + } + + run->ctx = ctx; + run->in_phys = in->phys0; + run->out_phys = out->phys0; + + ret = ipu_image_convert_queue(run); + if (ret) { + ipu_image_convert_unprepare(ctx); + kfree(run); + return ERR_PTR(ret); + } + + return run; +} +EXPORT_SYMBOL_GPL(ipu_image_convert); + +/* "Canned" synchronous single image conversion */ +static void image_convert_sync_complete(struct ipu_image_convert_run *run, + void *data) +{ + struct completion *comp = data; + + complete(comp); +} + +int ipu_image_convert_sync(struct ipu_soc *ipu, enum ipu_ic_task ic_task, + struct ipu_image *in, struct ipu_image *out, + enum ipu_rotate_mode rot_mode) +{ + struct ipu_image_convert_run *run; + struct completion comp; + int ret; + + init_completion(&comp); + + run = ipu_image_convert(ipu, ic_task, in, out, rot_mode, + image_convert_sync_complete, &comp); + if (IS_ERR(run)) + return PTR_ERR(run); + + ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000)); + ret = (ret == 0) ? -ETIMEDOUT : 0; + + ipu_image_convert_unprepare(run->ctx); + kfree(run); + + return ret; +} +EXPORT_SYMBOL_GPL(ipu_image_convert_sync); + +int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev) +{ + struct ipu_image_convert_priv *priv; + int i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ipu->image_convert_priv = priv; + priv->ipu = ipu; + + for (i = 0; i < IC_NUM_TASKS; i++) { + struct ipu_image_convert_chan *chan = &priv->chan[i]; + + chan->ic_task = i; + chan->priv = priv; + chan->dma_ch = &image_convert_dma_chan[i]; + chan->out_eof_irq = -1; + chan->rot_out_eof_irq = -1; + + spin_lock_init(&chan->irqlock); + INIT_LIST_HEAD(&chan->ctx_list); + INIT_LIST_HEAD(&chan->pending_q); + INIT_LIST_HEAD(&chan->done_q); + } + + return 0; +} + +void ipu_image_convert_exit(struct ipu_soc *ipu) +{ +} diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h index bfb1e8a..22e47b6 100644 --- a/drivers/gpu/ipu-v3/ipu-prv.h +++ b/drivers/gpu/ipu-v3/ipu-prv.h @@ -75,6 +75,33 @@ struct ipu_soc; #define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * (n)) #define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n)) +/* FS_PROC_FLOW1 */ +#define FS_PRPENC_ROT_SRC_SEL_MASK (0xf << 0) +#define FS_PRPENC_ROT_SRC_SEL_ENC (0x7 << 0) +#define FS_PRPVF_ROT_SRC_SEL_MASK (0xf << 8) +#define FS_PRPVF_ROT_SRC_SEL_VF (0x8 << 8) +#define FS_PP_SRC_SEL_MASK (0xf << 12) +#define FS_PP_ROT_SRC_SEL_MASK (0xf << 16) +#define FS_PP_ROT_SRC_SEL_PP (0x5 << 16) +#define FS_VDI1_SRC_SEL_MASK (0x3 << 20) +#define FS_VDI3_SRC_SEL_MASK (0x3 << 20) +#define FS_PRP_SRC_SEL_MASK (0xf << 24) +#define FS_VDI_SRC_SEL_MASK (0x3 << 28) +#define FS_VDI_SRC_SEL_CSI_DIRECT (0x1 << 28) +#define FS_VDI_SRC_SEL_VDOA (0x2 << 28) + +/* FS_PROC_FLOW2 */ +#define FS_PRP_ENC_DEST_SEL_MASK (0xf << 0) +#define FS_PRP_ENC_DEST_SEL_IRT_ENC (0x1 << 0) +#define FS_PRPVF_DEST_SEL_MASK (0xf << 4) +#define FS_PRPVF_DEST_SEL_IRT_VF (0x1 << 4) +#define FS_PRPVF_ROT_DEST_SEL_MASK (0xf << 8) +#define FS_PP_DEST_SEL_MASK (0xf << 12) +#define FS_PP_DEST_SEL_IRT_PP (0x3 << 12) +#define FS_PP_ROT_DEST_SEL_MASK (0xf << 16) +#define FS_PRPENC_ROT_DEST_SEL_MASK (0xf << 20) +#define FS_PRP_DEST_SEL_MASK (0xf << 24) + #define IPU_DI0_COUNTER_RELEASE (1 << 24) #define IPU_DI1_COUNTER_RELEASE (1 << 25) @@ -138,6 +165,8 @@ struct ipu_dc_priv; struct ipu_dmfc_priv; struct ipu_di; struct ipu_ic_priv; +struct ipu_vdi; +struct ipu_image_convert_priv; struct ipu_smfc_priv; struct ipu_devtype; @@ -152,6 +181,7 @@ struct ipu_soc { void __iomem *cm_reg; void __iomem *idmac_reg; + int id; int usecount; struct clk *clk; @@ -169,6 +199,8 @@ struct ipu_soc { struct ipu_di *di_priv[2]; struct ipu_csi *csi_priv[2]; struct ipu_ic_priv *ic_priv; + struct ipu_vdi *vdi_priv; + struct ipu_image_convert_priv *image_convert_priv; struct ipu_smfc_priv *smfc_priv; }; @@ -199,6 +231,13 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, unsigned long tpmem_base); void ipu_ic_exit(struct ipu_soc *ipu); +int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev, + unsigned long base, u32 module); +void ipu_vdi_exit(struct ipu_soc *ipu); + +int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev); +void ipu_image_convert_exit(struct ipu_soc *ipu); + int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, unsigned long base, u32 module, struct clk *ipu_clk); void ipu_di_exit(struct ipu_soc *ipu, int id); diff --git a/drivers/gpu/ipu-v3/ipu-vdi.c b/drivers/gpu/ipu-v3/ipu-vdi.c new file mode 100644 index 0000000..f27bf5a --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-vdi.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2012-2016 Mentor Graphics Inc. + * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <linux/io.h> +#include "ipu-prv.h" + +struct ipu_vdi { + void __iomem *base; + u32 module; + spinlock_t lock; + int use_count; + struct ipu_soc *ipu; +}; + + +/* VDI Register Offsets */ +#define VDI_FSIZE 0x0000 +#define VDI_C 0x0004 + +/* VDI Register Fields */ +#define VDI_C_CH_420 (0 << 1) +#define VDI_C_CH_422 (1 << 1) +#define VDI_C_MOT_SEL_MASK (0x3 << 2) +#define VDI_C_MOT_SEL_FULL (2 << 2) +#define VDI_C_MOT_SEL_LOW (1 << 2) +#define VDI_C_MOT_SEL_MED (0 << 2) +#define VDI_C_BURST_SIZE1_4 (3 << 4) +#define VDI_C_BURST_SIZE2_4 (3 << 8) +#define VDI_C_BURST_SIZE3_4 (3 << 12) +#define VDI_C_BURST_SIZE_MASK 0xF +#define VDI_C_BURST_SIZE1_OFFSET 4 +#define VDI_C_BURST_SIZE2_OFFSET 8 +#define VDI_C_BURST_SIZE3_OFFSET 12 +#define VDI_C_VWM1_SET_1 (0 << 16) +#define VDI_C_VWM1_SET_2 (1 << 16) +#define VDI_C_VWM1_CLR_2 (1 << 19) +#define VDI_C_VWM3_SET_1 (0 << 22) +#define VDI_C_VWM3_SET_2 (1 << 22) +#define VDI_C_VWM3_CLR_2 (1 << 25) +#define VDI_C_TOP_FIELD_MAN_1 (1 << 30) +#define VDI_C_TOP_FIELD_AUTO_1 (1 << 31) + +static inline u32 ipu_vdi_read(struct ipu_vdi *vdi, unsigned int offset) +{ + return readl(vdi->base + offset); +} + +static inline void ipu_vdi_write(struct ipu_vdi *vdi, u32 value, + unsigned int offset) +{ + writel(value, vdi->base + offset); +} + +void ipu_vdi_set_field_order(struct ipu_vdi *vdi, v4l2_std_id std, u32 field) +{ + bool top_field_0 = false; + unsigned long flags; + u32 reg; + + switch (field) { + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_TOP: + top_field_0 = true; + break; + case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_SEQ_BT: + case V4L2_FIELD_BOTTOM: + top_field_0 = false; + break; + default: + top_field_0 = (std & V4L2_STD_525_60) ? true : false; + break; + } + + spin_lock_irqsave(&vdi->lock, flags); + + reg = ipu_vdi_read(vdi, VDI_C); + if (top_field_0) + reg &= ~VDI_C_TOP_FIELD_MAN_1; + else + reg |= VDI_C_TOP_FIELD_MAN_1; + ipu_vdi_write(vdi, reg, VDI_C); + + spin_unlock_irqrestore(&vdi->lock, flags); +} +EXPORT_SYMBOL_GPL(ipu_vdi_set_field_order); + +void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel) +{ + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&vdi->lock, flags); + + reg = ipu_vdi_read(vdi, VDI_C); + + reg &= ~VDI_C_MOT_SEL_MASK; + + switch (motion_sel) { + case MED_MOTION: + reg |= VDI_C_MOT_SEL_MED; + break; + case HIGH_MOTION: + reg |= VDI_C_MOT_SEL_FULL; + break; + default: + reg |= VDI_C_MOT_SEL_LOW; + break; + } + + ipu_vdi_write(vdi, reg, VDI_C); + + spin_unlock_irqrestore(&vdi->lock, flags); +} +EXPORT_SYMBOL_GPL(ipu_vdi_set_motion); + +void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres) +{ + unsigned long flags; + u32 pixel_fmt, reg; + + spin_lock_irqsave(&vdi->lock, flags); + + reg = ((yres - 1) << 16) | (xres - 1); + ipu_vdi_write(vdi, reg, VDI_FSIZE); + + /* + * Full motion, only vertical filter is used. + * Burst size is 4 accesses + */ + if (code == MEDIA_BUS_FMT_UYVY8_2X8 || + code == MEDIA_BUS_FMT_UYVY8_1X16 || + code == MEDIA_BUS_FMT_YUYV8_2X8 || + code == MEDIA_BUS_FMT_YUYV8_1X16) + pixel_fmt = VDI_C_CH_422; + else + pixel_fmt = VDI_C_CH_420; + + reg = ipu_vdi_read(vdi, VDI_C); + reg |= pixel_fmt; + reg |= VDI_C_BURST_SIZE2_4; + reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_CLR_2; + reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_CLR_2; + ipu_vdi_write(vdi, reg, VDI_C); + + spin_unlock_irqrestore(&vdi->lock, flags); +} +EXPORT_SYMBOL_GPL(ipu_vdi_setup); + +void ipu_vdi_unsetup(struct ipu_vdi *vdi) +{ + unsigned long flags; + + spin_lock_irqsave(&vdi->lock, flags); + ipu_vdi_write(vdi, 0, VDI_FSIZE); + ipu_vdi_write(vdi, 0, VDI_C); + spin_unlock_irqrestore(&vdi->lock, flags); +} +EXPORT_SYMBOL_GPL(ipu_vdi_unsetup); + +int ipu_vdi_enable(struct ipu_vdi *vdi) +{ + unsigned long flags; + + spin_lock_irqsave(&vdi->lock, flags); + + if (!vdi->use_count) + ipu_module_enable(vdi->ipu, vdi->module); + + vdi->use_count++; + + spin_unlock_irqrestore(&vdi->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_vdi_enable); + +int ipu_vdi_disable(struct ipu_vdi *vdi) +{ + unsigned long flags; + + spin_lock_irqsave(&vdi->lock, flags); + + if (vdi->use_count) { + if (!--vdi->use_count) + ipu_module_disable(vdi->ipu, vdi->module); + } + + spin_unlock_irqrestore(&vdi->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(ipu_vdi_disable); + +struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu) +{ + return ipu->vdi_priv; +} +EXPORT_SYMBOL_GPL(ipu_vdi_get); + +void ipu_vdi_put(struct ipu_vdi *vdi) +{ +} +EXPORT_SYMBOL_GPL(ipu_vdi_put); + +int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev, + unsigned long base, u32 module) +{ + struct ipu_vdi *vdi; + + vdi = devm_kzalloc(dev, sizeof(*vdi), GFP_KERNEL); + if (!vdi) + return -ENOMEM; + + ipu->vdi_priv = vdi; + + spin_lock_init(&vdi->lock); + vdi->module = module; + vdi->base = devm_ioremap(dev, base, PAGE_SIZE); + if (!vdi->base) + return -ENOMEM; + + dev_dbg(dev, "VDI base: 0x%08lx remapped to %p\n", base, vdi->base); + vdi->ipu = ipu; + + return 0; +} + +void ipu_vdi_exit(struct ipu_soc *ipu) +{ +} diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c index f17cb04..1887f19 100644 --- a/drivers/gpu/vga/vgaarb.c +++ b/drivers/gpu/vga/vgaarb.c @@ -131,7 +131,24 @@ static struct vga_device *vgadev_find(struct pci_dev *pdev) return NULL; } -/* Returns the default VGA device (vgacon's babe) */ +/** + * vga_default_device - return the default VGA device, for vgacon + * + * This can be defined by the platform. The default implementation + * is rather dumb and will probably only work properly on single + * vga card setups and/or x86 platforms. + * + * If your VGA default device is not PCI, you'll have to return + * NULL here. In this case, I assume it will not conflict with + * any PCI card. If this is not true, I'll have to define two archs + * hooks for enabling/disabling the VGA default device if that is + * possible. This may be a problem with real _ISA_ VGA cards, in + * addition to a PCI one. I don't know at this point how to deal + * with that card. Can theirs IOs be disabled at all ? If not, then + * I suppose it's a matter of having the proper arch hook telling + * us about it, so we basically never allow anybody to succeed a + * vga_get()... + */ struct pci_dev *vga_default_device(void) { return vga_default; @@ -356,6 +373,40 @@ static void __vga_put(struct vga_device *vgadev, unsigned int rsrc) wake_up_all(&vga_wait_queue); } +/** + * vga_get - acquire & locks VGA resources + * @pdev: pci device of the VGA card or NULL for the system default + * @rsrc: bit mask of resources to acquire and lock + * @interruptible: blocking should be interruptible by signals ? + * + * This function acquires VGA resources for the given card and mark those + * resources locked. If the resource requested are "normal" (and not legacy) + * resources, the arbiter will first check whether the card is doing legacy + * decoding for that type of resource. If yes, the lock is "converted" into a + * legacy resource lock. + * + * The arbiter will first look for all VGA cards that might conflict and disable + * their IOs and/or Memory access, including VGA forwarding on P2P bridges if + * necessary, so that the requested resources can be used. Then, the card is + * marked as locking these resources and the IO and/or Memory accesses are + * enabled on the card (including VGA forwarding on parent P2P bridges if any). + * + * This function will block if some conflicting card is already locking one of + * the required resources (or any resource on a different bus segment, since P2P + * bridges don't differentiate VGA memory and IO afaik). You can indicate + * whether this blocking should be interruptible by a signal (for userland + * interface) or not. + * + * Must not be called at interrupt time or in atomic context. If the card + * already owns the resources, the function succeeds. Nested calls are + * supported (a per-resource counter is maintained) + * + * On success, release the VGA resource again with vga_put(). + * + * Returns: + * + * 0 on success, negative error code on failure. + */ int vga_get(struct pci_dev *pdev, unsigned int rsrc, int interruptible) { struct vga_device *vgadev, *conflict; @@ -408,6 +459,21 @@ int vga_get(struct pci_dev *pdev, unsigned int rsrc, int interruptible) } EXPORT_SYMBOL(vga_get); +/** + * vga_tryget - try to acquire & lock legacy VGA resources + * @pdev: pci devivce of VGA card or NULL for system default + * @rsrc: bit mask of resources to acquire and lock + * + * This function performs the same operation as vga_get(), but will return an + * error (-EBUSY) instead of blocking if the resources are already locked by + * another card. It can be called in any context + * + * On success, release the VGA resource again with vga_put(). + * + * Returns: + * + * 0 on success, negative error code on failure. + */ int vga_tryget(struct pci_dev *pdev, unsigned int rsrc) { struct vga_device *vgadev; @@ -435,6 +501,16 @@ bail: } EXPORT_SYMBOL(vga_tryget); +/** + * vga_put - release lock on legacy VGA resources + * @pdev: pci device of VGA card or NULL for system default + * @rsrc: but mask of resource to release + * + * This fuction releases resources previously locked by vga_get() or + * vga_tryget(). The resources aren't disabled right away, so that a subsequence + * vga_get() on the same card will succeed immediately. Resources have a + * counter, so locks are only released if the counter reaches 0. + */ void vga_put(struct pci_dev *pdev, unsigned int rsrc) { struct vga_device *vgadev; @@ -716,7 +792,37 @@ void vga_set_legacy_decoding(struct pci_dev *pdev, unsigned int decodes) } EXPORT_SYMBOL(vga_set_legacy_decoding); -/* call with NULL to unregister */ +/** + * vga_client_register - register or unregister a VGA arbitration client + * @pdev: pci device of the VGA client + * @cookie: client cookie to be used in callbacks + * @irq_set_state: irq state change callback + * @set_vga_decode: vga decode change callback + * + * Clients have two callback mechanisms they can use. + * + * @irq_set_state callback: If a client can't disable its GPUs VGA + * resources, then we need to be able to ask it to turn off its irqs when we + * turn off its mem and io decoding. + * + * @set_vga_decode callback: If a client can disable its GPU VGA resource, it + * will get a callback from this to set the encode/decode state. + * + * Rationale: we cannot disable VGA decode resources unconditionally some single + * GPU laptops seem to require ACPI or BIOS access to the VGA registers to + * control things like backlights etc. Hopefully newer multi-GPU laptops do + * something saner, and desktops won't have any special ACPI for this. The + * driver will get a callback when VGA arbitration is first used by userspace + * since some older X servers have issues. + * + * This function does not check whether a client for @pdev has been registered + * already. + * + * To unregister just call this function with @irq_set_state and @set_vga_decode + * both set to NULL for the same @pdev as originally used to register them. + * + * Returns: 0 on success, -1 on failure + */ int vga_client_register(struct pci_dev *pdev, void *cookie, void (*irq_set_state)(void *cookie, bool state), unsigned int (*set_vga_decode)(void *cookie, |