diff options
Diffstat (limited to 'drivers')
381 files changed, 18647 insertions, 7195 deletions
diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 4b34a5195c65..5bfdf222d5f9 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -304,8 +304,10 @@ static int intel_gtt_setup_scratch_page(void) if (intel_private.needs_dmar) { dma_addr = pci_map_page(intel_private.pcidev, page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(intel_private.pcidev, dma_addr)) + if (pci_dma_mapping_error(intel_private.pcidev, dma_addr)) { + __free_page(page); return -EINVAL; + } intel_private.scratch_page_dma = dma_addr; } else diff --git a/drivers/dma-buf/dma-fence-chain.c b/drivers/dma-buf/dma-fence-chain.c index c435bbba851c..3d123502ff12 100644 --- a/drivers/dma-buf/dma-fence-chain.c +++ b/drivers/dma-buf/dma-fence-chain.c @@ -99,12 +99,6 @@ int dma_fence_chain_find_seqno(struct dma_fence **pfence, uint64_t seqno) return -EINVAL; dma_fence_chain_for_each(*pfence, &chain->base) { - if ((*pfence)->seqno < seqno) { /* already signaled */ - dma_fence_put(*pfence); - *pfence = NULL; - break; - } - if ((*pfence)->context != chain->base.context || to_dma_fence_chain(*pfence)->prev_seqno < seqno) break; @@ -228,7 +222,6 @@ EXPORT_SYMBOL(dma_fence_chain_ops); * @chain: the chain node to initialize * @prev: the previous fence * @fence: the current fence - * @seqno: the sequence number (syncpt) of the fence within the chain * * Initialize a new chain node and either start a new chain or add the node to * the existing chain of the previous fence. diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index 656e9ac2d028..43624b4ee13d 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -64,6 +64,52 @@ static atomic64_t dma_fence_context_counter = ATOMIC64_INIT(1); * &dma_buf.resv pointer. */ +/** + * DOC: fence cross-driver contract + * + * Since &dma_fence provide a cross driver contract, all drivers must follow the + * same rules: + * + * * Fences must complete in a reasonable time. Fences which represent kernels + * and shaders submitted by userspace, which could run forever, must be backed + * up by timeout and gpu hang recovery code. Minimally that code must prevent + * further command submission and force complete all in-flight fences, e.g. + * when the driver or hardware do not support gpu reset, or if the gpu reset + * failed for some reason. Ideally the driver supports gpu recovery which only + * affects the offending userspace context, and no other userspace + * submissions. + * + * * Drivers may have different ideas of what completion within a reasonable + * time means. Some hang recovery code uses a fixed timeout, others a mix + * between observing forward progress and increasingly strict timeouts. + * Drivers should not try to second guess timeout handling of fences from + * other drivers. + * + * * To ensure there's no deadlocks of dma_fence_wait() against other locks + * drivers should annotate all code required to reach dma_fence_signal(), + * which completes the fences, with dma_fence_begin_signalling() and + * dma_fence_end_signalling(). + * + * * Drivers are allowed to call dma_fence_wait() while holding dma_resv_lock(). + * This means any code required for fence completion cannot acquire a + * &dma_resv lock. Note that this also pulls in the entire established + * locking hierarchy around dma_resv_lock() and dma_resv_unlock(). + * + * * Drivers are allowed to call dma_fence_wait() from their &shrinker + * callbacks. This means any code required for fence completion cannot + * allocate memory with GFP_KERNEL. + * + * * Drivers are allowed to call dma_fence_wait() from their &mmu_notifier + * respectively &mmu_interval_notifier callbacks. This means any code required + * for fence completeion cannot allocate memory with GFP_NOFS or GFP_NOIO. + * Only GFP_ATOMIC is permissible, which might fail. + * + * Note that only GPU drivers have a reasonable excuse for both requiring + * &mmu_interval_notifier and &shrinker callbacks at the same time as having to + * track asynchronous compute work using &dma_fence. No driver outside of + * drivers/gpu should ever call dma_fence_wait() in such contexts. + */ + static const char *dma_fence_stub_get_name(struct dma_fence *fence) { return "stub"; @@ -111,6 +157,160 @@ u64 dma_fence_context_alloc(unsigned num) EXPORT_SYMBOL(dma_fence_context_alloc); /** + * DOC: fence signalling annotation + * + * Proving correctness of all the kernel code around &dma_fence through code + * review and testing is tricky for a few reasons: + * + * * It is a cross-driver contract, and therefore all drivers must follow the + * same rules for lock nesting order, calling contexts for various functions + * and anything else significant for in-kernel interfaces. But it is also + * impossible to test all drivers in a single machine, hence brute-force N vs. + * N testing of all combinations is impossible. Even just limiting to the + * possible combinations is infeasible. + * + * * There is an enormous amount of driver code involved. For render drivers + * there's the tail of command submission, after fences are published, + * scheduler code, interrupt and workers to process job completion, + * and timeout, gpu reset and gpu hang recovery code. Plus for integration + * with core mm with have &mmu_notifier, respectively &mmu_interval_notifier, + * and &shrinker. For modesetting drivers there's the commit tail functions + * between when fences for an atomic modeset are published, and when the + * corresponding vblank completes, including any interrupt processing and + * related workers. Auditing all that code, across all drivers, is not + * feasible. + * + * * Due to how many other subsystems are involved and the locking hierarchies + * this pulls in there is extremely thin wiggle-room for driver-specific + * differences. &dma_fence interacts with almost all of the core memory + * handling through page fault handlers via &dma_resv, dma_resv_lock() and + * dma_resv_unlock(). On the other side it also interacts through all + * allocation sites through &mmu_notifier and &shrinker. + * + * Furthermore lockdep does not handle cross-release dependencies, which means + * any deadlocks between dma_fence_wait() and dma_fence_signal() can't be caught + * at runtime with some quick testing. The simplest example is one thread + * waiting on a &dma_fence while holding a lock:: + * + * lock(A); + * dma_fence_wait(B); + * unlock(A); + * + * while the other thread is stuck trying to acquire the same lock, which + * prevents it from signalling the fence the previous thread is stuck waiting + * on:: + * + * lock(A); + * unlock(A); + * dma_fence_signal(B); + * + * By manually annotating all code relevant to signalling a &dma_fence we can + * teach lockdep about these dependencies, which also helps with the validation + * headache since now lockdep can check all the rules for us:: + * + * cookie = dma_fence_begin_signalling(); + * lock(A); + * unlock(A); + * dma_fence_signal(B); + * dma_fence_end_signalling(cookie); + * + * For using dma_fence_begin_signalling() and dma_fence_end_signalling() to + * annotate critical sections the following rules need to be observed: + * + * * All code necessary to complete a &dma_fence must be annotated, from the + * point where a fence is accessible to other threads, to the point where + * dma_fence_signal() is called. Un-annotated code can contain deadlock issues, + * and due to the very strict rules and many corner cases it is infeasible to + * catch these just with review or normal stress testing. + * + * * &struct dma_resv deserves a special note, since the readers are only + * protected by rcu. This means the signalling critical section starts as soon + * as the new fences are installed, even before dma_resv_unlock() is called. + * + * * The only exception are fast paths and opportunistic signalling code, which + * calls dma_fence_signal() purely as an optimization, but is not required to + * guarantee completion of a &dma_fence. The usual example is a wait IOCTL + * which calls dma_fence_signal(), while the mandatory completion path goes + * through a hardware interrupt and possible job completion worker. + * + * * To aid composability of code, the annotations can be freely nested, as long + * as the overall locking hierarchy is consistent. The annotations also work + * both in interrupt and process context. Due to implementation details this + * requires that callers pass an opaque cookie from + * dma_fence_begin_signalling() to dma_fence_end_signalling(). + * + * * Validation against the cross driver contract is implemented by priming + * lockdep with the relevant hierarchy at boot-up. This means even just + * testing with a single device is enough to validate a driver, at least as + * far as deadlocks with dma_fence_wait() against dma_fence_signal() are + * concerned. + */ +#ifdef CONFIG_LOCKDEP +static struct lockdep_map dma_fence_lockdep_map = { + .name = "dma_fence_map" +}; + +/** + * dma_fence_begin_signalling - begin a critical DMA fence signalling section + * + * Drivers should use this to annotate the beginning of any code section + * required to eventually complete &dma_fence by calling dma_fence_signal(). + * + * The end of these critical sections are annotated with + * dma_fence_end_signalling(). + * + * Returns: + * + * Opaque cookie needed by the implementation, which needs to be passed to + * dma_fence_end_signalling(). + */ +bool dma_fence_begin_signalling(void) +{ + /* explicitly nesting ... */ + if (lock_is_held_type(&dma_fence_lockdep_map, 1)) + return true; + + /* rely on might_sleep check for soft/hardirq locks */ + if (in_atomic()) + return true; + + /* ... and non-recursive readlock */ + lock_acquire(&dma_fence_lockdep_map, 0, 0, 1, 1, NULL, _RET_IP_); + + return false; +} +EXPORT_SYMBOL(dma_fence_begin_signalling); + +/** + * dma_fence_end_signalling - end a critical DMA fence signalling section + * + * Closes a critical section annotation opened by dma_fence_begin_signalling(). + */ +void dma_fence_end_signalling(bool cookie) +{ + if (cookie) + return; + + lock_release(&dma_fence_lockdep_map, _RET_IP_); +} +EXPORT_SYMBOL(dma_fence_end_signalling); + +void __dma_fence_might_wait(void) +{ + bool tmp; + + tmp = lock_is_held_type(&dma_fence_lockdep_map, 1); + if (tmp) + lock_release(&dma_fence_lockdep_map, _THIS_IP_); + lock_map_acquire(&dma_fence_lockdep_map); + lock_map_release(&dma_fence_lockdep_map); + if (tmp) + lock_acquire(&dma_fence_lockdep_map, 0, 0, 1, 1, NULL, _THIS_IP_); +} +#endif + + +/** * dma_fence_signal_locked - signal completion of a fence * @fence: the fence to signal * @@ -170,14 +370,19 @@ int dma_fence_signal(struct dma_fence *fence) { unsigned long flags; int ret; + bool tmp; if (!fence) return -EINVAL; + tmp = dma_fence_begin_signalling(); + spin_lock_irqsave(fence->lock, flags); ret = dma_fence_signal_locked(fence); spin_unlock_irqrestore(fence->lock, flags); + dma_fence_end_signalling(tmp); + return ret; } EXPORT_SYMBOL(dma_fence_signal); @@ -210,6 +415,8 @@ dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout) might_sleep(); + __dma_fence_might_wait(); + trace_dma_fence_wait_start(fence); if (fence->ops->wait) ret = fence->ops->wait(fence, intr, timeout); diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c index b45f8514dc82..07f5273207e7 100644 --- a/drivers/dma-buf/dma-resv.c +++ b/drivers/dma-buf/dma-resv.c @@ -36,6 +36,7 @@ #include <linux/export.h> #include <linux/mm.h> #include <linux/sched/mm.h> +#include <linux/mmu_notifier.h> /** * DOC: Reservation Object Overview @@ -116,6 +117,13 @@ static int __init dma_resv_lockdep(void) if (ret == -EDEADLK) dma_resv_lock_slow(&obj, &ctx); fs_reclaim_acquire(GFP_KERNEL); +#ifdef CONFIG_MMU_NOTIFIER + lock_map_acquire(&__mmu_notifier_invalidate_range_start_map); + __dma_fence_might_wait(); + lock_map_release(&__mmu_notifier_invalidate_range_start_map); +#else + __dma_fence_might_wait(); +#endif fs_reclaim_release(GFP_KERNEL); ww_mutex_unlock(&obj.lock); ww_acquire_fini(&ctx); diff --git a/drivers/dma-buf/selftests.h b/drivers/dma-buf/selftests.h index 55918ef9adab..bc8cea67bf1e 100644 --- a/drivers/dma-buf/selftests.h +++ b/drivers/dma-buf/selftests.h @@ -5,7 +5,7 @@ * a module parameter. It must be unique and legal for a C identifier. * * The function should be of type int function(void). It may be conditionally - * compiled using #if IS_ENABLED(DRM_I915_SELFTEST). + * compiled using #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST). * * Tests are executed in order by igt/dmabuf_selftest */ diff --git a/drivers/dma-buf/st-dma-fence-chain.c b/drivers/dma-buf/st-dma-fence-chain.c index 5d45ba7ba3cd..9525f7f56119 100644 --- a/drivers/dma-buf/st-dma-fence-chain.c +++ b/drivers/dma-buf/st-dma-fence-chain.c @@ -318,15 +318,16 @@ static int find_out_of_order(void *arg) goto err; } - if (fence && fence != fc.chains[1]) { + /* + * We signaled the middle fence (2) of the 1-2-3 chain. The behavior + * of the dma-fence-chain is to make us wait for all the fences up to + * the point we want. Since fence 1 is still not signaled, this what + * we should get as fence to wait upon (fence 2 being garbage + * collected during the traversal of the chain). + */ + if (fence != fc.chains[0]) { pr_err("Incorrect chain-fence.seqno:%lld reported for completed seqno:2\n", - fence->seqno); - - dma_fence_get(fence); - err = dma_fence_chain_find_seqno(&fence, 2); - dma_fence_put(fence); - if (err) - pr_err("Reported %d for finding self!\n", err); + fence ? fence->seqno : 0); err = -EINVAL; } @@ -415,20 +416,18 @@ static int __find_race(void *arg) if (!fence) goto signal; - err = dma_fence_chain_find_seqno(&fence, seqno); - if (err) { - pr_err("Reported an invalid fence for find-self:%d\n", - seqno); - dma_fence_put(fence); - break; - } - - if (fence->seqno < seqno) { - pr_err("Reported an earlier fence.seqno:%lld for seqno:%d\n", - fence->seqno, seqno); - err = -EINVAL; - dma_fence_put(fence); - break; + /* + * We can only find ourselves if we are on fence we were + * looking for. + */ + if (fence->seqno == seqno) { + err = dma_fence_chain_find_seqno(&fence, seqno); + if (err) { + pr_err("Reported an invalid fence for find-self:%d\n", + seqno); + dma_fence_put(fence); + break; + } } dma_fence_put(fence); diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index de41d7928bff..668e9636c547 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -707,6 +707,16 @@ config XILINX_ZYNQMP_DMA help Enable support for Xilinx ZynqMP DMA controller. +config XILINX_ZYNQMP_DPDMA + tristate "Xilinx DPDMA Engine" + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + Enable support for Xilinx ZynqMP DisplayPort DMA. Choose this option + if you have a Xilinx ZynqMP SoC with a DisplayPort subsystem. The + driver provides the dmaengine required by the DisplayPort subsystem + display driver. + config ZX_DMA tristate "ZTE ZX DMA support" depends on ARCH_ZX || COMPILE_TEST diff --git a/drivers/dma/xilinx/Makefile b/drivers/dma/xilinx/Makefile index e921de575b55..767bb45f641f 100644 --- a/drivers/dma/xilinx/Makefile +++ b/drivers/dma/xilinx/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_XILINX_DMA) += xilinx_dma.o obj-$(CONFIG_XILINX_ZYNQMP_DMA) += zynqmp_dma.o +obj-$(CONFIG_XILINX_ZYNQMP_DPDMA) += xilinx_dpdma.o diff --git a/drivers/dma/xilinx/xilinx_dpdma.c b/drivers/dma/xilinx/xilinx_dpdma.c new file mode 100644 index 000000000000..af88a6762ef4 --- /dev/null +++ b/drivers/dma/xilinx/xilinx_dpdma.c @@ -0,0 +1,1533 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx ZynqMP DPDMA Engine driver + * + * Copyright (C) 2015 - 2020 Xilinx, Inc. + * + * Author: Hyun Woo Kwon <hyun.kwon@xilinx.com> + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/dmapool.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_dma.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/wait.h> + +#include <dt-bindings/dma/xlnx-zynqmp-dpdma.h> + +#include "../dmaengine.h" +#include "../virt-dma.h" + +/* DPDMA registers */ +#define XILINX_DPDMA_ERR_CTRL 0x000 +#define XILINX_DPDMA_ISR 0x004 +#define XILINX_DPDMA_IMR 0x008 +#define XILINX_DPDMA_IEN 0x00c +#define XILINX_DPDMA_IDS 0x010 +#define XILINX_DPDMA_INTR_DESC_DONE(n) BIT((n) + 0) +#define XILINX_DPDMA_INTR_DESC_DONE_MASK GENMASK(5, 0) +#define XILINX_DPDMA_INTR_NO_OSTAND(n) BIT((n) + 6) +#define XILINX_DPDMA_INTR_NO_OSTAND_MASK GENMASK(11, 6) +#define XILINX_DPDMA_INTR_AXI_ERR(n) BIT((n) + 12) +#define XILINX_DPDMA_INTR_AXI_ERR_MASK GENMASK(17, 12) +#define XILINX_DPDMA_INTR_DESC_ERR(n) BIT((n) + 16) +#define XILINX_DPDMA_INTR_DESC_ERR_MASK GENMASK(23, 18) +#define XILINX_DPDMA_INTR_WR_CMD_FIFO_FULL BIT(24) +#define XILINX_DPDMA_INTR_WR_DATA_FIFO_FULL BIT(25) +#define XILINX_DPDMA_INTR_AXI_4K_CROSS BIT(26) +#define XILINX_DPDMA_INTR_VSYNC BIT(27) +#define XILINX_DPDMA_INTR_CHAN_ERR_MASK 0x00041000 +#define XILINX_DPDMA_INTR_CHAN_ERR 0x00fff000 +#define XILINX_DPDMA_INTR_GLOBAL_ERR 0x07000000 +#define XILINX_DPDMA_INTR_ERR_ALL 0x07fff000 +#define XILINX_DPDMA_INTR_CHAN_MASK 0x00041041 +#define XILINX_DPDMA_INTR_GLOBAL_MASK 0x0f000000 +#define XILINX_DPDMA_INTR_ALL 0x0fffffff +#define XILINX_DPDMA_EISR 0x014 +#define XILINX_DPDMA_EIMR 0x018 +#define XILINX_DPDMA_EIEN 0x01c +#define XILINX_DPDMA_EIDS 0x020 +#define XILINX_DPDMA_EINTR_INV_APB BIT(0) +#define XILINX_DPDMA_EINTR_RD_AXI_ERR(n) BIT((n) + 1) +#define XILINX_DPDMA_EINTR_RD_AXI_ERR_MASK GENMASK(6, 1) +#define XILINX_DPDMA_EINTR_PRE_ERR(n) BIT((n) + 7) +#define XILINX_DPDMA_EINTR_PRE_ERR_MASK GENMASK(12, 7) +#define XILINX_DPDMA_EINTR_CRC_ERR(n) BIT((n) + 13) +#define XILINX_DPDMA_EINTR_CRC_ERR_MASK GENMASK(18, 13) +#define XILINX_DPDMA_EINTR_WR_AXI_ERR(n) BIT((n) + 19) +#define XILINX_DPDMA_EINTR_WR_AXI_ERR_MASK GENMASK(24, 19) +#define XILINX_DPDMA_EINTR_DESC_DONE_ERR(n) BIT((n) + 25) +#define XILINX_DPDMA_EINTR_DESC_DONE_ERR_MASK GENMASK(30, 25) +#define XILINX_DPDMA_EINTR_RD_CMD_FIFO_FULL BIT(32) +#define XILINX_DPDMA_EINTR_CHAN_ERR_MASK 0x02082082 +#define XILINX_DPDMA_EINTR_CHAN_ERR 0x7ffffffe +#define XILINX_DPDMA_EINTR_GLOBAL_ERR 0x80000001 +#define XILINX_DPDMA_EINTR_ALL 0xffffffff +#define XILINX_DPDMA_CNTL 0x100 +#define XILINX_DPDMA_GBL 0x104 +#define XILINX_DPDMA_GBL_TRIG_MASK(n) ((n) << 0) +#define XILINX_DPDMA_GBL_RETRIG_MASK(n) ((n) << 6) +#define XILINX_DPDMA_ALC0_CNTL 0x108 +#define XILINX_DPDMA_ALC0_STATUS 0x10c +#define XILINX_DPDMA_ALC0_MAX 0x110 +#define XILINX_DPDMA_ALC0_MIN 0x114 +#define XILINX_DPDMA_ALC0_ACC 0x118 +#define XILINX_DPDMA_ALC0_ACC_TRAN 0x11c +#define XILINX_DPDMA_ALC1_CNTL 0x120 +#define XILINX_DPDMA_ALC1_STATUS 0x124 +#define XILINX_DPDMA_ALC1_MAX 0x128 +#define XILINX_DPDMA_ALC1_MIN 0x12c +#define XILINX_DPDMA_ALC1_ACC 0x130 +#define XILINX_DPDMA_ALC1_ACC_TRAN 0x134 + +/* Channel register */ +#define XILINX_DPDMA_CH_BASE 0x200 +#define XILINX_DPDMA_CH_OFFSET 0x100 +#define XILINX_DPDMA_CH_DESC_START_ADDRE 0x000 +#define XILINX_DPDMA_CH_DESC_START_ADDRE_MASK GENMASK(15, 0) +#define XILINX_DPDMA_CH_DESC_START_ADDR 0x004 +#define XILINX_DPDMA_CH_DESC_NEXT_ADDRE 0x008 +#define XILINX_DPDMA_CH_DESC_NEXT_ADDR 0x00c +#define XILINX_DPDMA_CH_PYLD_CUR_ADDRE 0x010 +#define XILINX_DPDMA_CH_PYLD_CUR_ADDR 0x014 +#define XILINX_DPDMA_CH_CNTL 0x018 +#define XILINX_DPDMA_CH_CNTL_ENABLE BIT(0) +#define XILINX_DPDMA_CH_CNTL_PAUSE BIT(1) +#define XILINX_DPDMA_CH_CNTL_QOS_DSCR_WR_MASK GENMASK(5, 2) +#define XILINX_DPDMA_CH_CNTL_QOS_DSCR_RD_MASK GENMASK(9, 6) +#define XILINX_DPDMA_CH_CNTL_QOS_DATA_RD_MASK GENMASK(13, 10) +#define XILINX_DPDMA_CH_CNTL_QOS_VID_CLASS 11 +#define XILINX_DPDMA_CH_STATUS 0x01c +#define XILINX_DPDMA_CH_STATUS_OTRAN_CNT_MASK GENMASK(24, 21) +#define XILINX_DPDMA_CH_VDO 0x020 +#define XILINX_DPDMA_CH_PYLD_SZ 0x024 +#define XILINX_DPDMA_CH_DESC_ID 0x028 + +/* DPDMA descriptor fields */ +#define XILINX_DPDMA_DESC_CONTROL_PREEMBLE 0xa5 +#define XILINX_DPDMA_DESC_CONTROL_COMPLETE_INTR BIT(8) +#define XILINX_DPDMA_DESC_CONTROL_DESC_UPDATE BIT(9) +#define XILINX_DPDMA_DESC_CONTROL_IGNORE_DONE BIT(10) +#define XILINX_DPDMA_DESC_CONTROL_FRAG_MODE BIT(18) +#define XILINX_DPDMA_DESC_CONTROL_LAST BIT(19) +#define XILINX_DPDMA_DESC_CONTROL_ENABLE_CRC BIT(20) +#define XILINX_DPDMA_DESC_CONTROL_LAST_OF_FRAME BIT(21) +#define XILINX_DPDMA_DESC_ID_MASK GENMASK(15, 0) +#define XILINX_DPDMA_DESC_HSIZE_STRIDE_HSIZE_MASK GENMASK(17, 0) +#define XILINX_DPDMA_DESC_HSIZE_STRIDE_STRIDE_MASK GENMASK(31, 18) +#define XILINX_DPDMA_DESC_ADDR_EXT_NEXT_ADDR_MASK GENMASK(15, 0) +#define XILINX_DPDMA_DESC_ADDR_EXT_SRC_ADDR_MASK GENMASK(31, 16) + +#define XILINX_DPDMA_ALIGN_BYTES 256 +#define XILINX_DPDMA_LINESIZE_ALIGN_BITS 128 + +#define XILINX_DPDMA_NUM_CHAN 6 + +struct xilinx_dpdma_chan; + +/** + * struct xilinx_dpdma_hw_desc - DPDMA hardware descriptor + * @control: control configuration field + * @desc_id: descriptor ID + * @xfer_size: transfer size + * @hsize_stride: horizontal size and stride + * @timestamp_lsb: LSB of time stamp + * @timestamp_msb: MSB of time stamp + * @addr_ext: upper 16 bit of 48 bit address (next_desc and src_addr) + * @next_desc: next descriptor 32 bit address + * @src_addr: payload source address (1st page, 32 LSB) + * @addr_ext_23: payload source address (3nd and 3rd pages, 16 LSBs) + * @addr_ext_45: payload source address (4th and 5th pages, 16 LSBs) + * @src_addr2: payload source address (2nd page, 32 LSB) + * @src_addr3: payload source address (3rd page, 32 LSB) + * @src_addr4: payload source address (4th page, 32 LSB) + * @src_addr5: payload source address (5th page, 32 LSB) + * @crc: descriptor CRC + */ +struct xilinx_dpdma_hw_desc { + u32 control; + u32 desc_id; + u32 xfer_size; + u32 hsize_stride; + u32 timestamp_lsb; + u32 timestamp_msb; + u32 addr_ext; + u32 next_desc; + u32 src_addr; + u32 addr_ext_23; + u32 addr_ext_45; + u32 src_addr2; + u32 src_addr3; + u32 src_addr4; + u32 src_addr5; + u32 crc; +} __aligned(XILINX_DPDMA_ALIGN_BYTES); + +/** + * struct xilinx_dpdma_sw_desc - DPDMA software descriptor + * @hw: DPDMA hardware descriptor + * @node: list node for software descriptors + * @dma_addr: DMA address of the software descriptor + */ +struct xilinx_dpdma_sw_desc { + struct xilinx_dpdma_hw_desc hw; + struct list_head node; + dma_addr_t dma_addr; +}; + +/** + * struct xilinx_dpdma_tx_desc - DPDMA transaction descriptor + * @vdesc: virtual DMA descriptor + * @chan: DMA channel + * @descriptors: list of software descriptors + * @error: an error has been detected with this descriptor + */ +struct xilinx_dpdma_tx_desc { + struct virt_dma_desc vdesc; + struct xilinx_dpdma_chan *chan; + struct list_head descriptors; + bool error; +}; + +#define to_dpdma_tx_desc(_desc) \ + container_of(_desc, struct xilinx_dpdma_tx_desc, vdesc) + +/** + * struct xilinx_dpdma_chan - DPDMA channel + * @vchan: virtual DMA channel + * @reg: register base address + * @id: channel ID + * @wait_to_stop: queue to wait for outstanding transacitons before stopping + * @running: true if the channel is running + * @first_frame: flag for the first frame of stream + * @video_group: flag if multi-channel operation is needed for video channels + * @lock: lock to access struct xilinx_dpdma_chan + * @desc_pool: descriptor allocation pool + * @err_task: error IRQ bottom half handler + * @desc.pending: Descriptor schedule to the hardware, pending execution + * @desc.active: Descriptor being executed by the hardware + * @xdev: DPDMA device + */ +struct xilinx_dpdma_chan { + struct virt_dma_chan vchan; + void __iomem *reg; + unsigned int id; + + wait_queue_head_t wait_to_stop; + bool running; + bool first_frame; + bool video_group; + + spinlock_t lock; /* lock to access struct xilinx_dpdma_chan */ + struct dma_pool *desc_pool; + struct tasklet_struct err_task; + + struct { + struct xilinx_dpdma_tx_desc *pending; + struct xilinx_dpdma_tx_desc *active; + } desc; + + struct xilinx_dpdma_device *xdev; +}; + +#define to_xilinx_chan(_chan) \ + container_of(_chan, struct xilinx_dpdma_chan, vchan.chan) + +/** + * struct xilinx_dpdma_device - DPDMA device + * @common: generic dma device structure + * @reg: register base address + * @dev: generic device structure + * @irq: the interrupt number + * @axi_clk: axi clock + * @chan: DPDMA channels + * @ext_addr: flag for 64 bit system (48 bit addressing) + */ +struct xilinx_dpdma_device { + struct dma_device common; + void __iomem *reg; + struct device *dev; + int irq; + + struct clk *axi_clk; + struct xilinx_dpdma_chan *chan[XILINX_DPDMA_NUM_CHAN]; + + bool ext_addr; +}; + +/* ----------------------------------------------------------------------------- + * I/O Accessors + */ + +static inline u32 dpdma_read(void __iomem *base, u32 offset) +{ + return ioread32(base + offset); +} + +static inline void dpdma_write(void __iomem *base, u32 offset, u32 val) +{ + iowrite32(val, base + offset); +} + +static inline void dpdma_clr(void __iomem *base, u32 offset, u32 clr) +{ + dpdma_write(base, offset, dpdma_read(base, offset) & ~clr); +} + +static inline void dpdma_set(void __iomem *base, u32 offset, u32 set) +{ + dpdma_write(base, offset, dpdma_read(base, offset) | set); +} + +/* ----------------------------------------------------------------------------- + * Descriptor Operations + */ + +/** + * xilinx_dpdma_sw_desc_set_dma_addrs - Set DMA addresses in the descriptor + * @sw_desc: The software descriptor in which to set DMA addresses + * @prev: The previous descriptor + * @dma_addr: array of dma addresses + * @num_src_addr: number of addresses in @dma_addr + * + * Set all the DMA addresses in the hardware descriptor corresponding to @dev + * from @dma_addr. If a previous descriptor is specified in @prev, its next + * descriptor DMA address is set to the DMA address of @sw_desc. @prev may be + * identical to @sw_desc for cyclic transfers. + */ +static void xilinx_dpdma_sw_desc_set_dma_addrs(struct xilinx_dpdma_device *xdev, + struct xilinx_dpdma_sw_desc *sw_desc, + struct xilinx_dpdma_sw_desc *prev, + dma_addr_t dma_addr[], + unsigned int num_src_addr) +{ + struct xilinx_dpdma_hw_desc *hw_desc = &sw_desc->hw; + unsigned int i; + + hw_desc->src_addr = lower_32_bits(dma_addr[0]); + if (xdev->ext_addr) + hw_desc->addr_ext |= + FIELD_PREP(XILINX_DPDMA_DESC_ADDR_EXT_SRC_ADDR_MASK, + upper_32_bits(dma_addr[0])); + + for (i = 1; i < num_src_addr; i++) { + u32 *addr = &hw_desc->src_addr2; + + addr[i-1] = lower_32_bits(dma_addr[i]); + + if (xdev->ext_addr) { + u32 *addr_ext = &hw_desc->addr_ext_23; + u32 addr_msb; + + addr_msb = upper_32_bits(dma_addr[i]) & GENMASK(15, 0); + addr_msb <<= 16 * ((i - 1) % 2); + addr_ext[(i - 1) / 2] |= addr_msb; + } + } + + if (!prev) + return; + + prev->hw.next_desc = lower_32_bits(sw_desc->dma_addr); + if (xdev->ext_addr) + prev->hw.addr_ext |= + FIELD_PREP(XILINX_DPDMA_DESC_ADDR_EXT_NEXT_ADDR_MASK, + upper_32_bits(sw_desc->dma_addr)); +} + +/** + * xilinx_dpdma_chan_alloc_sw_desc - Allocate a software descriptor + * @chan: DPDMA channel + * + * Allocate a software descriptor from the channel's descriptor pool. + * + * Return: a software descriptor or NULL. + */ +static struct xilinx_dpdma_sw_desc * +xilinx_dpdma_chan_alloc_sw_desc(struct xilinx_dpdma_chan *chan) +{ + struct xilinx_dpdma_sw_desc *sw_desc; + dma_addr_t dma_addr; + + sw_desc = dma_pool_zalloc(chan->desc_pool, GFP_ATOMIC, &dma_addr); + if (!sw_desc) + return NULL; + + sw_desc->dma_addr = dma_addr; + + return sw_desc; +} + +/** + * xilinx_dpdma_chan_free_sw_desc - Free a software descriptor + * @chan: DPDMA channel + * @sw_desc: software descriptor to free + * + * Free a software descriptor from the channel's descriptor pool. + */ +static void +xilinx_dpdma_chan_free_sw_desc(struct xilinx_dpdma_chan *chan, + struct xilinx_dpdma_sw_desc *sw_desc) +{ + dma_pool_free(chan->desc_pool, sw_desc, sw_desc->dma_addr); +} + +/** + * xilinx_dpdma_chan_dump_tx_desc - Dump a tx descriptor + * @chan: DPDMA channel + * @tx_desc: tx descriptor to dump + * + * Dump contents of a tx descriptor + */ +static void xilinx_dpdma_chan_dump_tx_desc(struct xilinx_dpdma_chan *chan, + struct xilinx_dpdma_tx_desc *tx_desc) +{ + struct xilinx_dpdma_sw_desc *sw_desc; + struct device *dev = chan->xdev->dev; + unsigned int i = 0; + + dev_dbg(dev, "------- TX descriptor dump start -------\n"); + dev_dbg(dev, "------- channel ID = %d -------\n", chan->id); + + list_for_each_entry(sw_desc, &tx_desc->descriptors, node) { + struct xilinx_dpdma_hw_desc *hw_desc = &sw_desc->hw; + + dev_dbg(dev, "------- HW descriptor %d -------\n", i++); + dev_dbg(dev, "descriptor DMA addr: %pad\n", &sw_desc->dma_addr); + dev_dbg(dev, "control: 0x%08x\n", hw_desc->control); + dev_dbg(dev, "desc_id: 0x%08x\n", hw_desc->desc_id); + dev_dbg(dev, "xfer_size: 0x%08x\n", hw_desc->xfer_size); + dev_dbg(dev, "hsize_stride: 0x%08x\n", hw_desc->hsize_stride); + dev_dbg(dev, "timestamp_lsb: 0x%08x\n", hw_desc->timestamp_lsb); + dev_dbg(dev, "timestamp_msb: 0x%08x\n", hw_desc->timestamp_msb); + dev_dbg(dev, "addr_ext: 0x%08x\n", hw_desc->addr_ext); + dev_dbg(dev, "next_desc: 0x%08x\n", hw_desc->next_desc); + dev_dbg(dev, "src_addr: 0x%08x\n", hw_desc->src_addr); + dev_dbg(dev, "addr_ext_23: 0x%08x\n", hw_desc->addr_ext_23); + dev_dbg(dev, "addr_ext_45: 0x%08x\n", hw_desc->addr_ext_45); + dev_dbg(dev, "src_addr2: 0x%08x\n", hw_desc->src_addr2); + dev_dbg(dev, "src_addr3: 0x%08x\n", hw_desc->src_addr3); + dev_dbg(dev, "src_addr4: 0x%08x\n", hw_desc->src_addr4); + dev_dbg(dev, "src_addr5: 0x%08x\n", hw_desc->src_addr5); + dev_dbg(dev, "crc: 0x%08x\n", hw_desc->crc); + } + + dev_dbg(dev, "------- TX descriptor dump end -------\n"); +} + +/** + * xilinx_dpdma_chan_alloc_tx_desc - Allocate a transaction descriptor + * @chan: DPDMA channel + * + * Allocate a tx descriptor. + * + * Return: a tx descriptor or NULL. + */ +static struct xilinx_dpdma_tx_desc * +xilinx_dpdma_chan_alloc_tx_desc(struct xilinx_dpdma_chan *chan) +{ + struct xilinx_dpdma_tx_desc *tx_desc; + + tx_desc = kzalloc(sizeof(*tx_desc), GFP_NOWAIT); + if (!tx_desc) + return NULL; + + INIT_LIST_HEAD(&tx_desc->descriptors); + tx_desc->chan = chan; + tx_desc->error = false; + + return tx_desc; +} + +/** + * xilinx_dpdma_chan_free_tx_desc - Free a virtual DMA descriptor + * @vdesc: virtual DMA descriptor + * + * Free the virtual DMA descriptor @vdesc including its software descriptors. + */ +static void xilinx_dpdma_chan_free_tx_desc(struct virt_dma_desc *vdesc) +{ + struct xilinx_dpdma_sw_desc *sw_desc, *next; + struct xilinx_dpdma_tx_desc *desc; + + if (!vdesc) + return; + + desc = to_dpdma_tx_desc(vdesc); + + list_for_each_entry_safe(sw_desc, next, &desc->descriptors, node) { + list_del(&sw_desc->node); + xilinx_dpdma_chan_free_sw_desc(desc->chan, sw_desc); + } + + kfree(desc); +} + +/** + * xilinx_dpdma_chan_prep_interleaved_dma - Prepare an interleaved dma + * descriptor + * @chan: DPDMA channel + * @xt: dma interleaved template + * + * Prepare a tx descriptor including internal software/hardware descriptors + * based on @xt. + * + * Return: A DPDMA TX descriptor on success, or NULL. + */ +static struct xilinx_dpdma_tx_desc * +xilinx_dpdma_chan_prep_interleaved_dma(struct xilinx_dpdma_chan *chan, + struct dma_interleaved_template *xt) +{ + struct xilinx_dpdma_tx_desc *tx_desc; + struct xilinx_dpdma_sw_desc *sw_desc; + struct xilinx_dpdma_hw_desc *hw_desc; + size_t hsize = xt->sgl[0].size; + size_t stride = hsize + xt->sgl[0].icg; + + if (!IS_ALIGNED(xt->src_start, XILINX_DPDMA_ALIGN_BYTES)) { + dev_err(chan->xdev->dev, "buffer should be aligned at %d B\n", + XILINX_DPDMA_ALIGN_BYTES); + return NULL; + } + + tx_desc = xilinx_dpdma_chan_alloc_tx_desc(chan); + if (!tx_desc) + return NULL; + + sw_desc = xilinx_dpdma_chan_alloc_sw_desc(chan); + if (!sw_desc) { + xilinx_dpdma_chan_free_tx_desc(&tx_desc->vdesc); + return NULL; + } + + xilinx_dpdma_sw_desc_set_dma_addrs(chan->xdev, sw_desc, sw_desc, + &xt->src_start, 1); + + hw_desc = &sw_desc->hw; + hsize = ALIGN(hsize, XILINX_DPDMA_LINESIZE_ALIGN_BITS / 8); + hw_desc->xfer_size = hsize * xt->numf; + hw_desc->hsize_stride = + FIELD_PREP(XILINX_DPDMA_DESC_HSIZE_STRIDE_HSIZE_MASK, hsize) | + FIELD_PREP(XILINX_DPDMA_DESC_HSIZE_STRIDE_STRIDE_MASK, + stride / 16); + hw_desc->control |= XILINX_DPDMA_DESC_CONTROL_PREEMBLE; + hw_desc->control |= XILINX_DPDMA_DESC_CONTROL_COMPLETE_INTR; + hw_desc->control |= XILINX_DPDMA_DESC_CONTROL_IGNORE_DONE; + hw_desc->control |= XILINX_DPDMA_DESC_CONTROL_LAST_OF_FRAME; + + list_add_tail(&sw_desc->node, &tx_desc->descriptors); + + return tx_desc; +} + +/* ----------------------------------------------------------------------------- + * DPDMA Channel Operations + */ + +/** + * xilinx_dpdma_chan_enable - Enable the channel + * @chan: DPDMA channel + * + * Enable the channel and its interrupts. Set the QoS values for video class. + */ +static void xilinx_dpdma_chan_enable(struct xilinx_dpdma_chan *chan) +{ + u32 reg; + + reg = (XILINX_DPDMA_INTR_CHAN_MASK << chan->id) + | XILINX_DPDMA_INTR_GLOBAL_MASK; + dpdma_write(chan->xdev->reg, XILINX_DPDMA_IEN, reg); + reg = (XILINX_DPDMA_EINTR_CHAN_ERR_MASK << chan->id) + | XILINX_DPDMA_INTR_GLOBAL_ERR; + dpdma_write(chan->xdev->reg, XILINX_DPDMA_EIEN, reg); + + reg = XILINX_DPDMA_CH_CNTL_ENABLE + | FIELD_PREP(XILINX_DPDMA_CH_CNTL_QOS_DSCR_WR_MASK, + XILINX_DPDMA_CH_CNTL_QOS_VID_CLASS) + | FIELD_PREP(XILINX_DPDMA_CH_CNTL_QOS_DSCR_RD_MASK, + XILINX_DPDMA_CH_CNTL_QOS_VID_CLASS) + | FIELD_PREP(XILINX_DPDMA_CH_CNTL_QOS_DATA_RD_MASK, + XILINX_DPDMA_CH_CNTL_QOS_VID_CLASS); + dpdma_set(chan->reg, XILINX_DPDMA_CH_CNTL, reg); +} + +/** + * xilinx_dpdma_chan_disable - Disable the channel + * @chan: DPDMA channel + * + * Disable the channel and its interrupts. + */ +static void xilinx_dpdma_chan_disable(struct xilinx_dpdma_chan *chan) +{ + u32 reg; + + reg = XILINX_DPDMA_INTR_CHAN_MASK << chan->id; + dpdma_write(chan->xdev->reg, XILINX_DPDMA_IEN, reg); + reg = XILINX_DPDMA_EINTR_CHAN_ERR_MASK << chan->id; + dpdma_write(chan->xdev->reg, XILINX_DPDMA_EIEN, reg); + + dpdma_clr(chan->reg, XILINX_DPDMA_CH_CNTL, XILINX_DPDMA_CH_CNTL_ENABLE); +} + +/** + * xilinx_dpdma_chan_pause - Pause the channel + * @chan: DPDMA channel + * + * Pause the channel. + */ +static void xilinx_dpdma_chan_pause(struct xilinx_dpdma_chan *chan) +{ + dpdma_set(chan->reg, XILINX_DPDMA_CH_CNTL, XILINX_DPDMA_CH_CNTL_PAUSE); +} + +/** + * xilinx_dpdma_chan_unpause - Unpause the channel + * @chan: DPDMA channel + * + * Unpause the channel. + */ +static void xilinx_dpdma_chan_unpause(struct xilinx_dpdma_chan *chan) +{ + dpdma_clr(chan->reg, XILINX_DPDMA_CH_CNTL, XILINX_DPDMA_CH_CNTL_PAUSE); +} + +static u32 xilinx_dpdma_chan_video_group_ready(struct xilinx_dpdma_chan *chan) +{ + struct xilinx_dpdma_device *xdev = chan->xdev; + u32 channels = 0; + unsigned int i; + + for (i = ZYNQMP_DPDMA_VIDEO0; i <= ZYNQMP_DPDMA_VIDEO2; i++) { + if (xdev->chan[i]->video_group && !xdev->chan[i]->running) + return 0; + + if (xdev->chan[i]->video_group) + channels |= BIT(i); + } + + return channels; +} + +/** + * xilinx_dpdma_chan_queue_transfer - Queue the next transfer + * @chan: DPDMA channel + * + * Queue the next descriptor, if any, to the hardware. If the channel is + * stopped, start it first. Otherwise retrigger it with the next descriptor. + */ +static void xilinx_dpdma_chan_queue_transfer(struct xilinx_dpdma_chan *chan) +{ + struct xilinx_dpdma_device *xdev = chan->xdev; + struct xilinx_dpdma_sw_desc *sw_desc; + struct xilinx_dpdma_tx_desc *desc; + struct virt_dma_desc *vdesc; + u32 reg, channels; + + lockdep_assert_held(&chan->lock); + + if (chan->desc.pending) + return; + + if (!chan->running) { + xilinx_dpdma_chan_unpause(chan); + xilinx_dpdma_chan_enable(chan); + chan->first_frame = true; + chan->running = true; + } + + if (chan->video_group) + channels = xilinx_dpdma_chan_video_group_ready(chan); + else + channels = BIT(chan->id); + + if (!channels) + return; + + vdesc = vchan_next_desc(&chan->vchan); + if (!vdesc) + return; + + desc = to_dpdma_tx_desc(vdesc); + chan->desc.pending = desc; + list_del(&desc->vdesc.node); + + /* + * Assign the cookie to descriptors in this transaction. Only 16 bit + * will be used, but it should be enough. + */ + list_for_each_entry(sw_desc, &desc->descriptors, node) + sw_desc->hw.desc_id = desc->vdesc.tx.cookie; + + sw_desc = list_first_entry(&desc->descriptors, + struct xilinx_dpdma_sw_desc, node); + dpdma_write(chan->reg, XILINX_DPDMA_CH_DESC_START_ADDR, + lower_32_bits(sw_desc->dma_addr)); + if (xdev->ext_addr) + dpdma_write(chan->reg, XILINX_DPDMA_CH_DESC_START_ADDRE, + FIELD_PREP(XILINX_DPDMA_CH_DESC_START_ADDRE_MASK, + upper_32_bits(sw_desc->dma_addr))); + + if (chan->first_frame) + reg = XILINX_DPDMA_GBL_TRIG_MASK(channels); + else + reg = XILINX_DPDMA_GBL_RETRIG_MASK(channels); + + chan->first_frame = false; + + dpdma_write(xdev->reg, XILINX_DPDMA_GBL, reg); +} + +/** + * xilinx_dpdma_chan_ostand - Number of outstanding transactions + * @chan: DPDMA channel + * + * Read and return the number of outstanding transactions from register. + * + * Return: Number of outstanding transactions from the status register. + */ +static u32 xilinx_dpdma_chan_ostand(struct xilinx_dpdma_chan *chan) +{ + return FIELD_GET(XILINX_DPDMA_CH_STATUS_OTRAN_CNT_MASK, + dpdma_read(chan->reg, XILINX_DPDMA_CH_STATUS)); +} + +/** + * xilinx_dpdma_chan_no_ostand - Notify no outstanding transaction event + * @chan: DPDMA channel + * + * Notify waiters for no outstanding event, so waiters can stop the channel + * safely. This function is supposed to be called when 'no outstanding' + * interrupt is generated. The 'no outstanding' interrupt is disabled and + * should be re-enabled when this event is handled. If the channel status + * register still shows some number of outstanding transactions, the interrupt + * remains enabled. + * + * Return: 0 on success. On failure, -EWOULDBLOCK if there's still outstanding + * transaction(s). + */ +static int xilinx_dpdma_chan_notify_no_ostand(struct xilinx_dpdma_chan *chan) +{ + u32 cnt; + + cnt = xilinx_dpdma_chan_ostand(chan); + if (cnt) { + dev_dbg(chan->xdev->dev, "%d outstanding transactions\n", cnt); + return -EWOULDBLOCK; + } + + /* Disable 'no outstanding' interrupt */ + dpdma_write(chan->xdev->reg, XILINX_DPDMA_IDS, + XILINX_DPDMA_INTR_NO_OSTAND(chan->id)); + wake_up(&chan->wait_to_stop); + + return 0; +} + +/** + * xilinx_dpdma_chan_wait_no_ostand - Wait for the no outstanding irq + * @chan: DPDMA channel + * + * Wait for the no outstanding transaction interrupt. This functions can sleep + * for 50ms. + * + * Return: 0 on success. On failure, -ETIMEOUT for time out, or the error code + * from wait_event_interruptible_timeout(). + */ +static int xilinx_dpdma_chan_wait_no_ostand(struct xilinx_dpdma_chan *chan) +{ + int ret; + + /* Wait for a no outstanding transaction interrupt upto 50msec */ + ret = wait_event_interruptible_timeout(chan->wait_to_stop, + !xilinx_dpdma_chan_ostand(chan), + msecs_to_jiffies(50)); + if (ret > 0) { + dpdma_write(chan->xdev->reg, XILINX_DPDMA_IEN, + XILINX_DPDMA_INTR_NO_OSTAND(chan->id)); + return 0; + } + + dev_err(chan->xdev->dev, "not ready to stop: %d trans\n", + xilinx_dpdma_chan_ostand(chan)); + + if (ret == 0) + return -ETIMEDOUT; + + return ret; +} + +/** + * xilinx_dpdma_chan_poll_no_ostand - Poll the outstanding transaction status + * @chan: DPDMA channel + * + * Poll the outstanding transaction status, and return when there's no + * outstanding transaction. This functions can be used in the interrupt context + * or where the atomicity is required. Calling thread may wait more than 50ms. + * + * Return: 0 on success, or -ETIMEDOUT. + */ +static int xilinx_dpdma_chan_poll_no_ostand(struct xilinx_dpdma_chan *chan) +{ + u32 cnt, loop = 50000; + + /* Poll at least for 50ms (20 fps). */ + do { + cnt = xilinx_dpdma_chan_ostand(chan); + udelay(1); + } while (loop-- > 0 && cnt); + + if (loop) { + dpdma_write(chan->xdev->reg, XILINX_DPDMA_IEN, + XILINX_DPDMA_INTR_NO_OSTAND(chan->id)); + return 0; + } + + dev_err(chan->xdev->dev, "not ready to stop: %d trans\n", + xilinx_dpdma_chan_ostand(chan)); + + return -ETIMEDOUT; +} + +/** + * xilinx_dpdma_chan_stop - Stop the channel + * @chan: DPDMA channel + * + * Stop a previously paused channel by first waiting for completion of all + * outstanding transaction and then disabling the channel. + * + * Return: 0 on success, or -ETIMEDOUT if the channel failed to stop. + */ +static int xilinx_dpdma_chan_stop(struct xilinx_dpdma_chan *chan) +{ + unsigned long flags; + int ret; + + ret = xilinx_dpdma_chan_wait_no_ostand(chan); + if (ret) + return ret; + + spin_lock_irqsave(&chan->lock, flags); + xilinx_dpdma_chan_disable(chan); + chan->running = false; + spin_unlock_irqrestore(&chan->lock, flags); + + return 0; +} + +/** + * xilinx_dpdma_chan_done_irq - Handle hardware descriptor completion + * @chan: DPDMA channel + * + * Handle completion of the currently active descriptor (@chan->desc.active). As + * we currently support cyclic transfers only, this just invokes the cyclic + * callback. The descriptor will be completed at the VSYNC interrupt when a new + * descriptor replaces it. + */ +static void xilinx_dpdma_chan_done_irq(struct xilinx_dpdma_chan *chan) +{ + struct xilinx_dpdma_tx_desc *active = chan->desc.active; + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + + if (active) + vchan_cyclic_callback(&active->vdesc); + else + dev_warn(chan->xdev->dev, + "DONE IRQ with no active descriptor!\n"); + + spin_unlock_irqrestore(&chan->lock, flags); +} + +/** + * xilinx_dpdma_chan_vsync_irq - Handle hardware descriptor scheduling + * @chan: DPDMA channel + * + * At VSYNC the active descriptor may have been replaced by the pending + * descriptor. Detect this through the DESC_ID and perform appropriate + * bookkeeping. + */ +static void xilinx_dpdma_chan_vsync_irq(struct xilinx_dpdma_chan *chan) +{ + struct xilinx_dpdma_tx_desc *pending; + struct xilinx_dpdma_sw_desc *sw_desc; + unsigned long flags; + u32 desc_id; + + spin_lock_irqsave(&chan->lock, flags); + + pending = chan->desc.pending; + if (!chan->running || !pending) + goto out; + + desc_id = dpdma_read(chan->reg, XILINX_DPDMA_CH_DESC_ID); + + /* If the retrigger raced with vsync, retry at the next frame. */ + sw_desc = list_first_entry(&pending->descriptors, + struct xilinx_dpdma_sw_desc, node); + if (sw_desc->hw.desc_id != desc_id) + goto out; + + /* + * Complete the active descriptor, if any, promote the pending + * descriptor to active, and queue the next transfer, if any. + */ + if (chan->desc.active) + vchan_cookie_complete(&chan->desc.active->vdesc); + chan->desc.active = pending; + chan->desc.pending = NULL; + + xilinx_dpdma_chan_queue_transfer(chan); + +out: + spin_unlock_irqrestore(&chan->lock, flags); +} + +/** + * xilinx_dpdma_chan_err - Detect any channel error + * @chan: DPDMA channel + * @isr: masked Interrupt Status Register + * @eisr: Error Interrupt Status Register + * + * Return: true if any channel error occurs, or false otherwise. + */ +static bool +xilinx_dpdma_chan_err(struct xilinx_dpdma_chan *chan, u32 isr, u32 eisr) +{ + if (!chan) + return false; + + if (chan->running && + ((isr & (XILINX_DPDMA_INTR_CHAN_ERR_MASK << chan->id)) || + (eisr & (XILINX_DPDMA_EINTR_CHAN_ERR_MASK << chan->id)))) + return true; + + return false; +} + +/** + * xilinx_dpdma_chan_handle_err - DPDMA channel error handling + * @chan: DPDMA channel + * + * This function is called when any channel error or any global error occurs. + * The function disables the paused channel by errors and determines + * if the current active descriptor can be rescheduled depending on + * the descriptor status. + */ +static void xilinx_dpdma_chan_handle_err(struct xilinx_dpdma_chan *chan) +{ + struct xilinx_dpdma_device *xdev = chan->xdev; + struct xilinx_dpdma_tx_desc *active; + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + + dev_dbg(xdev->dev, "cur desc addr = 0x%04x%08x\n", + dpdma_read(chan->reg, XILINX_DPDMA_CH_DESC_START_ADDRE), + dpdma_read(chan->reg, XILINX_DPDMA_CH_DESC_START_ADDR)); + dev_dbg(xdev->dev, "cur payload addr = 0x%04x%08x\n", + dpdma_read(chan->reg, XILINX_DPDMA_CH_PYLD_CUR_ADDRE), + dpdma_read(chan->reg, XILINX_DPDMA_CH_PYLD_CUR_ADDR)); + + xilinx_dpdma_chan_disable(chan); + chan->running = false; + + if (!chan->desc.active) + goto out_unlock; + + active = chan->desc.active; + chan->desc.active = NULL; + + xilinx_dpdma_chan_dump_tx_desc(chan, active); + + if (active->error) + dev_dbg(xdev->dev, "repeated error on desc\n"); + + /* Reschedule if there's no new descriptor */ + if (!chan->desc.pending && + list_empty(&chan->vchan.desc_issued)) { + active->error = true; + list_add_tail(&active->vdesc.node, + &chan->vchan.desc_issued); + } else { + xilinx_dpdma_chan_free_tx_desc(&active->vdesc); + } + +out_unlock: + spin_unlock_irqrestore(&chan->lock, flags); +} + +/* ----------------------------------------------------------------------------- + * DMA Engine Operations + */ + +static struct dma_async_tx_descriptor * +xilinx_dpdma_prep_interleaved_dma(struct dma_chan *dchan, + struct dma_interleaved_template *xt, + unsigned long flags) +{ + struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan); + struct xilinx_dpdma_tx_desc *desc; + + if (xt->dir != DMA_MEM_TO_DEV) + return NULL; + + if (!xt->numf || !xt->sgl[0].size) + return NULL; + + if (!(flags & DMA_PREP_REPEAT) || !(flags & DMA_PREP_LOAD_EOT)) + return NULL; + + desc = xilinx_dpdma_chan_prep_interleaved_dma(chan, xt); + if (!desc) + return NULL; + + vchan_tx_prep(&chan->vchan, &desc->vdesc, flags | DMA_CTRL_ACK); + + return &desc->vdesc.tx; +} + +/** + * xilinx_dpdma_alloc_chan_resources - Allocate resources for the channel + * @dchan: DMA channel + * + * Allocate a descriptor pool for the channel. + * + * Return: 0 on success, or -ENOMEM if failed to allocate a pool. + */ +static int xilinx_dpdma_alloc_chan_resources(struct dma_chan *dchan) +{ + struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan); + size_t align = __alignof__(struct xilinx_dpdma_sw_desc); + + chan->desc_pool = dma_pool_create(dev_name(chan->xdev->dev), + chan->xdev->dev, + sizeof(struct xilinx_dpdma_sw_desc), + align, 0); + if (!chan->desc_pool) { + dev_err(chan->xdev->dev, + "failed to allocate a descriptor pool\n"); + return -ENOMEM; + } + + return 0; +} + +/** + * xilinx_dpdma_free_chan_resources - Free all resources for the channel + * @dchan: DMA channel + * + * Free resources associated with the virtual DMA channel, and destroy the + * descriptor pool. + */ +static void xilinx_dpdma_free_chan_resources(struct dma_chan *dchan) +{ + struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan); + + vchan_free_chan_resources(&chan->vchan); + + dma_pool_destroy(chan->desc_pool); + chan->desc_pool = NULL; +} + +static void xilinx_dpdma_issue_pending(struct dma_chan *dchan) +{ + struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan); + unsigned long flags; + + spin_lock_irqsave(&chan->vchan.lock, flags); + if (vchan_issue_pending(&chan->vchan)) + xilinx_dpdma_chan_queue_transfer(chan); + spin_unlock_irqrestore(&chan->vchan.lock, flags); +} + +static int xilinx_dpdma_config(struct dma_chan *dchan, + struct dma_slave_config *config) +{ + struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan); + unsigned long flags; + + /* + * The destination address doesn't need to be specified as the DPDMA is + * hardwired to the destination (the DP controller). The transfer + * width, burst size and port window size are thus meaningless, they're + * fixed both on the DPDMA side and on the DP controller side. + */ + + spin_lock_irqsave(&chan->lock, flags); + + /* + * Abuse the slave_id to indicate that the channel is part of a video + * group. + */ + if (chan->id >= ZYNQMP_DPDMA_VIDEO0 && chan->id <= ZYNQMP_DPDMA_VIDEO2) + chan->video_group = config->slave_id != 0; + + spin_unlock_irqrestore(&chan->lock, flags); + + return 0; +} + +static int xilinx_dpdma_pause(struct dma_chan *dchan) +{ + xilinx_dpdma_chan_pause(to_xilinx_chan(dchan)); + + return 0; +} + +static int xilinx_dpdma_resume(struct dma_chan *dchan) +{ + xilinx_dpdma_chan_unpause(to_xilinx_chan(dchan)); + + return 0; +} + +/** + * xilinx_dpdma_terminate_all - Terminate the channel and descriptors + * @dchan: DMA channel + * + * Pause the channel without waiting for ongoing transfers to complete. Waiting + * for completion is performed by xilinx_dpdma_synchronize() that will disable + * the channel to complete the stop. + * + * All the descriptors associated with the channel that are guaranteed not to + * be touched by the hardware. The pending and active descriptor are not + * touched, and will be freed either upon completion, or by + * xilinx_dpdma_synchronize(). + * + * Return: 0 on success, or -ETIMEDOUT if the channel failed to stop. + */ +static int xilinx_dpdma_terminate_all(struct dma_chan *dchan) +{ + struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan); + struct xilinx_dpdma_device *xdev = chan->xdev; + LIST_HEAD(descriptors); + unsigned long flags; + unsigned int i; + + /* Pause the channel (including the whole video group if applicable). */ + if (chan->video_group) { + for (i = ZYNQMP_DPDMA_VIDEO0; i <= ZYNQMP_DPDMA_VIDEO2; i++) { + if (xdev->chan[i]->video_group && + xdev->chan[i]->running) { + xilinx_dpdma_chan_pause(xdev->chan[i]); + xdev->chan[i]->video_group = false; + } + } + } else { + xilinx_dpdma_chan_pause(chan); + } + + /* Gather all the descriptors we can free and free them. */ + spin_lock_irqsave(&chan->vchan.lock, flags); + vchan_get_all_descriptors(&chan->vchan, &descriptors); + spin_unlock_irqrestore(&chan->vchan.lock, flags); + + vchan_dma_desc_free_list(&chan->vchan, &descriptors); + + return 0; +} + +/** + * xilinx_dpdma_synchronize - Synchronize callback execution + * @dchan: DMA channel + * + * Synchronizing callback execution ensures that all previously issued + * transfers have completed and all associated callbacks have been called and + * have returned. + * + * This function waits for the DMA channel to stop. It assumes it has been + * paused by a previous call to dmaengine_terminate_async(), and that no new + * pending descriptors have been issued with dma_async_issue_pending(). The + * behaviour is undefined otherwise. + */ +static void xilinx_dpdma_synchronize(struct dma_chan *dchan) +{ + struct xilinx_dpdma_chan *chan = to_xilinx_chan(dchan); + unsigned long flags; + + xilinx_dpdma_chan_stop(chan); + + spin_lock_irqsave(&chan->vchan.lock, flags); + if (chan->desc.pending) { + vchan_terminate_vdesc(&chan->desc.pending->vdesc); + chan->desc.pending = NULL; + } + if (chan->desc.active) { + vchan_terminate_vdesc(&chan->desc.active->vdesc); + chan->desc.active = NULL; + } + spin_unlock_irqrestore(&chan->vchan.lock, flags); + + vchan_synchronize(&chan->vchan); +} + +/* ----------------------------------------------------------------------------- + * Interrupt and Tasklet Handling + */ + +/** + * xilinx_dpdma_err - Detect any global error + * @isr: Interrupt Status Register + * @eisr: Error Interrupt Status Register + * + * Return: True if any global error occurs, or false otherwise. + */ +static bool xilinx_dpdma_err(u32 isr, u32 eisr) +{ + if (isr & XILINX_DPDMA_INTR_GLOBAL_ERR || + eisr & XILINX_DPDMA_EINTR_GLOBAL_ERR) + return true; + + return false; +} + +/** + * xilinx_dpdma_handle_err_irq - Handle DPDMA error interrupt + * @xdev: DPDMA device + * @isr: masked Interrupt Status Register + * @eisr: Error Interrupt Status Register + * + * Handle if any error occurs based on @isr and @eisr. This function disables + * corresponding error interrupts, and those should be re-enabled once handling + * is done. + */ +static void xilinx_dpdma_handle_err_irq(struct xilinx_dpdma_device *xdev, + u32 isr, u32 eisr) +{ + bool err = xilinx_dpdma_err(isr, eisr); + unsigned int i; + + dev_dbg_ratelimited(xdev->dev, + "error irq: isr = 0x%08x, eisr = 0x%08x\n", + isr, eisr); + + /* Disable channel error interrupts until errors are handled. */ + dpdma_write(xdev->reg, XILINX_DPDMA_IDS, + isr & ~XILINX_DPDMA_INTR_GLOBAL_ERR); + dpdma_write(xdev->reg, XILINX_DPDMA_EIDS, + eisr & ~XILINX_DPDMA_EINTR_GLOBAL_ERR); + + for (i = 0; i < ARRAY_SIZE(xdev->chan); i++) + if (err || xilinx_dpdma_chan_err(xdev->chan[i], isr, eisr)) + tasklet_schedule(&xdev->chan[i]->err_task); +} + +/** + * xilinx_dpdma_enable_irq - Enable interrupts + * @xdev: DPDMA device + * + * Enable interrupts. + */ +static void xilinx_dpdma_enable_irq(struct xilinx_dpdma_device *xdev) +{ + dpdma_write(xdev->reg, XILINX_DPDMA_IEN, XILINX_DPDMA_INTR_ALL); + dpdma_write(xdev->reg, XILINX_DPDMA_EIEN, XILINX_DPDMA_EINTR_ALL); +} + +/** + * xilinx_dpdma_disable_irq - Disable interrupts + * @xdev: DPDMA device + * + * Disable interrupts. + */ +static void xilinx_dpdma_disable_irq(struct xilinx_dpdma_device *xdev) +{ + dpdma_write(xdev->reg, XILINX_DPDMA_IDS, XILINX_DPDMA_INTR_ERR_ALL); + dpdma_write(xdev->reg, XILINX_DPDMA_EIDS, XILINX_DPDMA_EINTR_ALL); +} + +/** + * xilinx_dpdma_chan_err_task - Per channel tasklet for error handling + * @data: tasklet data to be casted to DPDMA channel structure + * + * Per channel error handling tasklet. This function waits for the outstanding + * transaction to complete and triggers error handling. After error handling, + * re-enable channel error interrupts, and restart the channel if needed. + */ +static void xilinx_dpdma_chan_err_task(unsigned long data) +{ + struct xilinx_dpdma_chan *chan = (struct xilinx_dpdma_chan *)data; + struct xilinx_dpdma_device *xdev = chan->xdev; + unsigned long flags; + + /* Proceed error handling even when polling fails. */ + xilinx_dpdma_chan_poll_no_ostand(chan); + + xilinx_dpdma_chan_handle_err(chan); + + dpdma_write(xdev->reg, XILINX_DPDMA_IEN, + XILINX_DPDMA_INTR_CHAN_ERR_MASK << chan->id); + dpdma_write(xdev->reg, XILINX_DPDMA_EIEN, + XILINX_DPDMA_EINTR_CHAN_ERR_MASK << chan->id); + + spin_lock_irqsave(&chan->lock, flags); + xilinx_dpdma_chan_queue_transfer(chan); + spin_unlock_irqrestore(&chan->lock, flags); +} + +static irqreturn_t xilinx_dpdma_irq_handler(int irq, void *data) +{ + struct xilinx_dpdma_device *xdev = data; + unsigned long mask; + unsigned int i; + u32 status; + u32 error; + + status = dpdma_read(xdev->reg, XILINX_DPDMA_ISR); + error = dpdma_read(xdev->reg, XILINX_DPDMA_EISR); + if (!status && !error) + return IRQ_NONE; + + dpdma_write(xdev->reg, XILINX_DPDMA_ISR, status); + dpdma_write(xdev->reg, XILINX_DPDMA_EISR, error); + + if (status & XILINX_DPDMA_INTR_VSYNC) { + /* + * There's a single VSYNC interrupt that needs to be processed + * by each running channel to update the active descriptor. + */ + for (i = 0; i < ARRAY_SIZE(xdev->chan); i++) { + struct xilinx_dpdma_chan *chan = xdev->chan[i]; + + if (chan) + xilinx_dpdma_chan_vsync_irq(chan); + } + } + + mask = FIELD_GET(XILINX_DPDMA_INTR_DESC_DONE_MASK, status); + if (mask) { + for_each_set_bit(i, &mask, ARRAY_SIZE(xdev->chan)) + xilinx_dpdma_chan_done_irq(xdev->chan[i]); + } + + mask = FIELD_GET(XILINX_DPDMA_INTR_NO_OSTAND_MASK, status); + if (mask) { + for_each_set_bit(i, &mask, ARRAY_SIZE(xdev->chan)) + xilinx_dpdma_chan_notify_no_ostand(xdev->chan[i]); + } + + mask = status & XILINX_DPDMA_INTR_ERR_ALL; + if (mask || error) + xilinx_dpdma_handle_err_irq(xdev, mask, error); + + return IRQ_HANDLED; +} + +/* ----------------------------------------------------------------------------- + * Initialization & Cleanup + */ + +static int xilinx_dpdma_chan_init(struct xilinx_dpdma_device *xdev, + unsigned int chan_id) +{ + struct xilinx_dpdma_chan *chan; + + chan = devm_kzalloc(xdev->dev, sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + chan->id = chan_id; + chan->reg = xdev->reg + XILINX_DPDMA_CH_BASE + + XILINX_DPDMA_CH_OFFSET * chan->id; + chan->running = false; + chan->xdev = xdev; + + spin_lock_init(&chan->lock); + init_waitqueue_head(&chan->wait_to_stop); + + tasklet_init(&chan->err_task, xilinx_dpdma_chan_err_task, + (unsigned long)chan); + + chan->vchan.desc_free = xilinx_dpdma_chan_free_tx_desc; + vchan_init(&chan->vchan, &xdev->common); + + xdev->chan[chan->id] = chan; + + return 0; +} + +static void xilinx_dpdma_chan_remove(struct xilinx_dpdma_chan *chan) +{ + if (!chan) + return; + + tasklet_kill(&chan->err_task); + list_del(&chan->vchan.chan.device_node); +} + +static struct dma_chan *of_dma_xilinx_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct xilinx_dpdma_device *xdev = ofdma->of_dma_data; + uint32_t chan_id = dma_spec->args[0]; + + if (chan_id >= ARRAY_SIZE(xdev->chan)) + return NULL; + + if (!xdev->chan[chan_id]) + return NULL; + + return dma_get_slave_channel(&xdev->chan[chan_id]->vchan.chan); +} + +static int xilinx_dpdma_probe(struct platform_device *pdev) +{ + struct xilinx_dpdma_device *xdev; + struct dma_device *ddev; + unsigned int i; + int ret; + + xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL); + if (!xdev) + return -ENOMEM; + + xdev->dev = &pdev->dev; + xdev->ext_addr = sizeof(dma_addr_t) > 4; + + INIT_LIST_HEAD(&xdev->common.channels); + + platform_set_drvdata(pdev, xdev); + + xdev->axi_clk = devm_clk_get(xdev->dev, "axi_clk"); + if (IS_ERR(xdev->axi_clk)) + return PTR_ERR(xdev->axi_clk); + + xdev->reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(xdev->reg)) + return PTR_ERR(xdev->reg); + + xdev->irq = platform_get_irq(pdev, 0); + if (xdev->irq < 0) { + dev_err(xdev->dev, "failed to get platform irq\n"); + return xdev->irq; + } + + ret = request_irq(xdev->irq, xilinx_dpdma_irq_handler, IRQF_SHARED, + dev_name(xdev->dev), xdev); + if (ret) { + dev_err(xdev->dev, "failed to request IRQ\n"); + return ret; + } + + ddev = &xdev->common; + ddev->dev = &pdev->dev; + + dma_cap_set(DMA_SLAVE, ddev->cap_mask); + dma_cap_set(DMA_PRIVATE, ddev->cap_mask); + dma_cap_set(DMA_INTERLEAVE, ddev->cap_mask); + dma_cap_set(DMA_REPEAT, ddev->cap_mask); + dma_cap_set(DMA_LOAD_EOT, ddev->cap_mask); + ddev->copy_align = fls(XILINX_DPDMA_ALIGN_BYTES - 1); + + ddev->device_alloc_chan_resources = xilinx_dpdma_alloc_chan_resources; + ddev->device_free_chan_resources = xilinx_dpdma_free_chan_resources; + ddev->device_prep_interleaved_dma = xilinx_dpdma_prep_interleaved_dma; + /* TODO: Can we achieve better granularity ? */ + ddev->device_tx_status = dma_cookie_status; + ddev->device_issue_pending = xilinx_dpdma_issue_pending; + ddev->device_config = xilinx_dpdma_config; + ddev->device_pause = xilinx_dpdma_pause; + ddev->device_resume = xilinx_dpdma_resume; + ddev->device_terminate_all = xilinx_dpdma_terminate_all; + ddev->device_synchronize = xilinx_dpdma_synchronize; + ddev->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED); + ddev->directions = BIT(DMA_MEM_TO_DEV); + ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; + + for (i = 0; i < ARRAY_SIZE(xdev->chan); ++i) { + ret = xilinx_dpdma_chan_init(xdev, i); + if (ret < 0) { + dev_err(xdev->dev, "failed to initialize channel %u\n", + i); + goto error; + } + } + + ret = clk_prepare_enable(xdev->axi_clk); + if (ret) { + dev_err(xdev->dev, "failed to enable the axi clock\n"); + goto error; + } + + ret = dma_async_device_register(ddev); + if (ret) { + dev_err(xdev->dev, "failed to register the dma device\n"); + goto error_dma_async; + } + + ret = of_dma_controller_register(xdev->dev->of_node, + of_dma_xilinx_xlate, ddev); + if (ret) { + dev_err(xdev->dev, "failed to register DMA to DT DMA helper\n"); + goto error_of_dma; + } + + xilinx_dpdma_enable_irq(xdev); + + dev_info(&pdev->dev, "Xilinx DPDMA engine is probed\n"); + + return 0; + +error_of_dma: + dma_async_device_unregister(ddev); +error_dma_async: + clk_disable_unprepare(xdev->axi_clk); +error: + for (i = 0; i < ARRAY_SIZE(xdev->chan); i++) + xilinx_dpdma_chan_remove(xdev->chan[i]); + + free_irq(xdev->irq, xdev); + + return ret; +} + +static int xilinx_dpdma_remove(struct platform_device *pdev) +{ + struct xilinx_dpdma_device *xdev = platform_get_drvdata(pdev); + unsigned int i; + + /* Start by disabling the IRQ to avoid races during cleanup. */ + free_irq(xdev->irq, xdev); + + xilinx_dpdma_disable_irq(xdev); + of_dma_controller_free(pdev->dev.of_node); + dma_async_device_unregister(&xdev->common); + clk_disable_unprepare(xdev->axi_clk); + + for (i = 0; i < ARRAY_SIZE(xdev->chan); i++) + xilinx_dpdma_chan_remove(xdev->chan[i]); + + return 0; +} + +static const struct of_device_id xilinx_dpdma_of_match[] = { + { .compatible = "xlnx,zynqmp-dpdma",}, + { /* end of table */ }, +}; +MODULE_DEVICE_TABLE(of, xilinx_dpdma_of_match); + +static struct platform_driver xilinx_dpdma_driver = { + .probe = xilinx_dpdma_probe, + .remove = xilinx_dpdma_remove, + .driver = { + .name = "xilinx-zynqmp-dpdma", + .of_match_table = xilinx_dpdma_of_match, + }, +}; + +module_platform_driver(xilinx_dpdma_driver); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("Xilinx ZynqMP DPDMA driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index c4fd57d8b717..147d61b9674e 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -386,6 +386,8 @@ source "drivers/gpu/drm/mcde/Kconfig" source "drivers/gpu/drm/tidss/Kconfig" +source "drivers/gpu/drm/xlnx/Kconfig" + # Keep legacy drivers last menuconfig DRM_LEGACY diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 2c0e5a7e5953..2f31579f91d4 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -18,7 +18,7 @@ drm-y := drm_auth.o drm_cache.o \ drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \ drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o \ - drm_managed.o + drm_managed.o drm_vblank_work.o drm-$(CONFIG_DRM_LEGACY) += drm_legacy_misc.o drm_bufs.o drm_context.o drm_dma.o drm_scatter.o drm_lock.o drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o @@ -123,3 +123,4 @@ obj-$(CONFIG_DRM_PANFROST) += panfrost/ obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/ obj-$(CONFIG_DRM_MCDE) += mcde/ obj-$(CONFIG_DRM_TIDSS) += tidss/ +obj-y += xlnx/ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c index 43d8ed7dbd00..519ce4427fce 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c @@ -307,8 +307,8 @@ static struct sg_table *amdgpu_dma_buf_map(struct dma_buf_attachment *attach, if (IS_ERR(sgt)) return sgt; - if (!dma_map_sg_attrs(attach->dev, sgt->sgl, sgt->nents, dir, - DMA_ATTR_SKIP_CPU_SYNC)) + if (dma_map_sgtable(attach->dev, sgt, dir, + DMA_ATTR_SKIP_CPU_SYNC)) goto error_free; break; @@ -349,7 +349,7 @@ static void amdgpu_dma_buf_unmap(struct dma_buf_attachment *attach, struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); if (sgt->sgl->page_link) { - dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir); + dma_unmap_sgtable(attach->dev, sgt, dir, 0); sg_free_table(sgt); kfree(sgt); } else { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c index bc01a06546aa..77fae40197ab 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c @@ -150,60 +150,7 @@ static int amdgpu_gtt_mgr_fini(struct ttm_mem_type_manager *man) */ bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_mem_reg *mem) { - struct amdgpu_gtt_node *node = mem->mm_node; - - return (node->node.start != AMDGPU_BO_INVALID_OFFSET); -} - -/** - * amdgpu_gtt_mgr_alloc - allocate new ranges - * - * @man: TTM memory type manager - * @tbo: TTM BO we need this range for - * @place: placement flags and restrictions - * @mem: the resulting mem object - * - * Allocate the address space for a node. - */ -static int amdgpu_gtt_mgr_alloc(struct ttm_mem_type_manager *man, - struct ttm_buffer_object *tbo, - const struct ttm_place *place, - struct ttm_mem_reg *mem) -{ - struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev); - struct amdgpu_gtt_mgr *mgr = man->priv; - struct amdgpu_gtt_node *node = mem->mm_node; - enum drm_mm_insert_mode mode; - unsigned long fpfn, lpfn; - int r; - - if (amdgpu_gtt_mgr_has_gart_addr(mem)) - return 0; - - if (place) - fpfn = place->fpfn; - else - fpfn = 0; - - if (place && place->lpfn) - lpfn = place->lpfn; - else - lpfn = adev->gart.num_cpu_pages; - - mode = DRM_MM_INSERT_BEST; - if (place && place->flags & TTM_PL_FLAG_TOPDOWN) - mode = DRM_MM_INSERT_HIGH; - - spin_lock(&mgr->lock); - r = drm_mm_insert_node_in_range(&mgr->mm, &node->node, mem->num_pages, - mem->page_alignment, 0, fpfn, lpfn, - mode); - spin_unlock(&mgr->lock); - - if (!r) - mem->start = node->node.start; - - return r; + return mem->mm_node != NULL; } /** @@ -229,35 +176,42 @@ static int amdgpu_gtt_mgr_new(struct ttm_mem_type_manager *man, if ((&tbo->mem == mem || tbo->mem.mem_type != TTM_PL_TT) && atomic64_read(&mgr->available) < mem->num_pages) { spin_unlock(&mgr->lock); - return 0; + return -ENOSPC; } atomic64_sub(mem->num_pages, &mgr->available); spin_unlock(&mgr->lock); + if (!place->lpfn) { + mem->mm_node = NULL; + mem->start = AMDGPU_BO_INVALID_OFFSET; + return 0; + } + node = kzalloc(sizeof(*node), GFP_KERNEL); if (!node) { r = -ENOMEM; goto err_out; } - node->node.start = AMDGPU_BO_INVALID_OFFSET; - node->node.size = mem->num_pages; node->tbo = tbo; - mem->mm_node = node; - if (place->fpfn || place->lpfn || place->flags & TTM_PL_FLAG_TOPDOWN) { - r = amdgpu_gtt_mgr_alloc(man, tbo, place, mem); - if (unlikely(r)) { - kfree(node); - mem->mm_node = NULL; - r = 0; - goto err_out; - } - } else { - mem->start = node->node.start; - } + spin_lock(&mgr->lock); + r = drm_mm_insert_node_in_range(&mgr->mm, &node->node, mem->num_pages, + mem->page_alignment, 0, place->fpfn, + place->lpfn, DRM_MM_INSERT_BEST); + spin_unlock(&mgr->lock); + + if (unlikely(r)) + goto err_free; + + mem->mm_node = node; + mem->start = node->node.start; return 0; + +err_free: + kfree(node); + err_out: atomic64_add(mem->num_pages, &mgr->available); @@ -278,17 +232,14 @@ static void amdgpu_gtt_mgr_del(struct ttm_mem_type_manager *man, struct amdgpu_gtt_mgr *mgr = man->priv; struct amdgpu_gtt_node *node = mem->mm_node; - if (!node) - return; - - spin_lock(&mgr->lock); - if (node->node.start != AMDGPU_BO_INVALID_OFFSET) + if (node) { + spin_lock(&mgr->lock); drm_mm_remove_node(&node->node); - spin_unlock(&mgr->lock); - atomic64_add(mem->num_pages, &mgr->available); + spin_unlock(&mgr->lock); + kfree(node); + } - kfree(node); - mem->mm_node = NULL; + atomic64_add(mem->num_pages, &mgr->available); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 5f2f3faad792..fcff5671f6f8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -94,7 +94,7 @@ static int amdgpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, man->func = &amdgpu_gtt_mgr_func; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | TTM_MEMTYPE_FLAG_CMA; + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; break; case TTM_PL_VRAM: /* "On-card" video ram */ @@ -109,7 +109,7 @@ static int amdgpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, case AMDGPU_PL_OA: /* On-chip GDS memory*/ man->func = &ttm_bo_manager_func; - man->flags = TTM_MEMTYPE_FLAG_FIXED | TTM_MEMTYPE_FLAG_CMA; + man->flags = TTM_MEMTYPE_FLAG_FIXED; man->available_caching = TTM_PL_FLAG_UNCACHED; man->default_caching = TTM_PL_FLAG_UNCACHED; break; @@ -430,12 +430,22 @@ int amdgpu_ttm_copy_mem_to_mem(struct amdgpu_device *adev, } src_offset = src->offset; - src_mm = amdgpu_find_mm_node(src->mem, &src_offset); - src_node_size = (src_mm->size << PAGE_SHIFT) - src_offset; + if (src->mem->mm_node) { + src_mm = amdgpu_find_mm_node(src->mem, &src_offset); + src_node_size = (src_mm->size << PAGE_SHIFT) - src_offset; + } else { + src_mm = NULL; + src_node_size = ULLONG_MAX; + } dst_offset = dst->offset; - dst_mm = amdgpu_find_mm_node(dst->mem, &dst_offset); - dst_node_size = (dst_mm->size << PAGE_SHIFT) - dst_offset; + if (dst->mem->mm_node) { + dst_mm = amdgpu_find_mm_node(dst->mem, &dst_offset); + dst_node_size = (dst_mm->size << PAGE_SHIFT) - dst_offset; + } else { + dst_mm = NULL; + dst_node_size = ULLONG_MAX; + } mutex_lock(&adev->mman.gtt_window_lock); @@ -827,10 +837,6 @@ static int amdgpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_ return 0; } -static void amdgpu_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) -{ -} - static unsigned long amdgpu_ttm_io_mem_pfn(struct ttm_buffer_object *bo, unsigned long page_offset) { @@ -1044,7 +1050,6 @@ static int amdgpu_ttm_tt_pin_userptr(struct ttm_tt *ttm) { struct amdgpu_device *adev = amdgpu_ttm_adev(ttm->bdev); struct amdgpu_ttm_tt *gtt = (void *)ttm; - unsigned nents; int r; int write = !(gtt->userflags & AMDGPU_GEM_USERPTR_READONLY); @@ -1059,9 +1064,8 @@ static int amdgpu_ttm_tt_pin_userptr(struct ttm_tt *ttm) goto release_sg; /* Map SG to device */ - r = -ENOMEM; - nents = dma_map_sg(adev->dev, ttm->sg->sgl, ttm->sg->nents, direction); - if (nents == 0) + r = dma_map_sgtable(adev->dev, ttm->sg, direction, 0); + if (r) goto release_sg; /* convert SG to linear array of pages and dma addresses */ @@ -1092,8 +1096,7 @@ static void amdgpu_ttm_tt_unpin_userptr(struct ttm_tt *ttm) return; /* unmap the pages mapped to the device */ - dma_unmap_sg(adev->dev, ttm->sg->sgl, ttm->sg->nents, direction); - + dma_unmap_sgtable(adev->dev, ttm->sg, direction, 0); sg_free_table(ttm->sg); #if IS_ENABLED(CONFIG_DRM_AMDGPU_USERPTR) @@ -1748,7 +1751,6 @@ static struct ttm_bo_driver amdgpu_bo_driver = { .release_notify = &amdgpu_bo_release_notify, .fault_reserve_notify = &amdgpu_bo_fault_reserve_notify, .io_mem_reserve = &amdgpu_ttm_io_mem_reserve, - .io_mem_free = &amdgpu_ttm_io_mem_free, .io_mem_pfn = amdgpu_ttm_io_mem_pfn, .access_memory = &amdgpu_ttm_access_memory, .del_from_lru_notify = &amdgpu_vm_del_from_lru_notify diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c index c498804711d9..189d46ea603b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c @@ -144,7 +144,7 @@ static void amdgpu_vm_sdma_copy_ptes(struct amdgpu_vm_update_params *p, src += p->num_dw_left * 4; - pe += amdgpu_bo_gpu_offset_no_check(bo); + pe += amdgpu_gmc_sign_extend(amdgpu_bo_gpu_offset_no_check(bo)); trace_amdgpu_vm_copy_ptes(pe, src, count, p->immediate); amdgpu_vm_copy_pte(p->adev, ib, pe, src, count); @@ -171,7 +171,7 @@ static void amdgpu_vm_sdma_set_ptes(struct amdgpu_vm_update_params *p, { struct amdgpu_ib *ib = p->job->ibs; - pe += amdgpu_bo_gpu_offset_no_check(bo); + pe += amdgpu_gmc_sign_extend(amdgpu_bo_gpu_offset_no_check(bo)); trace_amdgpu_vm_set_ptes(pe, addr, count, incr, flags, p->immediate); if (count < 3) { amdgpu_vm_write_pte(p->adev, ib, pe, addr | flags, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index 97ad8ffe6c6c..134cc36e30c5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -319,8 +319,7 @@ static int amdgpu_vram_mgr_new(struct ttm_mem_type_manager *man, mem_bytes = (u64)mem->num_pages << PAGE_SHIFT; if (atomic64_add_return(mem_bytes, &mgr->usage) > max_bytes) { atomic64_sub(mem_bytes, &mgr->usage); - mem->mm_node = NULL; - return 0; + return -ENOSPC; } if (place->flags & TTM_PL_FLAG_CONTIGUOUS) { @@ -400,7 +399,7 @@ error: atomic64_sub(mem->num_pages << PAGE_SHIFT, &mgr->usage); kvfree(nodes); - return r == -ENOSPC ? 0 : r; + return r; } /** @@ -475,11 +474,11 @@ int amdgpu_vram_mgr_alloc_sgt(struct amdgpu_device *adev, if (r) goto error_free; - for_each_sg((*sgt)->sgl, sg, num_entries, i) + for_each_sgtable_sg((*sgt), sg, i) sg->length = 0; node = mem->mm_node; - for_each_sg((*sgt)->sgl, sg, num_entries, i) { + for_each_sgtable_sg((*sgt), sg, i) { phys_addr_t phys = (node->start << PAGE_SHIFT) + adev->gmc.aper_base; size_t size = node->size << PAGE_SHIFT; @@ -499,7 +498,7 @@ int amdgpu_vram_mgr_alloc_sgt(struct amdgpu_device *adev, return 0; error_unmap: - for_each_sg((*sgt)->sgl, sg, num_entries, i) { + for_each_sgtable_sg((*sgt), sg, i) { if (!sg->length) continue; @@ -530,7 +529,7 @@ void amdgpu_vram_mgr_free_sgt(struct amdgpu_device *adev, struct scatterlist *sg; int i; - for_each_sg(sgt->sgl, sg, sgt->nents, i) + for_each_sgtable_sg(sgt, sg, i) dma_unmap_resource(dev, sg->dma_address, sg->length, dir, DMA_ATTR_SKIP_CPU_SYNC); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 02e335a4468e..d8224156460e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -4629,9 +4629,7 @@ static void dm_crtc_reset_state(struct drm_crtc *crtc) if (WARN_ON(!state)) return; - crtc->state = &state->base; - crtc->state->crtc = crtc; - + __drm_atomic_helper_crtc_reset(crtc, &state->base); } static struct drm_crtc_state * diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c index 56bd938961ee..f33418d6e1a0 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c @@ -492,10 +492,8 @@ static void komeda_crtc_reset(struct drm_crtc *crtc) crtc->state = NULL; state = kzalloc(sizeof(*state), GFP_KERNEL); - if (state) { - crtc->state = &state->base; - crtc->state->crtc = crtc; - } + if (state) + __drm_atomic_helper_crtc_reset(crtc, &state->base); } static struct drm_crtc_state * @@ -616,7 +614,6 @@ static int komeda_crtc_add(struct komeda_kms_dev *kms, return err; drm_crtc_helper_add(crtc, &komeda_crtc_helper_funcs); - drm_crtc_vblank_reset(crtc); crtc->port = kcrtc->master->of_output_port; diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index a76aa3fb8d3c..69fee05c256c 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -848,7 +848,6 @@ static int malidp_bind(struct device *dev) drm->irq_enabled = true; ret = drm_vblank_init(drm, drm->mode_config.num_crtc); - drm_crtc_vblank_reset(&malidp->crtc); if (ret < 0) { DRM_ERROR("failed to initialise vblank\n"); goto vblank_fail; diff --git a/drivers/gpu/drm/ast/Makefile b/drivers/gpu/drm/ast/Makefile index 561f7c4199e4..2265a8a624dd 100644 --- a/drivers/gpu/drm/ast/Makefile +++ b/drivers/gpu/drm/ast/Makefile @@ -3,6 +3,7 @@ # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. -ast-y := ast_drv.o ast_main.o ast_mode.o ast_ttm.o ast_post.o ast_dp501.o +ast-y := ast_cursor.o ast_drv.o ast_main.o ast_mm.o ast_mode.o ast_post.o \ + ast_dp501.o obj-$(CONFIG_DRM_AST) := ast.o diff --git a/drivers/gpu/drm/ast/ast_cursor.c b/drivers/gpu/drm/ast/ast_cursor.c new file mode 100644 index 000000000000..acf0d23514e8 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_cursor.c @@ -0,0 +1,289 @@ +/* + * Copyright 2012 Red Hat Inc. + * Parts based on xf86-video-ast + * Copyright (c) 2005 ASPEED Technology Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ + +#include <drm/drm_gem_vram_helper.h> +#include <drm/drm_managed.h> + +#include "ast_drv.h" + +static void ast_cursor_fini(struct ast_private *ast) +{ + size_t i; + struct drm_gem_vram_object *gbo; + + for (i = 0; i < ARRAY_SIZE(ast->cursor.gbo); ++i) { + gbo = ast->cursor.gbo[i]; + drm_gem_vram_vunmap(gbo, ast->cursor.vaddr[i]); + drm_gem_vram_unpin(gbo); + drm_gem_vram_put(gbo); + } +} + +static void ast_cursor_release(struct drm_device *dev, void *ptr) +{ + struct ast_private *ast = dev->dev_private; + + ast_cursor_fini(ast); +} + +/* + * Allocate cursor BOs and pins them at the end of VRAM. + */ +int ast_cursor_init(struct ast_private *ast) +{ + struct drm_device *dev = ast->dev; + size_t size, i; + struct drm_gem_vram_object *gbo; + void __iomem *vaddr; + int ret; + + size = roundup(AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE, PAGE_SIZE); + + for (i = 0; i < ARRAY_SIZE(ast->cursor.gbo); ++i) { + gbo = drm_gem_vram_create(dev, size, 0); + if (IS_ERR(gbo)) { + ret = PTR_ERR(gbo); + goto err_drm_gem_vram_put; + } + ret = drm_gem_vram_pin(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM | + DRM_GEM_VRAM_PL_FLAG_TOPDOWN); + if (ret) { + drm_gem_vram_put(gbo); + goto err_drm_gem_vram_put; + } + vaddr = drm_gem_vram_vmap(gbo); + if (IS_ERR(vaddr)) { + ret = PTR_ERR(vaddr); + drm_gem_vram_unpin(gbo); + drm_gem_vram_put(gbo); + goto err_drm_gem_vram_put; + } + + ast->cursor.gbo[i] = gbo; + ast->cursor.vaddr[i] = vaddr; + } + + return drmm_add_action_or_reset(dev, ast_cursor_release, NULL); + +err_drm_gem_vram_put: + while (i) { + --i; + gbo = ast->cursor.gbo[i]; + drm_gem_vram_vunmap(gbo, ast->cursor.vaddr[i]); + drm_gem_vram_unpin(gbo); + drm_gem_vram_put(gbo); + } + return ret; +} + +static void update_cursor_image(u8 __iomem *dst, const u8 *src, int width, int height) +{ + union { + u32 ul; + u8 b[4]; + } srcdata32[2], data32; + union { + u16 us; + u8 b[2]; + } data16; + u32 csum = 0; + s32 alpha_dst_delta, last_alpha_dst_delta; + u8 __iomem *dstxor; + const u8 *srcxor; + int i, j; + u32 per_pixel_copy, two_pixel_copy; + + alpha_dst_delta = AST_MAX_HWC_WIDTH << 1; + last_alpha_dst_delta = alpha_dst_delta - (width << 1); + + srcxor = src; + dstxor = (u8 *)dst + last_alpha_dst_delta + (AST_MAX_HWC_HEIGHT - height) * alpha_dst_delta; + per_pixel_copy = width & 1; + two_pixel_copy = width >> 1; + + for (j = 0; j < height; j++) { + for (i = 0; i < two_pixel_copy; i++) { + srcdata32[0].ul = *((u32 *)srcxor) & 0xf0f0f0f0; + srcdata32[1].ul = *((u32 *)(srcxor + 4)) & 0xf0f0f0f0; + data32.b[0] = srcdata32[0].b[1] | (srcdata32[0].b[0] >> 4); + data32.b[1] = srcdata32[0].b[3] | (srcdata32[0].b[2] >> 4); + data32.b[2] = srcdata32[1].b[1] | (srcdata32[1].b[0] >> 4); + data32.b[3] = srcdata32[1].b[3] | (srcdata32[1].b[2] >> 4); + + writel(data32.ul, dstxor); + csum += data32.ul; + + dstxor += 4; + srcxor += 8; + + } + + for (i = 0; i < per_pixel_copy; i++) { + srcdata32[0].ul = *((u32 *)srcxor) & 0xf0f0f0f0; + data16.b[0] = srcdata32[0].b[1] | (srcdata32[0].b[0] >> 4); + data16.b[1] = srcdata32[0].b[3] | (srcdata32[0].b[2] >> 4); + writew(data16.us, dstxor); + csum += (u32)data16.us; + + dstxor += 2; + srcxor += 4; + } + dstxor += last_alpha_dst_delta; + } + + /* write checksum + signature */ + dst += AST_HWC_SIZE; + writel(csum, dst); + writel(width, dst + AST_HWC_SIGNATURE_SizeX); + writel(height, dst + AST_HWC_SIGNATURE_SizeY); + writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTX); + writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTY); +} + +int ast_cursor_blit(struct ast_private *ast, struct drm_framebuffer *fb) +{ + struct drm_device *dev = ast->dev; + struct drm_gem_vram_object *gbo; + int ret; + void *src; + void __iomem *dst; + + if (drm_WARN_ON_ONCE(dev, fb->width > AST_MAX_HWC_WIDTH) || + drm_WARN_ON_ONCE(dev, fb->height > AST_MAX_HWC_HEIGHT)) + return -EINVAL; + + gbo = drm_gem_vram_of_gem(fb->obj[0]); + + ret = drm_gem_vram_pin(gbo, 0); + if (ret) + return ret; + src = drm_gem_vram_vmap(gbo); + if (IS_ERR(src)) { + ret = PTR_ERR(src); + goto err_drm_gem_vram_unpin; + } + + dst = ast->cursor.vaddr[ast->cursor.next_index]; + + /* do data transfer to cursor BO */ + update_cursor_image(dst, src, fb->width, fb->height); + + drm_gem_vram_vunmap(gbo, src); + drm_gem_vram_unpin(gbo); + + return 0; + +err_drm_gem_vram_unpin: + drm_gem_vram_unpin(gbo); + return ret; +} + +static void ast_cursor_set_base(struct ast_private *ast, u64 address) +{ + u8 addr0 = (address >> 3) & 0xff; + u8 addr1 = (address >> 11) & 0xff; + u8 addr2 = (address >> 19) & 0xff; + + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc8, addr0); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc9, addr1); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xca, addr2); +} + +void ast_cursor_page_flip(struct ast_private *ast) +{ + struct drm_device *dev = ast->dev; + struct drm_gem_vram_object *gbo; + s64 off; + + gbo = ast->cursor.gbo[ast->cursor.next_index]; + + off = drm_gem_vram_offset(gbo); + if (drm_WARN_ON_ONCE(dev, off < 0)) + return; /* Bug: we didn't pin the cursor HW BO to VRAM. */ + + ast_cursor_set_base(ast, off); + + ++ast->cursor.next_index; + ast->cursor.next_index %= ARRAY_SIZE(ast->cursor.gbo); +} + +static void ast_cursor_set_location(struct ast_private *ast, u16 x, u16 y, + u8 x_offset, u8 y_offset) +{ + u8 x0 = (x & 0x00ff); + u8 x1 = (x & 0x0f00) >> 8; + u8 y0 = (y & 0x00ff); + u8 y1 = (y & 0x0700) >> 8; + + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc2, x_offset); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc3, y_offset); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc4, x0); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc5, x1); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc6, y0); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc7, y1); +} + +void ast_cursor_show(struct ast_private *ast, int x, int y, + unsigned int offset_x, unsigned int offset_y) +{ + u8 x_offset, y_offset; + u8 __iomem *dst, __iomem *sig; + u8 jreg; + + dst = ast->cursor.vaddr[ast->cursor.next_index]; + + sig = dst + AST_HWC_SIZE; + writel(x, sig + AST_HWC_SIGNATURE_X); + writel(y, sig + AST_HWC_SIGNATURE_Y); + + if (x < 0) { + x_offset = (-x) + offset_x; + x = 0; + } else { + x_offset = offset_x; + } + if (y < 0) { + y_offset = (-y) + offset_y; + y = 0; + } else { + y_offset = offset_y; + } + + ast_cursor_set_location(ast, x, y, x_offset, y_offset); + + /* dummy write to fire HWC */ + jreg = 0x02 | + 0x01; /* enable ARGB4444 cursor */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, jreg); +} + +void ast_cursor_hide(struct ast_private *ast) +{ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, 0x00); +} diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index c44c1376c697..e3a264ac7ee2 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -110,12 +110,12 @@ struct ast_private { uint32_t dram_bus_width; uint32_t dram_type; uint32_t mclk; - uint32_t vram_size; int fb_mtrr; struct { struct drm_gem_vram_object *gbo[AST_DEFAULT_HWC_NUM]; + void __iomem *vaddr[AST_DEFAULT_HWC_NUM]; unsigned int next_index; } cursor; @@ -237,12 +237,6 @@ struct ast_connector { struct ast_i2c_chan *i2c; }; -struct ast_crtc { - struct drm_crtc base; - u8 offset_x, offset_y; -}; - -#define to_ast_crtc(x) container_of(x, struct ast_crtc, base) #define to_ast_connector(x) container_of(x, struct ast_connector, base) struct ast_vbios_stdtable { @@ -291,14 +285,12 @@ struct ast_crtc_state { #define to_ast_crtc_state(state) container_of(state, struct ast_crtc_state, base) -extern int ast_mode_init(struct drm_device *dev); -extern void ast_mode_fini(struct drm_device *dev); +int ast_mode_config_init(struct ast_private *ast); #define AST_MM_ALIGN_SHIFT 4 #define AST_MM_ALIGN_MASK ((1 << AST_MM_ALIGN_SHIFT) - 1) int ast_mm_init(struct ast_private *ast); -void ast_mm_fini(struct ast_private *ast); /* ast post */ void ast_enable_vga(struct drm_device *dev); @@ -314,4 +306,13 @@ bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata); u8 ast_get_dp501_max_clk(struct drm_device *dev); void ast_init_3rdtx(struct drm_device *dev); void ast_release_firmware(struct drm_device *dev); + +/* ast_cursor.c */ +int ast_cursor_init(struct ast_private *ast); +int ast_cursor_blit(struct ast_private *ast, struct drm_framebuffer *fb); +void ast_cursor_page_flip(struct ast_private *ast); +void ast_cursor_show(struct ast_private *ast, int x, int y, + unsigned int offset_x, unsigned int offset_y); +void ast_cursor_hide(struct ast_private *ast); + #endif diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index 2eab19a9056f..dd12b55d57a2 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -31,7 +31,6 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_gem.h> -#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_gem_vram_helper.h> #include "ast_drv.h" @@ -379,45 +378,6 @@ static int ast_get_dram_info(struct drm_device *dev) return 0; } -static const struct drm_mode_config_funcs ast_mode_funcs = { - .fb_create = drm_gem_fb_create, - .mode_valid = drm_vram_helper_mode_valid, - .atomic_check = drm_atomic_helper_check, - .atomic_commit = drm_atomic_helper_commit, -}; - -static u32 ast_get_vram_info(struct drm_device *dev) -{ - struct ast_private *ast = to_ast_private(dev); - u8 jreg; - u32 vram_size; - ast_open_key(ast); - - vram_size = AST_VIDMEM_DEFAULT_SIZE; - jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff); - switch (jreg & 3) { - case 0: vram_size = AST_VIDMEM_SIZE_8M; break; - case 1: vram_size = AST_VIDMEM_SIZE_16M; break; - case 2: vram_size = AST_VIDMEM_SIZE_32M; break; - case 3: vram_size = AST_VIDMEM_SIZE_64M; break; - } - - jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xff); - switch (jreg & 0x03) { - case 1: - vram_size -= 0x100000; - break; - case 2: - vram_size -= 0x200000; - break; - case 3: - vram_size -= 0x400000; - break; - } - - return vram_size; -} - int ast_driver_load(struct drm_device *dev, unsigned long flags) { struct ast_private *ast; @@ -458,48 +418,23 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags) ast_detect_chip(dev, &need_post); - if (need_post) - ast_post_gpu(dev); - ret = ast_get_dram_info(dev); if (ret) goto out_free; - ast->vram_size = ast_get_vram_info(dev); - drm_info(dev, "dram MCLK=%u Mhz type=%d bus_width=%d size=%08x\n", - ast->mclk, ast->dram_type, - ast->dram_bus_width, ast->vram_size); + drm_info(dev, "dram MCLK=%u Mhz type=%d bus_width=%d\n", + ast->mclk, ast->dram_type, ast->dram_bus_width); + + if (need_post) + ast_post_gpu(dev); ret = ast_mm_init(ast); if (ret) goto out_free; - drm_mode_config_init(dev); - - dev->mode_config.funcs = (void *)&ast_mode_funcs; - dev->mode_config.min_width = 0; - dev->mode_config.min_height = 0; - dev->mode_config.preferred_depth = 24; - dev->mode_config.prefer_shadow = 1; - dev->mode_config.fb_base = pci_resource_start(ast->dev->pdev, 0); - - if (ast->chip == AST2100 || - ast->chip == AST2200 || - ast->chip == AST2300 || - ast->chip == AST2400 || - ast->chip == AST2500) { - dev->mode_config.max_width = 1920; - dev->mode_config.max_height = 2048; - } else { - dev->mode_config.max_width = 1600; - dev->mode_config.max_height = 1200; - } - - ret = ast_mode_init(dev); + ret = ast_mode_config_init(ast); if (ret) goto out_free; - drm_mode_config_reset(dev); - return 0; out_free: kfree(ast); @@ -516,9 +451,6 @@ void ast_driver_unload(struct drm_device *dev) ast_release_firmware(dev); kfree(ast->dp501_fw_addr); - ast_mode_fini(dev); - drm_mode_config_cleanup(dev); - ast_mm_fini(ast); kfree(ast); } diff --git a/drivers/gpu/drm/ast/ast_ttm.c b/drivers/gpu/drm/ast/ast_mm.c index 9c3788a4c1c5..9186ec3ebbe0 100644 --- a/drivers/gpu/drm/ast/ast_ttm.c +++ b/drivers/gpu/drm/ast/ast_mm.c @@ -28,22 +28,72 @@ #include <linux/pci.h> -#include <drm/drm_print.h> #include <drm/drm_gem_vram_helper.h> +#include <drm/drm_managed.h> +#include <drm/drm_print.h> #include "ast_drv.h" +static u32 ast_get_vram_size(struct ast_private *ast) +{ + u8 jreg; + u32 vram_size; + + ast_open_key(ast); + + vram_size = AST_VIDMEM_DEFAULT_SIZE; + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff); + switch (jreg & 3) { + case 0: + vram_size = AST_VIDMEM_SIZE_8M; + break; + case 1: + vram_size = AST_VIDMEM_SIZE_16M; + break; + case 2: + vram_size = AST_VIDMEM_SIZE_32M; + break; + case 3: + vram_size = AST_VIDMEM_SIZE_64M; + break; + } + + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xff); + switch (jreg & 0x03) { + case 1: + vram_size -= 0x100000; + break; + case 2: + vram_size -= 0x200000; + break; + case 3: + vram_size -= 0x400000; + break; + } + + return vram_size; +} + +static void ast_mm_release(struct drm_device *dev, void *ptr) +{ + struct ast_private *ast = to_ast_private(dev); + + arch_phys_wc_del(ast->fb_mtrr); + arch_io_free_memtype_wc(pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0)); +} + int ast_mm_init(struct ast_private *ast) { - struct drm_vram_mm *vmm; + u32 vram_size; int ret; struct drm_device *dev = ast->dev; - vmm = drm_vram_helper_alloc_mm( - dev, pci_resource_start(dev->pdev, 0), - ast->vram_size); - if (IS_ERR(vmm)) { - ret = PTR_ERR(vmm); + vram_size = ast_get_vram_size(ast); + + ret = drmm_vram_helper_init(dev, pci_resource_start(dev->pdev, 0), + vram_size); + if (ret) { drm_err(dev, "Error initializing VRAM MM; %d\n", ret); return ret; } @@ -53,16 +103,5 @@ int ast_mm_init(struct ast_private *ast) ast->fb_mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 0), pci_resource_len(dev->pdev, 0)); - return 0; -} - -void ast_mm_fini(struct ast_private *ast) -{ - struct drm_device *dev = ast->dev; - - drm_vram_helper_release_mm(dev); - - arch_phys_wc_del(ast->fb_mtrr); - arch_io_free_memtype_wc(pci_resource_start(dev->pdev, 0), - pci_resource_len(dev->pdev, 0)); + return drmm_add_action_or_reset(dev, ast_mm_release, NULL); } diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 510ffb497344..154cd877d9d1 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -37,6 +37,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fourcc.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_gem_vram_helper.h> #include <drm/drm_plane_helper.h> #include <drm/drm_probe_helper.h> @@ -47,16 +48,6 @@ static struct ast_i2c_chan *ast_i2c_create(struct drm_device *dev); static void ast_i2c_destroy(struct ast_i2c_chan *i2c); -static int ast_cursor_move(struct drm_crtc *crtc, - int x, int y); - - -static u32 copy_cursor_image(u8 *src, u8 *dst, int width, int height); -static int ast_cursor_update(void *dst, void *src, unsigned int width, - unsigned int height); -static void ast_cursor_set_base(struct ast_private *ast, u64 address); -static int ast_cursor_move(struct drm_crtc *crtc, - int x, int y); static inline void ast_load_palette_index(struct ast_private *ast, u8 index, u8 red, u8 green, @@ -621,56 +612,21 @@ static int ast_cursor_plane_helper_prepare_fb(struct drm_plane *plane, struct drm_plane_state *new_state) { - struct drm_device *dev = plane->dev; struct drm_framebuffer *fb = new_state->fb; struct drm_crtc *crtc = new_state->crtc; - struct drm_gem_vram_object *gbo; struct ast_private *ast; int ret; - void *src, *dst; if (!crtc || !fb) return 0; - if (drm_WARN_ON_ONCE(dev, fb->width > AST_MAX_HWC_WIDTH) || - drm_WARN_ON_ONCE(dev, fb->height > AST_MAX_HWC_HEIGHT)) - return -EINVAL; /* BUG: didn't test in atomic_check() */ - - ast = to_ast_private(dev); - - gbo = drm_gem_vram_of_gem(fb->obj[0]); - src = drm_gem_vram_vmap(gbo); - if (IS_ERR(src)) { - ret = PTR_ERR(src); - goto err_drm_gem_vram_unpin; - } - - dst = drm_gem_vram_vmap(ast->cursor.gbo[ast->cursor.next_index]); - if (IS_ERR(dst)) { - ret = PTR_ERR(dst); - goto err_drm_gem_vram_vunmap_src; - } + ast = to_ast_private(plane->dev); - ret = ast_cursor_update(dst, src, fb->width, fb->height); + ret = ast_cursor_blit(ast, fb); if (ret) - goto err_drm_gem_vram_vunmap_dst; - - /* Always unmap buffers here. Destination buffers are - * perma-pinned while the driver is active. We're only - * changing ref-counters here. - */ - drm_gem_vram_vunmap(ast->cursor.gbo[ast->cursor.next_index], dst); - drm_gem_vram_vunmap(gbo, src); + return ret; return 0; - -err_drm_gem_vram_vunmap_dst: - drm_gem_vram_vunmap(ast->cursor.gbo[ast->cursor.next_index], dst); -err_drm_gem_vram_vunmap_src: - drm_gem_vram_vunmap(gbo, src); -err_drm_gem_vram_unpin: - drm_gem_vram_unpin(gbo); - return ret; } static int ast_cursor_plane_helper_atomic_check(struct drm_plane *plane, @@ -705,37 +661,21 @@ static void ast_cursor_plane_helper_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { - struct drm_device *dev = plane->dev; struct drm_plane_state *state = plane->state; - struct drm_crtc *crtc = state->crtc; struct drm_framebuffer *fb = state->fb; - struct ast_private *ast = to_ast_private(plane->dev); - struct ast_crtc *ast_crtc = to_ast_crtc(crtc); - struct drm_gem_vram_object *gbo; - s64 off; - u8 jreg; + struct ast_private *ast = plane->dev->dev_private; + unsigned int offset_x, offset_y; - ast_crtc->offset_x = AST_MAX_HWC_WIDTH - fb->width; - ast_crtc->offset_y = AST_MAX_HWC_WIDTH - fb->height; + offset_x = AST_MAX_HWC_WIDTH - fb->width; + offset_y = AST_MAX_HWC_WIDTH - fb->height; if (state->fb != old_state->fb) { /* A new cursor image was installed. */ - gbo = ast->cursor.gbo[ast->cursor.next_index]; - off = drm_gem_vram_offset(gbo); - if (drm_WARN_ON_ONCE(dev, off < 0)) - return; /* Bug: we didn't pin cursor HW BO to VRAM. */ - ast_cursor_set_base(ast, off); - - ++ast->cursor.next_index; - ast->cursor.next_index %= ARRAY_SIZE(ast->cursor.gbo); + ast_cursor_page_flip(ast); } - ast_cursor_move(crtc, state->crtc_x, state->crtc_y); - - jreg = 0x2; - /* enable ARGB cursor */ - jreg |= 1; - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, jreg); + ast_cursor_show(ast, state->crtc_x, state->crtc_y, + offset_x, offset_y); } static void @@ -744,7 +684,7 @@ ast_cursor_plane_helper_atomic_disable(struct drm_plane *plane, { struct ast_private *ast = to_ast_private(plane->dev); - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, 0x00); + ast_cursor_hide(ast); } static const struct drm_plane_helper_funcs ast_cursor_plane_helper_funcs = { @@ -942,21 +882,22 @@ static const struct drm_crtc_funcs ast_crtc_funcs = { static int ast_crtc_init(struct drm_device *dev) { struct ast_private *ast = to_ast_private(dev); - struct ast_crtc *crtc; + struct drm_crtc *crtc; int ret; - crtc = kzalloc(sizeof(struct ast_crtc), GFP_KERNEL); + crtc = kzalloc(sizeof(*crtc), GFP_KERNEL); if (!crtc) return -ENOMEM; - ret = drm_crtc_init_with_planes(dev, &crtc->base, &ast->primary_plane, + ret = drm_crtc_init_with_planes(dev, crtc, &ast->primary_plane, &ast->cursor_plane, &ast_crtc_funcs, NULL); if (ret) goto err_kfree; - drm_mode_crtc_set_gamma_size(&crtc->base, 256); - drm_crtc_helper_add(&crtc->base, &ast_crtc_helper_funcs); + drm_mode_crtc_set_gamma_size(crtc, 256); + drm_crtc_helper_add(crtc, &ast_crtc_helper_funcs); + return 0; err_kfree: @@ -1129,62 +1070,48 @@ static int ast_connector_init(struct drm_device *dev) return 0; } -/* allocate cursor cache and pin at start of VRAM */ -static int ast_cursor_init(struct drm_device *dev) -{ - struct ast_private *ast = to_ast_private(dev); - size_t size, i; - struct drm_gem_vram_object *gbo; - int ret; - - size = roundup(AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE, PAGE_SIZE); - - for (i = 0; i < ARRAY_SIZE(ast->cursor.gbo); ++i) { - gbo = drm_gem_vram_create(dev, size, 0); - if (IS_ERR(gbo)) { - ret = PTR_ERR(gbo); - goto err_drm_gem_vram_put; - } - ret = drm_gem_vram_pin(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM | - DRM_GEM_VRAM_PL_FLAG_TOPDOWN); - if (ret) { - drm_gem_vram_put(gbo); - goto err_drm_gem_vram_put; - } +/* + * Mode config + */ - ast->cursor.gbo[i] = gbo; - } +static const struct drm_mode_config_funcs ast_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .mode_valid = drm_vram_helper_mode_valid, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; - return 0; +int ast_mode_config_init(struct ast_private *ast) +{ + struct drm_device *dev = ast->dev; + int ret; -err_drm_gem_vram_put: - while (i) { - --i; - gbo = ast->cursor.gbo[i]; - drm_gem_vram_unpin(gbo); - drm_gem_vram_put(gbo); - ast->cursor.gbo[i] = NULL; - } - return ret; -} + ret = ast_cursor_init(ast); + if (ret) + return ret; -static void ast_cursor_fini(struct drm_device *dev) -{ - struct ast_private *ast = to_ast_private(dev); - size_t i; - struct drm_gem_vram_object *gbo; + ret = drmm_mode_config_init(dev); + if (ret) + return ret; - for (i = 0; i < ARRAY_SIZE(ast->cursor.gbo); ++i) { - gbo = ast->cursor.gbo[i]; - drm_gem_vram_unpin(gbo); - drm_gem_vram_put(gbo); + dev->mode_config.funcs = &ast_mode_config_funcs; + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.preferred_depth = 24; + dev->mode_config.prefer_shadow = 1; + dev->mode_config.fb_base = pci_resource_start(ast->dev->pdev, 0); + + if (ast->chip == AST2100 || + ast->chip == AST2200 || + ast->chip == AST2300 || + ast->chip == AST2400 || + ast->chip == AST2500) { + dev->mode_config.max_width = 1920; + dev->mode_config.max_height = 2048; + } else { + dev->mode_config.max_width = 1600; + dev->mode_config.max_height = 1200; } -} - -int ast_mode_init(struct drm_device *dev) -{ - struct ast_private *ast = to_ast_private(dev); - int ret; memset(&ast->primary_plane, 0, sizeof(ast->primary_plane)); ret = drm_universal_plane_init(dev, &ast->primary_plane, 0x01, @@ -1211,17 +1138,13 @@ int ast_mode_init(struct drm_device *dev) drm_plane_helper_add(&ast->cursor_plane, &ast_cursor_plane_helper_funcs); - ast_cursor_init(dev); ast_crtc_init(dev); ast_encoder_init(dev); ast_connector_init(dev); - return 0; -} + drm_mode_config_reset(dev); -void ast_mode_fini(struct drm_device *dev) -{ - ast_cursor_fini(dev); + return 0; } static int get_clock(void *i2c_priv) @@ -1344,136 +1267,3 @@ static void ast_i2c_destroy(struct ast_i2c_chan *i2c) i2c_del_adapter(&i2c->adapter); kfree(i2c); } - -static u32 copy_cursor_image(u8 *src, u8 *dst, int width, int height) -{ - union { - u32 ul; - u8 b[4]; - } srcdata32[2], data32; - union { - u16 us; - u8 b[2]; - } data16; - u32 csum = 0; - s32 alpha_dst_delta, last_alpha_dst_delta; - u8 *srcxor, *dstxor; - int i, j; - u32 per_pixel_copy, two_pixel_copy; - - alpha_dst_delta = AST_MAX_HWC_WIDTH << 1; - last_alpha_dst_delta = alpha_dst_delta - (width << 1); - - srcxor = src; - dstxor = (u8 *)dst + last_alpha_dst_delta + (AST_MAX_HWC_HEIGHT - height) * alpha_dst_delta; - per_pixel_copy = width & 1; - two_pixel_copy = width >> 1; - - for (j = 0; j < height; j++) { - for (i = 0; i < two_pixel_copy; i++) { - srcdata32[0].ul = *((u32 *)srcxor) & 0xf0f0f0f0; - srcdata32[1].ul = *((u32 *)(srcxor + 4)) & 0xf0f0f0f0; - data32.b[0] = srcdata32[0].b[1] | (srcdata32[0].b[0] >> 4); - data32.b[1] = srcdata32[0].b[3] | (srcdata32[0].b[2] >> 4); - data32.b[2] = srcdata32[1].b[1] | (srcdata32[1].b[0] >> 4); - data32.b[3] = srcdata32[1].b[3] | (srcdata32[1].b[2] >> 4); - - writel(data32.ul, dstxor); - csum += data32.ul; - - dstxor += 4; - srcxor += 8; - - } - - for (i = 0; i < per_pixel_copy; i++) { - srcdata32[0].ul = *((u32 *)srcxor) & 0xf0f0f0f0; - data16.b[0] = srcdata32[0].b[1] | (srcdata32[0].b[0] >> 4); - data16.b[1] = srcdata32[0].b[3] | (srcdata32[0].b[2] >> 4); - writew(data16.us, dstxor); - csum += (u32)data16.us; - - dstxor += 2; - srcxor += 4; - } - dstxor += last_alpha_dst_delta; - } - return csum; -} - -static int ast_cursor_update(void *dst, void *src, unsigned int width, - unsigned int height) -{ - u32 csum; - - /* do data transfer to cursor cache */ - csum = copy_cursor_image(src, dst, width, height); - - /* write checksum + signature */ - dst += AST_HWC_SIZE; - writel(csum, dst); - writel(width, dst + AST_HWC_SIGNATURE_SizeX); - writel(height, dst + AST_HWC_SIGNATURE_SizeY); - writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTX); - writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTY); - - return 0; -} - -static void ast_cursor_set_base(struct ast_private *ast, u64 address) -{ - u8 addr0 = (address >> 3) & 0xff; - u8 addr1 = (address >> 11) & 0xff; - u8 addr2 = (address >> 19) & 0xff; - - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc8, addr0); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc9, addr1); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xca, addr2); -} - -static int ast_cursor_move(struct drm_crtc *crtc, - int x, int y) -{ - struct ast_crtc *ast_crtc = to_ast_crtc(crtc); - struct ast_private *ast = to_ast_private(crtc->dev); - struct drm_gem_vram_object *gbo; - int x_offset, y_offset; - u8 *dst, *sig; - u8 jreg; - - gbo = ast->cursor.gbo[ast->cursor.next_index]; - dst = drm_gem_vram_vmap(gbo); - if (IS_ERR(dst)) - return PTR_ERR(dst); - - sig = dst + AST_HWC_SIZE; - writel(x, sig + AST_HWC_SIGNATURE_X); - writel(y, sig + AST_HWC_SIGNATURE_Y); - - x_offset = ast_crtc->offset_x; - y_offset = ast_crtc->offset_y; - if (x < 0) { - x_offset = (-x) + ast_crtc->offset_x; - x = 0; - } - - if (y < 0) { - y_offset = (-y) + ast_crtc->offset_y; - y = 0; - } - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc2, x_offset); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc3, y_offset); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc4, (x & 0xff)); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc5, ((x >> 8) & 0x0f)); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc6, (y & 0xff)); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc7, ((y >> 8) & 0x07)); - - /* dummy write to fire HWC */ - jreg = 0x02 | - 0x01; /* enable ARGB4444 cursor */ - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, jreg); - - drm_gem_vram_vunmap(gbo, dst); - - return 0; -} diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index 10985134ce0b..ce246b96330b 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -411,10 +411,8 @@ static void atmel_hlcdc_crtc_reset(struct drm_crtc *crtc) } state = kzalloc(sizeof(*state), GFP_KERNEL); - if (state) { - crtc->state = &state->base; - crtc->state->crtc = crtc; - } + if (state) + __drm_atomic_helper_crtc_reset(crtc, &state->base); } static struct drm_crtc_state * @@ -528,7 +526,6 @@ int atmel_hlcdc_crtc_create(struct drm_device *dev) } drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs); - drm_crtc_vblank_reset(&crtc->base); drm_mode_crtc_set_gamma_size(&crtc->base, ATMEL_HLCDC_CLUT_SIZE); drm_crtc_enable_color_mgmt(&crtc->base, 0, false, diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 7c0c93c7e61f..95f3d8cfe9ec 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -178,7 +178,7 @@ static void sii8620_read_buf(struct sii8620 *ctx, u16 addr, u8 *buf, int len) static u8 sii8620_readb(struct sii8620 *ctx, u16 addr) { - u8 ret; + u8 ret = 0; sii8620_read_buf(ctx, addr, &ret, 1); return ret; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 6148a022569a..748df1cacd2b 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -3179,9 +3179,11 @@ static void dw_hdmi_init_hw(struct dw_hdmi *hdmi) hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); } -static struct dw_hdmi * -__dw_hdmi_probe(struct platform_device *pdev, - const struct dw_hdmi_plat_data *plat_data) +/* ----------------------------------------------------------------------------- + * Probe/remove API, used from platforms based on the DRM bridge API. + */ +struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, + const struct dw_hdmi_plat_data *plat_data) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; @@ -3438,14 +3440,11 @@ __dw_hdmi_probe(struct platform_device *pdev, hdmi->cec = platform_device_register_full(&pdevinfo); } + drm_bridge_add(&hdmi->bridge); + return hdmi; err_iahb: - if (hdmi->i2c) { - i2c_del_adapter(&hdmi->i2c->adap); - hdmi->ddc = NULL; - } - clk_disable_unprepare(hdmi->iahb_clk); if (hdmi->cec_clk) clk_disable_unprepare(hdmi->cec_clk); @@ -3456,9 +3455,12 @@ err_res: return ERR_PTR(ret); } +EXPORT_SYMBOL_GPL(dw_hdmi_probe); -static void __dw_hdmi_remove(struct dw_hdmi *hdmi) +void dw_hdmi_remove(struct dw_hdmi *hdmi) { + drm_bridge_remove(&hdmi->bridge); + if (hdmi->audio && !IS_ERR(hdmi->audio)) platform_device_unregister(hdmi->audio); if (!IS_ERR(hdmi->cec)) @@ -3477,31 +3479,6 @@ static void __dw_hdmi_remove(struct dw_hdmi *hdmi) else i2c_put_adapter(hdmi->ddc); } - -/* ----------------------------------------------------------------------------- - * Probe/remove API, used from platforms based on the DRM bridge API. - */ -struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, - const struct dw_hdmi_plat_data *plat_data) -{ - struct dw_hdmi *hdmi; - - hdmi = __dw_hdmi_probe(pdev, plat_data); - if (IS_ERR(hdmi)) - return hdmi; - - drm_bridge_add(&hdmi->bridge); - - return hdmi; -} -EXPORT_SYMBOL_GPL(dw_hdmi_probe); - -void dw_hdmi_remove(struct dw_hdmi *hdmi) -{ - drm_bridge_remove(&hdmi->bridge); - - __dw_hdmi_remove(hdmi); -} EXPORT_SYMBOL_GPL(dw_hdmi_remove); /* ----------------------------------------------------------------------------- @@ -3514,7 +3491,7 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev, struct dw_hdmi *hdmi; int ret; - hdmi = __dw_hdmi_probe(pdev, plat_data); + hdmi = dw_hdmi_probe(pdev, plat_data); if (IS_ERR(hdmi)) return hdmi; @@ -3531,7 +3508,7 @@ EXPORT_SYMBOL_GPL(dw_hdmi_bind); void dw_hdmi_unbind(struct dw_hdmi *hdmi) { - __dw_hdmi_remove(hdmi); + dw_hdmi_remove(hdmi); } EXPORT_SYMBOL_GPL(dw_hdmi_unbind); diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 4a463fadf743..8ed8302d6bbb 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> */ diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index bd3eb0a09732..86b9f0f87a14 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018, The Linux Foundation. All rights reserved. - * datasheet: http://www.ti.com/lit/ds/symlink/sn65dsi86.pdf + * datasheet: https://www.ti.com/lit/ds/symlink/sn65dsi86.pdf */ #include <linux/bits.h> @@ -212,6 +212,8 @@ static int __maybe_unused ti_sn_bridge_suspend(struct device *dev) static const struct dev_pm_ops ti_sn_bridge_pm_ops = { SET_RUNTIME_PM_OPS(ti_sn_bridge_suspend, ti_sn_bridge_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) }; static int status_show(struct seq_file *s, void *data) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 965173fd0ac2..58527f151984 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -575,6 +575,7 @@ static int drm_atomic_plane_check(const struct drm_plane_state *old_plane_state, fb->modifier); if (ret) { struct drm_format_name_buf format_name; + DRM_DEBUG_ATOMIC("[PLANE:%d:%s] invalid pixel format %s, modifier 0x%llx\n", plane->base.id, plane->name, drm_get_format_name(fb->format->format, diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 8fce6a115dfe..9ad74045158e 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -32,6 +32,7 @@ #include <drm/drm_device.h> #include <drm/drm_plane.h> #include <drm/drm_print.h> +#include <drm/drm_vblank.h> #include <drm/drm_writeback.h> #include <linux/slab.h> @@ -93,6 +94,9 @@ __drm_atomic_helper_crtc_reset(struct drm_crtc *crtc, if (crtc_state) __drm_atomic_helper_crtc_state_reset(crtc_state, crtc); + if (drm_dev_has_vblank(crtc->dev)) + drm_crtc_vblank_reset(crtc); + crtc->state = crtc_state; } EXPORT_SYMBOL(__drm_atomic_helper_crtc_reset); diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index a1e5e262bae2..25c269bc4681 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -522,6 +522,7 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane, if (property == config->prop_fb_id) { struct drm_framebuffer *fb; + fb = drm_framebuffer_lookup(dev, file_priv, val); drm_atomic_set_fb_for_plane(state, fb); if (fb) @@ -539,6 +540,7 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane, } else if (property == config->prop_crtc_id) { struct drm_crtc *crtc = drm_crtc_find(dev, file_priv, val); + if (val && !crtc) return -EACCES; return drm_atomic_set_crtc_for_plane(state, crtc); @@ -681,6 +683,7 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, if (property == config->prop_crtc_id) { struct drm_crtc *crtc = drm_crtc_find(dev, file_priv, val); + if (val && !crtc) return -EACCES; return drm_atomic_set_crtc_for_connector(state, crtc); @@ -754,6 +757,7 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, } else if (property == config->writeback_fb_id_property) { struct drm_framebuffer *fb; int ret; + fb = drm_framebuffer_lookup(dev, file_priv, val); ret = drm_atomic_set_writeback_fb_for_connector(state, fb); if (fb) @@ -861,6 +865,7 @@ int drm_atomic_get_property(struct drm_mode_object *obj, switch (obj->type) { case DRM_MODE_OBJECT_CONNECTOR: { struct drm_connector *connector = obj_to_connector(obj); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); ret = drm_atomic_connector_get_property(connector, connector->state, property, val); @@ -868,6 +873,7 @@ int drm_atomic_get_property(struct drm_mode_object *obj, } case DRM_MODE_OBJECT_CRTC: { struct drm_crtc *crtc = obj_to_crtc(obj); + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); ret = drm_atomic_crtc_get_property(crtc, crtc->state, property, val); @@ -875,6 +881,7 @@ int drm_atomic_get_property(struct drm_mode_object *obj, } case DRM_MODE_OBJECT_PLANE: { struct drm_plane *plane = obj_to_plane(obj); + WARN_ON(!drm_modeset_is_locked(&plane->mutex)); ret = drm_atomic_plane_get_property(plane, plane->state, property, val); diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index ef26ac57f039..a0735fbc144b 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -53,6 +53,7 @@ static struct drm_map_list *drm_find_matching_map(struct drm_device *dev, struct drm_local_map *map) { struct drm_map_list *entry; + list_for_each_entry(entry, &dev->maplist, head) { /* * Because the kernel-userspace ABI is fixed at a 32-bit offset @@ -102,6 +103,7 @@ static int drm_map_handle(struct drm_device *dev, struct drm_hash_item *hash, if (!use_hashed_handle) { int ret; + hash->key = user_token >> PAGE_SHIFT; ret = drm_ht_insert_item(&dev->map_hash, hash); if (ret != -EINVAL) @@ -391,6 +393,7 @@ struct drm_local_map *drm_legacy_findmap(struct drm_device *dev, unsigned int token) { struct drm_map_list *_entry; + list_for_each_entry(_entry, &dev->maplist, head) if (_entry->user_token == token) return _entry->map; @@ -1323,6 +1326,7 @@ int __drm_legacy_infobufs(struct drm_device *dev, if (*p >= count) { for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) { struct drm_buf_entry *from = &dma->bufs[i]; + if (from->buf_count) { if (f(data, count, from) < 0) return -EFAULT; @@ -1359,6 +1363,7 @@ int drm_legacy_infobufs(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_buf_info *request = data; + return __drm_legacy_infobufs(dev, data, &request->count, copy_one_buf); } @@ -1570,6 +1575,7 @@ int drm_legacy_mapbufs(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_buf_map *request = data; + return __drm_legacy_mapbufs(dev, data, &request->count, &request->virtual, map_one_buf, file_priv); diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index b7bd46033807..00e40a26a800 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -38,7 +38,7 @@ * 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 + * also 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_get() and @@ -129,7 +129,7 @@ EXPORT_SYMBOL(drm_get_connector_type_name); /** * drm_connector_get_cmdline_mode - reads the user's cmdline mode - * @connector: connector to quwery + * @connector: connector to query * * The kernel supports per-connector configuration of its consoles through * use of the video= parameter. This function parses that option and @@ -269,6 +269,7 @@ int drm_connector_init(struct drm_device *dev, INIT_LIST_HEAD(&connector->modes); mutex_init(&connector->mutex); connector->edid_blob_ptr = NULL; + connector->epoch_counter = 0; connector->tile_blob_ptr = NULL; connector->status = connector_status_unknown; connector->display_info.panel_orientation = @@ -990,7 +991,7 @@ static const struct drm_prop_enum_list dp_colorspaces[] = { * DP MST sinks), or high-res integrated panels (like dual-link DSI) which * are not gen-locked. Note that for tiled panels which are genlocked, like * dual-link LVDS or dual-link DSI, the driver should try to not expose the - * tiling and virtualize both &drm_crtc and &drm_plane if needed. Drivers + * tiling and virtualise both &drm_crtc and &drm_plane if needed. Drivers * should update this value using drm_connector_set_tile_property(). * Userspace cannot change this property. * link-status: @@ -1156,7 +1157,7 @@ static const struct drm_prop_enum_list dp_colorspaces[] = { * * It will even need to do colorspace conversion and get all layers * to one common colorspace for blending. It can use either GL, Media - * or display engine to get this done based on the capabilties of the + * or display engine to get this done based on the capabilities of the * associated hardware. * * Driver expects metadata to be put in &struct hdr_output_metadata @@ -1639,7 +1640,7 @@ EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); * variable refresh rate capability for a connector. * * Returns: - * Zero on success, negative errono on failure. + * Zero on success, negative errno on failure. */ int drm_connector_attach_vrr_capable_property( struct drm_connector *connector) @@ -1784,7 +1785,7 @@ EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property); * HDMI connectors. * * Returns: - * Zero on success, negative errono on failure. + * Zero on success, negative errno on failure. */ int drm_mode_create_hdmi_colorspace_property(struct drm_connector *connector) { @@ -1813,7 +1814,7 @@ EXPORT_SYMBOL(drm_mode_create_hdmi_colorspace_property); * DP connectors. * * Returns: - * Zero on success, negative errono on failure. + * Zero on success, negative errno on failure. */ int drm_mode_create_dp_colorspace_property(struct drm_connector *connector) { @@ -1865,7 +1866,7 @@ EXPORT_SYMBOL(drm_mode_create_content_type_property); * drm_mode_create_suggested_offset_properties - create suggests offset properties * @dev: DRM device * - * Create the the suggested x/y offset property for connectors. + * Create the suggested x/y offset property for connectors. */ int drm_mode_create_suggested_offset_properties(struct drm_device *dev) { @@ -1979,6 +1980,7 @@ int drm_connector_update_edid_property(struct drm_connector *connector, struct drm_device *dev = connector->dev; size_t size = 0; int ret; + const struct edid *old_edid; /* ignore requests to set edid when overridden */ if (connector->override_edid) @@ -1988,7 +1990,7 @@ int drm_connector_update_edid_property(struct drm_connector *connector, size = EDID_LENGTH * (1 + edid->extensions); /* Set the display info, using edid if available, otherwise - * reseting the values to defaults. This duplicates the work + * resetting the values to defaults. This duplicates the work * done in drm_add_edid_modes, but that function is not * consistently called before this one in all drivers and the * computation is cheap enough that it seems better to @@ -2002,6 +2004,20 @@ int drm_connector_update_edid_property(struct drm_connector *connector, drm_update_tile_info(connector, edid); + if (connector->edid_blob_ptr) { + old_edid = (const struct edid *)connector->edid_blob_ptr->data; + if (old_edid) { + if (!drm_edid_are_equal(edid, old_edid)) { + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] Edid was changed.\n", + connector->base.id, connector->name); + + connector->epoch_counter += 1; + DRM_DEBUG_KMS("Updating change counter to %llu\n", + connector->epoch_counter); + } + } + } + drm_object_property_set_value(&connector->base, dev->mode_config.non_desktop_property, connector->display_info.non_desktop); @@ -2101,7 +2117,7 @@ void drm_connector_set_vrr_capable_property( EXPORT_SYMBOL(drm_connector_set_vrr_capable_property); /** - * drm_connector_set_panel_orientation - sets the connecter's panel_orientation + * drm_connector_set_panel_orientation - sets the connector's panel_orientation * @connector: connector for which to set the panel-orientation property. * @panel_orientation: drm_panel_orientation value to set * @@ -2156,7 +2172,7 @@ EXPORT_SYMBOL(drm_connector_set_panel_orientation); /** * drm_connector_set_panel_orientation_with_quirk - - * set the connecter's panel_orientation after checking for quirks + * set the connector's panel_orientation after checking for quirks * @connector: connector for which to init the panel-orientation property. * @panel_orientation: drm_panel_orientation value to set * @width: width in pixels of the panel, used for panel quirk detection @@ -2393,6 +2409,7 @@ static void drm_tile_group_free(struct kref *kref) { struct drm_tile_group *tg = container_of(kref, struct drm_tile_group, refcount); struct drm_device *dev = tg->dev; + mutex_lock(&dev->mode_config.idr_mutex); idr_remove(&dev->mode_config.tile_idr, tg->id); mutex_unlock(&dev->mode_config.idr_mutex); @@ -2428,6 +2445,7 @@ struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev, { struct drm_tile_group *tg; int id; + mutex_lock(&dev->mode_config.idr_mutex); idr_for_each_entry(&dev->mode_config.tile_idr, tg, id) { if (!memcmp(tg->group_data, topology, 8)) { diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f1216088f65f..283bcc4362ca 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -656,6 +656,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, fb->modifier); if (ret) { struct drm_format_name_buf format_name; + DRM_DEBUG_KMS("Invalid pixel format %s, modifier 0x%llx\n", drm_get_format_name(fb->format->format, &format_name), diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index a4d36aca45ea..bff917531f33 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -185,6 +185,7 @@ static void __drm_helper_disable_unused_functions(struct drm_device *dev) drm_for_each_crtc(crtc, dev) { const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc->enabled = drm_helper_crtc_in_use(crtc); if (!crtc->enabled) { if (crtc_funcs->disable) @@ -884,6 +885,7 @@ int drm_helper_connector_dpms(struct drm_connector *connector, int mode) if (mode < old_dpms) { if (crtc) { const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + if (crtc_funcs->dpms) (*crtc_funcs->dpms) (crtc, drm_helper_choose_crtc_dpms(crtc)); @@ -898,6 +900,7 @@ int drm_helper_connector_dpms(struct drm_connector *connector, int mode) drm_helper_encoder_dpms(encoder, encoder_dpms); if (crtc) { const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + if (crtc_funcs->dpms) (*crtc_funcs->dpms) (crtc, drm_helper_choose_crtc_dpms(crtc)); diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 6d716dcb432c..a3c82e726057 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -57,6 +57,7 @@ static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE], int i = DP_LANE0_1_STATUS + (lane >> 1); int s = (lane & 1) * 4; u8 l = dp_link_status(link_status, i); + return (l >> s) & 0xf; } @@ -257,7 +258,8 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, err = ret; } - DRM_DEBUG_KMS("Too many retries, giving up. First error: %d\n", err); + DRM_DEBUG_KMS("%s: Too many retries, giving up. First error: %d\n", + aux->name, err); ret = err; unlock: @@ -376,43 +378,44 @@ bool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux, if (drm_dp_dpcd_read(aux, DP_DEVICE_SERVICE_IRQ_VECTOR, &auto_test_req, 1) < 1) { - DRM_ERROR("DPCD failed read at register 0x%x\n", - DP_DEVICE_SERVICE_IRQ_VECTOR); + DRM_ERROR("%s: DPCD failed read at register 0x%x\n", + aux->name, DP_DEVICE_SERVICE_IRQ_VECTOR); return false; } auto_test_req &= DP_AUTOMATED_TEST_REQUEST; if (drm_dp_dpcd_read(aux, DP_TEST_REQUEST, &link_edid_read, 1) < 1) { - DRM_ERROR("DPCD failed read at register 0x%x\n", - DP_TEST_REQUEST); + DRM_ERROR("%s: DPCD failed read at register 0x%x\n", + aux->name, DP_TEST_REQUEST); return false; } link_edid_read &= DP_TEST_LINK_EDID_READ; if (!auto_test_req || !link_edid_read) { - DRM_DEBUG_KMS("Source DUT does not support TEST_EDID_READ\n"); + DRM_DEBUG_KMS("%s: Source DUT does not support TEST_EDID_READ\n", + aux->name); return false; } if (drm_dp_dpcd_write(aux, DP_DEVICE_SERVICE_IRQ_VECTOR, &auto_test_req, 1) < 1) { - DRM_ERROR("DPCD failed write at register 0x%x\n", - DP_DEVICE_SERVICE_IRQ_VECTOR); + DRM_ERROR("%s: DPCD failed write at register 0x%x\n", + aux->name, DP_DEVICE_SERVICE_IRQ_VECTOR); return false; } /* send back checksum for the last edid extension block data */ if (drm_dp_dpcd_write(aux, DP_TEST_EDID_CHECKSUM, &real_edid_checksum, 1) < 1) { - DRM_ERROR("DPCD failed write at register 0x%x\n", - DP_TEST_EDID_CHECKSUM); + DRM_ERROR("%s: DPCD failed write at register 0x%x\n", + aux->name, DP_TEST_EDID_CHECKSUM); return false; } test_resp |= DP_TEST_EDID_CHECKSUM_WRITE; if (drm_dp_dpcd_write(aux, DP_TEST_RESPONSE, &test_resp, 1) < 1) { - DRM_ERROR("DPCD failed write at register 0x%x\n", - DP_TEST_RESPONSE); + DRM_ERROR("%s: DPCD failed write at register 0x%x\n", + aux->name, DP_TEST_RESPONSE); return false; } @@ -737,10 +740,11 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) * Avoid spamming the kernel log with timeout errors. */ if (ret == -ETIMEDOUT) - DRM_DEBUG_KMS_RATELIMITED("transaction timed out\n"); + DRM_DEBUG_KMS_RATELIMITED("%s: transaction timed out\n", + aux->name); else - DRM_DEBUG_KMS("transaction failed: %d\n", ret); - + DRM_DEBUG_KMS("%s: transaction failed: %d\n", + aux->name, ret); return ret; } @@ -754,11 +758,12 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) break; case DP_AUX_NATIVE_REPLY_NACK: - DRM_DEBUG_KMS("native nack (result=%d, size=%zu)\n", ret, msg->size); + DRM_DEBUG_KMS("%s: native nack (result=%d, size=%zu)\n", + aux->name, ret, msg->size); return -EREMOTEIO; case DP_AUX_NATIVE_REPLY_DEFER: - DRM_DEBUG_KMS("native defer\n"); + DRM_DEBUG_KMS("%s: native defer\n", aux->name); /* * We could check for I2C bit rate capabilities and if * available adjust this interval. We could also be @@ -772,7 +777,8 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) continue; default: - DRM_ERROR("invalid native reply %#04x\n", msg->reply); + DRM_ERROR("%s: invalid native reply %#04x\n", + aux->name, msg->reply); return -EREMOTEIO; } @@ -787,13 +793,13 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) return ret; case DP_AUX_I2C_REPLY_NACK: - DRM_DEBUG_KMS("I2C nack (result=%d, size=%zu)\n", - ret, msg->size); + DRM_DEBUG_KMS("%s: I2C nack (result=%d, size=%zu)\n", + aux->name, ret, msg->size); aux->i2c_nack_count++; return -EREMOTEIO; case DP_AUX_I2C_REPLY_DEFER: - DRM_DEBUG_KMS("I2C defer\n"); + DRM_DEBUG_KMS("%s: I2C defer\n", aux->name); /* DP Compliance Test 4.2.2.5 Requirement: * Must have at least 7 retries for I2C defers on the * transaction to pass this test @@ -807,12 +813,13 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) continue; default: - DRM_ERROR("invalid I2C reply %#04x\n", msg->reply); + DRM_ERROR("%s: invalid I2C reply %#04x\n", + aux->name, msg->reply); return -EREMOTEIO; } } - DRM_DEBUG_KMS("too many retries, giving up\n"); + DRM_DEBUG_KMS("%s: Too many retries, giving up\n", aux->name); return -EREMOTEIO; } @@ -841,8 +848,8 @@ static int drm_dp_i2c_drain_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *o return err == 0 ? -EPROTO : err; if (err < msg.size && err < ret) { - DRM_DEBUG_KMS("Partial I2C reply: requested %zu bytes got %d bytes\n", - msg.size, err); + DRM_DEBUG_KMS("%s: Partial I2C reply: requested %zu bytes got %d bytes\n", + aux->name, msg.size, err); ret = err; } @@ -1021,11 +1028,12 @@ static void drm_dp_aux_crc_work(struct work_struct *work) } if (ret == -EAGAIN) { - DRM_DEBUG_KMS("Get CRC failed after retrying: %d\n", - ret); + DRM_DEBUG_KMS("%s: Get CRC failed after retrying: %d\n", + aux->name, ret); continue; } else if (ret) { - DRM_DEBUG_KMS("Failed to get a CRC: %d\n", ret); + DRM_DEBUG_KMS("%s: Failed to get a CRC: %d\n", + aux->name, ret); continue; } @@ -1388,8 +1396,8 @@ int drm_dp_read_desc(struct drm_dp_aux *aux, struct drm_dp_desc *desc, dev_id_len = strnlen(ident->device_id, sizeof(ident->device_id)); - DRM_DEBUG_KMS("DP %s: OUI %*phD dev-ID %*pE HW-rev %d.%d SW-rev %d.%d quirks 0x%04x\n", - is_branch ? "branch" : "sink", + DRM_DEBUG_KMS("%s: DP %s: OUI %*phD dev-ID %*pE HW-rev %d.%d SW-rev %d.%d quirks 0x%04x\n", + aux->name, is_branch ? "branch" : "sink", (int)sizeof(ident->oui), ident->oui, dev_id_len, ident->device_id, ident->hw_rev >> 4, ident->hw_rev & 0xf, diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index b2f5a84b4cfb..09b32289497e 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -259,6 +259,7 @@ static u8 drm_dp_msg_data_crc4(const uint8_t *data, u8 number_of_bytes) static inline u8 drm_dp_calc_sb_hdr_size(struct drm_dp_sideband_msg_hdr *hdr) { u8 size = 3; + size += (hdr->lct / 2); return size; } @@ -269,6 +270,7 @@ static void drm_dp_encode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr, int idx = 0; int i; u8 crc4; + buf[idx++] = ((hdr->lct & 0xf) << 4) | (hdr->lcr & 0xf); for (i = 0; i < (hdr->lct / 2); i++) buf[idx++] = hdr->rad[i]; @@ -289,6 +291,7 @@ static bool drm_dp_decode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr, u8 len; int i; u8 idx; + if (buf[0] == 0) return false; len = 3; @@ -326,6 +329,7 @@ drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req, int idx = 0; int i; u8 *buf = raw->msg; + buf[idx++] = req->req_type & 0x7f; switch (req->req_type) { @@ -673,6 +677,7 @@ drm_dp_mst_dump_sideband_msg_tx(struct drm_printer *p, static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len) { u8 crc4; + crc4 = drm_dp_msg_data_crc4(msg, len); msg[len] = crc4; } @@ -747,6 +752,7 @@ static bool drm_dp_sideband_parse_link_address(struct drm_dp_sideband_msg_rx *ra { int idx = 1; int i; + memcpy(repmsg->u.link_addr.guid, &raw->msg[idx], 16); idx += 16; repmsg->u.link_addr.nports = raw->msg[idx] & 0xf; @@ -798,6 +804,7 @@ static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx struct drm_dp_sideband_msg_reply_body *repmsg) { int idx = 1; + repmsg->u.remote_dpcd_read_ack.port_number = raw->msg[idx] & 0xf; idx++; if (idx > raw->curlen) @@ -818,6 +825,7 @@ static bool drm_dp_sideband_parse_remote_dpcd_write(struct drm_dp_sideband_msg_r struct drm_dp_sideband_msg_reply_body *repmsg) { int idx = 1; + repmsg->u.remote_dpcd_write_ack.port_number = raw->msg[idx] & 0xf; idx++; if (idx > raw->curlen) @@ -851,6 +859,7 @@ static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband struct drm_dp_sideband_msg_reply_body *repmsg) { int idx = 1; + repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf; repmsg->u.path_resources.fec_capable = raw->msg[idx] & 0x1; idx++; @@ -874,6 +883,7 @@ static bool drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_ms struct drm_dp_sideband_msg_reply_body *repmsg) { int idx = 1; + repmsg->u.allocate_payload.port_number = (raw->msg[idx] >> 4) & 0xf; idx++; if (idx > raw->curlen) @@ -896,6 +906,7 @@ static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_r struct drm_dp_sideband_msg_reply_body *repmsg) { int idx = 1; + repmsg->u.query_payload.port_number = (raw->msg[idx] >> 4) & 0xf; idx++; if (idx > raw->curlen) @@ -1082,6 +1093,7 @@ static void build_allocate_payload(struct drm_dp_sideband_msg_tx *msg, u8 *sdp_stream_sink) { struct drm_dp_sideband_msg_req_body req; + memset(&req, 0, sizeof(req)); req.req_type = DP_ALLOCATE_PAYLOAD; req.u.allocate_payload.port_number = port_num; @@ -1142,6 +1154,7 @@ static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr, int vcpi) { int i; + if (vcpi == 0) return; @@ -1940,6 +1953,7 @@ static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port, int parent_lct = port->parent->lct; int shift = 4; int idx = (parent_lct - 1) / 2; + if (parent_lct > 1) { memcpy(rad, port->parent->rad, idx + 1); shift = (parent_lct % 2) ? 4 : 0; @@ -2118,10 +2132,12 @@ static void build_mst_prop_path(const struct drm_dp_mst_branch *mstb, { int i; char temp[8]; + snprintf(proppath, proppath_size, "mst:%d", mstb->mgr->conn_base_id); for (i = 0; i < (mstb->lct - 1); i++) { int shift = (i % 2) ? 0 : 4; int port_num = (mstb->rad[i / 2] >> shift) & 0xf; + snprintf(temp, sizeof(temp), "-%d", port_num); strlcat(proppath, temp, proppath_size); } @@ -3158,6 +3174,7 @@ static int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_payload *payload) { int ret; + ret = drm_dp_payload_send_msg(mgr, port, id, port->vcpi.pbn); if (ret < 0) return ret; @@ -3314,6 +3331,7 @@ int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr) struct drm_dp_mst_port *port; int i; int ret = 0; + mutex_lock(&mgr->payload_lock); for (i = 0; i < mgr->max_payloads; i++) { @@ -3779,6 +3797,7 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) /* Were we actually expecting a response, and from this mstb? */ if (!txmsg || txmsg->dst != mstb) { struct drm_dp_sideband_msg_hdr *hdr; + hdr = &msg->initial_hdr; DRM_DEBUG_KMS("Got MST reply with no msg %p %d %d %02x %02x\n", mstb, hdr->seqno, hdr->lct, hdr->rad[0], @@ -4326,6 +4345,7 @@ EXPORT_SYMBOL(drm_dp_mst_allocate_vcpi); int drm_dp_mst_get_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) { int slots = 0; + port = drm_dp_mst_topology_get_port_validated(mgr, port); if (!port) return slots; diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 71ae0cd6d576..f6c723904204 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1616,6 +1616,37 @@ static bool drm_edid_is_zero(const u8 *in_edid, int length) } /** + * drm_edid_are_equal - compare two edid blobs. + * @edid1: pointer to first blob + * @edid2: pointer to second blob + * This helper can be used during probing to determine if + * edid had changed. + */ +bool drm_edid_are_equal(const struct edid *edid1, const struct edid *edid2) +{ + int edid1_len, edid2_len; + bool edid1_present = edid1 != NULL; + bool edid2_present = edid2 != NULL; + + if (edid1_present != edid2_present) + return false; + + if (edid1) { + edid1_len = EDID_LENGTH * (1 + edid1->extensions); + edid2_len = EDID_LENGTH * (1 + edid2->extensions); + + if (edid1_len != edid2_len) + return false; + + if (memcmp(edid1, edid2, edid1_len)) + return false; + } + + return true; +} +EXPORT_SYMBOL(drm_edid_are_equal); + +/** * drm_edid_block_valid - Sanity check the EDID block (base or extension) * @raw_edid: pointer to raw EDID block * @block: type of block to validate (0 for base, extension otherwise) @@ -1641,6 +1672,7 @@ bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid, if (block == 0) { int score = drm_edid_header_is_valid(raw_edid); + if (score == 8) { if (edid_corrupt) *edid_corrupt = false; @@ -1812,9 +1844,7 @@ static void connector_bad_edid(struct drm_connector *connector, if (connector->bad_edid_counter++ && !drm_debug_enabled(DRM_UT_KMS)) return; - dev_warn(connector->dev->dev, - "%s: EDID is invalid:\n", - connector->name); + drm_warn(connector->dev, "%s: EDID is invalid:\n", connector->name); for (i = 0; i < num_blocks; i++) { u8 *block = edid + i * EDID_LENGTH; char prefix[20]; @@ -2017,13 +2047,17 @@ EXPORT_SYMBOL(drm_probe_ddc); struct edid *drm_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) { + struct edid *edid; + if (connector->force == DRM_FORCE_OFF) return NULL; if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter)) return NULL; - return drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter); + edid = drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter); + drm_connector_update_edid_property(connector, edid); + return edid; } EXPORT_SYMBOL(drm_get_edid); @@ -2188,6 +2222,7 @@ struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) { const struct drm_display_mode *ptr = &drm_dmt_modes[i]; + if (hsize != ptr->hdisplay) continue; if (vsize != ptr->vdisplay) @@ -2259,6 +2294,7 @@ drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure) for (i = 1; i <= raw_edid[0x7e]; i++) { u8 *ext = raw_edid + (i * EDID_LENGTH); + switch (*ext) { case CEA_EXT: cea_for_each_detailed_block(ext, cb, closure); @@ -2290,6 +2326,7 @@ drm_monitor_supports_rb(struct edid *edid) { if (edid->revision >= 4) { bool ret = false; + drm_for_each_detailed_block((u8 *)edid, is_rb, &ret); return ret; } @@ -2314,6 +2351,7 @@ static int drm_gtf2_hbreak(struct edid *edid) { u8 *r = NULL; + drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); return r ? (r[12] * 2) : 0; } @@ -2322,6 +2360,7 @@ static int drm_gtf2_2c(struct edid *edid) { u8 *r = NULL; + drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); return r ? r[13] : 0; } @@ -2330,6 +2369,7 @@ static int drm_gtf2_m(struct edid *edid) { u8 *r = NULL; + drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); return r ? (r[15] << 8) + r[14] : 0; } @@ -2338,6 +2378,7 @@ static int drm_gtf2_k(struct edid *edid) { u8 *r = NULL; + drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); return r ? r[16] : 0; } @@ -2346,6 +2387,7 @@ static int drm_gtf2_2j(struct edid *edid) { u8 *r = NULL; + drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); return r ? r[17] : 0; } @@ -2797,6 +2839,7 @@ drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, for (i = 0; i < ARRAY_SIZE(extra_modes); i++) { const struct minimode *m = &extra_modes[i]; + newmode = drm_gtf_mode(dev, m->w, m->h, m->r, 0, 0); if (!newmode) return modes; @@ -2826,6 +2869,7 @@ drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid, for (i = 0; i < ARRAY_SIZE(extra_modes); i++) { const struct minimode *m = &extra_modes[i]; + newmode = drm_cvt_mode(dev, m->w, m->h, m->r, rb, 0, 0); if (!newmode) return modes; @@ -2961,6 +3005,7 @@ add_established_modes(struct drm_connector *connector, struct edid *edid) for (i = 0; i <= EDID_EST_TIMINGS; i++) { if (est_bits & (1<<i)) { struct drm_display_mode *newmode; + newmode = drm_mode_duplicate(dev, &edid_est_modes[i]); if (newmode) { drm_mode_probed_add(connector, newmode); @@ -3049,6 +3094,7 @@ static int drm_cvt_modes(struct drm_connector *connector, for (i = 0; i < 4; i++) { int uninitialized_var(width), height; + cvt = &(timing->data.other_data.data.cvt[i]); if (!memcmp(cvt->code, empty, 3)) @@ -3188,7 +3234,8 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, /* * Search EDID for CEA extension block. */ -static u8 *drm_find_edid_extension(const struct edid *edid, int ext_id) +static u8 *drm_find_edid_extension(const struct edid *edid, + int ext_id, int *ext_index) { u8 *edid_ext = NULL; int i; @@ -3198,23 +3245,26 @@ static u8 *drm_find_edid_extension(const struct edid *edid, int ext_id) return NULL; /* Find CEA extension */ - for (i = 0; i < edid->extensions; i++) { + for (i = *ext_index; i < edid->extensions; i++) { edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); if (edid_ext[0] == ext_id) break; } - if (i == edid->extensions) + if (i >= edid->extensions) return NULL; + *ext_index = i + 1; + return edid_ext; } static u8 *drm_find_displayid_extension(const struct edid *edid, - int *length, int *idx) + int *length, int *idx, + int *ext_index) { - u8 *displayid = drm_find_edid_extension(edid, DISPLAYID_EXT); + u8 *displayid = drm_find_edid_extension(edid, DISPLAYID_EXT, ext_index); struct displayid_hdr *base; int ret; @@ -3241,26 +3291,31 @@ static u8 *drm_find_cea_extension(const struct edid *edid) struct displayid_block *block; u8 *cea; u8 *displayid; + int ext_index; /* Look for a top level CEA extension block */ - cea = drm_find_edid_extension(edid, CEA_EXT); + /* FIXME: make callers iterate through multiple CEA ext blocks? */ + ext_index = 0; + cea = drm_find_edid_extension(edid, CEA_EXT, &ext_index); if (cea) return cea; /* CEA blocks can also be found embedded in a DisplayID block */ - displayid = drm_find_displayid_extension(edid, &length, &idx); - if (!displayid) - return NULL; + ext_index = 0; + for (;;) { + displayid = drm_find_displayid_extension(edid, &length, &idx, + &ext_index); + if (!displayid) + return NULL; - idx += sizeof(struct displayid_hdr); - for_each_displayid_db(displayid, block, idx, length) { - if (block->tag == DATA_BLOCK_CTA) { - cea = (u8 *)block; - break; + idx += sizeof(struct displayid_hdr); + for_each_displayid_db(displayid, block, idx, length) { + if (block->tag == DATA_BLOCK_CTA) + return (u8 *)block; } } - return cea; + return NULL; } static __always_inline const struct drm_display_mode *cea_mode_for_vic(u8 vic) @@ -3691,6 +3746,7 @@ do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) for (i = 0; i < len; i++) { struct drm_display_mode *mode; + mode = drm_display_mode_from_vic_index(connector, db, len, i); if (mode) { /* @@ -4532,6 +4588,7 @@ int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads) if (cea_db_tag(db) == AUDIO_BLOCK) { int j; + dbl = cea_db_payload_len(db); count = dbl / 3; /* SAD is 3B */ @@ -5135,6 +5192,7 @@ static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *d unsigned vsync_width = (timings->vsw[0] | timings->vsw[1] << 8) + 1; bool hsync_positive = (timings->hsync[1] >> 7) & 0x1; bool vsync_positive = (timings->vsync[1] >> 7) & 0x1; + mode = drm_mode_create(dev); if (!mode) return NULL; @@ -5195,19 +5253,24 @@ static int add_displayid_detailed_modes(struct drm_connector *connector, int length, idx; struct displayid_block *block; int num_modes = 0; + int ext_index = 0; - displayid = drm_find_displayid_extension(edid, &length, &idx); - if (!displayid) - return 0; - - idx += sizeof(struct displayid_hdr); - for_each_displayid_db(displayid, block, idx, length) { - switch (block->tag) { - case DATA_BLOCK_TYPE_1_DETAILED_TIMING: - num_modes += add_displayid_detailed_1_modes(connector, block); + for (;;) { + displayid = drm_find_displayid_extension(edid, &length, &idx, + &ext_index); + if (!displayid) break; + + idx += sizeof(struct displayid_hdr); + for_each_displayid_db(displayid, block, idx, length) { + switch (block->tag) { + case DATA_BLOCK_TYPE_1_DETAILED_TIMING: + num_modes += add_displayid_detailed_1_modes(connector, block); + break; + } } } + return num_modes; } @@ -5233,7 +5296,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) } if (!drm_edid_is_valid(edid)) { clear_eld(connector); - dev_warn(connector->dev->dev, "%s: EDID invalid.\n", + drm_warn(connector->dev, "%s: EDID invalid.\n", connector->name); return 0; } @@ -5316,6 +5379,7 @@ int drm_add_modes_noedid(struct drm_connector *connector, for (i = 0; i < count; i++) { const struct drm_display_mode *ptr = &drm_dmt_modes[i]; + if (hdisplay && vdisplay) { /* * Only when two are valid, they will be used to check @@ -5787,8 +5851,8 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame, } EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode); -static int drm_parse_tiled_block(struct drm_connector *connector, - const struct displayid_block *block) +static void drm_parse_tiled_block(struct drm_connector *connector, + const struct displayid_block *block) { const struct displayid_tiled_block *tile = (struct displayid_tiled_block *)block; u16 w, h; @@ -5822,30 +5886,27 @@ static int drm_parse_tiled_block(struct drm_connector *connector, DRM_DEBUG_KMS("vend %c%c%c\n", tile->topology_id[0], tile->topology_id[1], tile->topology_id[2]); tg = drm_mode_get_tile_group(connector->dev, tile->topology_id); - if (!tg) { + if (!tg) tg = drm_mode_create_tile_group(connector->dev, tile->topology_id); - } if (!tg) - return -ENOMEM; + return; if (connector->tile_group != tg) { /* if we haven't got a pointer, take the reference, drop ref to old tile group */ - if (connector->tile_group) { + if (connector->tile_group) drm_mode_put_tile_group(connector->dev, connector->tile_group); - } connector->tile_group = tg; - } else + } else { /* if same tile group, then release the ref we just took. */ drm_mode_put_tile_group(connector->dev, tg); - return 0; + } } -static int drm_displayid_parse_tiled(struct drm_connector *connector, - const u8 *displayid, int length, int idx) +static void drm_displayid_parse_tiled(struct drm_connector *connector, + const u8 *displayid, int length, int idx) { const struct displayid_block *block; - int ret; idx += sizeof(struct displayid_hdr); for_each_displayid_db(displayid, block, idx, length) { @@ -5854,42 +5915,34 @@ static int drm_displayid_parse_tiled(struct drm_connector *connector, switch (block->tag) { case DATA_BLOCK_TILED_DISPLAY: - ret = drm_parse_tiled_block(connector, block); - if (ret) - return ret; + drm_parse_tiled_block(connector, block); break; default: DRM_DEBUG_KMS("found DisplayID tag 0x%x, unhandled\n", block->tag); break; } } - return 0; } void drm_update_tile_info(struct drm_connector *connector, const struct edid *edid) { const void *displayid = NULL; + int ext_index = 0; int length, idx; - int ret; connector->has_tile = false; - displayid = drm_find_displayid_extension(edid, &length, &idx); - if (!displayid) { - /* drop reference to any tile group we had */ - goto out_drop_ref; + for (;;) { + displayid = drm_find_displayid_extension(edid, &length, &idx, + &ext_index); + if (!displayid) + break; + + drm_displayid_parse_tiled(connector, displayid, length, idx); } - ret = drm_displayid_parse_tiled(connector, displayid, length, idx); - if (ret < 0) - goto out_drop_ref; - if (!connector->has_tile) - goto out_drop_ref; - return; -out_drop_ref: - if (connector->tile_group) { + if (!connector->has_tile && connector->tile_group) { drm_mode_put_tile_group(connector->dev, connector->tile_group); connector->tile_group = NULL; } - return; } diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 170aa7689110..da0d96a69570 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1784,7 +1784,7 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper, if (ret < 0) return ret; - dev_info(dev->dev, "fb%d: %s frame buffer device\n", + drm_info(dev, "fb%d: %s frame buffer device\n", info->node, info->fix.id); mutex_lock(&kernel_fb_helper_lock); diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 02b5ab626edb..0ac4566ae3f4 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -375,6 +375,7 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) */ if (!dev->hose) { struct pci_dev *pci_dev; + pci_dev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, NULL); if (pci_dev) { dev->hose = pci_dev->sysdata; @@ -758,6 +759,7 @@ void drm_event_cancel_free(struct drm_device *dev, struct drm_pending_event *p) { unsigned long flags; + spin_lock_irqsave(&dev->event_lock, flags); if (p->file_priv) { p->file_priv->event_space += p->event->length; diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index 0375b3d7f8d0..df656366a530 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -1110,6 +1110,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) if (drm_framebuffer_read_refcount(fb) > 1) { if (drm_drv_uses_atomic_modeset(dev)) { int ret = atomic_remove_fb(fb); + WARN(ret, "atomic remove_fb failed with %i\n", ret); } else legacy_remove_fb(fb); diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 06a5b9ee1fe0..822edeadbab3 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -105,8 +105,8 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, cma_obj->vaddr = dma_alloc_wc(drm->dev, size, &cma_obj->paddr, GFP_KERNEL | __GFP_NOWARN); if (!cma_obj->vaddr) { - dev_dbg(drm->dev, "failed to allocate buffer with size %zu\n", - size); + drm_dbg(drm, "failed to allocate buffer with size %zu\n", + size); ret = -ENOMEM; goto error; } diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index ad096600d89f..3296ed3df358 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -10,6 +10,7 @@ #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_gem_ttm_helper.h> #include <drm/drm_gem_vram_helper.h> +#include <drm/drm_managed.h> #include <drm/drm_mode.h> #include <drm/drm_plane.h> #include <drm/drm_prime.h> @@ -40,12 +41,11 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs; * the frame's scanout buffer or the cursor image. If there's no more space * left in VRAM, inactive GEM objects can be moved to system memory. * - * The easiest way to use the VRAM helper library is to call - * drm_vram_helper_alloc_mm(). The function allocates and initializes an - * instance of &struct drm_vram_mm in &struct drm_device.vram_mm . Use - * &DRM_GEM_VRAM_DRIVER to initialize &struct drm_driver and - * &DRM_VRAM_MM_FILE_OPERATIONS to initialize &struct file_operations; - * as illustrated below. + * To initialize the VRAM helper library call drmm_vram_helper_alloc_mm(). + * The function allocates and initializes an instance of &struct drm_vram_mm + * in &struct drm_device.vram_mm . Use &DRM_GEM_VRAM_DRIVER to initialize + * &struct drm_driver and &DRM_VRAM_MM_FILE_OPERATIONS to initialize + * &struct file_operations; as illustrated below. * * .. code-block:: c * @@ -69,7 +69,7 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs; * // setup device, vram base and size * // ... * - * ret = drm_vram_helper_alloc_mm(dev, vram_base, vram_size); + * ret = drmm_vram_helper_alloc_mm(dev, vram_base, vram_size); * if (ret) * return ret; * return 0; @@ -81,20 +81,12 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs; * manages an area of video RAM with VRAM MM and provides GEM VRAM objects * to userspace. * - * To clean up the VRAM memory management, call drm_vram_helper_release_mm() - * in the driver's clean-up code. + * You don't have to clean up the instance of VRAM MM. + * drmm_vram_helper_alloc_mm() is a managed interface that installs a + * clean-up handler to run during the DRM device's release. * - * .. code-block:: c - * - * void fini_drm_driver() - * { - * struct drm_device *dev = ...; - * - * drm_vram_helper_release_mm(dev); - * } - * - * For drawing or scanout operations, buffer object have to be pinned in video - * RAM. Call drm_gem_vram_pin() with &DRM_GEM_VRAM_PL_FLAG_VRAM or + * For drawing or scanout operations, rsp. buffer objects have to be pinned + * in video RAM. Call drm_gem_vram_pin() with &DRM_GEM_VRAM_PL_FLAG_VRAM or * &DRM_GEM_VRAM_PL_FLAG_SYSTEM to pin a buffer object in video RAM or system * memory. Call drm_gem_vram_unpin() to release the pinned object afterwards. * @@ -1017,14 +1009,13 @@ static int bo_driver_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, { switch (type) { case TTM_PL_SYSTEM: - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = 0; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; break; case TTM_PL_VRAM: man->func = &ttm_bo_manager_func; - man->flags = TTM_MEMTYPE_FLAG_FIXED | - TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = TTM_MEMTYPE_FLAG_FIXED; man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; man->default_caching = TTM_PL_FLAG_WC; @@ -1067,12 +1058,8 @@ static void bo_driver_move_notify(struct ttm_buffer_object *bo, static int bo_driver_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) { - struct ttm_mem_type_manager *man = bdev->man + mem->mem_type; struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bdev); - if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) - return -EINVAL; - mem->bus.addr = NULL; mem->bus.size = mem->num_pages << PAGE_SHIFT; @@ -1094,10 +1081,6 @@ static int bo_driver_io_mem_reserve(struct ttm_bo_device *bdev, return 0; } -static void bo_driver_io_mem_free(struct ttm_bo_device *bdev, - struct ttm_mem_reg *mem) -{ } - static struct ttm_bo_driver bo_driver = { .ttm_tt_create = bo_driver_ttm_tt_create, .ttm_tt_populate = ttm_pool_populate, @@ -1107,7 +1090,6 @@ static struct ttm_bo_driver bo_driver = { .evict_flags = bo_driver_evict_flags, .move_notify = bo_driver_move_notify, .io_mem_reserve = bo_driver_io_mem_reserve, - .io_mem_free = bo_driver_io_mem_free, }; /* @@ -1176,17 +1158,7 @@ static void drm_vram_mm_cleanup(struct drm_vram_mm *vmm) * Helpers for integration with struct drm_device */ -/** - * drm_vram_helper_alloc_mm - Allocates a device's instance of \ - &struct drm_vram_mm - * @dev: the DRM device - * @vram_base: the base address of the video memory - * @vram_size: the size of the video memory in bytes - * - * Returns: - * The new instance of &struct drm_vram_mm on success, or - * an ERR_PTR()-encoded errno code otherwise. - */ +/* deprecated; use drmm_vram_mm_init() */ struct drm_vram_mm *drm_vram_helper_alloc_mm( struct drm_device *dev, uint64_t vram_base, size_t vram_size) { @@ -1212,11 +1184,6 @@ err_kfree: } EXPORT_SYMBOL(drm_vram_helper_alloc_mm); -/** - * drm_vram_helper_release_mm - Releases a device's instance of \ - &struct drm_vram_mm - * @dev: the DRM device - */ void drm_vram_helper_release_mm(struct drm_device *dev) { if (!dev->vram_mm) @@ -1228,6 +1195,41 @@ void drm_vram_helper_release_mm(struct drm_device *dev) } EXPORT_SYMBOL(drm_vram_helper_release_mm); +static void drm_vram_mm_release(struct drm_device *dev, void *ptr) +{ + drm_vram_helper_release_mm(dev); +} + +/** + * drmm_vram_helper_init - Initializes a device's instance of + * &struct drm_vram_mm + * @dev: the DRM device + * @vram_base: the base address of the video memory + * @vram_size: the size of the video memory in bytes + * + * Creates a new instance of &struct drm_vram_mm and stores it in + * struct &drm_device.vram_mm. The instance is auto-managed and cleaned + * up as part of device cleanup. Calling this function multiple times + * will generate an error message. + * + * Returns: + * 0 on success, or a negative errno code otherwise. + */ +int drmm_vram_helper_init(struct drm_device *dev, uint64_t vram_base, + size_t vram_size) +{ + struct drm_vram_mm *vram_mm; + + if (drm_WARN_ON_ONCE(dev, dev->vram_mm)) + return 0; + + vram_mm = drm_vram_helper_alloc_mm(dev, vram_base, vram_size); + if (IS_ERR(vram_mm)) + return PTR_ERR(vram_mm); + return drmm_add_action_or_reset(dev, drm_vram_mm_release, NULL); +} +EXPORT_SYMBOL(drmm_vram_helper_init); + /* * Mode-config helpers */ diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 2470a352730b..8e01caaf95cc 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -21,7 +21,10 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +#include <linux/kthread.h> + #include <drm/drm_ioctl.h> +#include <drm/drm_vblank.h> #define DRM_IF_MAJOR 1 #define DRM_IF_MINOR 4 @@ -38,6 +41,7 @@ struct drm_master; struct drm_minor; struct drm_prime_file_private; struct drm_printer; +struct drm_vblank_crtc; /* drm_file.c */ extern struct mutex drm_global_mutex; @@ -93,7 +97,30 @@ void drm_minor_release(struct drm_minor *minor); void drm_managed_release(struct drm_device *dev); /* drm_vblank.c */ +static inline bool drm_vblank_passed(u64 seq, u64 ref) +{ + return (seq - ref) <= (1 << 23); +} + void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe); +int drm_vblank_get(struct drm_device *dev, unsigned int pipe); +void drm_vblank_put(struct drm_device *dev, unsigned int pipe); +u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe); + +/* drm_vblank_work.c */ +static inline void drm_vblank_flush_worker(struct drm_vblank_crtc *vblank) +{ + kthread_flush_worker(vblank->worker); +} + +static inline void drm_vblank_destroy_worker(struct drm_vblank_crtc *vblank) +{ + kthread_destroy_worker(vblank->worker); +} + +int drm_vblank_worker_init(struct drm_vblank_crtc *vblank); +void drm_vblank_cancel_pending_works(struct drm_vblank_crtc *vblank); +void drm_handle_vblank_works(struct drm_vblank_crtc *vblank); /* IOCTLS */ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c index ff5d40036e21..f86448ab1fe0 100644 --- a/drivers/gpu/drm/drm_ioc32.c +++ b/drivers/gpu/drm/drm_ioc32.c @@ -388,6 +388,7 @@ static int drm_legacy_infobufs32(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_buf_info32_t *request = data; + return __drm_legacy_infobufs(dev, data, &request->count, copy_one_buf32); } @@ -813,6 +814,7 @@ static int compat_drm_update_draw(struct file *file, unsigned int cmd, unsigned long arg) { drm_update_draw32_t update32; + if (copy_from_user(&update32, (void __user *)arg, sizeof(update32))) return -EFAULT; diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c index 825abe38201a..da4f085fc09e 100644 --- a/drivers/gpu/drm/drm_lease.c +++ b/drivers/gpu/drm/drm_lease.c @@ -166,8 +166,10 @@ uint32_t drm_lease_filter_crtcs(struct drm_file *file_priv, uint32_t crtcs_in) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (_drm_lease_held_master(master, crtc->base.id)) { uint32_t mask_in = 1ul << count_in; + if ((crtcs_in & mask_in) != 0) { uint32_t mask_out = 1ul << count_out; + crtcs_out |= mask_out; } count_out++; @@ -423,6 +425,7 @@ static int fill_object_idr(struct drm_device *dev, for (o = 0; o < object_count; o++) { struct drm_mode_object *obj = objects[o]; u32 object_id = objects[o]->id; + DRM_DEBUG_LEASE("Adding object %d to lease\n", object_id); /* @@ -441,6 +444,7 @@ static int fill_object_idr(struct drm_device *dev, } if (obj->type == DRM_MODE_OBJECT_CRTC && !universal_planes) { struct drm_crtc *crtc = obj_to_crtc(obj); + ret = idr_alloc(leases, &drm_lease_idr_object, crtc->primary->base.id, crtc->primary->base.id + 1, GFP_KERNEL); if (ret < 0) { DRM_DEBUG_LEASE("Object primary plane %d cannot be inserted into leases (%d)\n", diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c index f16eefbf2829..1efbd5389d89 100644 --- a/drivers/gpu/drm/drm_lock.c +++ b/drivers/gpu/drm/drm_lock.c @@ -330,6 +330,7 @@ static int drm_legacy_i_have_hw_lock(struct drm_device *dev, struct drm_file *file_priv) { struct drm_master *master = file_priv->master; + return (file_priv->lock_count && master->lock.hw_lock && _DRM_LOCK_IS_HELD(master->lock.hw_lock->lock) && master->lock.file_priv == file_priv); diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index 79532b9a324a..ef01bd0a13e2 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -225,9 +225,8 @@ int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, drm_fb_xrgb8888_to_rgb565(dst, src, fb, clip, swap); break; default: - dev_err_once(fb->dev->dev, "Format is not supported: %s\n", - drm_get_format_name(fb->format->format, - &format_name)); + drm_err_once(fb->dev, "Format is not supported: %s\n", + drm_get_format_name(fb->format->format, &format_name)); return -EINVAL; } @@ -295,7 +294,7 @@ static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) width * height * 2); err_msg: if (ret) - dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret); + drm_err_once(fb->dev, "Failed to update display %d\n", ret); drm_dev_exit(idx); } diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 55531895dde6..5dd475e82995 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -34,6 +34,7 @@ #include <linux/slab.h> #include <drm/drm_dsc.h> +#include <drm/drm_print.h> #include <video/mipi_display.h> /** @@ -155,19 +156,18 @@ static int mipi_dsi_device_add(struct mipi_dsi_device *dsi) static struct mipi_dsi_device * of_mipi_dsi_device_add(struct mipi_dsi_host *host, struct device_node *node) { - struct device *dev = host->dev; struct mipi_dsi_device_info info = { }; int ret; u32 reg; if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) { - dev_err(dev, "modalias failure on %pOF\n", node); + drm_err(host, "modalias failure on %pOF\n", node); return ERR_PTR(-EINVAL); } ret = of_property_read_u32(node, "reg", ®); if (ret) { - dev_err(dev, "device node %pOF has no valid reg property: %d\n", + drm_err(host, "device node %pOF has no valid reg property: %d\n", node, ret); return ERR_PTR(-EINVAL); } @@ -202,22 +202,21 @@ mipi_dsi_device_register_full(struct mipi_dsi_host *host, const struct mipi_dsi_device_info *info) { struct mipi_dsi_device *dsi; - struct device *dev = host->dev; int ret; if (!info) { - dev_err(dev, "invalid mipi_dsi_device_info pointer\n"); + drm_err(host, "invalid mipi_dsi_device_info pointer\n"); return ERR_PTR(-EINVAL); } if (info->channel > 3) { - dev_err(dev, "invalid virtual channel: %u\n", info->channel); + drm_err(host, "invalid virtual channel: %u\n", info->channel); return ERR_PTR(-EINVAL); } dsi = mipi_dsi_device_alloc(host); if (IS_ERR(dsi)) { - dev_err(dev, "failed to allocate DSI device %ld\n", + drm_err(host, "failed to allocate DSI device %ld\n", PTR_ERR(dsi)); return dsi; } @@ -228,7 +227,7 @@ mipi_dsi_device_register_full(struct mipi_dsi_host *host, ret = mipi_dsi_device_add(dsi); if (ret) { - dev_err(dev, "failed to add DSI device %d\n", ret); + drm_err(host, "failed to add DSI device %d\n", ret); kfree(dsi); return ERR_PTR(ret); } @@ -748,26 +747,26 @@ ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd, { ssize_t err; size_t size; + u8 stack_tx[8]; u8 *tx; - if (len > 0) { - size = 1 + len; - + size = 1 + len; + if (len > ARRAY_SIZE(stack_tx) - 1) { tx = kmalloc(size, GFP_KERNEL); if (!tx) return -ENOMEM; - - /* concatenate the DCS command byte and the payload */ - tx[0] = cmd; - memcpy(&tx[1], data, len); } else { - tx = &cmd; - size = 1; + tx = stack_tx; } + /* concatenate the DCS command byte and the payload */ + tx[0] = cmd; + if (data) + memcpy(&tx[1], data, len); + err = mipi_dsi_dcs_write_buffer(dsi, tx, size); - if (len > 0) + if (tx != stack_tx) kfree(tx); return err; @@ -1082,11 +1081,11 @@ EXPORT_SYMBOL(mipi_dsi_dcs_set_pixel_format); */ int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline) { - u8 payload[3] = { MIPI_DCS_SET_TEAR_SCANLINE, scanline >> 8, - scanline & 0xff }; + u8 payload[2] = { scanline >> 8, scanline & 0xff }; ssize_t err; - err = mipi_dsi_generic_write(dsi, payload, sizeof(payload)); + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_SCANLINE, payload, + sizeof(payload)); if (err < 0) return err; diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 5761f838a057..f1affc1bb679 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -538,6 +538,7 @@ void drm_mode_config_cleanup(struct drm_device *dev) WARN_ON(!list_empty(&dev->mode_config.fb_list)); list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { struct drm_printer p = drm_debug_printer("[leaked fb]"); + drm_printf(&p, "framebuffer[%u]:\n", fb->base.id); drm_framebuffer_print_info(&p, 1, fb); drm_framebuffer_free(&fb->base.refcount); diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index f2865f88bd54..14b6f7638728 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -548,7 +548,7 @@ EXPORT_SYMBOL(drm_gtf_mode_complex); * Generalized Timing Formula is derived from: * * GTF Spreadsheet by Andy Morrish (1/5/97) - * available at http://www.vesa.org + * available at https://www.vesa.org * * And it is copied from the file of xserver/hw/xfree86/modes/xf86gtf.c. * What I have done is to translate it by using integer calculation. diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index b50b44e76279..fdb05fbf72a0 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -246,6 +246,15 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, if (panel) *panel = NULL; + /* + * of_graph_get_remote_node() produces a noisy error message if port + * node isn't found and the absence of the port is a legit case here, + * so at first we silently check whether graph presents in the + * device-tree node. + */ + if (!of_graph_is_present(np)) + return -ENODEV; + remote = of_graph_get_remote_node(np, port, endpoint); if (!remote) return -ENODEV; diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 75e2b7053f35..c250fb5a88ca 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -298,6 +298,7 @@ EXPORT_SYMBOL(drm_legacy_pci_init); void drm_legacy_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver) { struct drm_device *dev, *tmp; + DRM_DEBUG("\n"); if (!(driver->driver_features & DRIVER_LEGACY)) { diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index 4af173ced327..b7b90b3a2e38 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -216,6 +216,7 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, if (format_modifiers) { const uint64_t *temp_modifiers = format_modifiers; + while (*temp_modifiers++ != DRM_FORMAT_MOD_INVALID) format_modifier_count++; } diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index bbfc713bfdc3..1693aa7c14b5 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -1014,6 +1014,7 @@ void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg) { struct dma_buf_attachment *attach; struct dma_buf *dma_buf; + attach = obj->import_attach; if (sg) dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 09e872e61315..e0ed58d291ed 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -292,6 +292,9 @@ retry: if (WARN_ON(ret < 0)) ret = connector_status_unknown; + if (ret != connector->status) + connector->epoch_counter += 1; + drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); @@ -325,11 +328,16 @@ drm_helper_probe_detect(struct drm_connector *connector, return ret; if (funcs->detect_ctx) - return funcs->detect_ctx(connector, ctx, force); + ret = funcs->detect_ctx(connector, ctx, force); else if (connector->funcs->detect) - return connector->funcs->detect(connector, force); + ret = connector->funcs->detect(connector, force); else - return connector_status_connected; + ret = connector_status_connected; + + if (ret != connector->status) + connector->epoch_counter += 1; + + return ret; } EXPORT_SYMBOL(drm_helper_probe_detect); @@ -779,6 +787,7 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) struct drm_connector_list_iter conn_iter; enum drm_connector_status old_status; bool changed = false; + u64 old_epoch_counter; if (!dev->mode_config.poll_enabled) return false; @@ -792,20 +801,39 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) old_status = connector->status; + old_epoch_counter = connector->epoch_counter; + + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] Old epoch counter %llu\n", connector->base.id, + connector->name, + old_epoch_counter); + connector->status = drm_helper_probe_detect(connector, NULL, false); DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n", connector->base.id, connector->name, drm_get_connector_status_name(old_status), drm_get_connector_status_name(connector->status)); - if (old_status != connector->status) + + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] New epoch counter %llu\n", + connector->base.id, + connector->name, + connector->epoch_counter); + + /* + * Check if epoch counter had changed, meaning that we need + * to send a uevent. + */ + if (old_epoch_counter != connector->epoch_counter) changed = true; + } drm_connector_list_iter_end(&conn_iter); mutex_unlock(&dev->mode_config.mutex); - if (changed) + if (changed) { drm_kms_helper_hotplug_event(dev); + DRM_DEBUG_KMS("Sent hotplug event\n"); + } return changed; } diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index 42d46414f767..3bf73971daf3 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -1188,6 +1188,7 @@ static void drm_syncobj_array_free(struct drm_syncobj **syncobjs, uint32_t count) { uint32_t i; + for (i = 0; i < count; i++) drm_syncobj_put(syncobjs[i]); kfree(syncobjs); diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 85e5f2db1608..b18e1efbbae1 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -25,6 +25,7 @@ */ #include <linux/export.h> +#include <linux/kthread.h> #include <linux/moduleparam.h> #include <drm/drm_crtc.h> @@ -363,7 +364,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, store_vblank(dev, pipe, diff, t_vblank, cur_vblank); } -static u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe) +u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; u64 count; @@ -492,16 +493,13 @@ static void vblank_disable_fn(struct timer_list *t) static void drm_vblank_init_release(struct drm_device *dev, void *ptr) { - unsigned int pipe; - - for (pipe = 0; pipe < dev->num_crtcs; pipe++) { - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + struct drm_vblank_crtc *vblank = ptr; - drm_WARN_ON(dev, READ_ONCE(vblank->enabled) && - drm_core_check_feature(dev, DRIVER_MODESET)); + drm_WARN_ON(dev, READ_ONCE(vblank->enabled) && + drm_core_check_feature(dev, DRIVER_MODESET)); - del_timer_sync(&vblank->disable_timer); - } + drm_vblank_destroy_worker(vblank); + del_timer_sync(&vblank->disable_timer); } /** @@ -511,7 +509,7 @@ static void drm_vblank_init_release(struct drm_device *dev, void *ptr) * * This function initializes vblank support for @num_crtcs display pipelines. * Cleanup is handled automatically through a cleanup function added with - * drmm_add_action(). + * drmm_add_action_or_reset(). * * Returns: * Zero on success or a negative error code on failure. @@ -530,10 +528,6 @@ int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs) dev->num_crtcs = num_crtcs; - ret = drmm_add_action(dev, drm_vblank_init_release, NULL); - if (ret) - return ret; - for (i = 0; i < num_crtcs; i++) { struct drm_vblank_crtc *vblank = &dev->vblank[i]; @@ -542,6 +536,15 @@ int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs) init_waitqueue_head(&vblank->queue); timer_setup(&vblank->disable_timer, vblank_disable_fn, 0); seqlock_init(&vblank->seqlock); + + ret = drmm_add_action_or_reset(dev, drm_vblank_init_release, + vblank); + if (ret) + return ret; + + ret = drm_vblank_worker_init(vblank); + if (ret) + return ret; } return 0; @@ -1138,7 +1141,7 @@ static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe) return ret; } -static int drm_vblank_get(struct drm_device *dev, unsigned int pipe) +int drm_vblank_get(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; unsigned long irqflags; @@ -1181,7 +1184,7 @@ int drm_crtc_vblank_get(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_crtc_vblank_get); -static void drm_vblank_put(struct drm_device *dev, unsigned int pipe) +void drm_vblank_put(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; @@ -1284,15 +1287,17 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc) unsigned int pipe = drm_crtc_index(crtc); struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; struct drm_pending_vblank_event *e, *t; - ktime_t now; - unsigned long irqflags; u64 seq; if (drm_WARN_ON(dev, pipe >= dev->num_crtcs)) return; - spin_lock_irqsave(&dev->event_lock, irqflags); + /* + * Grab event_lock early to prevent vblank work from being scheduled + * while we're in the middle of shutting down vblank interrupts + */ + spin_lock_irq(&dev->event_lock); spin_lock(&dev->vbl_lock); drm_dbg_vbl(dev, "crtc %d, vblank enabled %d, inmodeset %d\n", @@ -1328,11 +1333,18 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc) drm_vblank_put(dev, pipe); send_vblank_event(dev, e, seq, now); } - spin_unlock_irqrestore(&dev->event_lock, irqflags); + + /* Cancel any leftover pending vblank work */ + drm_vblank_cancel_pending_works(vblank); + + spin_unlock_irq(&dev->event_lock); /* Will be reset by the modeset helpers when re-enabling the crtc by * calling drm_calc_timestamping_constants(). */ vblank->hwmode.crtc_clock = 0; + + /* Wait for any vblank work that's still executing to finish */ + drm_vblank_flush_worker(vblank); } EXPORT_SYMBOL(drm_crtc_vblank_off); @@ -1351,11 +1363,10 @@ EXPORT_SYMBOL(drm_crtc_vblank_off); void drm_crtc_vblank_reset(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - unsigned long irqflags; unsigned int pipe = drm_crtc_index(crtc); struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - spin_lock_irqsave(&dev->vbl_lock, irqflags); + spin_lock_irq(&dev->vbl_lock); /* * Prevent subsequent drm_vblank_get() from enabling the vblank * interrupt by bumping the refcount. @@ -1364,9 +1375,10 @@ void drm_crtc_vblank_reset(struct drm_crtc *crtc) atomic_inc(&vblank->refcount); vblank->inmodeset = 1; } - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + spin_unlock_irq(&dev->vbl_lock); drm_WARN_ON(dev, !list_empty(&dev->vblank_event_list)); + drm_WARN_ON(dev, !list_empty(&vblank->pending_work)); } EXPORT_SYMBOL(drm_crtc_vblank_reset); @@ -1416,12 +1428,11 @@ void drm_crtc_vblank_on(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; unsigned int pipe = drm_crtc_index(crtc); struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; if (drm_WARN_ON(dev, pipe >= dev->num_crtcs)) return; - spin_lock_irqsave(&dev->vbl_lock, irqflags); + spin_lock_irq(&dev->vbl_lock); drm_dbg_vbl(dev, "crtc %d, vblank enabled %d, inmodeset %d\n", pipe, vblank->enabled, vblank->inmodeset); @@ -1439,7 +1450,7 @@ void drm_crtc_vblank_on(struct drm_crtc *crtc) */ if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0) drm_WARN_ON(dev, drm_vblank_enable(dev, pipe)); - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + spin_unlock_irq(&dev->vbl_lock); } EXPORT_SYMBOL(drm_crtc_vblank_on); @@ -1540,7 +1551,6 @@ static void drm_legacy_vblank_post_modeset(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; /* vblank is not initialized (IRQ not installed ?), or has been freed */ if (!drm_dev_has_vblank(dev)) @@ -1550,9 +1560,9 @@ static void drm_legacy_vblank_post_modeset(struct drm_device *dev, return; if (vblank->inmodeset) { - spin_lock_irqsave(&dev->vbl_lock, irqflags); + spin_lock_irq(&dev->vbl_lock); drm_reset_vblank_timestamp(dev, pipe); - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + spin_unlock_irq(&dev->vbl_lock); if (vblank->inmodeset & 0x2) drm_vblank_put(dev, pipe); @@ -1593,11 +1603,6 @@ int drm_legacy_modeset_ctl_ioctl(struct drm_device *dev, void *data, return 0; } -static inline bool vblank_passed(u64 seq, u64 ref) -{ - return (seq - ref) <= (1 << 23); -} - static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, u64 req_seq, union drm_wait_vblank *vblwait, @@ -1606,7 +1611,6 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; struct drm_pending_vblank_event *e; ktime_t now; - unsigned long flags; u64 seq; int ret; @@ -1623,11 +1627,12 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, e->event.vbl.crtc_id = 0; if (drm_core_check_feature(dev, DRIVER_MODESET)) { struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); + if (crtc) e->event.vbl.crtc_id = crtc->base.id; } - spin_lock_irqsave(&dev->event_lock, flags); + spin_lock_irq(&dev->event_lock); /* * drm_crtc_vblank_off() might have been called after we called @@ -1654,7 +1659,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, trace_drm_vblank_event_queued(file_priv, pipe, req_seq); e->sequence = req_seq; - if (vblank_passed(seq, req_seq)) { + if (drm_vblank_passed(seq, req_seq)) { drm_vblank_put(dev, pipe); send_vblank_event(dev, e, seq, now); vblwait->reply.sequence = seq; @@ -1664,12 +1669,12 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, vblwait->reply.sequence = req_seq; } - spin_unlock_irqrestore(&dev->event_lock, flags); + spin_unlock_irq(&dev->event_lock); return 0; err_unlock: - spin_unlock_irqrestore(&dev->event_lock, flags); + spin_unlock_irq(&dev->event_lock); kfree(e); err_put: drm_vblank_put(dev, pipe); @@ -1809,7 +1814,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, } if ((flags & _DRM_VBLANK_NEXTONMISS) && - vblank_passed(seq, req_seq)) { + drm_vblank_passed(seq, req_seq)) { req_seq = seq + 1; vblwait->request.type &= ~_DRM_VBLANK_NEXTONMISS; vblwait->request.sequence = req_seq; @@ -1828,7 +1833,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, drm_dbg_core(dev, "waiting on vblank count %llu, crtc %u\n", req_seq, pipe); wait = wait_event_interruptible_timeout(vblank->queue, - vblank_passed(drm_vblank_count(dev, pipe), req_seq) || + drm_vblank_passed(drm_vblank_count(dev, pipe), req_seq) || !READ_ONCE(vblank->enabled), msecs_to_jiffies(3000)); @@ -1877,7 +1882,7 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe) list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { if (e->pipe != pipe) continue; - if (!vblank_passed(seq, e->sequence)) + if (!drm_vblank_passed(seq, e->sequence)) continue; drm_dbg_core(dev, "vblank event on %llu, current %llu\n", @@ -1947,6 +1952,7 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe) !atomic_read(&vblank->refcount)); drm_handle_vblank_events(dev, pipe); + drm_handle_vblank_works(vblank); spin_unlock_irqrestore(&dev->event_lock, irqflags); @@ -2060,7 +2066,6 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, u64 seq; u64 req_seq; int ret; - unsigned long spin_flags; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EOPNOTSUPP; @@ -2100,7 +2105,7 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, if (flags & DRM_CRTC_SEQUENCE_RELATIVE) req_seq += seq; - if ((flags & DRM_CRTC_SEQUENCE_NEXT_ON_MISS) && vblank_passed(seq, req_seq)) + if ((flags & DRM_CRTC_SEQUENCE_NEXT_ON_MISS) && drm_vblank_passed(seq, req_seq)) req_seq = seq + 1; e->pipe = pipe; @@ -2108,7 +2113,7 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, e->event.base.length = sizeof(e->event.seq); e->event.seq.user_data = queue_seq->user_data; - spin_lock_irqsave(&dev->event_lock, spin_flags); + spin_lock_irq(&dev->event_lock); /* * drm_crtc_vblank_off() might have been called after we called @@ -2129,7 +2134,7 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, e->sequence = req_seq; - if (vblank_passed(seq, req_seq)) { + if (drm_vblank_passed(seq, req_seq)) { drm_crtc_vblank_put(crtc); send_vblank_event(dev, e, seq, now); queue_seq->sequence = seq; @@ -2139,13 +2144,14 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, queue_seq->sequence = req_seq; } - spin_unlock_irqrestore(&dev->event_lock, spin_flags); + spin_unlock_irq(&dev->event_lock); return 0; err_unlock: - spin_unlock_irqrestore(&dev->event_lock, spin_flags); + spin_unlock_irq(&dev->event_lock); drm_crtc_vblank_put(crtc); err_free: kfree(e); return ret; } + diff --git a/drivers/gpu/drm/drm_vblank_work.c b/drivers/gpu/drm/drm_vblank_work.c new file mode 100644 index 000000000000..7ac0fc0a9415 --- /dev/null +++ b/drivers/gpu/drm/drm_vblank_work.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: MIT + +#include <uapi/linux/sched/types.h> + +#include <drm/drm_print.h> +#include <drm/drm_vblank.h> +#include <drm/drm_vblank_work.h> +#include <drm/drm_crtc.h> + +#include "drm_internal.h" + +/** + * DOC: vblank works + * + * Many DRM drivers need to program hardware in a time-sensitive manner, many + * times with a deadline of starting and finishing within a certain region of + * the scanout. Most of the time the safest way to accomplish this is to + * simply do said time-sensitive programming in the driver's IRQ handler, + * which allows drivers to avoid being preempted during these critical + * regions. Or even better, the hardware may even handle applying such + * time-critical programming independently of the CPU. + * + * While there's a decent amount of hardware that's designed so that the CPU + * doesn't need to be concerned with extremely time-sensitive programming, + * there's a few situations where it can't be helped. Some unforgiving + * hardware may require that certain time-sensitive programming be handled + * completely by the CPU, and said programming may even take too long to + * handle in an IRQ handler. Another such situation would be where the driver + * needs to perform a task that needs to complete within a specific scanout + * period, but might possibly block and thus cannot be handled in an IRQ + * context. Both of these situations can't be solved perfectly in Linux since + * we're not a realtime kernel, and thus the scheduler may cause us to miss + * our deadline if it decides to preempt us. But for some drivers, it's good + * enough if we can lower our chance of being preempted to an absolute + * minimum. + * + * This is where &drm_vblank_work comes in. &drm_vblank_work provides a simple + * generic delayed work implementation which delays work execution until a + * particular vblank has passed, and then executes the work at realtime + * priority. This provides the best possible chance at performing + * time-sensitive hardware programming on time, even when the system is under + * heavy load. &drm_vblank_work also supports rescheduling, so that self + * re-arming work items can be easily implemented. + */ + +void drm_handle_vblank_works(struct drm_vblank_crtc *vblank) +{ + struct drm_vblank_work *work, *next; + u64 count = atomic64_read(&vblank->count); + bool wake = false; + + assert_spin_locked(&vblank->dev->event_lock); + + list_for_each_entry_safe(work, next, &vblank->pending_work, node) { + if (!drm_vblank_passed(count, work->count)) + continue; + + list_del_init(&work->node); + drm_vblank_put(vblank->dev, vblank->pipe); + kthread_queue_work(vblank->worker, &work->base); + wake = true; + } + if (wake) + wake_up_all(&vblank->work_wait_queue); +} + +/* Handle cancelling any pending vblank work items and drop respective vblank + * references in response to vblank interrupts being disabled. + */ +void drm_vblank_cancel_pending_works(struct drm_vblank_crtc *vblank) +{ + struct drm_vblank_work *work, *next; + + assert_spin_locked(&vblank->dev->event_lock); + + list_for_each_entry_safe(work, next, &vblank->pending_work, node) { + list_del_init(&work->node); + drm_vblank_put(vblank->dev, vblank->pipe); + } + + wake_up_all(&vblank->work_wait_queue); +} + +/** + * drm_vblank_work_schedule - schedule a vblank work + * @work: vblank work to schedule + * @count: target vblank count + * @nextonmiss: defer until the next vblank if target vblank was missed + * + * Schedule @work for execution once the crtc vblank count reaches @count. + * + * If the crtc vblank count has already reached @count and @nextonmiss is + * %false the work starts to execute immediately. + * + * If the crtc vblank count has already reached @count and @nextonmiss is + * %true the work is deferred until the next vblank (as if @count has been + * specified as crtc vblank count + 1). + * + * If @work is already scheduled, this function will reschedule said work + * using the new @count. This can be used for self-rearming work items. + * + * Returns: + * %1 if @work was successfully (re)scheduled, %0 if it was either already + * scheduled or cancelled, or a negative error code on failure. + */ +int drm_vblank_work_schedule(struct drm_vblank_work *work, + u64 count, bool nextonmiss) +{ + struct drm_vblank_crtc *vblank = work->vblank; + struct drm_device *dev = vblank->dev; + u64 cur_vbl; + unsigned long irqflags; + bool passed, inmodeset, rescheduling = false, wake = false; + int ret = 0; + + spin_lock_irqsave(&dev->event_lock, irqflags); + if (work->cancelling) + goto out; + + spin_lock(&dev->vbl_lock); + inmodeset = vblank->inmodeset; + spin_unlock(&dev->vbl_lock); + if (inmodeset) + goto out; + + if (list_empty(&work->node)) { + ret = drm_vblank_get(dev, vblank->pipe); + if (ret < 0) + goto out; + } else if (work->count == count) { + /* Already scheduled w/ same vbl count */ + goto out; + } else { + rescheduling = true; + } + + work->count = count; + cur_vbl = drm_vblank_count(dev, vblank->pipe); + passed = drm_vblank_passed(cur_vbl, count); + if (passed) + drm_dbg_core(dev, + "crtc %d vblank %llu already passed (current %llu)\n", + vblank->pipe, count, cur_vbl); + + if (!nextonmiss && passed) { + drm_vblank_put(dev, vblank->pipe); + ret = kthread_queue_work(vblank->worker, &work->base); + + if (rescheduling) { + list_del_init(&work->node); + wake = true; + } + } else { + if (!rescheduling) + list_add_tail(&work->node, &vblank->pending_work); + ret = true; + } + +out: + spin_unlock_irqrestore(&dev->event_lock, irqflags); + if (wake) + wake_up_all(&vblank->work_wait_queue); + return ret; +} +EXPORT_SYMBOL(drm_vblank_work_schedule); + +/** + * drm_vblank_work_cancel_sync - cancel a vblank work and wait for it to + * finish executing + * @work: vblank work to cancel + * + * Cancel an already scheduled vblank work and wait for its + * execution to finish. + * + * On return, @work is guaranteed to no longer be scheduled or running, even + * if it's self-arming. + * + * Returns: + * %True if the work was cancelled before it started to execute, %false + * otherwise. + */ +bool drm_vblank_work_cancel_sync(struct drm_vblank_work *work) +{ + struct drm_vblank_crtc *vblank = work->vblank; + struct drm_device *dev = vblank->dev; + bool ret = false; + + spin_lock_irq(&dev->event_lock); + if (!list_empty(&work->node)) { + list_del_init(&work->node); + drm_vblank_put(vblank->dev, vblank->pipe); + ret = true; + } + + work->cancelling++; + spin_unlock_irq(&dev->event_lock); + + wake_up_all(&vblank->work_wait_queue); + + if (kthread_cancel_work_sync(&work->base)) + ret = true; + + spin_lock_irq(&dev->event_lock); + work->cancelling--; + spin_unlock_irq(&dev->event_lock); + + return ret; +} +EXPORT_SYMBOL(drm_vblank_work_cancel_sync); + +/** + * drm_vblank_work_flush - wait for a scheduled vblank work to finish + * executing + * @work: vblank work to flush + * + * Wait until @work has finished executing once. + */ +void drm_vblank_work_flush(struct drm_vblank_work *work) +{ + struct drm_vblank_crtc *vblank = work->vblank; + struct drm_device *dev = vblank->dev; + + spin_lock_irq(&dev->event_lock); + wait_event_lock_irq(vblank->work_wait_queue, list_empty(&work->node), + dev->event_lock); + spin_unlock_irq(&dev->event_lock); + + kthread_flush_work(&work->base); +} +EXPORT_SYMBOL(drm_vblank_work_flush); + +/** + * drm_vblank_work_init - initialize a vblank work item + * @work: vblank work item + * @crtc: CRTC whose vblank will trigger the work execution + * @func: work function to be executed + * + * Initialize a vblank work item for a specific crtc. + */ +void drm_vblank_work_init(struct drm_vblank_work *work, struct drm_crtc *crtc, + void (*func)(struct kthread_work *work)) +{ + kthread_init_work(&work->base, func); + INIT_LIST_HEAD(&work->node); + work->vblank = &crtc->dev->vblank[drm_crtc_index(crtc)]; +} +EXPORT_SYMBOL(drm_vblank_work_init); + +int drm_vblank_worker_init(struct drm_vblank_crtc *vblank) +{ + struct sched_param param = { + .sched_priority = MAX_RT_PRIO - 1, + }; + struct kthread_worker *worker; + + INIT_LIST_HEAD(&vblank->pending_work); + init_waitqueue_head(&vblank->work_wait_queue); + worker = kthread_create_worker(0, "card%d-crtc%d", + vblank->dev->primary->index, + vblank->pipe); + if (IS_ERR(worker)) + return PTR_ERR(worker); + + vblank->worker = worker; + + return sched_setscheduler(vblank->worker->task, SCHED_FIFO, ¶m); +} diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index f5e5bb8ba953..f06e19e7be04 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -675,10 +675,10 @@ static int etnaviv_gem_userptr_get_pages(struct etnaviv_gem_object *etnaviv_obj) uint64_t ptr = userptr->ptr + pinned * PAGE_SIZE; struct page **pages = pvec + pinned; - ret = get_user_pages_fast(ptr, num_pages, + ret = pin_user_pages_fast(ptr, num_pages, !userptr->ro ? FOLL_WRITE : 0, pages); if (ret < 0) { - release_pages(pvec, pinned); + unpin_user_pages(pvec, pinned); kvfree(pvec); return ret; } @@ -702,7 +702,7 @@ static void etnaviv_gem_userptr_release(struct etnaviv_gem_object *etnaviv_obj) if (etnaviv_obj->pages) { int npages = etnaviv_obj->base.size >> PAGE_SHIFT; - release_pages(etnaviv_obj->pages, npages); + unpin_user_pages(etnaviv_obj->pages, npages); kvfree(etnaviv_obj->pages); } } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index a31eeff2b297..d5a4cd85a0f6 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -722,7 +722,7 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) ret = pm_runtime_get_sync(gpu->dev); if (ret < 0) { dev_err(gpu->dev, "Failed to enable GPU power domain\n"); - return ret; + goto pm_put; } etnaviv_hw_identify(gpu); @@ -819,6 +819,7 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) fail: pm_runtime_mark_last_busy(gpu->dev); +pm_put: pm_runtime_put_autosuspend(gpu->dev); return ret; @@ -859,7 +860,7 @@ int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m) ret = pm_runtime_get_sync(gpu->dev); if (ret < 0) - return ret; + goto pm_put; dma_lo = gpu_read(gpu, VIVS_FE_DMA_LOW); dma_hi = gpu_read(gpu, VIVS_FE_DMA_HIGH); @@ -1003,6 +1004,7 @@ int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m) ret = 0; pm_runtime_mark_last_busy(gpu->dev); +pm_put: pm_runtime_put_autosuspend(gpu->dev); return ret; @@ -1016,7 +1018,7 @@ void etnaviv_gpu_recover_hang(struct etnaviv_gpu *gpu) dev_err(gpu->dev, "recover hung GPU!\n"); if (pm_runtime_get_sync(gpu->dev) < 0) - return; + goto pm_put; mutex_lock(&gpu->lock); @@ -1035,6 +1037,7 @@ void etnaviv_gpu_recover_hang(struct etnaviv_gpu *gpu) mutex_unlock(&gpu->lock); pm_runtime_mark_last_busy(gpu->dev); +pm_put: pm_runtime_put_autosuspend(gpu->dev); } @@ -1308,8 +1311,10 @@ struct dma_fence *etnaviv_gpu_submit(struct etnaviv_gem_submit *submit) if (!submit->runtime_resumed) { ret = pm_runtime_get_sync(gpu->dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_noidle(gpu->dev); return NULL; + } submit->runtime_resumed = true; } @@ -1326,6 +1331,7 @@ struct dma_fence *etnaviv_gpu_submit(struct etnaviv_gem_submit *submit) ret = event_alloc(gpu, nr_events, event); if (ret) { DRM_ERROR("no free events\n"); + pm_runtime_put_noidle(gpu->dev); return NULL; } @@ -1487,52 +1493,40 @@ static int etnaviv_gpu_clk_enable(struct etnaviv_gpu *gpu) { int ret; - if (gpu->clk_reg) { - ret = clk_prepare_enable(gpu->clk_reg); - if (ret) - return ret; - } + ret = clk_prepare_enable(gpu->clk_reg); + if (ret) + return ret; - if (gpu->clk_bus) { - ret = clk_prepare_enable(gpu->clk_bus); - if (ret) - return ret; - } + ret = clk_prepare_enable(gpu->clk_bus); + if (ret) + goto disable_clk_reg; - if (gpu->clk_core) { - ret = clk_prepare_enable(gpu->clk_core); - if (ret) - goto disable_clk_bus; - } + 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; - } + 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); + clk_disable_unprepare(gpu->clk_core); disable_clk_bus: - if (gpu->clk_bus) - clk_disable_unprepare(gpu->clk_bus); + clk_disable_unprepare(gpu->clk_bus); +disable_clk_reg: + clk_disable_unprepare(gpu->clk_reg); return ret; } static int etnaviv_gpu_clk_disable(struct etnaviv_gpu *gpu) { - 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); - if (gpu->clk_reg) - clk_disable_unprepare(gpu->clk_reg); + clk_disable_unprepare(gpu->clk_shader); + clk_disable_unprepare(gpu->clk_core); + clk_disable_unprepare(gpu->clk_bus); + clk_disable_unprepare(gpu->clk_reg); return 0; } @@ -1783,26 +1777,26 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) } /* Get Clocks: */ - gpu->clk_reg = devm_clk_get(&pdev->dev, "reg"); + gpu->clk_reg = devm_clk_get_optional(&pdev->dev, "reg"); DBG("clk_reg: %p", gpu->clk_reg); if (IS_ERR(gpu->clk_reg)) - gpu->clk_reg = NULL; + return PTR_ERR(gpu->clk_reg); - gpu->clk_bus = devm_clk_get(&pdev->dev, "bus"); + gpu->clk_bus = devm_clk_get_optional(&pdev->dev, "bus"); DBG("clk_bus: %p", gpu->clk_bus); if (IS_ERR(gpu->clk_bus)) - gpu->clk_bus = NULL; + return PTR_ERR(gpu->clk_bus); gpu->clk_core = devm_clk_get(&pdev->dev, "core"); DBG("clk_core: %p", gpu->clk_core); if (IS_ERR(gpu->clk_core)) - gpu->clk_core = NULL; + return PTR_ERR(gpu->clk_core); gpu->base_rate_core = clk_get_rate(gpu->clk_core); - gpu->clk_shader = devm_clk_get(&pdev->dev, "shader"); + gpu->clk_shader = devm_clk_get_optional(&pdev->dev, "shader"); DBG("clk_shader: %p", gpu->clk_shader); if (IS_ERR(gpu->clk_shader)) - gpu->clk_shader = NULL; + return PTR_ERR(gpu->clk_shader); gpu->base_rate_shader = clk_get_rate(gpu->clk_shader); /* TODO: figure out max mapped size */ diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index a6fd0c29e5b8..249c298e0987 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -20,6 +20,7 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_gem_vram_helper.h> #include <drm/drm_irq.h> +#include <drm/drm_managed.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> #include <drm/drm_vblank.h> @@ -267,7 +268,7 @@ static int hibmc_load(struct drm_device *dev) struct hibmc_drm_private *priv; int ret; - priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL); + priv = drmm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) { DRM_ERROR("no memory to allocate for hibmc_drm_private\n"); return -ENOMEM; diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h index 50a0c1f9d211..609768748de6 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -29,6 +29,8 @@ struct hibmc_drm_private { /* drm */ struct drm_device *dev; + struct drm_encoder encoder; + struct drm_connector connector; bool mode_config_initialized; }; diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c index 678ac2ef2a93..2ca69c38491a 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c @@ -52,32 +52,6 @@ static const struct drm_connector_funcs hibmc_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static struct drm_connector * -hibmc_connector_init(struct hibmc_drm_private *priv) -{ - struct drm_device *dev = priv->dev; - struct drm_connector *connector; - int ret; - - connector = devm_kzalloc(dev->dev, sizeof(*connector), GFP_KERNEL); - if (!connector) { - DRM_ERROR("failed to alloc memory when init connector\n"); - return ERR_PTR(-ENOMEM); - } - - ret = drm_connector_init(dev, connector, - &hibmc_connector_funcs, - DRM_MODE_CONNECTOR_VGA); - if (ret) { - DRM_ERROR("failed to init connector: %d\n", ret); - return ERR_PTR(ret); - } - drm_connector_helper_add(connector, - &hibmc_connector_helper_funcs); - - return connector; -} - static void hibmc_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adj_mode) @@ -105,23 +79,10 @@ static const struct drm_encoder_funcs hibmc_encoder_funcs = { int hibmc_vdac_init(struct hibmc_drm_private *priv) { struct drm_device *dev = priv->dev; - struct drm_encoder *encoder; - struct drm_connector *connector; + struct drm_encoder *encoder = &priv->encoder; + struct drm_connector *connector = &priv->connector; int ret; - connector = hibmc_connector_init(priv); - if (IS_ERR(connector)) { - DRM_ERROR("failed to create connector: %ld\n", - PTR_ERR(connector)); - return PTR_ERR(connector); - } - - encoder = devm_kzalloc(dev->dev, sizeof(*encoder), GFP_KERNEL); - if (!encoder) { - DRM_ERROR("failed to alloc memory when init encoder\n"); - return -ENOMEM; - } - encoder->possible_crtcs = 0x1; ret = drm_encoder_init(dev, encoder, &hibmc_encoder_funcs, DRM_MODE_ENCODER_DAC, NULL); @@ -131,6 +92,15 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv) } drm_encoder_helper_add(encoder, &hibmc_encoder_helper_funcs); + + ret = drm_connector_init(dev, connector, &hibmc_connector_funcs, + DRM_MODE_CONNECTOR_VGA); + if (ret) { + DRM_ERROR("failed to init connector: %d\n", ret); + return ret; + } + drm_connector_helper_add(connector, &hibmc_connector_helper_funcs); + drm_connector_attach_encoder(connector, encoder); return 0; diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c index b88c3d5f92b4..303c2d483c6e 100644 --- a/drivers/gpu/drm/i810/i810_dma.c +++ b/drivers/gpu/drm/i810/i810_dma.c @@ -220,9 +220,9 @@ static int i810_dma_cleanup(struct drm_device *dev) if (dev_priv->ring.virtual_start) drm_legacy_ioremapfree(&dev_priv->ring.map, dev); if (dev_priv->hw_status_page) { - pci_free_consistent(dev->pdev, PAGE_SIZE, - dev_priv->hw_status_page, - dev_priv->dma_status_page); + dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, + dev_priv->hw_status_page, + dev_priv->dma_status_page); } kfree(dev->dev_private); dev->dev_private = NULL; @@ -398,8 +398,8 @@ static int i810_dma_initialize(struct drm_device *dev, /* Program Hardware Status Page */ dev_priv->hw_status_page = - pci_zalloc_consistent(dev->pdev, PAGE_SIZE, - &dev_priv->dma_status_page); + dma_alloc_coherent(&dev->pdev->dev, PAGE_SIZE, + &dev_priv->dma_status_page, GFP_KERNEL); if (!dev_priv->hw_status_page) { dev->dev_private = (void *)dev_priv; i810_dma_cleanup(dev); diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index b0da6ea6e3f1..41a27fd5dbc7 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -78,6 +78,8 @@ gt-y += \ gt/debugfs_engines.o \ gt/debugfs_gt.o \ gt/debugfs_gt_pm.o \ + gt/gen2_engine_cs.o \ + gt/gen6_engine_cs.o \ gt/gen6_ppgtt.o \ gt/gen7_renderclear.o \ gt/gen8_ppgtt.o \ diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index 4fec5bd64920..8c55f5bee9ab 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -1469,8 +1469,7 @@ static void gen11_dsi_get_config(struct intel_encoder *encoder, pipe_config->pipe_bpp = bdw_get_pipemisc_bpp(crtc); if (gen11_dsi_is_periodic_cmd_mode(intel_dsi)) - pipe_config->hw.adjusted_mode.private_flags |= - I915_MODE_FLAG_DSI_PERIODIC_CMD_MODE; + pipe_config->mode_flags |= I915_MODE_FLAG_DSI_PERIODIC_CMD_MODE; } static int gen11_dsi_dsc_compute_config(struct intel_encoder *encoder, @@ -1558,10 +1557,6 @@ static int gen11_dsi_compute_config(struct intel_encoder *encoder, pipe_config->port_clock = afe_clk(encoder, pipe_config) / 5; - /* We would not operate in periodic command mode */ - pipe_config->hw.adjusted_mode.private_flags &= - ~I915_MODE_FLAG_DSI_PERIODIC_CMD_MODE; - /* * In case of TE GATE cmd mode, we * receive TE from the slave if @@ -1569,14 +1564,14 @@ static int gen11_dsi_compute_config(struct intel_encoder *encoder, */ if (is_cmd_mode(intel_dsi)) { if (intel_dsi->ports == (BIT(PORT_B) | BIT(PORT_A))) - pipe_config->hw.adjusted_mode.private_flags |= + pipe_config->mode_flags |= I915_MODE_FLAG_DSI_USE_TE1 | I915_MODE_FLAG_DSI_USE_TE0; else if (intel_dsi->ports == BIT(PORT_B)) - pipe_config->hw.adjusted_mode.private_flags |= + pipe_config->mode_flags |= I915_MODE_FLAG_DSI_USE_TE1; else - pipe_config->hw.adjusted_mode.private_flags |= + pipe_config->mode_flags |= I915_MODE_FLAG_DSI_USE_TE0; } @@ -1954,6 +1949,7 @@ void icl_dsi_init(struct drm_i915_private *dev_priv) return; err: + drm_connector_cleanup(connector); drm_encoder_cleanup(&encoder->base); kfree(intel_dsi); kfree(intel_connector); diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c index d043057d2fa0..630f49b7aa01 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.c +++ b/drivers/gpu/drm/i915/display/intel_atomic.c @@ -249,9 +249,11 @@ intel_crtc_duplicate_state(struct drm_crtc *crtc) crtc_state->update_wm_post = false; crtc_state->fifo_changed = false; crtc_state->preload_luts = false; + crtc_state->inherited = false; crtc_state->wm.need_postvbl_update = false; crtc_state->fb_bits = 0; crtc_state->update_planes = 0; + crtc_state->dsb = NULL; return &crtc_state->uapi; } @@ -292,6 +294,8 @@ intel_crtc_destroy_state(struct drm_crtc *crtc, { struct intel_crtc_state *crtc_state = to_intel_crtc_state(state); + drm_WARN_ON(crtc->dev, crtc_state->dsb); + __drm_atomic_helper_crtc_destroy_state(&crtc_state->uapi); intel_crtc_free_hw_state(crtc_state); kfree(crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index 839124647202..6593e2c38043 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -479,7 +479,7 @@ parse_sdvo_panel_data(struct drm_i915_private *dev_priv, struct drm_display_mode *panel_fixed_mode; int index; - index = i915_modparams.vbt_sdvo_panel_type; + index = dev_priv->params.vbt_sdvo_panel_type; if (index == -2) { drm_dbg_kms(&dev_priv->drm, "Ignore SDVO panel mode from BIOS VBT tables.\n"); @@ -829,9 +829,9 @@ parse_edp(struct drm_i915_private *dev_priv, const struct bdb_header *bdb) u8 vswing; /* Don't read from VBT if module parameter has valid value*/ - if (i915_modparams.edp_vswing) { + if (dev_priv->params.edp_vswing) { dev_priv->vbt.edp.low_vswing = - i915_modparams.edp_vswing == 1; + dev_priv->params.edp_vswing == 1; } else { vswing = (edp->edp_vswing_preemph >> (panel_type * 4)) & 0xF; dev_priv->vbt.edp.low_vswing = vswing == 0; @@ -1619,30 +1619,18 @@ static u8 map_ddc_pin(struct drm_i915_private *dev_priv, u8 vbt_pin) return 0; } -static enum port dvo_port_to_port(u8 dvo_port) +static enum port __dvo_port_to_port(int n_ports, int n_dvo, + const int port_mapping[][3], u8 dvo_port) { - /* - * Each DDI port can have more than one value on the "DVO Port" field, - * so look for all the possible values for each port. - */ - static const int dvo_ports[][3] = { - [PORT_A] = { DVO_PORT_HDMIA, DVO_PORT_DPA, -1}, - [PORT_B] = { DVO_PORT_HDMIB, DVO_PORT_DPB, -1}, - [PORT_C] = { DVO_PORT_HDMIC, DVO_PORT_DPC, -1}, - [PORT_D] = { DVO_PORT_HDMID, DVO_PORT_DPD, -1}, - [PORT_E] = { DVO_PORT_CRT, DVO_PORT_HDMIE, DVO_PORT_DPE}, - [PORT_F] = { DVO_PORT_HDMIF, DVO_PORT_DPF, -1}, - [PORT_G] = { DVO_PORT_HDMIG, DVO_PORT_DPG, -1}, - }; enum port port; int i; - for (port = PORT_A; port < ARRAY_SIZE(dvo_ports); port++) { - for (i = 0; i < ARRAY_SIZE(dvo_ports[port]); i++) { - if (dvo_ports[port][i] == -1) + for (port = PORT_A; port < n_ports; port++) { + for (i = 0; i < n_dvo; i++) { + if (port_mapping[port][i] == -1) break; - if (dvo_port == dvo_ports[port][i]) + if (dvo_port == port_mapping[port][i]) return port; } } @@ -1650,6 +1638,48 @@ static enum port dvo_port_to_port(u8 dvo_port) return PORT_NONE; } +static enum port dvo_port_to_port(struct drm_i915_private *dev_priv, + u8 dvo_port) +{ + /* + * Each DDI port can have more than one value on the "DVO Port" field, + * so look for all the possible values for each port. + */ + static const int port_mapping[][3] = { + [PORT_A] = { DVO_PORT_HDMIA, DVO_PORT_DPA, -1 }, + [PORT_B] = { DVO_PORT_HDMIB, DVO_PORT_DPB, -1 }, + [PORT_C] = { DVO_PORT_HDMIC, DVO_PORT_DPC, -1 }, + [PORT_D] = { DVO_PORT_HDMID, DVO_PORT_DPD, -1 }, + [PORT_E] = { DVO_PORT_HDMIE, DVO_PORT_DPE, DVO_PORT_CRT }, + [PORT_F] = { DVO_PORT_HDMIF, DVO_PORT_DPF, -1 }, + [PORT_G] = { DVO_PORT_HDMIG, DVO_PORT_DPG, -1 }, + }; + /* + * Bspec lists the ports as A, B, C, D - however internally in our + * driver we keep them as PORT_A, PORT_B, PORT_D and PORT_E so the + * registers in Display Engine match the right offsets. Apply the + * mapping here to translate from VBT to internal convention. + */ + static const int rkl_port_mapping[][3] = { + [PORT_A] = { DVO_PORT_HDMIA, DVO_PORT_DPA, -1 }, + [PORT_B] = { DVO_PORT_HDMIB, DVO_PORT_DPB, -1 }, + [PORT_C] = { -1 }, + [PORT_D] = { DVO_PORT_HDMIC, DVO_PORT_DPC, -1 }, + [PORT_E] = { DVO_PORT_HDMID, DVO_PORT_DPD, -1 }, + }; + + if (IS_ROCKETLAKE(dev_priv)) + return __dvo_port_to_port(ARRAY_SIZE(rkl_port_mapping), + ARRAY_SIZE(rkl_port_mapping[0]), + rkl_port_mapping, + dvo_port); + else + return __dvo_port_to_port(ARRAY_SIZE(port_mapping), + ARRAY_SIZE(port_mapping[0]), + port_mapping, + dvo_port); +} + static void parse_ddi_port(struct drm_i915_private *dev_priv, struct display_device_data *devdata, u8 bdb_version) @@ -1659,7 +1689,7 @@ static void parse_ddi_port(struct drm_i915_private *dev_priv, bool is_dvi, is_hdmi, is_dp, is_edp, is_crt; enum port port; - port = dvo_port_to_port(child->dvo_port); + port = dvo_port_to_port(dev_priv, child->dvo_port); if (port == PORT_NONE) return; @@ -2603,10 +2633,10 @@ enum aux_ch intel_bios_port_aux_ch(struct drm_i915_private *dev_priv, aux_ch = AUX_CH_B; break; case DP_AUX_C: - aux_ch = AUX_CH_C; + aux_ch = IS_ROCKETLAKE(dev_priv) ? AUX_CH_D : AUX_CH_C; break; case DP_AUX_D: - aux_ch = AUX_CH_D; + aux_ch = IS_ROCKETLAKE(dev_priv) ? AUX_CH_E : AUX_CH_D; break; case DP_AUX_E: aux_ch = AUX_CH_E; diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index fef04e2d954e..bd060404d249 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -5,12 +5,12 @@ #include <drm/drm_atomic_state_helper.h> +#include "intel_atomic.h" #include "intel_bw.h" +#include "intel_cdclk.h" #include "intel_display_types.h" -#include "intel_sideband.h" -#include "intel_atomic.h" #include "intel_pm.h" - +#include "intel_sideband.h" /* Parameters for Qclk Geyserville (QGV) */ struct intel_qgv_point { @@ -199,6 +199,12 @@ static const struct intel_sa_info tgl_sa_info = { .displayrtids = 256, }; +static const struct intel_sa_info rkl_sa_info = { + .deburst = 16, + .deprogbwlimit = 20, /* GB/s */ + .displayrtids = 128, +}; + static int icl_get_bw_info(struct drm_i915_private *dev_priv, const struct intel_sa_info *sa) { struct intel_qgv_info qi = {}; @@ -309,7 +315,9 @@ void intel_bw_init_hw(struct drm_i915_private *dev_priv) if (!HAS_DISPLAY(dev_priv)) return; - if (IS_GEN(dev_priv, 12)) + if (IS_ROCKETLAKE(dev_priv)) + icl_get_bw_info(dev_priv, &rkl_sa_info); + else if (IS_GEN(dev_priv, 12)) icl_get_bw_info(dev_priv, &tgl_sa_info); else if (IS_GEN(dev_priv, 11)) icl_get_bw_info(dev_priv, &icl_sa_info); @@ -420,6 +428,141 @@ intel_atomic_get_bw_state(struct intel_atomic_state *state) return to_intel_bw_state(bw_state); } +int skl_bw_calc_min_cdclk(struct intel_atomic_state *state) +{ + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct intel_bw_state *new_bw_state = NULL; + struct intel_bw_state *old_bw_state = NULL; + const struct intel_crtc_state *crtc_state; + struct intel_crtc *crtc; + int max_bw = 0; + int slice_id; + enum pipe pipe; + int i; + + for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) { + enum plane_id plane_id; + struct intel_dbuf_bw *crtc_bw; + + new_bw_state = intel_atomic_get_bw_state(state); + if (IS_ERR(new_bw_state)) + return PTR_ERR(new_bw_state); + + old_bw_state = intel_atomic_get_old_bw_state(state); + + crtc_bw = &new_bw_state->dbuf_bw[crtc->pipe]; + + memset(&crtc_bw->used_bw, 0, sizeof(crtc_bw->used_bw)); + + if (!crtc_state->hw.active) + continue; + + for_each_plane_id_on_crtc(crtc, plane_id) { + const struct skl_ddb_entry *plane_alloc = + &crtc_state->wm.skl.plane_ddb_y[plane_id]; + const struct skl_ddb_entry *uv_plane_alloc = + &crtc_state->wm.skl.plane_ddb_uv[plane_id]; + unsigned int data_rate = crtc_state->data_rate[plane_id]; + unsigned int dbuf_mask = 0; + + dbuf_mask |= skl_ddb_dbuf_slice_mask(dev_priv, plane_alloc); + dbuf_mask |= skl_ddb_dbuf_slice_mask(dev_priv, uv_plane_alloc); + + /* + * FIXME: To calculate that more properly we probably + * need to to split per plane data_rate into data_rate_y + * and data_rate_uv for multiplanar formats in order not + * to get accounted those twice if they happen to reside + * on different slices. + * However for pre-icl this would work anyway because + * we have only single slice and for icl+ uv plane has + * non-zero data rate. + * So in worst case those calculation are a bit + * pessimistic, which shouldn't pose any significant + * problem anyway. + */ + for_each_dbuf_slice_in_mask(slice_id, dbuf_mask) + crtc_bw->used_bw[slice_id] += data_rate; + } + } + + if (!old_bw_state) + return 0; + + for_each_pipe(dev_priv, pipe) { + struct intel_dbuf_bw *crtc_bw; + + crtc_bw = &new_bw_state->dbuf_bw[pipe]; + + for_each_dbuf_slice(slice_id) { + /* + * Current experimental observations show that contrary + * to BSpec we get underruns once we exceed 64 * CDCLK + * for slices in total. + * As a temporary measure in order not to keep CDCLK + * bumped up all the time we calculate CDCLK according + * to this formula for overall bw consumed by slices. + */ + max_bw += crtc_bw->used_bw[slice_id]; + } + } + + new_bw_state->min_cdclk = max_bw / 64; + + if (new_bw_state->min_cdclk != old_bw_state->min_cdclk) { + int ret = intel_atomic_lock_global_state(&new_bw_state->base); + + if (ret) + return ret; + } + + return 0; +} + +int intel_bw_calc_min_cdclk(struct intel_atomic_state *state) +{ + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct intel_bw_state *new_bw_state = NULL; + struct intel_bw_state *old_bw_state = NULL; + const struct intel_crtc_state *crtc_state; + struct intel_crtc *crtc; + int min_cdclk = 0; + enum pipe pipe; + int i; + + for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) { + new_bw_state = intel_atomic_get_bw_state(state); + if (IS_ERR(new_bw_state)) + return PTR_ERR(new_bw_state); + + old_bw_state = intel_atomic_get_old_bw_state(state); + } + + if (!old_bw_state) + return 0; + + for_each_pipe(dev_priv, pipe) { + struct intel_cdclk_state *cdclk_state; + + cdclk_state = intel_atomic_get_new_cdclk_state(state); + if (!cdclk_state) + return 0; + + min_cdclk = max(cdclk_state->min_cdclk[pipe], min_cdclk); + } + + new_bw_state->min_cdclk = min_cdclk; + + if (new_bw_state->min_cdclk != old_bw_state->min_cdclk) { + int ret = intel_atomic_lock_global_state(&new_bw_state->base); + + if (ret) + return ret; + } + + return 0; +} + int intel_bw_atomic_check(struct intel_atomic_state *state) { struct drm_i915_private *dev_priv = to_i915(state->base.dev); diff --git a/drivers/gpu/drm/i915/display/intel_bw.h b/drivers/gpu/drm/i915/display/intel_bw.h index bbcaaa73ec1b..46c6eecbd917 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.h +++ b/drivers/gpu/drm/i915/display/intel_bw.h @@ -9,14 +9,20 @@ #include <drm/drm_atomic.h> #include "intel_display.h" +#include "intel_display_power.h" #include "intel_global_state.h" struct drm_i915_private; struct intel_atomic_state; struct intel_crtc_state; +struct intel_dbuf_bw { + int used_bw[I915_MAX_DBUF_SLICES]; +}; + struct intel_bw_state { struct intel_global_state base; + struct intel_dbuf_bw dbuf_bw[I915_MAX_PIPES]; /* * Contains a bit mask, used to determine, whether correspondent @@ -36,6 +42,8 @@ struct intel_bw_state { /* bitmask of active pipes */ u8 active_pipes; + + int min_cdclk; }; #define to_intel_bw_state(x) container_of((x), struct intel_bw_state, base) @@ -56,5 +64,7 @@ void intel_bw_crtc_update(struct intel_bw_state *bw_state, const struct intel_crtc_state *crtc_state); int icl_pcode_restrict_qgv_points(struct drm_i915_private *dev_priv, u32 points_mask); +int intel_bw_calc_min_cdclk(struct intel_atomic_state *state); +int skl_bw_calc_min_cdclk(struct intel_atomic_state *state); #endif /* __INTEL_BW_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 979a0241fdcb..45f7f33d1144 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -21,7 +21,10 @@ * DEALINGS IN THE SOFTWARE. */ +#include <linux/time.h> + #include "intel_atomic.h" +#include "intel_bw.h" #include "intel_cdclk.h" #include "intel_display_types.h" #include "intel_sideband.h" @@ -2094,6 +2097,7 @@ static int intel_compute_min_cdclk(struct intel_cdclk_state *cdclk_state) { struct intel_atomic_state *state = cdclk_state->base.state; struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct intel_bw_state *bw_state = NULL; struct intel_crtc *crtc; struct intel_crtc_state *crtc_state; int min_cdclk, i; @@ -2106,6 +2110,10 @@ static int intel_compute_min_cdclk(struct intel_cdclk_state *cdclk_state) if (min_cdclk < 0) return min_cdclk; + bw_state = intel_atomic_get_bw_state(state); + if (IS_ERR(bw_state)) + return PTR_ERR(bw_state); + if (cdclk_state->min_cdclk[i] == min_cdclk) continue; @@ -2117,9 +2125,15 @@ static int intel_compute_min_cdclk(struct intel_cdclk_state *cdclk_state) } min_cdclk = cdclk_state->force_min_cdclk; - for_each_pipe(dev_priv, pipe) + for_each_pipe(dev_priv, pipe) { min_cdclk = max(cdclk_state->min_cdclk[pipe], min_cdclk); + if (!bw_state) + continue; + + min_cdclk = max(bw_state->min_cdclk, min_cdclk); + } + return min_cdclk; } @@ -2700,29 +2714,59 @@ static int vlv_hrawclk(struct drm_i915_private *dev_priv) CCK_DISPLAY_REF_CLOCK_CONTROL); } -static int g4x_hrawclk(struct drm_i915_private *dev_priv) +static int i9xx_hrawclk(struct drm_i915_private *dev_priv) { u32 clkcfg; - /* hrawclock is 1/4 the FSB frequency */ - clkcfg = intel_de_read(dev_priv, CLKCFG); - switch (clkcfg & CLKCFG_FSB_MASK) { - case CLKCFG_FSB_400: - return 100000; - case CLKCFG_FSB_533: - return 133333; - case CLKCFG_FSB_667: - return 166667; - case CLKCFG_FSB_800: - return 200000; - case CLKCFG_FSB_1067: - case CLKCFG_FSB_1067_ALT: - return 266667; - case CLKCFG_FSB_1333: - case CLKCFG_FSB_1333_ALT: - return 333333; - default: - return 133333; + /* + * hrawclock is 1/4 the FSB frequency + * + * Note that this only reads the state of the FSB + * straps, not the actual FSB frequency. Some BIOSen + * let you configure each independently. Ideally we'd + * read out the actual FSB frequency but sadly we + * don't know which registers have that information, + * and all the relevant docs have gone to bit heaven :( + */ + clkcfg = intel_de_read(dev_priv, CLKCFG) & CLKCFG_FSB_MASK; + + if (IS_MOBILE(dev_priv)) { + switch (clkcfg) { + case CLKCFG_FSB_400: + return 100000; + case CLKCFG_FSB_533: + return 133333; + case CLKCFG_FSB_667: + return 166667; + case CLKCFG_FSB_800: + return 200000; + case CLKCFG_FSB_1067: + return 266667; + case CLKCFG_FSB_1333: + return 333333; + default: + MISSING_CASE(clkcfg); + return 133333; + } + } else { + switch (clkcfg) { + case CLKCFG_FSB_400_ALT: + return 100000; + case CLKCFG_FSB_533: + return 133333; + case CLKCFG_FSB_667: + return 166667; + case CLKCFG_FSB_800: + return 200000; + case CLKCFG_FSB_1067_ALT: + return 266667; + case CLKCFG_FSB_1333_ALT: + return 333333; + case CLKCFG_FSB_1600_ALT: + return 400000; + default: + return 133333; + } } } @@ -2743,8 +2787,8 @@ u32 intel_read_rawclk(struct drm_i915_private *dev_priv) freq = pch_rawclk(dev_priv); else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) freq = vlv_hrawclk(dev_priv); - else if (IS_G4X(dev_priv) || IS_PINEVIEW(dev_priv)) - freq = g4x_hrawclk(dev_priv); + else if (INTEL_GEN(dev_priv) >= 3) + freq = i9xx_hrawclk(dev_priv); else /* no rawclk on other platforms, or no need to know it */ return 0; @@ -2760,25 +2804,30 @@ void intel_init_cdclk_hooks(struct drm_i915_private *dev_priv) { if (INTEL_GEN(dev_priv) >= 12) { dev_priv->display.set_cdclk = bxt_set_cdclk; + dev_priv->display.bw_calc_min_cdclk = skl_bw_calc_min_cdclk; dev_priv->display.modeset_calc_cdclk = bxt_modeset_calc_cdclk; dev_priv->display.calc_voltage_level = tgl_calc_voltage_level; dev_priv->cdclk.table = icl_cdclk_table; } else if (IS_ELKHARTLAKE(dev_priv)) { dev_priv->display.set_cdclk = bxt_set_cdclk; + dev_priv->display.bw_calc_min_cdclk = skl_bw_calc_min_cdclk; dev_priv->display.modeset_calc_cdclk = bxt_modeset_calc_cdclk; dev_priv->display.calc_voltage_level = ehl_calc_voltage_level; dev_priv->cdclk.table = icl_cdclk_table; } else if (INTEL_GEN(dev_priv) >= 11) { dev_priv->display.set_cdclk = bxt_set_cdclk; + dev_priv->display.bw_calc_min_cdclk = skl_bw_calc_min_cdclk; dev_priv->display.modeset_calc_cdclk = bxt_modeset_calc_cdclk; dev_priv->display.calc_voltage_level = icl_calc_voltage_level; dev_priv->cdclk.table = icl_cdclk_table; } else if (IS_CANNONLAKE(dev_priv)) { + dev_priv->display.bw_calc_min_cdclk = skl_bw_calc_min_cdclk; dev_priv->display.set_cdclk = bxt_set_cdclk; dev_priv->display.modeset_calc_cdclk = bxt_modeset_calc_cdclk; dev_priv->display.calc_voltage_level = cnl_calc_voltage_level; dev_priv->cdclk.table = cnl_cdclk_table; } else if (IS_GEN9_LP(dev_priv)) { + dev_priv->display.bw_calc_min_cdclk = skl_bw_calc_min_cdclk; dev_priv->display.set_cdclk = bxt_set_cdclk; dev_priv->display.modeset_calc_cdclk = bxt_modeset_calc_cdclk; dev_priv->display.calc_voltage_level = bxt_calc_voltage_level; @@ -2787,18 +2836,23 @@ void intel_init_cdclk_hooks(struct drm_i915_private *dev_priv) else dev_priv->cdclk.table = bxt_cdclk_table; } else if (IS_GEN9_BC(dev_priv)) { + dev_priv->display.bw_calc_min_cdclk = skl_bw_calc_min_cdclk; dev_priv->display.set_cdclk = skl_set_cdclk; dev_priv->display.modeset_calc_cdclk = skl_modeset_calc_cdclk; } else if (IS_BROADWELL(dev_priv)) { + dev_priv->display.bw_calc_min_cdclk = intel_bw_calc_min_cdclk; dev_priv->display.set_cdclk = bdw_set_cdclk; dev_priv->display.modeset_calc_cdclk = bdw_modeset_calc_cdclk; } else if (IS_CHERRYVIEW(dev_priv)) { + dev_priv->display.bw_calc_min_cdclk = intel_bw_calc_min_cdclk; dev_priv->display.set_cdclk = chv_set_cdclk; dev_priv->display.modeset_calc_cdclk = vlv_modeset_calc_cdclk; } else if (IS_VALLEYVIEW(dev_priv)) { + dev_priv->display.bw_calc_min_cdclk = intel_bw_calc_min_cdclk; dev_priv->display.set_cdclk = vlv_set_cdclk; dev_priv->display.modeset_calc_cdclk = vlv_modeset_calc_cdclk; } else { + dev_priv->display.bw_calc_min_cdclk = intel_bw_calc_min_cdclk; dev_priv->display.modeset_calc_cdclk = fixed_modeset_calc_cdclk; } diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index 98ece9cd7cdd..945bb03bdd4d 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -714,16 +714,16 @@ static void bdw_load_lut_10(struct intel_crtc *crtc, intel_de_write(dev_priv, PREC_PAL_INDEX(pipe), 0); } -static void ivb_load_lut_ext_max(struct intel_crtc *crtc) +static void ivb_load_lut_ext_max(const struct intel_crtc_state *crtc_state) { + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - struct intel_dsb *dsb = intel_dsb_get(crtc); enum pipe pipe = crtc->pipe; /* Program the max register to clamp values > 1.0. */ - intel_dsb_reg_write(dsb, PREC_PAL_EXT_GC_MAX(pipe, 0), 1 << 16); - intel_dsb_reg_write(dsb, PREC_PAL_EXT_GC_MAX(pipe, 1), 1 << 16); - intel_dsb_reg_write(dsb, PREC_PAL_EXT_GC_MAX(pipe, 2), 1 << 16); + intel_dsb_reg_write(crtc_state, PREC_PAL_EXT_GC_MAX(pipe, 0), 1 << 16); + intel_dsb_reg_write(crtc_state, PREC_PAL_EXT_GC_MAX(pipe, 1), 1 << 16); + intel_dsb_reg_write(crtc_state, PREC_PAL_EXT_GC_MAX(pipe, 2), 1 << 16); /* * Program the gc max 2 register to clamp values > 1.0. @@ -731,15 +731,13 @@ static void ivb_load_lut_ext_max(struct intel_crtc *crtc) * from 3.0 to 7.0 */ if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv)) { - intel_dsb_reg_write(dsb, PREC_PAL_EXT2_GC_MAX(pipe, 0), + intel_dsb_reg_write(crtc_state, PREC_PAL_EXT2_GC_MAX(pipe, 0), 1 << 16); - intel_dsb_reg_write(dsb, PREC_PAL_EXT2_GC_MAX(pipe, 1), + intel_dsb_reg_write(crtc_state, PREC_PAL_EXT2_GC_MAX(pipe, 1), 1 << 16); - intel_dsb_reg_write(dsb, PREC_PAL_EXT2_GC_MAX(pipe, 2), + intel_dsb_reg_write(crtc_state, PREC_PAL_EXT2_GC_MAX(pipe, 2), 1 << 16); } - - intel_dsb_put(dsb); } static void ivb_load_luts(const struct intel_crtc_state *crtc_state) @@ -753,7 +751,7 @@ static void ivb_load_luts(const struct intel_crtc_state *crtc_state) } else if (crtc_state->gamma_mode == GAMMA_MODE_MODE_SPLIT) { ivb_load_lut_10(crtc, degamma_lut, PAL_PREC_SPLIT_MODE | PAL_PREC_INDEX_VALUE(0)); - ivb_load_lut_ext_max(crtc); + ivb_load_lut_ext_max(crtc_state); ivb_load_lut_10(crtc, gamma_lut, PAL_PREC_SPLIT_MODE | PAL_PREC_INDEX_VALUE(512)); } else { @@ -761,7 +759,7 @@ static void ivb_load_luts(const struct intel_crtc_state *crtc_state) ivb_load_lut_10(crtc, blob, PAL_PREC_INDEX_VALUE(0)); - ivb_load_lut_ext_max(crtc); + ivb_load_lut_ext_max(crtc_state); } } @@ -776,7 +774,7 @@ static void bdw_load_luts(const struct intel_crtc_state *crtc_state) } else if (crtc_state->gamma_mode == GAMMA_MODE_MODE_SPLIT) { bdw_load_lut_10(crtc, degamma_lut, PAL_PREC_SPLIT_MODE | PAL_PREC_INDEX_VALUE(0)); - ivb_load_lut_ext_max(crtc); + ivb_load_lut_ext_max(crtc_state); bdw_load_lut_10(crtc, gamma_lut, PAL_PREC_SPLIT_MODE | PAL_PREC_INDEX_VALUE(512)); } else { @@ -784,7 +782,7 @@ static void bdw_load_luts(const struct intel_crtc_state *crtc_state) bdw_load_lut_10(crtc, blob, PAL_PREC_INDEX_VALUE(0)); - ivb_load_lut_ext_max(crtc); + ivb_load_lut_ext_max(crtc_state); } } @@ -877,7 +875,7 @@ static void glk_load_luts(const struct intel_crtc_state *crtc_state) ilk_load_lut_8(crtc, gamma_lut); } else { bdw_load_lut_10(crtc, gamma_lut, PAL_PREC_INDEX_VALUE(0)); - ivb_load_lut_ext_max(crtc); + ivb_load_lut_ext_max(crtc_state); } } @@ -900,14 +898,12 @@ icl_load_gcmax(const struct intel_crtc_state *crtc_state, const struct drm_color_lut *color) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct intel_dsb *dsb = intel_dsb_get(crtc); enum pipe pipe = crtc->pipe; /* FIXME LUT entries are 16 bit only, so we can prog 0xFFFF max */ - intel_dsb_reg_write(dsb, PREC_PAL_GC_MAX(pipe, 0), color->red); - intel_dsb_reg_write(dsb, PREC_PAL_GC_MAX(pipe, 1), color->green); - intel_dsb_reg_write(dsb, PREC_PAL_GC_MAX(pipe, 2), color->blue); - intel_dsb_put(dsb); + intel_dsb_reg_write(crtc_state, PREC_PAL_GC_MAX(pipe, 0), color->red); + intel_dsb_reg_write(crtc_state, PREC_PAL_GC_MAX(pipe, 1), color->green); + intel_dsb_reg_write(crtc_state, PREC_PAL_GC_MAX(pipe, 2), color->blue); } static void @@ -916,7 +912,6 @@ icl_program_gamma_superfine_segment(const struct intel_crtc_state *crtc_state) struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); const struct drm_property_blob *blob = crtc_state->hw.gamma_lut; const struct drm_color_lut *lut = blob->data; - struct intel_dsb *dsb = intel_dsb_get(crtc); enum pipe pipe = crtc->pipe; int i; @@ -927,19 +922,17 @@ icl_program_gamma_superfine_segment(const struct intel_crtc_state *crtc_state) * 9 entries, corresponding to values 0, 1/(8 * 128 * 256), * 2/(8 * 128 * 256) ... 8/(8 * 128 * 256). */ - intel_dsb_reg_write(dsb, PREC_PAL_MULTI_SEG_INDEX(pipe), + intel_dsb_reg_write(crtc_state, PREC_PAL_MULTI_SEG_INDEX(pipe), PAL_PREC_AUTO_INCREMENT); for (i = 0; i < 9; i++) { const struct drm_color_lut *entry = &lut[i]; - intel_dsb_indexed_reg_write(dsb, PREC_PAL_MULTI_SEG_DATA(pipe), + intel_dsb_indexed_reg_write(crtc_state, PREC_PAL_MULTI_SEG_DATA(pipe), ilk_lut_12p4_ldw(entry)); - intel_dsb_indexed_reg_write(dsb, PREC_PAL_MULTI_SEG_DATA(pipe), + intel_dsb_indexed_reg_write(crtc_state, PREC_PAL_MULTI_SEG_DATA(pipe), ilk_lut_12p4_udw(entry)); } - - intel_dsb_put(dsb); } static void @@ -949,7 +942,6 @@ icl_program_gamma_multi_segment(const struct intel_crtc_state *crtc_state) const struct drm_property_blob *blob = crtc_state->hw.gamma_lut; const struct drm_color_lut *lut = blob->data; const struct drm_color_lut *entry; - struct intel_dsb *dsb = intel_dsb_get(crtc); enum pipe pipe = crtc->pipe; int i; @@ -963,12 +955,13 @@ icl_program_gamma_multi_segment(const struct intel_crtc_state *crtc_state) * PAL_PREC_INDEX[0] and PAL_PREC_INDEX[1] map to seg2[1], * seg2[0] being unused by the hardware. */ - intel_dsb_reg_write(dsb, PREC_PAL_INDEX(pipe), PAL_PREC_AUTO_INCREMENT); + intel_dsb_reg_write(crtc_state, PREC_PAL_INDEX(pipe), + PAL_PREC_AUTO_INCREMENT); for (i = 1; i < 257; i++) { entry = &lut[i * 8]; - intel_dsb_indexed_reg_write(dsb, PREC_PAL_DATA(pipe), + intel_dsb_indexed_reg_write(crtc_state, PREC_PAL_DATA(pipe), ilk_lut_12p4_ldw(entry)); - intel_dsb_indexed_reg_write(dsb, PREC_PAL_DATA(pipe), + intel_dsb_indexed_reg_write(crtc_state, PREC_PAL_DATA(pipe), ilk_lut_12p4_udw(entry)); } @@ -986,24 +979,22 @@ icl_program_gamma_multi_segment(const struct intel_crtc_state *crtc_state) */ for (i = 0; i < 256; i++) { entry = &lut[i * 8 * 128]; - intel_dsb_indexed_reg_write(dsb, PREC_PAL_DATA(pipe), + intel_dsb_indexed_reg_write(crtc_state, PREC_PAL_DATA(pipe), ilk_lut_12p4_ldw(entry)); - intel_dsb_indexed_reg_write(dsb, PREC_PAL_DATA(pipe), + intel_dsb_indexed_reg_write(crtc_state, PREC_PAL_DATA(pipe), ilk_lut_12p4_udw(entry)); } /* The last entry in the LUT is to be programmed in GCMAX */ entry = &lut[256 * 8 * 128]; icl_load_gcmax(crtc_state, entry); - ivb_load_lut_ext_max(crtc); - intel_dsb_put(dsb); + ivb_load_lut_ext_max(crtc_state); } static void icl_load_luts(const struct intel_crtc_state *crtc_state) { const struct drm_property_blob *gamma_lut = crtc_state->hw.gamma_lut; struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct intel_dsb *dsb = intel_dsb_get(crtc); if (crtc_state->hw.degamma_lut) glk_load_degamma_lut(crtc_state); @@ -1018,11 +1009,10 @@ static void icl_load_luts(const struct intel_crtc_state *crtc_state) break; default: bdw_load_lut_10(crtc, gamma_lut, PAL_PREC_INDEX_VALUE(0)); - ivb_load_lut_ext_max(crtc); + ivb_load_lut_ext_max(crtc_state); } - intel_dsb_commit(dsb); - intel_dsb_put(dsb); + intel_dsb_commit(crtc_state); } static u32 chv_cgm_degamma_ldw(const struct drm_color_lut *color) diff --git a/drivers/gpu/drm/i915/display/intel_combo_phy.c b/drivers/gpu/drm/i915/display/intel_combo_phy.c index 9ff05ec12115..77b04bb3ec62 100644 --- a/drivers/gpu/drm/i915/display/intel_combo_phy.c +++ b/drivers/gpu/drm/i915/display/intel_combo_phy.c @@ -181,11 +181,25 @@ static void cnl_combo_phys_uninit(struct drm_i915_private *dev_priv) intel_de_write(dev_priv, CHICKEN_MISC_2, val); } +static bool has_phy_misc(struct drm_i915_private *i915, enum phy phy) +{ + /* + * Some platforms only expect PHY_MISC to be programmed for PHY-A and + * PHY-B and may not even have instances of the register for the + * other combo PHY's. + */ + if (IS_ELKHARTLAKE(i915) || + IS_ROCKETLAKE(i915)) + return phy < PHY_C; + + return true; +} + static bool icl_combo_phy_enabled(struct drm_i915_private *dev_priv, enum phy phy) { /* The PHY C added by EHL has no PHY_MISC register */ - if (IS_ELKHARTLAKE(dev_priv) && phy == PHY_C) + if (!has_phy_misc(dev_priv, phy)) return intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)) & COMP_INIT; else return !(intel_de_read(dev_priv, ICL_PHY_MISC(phy)) & @@ -220,6 +234,27 @@ static bool ehl_vbt_ddi_d_present(struct drm_i915_private *i915) return false; } +static bool phy_is_master(struct drm_i915_private *dev_priv, enum phy phy) +{ + /* + * Certain PHYs are connected to compensation resistors and act + * as masters to other PHYs. + * + * ICL,TGL: + * A(master) -> B(slave), C(slave) + * RKL: + * A(master) -> B(slave) + * C(master) -> D(slave) + * + * We must set the IREFGEN bit for any PHY acting as a master + * to another PHY. + */ + if (IS_ROCKETLAKE(dev_priv) && phy == PHY_C) + return true; + + return phy == PHY_A; +} + static bool icl_combo_phy_verify_state(struct drm_i915_private *dev_priv, enum phy phy) { @@ -231,7 +266,7 @@ static bool icl_combo_phy_verify_state(struct drm_i915_private *dev_priv, ret = cnl_verify_procmon_ref_values(dev_priv, phy); - if (phy == PHY_A) { + if (phy_is_master(dev_priv, phy)) { ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW8(phy), IREFGEN, IREFGEN); @@ -317,12 +352,7 @@ static void icl_combo_phys_init(struct drm_i915_private *dev_priv) continue; } - /* - * Although EHL adds a combo PHY C, there's no PHY_MISC - * register for it and no need to program the - * DE_IO_COMP_PWR_DOWN setting on PHY C. - */ - if (IS_ELKHARTLAKE(dev_priv) && phy == PHY_C) + if (!has_phy_misc(dev_priv, phy)) goto skip_phy_misc; /* @@ -347,7 +377,7 @@ static void icl_combo_phys_init(struct drm_i915_private *dev_priv) skip_phy_misc: cnl_set_procmon_ref_values(dev_priv, phy); - if (phy == PHY_A) { + if (phy_is_master(dev_priv, phy)) { val = intel_de_read(dev_priv, ICL_PORT_COMP_DW8(phy)); val |= IREFGEN; intel_de_write(dev_priv, ICL_PORT_COMP_DW8(phy), val); @@ -376,12 +406,7 @@ static void icl_combo_phys_uninit(struct drm_i915_private *dev_priv) "Combo PHY %c HW state changed unexpectedly\n", phy_name(phy)); - /* - * Although EHL adds a combo PHY C, there's no PHY_MISC - * register for it and no need to program the - * DE_IO_COMP_PWR_DOWN setting on PHY C. - */ - if (IS_ELKHARTLAKE(dev_priv) && phy == PHY_C) + if (!has_phy_misc(dev_priv, phy)) goto skip_phy_misc; val = intel_de_read(dev_priv, ICL_PHY_MISC(phy)); diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c index 2f5b9a4baafd..5b4510ce5693 100644 --- a/drivers/gpu/drm/i915/display/intel_crt.c +++ b/drivers/gpu/drm/i915/display/intel_crt.c @@ -833,7 +833,7 @@ intel_crt_detect(struct drm_connector *connector, connector->base.id, connector->name, force); - if (i915_modparams.load_detect_test) { + if (dev_priv->params.load_detect_test) { wakeref = intel_display_power_get(dev_priv, intel_encoder->power_domain); goto load_detect; @@ -889,7 +889,7 @@ load_detect: else if (INTEL_GEN(dev_priv) < 4) status = intel_crt_load_detect(crt, to_intel_crtc(connector->state->crtc)->pipe); - else if (i915_modparams.load_detect_test) + else if (dev_priv->params.load_detect_test) status = connector_status_disconnected; else status = connector_status_unknown; diff --git a/drivers/gpu/drm/i915/display/intel_csr.c b/drivers/gpu/drm/i915/display/intel_csr.c index 3112572cfb7d..f22a7645c249 100644 --- a/drivers/gpu/drm/i915/display/intel_csr.c +++ b/drivers/gpu/drm/i915/display/intel_csr.c @@ -40,6 +40,10 @@ #define GEN12_CSR_MAX_FW_SIZE ICL_CSR_MAX_FW_SIZE +#define RKL_CSR_PATH "i915/rkl_dmc_ver2_01.bin" +#define RKL_CSR_VERSION_REQUIRED CSR_VERSION(2, 1) +MODULE_FIRMWARE(RKL_CSR_PATH); + #define TGL_CSR_PATH "i915/tgl_dmc_ver2_06.bin" #define TGL_CSR_VERSION_REQUIRED CSR_VERSION(2, 6) #define TGL_CSR_MAX_FW_SIZE 0x6000 @@ -682,7 +686,11 @@ void intel_csr_ucode_init(struct drm_i915_private *dev_priv) */ intel_csr_runtime_pm_get(dev_priv); - if (INTEL_GEN(dev_priv) >= 12) { + if (IS_ROCKETLAKE(dev_priv)) { + csr->fw_path = RKL_CSR_PATH; + csr->required_version = RKL_CSR_VERSION_REQUIRED; + csr->max_fw_size = GEN12_CSR_MAX_FW_SIZE; + } else if (INTEL_GEN(dev_priv) >= 12) { csr->fw_path = TGL_CSR_PATH; csr->required_version = TGL_CSR_VERSION_REQUIRED; /* Allow to load fw via parameter using the last known size */ @@ -699,7 +707,9 @@ void intel_csr_ucode_init(struct drm_i915_private *dev_priv) csr->fw_path = GLK_CSR_PATH; csr->required_version = GLK_CSR_VERSION_REQUIRED; csr->max_fw_size = GLK_CSR_MAX_FW_SIZE; - } else if (IS_KABYLAKE(dev_priv) || IS_COFFEELAKE(dev_priv)) { + } else if (IS_KABYLAKE(dev_priv) || + IS_COFFEELAKE(dev_priv) || + IS_COMETLAKE(dev_priv)) { csr->fw_path = KBL_CSR_PATH; csr->required_version = KBL_CSR_VERSION_REQUIRED; csr->max_fw_size = KBL_CSR_MAX_FW_SIZE; @@ -713,15 +723,15 @@ void intel_csr_ucode_init(struct drm_i915_private *dev_priv) csr->max_fw_size = BXT_CSR_MAX_FW_SIZE; } - if (i915_modparams.dmc_firmware_path) { - if (strlen(i915_modparams.dmc_firmware_path) == 0) { + if (dev_priv->params.dmc_firmware_path) { + if (strlen(dev_priv->params.dmc_firmware_path) == 0) { csr->fw_path = NULL; drm_info(&dev_priv->drm, "Disabling CSR firmware and runtime PM\n"); return; } - csr->fw_path = i915_modparams.dmc_firmware_path; + csr->fw_path = dev_priv->params.dmc_firmware_path; /* Bypass version check for firmware override. */ csr->required_version = 0; } diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 0575a1eea2a1..025d4052f6f8 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -639,11 +639,25 @@ struct tgl_dkl_phy_ddi_buf_trans { static const struct tgl_dkl_phy_ddi_buf_trans tgl_dkl_phy_dp_ddi_trans[] = { /* VS pre-emp Non-trans mV Pre-emph dB */ { 0x7, 0x0, 0x00 }, /* 0 0 400mV 0 dB */ - { 0x5, 0x0, 0x03 }, /* 0 1 400mV 3.5 dB */ - { 0x2, 0x0, 0x0b }, /* 0 2 400mV 6 dB */ + { 0x5, 0x0, 0x05 }, /* 0 1 400mV 3.5 dB */ + { 0x2, 0x0, 0x0B }, /* 0 2 400mV 6 dB */ + { 0x0, 0x0, 0x18 }, /* 0 3 400mV 9.5 dB */ + { 0x5, 0x0, 0x00 }, /* 1 0 600mV 0 dB */ + { 0x2, 0x0, 0x08 }, /* 1 1 600mV 3.5 dB */ + { 0x0, 0x0, 0x14 }, /* 1 2 600mV 6 dB */ + { 0x2, 0x0, 0x00 }, /* 2 0 800mV 0 dB */ + { 0x0, 0x0, 0x0B }, /* 2 1 800mV 3.5 dB */ + { 0x0, 0x0, 0x00 }, /* 3 0 1200mV 0 dB HDMI default */ +}; + +static const struct tgl_dkl_phy_ddi_buf_trans tgl_dkl_phy_dp_ddi_trans_hbr2[] = { + /* VS pre-emp Non-trans mV Pre-emph dB */ + { 0x7, 0x0, 0x00 }, /* 0 0 400mV 0 dB */ + { 0x5, 0x0, 0x05 }, /* 0 1 400mV 3.5 dB */ + { 0x2, 0x0, 0x0B }, /* 0 2 400mV 6 dB */ { 0x0, 0x0, 0x19 }, /* 0 3 400mV 9.5 dB */ { 0x5, 0x0, 0x00 }, /* 1 0 600mV 0 dB */ - { 0x2, 0x0, 0x03 }, /* 1 1 600mV 3.5 dB */ + { 0x2, 0x0, 0x08 }, /* 1 1 600mV 3.5 dB */ { 0x0, 0x0, 0x14 }, /* 1 2 600mV 6 dB */ { 0x2, 0x0, 0x00 }, /* 2 0 800mV 0 dB */ { 0x0, 0x0, 0x0B }, /* 2 1 800mV 3.5 dB */ @@ -722,10 +736,14 @@ skl_get_buf_trans_dp(struct drm_i915_private *dev_priv, int *n_entries) static const struct ddi_buf_trans * kbl_get_buf_trans_dp(struct drm_i915_private *dev_priv, int *n_entries) { - if (IS_KBL_ULX(dev_priv) || IS_CFL_ULX(dev_priv)) { + if (IS_KBL_ULX(dev_priv) || + IS_CFL_ULX(dev_priv) || + IS_CML_ULX(dev_priv)) { *n_entries = ARRAY_SIZE(kbl_y_ddi_translations_dp); return kbl_y_ddi_translations_dp; - } else if (IS_KBL_ULT(dev_priv) || IS_CFL_ULT(dev_priv)) { + } else if (IS_KBL_ULT(dev_priv) || + IS_CFL_ULT(dev_priv) || + IS_CML_ULT(dev_priv)) { *n_entries = ARRAY_SIZE(kbl_u_ddi_translations_dp); return kbl_u_ddi_translations_dp; } else { @@ -738,12 +756,16 @@ static const struct ddi_buf_trans * skl_get_buf_trans_edp(struct drm_i915_private *dev_priv, int *n_entries) { if (dev_priv->vbt.edp.low_vswing) { - if (IS_SKL_ULX(dev_priv) || IS_KBL_ULX(dev_priv) || - IS_CFL_ULX(dev_priv)) { + if (IS_SKL_ULX(dev_priv) || + IS_KBL_ULX(dev_priv) || + IS_CFL_ULX(dev_priv) || + IS_CML_ULX(dev_priv)) { *n_entries = ARRAY_SIZE(skl_y_ddi_translations_edp); return skl_y_ddi_translations_edp; - } else if (IS_SKL_ULT(dev_priv) || IS_KBL_ULT(dev_priv) || - IS_CFL_ULT(dev_priv)) { + } else if (IS_SKL_ULT(dev_priv) || + IS_KBL_ULT(dev_priv) || + IS_CFL_ULT(dev_priv) || + IS_CML_ULT(dev_priv)) { *n_entries = ARRAY_SIZE(skl_u_ddi_translations_edp); return skl_u_ddi_translations_edp; } else { @@ -752,7 +774,9 @@ skl_get_buf_trans_edp(struct drm_i915_private *dev_priv, int *n_entries) } } - if (IS_KABYLAKE(dev_priv) || IS_COFFEELAKE(dev_priv)) + if (IS_KABYLAKE(dev_priv) || + IS_COFFEELAKE(dev_priv) || + IS_COMETLAKE(dev_priv)) return kbl_get_buf_trans_dp(dev_priv, n_entries); else return skl_get_buf_trans_dp(dev_priv, n_entries); @@ -761,8 +785,10 @@ skl_get_buf_trans_edp(struct drm_i915_private *dev_priv, int *n_entries) static const struct ddi_buf_trans * skl_get_buf_trans_hdmi(struct drm_i915_private *dev_priv, int *n_entries) { - if (IS_SKL_ULX(dev_priv) || IS_KBL_ULX(dev_priv) || - IS_CFL_ULX(dev_priv)) { + if (IS_SKL_ULX(dev_priv) || + IS_KBL_ULX(dev_priv) || + IS_CFL_ULX(dev_priv) || + IS_CML_ULX(dev_priv)) { *n_entries = ARRAY_SIZE(skl_y_ddi_translations_hdmi); return skl_y_ddi_translations_hdmi; } else { @@ -784,7 +810,9 @@ static const struct ddi_buf_trans * intel_ddi_get_buf_trans_dp(struct drm_i915_private *dev_priv, enum port port, int *n_entries) { - if (IS_KABYLAKE(dev_priv) || IS_COFFEELAKE(dev_priv)) { + if (IS_KABYLAKE(dev_priv) || + IS_COFFEELAKE(dev_priv) || + IS_COMETLAKE(dev_priv)) { const struct ddi_buf_trans *ddi_translations = kbl_get_buf_trans_dp(dev_priv, n_entries); *n_entries = skl_buf_trans_num_entries(port, *n_entries); @@ -1014,6 +1042,22 @@ tgl_get_combo_buf_trans(struct drm_i915_private *dev_priv, int type, int rate, return tgl_combo_phy_ddi_translations_dp_hbr; } +static const struct tgl_dkl_phy_ddi_buf_trans * +tgl_get_dkl_buf_trans(struct drm_i915_private *dev_priv, int type, int rate, + int *n_entries) +{ + if (type == INTEL_OUTPUT_HDMI) { + *n_entries = ARRAY_SIZE(tgl_dkl_phy_hdmi_ddi_trans); + return tgl_dkl_phy_hdmi_ddi_trans; + } else if (rate > 270000) { + *n_entries = ARRAY_SIZE(tgl_dkl_phy_dp_ddi_trans_hbr2); + return tgl_dkl_phy_dp_ddi_trans_hbr2; + } + + *n_entries = ARRAY_SIZE(tgl_dkl_phy_dp_ddi_trans); + return tgl_dkl_phy_dp_ddi_trans; +} + static int intel_ddi_hdmi_level(struct intel_encoder *encoder) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); @@ -1025,7 +1069,8 @@ static int intel_ddi_hdmi_level(struct intel_encoder *encoder) tgl_get_combo_buf_trans(dev_priv, INTEL_OUTPUT_HDMI, 0, &n_entries); else - n_entries = ARRAY_SIZE(tgl_dkl_phy_hdmi_ddi_trans); + tgl_get_dkl_buf_trans(dev_priv, INTEL_OUTPUT_HDMI, 0, + &n_entries); default_entry = n_entries - 1; } else if (INTEL_GEN(dev_priv) == 11) { if (intel_phy_is_combo(dev_priv, phy)) @@ -1608,7 +1653,6 @@ void intel_ddi_enable_transcoder_func(struct intel_encoder *encoder, struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; - u32 ctl; if (INTEL_GEN(dev_priv) >= 11) { enum transcoder master_transcoder = crtc_state->master_transcoder; @@ -1626,10 +1670,9 @@ void intel_ddi_enable_transcoder_func(struct intel_encoder *encoder, TRANS_DDI_FUNC_CTL2(cpu_transcoder), ctl2); } - ctl = intel_ddi_transcoder_func_reg_val_get(encoder, crtc_state); - if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST)) - ctl |= TRANS_DDI_DP_VC_PAYLOAD_ALLOC; - intel_de_write(dev_priv, TRANS_DDI_FUNC_CTL(cpu_transcoder), ctl); + intel_de_write(dev_priv, TRANS_DDI_FUNC_CTL(cpu_transcoder), + intel_ddi_transcoder_func_reg_val_get(encoder, + crtc_state)); } /* @@ -2095,10 +2138,10 @@ static void bxt_ddi_vswing_sequence(struct intel_encoder *encoder, ddi_translations[level].deemphasis); } -u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder) +static u8 intel_ddi_dp_voltage_max(struct intel_dp *intel_dp) { + struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); enum port port = encoder->port; enum phy phy = intel_port_to_phy(dev_priv, port); int n_entries; @@ -2108,7 +2151,8 @@ u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder) tgl_get_combo_buf_trans(dev_priv, encoder->type, intel_dp->link_rate, &n_entries); else - n_entries = ARRAY_SIZE(tgl_dkl_phy_dp_ddi_trans); + tgl_get_dkl_buf_trans(dev_priv, encoder->type, + intel_dp->link_rate, &n_entries); } else if (INTEL_GEN(dev_priv) == 11) { if (IS_ELKHARTLAKE(dev_priv)) ehl_get_combo_buf_trans(dev_priv, encoder->type, @@ -2151,19 +2195,9 @@ u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder) * used on all DDI platforms. Should that change we need to * rethink this code. */ -u8 intel_ddi_dp_pre_emphasis_max(struct intel_encoder *encoder, u8 voltage_swing) +static u8 intel_ddi_dp_preemph_max(struct intel_dp *intel_dp) { - switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: - return DP_TRAIN_PRE_EMPH_LEVEL_3; - case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: - return DP_TRAIN_PRE_EMPH_LEVEL_2; - case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: - return DP_TRAIN_PRE_EMPH_LEVEL_1; - case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: - default: - return DP_TRAIN_PRE_EMPH_LEVEL_0; - } + return DP_TRAIN_PRE_EMPH_LEVEL_3; } static void cnl_ddi_vswing_program(struct intel_encoder *encoder, @@ -2585,15 +2619,17 @@ tgl_dkl_phy_ddi_vswing_sequence(struct intel_encoder *encoder, int link_clock, enum tc_port tc_port = intel_port_to_tc(dev_priv, encoder->port); const struct tgl_dkl_phy_ddi_buf_trans *ddi_translations; u32 n_entries, val, ln, dpcnt_mask, dpcnt_val; + int rate = 0; if (type == INTEL_OUTPUT_HDMI) { - n_entries = ARRAY_SIZE(tgl_dkl_phy_hdmi_ddi_trans); - ddi_translations = tgl_dkl_phy_hdmi_ddi_trans; - } else { - n_entries = ARRAY_SIZE(tgl_dkl_phy_dp_ddi_trans); - ddi_translations = tgl_dkl_phy_dp_ddi_trans; + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + rate = intel_dp->link_rate; } + ddi_translations = tgl_get_dkl_buf_trans(dev_priv, encoder->type, rate, + &n_entries); + if (level >= n_entries) level = n_entries - 1; @@ -3344,11 +3380,10 @@ static void intel_ddi_pre_enable_hdmi(struct intel_atomic_state *state, const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state) { - struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); - struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi; + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); + struct intel_hdmi *intel_hdmi = &dig_port->hdmi; struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); int level = intel_ddi_hdmi_level(encoder); - struct intel_digital_port *dig_port = enc_to_dig_port(encoder); intel_dp_dual_mode_set_tmds_output(intel_hdmi, true); intel_ddi_clk_select(encoder, crtc_state); @@ -3375,9 +3410,9 @@ static void intel_ddi_pre_enable_hdmi(struct intel_atomic_state *state, intel_ddi_enable_pipe_clock(encoder, crtc_state); - intel_dig_port->set_infoframes(encoder, - crtc_state->has_infoframe, - crtc_state, conn_state); + dig_port->set_infoframes(encoder, + crtc_state->has_infoframe, + crtc_state, conn_state); } static void intel_ddi_pre_enable(struct intel_atomic_state *state, @@ -4155,11 +4190,6 @@ void intel_ddi_get_config(struct intel_encoder *encoder, if (drm_WARN_ON(&dev_priv->drm, transcoder_is_dsi(cpu_transcoder))) return; - if (INTEL_GEN(dev_priv) >= 12) { - intel_dp->regs.dp_tp_ctl = TGL_DP_TP_CTL(cpu_transcoder); - intel_dp->regs.dp_tp_status = TGL_DP_TP_STATUS(cpu_transcoder); - } - intel_dsc_get_config(encoder, pipe_config); temp = intel_de_read(dev_priv, TRANS_DDI_FUNC_CTL(cpu_transcoder)); @@ -4261,6 +4291,16 @@ void intel_ddi_get_config(struct intel_encoder *encoder, break; } + if (INTEL_GEN(dev_priv) >= 12) { + enum transcoder transcoder = + intel_dp_mst_is_slave_trans(pipe_config) ? + pipe_config->mst_master_transcoder : + pipe_config->cpu_transcoder; + + intel_dp->regs.dp_tp_ctl = TGL_DP_TP_CTL(transcoder); + intel_dp->regs.dp_tp_status = TGL_DP_TP_STATUS(transcoder); + } + pipe_config->has_audio = intel_ddi_is_audio_enabled(dev_priv, cpu_transcoder); @@ -4523,6 +4563,9 @@ intel_ddi_init_dp_connector(struct intel_digital_port *intel_dig_port) else intel_dig_port->dp.set_signal_levels = hsw_set_signal_levels; + intel_dig_port->dp.voltage_max = intel_ddi_dp_voltage_max; + intel_dig_port->dp.preemph_max = intel_ddi_dp_preemph_max; + if (INTEL_GEN(dev_priv) < 12) { intel_dig_port->dp.regs.dp_tp_ctl = DP_TP_CTL(port); intel_dig_port->dp.regs.dp_tp_status = DP_TP_STATUS(port); diff --git a/drivers/gpu/drm/i915/display/intel_ddi.h b/drivers/gpu/drm/i915/display/intel_ddi.h index fbdf8ddde486..077e9dbbe367 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.h +++ b/drivers/gpu/drm/i915/display/intel_ddi.h @@ -42,9 +42,6 @@ void intel_ddi_compute_min_voltage_level(struct drm_i915_private *dev_priv, struct intel_crtc_state *crtc_state); u32 bxt_signal_levels(struct intel_dp *intel_dp); u32 ddi_signal_levels(struct intel_dp *intel_dp); -u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder); -u8 intel_ddi_dp_pre_emphasis_max(struct intel_encoder *encoder, - u8 voltage_swing); int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder, bool enable); void icl_sanitize_encoder_pll_mapping(struct intel_encoder *encoder); diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index c1836095ea38..84e2a17b5ecb 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -35,6 +35,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_uapi.h> +#include <drm/drm_damage_helper.h> #include <drm/drm_dp_helper.h> #include <drm/drm_edid.h> #include <drm/drm_fourcc.h> @@ -3822,6 +3823,17 @@ skl_check_main_ccs_coordinates(struct intel_plane_state *plane_state, return true; } +unsigned int +intel_plane_fence_y_offset(const struct intel_plane_state *plane_state) +{ + int x = 0, y = 0; + + intel_plane_adjust_aligned_offset(&x, &y, plane_state, 0, + plane_state->color_plane[0].offset, 0); + + return y; +} + static int skl_check_main_surface(struct intel_plane_state *plane_state) { struct drm_i915_private *dev_priv = to_i915(plane_state->uapi.plane->dev); @@ -4812,11 +4824,18 @@ u32 glk_plane_color_ctl(const struct intel_crtc_state *crtc_state, plane_color_ctl |= glk_plane_color_ctl_alpha(plane_state); if (fb->format->is_yuv && !icl_is_hdr_plane(dev_priv, plane->id)) { - if (plane_state->hw.color_encoding == DRM_COLOR_YCBCR_BT709) + switch (plane_state->hw.color_encoding) { + case DRM_COLOR_YCBCR_BT709: plane_color_ctl |= PLANE_COLOR_CSC_MODE_YUV709_TO_RGB709; - else - plane_color_ctl |= PLANE_COLOR_CSC_MODE_YUV601_TO_RGB709; - + break; + case DRM_COLOR_YCBCR_BT2020: + plane_color_ctl |= + PLANE_COLOR_CSC_MODE_YUV2020_TO_RGB2020; + break; + default: + plane_color_ctl |= + PLANE_COLOR_CSC_MODE_YUV601_TO_RGB601; + } if (plane_state->hw.color_range == DRM_COLOR_YCBCR_FULL_RANGE) plane_color_ctl |= PLANE_COLOR_YUV_RANGE_CORRECTION_DISABLE; } else if (fb->format->is_yuv) { @@ -4879,7 +4898,7 @@ void intel_prepare_reset(struct drm_i915_private *dev_priv) int ret; /* reset doesn't touch the display */ - if (!i915_modparams.force_reset_modeset_test && + if (!dev_priv->params.force_reset_modeset_test && !gpu_reset_clobbers_display(dev_priv)) return; @@ -6424,8 +6443,7 @@ static bool hsw_post_update_enable_ips(const struct intel_crtc_state *old_crtc_s * We can't read out IPS on broadwell, assume the worst and * forcibly enable IPS on the first fastset. */ - if (new_crtc_state->update_pipe && - old_crtc_state->hw.adjusted_mode.private_flags & I915_MODE_FLAG_INHERITED) + if (new_crtc_state->update_pipe && old_crtc_state->inherited) return true; return !old_crtc_state->ips_enabled; @@ -7212,30 +7230,33 @@ bool intel_phy_is_combo(struct drm_i915_private *dev_priv, enum phy phy) { if (phy == PHY_NONE) return false; - - if (IS_ELKHARTLAKE(dev_priv)) + else if (IS_ROCKETLAKE(dev_priv)) + return phy <= PHY_D; + else if (IS_ELKHARTLAKE(dev_priv)) return phy <= PHY_C; - - if (INTEL_GEN(dev_priv) >= 11) + else if (INTEL_GEN(dev_priv) >= 11) return phy <= PHY_B; - - return false; + else + return false; } bool intel_phy_is_tc(struct drm_i915_private *dev_priv, enum phy phy) { - if (INTEL_GEN(dev_priv) >= 12) + if (IS_ROCKETLAKE(dev_priv)) + return false; + else if (INTEL_GEN(dev_priv) >= 12) return phy >= PHY_D && phy <= PHY_I; - - if (INTEL_GEN(dev_priv) >= 11 && !IS_ELKHARTLAKE(dev_priv)) + else if (INTEL_GEN(dev_priv) >= 11 && !IS_ELKHARTLAKE(dev_priv)) return phy >= PHY_C && phy <= PHY_F; - - return false; + else + return false; } enum phy intel_port_to_phy(struct drm_i915_private *i915, enum port port) { - if (IS_ELKHARTLAKE(i915) && port == PORT_D) + if (IS_ROCKETLAKE(i915) && port >= PORT_D) + return (enum phy)port - 1; + else if (IS_ELKHARTLAKE(i915) && port == PORT_D) return PHY_A; return (enum phy)port; @@ -7506,6 +7527,10 @@ static void i9xx_crtc_enable(struct intel_atomic_state *state, intel_crtc_vblank_on(new_crtc_state); intel_encoders_enable(state, crtc); + + /* prevents spurious underruns */ + if (IS_GEN(dev_priv, 2)) + intel_wait_for_vblank(dev_priv, pipe); } static void i9xx_pfit_disable(const struct intel_crtc_state *old_crtc_state) @@ -7579,6 +7604,8 @@ static void intel_crtc_disable_noatomic(struct intel_crtc *crtc, to_intel_bw_state(dev_priv->bw_obj.state); struct intel_cdclk_state *cdclk_state = to_intel_cdclk_state(dev_priv->cdclk.obj.state); + struct intel_dbuf_state *dbuf_state = + to_intel_dbuf_state(dev_priv->dbuf.obj.state); struct intel_crtc_state *crtc_state = to_intel_crtc_state(crtc->base.state); enum intel_display_power_domain domain; @@ -7652,6 +7679,8 @@ static void intel_crtc_disable_noatomic(struct intel_crtc *crtc, cdclk_state->min_voltage_level[pipe] = 0; cdclk_state->active_pipes &= ~BIT(pipe); + dbuf_state->active_pipes &= ~BIT(pipe); + bw_state->data_rate[pipe] = 0; bw_state->num_active_planes[pipe] = 0; } @@ -7869,7 +7898,7 @@ bool hsw_crtc_state_ips_capable(const struct intel_crtc_state *crtc_state) if (!hsw_crtc_supports_ips(crtc)) return false; - if (!i915_modparams.enable_ips) + if (!dev_priv->params.enable_ips) return false; if (crtc_state->pipe_bpp > 24) @@ -8140,8 +8169,8 @@ static void intel_panel_sanitize_ssc(struct drm_i915_private *dev_priv) static bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) { - if (i915_modparams.panel_use_ssc >= 0) - return i915_modparams.panel_use_ssc != 0; + if (dev_priv->params.panel_use_ssc >= 0) + return dev_priv->params.panel_use_ssc != 0; return dev_priv->vbt.lvds_use_ssc && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE); } @@ -10882,7 +10911,7 @@ static bool hsw_get_transcoder_state(struct intel_crtc *crtc, struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); enum intel_display_power_domain power_domain; - unsigned long panel_transcoder_mask = 0; + unsigned long panel_transcoder_mask = BIT(TRANSCODER_EDP); unsigned long enabled_panel_transcoders = 0; enum transcoder panel_transcoder; intel_wakeref_t wf; @@ -10892,9 +10921,6 @@ static bool hsw_get_transcoder_state(struct intel_crtc *crtc, panel_transcoder_mask |= BIT(TRANSCODER_DSI_0) | BIT(TRANSCODER_DSI_1); - if (HAS_TRANSCODER(dev_priv, TRANSCODER_EDP)) - panel_transcoder_mask |= BIT(TRANSCODER_EDP); - /* * The pipe->transcoder mapping is fixed with the exception of the eDP * and DSI transcoders handled below. @@ -10905,9 +10931,8 @@ static bool hsw_get_transcoder_state(struct intel_crtc *crtc, * XXX: Do intel_display_power_get_if_enabled before reading this (for * consistency and less surprising code; it's in always on power). */ - for_each_set_bit(panel_transcoder, - &panel_transcoder_mask, - ARRAY_SIZE(INTEL_INFO(dev_priv)->trans_offsets)) { + for_each_cpu_transcoder_masked(dev_priv, panel_transcoder, + panel_transcoder_mask) { bool force_thru = false; enum pipe trans_pipe; @@ -12499,7 +12524,7 @@ static int icl_check_nv12_planes(struct intel_crtc_state *crtc_state) continue; for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, linked) { - if (!icl_is_nv12_y_plane(linked->id)) + if (!icl_is_nv12_y_plane(dev_priv, linked->id)) continue; if (crtc_state->active_planes & BIT(linked->id)) @@ -12545,6 +12570,10 @@ static int icl_check_nv12_planes(struct intel_crtc_state *crtc_state) plane_state->cus_ctl |= PLANE_CUS_PLANE_7; else if (linked->id == PLANE_SPRITE4) plane_state->cus_ctl |= PLANE_CUS_PLANE_6; + else if (linked->id == PLANE_SPRITE3) + plane_state->cus_ctl |= PLANE_CUS_PLANE_5_RKL; + else if (linked->id == PLANE_SPRITE2) + plane_state->cus_ctl |= PLANE_CUS_PLANE_4_RKL; else MISSING_CASE(linked->id); } @@ -12568,12 +12597,15 @@ static u16 hsw_linetime_wm(const struct intel_crtc_state *crtc_state) { const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + int linetime_wm; if (!crtc_state->hw.enable) return 0; - return DIV_ROUND_CLOSEST(adjusted_mode->crtc_htotal * 1000 * 8, - adjusted_mode->crtc_clock); + linetime_wm = DIV_ROUND_CLOSEST(adjusted_mode->crtc_htotal * 1000 * 8, + adjusted_mode->crtc_clock); + + return min(linetime_wm, 0x1ff); } static u16 hsw_ips_linetime_wm(const struct intel_crtc_state *crtc_state, @@ -12581,12 +12613,15 @@ static u16 hsw_ips_linetime_wm(const struct intel_crtc_state *crtc_state, { const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + int linetime_wm; if (!crtc_state->hw.enable) return 0; - return DIV_ROUND_CLOSEST(adjusted_mode->crtc_htotal * 1000 * 8, - cdclk_state->logical.cdclk); + linetime_wm = DIV_ROUND_CLOSEST(adjusted_mode->crtc_htotal * 1000 * 8, + cdclk_state->logical.cdclk); + + return min(linetime_wm, 0x1ff); } static u16 skl_linetime_wm(const struct intel_crtc_state *crtc_state) @@ -12595,7 +12630,7 @@ static u16 skl_linetime_wm(const struct intel_crtc_state *crtc_state) struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; - u16 linetime_wm; + int linetime_wm; if (!crtc_state->hw.enable) return 0; @@ -12607,7 +12642,7 @@ static u16 skl_linetime_wm(const struct intel_crtc_state *crtc_state) if (IS_GEN9_LP(dev_priv) && dev_priv->ipc_enabled) linetime_wm /= 2; - return linetime_wm; + return min(linetime_wm, 0x1ff); } static int hsw_compute_linetime_wm(struct intel_atomic_state *state, @@ -13570,8 +13605,8 @@ pipe_config_mismatch(bool fastset, const struct intel_crtc *crtc, static bool fastboot_enabled(struct drm_i915_private *dev_priv) { - if (i915_modparams.fastboot != -1) - return i915_modparams.fastboot; + if (dev_priv->params.fastboot != -1) + return dev_priv->params.fastboot; /* Enable fastboot by default on Skylake and newer */ if (INTEL_GEN(dev_priv) >= 9) @@ -13595,8 +13630,7 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, bool ret = true; u32 bp_gamma = 0; bool fixup_inherited = fastset && - (current_config->hw.mode.private_flags & I915_MODE_FLAG_INHERITED) && - !(pipe_config->hw.mode.private_flags & I915_MODE_FLAG_INHERITED); + current_config->inherited && !pipe_config->inherited; if (fixup_inherited && !fastboot_enabled(dev_priv)) { drm_dbg_kms(&dev_priv->drm, @@ -14007,10 +14041,10 @@ static void verify_wm_state(struct intel_crtc *crtc, hw_enabled_slices = intel_enabled_dbuf_slices_mask(dev_priv); if (INTEL_GEN(dev_priv) >= 11 && - hw_enabled_slices != dev_priv->enabled_dbuf_slices_mask) + hw_enabled_slices != dev_priv->dbuf.enabled_slices) drm_err(&dev_priv->drm, "mismatch in DBUF Slices (expected 0x%x, got 0x%x)\n", - dev_priv->enabled_dbuf_slices_mask, + dev_priv->dbuf.enabled_slices, hw_enabled_slices); /* planes */ @@ -14404,6 +14438,8 @@ intel_crtc_update_active_timings(const struct intel_crtc_state *crtc_state) drm_calc_timestamping_constants(&crtc->base, adjusted_mode); + crtc->mode_flags = crtc_state->mode_flags; + /* * The scanline counter increments at the leading edge of hsync. * @@ -14551,20 +14587,12 @@ static int intel_modeset_checks(struct intel_atomic_state *state) state->modeset = true; state->active_pipes = intel_calc_active_pipes(state, dev_priv->active_pipes); - state->active_pipe_changes = state->active_pipes ^ dev_priv->active_pipes; - - if (state->active_pipe_changes) { + if (state->active_pipes != dev_priv->active_pipes) { ret = _intel_atomic_lock_global_state(state); if (ret) return ret; } - ret = intel_modeset_calc_cdclk(state); - if (ret) - return ret; - - intel_modeset_clear_plls(state); - if (IS_HASWELL(dev_priv)) return hsw_mode_set_planes_workaround(state); @@ -14641,11 +14669,10 @@ static bool active_planes_affects_min_cdclk(struct drm_i915_private *dev_priv) /* See {hsw,vlv,ivb}_plane_ratio() */ return IS_BROADWELL(dev_priv) || IS_HASWELL(dev_priv) || IS_CHERRYVIEW(dev_priv) || IS_VALLEYVIEW(dev_priv) || - IS_IVYBRIDGE(dev_priv); + IS_IVYBRIDGE(dev_priv) || (INTEL_GEN(dev_priv) >= 11); } -static int intel_atomic_check_planes(struct intel_atomic_state *state, - bool *need_cdclk_calc) +static int intel_atomic_check_planes(struct intel_atomic_state *state) { struct drm_i915_private *dev_priv = to_i915(state->base.dev); struct intel_crtc_state *old_crtc_state, *new_crtc_state; @@ -14687,7 +14714,13 @@ static int intel_atomic_check_planes(struct intel_atomic_state *state, old_active_planes = old_crtc_state->active_planes & ~BIT(PLANE_CURSOR); new_active_planes = new_crtc_state->active_planes & ~BIT(PLANE_CURSOR); - if (hweight8(old_active_planes) == hweight8(new_active_planes)) + /* + * Not only the number of planes, but if the plane configuration had + * changed might already mean we need to recompute min CDCLK, + * because different planes might consume different amount of Dbuf bandwidth + * according to formula: Bw per plane = Pixel rate * bpp * pipe/plane scale factor + */ + if (old_active_planes == new_active_planes) continue; ret = intel_crtc_add_planes_to_state(state, crtc, new_active_planes); @@ -14695,6 +14728,21 @@ static int intel_atomic_check_planes(struct intel_atomic_state *state, return ret; } + return 0; +} + +static int intel_atomic_check_cdclk(struct intel_atomic_state *state, + bool *need_cdclk_calc) +{ + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct intel_cdclk_state *new_cdclk_state; + struct intel_plane_state *plane_state; + struct intel_bw_state *new_bw_state; + struct intel_plane *plane; + int min_cdclk = 0; + enum pipe pipe; + int ret; + int i; /* * active_planes bitmask has been updated, and potentially * affected planes are part of the state. We can now @@ -14706,6 +14754,30 @@ static int intel_atomic_check_planes(struct intel_atomic_state *state, return ret; } + new_cdclk_state = intel_atomic_get_new_cdclk_state(state); + + if (new_cdclk_state && new_cdclk_state->force_min_cdclk_changed) + *need_cdclk_calc = true; + + ret = dev_priv->display.bw_calc_min_cdclk(state); + if (ret) + return ret; + + new_bw_state = intel_atomic_get_new_bw_state(state); + + if (!new_cdclk_state || !new_bw_state) + return 0; + + for_each_pipe(dev_priv, pipe) { + min_cdclk = max(new_cdclk_state->min_cdclk[pipe], min_cdclk); + + /* + * Currently do this change only if we need to increase + */ + if (new_bw_state->min_cdclk > min_cdclk) + *need_cdclk_calc = true; + } + return 0; } @@ -14757,16 +14829,13 @@ static int intel_atomic_check(struct drm_device *dev, struct drm_i915_private *dev_priv = to_i915(dev); struct intel_atomic_state *state = to_intel_atomic_state(_state); struct intel_crtc_state *old_crtc_state, *new_crtc_state; - struct intel_cdclk_state *new_cdclk_state; struct intel_crtc *crtc; int ret, i; bool any_ms = false; - /* Catch I915_MODE_FLAG_INHERITED */ for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { - if (new_crtc_state->uapi.mode.private_flags != - old_crtc_state->uapi.mode.private_flags) + if (new_crtc_state->inherited != old_crtc_state->inherited) new_crtc_state->uapi.mode_changed = true; } @@ -14868,14 +14937,10 @@ static int intel_atomic_check(struct drm_device *dev, if (ret) goto fail; - ret = intel_atomic_check_planes(state, &any_ms); + ret = intel_atomic_check_planes(state); if (ret) goto fail; - new_cdclk_state = intel_atomic_get_new_cdclk_state(state); - if (new_cdclk_state && new_cdclk_state->force_min_cdclk_changed) - any_ms = true; - /* * distrust_bios_wm will force a full dbuf recomputation * but the hardware state will only get updated accordingly @@ -14896,10 +14961,6 @@ static int intel_atomic_check(struct drm_device *dev, goto fail; } - ret = intel_atomic_check_crtcs(state); - if (ret) - goto fail; - intel_fbc_choose_crtc(dev_priv, state); ret = calc_watermark_data(state); if (ret) @@ -14909,6 +14970,22 @@ static int intel_atomic_check(struct drm_device *dev, if (ret) goto fail; + ret = intel_atomic_check_cdclk(state, &any_ms); + if (ret) + goto fail; + + if (any_ms) { + ret = intel_modeset_calc_cdclk(state); + if (ret) + return ret; + + intel_modeset_clear_plls(state); + } + + ret = intel_atomic_check_crtcs(state); + if (ret) + goto fail; + for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { if (!needs_modeset(new_crtc_state) && @@ -14939,8 +15016,24 @@ static int intel_atomic_check(struct drm_device *dev, static int intel_atomic_prepare_commit(struct intel_atomic_state *state) { - return drm_atomic_helper_prepare_planes(state->base.dev, - &state->base); + struct intel_crtc_state *crtc_state; + struct intel_crtc *crtc; + int i, ret; + + ret = drm_atomic_helper_prepare_planes(state->base.dev, &state->base); + if (ret < 0) + return ret; + + for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) { + bool mode_changed = needs_modeset(crtc_state); + + if (mode_changed || crtc_state->update_pipe || + crtc_state->uapi.color_mgmt_changed) { + intel_dsb_prepare(crtc_state); + } + } + + return 0; } u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc) @@ -15112,7 +15205,7 @@ static void intel_update_crtc(struct intel_atomic_state *state, * of enabling them on the CRTC's first fastset. */ if (new_crtc_state->update_pipe && !modeset && - old_crtc_state->hw.mode.private_flags & I915_MODE_FLAG_INHERITED) + old_crtc_state->inherited) intel_crtc_arm_fifo_underrun(crtc, new_crtc_state); } @@ -15204,29 +15297,6 @@ static void intel_commit_modeset_enables(struct intel_atomic_state *state) } } -static void icl_dbuf_slice_pre_update(struct intel_atomic_state *state) -{ - struct drm_i915_private *dev_priv = to_i915(state->base.dev); - u8 hw_enabled_slices = dev_priv->enabled_dbuf_slices_mask; - u8 required_slices = state->enabled_dbuf_slices_mask; - u8 slices_union = hw_enabled_slices | required_slices; - - /* If 2nd DBuf slice required, enable it here */ - if (INTEL_GEN(dev_priv) >= 11 && slices_union != hw_enabled_slices) - icl_dbuf_slices_update(dev_priv, slices_union); -} - -static void icl_dbuf_slice_post_update(struct intel_atomic_state *state) -{ - struct drm_i915_private *dev_priv = to_i915(state->base.dev); - u8 hw_enabled_slices = dev_priv->enabled_dbuf_slices_mask; - u8 required_slices = state->enabled_dbuf_slices_mask; - - /* If 2nd DBuf slice is no more required disable it */ - if (INTEL_GEN(dev_priv) >= 11 && required_slices != hw_enabled_slices) - icl_dbuf_slices_update(dev_priv, required_slices); -} - static void skl_commit_modeset_enables(struct intel_atomic_state *state) { struct drm_i915_private *dev_priv = to_i915(state->base.dev); @@ -15393,15 +15463,27 @@ static void intel_atomic_commit_fence_wait(struct intel_atomic_state *intel_stat &wait_reset); } +static void intel_cleanup_dsbs(struct intel_atomic_state *state) +{ + struct intel_crtc_state *old_crtc_state, *new_crtc_state; + struct intel_crtc *crtc; + int i; + + for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, + new_crtc_state, i) + intel_dsb_cleanup(old_crtc_state); +} + static void intel_atomic_cleanup_work(struct work_struct *work) { - struct drm_atomic_state *state = - container_of(work, struct drm_atomic_state, commit_work); - struct drm_i915_private *i915 = to_i915(state->dev); + struct intel_atomic_state *state = + container_of(work, struct intel_atomic_state, base.commit_work); + struct drm_i915_private *i915 = to_i915(state->base.dev); - drm_atomic_helper_cleanup_planes(&i915->drm, state); - drm_atomic_helper_commit_cleanup_done(state); - drm_atomic_state_put(state); + intel_cleanup_dsbs(state); + drm_atomic_helper_cleanup_planes(&i915->drm, &state->base); + drm_atomic_helper_commit_cleanup_done(&state->base); + drm_atomic_state_put(&state->base); intel_atomic_helper_free_state(i915); } @@ -15467,9 +15549,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) if (state->modeset) intel_encoders_update_prepare(state); - /* Enable all new slices, we might need */ - if (state->modeset) - icl_dbuf_slice_pre_update(state); + intel_dbuf_pre_plane_update(state); /* Now enable the clocks, plane, pipe, and connectors that we set up. */ dev_priv->display.commit_modeset_enables(state); @@ -15524,9 +15604,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) dev_priv->display.optimize_watermarks(state, crtc); } - /* Disable all slices, we don't need */ - if (state->modeset) - icl_dbuf_slice_post_update(state); + intel_dbuf_post_plane_update(state); for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { intel_post_plane_update(state, crtc); @@ -15535,6 +15613,13 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) modeset_put_power_domains(dev_priv, put_domains[i]); intel_modeset_verify_crtc(crtc, state, old_crtc_state, new_crtc_state); + + /* + * DSB cleanup is done in cleanup_work aligning with framebuffer + * cleanup. So copy and reset the dsb structure to sync with + * commit_done and later do dsb cleanup in cleanup_work. + */ + old_crtc_state->dsb = fetch_and_zero(&new_crtc_state->dsb); } /* Underruns don't always raise interrupts, so check manually */ @@ -15684,8 +15769,15 @@ static int intel_atomic_commit(struct drm_device *dev, intel_atomic_swap_global_state(state); if (ret) { + struct intel_crtc_state *new_crtc_state; + struct intel_crtc *crtc; + int i; + i915_sw_fence_commit(&state->commit_ready); + for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) + intel_dsb_cleanup(new_crtc_state); + drm_atomic_helper_cleanup_planes(dev, &state->base); intel_runtime_pm_put(&dev_priv->runtime_pm, state->wakeref); return ret; @@ -16405,6 +16497,9 @@ intel_cursor_plane_create(struct drm_i915_private *dev_priv, zpos = RUNTIME_INFO(dev_priv)->num_sprites[pipe] + 1; drm_plane_create_zpos_immutable_property(&cursor->base, zpos); + if (INTEL_GEN(dev_priv) >= 12) + drm_plane_enable_fb_damage_clips(&cursor->base); + drm_plane_helper_add(&cursor->base, &intel_plane_helper_funcs); return cursor; @@ -16745,7 +16840,12 @@ static void intel_setup_outputs(struct drm_i915_private *dev_priv) if (!HAS_DISPLAY(dev_priv) || !INTEL_DISPLAY_ENABLED(dev_priv)) return; - if (INTEL_GEN(dev_priv) >= 12) { + if (IS_ROCKETLAKE(dev_priv)) { + intel_ddi_init(dev_priv, PORT_A); + intel_ddi_init(dev_priv, PORT_B); + intel_ddi_init(dev_priv, PORT_D); /* DDI TC1 */ + intel_ddi_init(dev_priv, PORT_E); /* DDI TC2 */ + } else if (INTEL_GEN(dev_priv) >= 12) { intel_ddi_init(dev_priv, PORT_A); intel_ddi_init(dev_priv, PORT_B); intel_ddi_init(dev_priv, PORT_D); @@ -17420,23 +17520,35 @@ void intel_modeset_init_hw(struct drm_i915_private *i915) { struct intel_cdclk_state *cdclk_state = to_intel_cdclk_state(i915->cdclk.obj.state); + struct intel_dbuf_state *dbuf_state = + to_intel_dbuf_state(i915->dbuf.obj.state); intel_update_cdclk(i915); intel_dump_cdclk_config(&i915->cdclk.hw, "Current CDCLK"); cdclk_state->logical = cdclk_state->actual = i915->cdclk.hw; + + dbuf_state->enabled_slices = i915->dbuf.enabled_slices; } static int sanitize_watermarks_add_affected(struct drm_atomic_state *state) { struct drm_plane *plane; - struct drm_crtc *crtc; + struct intel_crtc *crtc; - drm_for_each_crtc(crtc, state->dev) { - struct drm_crtc_state *crtc_state; + for_each_intel_crtc(state->dev, crtc) { + struct intel_crtc_state *crtc_state; - crtc_state = drm_atomic_get_crtc_state(state, crtc); + crtc_state = intel_atomic_get_crtc_state(state, crtc); if (IS_ERR(crtc_state)) return PTR_ERR(crtc_state); + + if (crtc_state->hw.active) { + /* + * Preserve the inherited flag to avoid + * taking the full modeset path. + */ + crtc_state->inherited = true; + } } drm_for_each_plane(plane, state->dev) { @@ -17578,6 +17690,15 @@ retry: } if (crtc_state->hw.active) { + /* + * We've not yet detected sink capabilities + * (audio,infoframes,etc.) and thus we don't want to + * force a full state recomputation yet. We want that to + * happen only for the first real commit from userspace. + * So preserve the inherited flag for the time being. + */ + crtc_state->inherited = true; + ret = drm_atomic_add_affected_planes(state, &crtc->base); if (ret) goto out; @@ -17665,7 +17786,8 @@ static void intel_mode_config_init(struct drm_i915_private *i915) if (IS_I845G(i915) || IS_I865G(i915)) { mode_config->cursor_width = IS_I845G(i915) ? 64 : 512; mode_config->cursor_height = 1023; - } else if (IS_GEN(i915, 2)) { + } else if (IS_I830(i915) || IS_I85X(i915) || + IS_I915G(i915) || IS_I915GM(i915)) { mode_config->cursor_width = 64; mode_config->cursor_height = 64; } else { @@ -17711,6 +17833,10 @@ int intel_modeset_init_noirq(struct drm_i915_private *i915) if (ret) return ret; + ret = intel_dbuf_init(i915); + if (ret) + return ret; + ret = intel_bw_init(i915); if (ret) return ret; @@ -18227,6 +18353,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) struct drm_i915_private *dev_priv = to_i915(dev); struct intel_cdclk_state *cdclk_state = to_intel_cdclk_state(dev_priv->cdclk.obj.state); + struct intel_dbuf_state *dbuf_state = + to_intel_dbuf_state(dev_priv->dbuf.obj.state); enum pipe pipe; struct intel_crtc *crtc; struct intel_encoder *encoder; @@ -18257,7 +18385,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) enableddisabled(crtc_state->hw.active)); } - dev_priv->active_pipes = cdclk_state->active_pipes = active_pipes; + dev_priv->active_pipes = cdclk_state->active_pipes = + dbuf_state->active_pipes = active_pipes; readout_plane_state(dev_priv); @@ -18348,7 +18477,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) * set a flag to indicate that a full recalculation is * needed on the next commit. */ - mode->private_flags = I915_MODE_FLAG_INHERITED; + crtc_state->inherited = true; intel_crtc_compute_pixel_rate(crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h index efb4da205ea2..f68007ff8a13 100644 --- a/drivers/gpu/drm/i915/display/intel_display.h +++ b/drivers/gpu/drm/i915/display/intel_display.h @@ -187,6 +187,13 @@ enum plane_id { for ((__p) = PLANE_PRIMARY; (__p) < I915_MAX_PLANES; (__p)++) \ for_each_if((__crtc)->plane_ids_mask & BIT(__p)) +#define for_each_dbuf_slice_in_mask(__slice, __mask) \ + for ((__slice) = DBUF_S1; (__slice) < I915_MAX_DBUF_SLICES; (__slice)++) \ + for_each_if((BIT(__slice)) & (__mask)) + +#define for_each_dbuf_slice(__slice) \ + for_each_dbuf_slice_in_mask(__slice, BIT(I915_MAX_DBUF_SLICES) - 1) + enum port { PORT_NONE = -1, @@ -608,6 +615,7 @@ unsigned int i9xx_plane_max_stride(struct intel_plane *plane, u32 pixel_format, u64 modifier, unsigned int rotation); int bdw_get_pipemisc_bpp(struct intel_crtc *crtc); +unsigned int intel_plane_fence_y_offset(const struct intel_plane_state *plane_state); struct intel_display_error_state * intel_display_capture_error_state(struct drm_i915_private *dev_priv); diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c index 2b640d8ab9d2..d1cb48b3f462 100644 --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c @@ -125,7 +125,7 @@ static int i915_ips_status(struct seq_file *m, void *unused) wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm); seq_printf(m, "Enabled by kernel parameter: %s\n", - yesno(i915_modparams.enable_ips)); + yesno(dev_priv->params.enable_ips)); if (INTEL_GEN(dev_priv) >= 8) { seq_puts(m, "Currently: unknown\n"); @@ -2218,7 +2218,8 @@ int intel_connector_debugfs_add(struct drm_connector *connector) } if (INTEL_GEN(dev_priv) >= 10 && - (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort || + ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort && + !to_intel_connector(connector)->mst_port) || connector->connector_type == DRM_MODE_CONNECTOR_eDP)) debugfs_create_file("i915_dsc_fec_support", S_IRUGO, root, connector, &i915_dsc_fec_support_fops); diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index 49998906cc61..8a277dfbc070 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -1161,7 +1161,7 @@ static bool gen9_dc_off_power_well_enabled(struct drm_i915_private *dev_priv, static void gen9_assert_dbuf_enabled(struct drm_i915_private *dev_priv) { u8 hw_enabled_dbuf_slices = intel_enabled_dbuf_slices_mask(dev_priv); - u8 enabled_dbuf_slices = dev_priv->enabled_dbuf_slices_mask; + u8 enabled_dbuf_slices = dev_priv->dbuf.enabled_slices; drm_WARN(&dev_priv->drm, hw_enabled_dbuf_slices != enabled_dbuf_slices, @@ -1943,22 +1943,29 @@ static u64 __async_put_domains_mask(struct i915_power_domains *power_domains) static bool assert_async_put_domain_masks_disjoint(struct i915_power_domains *power_domains) { - return !WARN_ON(power_domains->async_put_domains[0] & - power_domains->async_put_domains[1]); + struct drm_i915_private *i915 = container_of(power_domains, + struct drm_i915_private, + power_domains); + return !drm_WARN_ON(&i915->drm, power_domains->async_put_domains[0] & + power_domains->async_put_domains[1]); } static bool __async_put_domains_state_ok(struct i915_power_domains *power_domains) { + struct drm_i915_private *i915 = container_of(power_domains, + struct drm_i915_private, + power_domains); enum intel_display_power_domain domain; bool err = false; err |= !assert_async_put_domain_masks_disjoint(power_domains); - err |= WARN_ON(!!power_domains->async_put_wakeref != - !!__async_put_domains_mask(power_domains)); + err |= drm_WARN_ON(&i915->drm, !!power_domains->async_put_wakeref != + !!__async_put_domains_mask(power_domains)); for_each_power_domain(domain, __async_put_domains_mask(power_domains)) - err |= WARN_ON(power_domains->domain_use_count[domain] != 1); + err |= drm_WARN_ON(&i915->drm, + power_domains->domain_use_count[domain] != 1); return !err; } @@ -2200,11 +2207,14 @@ static void queue_async_put_domains_work(struct i915_power_domains *power_domains, intel_wakeref_t wakeref) { - WARN_ON(power_domains->async_put_wakeref); + struct drm_i915_private *i915 = container_of(power_domains, + struct drm_i915_private, + power_domains); + drm_WARN_ON(&i915->drm, power_domains->async_put_wakeref); power_domains->async_put_wakeref = wakeref; - WARN_ON(!queue_delayed_work(system_unbound_wq, - &power_domains->async_put_work, - msecs_to_jiffies(100))); + drm_WARN_ON(&i915->drm, !queue_delayed_work(system_unbound_wq, + &power_domains->async_put_work, + msecs_to_jiffies(100))); } static void @@ -2913,6 +2923,53 @@ void intel_display_power_put(struct drm_i915_private *dev_priv, BIT_ULL(POWER_DOMAIN_AUX_I_TBT) | \ BIT_ULL(POWER_DOMAIN_TC_COLD_OFF)) +#define RKL_PW_4_POWER_DOMAINS ( \ + BIT_ULL(POWER_DOMAIN_PIPE_C) | \ + BIT_ULL(POWER_DOMAIN_PIPE_C_PANEL_FITTER) | \ + BIT_ULL(POWER_DOMAIN_TRANSCODER_C) | \ + BIT_ULL(POWER_DOMAIN_INIT)) + +#define RKL_PW_3_POWER_DOMAINS ( \ + RKL_PW_4_POWER_DOMAINS | \ + BIT_ULL(POWER_DOMAIN_PIPE_B) | \ + BIT_ULL(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \ + BIT_ULL(POWER_DOMAIN_AUDIO) | \ + BIT_ULL(POWER_DOMAIN_VGA) | \ + BIT_ULL(POWER_DOMAIN_TRANSCODER_B) | \ + BIT_ULL(POWER_DOMAIN_PORT_DDI_D_LANES) | \ + BIT_ULL(POWER_DOMAIN_PORT_DDI_E_LANES) | \ + BIT_ULL(POWER_DOMAIN_AUX_D) | \ + BIT_ULL(POWER_DOMAIN_AUX_E) | \ + BIT_ULL(POWER_DOMAIN_INIT)) + +/* + * There is no PW_2/PG_2 on RKL. + * + * RKL PW_1/PG_1 domains (under HW/DMC control): + * - DBUF function (note: registers are in PW0) + * - PIPE_A and its planes and VDSC/joining, except VGA + * - transcoder A + * - DDI_A and DDI_B + * - FBC + * + * RKL PW_0/PG_0 domains (under HW/DMC control): + * - PCI + * - clocks except port PLL + * - shared functions: + * * interrupts except pipe interrupts + * * MBus except PIPE_MBUS_DBOX_CTL + * * DBUF registers + * - central power except FBC + * - top-level GTC (DDI-level GTC is in the well associated with the DDI) + */ + +#define RKL_DISPLAY_DC_OFF_POWER_DOMAINS ( \ + RKL_PW_3_POWER_DOMAINS | \ + BIT_ULL(POWER_DOMAIN_MODESET) | \ + BIT_ULL(POWER_DOMAIN_AUX_A) | \ + BIT_ULL(POWER_DOMAIN_AUX_B) | \ + BIT_ULL(POWER_DOMAIN_INIT)) + static const struct i915_power_well_ops i9xx_always_on_power_well_ops = { .sync_hw = i9xx_power_well_sync_hw_noop, .enable = i9xx_always_on_power_well_noop, @@ -4283,6 +4340,140 @@ static const struct i915_power_well_desc tgl_power_wells[] = { }, }; +static const struct i915_power_well_desc rkl_power_wells[] = { + { + .name = "always-on", + .always_on = true, + .domains = POWER_DOMAIN_MASK, + .ops = &i9xx_always_on_power_well_ops, + .id = DISP_PW_ID_NONE, + }, + { + .name = "power well 1", + /* Handled by the DMC firmware */ + .always_on = true, + .domains = 0, + .ops = &hsw_power_well_ops, + .id = SKL_DISP_PW_1, + { + .hsw.regs = &hsw_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_PW_1, + .hsw.has_fuses = true, + }, + }, + { + .name = "DC off", + .domains = RKL_DISPLAY_DC_OFF_POWER_DOMAINS, + .ops = &gen9_dc_off_power_well_ops, + .id = SKL_DISP_DC_OFF, + }, + { + .name = "power well 3", + .domains = RKL_PW_3_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = ICL_DISP_PW_3, + { + .hsw.regs = &hsw_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_PW_3, + .hsw.irq_pipe_mask = BIT(PIPE_B), + .hsw.has_vga = true, + .hsw.has_fuses = true, + }, + }, + { + .name = "power well 4", + .domains = RKL_PW_4_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &hsw_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_PW_4, + .hsw.has_fuses = true, + .hsw.irq_pipe_mask = BIT(PIPE_C), + } + }, + { + .name = "DDI A IO", + .domains = ICL_DDI_IO_A_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_ddi_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_DDI_A, + } + }, + { + .name = "DDI B IO", + .domains = ICL_DDI_IO_B_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_ddi_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_DDI_B, + } + }, + { + .name = "DDI D TC1 IO", + .domains = TGL_DDI_IO_D_TC1_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_ddi_power_well_regs, + .hsw.idx = TGL_PW_CTL_IDX_DDI_TC1, + }, + }, + { + .name = "DDI E TC2 IO", + .domains = TGL_DDI_IO_E_TC2_POWER_DOMAINS, + .ops = &hsw_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_ddi_power_well_regs, + .hsw.idx = TGL_PW_CTL_IDX_DDI_TC2, + }, + }, + { + .name = "AUX A", + .domains = ICL_AUX_A_IO_POWER_DOMAINS, + .ops = &icl_aux_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_aux_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_AUX_A, + }, + }, + { + .name = "AUX B", + .domains = ICL_AUX_B_IO_POWER_DOMAINS, + .ops = &icl_aux_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_aux_power_well_regs, + .hsw.idx = ICL_PW_CTL_IDX_AUX_B, + }, + }, + { + .name = "AUX D TC1", + .domains = TGL_AUX_D_TC1_IO_POWER_DOMAINS, + .ops = &icl_aux_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_aux_power_well_regs, + .hsw.idx = TGL_PW_CTL_IDX_AUX_TC1, + }, + }, + { + .name = "AUX E TC2", + .domains = TGL_AUX_E_TC2_IO_POWER_DOMAINS, + .ops = &icl_aux_power_well_ops, + .id = DISP_PW_ID_NONE, + { + .hsw.regs = &icl_aux_power_well_regs, + .hsw.idx = TGL_PW_CTL_IDX_AUX_TC2, + }, + }, +}; + static int sanitize_disable_power_well_option(const struct drm_i915_private *dev_priv, int disable_power_well) @@ -4322,7 +4513,7 @@ static u32 get_allowed_dc_mask(const struct drm_i915_private *dev_priv, mask = 0; } - if (!i915_modparams.disable_power_well) + if (!dev_priv->params.disable_power_well) max_dc = 0; if (enable_dc >= 0 && enable_dc <= max_dc) { @@ -4365,6 +4556,9 @@ __set_power_wells(struct i915_power_domains *power_domains, const struct i915_power_well_desc *power_well_descs, int power_well_count) { + struct drm_i915_private *i915 = container_of(power_domains, + struct drm_i915_private, + power_domains); u64 power_well_ids = 0; int i; @@ -4384,8 +4578,8 @@ __set_power_wells(struct i915_power_domains *power_domains, if (id == DISP_PW_ID_NONE) continue; - WARN_ON(id >= sizeof(power_well_ids) * 8); - WARN_ON(power_well_ids & BIT_ULL(id)); + drm_WARN_ON(&i915->drm, id >= sizeof(power_well_ids) * 8); + drm_WARN_ON(&i915->drm, power_well_ids & BIT_ULL(id)); power_well_ids |= BIT_ULL(id); } @@ -4408,11 +4602,11 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv) struct i915_power_domains *power_domains = &dev_priv->power_domains; int err; - i915_modparams.disable_power_well = + dev_priv->params.disable_power_well = sanitize_disable_power_well_option(dev_priv, - i915_modparams.disable_power_well); + dev_priv->params.disable_power_well); dev_priv->csr.allowed_dc_mask = - get_allowed_dc_mask(dev_priv, i915_modparams.enable_dc); + get_allowed_dc_mask(dev_priv, dev_priv->params.enable_dc); dev_priv->csr.target_dc_state = sanitize_target_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6); @@ -4428,7 +4622,9 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv) * The enabling order will be from lower to higher indexed wells, * the disabling order is reversed. */ - if (IS_GEN(dev_priv, 12)) { + if (IS_ROCKETLAKE(dev_priv)) { + err = set_power_wells(power_domains, rkl_power_wells); + } else if (IS_GEN(dev_priv, 12)) { err = set_power_wells(power_domains, tgl_power_wells); } else if (IS_GEN(dev_priv, 11)) { err = set_power_wells(power_domains, icl_power_wells); @@ -4491,45 +4687,38 @@ static void intel_power_domains_sync_hw(struct drm_i915_private *dev_priv) mutex_unlock(&power_domains->lock); } -static bool intel_dbuf_slice_set(struct drm_i915_private *dev_priv, - i915_reg_t reg, bool enable) +static void gen9_dbuf_slice_set(struct drm_i915_private *dev_priv, + enum dbuf_slice slice, bool enable) { - u32 val, status; + i915_reg_t reg = DBUF_CTL_S(slice); + bool state; + u32 val; val = intel_de_read(dev_priv, reg); - val = enable ? (val | DBUF_POWER_REQUEST) : (val & ~DBUF_POWER_REQUEST); + if (enable) + val |= DBUF_POWER_REQUEST; + else + val &= ~DBUF_POWER_REQUEST; intel_de_write(dev_priv, reg, val); intel_de_posting_read(dev_priv, reg); udelay(10); - status = intel_de_read(dev_priv, reg) & DBUF_POWER_STATE; - if ((enable && !status) || (!enable && status)) { - drm_err(&dev_priv->drm, "DBus power %s timeout!\n", - enable ? "enable" : "disable"); - return false; - } - return true; + state = intel_de_read(dev_priv, reg) & DBUF_POWER_STATE; + drm_WARN(&dev_priv->drm, enable != state, + "DBuf slice %d power %s timeout!\n", + slice, enable ? "enable" : "disable"); } -static void gen9_dbuf_enable(struct drm_i915_private *dev_priv) +void gen9_dbuf_slices_update(struct drm_i915_private *dev_priv, + u8 req_slices) { - icl_dbuf_slices_update(dev_priv, BIT(DBUF_S1)); -} - -static void gen9_dbuf_disable(struct drm_i915_private *dev_priv) -{ - icl_dbuf_slices_update(dev_priv, 0); -} - -void icl_dbuf_slices_update(struct drm_i915_private *dev_priv, - u8 req_slices) -{ - int i; - int max_slices = INTEL_INFO(dev_priv)->num_supported_dbuf_slices; + int num_slices = INTEL_INFO(dev_priv)->num_supported_dbuf_slices; struct i915_power_domains *power_domains = &dev_priv->power_domains; + enum dbuf_slice slice; - drm_WARN(&dev_priv->drm, hweight8(req_slices) > max_slices, - "Invalid number of dbuf slices requested\n"); + drm_WARN(&dev_priv->drm, req_slices & ~(BIT(num_slices) - 1), + "Invalid set of dbuf slices (0x%x) requested (num dbuf slices %d)\n", + req_slices, num_slices); drm_dbg_kms(&dev_priv->drm, "Updating dbuf slices to 0x%x\n", req_slices); @@ -4543,36 +4732,36 @@ void icl_dbuf_slices_update(struct drm_i915_private *dev_priv, */ mutex_lock(&power_domains->lock); - for (i = 0; i < max_slices; i++) { - intel_dbuf_slice_set(dev_priv, - DBUF_CTL_S(i), - (req_slices & BIT(i)) != 0); - } + for (slice = DBUF_S1; slice < num_slices; slice++) + gen9_dbuf_slice_set(dev_priv, slice, req_slices & BIT(slice)); - dev_priv->enabled_dbuf_slices_mask = req_slices; + dev_priv->dbuf.enabled_slices = req_slices; mutex_unlock(&power_domains->lock); } -static void icl_dbuf_enable(struct drm_i915_private *dev_priv) +static void gen9_dbuf_enable(struct drm_i915_private *dev_priv) { - skl_ddb_get_hw_state(dev_priv); + dev_priv->dbuf.enabled_slices = + intel_enabled_dbuf_slices_mask(dev_priv); + /* * Just power up at least 1 slice, we will * figure out later which slices we have and what we need. */ - icl_dbuf_slices_update(dev_priv, dev_priv->enabled_dbuf_slices_mask | - BIT(DBUF_S1)); + gen9_dbuf_slices_update(dev_priv, BIT(DBUF_S1) | + dev_priv->dbuf.enabled_slices); } -static void icl_dbuf_disable(struct drm_i915_private *dev_priv) +static void gen9_dbuf_disable(struct drm_i915_private *dev_priv) { - icl_dbuf_slices_update(dev_priv, 0); + gen9_dbuf_slices_update(dev_priv, 0); } static void icl_mbus_init(struct drm_i915_private *dev_priv) { - u32 mask, val; + unsigned long abox_regs = INTEL_INFO(dev_priv)->abox_mask; + u32 mask, val, i; mask = MBUS_ABOX_BT_CREDIT_POOL1_MASK | MBUS_ABOX_BT_CREDIT_POOL2_MASK | @@ -4583,11 +4772,16 @@ static void icl_mbus_init(struct drm_i915_private *dev_priv) MBUS_ABOX_B_CREDIT(1) | MBUS_ABOX_BW_CREDIT(1); - intel_de_rmw(dev_priv, MBUS_ABOX_CTL, mask, val); - if (INTEL_GEN(dev_priv) >= 12) { - intel_de_rmw(dev_priv, MBUS_ABOX1_CTL, mask, val); - intel_de_rmw(dev_priv, MBUS_ABOX2_CTL, mask, val); - } + /* + * gen12 platforms that use abox1 and abox2 for pixel data reads still + * expect us to program the abox_ctl0 register as well, even though + * we don't have to program other instance-0 registers like BW_BUDDY. + */ + if (IS_GEN(dev_priv, 12)) + abox_regs |= BIT(0); + + for_each_set_bit(i, &abox_regs, sizeof(abox_regs)) + intel_de_rmw(dev_priv, MBUS_ABOX_CTL(i), mask, val); } static void hsw_assert_cdclk(struct drm_i915_private *dev_priv) @@ -5066,7 +5260,8 @@ static void tgl_bw_buddy_init(struct drm_i915_private *dev_priv) enum intel_dram_type type = dev_priv->dram_info.type; u8 num_channels = dev_priv->dram_info.num_channels; const struct buddy_page_mask *table; - int i; + unsigned long abox_mask = INTEL_INFO(dev_priv)->abox_mask; + int config, i; if (IS_TGL_REVID(dev_priv, TGL_REVID_A0, TGL_REVID_B0)) /* Wa_1409767108: tgl */ @@ -5074,29 +5269,27 @@ static void tgl_bw_buddy_init(struct drm_i915_private *dev_priv) else table = tgl_buddy_page_masks; - for (i = 0; table[i].page_mask != 0; i++) - if (table[i].num_channels == num_channels && - table[i].type == type) + for (config = 0; table[config].page_mask != 0; config++) + if (table[config].num_channels == num_channels && + table[config].type == type) break; - if (table[i].page_mask == 0) { + if (table[config].page_mask == 0) { drm_dbg(&dev_priv->drm, "Unknown memory configuration; disabling address buddy logic.\n"); - intel_de_write(dev_priv, BW_BUDDY1_CTL, BW_BUDDY_DISABLE); - intel_de_write(dev_priv, BW_BUDDY2_CTL, BW_BUDDY_DISABLE); + for_each_set_bit(i, &abox_mask, sizeof(abox_mask)) + intel_de_write(dev_priv, BW_BUDDY_CTL(i), + BW_BUDDY_DISABLE); } else { - intel_de_write(dev_priv, BW_BUDDY1_PAGE_MASK, - table[i].page_mask); - intel_de_write(dev_priv, BW_BUDDY2_PAGE_MASK, - table[i].page_mask); - - /* Wa_22010178259:tgl */ - intel_de_rmw(dev_priv, BW_BUDDY1_CTL, - BW_BUDDY_TLB_REQ_TIMER_MASK, - REG_FIELD_PREP(BW_BUDDY_TLB_REQ_TIMER_MASK, 0x8)); - intel_de_rmw(dev_priv, BW_BUDDY2_CTL, - BW_BUDDY_TLB_REQ_TIMER_MASK, - REG_FIELD_PREP(BW_BUDDY_TLB_REQ_TIMER_MASK, 0x8)); + for_each_set_bit(i, &abox_mask, sizeof(abox_mask)) { + intel_de_write(dev_priv, BW_BUDDY_PAGE_MASK(i), + table[config].page_mask); + + /* Wa_22010178259:tgl,rkl */ + intel_de_rmw(dev_priv, BW_BUDDY_CTL(i), + BW_BUDDY_TLB_REQ_TIMER_MASK, + BW_BUDDY_TLB_REQ_TIMER(0x8)); + } } } @@ -5105,6 +5298,7 @@ static void icl_display_core_init(struct drm_i915_private *dev_priv, { struct i915_power_domains *power_domains = &dev_priv->power_domains; struct i915_power_well *well; + u32 val; gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); @@ -5127,7 +5321,7 @@ static void icl_display_core_init(struct drm_i915_private *dev_priv, intel_cdclk_init_hw(dev_priv); /* 5. Enable DBUF. */ - icl_dbuf_enable(dev_priv); + gen9_dbuf_enable(dev_priv); /* 6. Setup MBUS. */ icl_mbus_init(dev_priv); @@ -5138,6 +5332,13 @@ static void icl_display_core_init(struct drm_i915_private *dev_priv, if (resume && dev_priv->csr.dmc_payload) intel_csr_load_program(dev_priv); + + /* Wa_14011508470 */ + if (IS_GEN(dev_priv, 12)) { + val = DCPR_CLEAR_MEMSTAT_DIS | DCPR_SEND_RESP_IMM | + DCPR_MASK_LPMODE | DCPR_MASK_MAXLATENCY_MEMUP_CLR; + intel_uncore_rmw(&dev_priv->uncore, GEN11_CHICKEN_DCPR_2, 0, val); + } } static void icl_display_core_uninit(struct drm_i915_private *dev_priv) @@ -5150,7 +5351,7 @@ static void icl_display_core_uninit(struct drm_i915_private *dev_priv) /* 1. Disable all display engine functions -> aready done */ /* 2. Disable DBUF */ - icl_dbuf_disable(dev_priv); + gen9_dbuf_disable(dev_priv); /* 3. Disable CD clock */ intel_cdclk_uninit_hw(dev_priv); @@ -5375,7 +5576,7 @@ void intel_power_domains_init_hw(struct drm_i915_private *i915, bool resume) intel_display_power_get(i915, POWER_DOMAIN_INIT); /* Disable power support if the user asked so. */ - if (!i915_modparams.disable_power_well) + if (!i915->params.disable_power_well) intel_display_power_get(i915, POWER_DOMAIN_INIT); intel_power_domains_sync_hw(i915); @@ -5399,7 +5600,7 @@ void intel_power_domains_driver_remove(struct drm_i915_private *i915) fetch_and_zero(&i915->power_domains.wakeref); /* Remove the refcount we took to keep power well support disabled. */ - if (!i915_modparams.disable_power_well) + if (!i915->params.disable_power_well) intel_display_power_put_unchecked(i915, POWER_DOMAIN_INIT); intel_display_power_flush_work_sync(i915); @@ -5488,7 +5689,7 @@ void intel_power_domains_suspend(struct drm_i915_private *i915, * Even if power well support was disabled we still want to disable * power wells if power domains must be deinitialized for suspend. */ - if (!i915_modparams.disable_power_well) + if (!i915->params.disable_power_well) intel_display_power_put_unchecked(i915, POWER_DOMAIN_INIT); intel_display_power_flush_work(i915); diff --git a/drivers/gpu/drm/i915/display/intel_display_power.h b/drivers/gpu/drm/i915/display/intel_display_power.h index 6c917699293b..54c20c76057e 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.h +++ b/drivers/gpu/drm/i915/display/intel_display_power.h @@ -314,15 +314,16 @@ intel_display_power_put_async(struct drm_i915_private *i915, enum dbuf_slice { DBUF_S1, DBUF_S2, + I915_MAX_DBUF_SLICES }; +void gen9_dbuf_slices_update(struct drm_i915_private *dev_priv, + u8 req_slices); + #define with_intel_display_power(i915, domain, wf) \ for ((wf) = intel_display_power_get((i915), (domain)); (wf); \ intel_display_power_put_async((i915), (domain), (wf)), (wf) = 0) -void icl_dbuf_slices_update(struct drm_i915_private *dev_priv, - u8 req_slices); - void chv_phy_powergate_lanes(struct intel_encoder *encoder, bool override, unsigned int mask); bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy, diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 2bf3d4cb4ea9..4b0aaa3081c9 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -479,16 +479,6 @@ struct intel_atomic_state { bool dpll_set, modeset; - /* - * Does this transaction change the pipes that are active? This mask - * tracks which CRTC's have changed their active state at the end of - * the transaction (not counting the temporary disable during modesets). - * This mask should only be non-zero when intel_state->modeset is true, - * but the converse is not necessarily true; simply changing a mode may - * not flip the final active status of any CRTC's - */ - u8 active_pipe_changes; - u8 active_pipes; struct intel_shared_dpll_state shared_dpll[I915_NUM_PLLS]; @@ -506,9 +496,6 @@ struct intel_atomic_state { */ bool global_state_changed; - /* Number of enabled DBuf slices */ - u8 enabled_dbuf_slices_mask; - struct i915_sw_fence commit_ready; struct llist_node freed; @@ -643,8 +630,7 @@ struct intel_crtc_scaler_state { int scaler_id; }; -/* drm_mode->private_flags */ -#define I915_MODE_FLAG_INHERITED (1<<0) +/* {crtc,crtc_state}->mode_flags */ /* Flag to get scanline using frame time stamps */ #define I915_MODE_FLAG_GET_SCANLINE_FROM_TIMESTAMP (1<<1) /* Flag to use the scanline counter instead of the pixel counter */ @@ -841,6 +827,7 @@ struct intel_crtc_state { bool update_wm_pre, update_wm_post; /* watermarks are updated */ bool fifo_changed; /* FIFO split is changed */ bool preload_luts; + bool inherited; /* state inherited from BIOS? */ /* Pipe source size (ie. panel fitter input size) * All planes will be positioned inside this space, @@ -956,6 +943,9 @@ struct intel_crtc_state { /* Used by SDVO (and if we ever fix it, HDMI). */ unsigned pixel_multiplier; + /* I915_MODE_FLAG_* */ + u8 mode_flags; + u8 lane_count; /* @@ -1080,6 +1070,9 @@ struct intel_crtc_state { /* Only valid on TGL+ */ enum transcoder mst_master_transcoder; + + /* For DSB related info */ + struct intel_dsb *dsb; }; enum intel_pipe_crc_source { @@ -1118,6 +1111,10 @@ struct intel_crtc { */ bool active; u8 plane_ids_mask; + + /* I915_MODE_FLAG_* */ + u8 mode_flags; + unsigned long long enabled_power_domains; struct intel_overlay *overlay; @@ -1149,9 +1146,6 @@ struct intel_crtc { /* scalers available on this crtc */ int num_scalers; - /* per pipe DSB related info */ - struct intel_dsb dsb; - #ifdef CONFIG_DEBUG_FS struct intel_pipe_crc pipe_crc; #endif @@ -1373,6 +1367,9 @@ struct intel_dp { void (*set_idle_link_train)(struct intel_dp *intel_dp); void (*set_signal_levels)(struct intel_dp *intel_dp); + u8 (*preemph_max)(struct intel_dp *intel_dp); + u8 (*voltage_max)(struct intel_dp *intel_dp); + /* Displayport compliance testing */ struct intel_dp_compliance compliance; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index cc525fda441a..c9b93c5706af 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -137,6 +137,8 @@ static const u8 valid_dsc_slicecount[] = {1, 2, 4}; * * If a CPU or PCH DP output is attached to an eDP panel, this function * will return true, and false otherwise. + * + * This function is not safe to use prior to encoder type being set. */ bool intel_dp_is_edp(struct intel_dp *intel_dp) { @@ -409,7 +411,10 @@ static int intel_dp_rate_index(const int *rates, int len, int rate) static void intel_dp_set_common_rates(struct intel_dp *intel_dp) { - WARN_ON(!intel_dp->num_source_rates || !intel_dp->num_sink_rates); + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + + drm_WARN_ON(&i915->drm, + !intel_dp->num_source_rates || !intel_dp->num_sink_rates); intel_dp->num_common_rates = intersect_rates(intel_dp->source_rates, intel_dp->num_source_rates, @@ -418,7 +423,7 @@ static void intel_dp_set_common_rates(struct intel_dp *intel_dp) intel_dp->common_rates); /* Paranoia, there should always be something in common. */ - if (WARN_ON(intel_dp->num_common_rates == 0)) { + if (drm_WARN_ON(&i915->drm, intel_dp->num_common_rates == 0)) { intel_dp->common_rates[0] = 162000; intel_dp->num_common_rates = 1; } @@ -465,6 +470,15 @@ int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, struct drm_i915_private *i915 = dp_to_i915(intel_dp); int index; + /* + * TODO: Enable fallback on MST links once MST link compute can handle + * the fallback params. + */ + if (intel_dp->is_mst) { + drm_err(&i915->drm, "Link Training Unsuccessful\n"); + return -1; + } + index = intel_dp_rate_index(intel_dp->common_rates, intel_dp->num_common_rates, link_rate); @@ -1555,6 +1569,7 @@ static ssize_t intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) { struct intel_dp *intel_dp = container_of(aux, struct intel_dp, aux); + struct drm_i915_private *i915 = dp_to_i915(intel_dp); u8 txbuf[20], rxbuf[20]; size_t txsize, rxsize; int ret; @@ -1568,10 +1583,10 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) txsize = msg->size ? HEADER_SIZE + msg->size : BARE_ADDRESS_SIZE; rxsize = 2; /* 0 or 1 data bytes */ - if (WARN_ON(txsize > 20)) + if (drm_WARN_ON(&i915->drm, txsize > 20)) return -E2BIG; - WARN_ON(!msg->buffer != !msg->size); + drm_WARN_ON(&i915->drm, !msg->buffer != !msg->size); if (msg->buffer) memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size); @@ -1596,7 +1611,7 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) txsize = msg->size ? HEADER_SIZE : BARE_ADDRESS_SIZE; rxsize = msg->size + 1; - if (WARN_ON(rxsize > 20)) + if (drm_WARN_ON(&i915->drm, rxsize > 20)) return -E2BIG; ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize, @@ -1871,10 +1886,11 @@ static void intel_dp_print_rates(struct intel_dp *intel_dp) int intel_dp_max_link_rate(struct intel_dp *intel_dp) { + struct drm_i915_private *i915 = dp_to_i915(intel_dp); int len; len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->max_link_rate); - if (WARN_ON(len <= 0)) + if (drm_WARN_ON(&i915->drm, len <= 0)) return 162000; return intel_dp->common_rates[len - 1]; @@ -1882,10 +1898,11 @@ intel_dp_max_link_rate(struct intel_dp *intel_dp) int intel_dp_rate_select(struct intel_dp *intel_dp, int rate) { + struct drm_i915_private *i915 = dp_to_i915(intel_dp); int i = intel_dp_rate_index(intel_dp->sink_rates, intel_dp->num_sink_rates, rate); - if (WARN_ON(i < 0)) + if (drm_WARN_ON(&i915->drm, i < 0)) i = 0; return i; @@ -3984,70 +4001,24 @@ intel_dp_get_link_status(struct intel_dp *intel_dp, u8 link_status[DP_LINK_STATU DP_LINK_STATUS_SIZE) == DP_LINK_STATUS_SIZE; } -/* These are source-specific values. */ -u8 -intel_dp_voltage_max(struct intel_dp *intel_dp) +static u8 intel_dp_voltage_max_2(struct intel_dp *intel_dp) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; - enum port port = encoder->port; + return DP_TRAIN_VOLTAGE_SWING_LEVEL_2; +} - if (HAS_DDI(dev_priv)) - return intel_ddi_dp_voltage_max(encoder); - else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) - return DP_TRAIN_VOLTAGE_SWING_LEVEL_3; - else if (IS_IVYBRIDGE(dev_priv) && port == PORT_A) - return DP_TRAIN_VOLTAGE_SWING_LEVEL_2; - else if (HAS_PCH_CPT(dev_priv) && port != PORT_A) - return DP_TRAIN_VOLTAGE_SWING_LEVEL_3; - else - return DP_TRAIN_VOLTAGE_SWING_LEVEL_2; +static u8 intel_dp_voltage_max_3(struct intel_dp *intel_dp) +{ + return DP_TRAIN_VOLTAGE_SWING_LEVEL_3; } -u8 -intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, u8 voltage_swing) +static u8 intel_dp_pre_empemph_max_2(struct intel_dp *intel_dp) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; - enum port port = encoder->port; + return DP_TRAIN_PRE_EMPH_LEVEL_2; +} - if (HAS_DDI(dev_priv)) { - return intel_ddi_dp_pre_emphasis_max(encoder, voltage_swing); - } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { - switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: - return DP_TRAIN_PRE_EMPH_LEVEL_3; - case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: - return DP_TRAIN_PRE_EMPH_LEVEL_2; - case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: - return DP_TRAIN_PRE_EMPH_LEVEL_1; - case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: - default: - return DP_TRAIN_PRE_EMPH_LEVEL_0; - } - } else if (IS_IVYBRIDGE(dev_priv) && port == PORT_A) { - switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: - return DP_TRAIN_PRE_EMPH_LEVEL_2; - case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: - case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: - return DP_TRAIN_PRE_EMPH_LEVEL_1; - default: - return DP_TRAIN_PRE_EMPH_LEVEL_0; - } - } else { - switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: - return DP_TRAIN_PRE_EMPH_LEVEL_2; - case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: - return DP_TRAIN_PRE_EMPH_LEVEL_2; - case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: - return DP_TRAIN_PRE_EMPH_LEVEL_1; - case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: - default: - return DP_TRAIN_PRE_EMPH_LEVEL_0; - } - } +static u8 intel_dp_pre_empemph_max_3(struct intel_dp *intel_dp) +{ + return DP_TRAIN_PRE_EMPH_LEVEL_3; } static void vlv_set_signal_levels(struct intel_dp *intel_dp) @@ -4330,6 +4301,7 @@ static u32 ivb_cpu_edp_signal_levels(u8 train_set) case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_1: return EDP_LINK_TRAIN_400MV_3_5DB_IVB; case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_2: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_2: return EDP_LINK_TRAIN_400MV_6DB_IVB; case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_0: @@ -4746,7 +4718,9 @@ intel_dp_sink_can_mst(struct intel_dp *intel_dp) static bool intel_dp_can_mst(struct intel_dp *intel_dp) { - return i915_modparams.enable_dp_mst && + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + + return i915->params.enable_dp_mst && intel_dp->can_mst && intel_dp_sink_can_mst(intel_dp); } @@ -4763,13 +4737,13 @@ intel_dp_configure_mst(struct intel_dp *intel_dp) "[ENCODER:%d:%s] MST support: port: %s, sink: %s, modparam: %s\n", encoder->base.base.id, encoder->base.name, yesno(intel_dp->can_mst), yesno(sink_can_mst), - yesno(i915_modparams.enable_dp_mst)); + yesno(i915->params.enable_dp_mst)); if (!intel_dp->can_mst) return; intel_dp->is_mst = sink_can_mst && - i915_modparams.enable_dp_mst; + i915->params.enable_dp_mst; drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst); @@ -5595,35 +5569,46 @@ update_status: "Could not write test response to sink\n"); } -static int +/** + * intel_dp_check_mst_status - service any pending MST interrupts, check link status + * @intel_dp: Intel DP struct + * + * Read any pending MST interrupts, call MST core to handle these and ack the + * interrupts. Check if the main and AUX link state is ok. + * + * Returns: + * - %true if pending interrupts were serviced (or no interrupts were + * pending) w/o detecting an error condition. + * - %false if an error condition - like AUX failure or a loss of link - is + * detected, which needs servicing from the hotplug work. + */ +static bool intel_dp_check_mst_status(struct intel_dp *intel_dp) { struct drm_i915_private *i915 = dp_to_i915(intel_dp); - bool need_retrain = false; + bool link_ok = true; - if (!intel_dp->is_mst) - return -EINVAL; - - WARN_ON_ONCE(intel_dp->active_mst_links < 0); + drm_WARN_ON_ONCE(&i915->drm, intel_dp->active_mst_links < 0); for (;;) { u8 esi[DP_DPRX_ESI_LEN] = {}; - bool bret, handled; + bool handled; int retry; - bret = intel_dp_get_sink_irq_esi(intel_dp, esi); - if (!bret) { + if (!intel_dp_get_sink_irq_esi(intel_dp, esi)) { drm_dbg_kms(&i915->drm, "failed to get ESI - device may have failed\n"); - return -EINVAL; + link_ok = false; + + break; } /* check link status - esi[10] = 0x200c */ - if (intel_dp->active_mst_links > 0 && !need_retrain && + if (intel_dp->active_mst_links > 0 && link_ok && !drm_dp_channel_eq_ok(&esi[10], intel_dp->lane_count)) { drm_dbg_kms(&i915->drm, "channel EQ not ok, retraining\n"); - need_retrain = true; + link_ok = false; } drm_dbg_kms(&i915->drm, "got esi %3ph\n", esi); @@ -5643,7 +5628,7 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) } } - return need_retrain; + return link_ok; } static bool @@ -5966,7 +5951,7 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp) u8 *dpcd = intel_dp->dpcd; u8 type; - if (WARN_ON(intel_dp_is_edp(intel_dp))) + if (drm_WARN_ON(&i915->drm, intel_dp_is_edp(intel_dp))) return connector_status_connected; if (lspcon->active) @@ -6191,7 +6176,17 @@ intel_dp_detect(struct drm_connector *connector, goto out; } - if (intel_dp->reset_link_params) { + /* Read DP Sink DSC Cap DPCD regs for DP v1.4 */ + if (INTEL_GEN(dev_priv) >= 11) + intel_dp_get_dsc_sink_cap(intel_dp); + + intel_dp_configure_mst(intel_dp); + + /* + * TODO: Reset link params when switching to MST mode, until MST + * supports link training fallback params. + */ + if (intel_dp->reset_link_params || intel_dp->is_mst) { /* Initial max link lane count */ intel_dp->max_link_lane_count = intel_dp_max_common_lane_count(intel_dp); @@ -6203,12 +6198,6 @@ intel_dp_detect(struct drm_connector *connector, intel_dp_print_rates(intel_dp); - /* Read DP Sink DSC Cap DPCD regs for DP v1.4 */ - if (INTEL_GEN(dev_priv) >= 11) - intel_dp_get_dsc_sink_cap(intel_dp); - - intel_dp_configure_mst(intel_dp); - if (intel_dp->is_mst) { /* * If we are in MST mode then this connector @@ -7294,35 +7283,10 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) } if (intel_dp->is_mst) { - switch (intel_dp_check_mst_status(intel_dp)) { - case -EINVAL: - /* - * If we were in MST mode, and device is not - * there, get out of MST mode - */ - drm_dbg_kms(&i915->drm, - "MST device may have disappeared %d vs %d\n", - intel_dp->is_mst, - intel_dp->mst_mgr.mst_state); - intel_dp->is_mst = false; - drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, - intel_dp->is_mst); - - return IRQ_NONE; - case 1: - return IRQ_NONE; - default: - break; - } - } - - if (!intel_dp->is_mst) { - bool handled; - - handled = intel_dp_short_pulse(intel_dp); - - if (!handled) + if (!intel_dp_check_mst_status(intel_dp)) return IRQ_NONE; + } else if (!intel_dp_short_pulse(intel_dp)) { + return IRQ_NONE; } return IRQ_HANDLED; @@ -8195,8 +8159,6 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_encoder->base.name)) return false; - intel_dp_set_source_rates(intel_dp); - intel_dp->reset_link_params = true; intel_dp->pps_pipe = INVALID_PIPE; intel_dp->active_pipe = INVALID_PIPE; @@ -8212,28 +8174,22 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, */ drm_WARN_ON(dev, intel_phy_is_tc(dev_priv, phy)); type = DRM_MODE_CONNECTOR_eDP; + intel_encoder->type = INTEL_OUTPUT_EDP; + + /* eDP only on port B and/or C on vlv/chv */ + if (drm_WARN_ON(dev, (IS_VALLEYVIEW(dev_priv) || + IS_CHERRYVIEW(dev_priv)) && + port != PORT_B && port != PORT_C)) + return false; } else { type = DRM_MODE_CONNECTOR_DisplayPort; } + intel_dp_set_source_rates(intel_dp); + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) intel_dp->active_pipe = vlv_active_pipe(intel_dp); - /* - * For eDP we always set the encoder type to INTEL_OUTPUT_EDP, but - * for DP the encoder type can be set by the caller to - * INTEL_OUTPUT_UNKNOWN for DDI, so don't rewrite it. - */ - if (type == DRM_MODE_CONNECTOR_eDP) - intel_encoder->type = INTEL_OUTPUT_EDP; - - /* eDP only on port B and/or C on vlv/chv */ - if (drm_WARN_ON(dev, (IS_VALLEYVIEW(dev_priv) || - IS_CHERRYVIEW(dev_priv)) && - intel_dp_is_edp(intel_dp) && - port != PORT_B && port != PORT_C)) - return false; - drm_dbg_kms(&dev_priv->drm, "Adding %s connector on [ENCODER:%d:%s]\n", type == DRM_MODE_CONNECTOR_eDP ? "eDP" : "DP", @@ -8366,6 +8322,15 @@ bool intel_dp_init(struct drm_i915_private *dev_priv, else intel_dig_port->dp.set_signal_levels = g4x_set_signal_levels; + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv) || + (HAS_PCH_SPLIT(dev_priv) && port != PORT_A)) { + intel_dig_port->dp.preemph_max = intel_dp_pre_empemph_max_3; + intel_dig_port->dp.voltage_max = intel_dp_voltage_max_3; + } else { + intel_dig_port->dp.preemph_max = intel_dp_pre_empemph_max_2; + intel_dig_port->dp.voltage_max = intel_dp_voltage_max_2; + } + intel_dig_port->dp.output_reg = output_reg; intel_dig_port->max_lanes = 4; intel_dig_port->dp.regs.dp_tp_ctl = DP_TP_CTL(port); diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 1702959ca079..0a8950f744f6 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -92,10 +92,6 @@ intel_dp_program_link_training_pattern(struct intel_dp *intel_dp, void intel_dp_set_signal_levels(struct intel_dp *intel_dp); void intel_dp_set_idle_link_train(struct intel_dp *intel_dp); -u8 -intel_dp_voltage_max(struct intel_dp *intel_dp); -u8 -intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, u8 voltage_swing); void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock, u8 *link_bw, u8 *rate_select); bool intel_dp_source_supports_hbr2(struct intel_dp *intel_dp); diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c index 0722540d64ad..acbd7eb66cbe 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -348,7 +348,7 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *intel_connector) struct intel_dp *intel_dp = enc_to_intel_dp(intel_connector->encoder); struct drm_i915_private *i915 = dp_to_i915(intel_dp); - if (i915_modparams.enable_dpcd_backlight == 0 || + if (i915->params.enable_dpcd_backlight == 0 || !intel_dp_aux_display_control_capable(intel_connector)) return -ENODEV; @@ -358,7 +358,7 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *intel_connector) */ if (i915->vbt.backlight.type != INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE && - i915_modparams.enable_dpcd_backlight != 1 && + i915->params.enable_dpcd_backlight != 1 && !drm_dp_has_quirk(&intel_dp->desc, intel_dp->edid_quirks, DP_QUIRK_FORCE_DPCD_BACKLIGHT)) { drm_info(&i915->drm, diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index e4f1843170b7..2493142a70e9 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -34,6 +34,21 @@ intel_dp_dump_link_status(const u8 link_status[DP_LINK_STATUS_SIZE]) link_status[3], link_status[4], link_status[5]); } +static u8 dp_voltage_max(u8 preemph) +{ + switch (preemph & DP_TRAIN_PRE_EMPHASIS_MASK) { + case DP_TRAIN_PRE_EMPH_LEVEL_0: + return DP_TRAIN_VOLTAGE_SWING_LEVEL_3; + case DP_TRAIN_PRE_EMPH_LEVEL_1: + return DP_TRAIN_VOLTAGE_SWING_LEVEL_2; + case DP_TRAIN_PRE_EMPH_LEVEL_2: + return DP_TRAIN_VOLTAGE_SWING_LEVEL_1; + case DP_TRAIN_PRE_EMPH_LEVEL_3: + default: + return DP_TRAIN_VOLTAGE_SWING_LEVEL_0; + } +} + void intel_dp_get_adjust_train(struct intel_dp *intel_dp, const u8 link_status[DP_LINK_STATUS_SIZE]) { @@ -44,23 +59,20 @@ void intel_dp_get_adjust_train(struct intel_dp *intel_dp, u8 preemph_max; for (lane = 0; lane < intel_dp->lane_count; lane++) { - u8 this_v = drm_dp_get_adjust_request_voltage(link_status, lane); - u8 this_p = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); - - if (this_v > v) - v = this_v; - if (this_p > p) - p = this_p; + v = max(v, drm_dp_get_adjust_request_voltage(link_status, lane)); + p = max(p, drm_dp_get_adjust_request_pre_emphasis(link_status, lane)); } - voltage_max = intel_dp_voltage_max(intel_dp); - if (v >= voltage_max) - v = voltage_max | DP_TRAIN_MAX_SWING_REACHED; - - preemph_max = intel_dp_pre_emphasis_max(intel_dp, v); + preemph_max = intel_dp->preemph_max(intel_dp); if (p >= preemph_max) p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + v = min(v, dp_voltage_max(p)); + + voltage_max = intel_dp->voltage_max(intel_dp); + if (v >= voltage_max) + v = voltage_max | DP_TRAIN_MAX_SWING_REACHED; + for (lane = 0; lane < 4; lane++) intel_dp->train_set[lane] = v | p; } diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 2e6c6375a23b..8273f2e07427 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -317,6 +317,25 @@ intel_dp_mst_atomic_check(struct drm_connector *connector, return ret; } +static void clear_act_sent(struct intel_dp *intel_dp) +{ + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + + intel_de_write(i915, intel_dp->regs.dp_tp_status, + DP_TP_STATUS_ACT_SENT); +} + +static void wait_for_act_sent(struct intel_dp *intel_dp) +{ + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + + if (intel_de_wait_for_set(i915, intel_dp->regs.dp_tp_status, + DP_TP_STATUS_ACT_SENT, 1)) + drm_err(&i915->drm, "Timed out waiting for ACT sent\n"); + + drm_dp_check_act_status(&intel_dp->mst_mgr); +} + static void intel_mst_disable_dp(struct intel_atomic_state *state, struct intel_encoder *encoder, const struct intel_crtc_state *old_crtc_state, @@ -370,6 +389,8 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state, drm_dp_update_payload_part2(&intel_dp->mst_mgr); + clear_act_sent(intel_dp); + val = intel_de_read(dev_priv, TRANS_DDI_FUNC_CTL(old_crtc_state->cpu_transcoder)); val &= ~TRANS_DDI_DP_VC_PAYLOAD_ALLOC; @@ -377,11 +398,7 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state, TRANS_DDI_FUNC_CTL(old_crtc_state->cpu_transcoder), val); - if (intel_de_wait_for_set(dev_priv, intel_dp->regs.dp_tp_status, - DP_TP_STATUS_ACT_SENT, 1)) - drm_err(&dev_priv->drm, - "Timed out waiting for ACT sent when disabling\n"); - drm_dp_check_act_status(&intel_dp->mst_mgr); + wait_for_act_sent(intel_dp); drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, connector->port); @@ -452,7 +469,6 @@ static void intel_mst_pre_enable_dp(struct intel_atomic_state *state, struct intel_connector *connector = to_intel_connector(conn_state->connector); int ret; - u32 temp; bool first_mst_stream; /* MST encoders are bound to a crtc, not to a connector, @@ -485,8 +501,6 @@ static void intel_mst_pre_enable_dp(struct intel_atomic_state *state, drm_err(&dev_priv->drm, "failed to allocate vcpi\n"); intel_dp->active_mst_links++; - temp = intel_de_read(dev_priv, intel_dp->regs.dp_tp_status); - intel_de_write(dev_priv, intel_dp->regs.dp_tp_status, temp); ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr); @@ -514,19 +528,25 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state, struct intel_digital_port *intel_dig_port = intel_mst->primary; struct intel_dp *intel_dp = &intel_dig_port->dp; struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + u32 val; drm_WARN_ON(&dev_priv->drm, pipe_config->has_pch_encoder); + clear_act_sent(intel_dp); + intel_ddi_enable_transcoder_func(encoder, pipe_config); + val = intel_de_read(dev_priv, + TRANS_DDI_FUNC_CTL(pipe_config->cpu_transcoder)); + val |= TRANS_DDI_DP_VC_PAYLOAD_ALLOC; + intel_de_write(dev_priv, + TRANS_DDI_FUNC_CTL(pipe_config->cpu_transcoder), + val); + drm_dbg_kms(&dev_priv->drm, "active links %d\n", intel_dp->active_mst_links); - if (intel_de_wait_for_set(dev_priv, intel_dp->regs.dp_tp_status, - DP_TP_STATUS_ACT_SENT, 1)) - drm_err(&dev_priv->drm, "Timed out waiting for ACT sent\n"); - - drm_dp_check_act_status(&intel_dp->mst_mgr); + wait_for_act_sent(intel_dp); drm_dp_update_payload_part2(&intel_dp->mst_mgr); diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index b45185b80bec..aeb6ee395cce 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -2934,6 +2934,15 @@ static const struct skl_wrpll_params tgl_tbt_pll_19_2MHz_values = { static const struct skl_wrpll_params tgl_tbt_pll_24MHz_values = { .dco_integer = 0x43, .dco_fraction = 0x4000, /* the following params are unused */ +}; + +/* + * Display WA #22010492432: tgl + * Divide the nominal .dco_fraction value by 2. + */ +static const struct skl_wrpll_params tgl_tbt_pll_38_4MHz_values = { + .dco_integer = 0x54, .dco_fraction = 0x1800, + /* the following params are unused */ .pdiv = 0, .kdiv = 0, .qdiv_mode = 0, .qdiv_ratio = 0, }; @@ -2970,12 +2979,14 @@ static bool icl_calc_tbt_pll(struct intel_crtc_state *crtc_state, MISSING_CASE(dev_priv->dpll.ref_clks.nssc); /* fall-through */ case 19200: - case 38400: *pll_params = tgl_tbt_pll_19_2MHz_values; break; case 24000: *pll_params = tgl_tbt_pll_24MHz_values; break; + case 38400: + *pll_params = tgl_tbt_pll_38_4MHz_values; + break; } } else { switch (dev_priv->dpll.ref_clks.nssc) { @@ -3038,49 +3049,26 @@ static int icl_ddi_combo_pll_get_freq(struct drm_i915_private *i915, icl_wrpll_ref_clock(i915)); } -static bool icl_calc_dpll_state(struct intel_crtc_state *crtc_state, - struct intel_encoder *encoder, +static void icl_calc_dpll_state(struct drm_i915_private *i915, + const struct skl_wrpll_params *pll_params, struct intel_dpll_hw_state *pll_state) { - struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); - u32 cfgcr0, cfgcr1; - struct skl_wrpll_params pll_params = { 0 }; - bool ret; - - if (intel_phy_is_tc(dev_priv, intel_port_to_phy(dev_priv, - encoder->port))) - ret = icl_calc_tbt_pll(crtc_state, &pll_params); - else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI) || - intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI)) - ret = icl_calc_wrpll(crtc_state, &pll_params); - else - ret = icl_calc_dp_combo_pll(crtc_state, &pll_params); - - if (!ret) - return false; + memset(pll_state, 0, sizeof(*pll_state)); - cfgcr0 = DPLL_CFGCR0_DCO_FRACTION(pll_params.dco_fraction) | - pll_params.dco_integer; + pll_state->cfgcr0 = DPLL_CFGCR0_DCO_FRACTION(pll_params->dco_fraction) | + pll_params->dco_integer; - cfgcr1 = DPLL_CFGCR1_QDIV_RATIO(pll_params.qdiv_ratio) | - DPLL_CFGCR1_QDIV_MODE(pll_params.qdiv_mode) | - DPLL_CFGCR1_KDIV(pll_params.kdiv) | - DPLL_CFGCR1_PDIV(pll_params.pdiv); + pll_state->cfgcr1 = DPLL_CFGCR1_QDIV_RATIO(pll_params->qdiv_ratio) | + DPLL_CFGCR1_QDIV_MODE(pll_params->qdiv_mode) | + DPLL_CFGCR1_KDIV(pll_params->kdiv) | + DPLL_CFGCR1_PDIV(pll_params->pdiv); - if (INTEL_GEN(dev_priv) >= 12) - cfgcr1 |= TGL_DPLL_CFGCR1_CFSELOVRD_NORMAL_XTAL; + if (INTEL_GEN(i915) >= 12) + pll_state->cfgcr1 |= TGL_DPLL_CFGCR1_CFSELOVRD_NORMAL_XTAL; else - cfgcr1 |= DPLL_CFGCR1_CENTRAL_FREQ_8400; - - memset(pll_state, 0, sizeof(*pll_state)); - - pll_state->cfgcr0 = cfgcr0; - pll_state->cfgcr1 = cfgcr1; - - return true; + pll_state->cfgcr1 |= DPLL_CFGCR1_CENTRAL_FREQ_8400; } - static enum tc_port icl_pll_id_to_tc_port(enum intel_dpll_id id) { return id - DPLL_ID_ICL_MGPLL1; @@ -3493,19 +3481,29 @@ static bool icl_get_combo_phy_dpll(struct intel_atomic_state *state, { struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); + struct skl_wrpll_params pll_params = { }; struct icl_port_dpll *port_dpll = &crtc_state->icl_port_dplls[ICL_PORT_DPLL_DEFAULT]; struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); enum port port = encoder->port; unsigned long dpll_mask; + int ret; - if (!icl_calc_dpll_state(crtc_state, encoder, &port_dpll->hw_state)) { + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI) || + intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI)) + ret = icl_calc_wrpll(crtc_state, &pll_params); + else + ret = icl_calc_dp_combo_pll(crtc_state, &pll_params); + + if (!ret) { drm_dbg_kms(&dev_priv->drm, "Could not calculate combo PHY PLL state.\n"); return false; } + icl_calc_dpll_state(dev_priv, &pll_params, &port_dpll->hw_state); + if (IS_ELKHARTLAKE(dev_priv) && port != PORT_A) dpll_mask = BIT(DPLL_ID_EHL_DPLL4) | @@ -3539,16 +3537,19 @@ static bool icl_get_tc_phy_dplls(struct intel_atomic_state *state, struct drm_i915_private *dev_priv = to_i915(state->base.dev); struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); + struct skl_wrpll_params pll_params = { }; struct icl_port_dpll *port_dpll; enum intel_dpll_id dpll_id; port_dpll = &crtc_state->icl_port_dplls[ICL_PORT_DPLL_DEFAULT]; - if (!icl_calc_dpll_state(crtc_state, encoder, &port_dpll->hw_state)) { + if (!icl_calc_tbt_pll(crtc_state, &pll_params)) { drm_dbg_kms(&dev_priv->drm, "Could not calculate TBT PLL state.\n"); return false; } + icl_calc_dpll_state(dev_priv, &pll_params, &port_dpll->hw_state); + port_dpll->pll = intel_find_shared_dpll(state, crtc, &port_dpll->hw_state, BIT(DPLL_ID_ICL_TBTPLL)); diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c b/drivers/gpu/drm/i915/display/intel_dsb.c index 29fec6a92d17..566fa72427b3 100644 --- a/drivers/gpu/drm/i915/display/intel_dsb.c +++ b/drivers/gpu/drm/i915/display/intel_dsb.c @@ -34,152 +34,52 @@ #define DSB_BYTE_EN_SHIFT 20 #define DSB_REG_VALUE_MASK 0xfffff -static bool is_dsb_busy(struct intel_dsb *dsb) +static bool is_dsb_busy(struct drm_i915_private *i915, enum pipe pipe, + enum dsb_id id) { - struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb); - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - enum pipe pipe = crtc->pipe; - - return DSB_STATUS & intel_de_read(dev_priv, DSB_CTRL(pipe, dsb->id)); + return DSB_STATUS & intel_de_read(i915, DSB_CTRL(pipe, id)); } -static bool intel_dsb_enable_engine(struct intel_dsb *dsb) +static bool intel_dsb_enable_engine(struct drm_i915_private *i915, + enum pipe pipe, enum dsb_id id) { - struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb); - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - enum pipe pipe = crtc->pipe; u32 dsb_ctrl; - dsb_ctrl = intel_de_read(dev_priv, DSB_CTRL(pipe, dsb->id)); + dsb_ctrl = intel_de_read(i915, DSB_CTRL(pipe, id)); if (DSB_STATUS & dsb_ctrl) { - drm_dbg_kms(&dev_priv->drm, "DSB engine is busy.\n"); + drm_dbg_kms(&i915->drm, "DSB engine is busy.\n"); return false; } dsb_ctrl |= DSB_ENABLE; - intel_de_write(dev_priv, DSB_CTRL(pipe, dsb->id), dsb_ctrl); + intel_de_write(i915, DSB_CTRL(pipe, id), dsb_ctrl); - intel_de_posting_read(dev_priv, DSB_CTRL(pipe, dsb->id)); + intel_de_posting_read(i915, DSB_CTRL(pipe, id)); return true; } -static bool intel_dsb_disable_engine(struct intel_dsb *dsb) +static bool intel_dsb_disable_engine(struct drm_i915_private *i915, + enum pipe pipe, enum dsb_id id) { - struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb); - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - enum pipe pipe = crtc->pipe; u32 dsb_ctrl; - dsb_ctrl = intel_de_read(dev_priv, DSB_CTRL(pipe, dsb->id)); + dsb_ctrl = intel_de_read(i915, DSB_CTRL(pipe, id)); if (DSB_STATUS & dsb_ctrl) { - drm_dbg_kms(&dev_priv->drm, "DSB engine is busy.\n"); + drm_dbg_kms(&i915->drm, "DSB engine is busy.\n"); return false; } dsb_ctrl &= ~DSB_ENABLE; - intel_de_write(dev_priv, DSB_CTRL(pipe, dsb->id), dsb_ctrl); + intel_de_write(i915, DSB_CTRL(pipe, id), dsb_ctrl); - intel_de_posting_read(dev_priv, DSB_CTRL(pipe, dsb->id)); + intel_de_posting_read(i915, DSB_CTRL(pipe, id)); return true; } /** - * intel_dsb_get() - Allocate DSB context and return a DSB instance. - * @crtc: intel_crtc structure to get pipe info. - * - * This function provides handle of a DSB instance, for the further DSB - * operations. - * - * Returns: address of Intel_dsb instance requested for. - * Failure: Returns the same DSB instance, but without a command buffer. - */ - -struct intel_dsb * -intel_dsb_get(struct intel_crtc *crtc) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *i915 = to_i915(dev); - struct intel_dsb *dsb = &crtc->dsb; - struct drm_i915_gem_object *obj; - struct i915_vma *vma; - u32 *buf; - intel_wakeref_t wakeref; - - if (!HAS_DSB(i915)) - return dsb; - - if (dsb->refcount++ != 0) - return dsb; - - wakeref = intel_runtime_pm_get(&i915->runtime_pm); - - obj = i915_gem_object_create_internal(i915, DSB_BUF_SIZE); - if (IS_ERR(obj)) { - drm_err(&i915->drm, "Gem object creation failed\n"); - goto out; - } - - vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0); - if (IS_ERR(vma)) { - drm_err(&i915->drm, "Vma creation failed\n"); - i915_gem_object_put(obj); - goto out; - } - - buf = i915_gem_object_pin_map(vma->obj, I915_MAP_WC); - if (IS_ERR(buf)) { - drm_err(&i915->drm, "Command buffer creation failed\n"); - goto out; - } - - dsb->id = DSB1; - dsb->vma = vma; - dsb->cmd_buf = buf; - -out: - /* - * On error dsb->cmd_buf will continue to be NULL, making the writes - * pass-through. Leave the dangling ref to be removed later by the - * corresponding intel_dsb_put(): the important error message will - * already be logged above. - */ - - intel_runtime_pm_put(&i915->runtime_pm, wakeref); - - return dsb; -} - -/** - * intel_dsb_put() - To destroy DSB context. - * @dsb: intel_dsb structure. - * - * This function destroys the DSB context allocated by a dsb_get(), by - * unpinning and releasing the VMA object associated with it. - */ - -void intel_dsb_put(struct intel_dsb *dsb) -{ - struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb); - struct drm_i915_private *i915 = to_i915(crtc->base.dev); - - if (!HAS_DSB(i915)) - return; - - if (drm_WARN_ON(&i915->drm, dsb->refcount == 0)) - return; - - if (--dsb->refcount == 0) { - i915_vma_unpin_and_release(&dsb->vma, I915_VMA_RELEASE_MAP); - dsb->cmd_buf = NULL; - dsb->free_pos = 0; - dsb->ins_start_offset = 0; - } -} - -/** * intel_dsb_indexed_reg_write() -Write to the DSB context for auto * increment register. - * @dsb: intel_dsb structure. + * @crtc_state: intel_crtc_state structure * @reg: register address. * @val: value. * @@ -189,19 +89,20 @@ void intel_dsb_put(struct intel_dsb *dsb) * is done through mmio write. */ -void intel_dsb_indexed_reg_write(struct intel_dsb *dsb, i915_reg_t reg, - u32 val) +void intel_dsb_indexed_reg_write(const struct intel_crtc_state *crtc_state, + i915_reg_t reg, u32 val) { - struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb); + struct intel_dsb *dsb = crtc_state->dsb; + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - u32 *buf = dsb->cmd_buf; + u32 *buf; u32 reg_val; - if (!buf) { + if (!dsb) { intel_de_write(dev_priv, reg, val); return; } - + buf = dsb->cmd_buf; if (drm_WARN_ON(&dev_priv->drm, dsb->free_pos >= DSB_BUF_SIZE)) { drm_dbg_kms(&dev_priv->drm, "DSB buffer overflow\n"); return; @@ -256,7 +157,7 @@ void intel_dsb_indexed_reg_write(struct intel_dsb *dsb, i915_reg_t reg, /** * intel_dsb_reg_write() -Write to the DSB context for normal * register. - * @dsb: intel_dsb structure. + * @crtc_state: intel_crtc_state structure * @reg: register address. * @val: value. * @@ -265,17 +166,21 @@ void intel_dsb_indexed_reg_write(struct intel_dsb *dsb, i915_reg_t reg, * and rest all erroneous condition register programming is done * through mmio write. */ -void intel_dsb_reg_write(struct intel_dsb *dsb, i915_reg_t reg, u32 val) +void intel_dsb_reg_write(const struct intel_crtc_state *crtc_state, + i915_reg_t reg, u32 val) { - struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - u32 *buf = dsb->cmd_buf; + struct intel_dsb *dsb; + u32 *buf; - if (!buf) { + dsb = crtc_state->dsb; + if (!dsb) { intel_de_write(dev_priv, reg, val); return; } + buf = dsb->cmd_buf; if (drm_WARN_ON(&dev_priv->drm, dsb->free_pos >= DSB_BUF_SIZE)) { drm_dbg_kms(&dev_priv->drm, "DSB buffer overflow\n"); return; @@ -290,26 +195,27 @@ void intel_dsb_reg_write(struct intel_dsb *dsb, i915_reg_t reg, u32 val) /** * intel_dsb_commit() - Trigger workload execution of DSB. - * @dsb: intel_dsb structure. + * @crtc_state: intel_crtc_state structure * * This function is used to do actual write to hardware using DSB. * On errors, fall back to MMIO. Also this function help to reset the context. */ -void intel_dsb_commit(struct intel_dsb *dsb) +void intel_dsb_commit(const struct intel_crtc_state *crtc_state) { - struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb); + struct intel_dsb *dsb = crtc_state->dsb; + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); enum pipe pipe = crtc->pipe; u32 tail; - if (!dsb->free_pos) + if (!(dsb && dsb->free_pos)) return; - if (!intel_dsb_enable_engine(dsb)) + if (!intel_dsb_enable_engine(dev_priv, pipe, dsb->id)) goto reset; - if (is_dsb_busy(dsb)) { + if (is_dsb_busy(dev_priv, pipe, dsb->id)) { drm_err(&dev_priv->drm, "HEAD_PTR write failed - dsb engine is busy.\n"); goto reset; @@ -322,7 +228,7 @@ void intel_dsb_commit(struct intel_dsb *dsb) memset(&dsb->cmd_buf[dsb->free_pos], 0, (tail - dsb->free_pos * 4)); - if (is_dsb_busy(dsb)) { + if (is_dsb_busy(dev_priv, pipe, dsb->id)) { drm_err(&dev_priv->drm, "TAIL_PTR write failed - dsb engine is busy.\n"); goto reset; @@ -332,7 +238,7 @@ void intel_dsb_commit(struct intel_dsb *dsb) i915_ggtt_offset(dsb->vma), tail); intel_de_write(dev_priv, DSB_TAIL(pipe, dsb->id), i915_ggtt_offset(dsb->vma) + tail); - if (wait_for(!is_dsb_busy(dsb), 1)) { + if (wait_for(!is_dsb_busy(dev_priv, pipe, dsb->id), 1)) { drm_err(&dev_priv->drm, "Timed out waiting for DSB workload completion.\n"); goto reset; @@ -341,5 +247,83 @@ void intel_dsb_commit(struct intel_dsb *dsb) reset: dsb->free_pos = 0; dsb->ins_start_offset = 0; - intel_dsb_disable_engine(dsb); + intel_dsb_disable_engine(dev_priv, pipe, dsb->id); +} + +/** + * intel_dsb_prepare() - Allocate, pin and map the DSB command buffer. + * @crtc_state: intel_crtc_state structure to prepare associated dsb instance. + * + * This function prepare the command buffer which is used to store dsb + * instructions with data. + */ +void intel_dsb_prepare(struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + struct drm_i915_private *i915 = to_i915(crtc->base.dev); + struct intel_dsb *dsb; + struct drm_i915_gem_object *obj; + struct i915_vma *vma; + u32 *buf; + intel_wakeref_t wakeref; + + if (!HAS_DSB(i915)) + return; + + dsb = kmalloc(sizeof(*dsb), GFP_KERNEL); + if (!dsb) { + drm_err(&i915->drm, "DSB object creation failed\n"); + return; + } + + wakeref = intel_runtime_pm_get(&i915->runtime_pm); + + obj = i915_gem_object_create_internal(i915, DSB_BUF_SIZE); + if (IS_ERR(obj)) { + drm_err(&i915->drm, "Gem object creation failed\n"); + kfree(dsb); + goto out; + } + + vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0); + if (IS_ERR(vma)) { + drm_err(&i915->drm, "Vma creation failed\n"); + i915_gem_object_put(obj); + kfree(dsb); + goto out; + } + + buf = i915_gem_object_pin_map(vma->obj, I915_MAP_WC); + if (IS_ERR(buf)) { + drm_err(&i915->drm, "Command buffer creation failed\n"); + i915_vma_unpin_and_release(&vma, I915_VMA_RELEASE_MAP); + kfree(dsb); + goto out; + } + + dsb->id = DSB1; + dsb->vma = vma; + dsb->cmd_buf = buf; + dsb->free_pos = 0; + dsb->ins_start_offset = 0; + crtc_state->dsb = dsb; +out: + intel_runtime_pm_put(&i915->runtime_pm, wakeref); +} + +/** + * intel_dsb_cleanup() - To cleanup DSB context. + * @crtc_state: intel_crtc_state structure to cleanup associated dsb instance. + * + * This function cleanup the DSB context by unpinning and releasing + * the VMA object associated with it. + */ +void intel_dsb_cleanup(struct intel_crtc_state *crtc_state) +{ + if (!crtc_state->dsb) + return; + + i915_vma_unpin_and_release(&crtc_state->dsb->vma, I915_VMA_RELEASE_MAP); + kfree(crtc_state->dsb); + crtc_state->dsb = NULL; } diff --git a/drivers/gpu/drm/i915/display/intel_dsb.h b/drivers/gpu/drm/i915/display/intel_dsb.h index 395ef9ce558e..654a11f24b80 100644 --- a/drivers/gpu/drm/i915/display/intel_dsb.h +++ b/drivers/gpu/drm/i915/display/intel_dsb.h @@ -10,7 +10,7 @@ #include "i915_reg.h" -struct intel_crtc; +struct intel_crtc_state; struct i915_vma; enum dsb_id { @@ -22,7 +22,6 @@ enum dsb_id { }; struct intel_dsb { - long refcount; enum dsb_id id; u32 *cmd_buf; struct i915_vma *vma; @@ -41,12 +40,12 @@ struct intel_dsb { u32 ins_start_offset; }; -struct intel_dsb * -intel_dsb_get(struct intel_crtc *crtc); -void intel_dsb_put(struct intel_dsb *dsb); -void intel_dsb_reg_write(struct intel_dsb *dsb, i915_reg_t reg, u32 val); -void intel_dsb_indexed_reg_write(struct intel_dsb *dsb, i915_reg_t reg, - u32 val); -void intel_dsb_commit(struct intel_dsb *dsb); +void intel_dsb_prepare(struct intel_crtc_state *crtc_state); +void intel_dsb_cleanup(struct intel_crtc_state *crtc_state); +void intel_dsb_reg_write(const struct intel_crtc_state *crtc_state, + i915_reg_t reg, u32 val); +void intel_dsb_indexed_reg_write(const struct intel_crtc_state *crtc_state, + i915_reg_t reg, u32 val); +void intel_dsb_commit(const struct intel_crtc_state *crtc_state); #endif diff --git a/drivers/gpu/drm/i915/display/intel_fbc.c b/drivers/gpu/drm/i915/display/intel_fbc.c index 1c26673acb2d..69a0682ddb6a 100644 --- a/drivers/gpu/drm/i915/display/intel_fbc.c +++ b/drivers/gpu/drm/i915/display/intel_fbc.c @@ -48,19 +48,6 @@ #include "intel_frontbuffer.h" /* - * In some platforms where the CRTC's x:0/y:0 coordinates doesn't match the - * frontbuffer's x:0/y:0 coordinates we lie to the hardware about the plane's - * origin so the x and y offsets can actually fit the registers. As a - * consequence, the fence doesn't really start exactly at the display plane - * address we program because it starts at the real start of the buffer, so we - * have to take this into consideration here. - */ -static unsigned int get_crtc_fence_y_offset(struct intel_fbc *fbc) -{ - return fbc->state_cache.plane.y - fbc->state_cache.plane.adjusted_y; -} - -/* * For SKL+, the plane source size used by the hardware is based on the value we * write to the PLANE_SIZE register. For BDW-, the hardware looks at the value * we wrote to PIPESRC. @@ -141,18 +128,17 @@ static void i8xx_fbc_activate(struct drm_i915_private *dev_priv) fbc_ctl2 |= FBC_CTL_CPU_FENCE; intel_de_write(dev_priv, FBC_CONTROL2, fbc_ctl2); intel_de_write(dev_priv, FBC_FENCE_OFF, - params->crtc.fence_y_offset); + params->fence_y_offset); } /* enable it... */ - fbc_ctl = intel_de_read(dev_priv, FBC_CONTROL); - fbc_ctl &= 0x3fff << FBC_CTL_INTERVAL_SHIFT; + fbc_ctl = FBC_CTL_INTERVAL(params->interval); fbc_ctl |= FBC_CTL_EN | FBC_CTL_PERIODIC; if (IS_I945GM(dev_priv)) fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */ - fbc_ctl |= (cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT; + fbc_ctl |= FBC_CTL_STRIDE(cfb_pitch & 0xff); if (params->fence_id >= 0) - fbc_ctl |= params->fence_id; + fbc_ctl |= FBC_CTL_FENCENO(params->fence_id); intel_de_write(dev_priv, FBC_CONTROL, fbc_ctl); } @@ -175,7 +161,7 @@ static void g4x_fbc_activate(struct drm_i915_private *dev_priv) if (params->fence_id >= 0) { dpfc_ctl |= DPFC_CTL_FENCE_EN | params->fence_id; intel_de_write(dev_priv, DPFC_FENCE_YOFF, - params->crtc.fence_y_offset); + params->fence_y_offset); } else { intel_de_write(dev_priv, DPFC_FENCE_YOFF, 0); } @@ -243,7 +229,7 @@ static void ilk_fbc_activate(struct drm_i915_private *dev_priv) intel_de_write(dev_priv, SNB_DPFC_CTL_SA, SNB_CPU_FENCE_ENABLE | params->fence_id); intel_de_write(dev_priv, DPFC_CPU_FENCE_OFFSET, - params->crtc.fence_y_offset); + params->fence_y_offset); } } else { if (IS_GEN(dev_priv, 6)) { @@ -253,7 +239,7 @@ static void ilk_fbc_activate(struct drm_i915_private *dev_priv) } intel_de_write(dev_priv, ILK_DPFC_FENCE_YOFF, - params->crtc.fence_y_offset); + params->fence_y_offset); /* enable it... */ intel_de_write(dev_priv, ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN); @@ -320,7 +306,7 @@ static void gen7_fbc_activate(struct drm_i915_private *dev_priv) intel_de_write(dev_priv, SNB_DPFC_CTL_SA, SNB_CPU_FENCE_ENABLE | params->fence_id); intel_de_write(dev_priv, DPFC_CPU_FENCE_OFFSET, - params->crtc.fence_y_offset); + params->fence_y_offset); } else if (dev_priv->ggtt.num_fences) { intel_de_write(dev_priv, SNB_DPFC_CTL_SA, 0); intel_de_write(dev_priv, DPFC_CPU_FENCE_OFFSET, 0); @@ -631,8 +617,8 @@ static bool rotation_is_valid(struct drm_i915_private *dev_priv, /* * For some reason, the hardware tracking starts looking at whatever we * programmed as the display plane base address register. It does not look at - * the X and Y offset registers. That's why we look at the crtc->adjusted{x,y} - * variables instead of just looking at the pipe/plane size. + * the X and Y offset registers. That's why we include the src x/y offsets + * instead of just looking at the plane size. */ static bool intel_fbc_hw_tracking_covers_screen(struct intel_crtc *crtc) { @@ -705,7 +691,6 @@ static void intel_fbc_update_state_cache(struct intel_crtc *crtc, cache->plane.src_h = drm_rect_height(&plane_state->uapi.src) >> 16; cache->plane.adjusted_x = plane_state->color_plane[0].x; cache->plane.adjusted_y = plane_state->color_plane[0].y; - cache->plane.y = plane_state->uapi.src.y1 >> 16; cache->plane.pixel_blend_mode = plane_state->hw.pixel_blend_mode; @@ -713,6 +698,11 @@ static void intel_fbc_update_state_cache(struct intel_crtc *crtc, cache->fb.stride = fb->pitches[0]; cache->fb.modifier = fb->modifier; + /* FBC1 compression interval: arbitrary choice of 1 second */ + cache->interval = drm_mode_vrefresh(&crtc_state->hw.adjusted_mode); + + cache->fence_y_offset = intel_plane_fence_y_offset(plane_state); + drm_WARN_ON(&dev_priv->drm, plane_state->flags & PLANE_HAS_FENCE && !plane_state->vma->fence); @@ -740,7 +730,7 @@ static bool intel_fbc_can_enable(struct drm_i915_private *dev_priv) return false; } - if (!i915_modparams.enable_fbc) { + if (!dev_priv->params.enable_fbc) { fbc->no_fbc_reason = "disabled per module param or by default"; return false; } @@ -883,10 +873,12 @@ static void intel_fbc_get_reg_params(struct intel_crtc *crtc, memset(params, 0, sizeof(*params)); params->fence_id = cache->fence_id; + params->fence_y_offset = cache->fence_y_offset; + + params->interval = cache->interval; params->crtc.pipe = crtc->pipe; params->crtc.i9xx_plane = to_intel_plane(crtc->base.primary)->i9xx_plane; - params->crtc.fence_y_offset = get_crtc_fence_y_offset(fbc); params->fb.format = cache->fb.format; params->fb.stride = cache->fb.stride; @@ -1017,7 +1009,7 @@ static void __intel_fbc_post_update(struct intel_crtc *crtc) fbc->flip_pending = false; - if (!i915_modparams.enable_fbc) { + if (!dev_priv->params.enable_fbc) { intel_fbc_deactivate(dev_priv, "disabled at runtime per module param"); __intel_fbc_disable(dev_priv); @@ -1090,11 +1082,19 @@ void intel_fbc_flush(struct drm_i915_private *dev_priv, if (!HAS_FBC(dev_priv)) return; + /* + * GTT tracking does not nuke the entire cfb + * so don't clear busy_bits set for some other + * reason. + */ + if (origin == ORIGIN_GTT) + return; + mutex_lock(&fbc->lock); fbc->busy_bits &= ~frontbuffer_bits; - if (origin == ORIGIN_GTT || origin == ORIGIN_FLIP) + if (origin == ORIGIN_FLIP) goto out; if (!fbc->busy_bits && fbc->crtc && @@ -1370,8 +1370,8 @@ void intel_fbc_handle_fifo_underrun_irq(struct drm_i915_private *dev_priv) */ static int intel_sanitize_fbc_option(struct drm_i915_private *dev_priv) { - if (i915_modparams.enable_fbc >= 0) - return !!i915_modparams.enable_fbc; + if (dev_priv->params.enable_fbc >= 0) + return !!dev_priv->params.enable_fbc; if (!HAS_FBC(dev_priv)) return 0; @@ -1415,20 +1415,15 @@ void intel_fbc_init(struct drm_i915_private *dev_priv) if (need_fbc_vtd_wa(dev_priv)) mkwrite_device_info(dev_priv)->display.has_fbc = false; - i915_modparams.enable_fbc = intel_sanitize_fbc_option(dev_priv); + dev_priv->params.enable_fbc = intel_sanitize_fbc_option(dev_priv); drm_dbg_kms(&dev_priv->drm, "Sanitized enable_fbc value: %d\n", - i915_modparams.enable_fbc); + dev_priv->params.enable_fbc); if (!HAS_FBC(dev_priv)) { fbc->no_fbc_reason = "unsupported by this chipset"; return; } - /* This value was pulled out of someone's hat */ - if (INTEL_GEN(dev_priv) <= 4 && !IS_GM45(dev_priv)) - intel_de_write(dev_priv, FBC_CONTROL, - 500 << FBC_CTL_INTERVAL_SHIFT); - /* We still don't have any sort of hardware state readout for FBC, so * deactivate it in case the BIOS activated it to make sure software * matches the hardware state. */ diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index 2cbc4619b4ce..815b054bb167 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -1923,8 +1923,11 @@ static bool is_hdcp2_supported(struct drm_i915_private *dev_priv) if (!IS_ENABLED(CONFIG_INTEL_MEI_HDCP)) return false; - return (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) || - IS_KABYLAKE(dev_priv) || IS_COFFEELAKE(dev_priv)); + return (INTEL_GEN(dev_priv) >= 10 || + IS_GEMINILAKE(dev_priv) || + IS_KABYLAKE(dev_priv) || + IS_COFFEELAKE(dev_priv) || + IS_COMETLAKE(dev_priv)); } void intel_hdcp_component_init(struct drm_i915_private *dev_priv) diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 010f37240710..864a1642e81c 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -1540,7 +1540,7 @@ int intel_hdmi_hdcp_toggle_signalling(struct intel_digital_port *intel_dig_port, } static -bool intel_hdmi_hdcp_check_link(struct intel_digital_port *intel_dig_port) +bool intel_hdmi_hdcp_check_link_once(struct intel_digital_port *intel_dig_port) { struct drm_i915_private *i915 = to_i915(intel_dig_port->base.base.dev); struct intel_connector *connector = @@ -1563,8 +1563,7 @@ bool intel_hdmi_hdcp_check_link(struct intel_digital_port *intel_dig_port) if (wait_for((intel_de_read(i915, HDCP_STATUS(i915, cpu_transcoder, port)) & (HDCP_STATUS_RI_MATCH | HDCP_STATUS_ENC)) == (HDCP_STATUS_RI_MATCH | HDCP_STATUS_ENC), 1)) { - drm_err(&i915->drm, - "Ri' mismatch detected, link check failed (%x)\n", + drm_dbg_kms(&i915->drm, "Ri' mismatch detected (%x)\n", intel_de_read(i915, HDCP_STATUS(i915, cpu_transcoder, port))); return false; @@ -1572,6 +1571,20 @@ bool intel_hdmi_hdcp_check_link(struct intel_digital_port *intel_dig_port) return true; } +static +bool intel_hdmi_hdcp_check_link(struct intel_digital_port *intel_dig_port) +{ + struct drm_i915_private *i915 = to_i915(intel_dig_port->base.base.dev); + int retry; + + for (retry = 0; retry < 3; retry++) + if (intel_hdmi_hdcp_check_link_once(intel_dig_port)) + return true; + + drm_err(&i915->drm, "Link check failed\n"); + return false; +} + struct hdcp2_hdmi_msg_timeout { u8 msg_id; u16 timeout; @@ -3082,6 +3095,24 @@ static u8 mcc_port_to_ddc_pin(struct drm_i915_private *dev_priv, enum port port) return ddc_pin; } +static u8 rkl_port_to_ddc_pin(struct drm_i915_private *dev_priv, enum port port) +{ + enum phy phy = intel_port_to_phy(dev_priv, port); + + WARN_ON(port == PORT_C); + + /* + * Pin mapping for RKL depends on which PCH is present. With TGP, the + * final two outputs use type-c pins, even though they're actually + * combo outputs. With CMP, the traditional DDI A-D pins are used for + * all outputs. + */ + if (INTEL_PCH_TYPE(dev_priv) >= PCH_TGP && phy >= PHY_C) + return GMBUS_PIN_9_TC1_ICP + phy - PHY_C; + + return GMBUS_PIN_1_BXT + phy; +} + static u8 g4x_port_to_ddc_pin(struct drm_i915_private *dev_priv, enum port port) { @@ -3119,7 +3150,9 @@ static u8 intel_hdmi_ddc_pin(struct intel_encoder *encoder) return ddc_pin; } - if (HAS_PCH_MCC(dev_priv)) + if (IS_ROCKETLAKE(dev_priv)) + ddc_pin = rkl_port_to_ddc_pin(dev_priv, port); + else if (HAS_PCH_MCC(dev_priv)) ddc_pin = mcc_port_to_ddc_pin(dev_priv, port); else if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP) ddc_pin = icl_port_to_ddc_pin(dev_priv, port); diff --git a/drivers/gpu/drm/i915/display/intel_hotplug.c b/drivers/gpu/drm/i915/display/intel_hotplug.c index 664f88354101..3f1d7b804a66 100644 --- a/drivers/gpu/drm/i915/display/intel_hotplug.c +++ b/drivers/gpu/drm/i915/display/intel_hotplug.c @@ -89,6 +89,15 @@ enum hpd_pin intel_hpd_pin_default(struct drm_i915_private *dev_priv, { enum phy phy = intel_port_to_phy(dev_priv, port); + /* + * RKL + TGP PCH is a special case; we effectively choose the hpd_pin + * based on the DDI rather than the PHY (i.e., the last two outputs + * shold be HPD_PORT_{D,E} rather than {C,D}. Note that this differs + * from the behavior of both TGL+TGP and RKL+CMP. + */ + if (IS_ROCKETLAKE(dev_priv) && HAS_PCH_TGP(dev_priv)) + return HPD_PORT_A + port - PORT_A; + switch (phy) { case PHY_F: return IS_CNL_WITH_PORT_F(dev_priv) ? HPD_PORT_E : HPD_PORT_F; @@ -274,24 +283,30 @@ intel_encoder_hotplug(struct intel_encoder *encoder, { struct drm_device *dev = connector->base.dev; enum drm_connector_status old_status; + u64 old_epoch_counter; + bool ret = false; drm_WARN_ON(dev, !mutex_is_locked(&dev->mode_config.mutex)); old_status = connector->base.status; + old_epoch_counter = connector->base.epoch_counter; connector->base.status = drm_helper_probe_detect(&connector->base, NULL, false); - if (old_status == connector->base.status) - return INTEL_HOTPLUG_UNCHANGED; - - drm_dbg_kms(&to_i915(dev)->drm, - "[CONNECTOR:%d:%s] status updated from %s to %s\n", - connector->base.base.id, - connector->base.name, - drm_get_connector_status_name(old_status), - drm_get_connector_status_name(connector->base.status)); + if (old_epoch_counter != connector->base.epoch_counter) + ret = true; - return INTEL_HOTPLUG_CHANGED; + if (ret) { + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s (epoch counter %llu->%llu)\n", + connector->base.base.id, + connector->base.name, + drm_get_connector_status_name(old_status), + drm_get_connector_status_name(connector->base.status), + old_epoch_counter, + connector->base.epoch_counter); + return INTEL_HOTPLUG_CHANGED; + } + return INTEL_HOTPLUG_UNCHANGED; } static bool intel_encoder_has_hpd_pulse(struct intel_encoder *encoder) diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c index 872f2a489339..1888611244db 100644 --- a/drivers/gpu/drm/i915/display/intel_lvds.c +++ b/drivers/gpu/drm/i915/display/intel_lvds.c @@ -784,8 +784,8 @@ static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder) struct drm_i915_private *dev_priv = to_i915(dev); /* use the module option value if specified */ - if (i915_modparams.lvds_channel_mode > 0) - return i915_modparams.lvds_channel_mode == 2; + if (dev_priv->params.lvds_channel_mode > 0) + return dev_priv->params.lvds_channel_mode == 2; /* single channel LVDS is limited to 112 MHz */ if (lvds_encoder->attached_connector->panel.fixed_mode->clock > 112999) diff --git a/drivers/gpu/drm/i915/display/intel_opregion.c b/drivers/gpu/drm/i915/display/intel_opregion.c index cc6b00959586..de995362f428 100644 --- a/drivers/gpu/drm/i915/display/intel_opregion.c +++ b/drivers/gpu/drm/i915/display/intel_opregion.c @@ -801,7 +801,7 @@ static int intel_load_vbt_firmware(struct drm_i915_private *dev_priv) { struct intel_opregion *opregion = &dev_priv->opregion; const struct firmware *fw = NULL; - const char *name = i915_modparams.vbt_firmware; + const char *name = dev_priv->params.vbt_firmware; int ret; if (!name || !*name) diff --git a/drivers/gpu/drm/i915/display/intel_overlay.c b/drivers/gpu/drm/i915/display/intel_overlay.c index 66711e62fa71..52b4f6193b4c 100644 --- a/drivers/gpu/drm/i915/display/intel_overlay.c +++ b/drivers/gpu/drm/i915/display/intel_overlay.c @@ -100,12 +100,15 @@ #define CLK_RGB24_MASK 0x0 #define CLK_RGB16_MASK 0x070307 #define CLK_RGB15_MASK 0x070707 -#define CLK_RGB8I_MASK 0xffffff +#define RGB30_TO_COLORKEY(c) \ + ((((c) & 0x3fc00000) >> 6) | (((c) & 0x000ff000) >> 4) | (((c) & 0x000003fc) >> 2)) #define RGB16_TO_COLORKEY(c) \ - (((c & 0xF800) << 8) | ((c & 0x07E0) << 5) | ((c & 0x001F) << 3)) + ((((c) & 0xf800) << 8) | (((c) & 0x07e0) << 5) | (((c) & 0x001f) << 3)) #define RGB15_TO_COLORKEY(c) \ - (((c & 0x7c00) << 9) | ((c & 0x03E0) << 6) | ((c & 0x001F) << 3)) + ((((c) & 0x7c00) << 9) | (((c) & 0x03e0) << 6) | (((c) & 0x001f) << 3)) +#define RGB8I_TO_COLORKEY(c) \ + ((((c) & 0xff) << 16) | (((c) & 0xff) << 8) | (((c) & 0xff) << 0)) /* overlay flip addr flag */ #define OFC_UPDATE 0x1 @@ -682,8 +685,8 @@ static void update_colorkey(struct intel_overlay *overlay, switch (format) { case DRM_FORMAT_C8: - key = 0; - flags |= CLK_RGB8I_MASK; + key = RGB8I_TO_COLORKEY(key); + flags |= CLK_RGB24_MASK; break; case DRM_FORMAT_XRGB1555: key = RGB15_TO_COLORKEY(key); @@ -693,6 +696,11 @@ static void update_colorkey(struct intel_overlay *overlay, key = RGB16_TO_COLORKEY(key); flags |= CLK_RGB16_MASK; break; + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_XBGR2101010: + key = RGB30_TO_COLORKEY(key); + flags |= CLK_RGB24_MASK; + break; default: flags |= CLK_RGB24_MASK; break; @@ -777,9 +785,15 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, i915_gem_object_flush_frontbuffer(new_bo, ORIGIN_DIRTYFB); if (!overlay->active) { - u32 oconfig; - - oconfig = OCONF_CC_OUT_8BIT; + const struct intel_crtc_state *crtc_state = + overlay->crtc->config; + u32 oconfig = 0; + + if (crtc_state->gamma_enable && + crtc_state->gamma_mode == GAMMA_MODE_MODE_8BIT) + oconfig |= OCONF_CC_OUT_8BIT; + if (crtc_state->gamma_enable) + oconfig |= OCONF_GAMMA2_ENABLE; if (IS_GEN(dev_priv, 4)) oconfig |= OCONF_CSC_MODE_BT709; oconfig |= pipe == 0 ? diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 3c5056dbf607..aaed9eb3b56c 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -521,10 +521,10 @@ static u32 intel_panel_compute_brightness(struct intel_connector *connector, drm_WARN_ON(&dev_priv->drm, panel->backlight.max == 0); - if (i915_modparams.invert_brightness < 0) + if (dev_priv->params.invert_brightness < 0) return val; - if (i915_modparams.invert_brightness > 0 || + if (dev_priv->params.invert_brightness > 0 || dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) { return panel->backlight.max - val + panel->backlight.min; } diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index b7a2c102648a..611cb8d74811 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -83,7 +83,7 @@ static bool psr_global_enabled(struct drm_i915_private *i915) { switch (i915->psr.debug & I915_PSR_DEBUG_MODE_MASK) { case I915_PSR_DEBUG_DEFAULT: - return i915_modparams.enable_psr; + return i915->params.enable_psr; case I915_PSR_DEBUG_DISABLE: return false; default: @@ -426,6 +426,12 @@ static u32 intel_psr1_get_tp_time(struct intel_dp *intel_dp) if (INTEL_GEN(dev_priv) >= 11) val |= EDP_PSR_TP4_TIME_0US; + if (dev_priv->params.psr_safest_params) { + val |= EDP_PSR_TP1_TIME_2500us; + val |= EDP_PSR_TP2_TP3_TIME_2500us; + goto check_tp3_sel; + } + if (dev_priv->vbt.psr.tp1_wakeup_time_us == 0) val |= EDP_PSR_TP1_TIME_0us; else if (dev_priv->vbt.psr.tp1_wakeup_time_us <= 100) @@ -444,6 +450,7 @@ static u32 intel_psr1_get_tp_time(struct intel_dp *intel_dp) else val |= EDP_PSR_TP2_TP3_TIME_2500us; +check_tp3_sel: if (intel_dp_source_supports_hbr2(intel_dp) && drm_dp_tps3_supported(intel_dp->dpcd)) val |= EDP_PSR_TP1_TP3_SEL; @@ -495,18 +502,13 @@ static void hsw_activate_psr1(struct intel_dp *intel_dp) intel_de_write(dev_priv, EDP_PSR_CTL(dev_priv->psr.transcoder), val); } -static void hsw_activate_psr2(struct intel_dp *intel_dp) +static u32 intel_psr2_get_tp_time(struct intel_dp *intel_dp) { struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - u32 val; - - val = psr_compute_idle_frames(intel_dp) << EDP_PSR2_IDLE_FRAME_SHIFT; - - val |= EDP_PSR2_ENABLE | EDP_SU_TRACK_ENABLE; - if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv)) - val |= EDP_Y_COORDINATE_ENABLE; + u32 val = 0; - val |= EDP_PSR2_FRAME_BEFORE_SU(dev_priv->psr.sink_sync_latency + 1); + if (dev_priv->params.psr_safest_params) + return EDP_PSR2_TP2_TIME_2500us; if (dev_priv->vbt.psr.psr2_tp2_tp3_wakeup_time_us >= 0 && dev_priv->vbt.psr.psr2_tp2_tp3_wakeup_time_us <= 50) @@ -518,6 +520,39 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp) else val |= EDP_PSR2_TP2_TIME_2500us; + return val; +} + +static void hsw_activate_psr2(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + u32 val; + + val = psr_compute_idle_frames(intel_dp) << EDP_PSR2_IDLE_FRAME_SHIFT; + + val |= EDP_PSR2_ENABLE | EDP_SU_TRACK_ENABLE; + if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv)) + val |= EDP_Y_COORDINATE_ENABLE; + + val |= EDP_PSR2_FRAME_BEFORE_SU(dev_priv->psr.sink_sync_latency + 1); + val |= intel_psr2_get_tp_time(intel_dp); + + if (INTEL_GEN(dev_priv) >= 12) { + /* + * TODO: 7 lines of IO_BUFFER_WAKE and FAST_WAKE are default + * values from BSpec. In order to setting an optimal power + * consumption, lower than 4k resoluition mode needs to decrese + * IO_BUFFER_WAKE and FAST_WAKE. And higher than 4K resolution + * mode needs to increase IO_BUFFER_WAKE and FAST_WAKE. + */ + val |= TGL_EDP_PSR2_BLOCK_COUNT_NUM_2; + val |= TGL_EDP_PSR2_IO_BUFFER_WAKE(7); + val |= TGL_EDP_PSR2_FAST_WAKE(7); + } else if (INTEL_GEN(dev_priv) >= 9) { + val |= EDP_PSR2_IO_BUFFER_WAKE(7); + val |= EDP_PSR2_FAST_WAKE(7); + } + /* * PSR2 HW is incorrectly using EDP_PSR_TP1_TP3_SEL and BSpec is * recommending keep this bit unset while PSR2 is enabled. @@ -657,6 +692,12 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp, return false; } + if (crtc_state->crc_enabled) { + drm_dbg_kms(&dev_priv->drm, + "PSR2 not enabled because it would inhibit pipe CRC calculation\n"); + return false; + } + if (INTEL_GEN(dev_priv) >= 12) { psr_max_h = 5120; psr_max_v = 3200; @@ -671,14 +712,6 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp, max_bpp = 24; } - if (crtc_hdisplay > psr_max_h || crtc_vdisplay > psr_max_v) { - drm_dbg_kms(&dev_priv->drm, - "PSR2 not enabled, resolution %dx%d > max supported %dx%d\n", - crtc_hdisplay, crtc_vdisplay, - psr_max_h, psr_max_v); - return false; - } - if (crtc_state->pipe_bpp > max_bpp) { drm_dbg_kms(&dev_priv->drm, "PSR2 not enabled, pipe bpp %d > max supported %d\n", @@ -699,9 +732,26 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp, return false; } - if (crtc_state->crc_enabled) { + /* + * Some platforms lack PSR2 HW tracking and instead require manual + * tracking by software. In this case, the driver is required to track + * the areas that need updates and program hardware to send selective + * updates. + * + * So until the software tracking is implemented, PSR2 needs to be + * disabled for platforms without PSR2 HW tracking. + */ + if (!HAS_PSR_HW_TRACKING(dev_priv)) { drm_dbg_kms(&dev_priv->drm, - "PSR2 not enabled because it would inhibit pipe CRC calculation\n"); + "No PSR2 HW tracking in the platform\n"); + return false; + } + + if (crtc_hdisplay > psr_max_h || crtc_vdisplay > psr_max_v) { + drm_dbg_kms(&dev_priv->drm, + "PSR2 not enabled, resolution %dx%d > max supported %dx%d\n", + crtc_hdisplay, crtc_vdisplay, + psr_max_h, psr_max_v); return false; } @@ -1450,9 +1500,9 @@ void intel_psr_init(struct drm_i915_private *dev_priv) */ dev_priv->hsw_psr_mmio_adjust = _SRD_CTL_EDP - _HSW_EDP_PSR_BASE; - if (i915_modparams.enable_psr == -1) + if (dev_priv->params.enable_psr == -1) if (INTEL_GEN(dev_priv) < 9 || !dev_priv->vbt.psr.enable) - i915_modparams.enable_psr = 0; + dev_priv->params.enable_psr = 0; /* Set link_standby x link_off defaults */ if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c index bc6c26818e15..773523dcd107 100644 --- a/drivers/gpu/drm/i915/display/intel_sdvo.c +++ b/drivers/gpu/drm/i915/display/intel_sdvo.c @@ -411,6 +411,7 @@ static const char *sdvo_cmd_name(u8 cmd) static void intel_sdvo_debug_write(struct intel_sdvo *intel_sdvo, u8 cmd, const void *args, int args_len) { + struct drm_i915_private *dev_priv = to_i915(intel_sdvo->base.base.dev); const char *cmd_name; int i, pos = 0; char buffer[64]; @@ -431,7 +432,7 @@ static void intel_sdvo_debug_write(struct intel_sdvo *intel_sdvo, u8 cmd, else BUF_PRINT("(%02X)", cmd); - WARN_ON(pos >= sizeof(buffer) - 1); + drm_WARN_ON(&dev_priv->drm, pos >= sizeof(buffer) - 1); #undef BUF_PRINT DRM_DEBUG_KMS("%s: W: %02X %s\n", SDVO_NAME(intel_sdvo), cmd, buffer); @@ -533,6 +534,7 @@ static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd, static bool intel_sdvo_read_response(struct intel_sdvo *intel_sdvo, void *response, int response_len) { + struct drm_i915_private *dev_priv = to_i915(intel_sdvo->base.base.dev); const char *cmd_status; u8 retry = 15; /* 5 quick checks, followed by 10 long checks */ u8 status; @@ -597,7 +599,7 @@ static bool intel_sdvo_read_response(struct intel_sdvo *intel_sdvo, BUF_PRINT(" %02X", ((u8 *)response)[i]); } - WARN_ON(pos >= sizeof(buffer) - 1); + drm_WARN_ON(&dev_priv->drm, pos >= sizeof(buffer) - 1); #undef BUF_PRINT DRM_DEBUG_KMS("%s: R: %s\n", SDVO_NAME(intel_sdvo), buffer); @@ -1081,6 +1083,7 @@ static bool intel_sdvo_compute_avi_infoframe(struct intel_sdvo *intel_sdvo, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { + struct drm_i915_private *dev_priv = to_i915(intel_sdvo->base.base.dev); struct hdmi_avi_infoframe *frame = &crtc_state->infoframes.avi.avi; const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; @@ -1106,7 +1109,7 @@ static bool intel_sdvo_compute_avi_infoframe(struct intel_sdvo *intel_sdvo, HDMI_QUANTIZATION_RANGE_FULL); ret = hdmi_avi_infoframe_check(frame); - if (WARN_ON(ret)) + if (drm_WARN_ON(&dev_priv->drm, ret)) return false; return true; @@ -1115,6 +1118,7 @@ static bool intel_sdvo_compute_avi_infoframe(struct intel_sdvo *intel_sdvo, static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo, const struct intel_crtc_state *crtc_state) { + struct drm_i915_private *dev_priv = to_i915(intel_sdvo->base.base.dev); u8 sdvo_data[HDMI_INFOFRAME_SIZE(AVI)]; const union hdmi_infoframe *frame = &crtc_state->infoframes.avi; ssize_t len; @@ -1123,11 +1127,12 @@ static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo, intel_hdmi_infoframe_enable(HDMI_INFOFRAME_TYPE_AVI)) == 0) return true; - if (WARN_ON(frame->any.type != HDMI_INFOFRAME_TYPE_AVI)) + if (drm_WARN_ON(&dev_priv->drm, + frame->any.type != HDMI_INFOFRAME_TYPE_AVI)) return false; len = hdmi_infoframe_pack_only(frame, sdvo_data, sizeof(sdvo_data)); - if (WARN_ON(len < 0)) + if (drm_WARN_ON(&dev_priv->drm, len < 0)) return false; return intel_sdvo_write_infoframe(intel_sdvo, SDVO_HBUF_INDEX_AVI_IF, @@ -1237,6 +1242,7 @@ intel_sdvo_get_preferred_input_mode(struct intel_sdvo *intel_sdvo, static void i9xx_adjust_sdvo_tv_clock(struct intel_crtc_state *pipe_config) { + struct drm_i915_private *dev_priv = to_i915(pipe_config->uapi.crtc->dev); unsigned dotclock = pipe_config->port_clock; struct dpll *clock = &pipe_config->dpll; @@ -1257,7 +1263,8 @@ static void i9xx_adjust_sdvo_tv_clock(struct intel_crtc_state *pipe_config) clock->m1 = 12; clock->m2 = 8; } else { - WARN(1, "SDVO TV clock out of range: %i\n", dotclock); + drm_WARN(&dev_priv->drm, 1, + "SDVO TV clock out of range: %i\n", dotclock); } pipe_config->clock_set = true; @@ -2293,7 +2300,7 @@ intel_sdvo_connector_atomic_get_property(struct drm_connector *connector, return 0; } - WARN_ON(1); + drm_WARN_ON(connector->dev, 1); *val = 0; } else if (property == intel_sdvo_connector->top || property == intel_sdvo_connector->bottom) diff --git a/drivers/gpu/drm/i915/display/intel_sprite.c b/drivers/gpu/drm/i915/display/intel_sprite.c index 0000ec7055f7..d03860fef2d7 100644 --- a/drivers/gpu/drm/i915/display/intel_sprite.c +++ b/drivers/gpu/drm/i915/display/intel_sprite.c @@ -34,6 +34,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_color_mgmt.h> #include <drm/drm_crtc.h> +#include <drm/drm_damage_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_plane_helper.h> #include <drm/drm_rect.h> @@ -333,6 +334,21 @@ int intel_plane_check_src_coordinates(struct intel_plane_state *plane_state) return 0; } +static u8 icl_nv12_y_plane_mask(struct drm_i915_private *i915) +{ + if (IS_ROCKETLAKE(i915)) + return BIT(PLANE_SPRITE2) | BIT(PLANE_SPRITE3); + else + return BIT(PLANE_SPRITE4) | BIT(PLANE_SPRITE5); +} + +bool icl_is_nv12_y_plane(struct drm_i915_private *dev_priv, + enum plane_id plane_id) +{ + return INTEL_GEN(dev_priv) >= 11 && + icl_nv12_y_plane_mask(dev_priv) & BIT(plane_id); +} + bool icl_is_hdr_plane(struct drm_i915_private *dev_priv, enum plane_id plane_id) { return INTEL_GEN(dev_priv) >= 11 && @@ -3003,7 +3019,7 @@ static const u32 *icl_get_plane_formats(struct drm_i915_private *dev_priv, if (icl_is_hdr_plane(dev_priv, plane_id)) { *num_formats = ARRAY_SIZE(icl_hdr_plane_formats); return icl_hdr_plane_formats; - } else if (icl_is_nv12_y_plane(plane_id)) { + } else if (icl_is_nv12_y_plane(dev_priv, plane_id)) { *num_formats = ARRAY_SIZE(icl_sdr_y_plane_formats); return icl_sdr_y_plane_formats; } else { @@ -3046,6 +3062,7 @@ skl_universal_plane_create(struct drm_i915_private *dev_priv, struct intel_plane *plane; enum drm_plane_type plane_type; unsigned int supported_rotations; + unsigned int supported_csc; const u64 *modifiers; const u32 *formats; int num_formats; @@ -3120,9 +3137,13 @@ skl_universal_plane_create(struct drm_i915_private *dev_priv, DRM_MODE_ROTATE_0, supported_rotations); + supported_csc = BIT(DRM_COLOR_YCBCR_BT601) | BIT(DRM_COLOR_YCBCR_BT709); + + if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv)) + supported_csc |= BIT(DRM_COLOR_YCBCR_BT2020); + drm_plane_create_color_properties(&plane->base, - BIT(DRM_COLOR_YCBCR_BT601) | - BIT(DRM_COLOR_YCBCR_BT709), + supported_csc, BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) | BIT(DRM_COLOR_YCBCR_FULL_RANGE), DRM_COLOR_YCBCR_BT709, @@ -3136,6 +3157,9 @@ skl_universal_plane_create(struct drm_i915_private *dev_priv, drm_plane_create_zpos_immutable_property(&plane->base, plane_id); + if (INTEL_GEN(dev_priv) >= 12) + drm_plane_enable_fb_damage_clips(&plane->base); + drm_plane_helper_add(&plane->base, &intel_plane_helper_funcs); return plane; diff --git a/drivers/gpu/drm/i915/display/intel_sprite.h b/drivers/gpu/drm/i915/display/intel_sprite.h index 5eeaa92420d1..cd2104ba1ca1 100644 --- a/drivers/gpu/drm/i915/display/intel_sprite.h +++ b/drivers/gpu/drm/i915/display/intel_sprite.h @@ -32,21 +32,14 @@ struct intel_plane * skl_universal_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe, enum plane_id plane_id); -static inline bool icl_is_nv12_y_plane(enum plane_id id) -{ - /* Don't need to do a gen check, these planes are only available on gen11 */ - if (id == PLANE_SPRITE4 || id == PLANE_SPRITE5) - return true; - - return false; -} - static inline u8 icl_hdr_plane_mask(void) { return BIT(PLANE_PRIMARY) | BIT(PLANE_SPRITE0) | BIT(PLANE_SPRITE1); } +bool icl_is_nv12_y_plane(struct drm_i915_private *dev_priv, + enum plane_id plane_id); bool icl_is_hdr_plane(struct drm_i915_private *dev_priv, enum plane_id plane_id); int ivb_plane_min_cdclk(const struct intel_crtc_state *crtc_state, diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index b161c15baf86..5b5dc86a5737 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -360,12 +360,12 @@ static void icl_tc_phy_connect(struct intel_digital_port *dig_port, } if (!icl_tc_phy_set_safe_mode(dig_port, false) && - !WARN_ON(dig_port->tc_legacy_port)) + !drm_WARN_ON(&i915->drm, dig_port->tc_legacy_port)) goto out_set_tbt_alt_mode; max_lanes = intel_tc_port_fia_max_lane_count(dig_port); if (dig_port->tc_legacy_port) { - WARN_ON(max_lanes != 4); + drm_WARN_ON(&i915->drm, max_lanes != 4); dig_port->tc_mode = TC_PORT_LEGACY; return; @@ -445,18 +445,20 @@ static bool icl_tc_phy_is_connected(struct intel_digital_port *dig_port) static enum tc_port_mode intel_tc_port_get_current_mode(struct intel_digital_port *dig_port) { + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); u32 live_status_mask = tc_port_live_status_mask(dig_port); bool in_safe_mode = icl_tc_phy_is_in_safe_mode(dig_port); enum tc_port_mode mode; - if (in_safe_mode || WARN_ON(!icl_tc_phy_status_complete(dig_port))) + if (in_safe_mode || + drm_WARN_ON(&i915->drm, !icl_tc_phy_status_complete(dig_port))) return TC_PORT_TBT_ALT; mode = dig_port->tc_legacy_port ? TC_PORT_LEGACY : TC_PORT_DP_ALT; if (live_status_mask) { enum tc_port_mode live_mode = fls(live_status_mask) - 1; - if (!WARN_ON(live_mode == TC_PORT_TBT_ALT)) + if (!drm_WARN_ON(&i915->drm, live_mode == TC_PORT_TBT_ALT)) mode = live_mode; } @@ -505,7 +507,9 @@ static void intel_tc_port_link_init_refcount(struct intel_digital_port *dig_port, int refcount) { - WARN_ON(dig_port->tc_link_refcount); + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + + drm_WARN_ON(&i915->drm, dig_port->tc_link_refcount); dig_port->tc_link_refcount = refcount; } diff --git a/drivers/gpu/drm/i915/display/intel_tv.c b/drivers/gpu/drm/i915/display/intel_tv.c index abc67207f2f3..777032d9697b 100644 --- a/drivers/gpu/drm/i915/display/intel_tv.c +++ b/drivers/gpu/drm/i915/display/intel_tv.c @@ -1158,7 +1158,7 @@ intel_tv_get_config(struct intel_encoder *encoder, /* pixel counter doesn't work on i965gm TV output */ if (IS_I965GM(dev_priv)) - adjusted_mode->private_flags |= + pipe_config->mode_flags |= I915_MODE_FLAG_USE_SCANLINE_COUNTER; } @@ -1328,7 +1328,7 @@ intel_tv_compute_config(struct intel_encoder *encoder, /* pixel counter doesn't work on i965gm TV output */ if (IS_I965GM(dev_priv)) - adjusted_mode->private_flags |= + pipe_config->mode_flags |= I915_MODE_FLAG_USE_SCANLINE_COUNTER; return 0; diff --git a/drivers/gpu/drm/i915/display/intel_vdsc.c b/drivers/gpu/drm/i915/display/intel_vdsc.c index 95ad87d4ccb3..d145fe2bed81 100644 --- a/drivers/gpu/drm/i915/display/intel_vdsc.c +++ b/drivers/gpu/drm/i915/display/intel_vdsc.c @@ -476,13 +476,13 @@ intel_dsc_power_domain(const struct intel_crtc_state *crtc_state) * POWER_DOMAIN_TRANSCODER_VDSC_PW2 power domain in two cases: * * - ICL eDP/DSI transcoder - * - TGL pipe A + * - Gen12+ (except RKL) pipe A * * For any other pipe, VDSC/joining uses the power well associated with * the pipe in use. Hence another reference on the pipe power domain * will suffice. (Except no VDSC/joining on ICL pipe A.) */ - if (INTEL_GEN(i915) >= 12 && pipe == PIPE_A) + if (INTEL_GEN(i915) >= 12 && !IS_ROCKETLAKE(i915) && pipe == PIPE_A) return POWER_DOMAIN_TRANSCODER_VDSC_PW2; else if (is_pipe_dsc(crtc_state)) return POWER_DOMAIN_PIPE(pipe); diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c index f582ab52f0b0..052e0b31a2da 100644 --- a/drivers/gpu/drm/i915/display/vlv_dsi.c +++ b/drivers/gpu/drm/i915/display/vlv_dsi.c @@ -298,7 +298,7 @@ static int intel_dsi_compute_config(struct intel_encoder *encoder, if (IS_GEN9_LP(dev_priv)) { /* Enable Frame time stamp based scanline reporting */ - adjusted_mode->private_flags |= + pipe_config->mode_flags |= I915_MODE_FLAG_GET_SCANLINE_FROM_TIMESTAMP; /* Dual link goes to DSI transcoder A. */ @@ -1097,8 +1097,8 @@ static void bxt_dsi_get_pipe_config(struct intel_encoder *encoder, pipe_config->pipe_bpp = bdw_get_pipemisc_bpp(crtc); /* Enable Frame time stamo based scanline reporting */ - adjusted_mode->private_flags |= - I915_MODE_FLAG_GET_SCANLINE_FROM_TIMESTAMP; + pipe_config->mode_flags |= + I915_MODE_FLAG_GET_SCANLINE_FROM_TIMESTAMP; /* In terms of pixels */ adjusted_mode->crtc_hdisplay = diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c index 30c229fcb404..6675447a47b9 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c @@ -112,8 +112,7 @@ static void lut_close(struct i915_gem_context *ctx) if (!kref_get_unless_zero(&obj->base.refcount)) continue; - rcu_read_unlock(); - i915_gem_object_lock(obj); + spin_lock(&obj->lut_lock); list_for_each_entry(lut, &obj->lut_list, obj_link) { if (lut->ctx != ctx) continue; @@ -124,8 +123,7 @@ static void lut_close(struct i915_gem_context *ctx) list_del(&lut->obj_link); break; } - i915_gem_object_unlock(obj); - rcu_read_lock(); + spin_unlock(&obj->lut_lock); if (&lut->obj_link != &obj->lut_list) { i915_lut_handle_free(lut); @@ -650,7 +648,7 @@ static void context_close(struct i915_gem_context *ctx) * context close. */ if (!i915_gem_context_is_persistent(ctx) || - !i915_modparams.enable_hangcheck) + !ctx->i915->params.enable_hangcheck) kill_context(ctx); i915_gem_context_put(ctx); @@ -667,7 +665,7 @@ static int __context_set_persistence(struct i915_gem_context *ctx, bool state) * reset] are allowed to survive past termination. We require * hangcheck to ensure that the persistent requests are healthy. */ - if (!i915_modparams.enable_hangcheck) + if (!ctx->i915->params.enable_hangcheck) return -EINVAL; i915_gem_context_set_persistence(ctx); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c index 7db5a793739d..2679380159fc 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c @@ -217,6 +217,7 @@ static void i915_gem_object_put_pages_dmabuf(struct drm_i915_gem_object *obj, } static const struct drm_i915_gem_object_ops i915_gem_object_dmabuf_ops = { + .name = "i915_gem_object_dmabuf", .get_pages = i915_gem_object_get_pages_dmabuf, .put_pages = i915_gem_object_put_pages_dmabuf, }; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index db8eb1c6afe9..b4862afaaf28 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -45,13 +45,6 @@ struct eb_vma_array { struct eb_vma vma[]; }; -enum { - FORCE_CPU_RELOC = 1, - FORCE_GTT_RELOC, - FORCE_GPU_RELOC, -#define DBG_FORCE_RELOC 0 /* choose one of the above! */ -}; - #define __EXEC_OBJECT_HAS_PIN BIT(31) #define __EXEC_OBJECT_HAS_FENCE BIT(30) #define __EXEC_OBJECT_NEEDS_MAP BIT(29) @@ -260,8 +253,6 @@ struct i915_execbuffer { */ struct reloc_cache { struct drm_mm_node node; /** temporary GTT binding */ - unsigned long vaddr; /** Current kmap address */ - unsigned long page; /** Currently mapped page index */ unsigned int gen; /** Cached value of INTEL_GEN */ bool use_64bit_reloc : 1; bool has_llc : 1; @@ -605,23 +596,6 @@ eb_add_vma(struct i915_execbuffer *eb, } } -static inline int use_cpu_reloc(const struct reloc_cache *cache, - const struct drm_i915_gem_object *obj) -{ - if (!i915_gem_object_has_struct_page(obj)) - return false; - - if (DBG_FORCE_RELOC == FORCE_CPU_RELOC) - return true; - - if (DBG_FORCE_RELOC == FORCE_GTT_RELOC) - return false; - - return (cache->has_llc || - obj->cache_dirty || - obj->cache_level != I915_CACHE_NONE); -} - static int eb_reserve_vma(const struct i915_execbuffer *eb, struct eb_vma *ev, u64 pin_flags) @@ -815,14 +789,14 @@ static int __eb_add_lut(struct i915_execbuffer *eb, if (err == 0) { /* And nor has this handle */ struct drm_i915_gem_object *obj = vma->obj; - i915_gem_object_lock(obj); + spin_lock(&obj->lut_lock); if (idr_find(&eb->file->object_idr, handle) == obj) { list_add(&lut->obj_link, &obj->lut_list); } else { radix_tree_delete(&ctx->handles_vma, handle); err = -ENOENT; } - i915_gem_object_unlock(obj); + spin_unlock(&obj->lut_lock); } mutex_unlock(&ctx->mutex); } @@ -945,8 +919,6 @@ relocation_target(const struct drm_i915_gem_relocation_entry *reloc, static void reloc_cache_init(struct reloc_cache *cache, struct drm_i915_private *i915) { - cache->page = -1; - cache->vaddr = 0; /* Must be a variable in the struct to allow GCC to unroll. */ cache->gen = INTEL_GEN(i915); cache->has_llc = HAS_LLC(i915); @@ -958,25 +930,6 @@ static void reloc_cache_init(struct reloc_cache *cache, cache->target = NULL; } -static inline void *unmask_page(unsigned long p) -{ - return (void *)(uintptr_t)(p & PAGE_MASK); -} - -static inline unsigned int unmask_flags(unsigned long p) -{ - return p & ~PAGE_MASK; -} - -#define KMAP 0x4 /* after CLFLUSH_FLAGS */ - -static inline struct i915_ggtt *cache_to_ggtt(struct reloc_cache *cache) -{ - struct drm_i915_private *i915 = - container_of(cache, struct i915_execbuffer, reloc_cache)->i915; - return &i915->ggtt; -} - #define RELOC_TAIL 4 static int reloc_gpu_chain(struct reloc_cache *cache) @@ -1089,181 +1042,6 @@ static int reloc_gpu_flush(struct reloc_cache *cache) return err; } -static void reloc_cache_reset(struct reloc_cache *cache) -{ - void *vaddr; - - if (!cache->vaddr) - return; - - vaddr = unmask_page(cache->vaddr); - if (cache->vaddr & KMAP) { - if (cache->vaddr & CLFLUSH_AFTER) - mb(); - - kunmap_atomic(vaddr); - i915_gem_object_finish_access((struct drm_i915_gem_object *)cache->node.mm); - } else { - struct i915_ggtt *ggtt = cache_to_ggtt(cache); - - intel_gt_flush_ggtt_writes(ggtt->vm.gt); - io_mapping_unmap_atomic((void __iomem *)vaddr); - - if (drm_mm_node_allocated(&cache->node)) { - ggtt->vm.clear_range(&ggtt->vm, - cache->node.start, - cache->node.size); - mutex_lock(&ggtt->vm.mutex); - drm_mm_remove_node(&cache->node); - mutex_unlock(&ggtt->vm.mutex); - } else { - i915_vma_unpin((struct i915_vma *)cache->node.mm); - } - } - - cache->vaddr = 0; - cache->page = -1; -} - -static void *reloc_kmap(struct drm_i915_gem_object *obj, - struct reloc_cache *cache, - unsigned long page) -{ - void *vaddr; - - if (cache->vaddr) { - kunmap_atomic(unmask_page(cache->vaddr)); - } else { - unsigned int flushes; - int err; - - err = i915_gem_object_prepare_write(obj, &flushes); - if (err) - return ERR_PTR(err); - - 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(); - } - - vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj, page)); - cache->vaddr = unmask_flags(cache->vaddr) | (unsigned long)vaddr; - cache->page = page; - - return vaddr; -} - -static void *reloc_iomap(struct drm_i915_gem_object *obj, - struct reloc_cache *cache, - unsigned long page) -{ - struct i915_ggtt *ggtt = cache_to_ggtt(cache); - unsigned long offset; - void *vaddr; - - if (cache->vaddr) { - intel_gt_flush_ggtt_writes(ggtt->vm.gt); - io_mapping_unmap_atomic((void __force __iomem *) unmask_page(cache->vaddr)); - } else { - struct i915_vma *vma; - int err; - - if (i915_gem_object_is_tiled(obj)) - return ERR_PTR(-EINVAL); - - if (use_cpu_reloc(cache, obj)) - return NULL; - - i915_gem_object_lock(obj); - err = i915_gem_object_set_to_gtt_domain(obj, true); - i915_gem_object_unlock(obj); - if (err) - return ERR_PTR(err); - - vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, - PIN_MAPPABLE | - PIN_NONBLOCK /* NOWARN */ | - PIN_NOEVICT); - if (IS_ERR(vma)) { - memset(&cache->node, 0, sizeof(cache->node)); - mutex_lock(&ggtt->vm.mutex); - err = drm_mm_insert_node_in_range - (&ggtt->vm.mm, &cache->node, - PAGE_SIZE, 0, I915_COLOR_UNEVICTABLE, - 0, ggtt->mappable_end, - DRM_MM_INSERT_LOW); - mutex_unlock(&ggtt->vm.mutex); - if (err) /* no inactive aperture space, use cpu reloc */ - return NULL; - } else { - cache->node.start = vma->node.start; - cache->node.mm = (void *)vma; - } - } - - offset = cache->node.start; - if (drm_mm_node_allocated(&cache->node)) { - ggtt->vm.insert_page(&ggtt->vm, - i915_gem_object_get_dma_address(obj, page), - offset, I915_CACHE_NONE, 0); - } else { - offset += page << PAGE_SHIFT; - } - - vaddr = (void __force *)io_mapping_map_atomic_wc(&ggtt->iomap, - offset); - cache->page = page; - cache->vaddr = (unsigned long)vaddr; - - return vaddr; -} - -static void *reloc_vaddr(struct drm_i915_gem_object *obj, - struct reloc_cache *cache, - unsigned long page) -{ - 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 void clflush_write32(u32 *addr, u32 value, unsigned int flushes) -{ - if (unlikely(flushes & (CLFLUSH_BEFORE | CLFLUSH_AFTER))) { - if (flushes & CLFLUSH_BEFORE) { - clflushopt(addr); - mb(); - } - - *addr = value; - - /* - * 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; -} - static int reloc_move_to_gpu(struct i915_request *rq, struct i915_vma *vma) { struct drm_i915_gem_object *obj = vma->obj; @@ -1429,17 +1207,6 @@ static u32 *reloc_gpu(struct i915_execbuffer *eb, return cmd; } -static inline bool use_reloc_gpu(struct i915_vma *vma) -{ - if (DBG_FORCE_RELOC == FORCE_GPU_RELOC) - return true; - - if (DBG_FORCE_RELOC) - return false; - - return !dma_resv_test_signaled_rcu(vma->resv, true); -} - static unsigned long vma_phys_addr(struct i915_vma *vma, u32 offset) { struct page *page; @@ -1454,10 +1221,10 @@ static unsigned long vma_phys_addr(struct i915_vma *vma, u32 offset) return addr + offset_in_page(offset); } -static bool __reloc_entry_gpu(struct i915_execbuffer *eb, - struct i915_vma *vma, - u64 offset, - u64 target_addr) +static int __reloc_entry_gpu(struct i915_execbuffer *eb, + struct i915_vma *vma, + u64 offset, + u64 target_addr) { const unsigned int gen = eb->reloc_cache.gen; unsigned int len; @@ -1473,7 +1240,7 @@ static bool __reloc_entry_gpu(struct i915_execbuffer *eb, batch = reloc_gpu(eb, vma, len); if (IS_ERR(batch)) - return false; + return PTR_ERR(batch); addr = gen8_canonical_addr(vma->node.start + offset); if (gen >= 8) { @@ -1522,55 +1289,21 @@ static bool __reloc_entry_gpu(struct i915_execbuffer *eb, *batch++ = target_addr; } - return true; -} - -static bool reloc_entry_gpu(struct i915_execbuffer *eb, - struct i915_vma *vma, - u64 offset, - u64 target_addr) -{ - if (eb->reloc_cache.vaddr) - return false; - - if (!use_reloc_gpu(vma)) - return false; - - return __reloc_entry_gpu(eb, vma, offset, target_addr); + return 0; } static u64 -relocate_entry(struct i915_vma *vma, +relocate_entry(struct i915_execbuffer *eb, + struct i915_vma *vma, const struct drm_i915_gem_relocation_entry *reloc, - struct i915_execbuffer *eb, const struct i915_vma *target) { u64 target_addr = relocation_target(reloc, target); - u64 offset = reloc->offset; - - if (!reloc_entry_gpu(eb, vma, offset, target_addr)) { - bool wide = eb->reloc_cache.use_64bit_reloc; - void *vaddr; - -repeat: - vaddr = reloc_vaddr(vma->obj, - &eb->reloc_cache, - offset >> PAGE_SHIFT); - if (IS_ERR(vaddr)) - return PTR_ERR(vaddr); - - GEM_BUG_ON(!IS_ALIGNED(offset, sizeof(u32))); - clflush_write32(vaddr + offset_in_page(offset), - lower_32_bits(target_addr), - eb->reloc_cache.vaddr); - - if (wide) { - offset += sizeof(u32); - target_addr >>= 32; - wide = false; - goto repeat; - } - } + int err; + + err = __reloc_entry_gpu(eb, vma, reloc->offset, target_addr); + if (err) + return err; return target->node.start | UPDATE; } @@ -1626,8 +1359,7 @@ eb_relocate_entry(struct i915_execbuffer *eb, err = i915_vma_bind(target->vma, target->vma->obj->cache_level, PIN_GLOBAL, NULL); - if (WARN_ONCE(err, - "Unexpected failure to bind target VMA!")) + if (err) return err; } } @@ -1636,8 +1368,7 @@ eb_relocate_entry(struct i915_execbuffer *eb, * If the relocation already has the right value in it, no * more work needs to be done. */ - if (!DBG_FORCE_RELOC && - gen8_canonical_addr(target->vma->node.start) == reloc->presumed_offset) + if (gen8_canonical_addr(target->vma->node.start) == reloc->presumed_offset) return 0; /* Check that the relocation address is valid... */ @@ -1669,7 +1400,7 @@ eb_relocate_entry(struct i915_execbuffer *eb, ev->flags &= ~EXEC_OBJECT_ASYNC; /* and update the user's relocation entry */ - return relocate_entry(ev->vma, reloc, eb, target->vma); + return relocate_entry(eb, ev->vma, reloc, target->vma); } static int eb_relocate_vma(struct i915_execbuffer *eb, struct eb_vma *ev) @@ -1707,10 +1438,8 @@ static int eb_relocate_vma(struct i915_execbuffer *eb, struct eb_vma *ev) * this is bad and so lockdep complains vehemently. */ copied = __copy_from_user(r, urelocs, count * sizeof(r[0])); - if (unlikely(copied)) { - remain = -EFAULT; - goto out; - } + if (unlikely(copied)) + return -EFAULT; remain -= count; do { @@ -1718,8 +1447,7 @@ static int eb_relocate_vma(struct i915_execbuffer *eb, struct eb_vma *ev) if (likely(offset == 0)) { } else if ((s64)offset < 0) { - remain = (int)offset; - goto out; + return (int)offset; } else { /* * Note that reporting an error now @@ -1749,9 +1477,8 @@ static int eb_relocate_vma(struct i915_execbuffer *eb, struct eb_vma *ev) } while (r++, --count); urelocs += ARRAY_SIZE(stack); } while (remain); -out: - reloc_cache_reset(&eb->reloc_cache); - return remain; + + return 0; } static int eb_relocate(struct i915_execbuffer *eb) @@ -1911,8 +1638,8 @@ static int i915_reset_gen7_sol_offsets(struct i915_request *rq) u32 *cs; int i; - if (!IS_GEN(rq->i915, 7) || rq->engine->id != RCS0) { - drm_dbg(&rq->i915->drm, "sol reset is gen7/rcs only\n"); + if (!IS_GEN(rq->engine->i915, 7) || rq->engine->id != RCS0) { + drm_dbg(&rq->engine->i915->drm, "sol reset is gen7/rcs only\n"); return -EINVAL; } @@ -2659,7 +2386,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, eb.i915 = i915; eb.file = file; eb.args = args; - if (DBG_FORCE_RELOC || !(args->flags & I915_EXEC_NO_RELOC)) + if (!(args->flags & I915_EXEC_NO_RELOC)) args->flags |= __EXEC_HAS_RELOC; eb.exec = exec; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_internal.c b/drivers/gpu/drm/i915/gem/i915_gem_internal.c index cbbff81aa0af..ad22f42541bd 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_internal.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_internal.c @@ -137,6 +137,7 @@ static void i915_gem_object_put_pages_internal(struct drm_i915_gem_object *obj, } static const struct drm_i915_gem_object_ops i915_gem_object_internal_ops = { + .name = "i915_gem_object_internal", .flags = I915_GEM_OBJECT_HAS_STRUCT_PAGE | I915_GEM_OBJECT_IS_SHRINKABLE, .get_pages = i915_gem_object_get_pages_internal, diff --git a/drivers/gpu/drm/i915/gem/i915_gem_lmem.c b/drivers/gpu/drm/i915/gem/i915_gem_lmem.c index 70543c83df06..932ee21e6609 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_lmem.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_lmem.c @@ -9,6 +9,7 @@ #include "i915_drv.h" const struct drm_i915_gem_object_ops i915_gem_lmem_obj_ops = { + .name = "i915_gem_object_lmem", .flags = I915_GEM_OBJECT_HAS_IOMEM, .get_pages = i915_gem_object_get_pages_buddy, diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c index fe45bd4d63a5..fe27c5b344e3 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c @@ -216,12 +216,12 @@ static vm_fault_t i915_error_to_vmf_fault(int err) case -ENXIO: /* unable to access backing store (on device) */ return VM_FAULT_SIGBUS; - case -ENOSPC: /* shmemfs allocation failure */ case -ENOMEM: /* our allocation failure */ return VM_FAULT_OOM; case 0: case -EAGAIN: + case -ENOSPC: /* transient failure to evict? */ case -ERESTARTSYS: case -EINTR: case -EBUSY: diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c index 99356c00c19e..6b69191c5543 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c @@ -53,7 +53,7 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj, const struct drm_i915_gem_object_ops *ops, struct lock_class_key *key) { - __mutex_init(&obj->mm.lock, "obj->mm.lock", key); + __mutex_init(&obj->mm.lock, ops->name ?: "obj->mm.lock", key); spin_lock_init(&obj->vma.lock); INIT_LIST_HEAD(&obj->vma.list); @@ -61,6 +61,7 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj, INIT_LIST_HEAD(&obj->mm.link); INIT_LIST_HEAD(&obj->lut_list); + spin_lock_init(&obj->lut_lock); spin_lock_init(&obj->mmo.lock); obj->mmo.offsets = RB_ROOT; @@ -72,6 +73,10 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj, obj->mm.madv = I915_MADV_WILLNEED; INIT_RADIX_TREE(&obj->mm.get_page.radix, GFP_KERNEL | __GFP_NOWARN); mutex_init(&obj->mm.get_page.lock); + + if (IS_ENABLED(CONFIG_LOCKDEP) && i915_gem_object_is_shrinkable(obj)) + i915_gem_shrinker_taints_mutex(to_i915(obj->base.dev), + &obj->mm.lock); } /** @@ -100,21 +105,29 @@ void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file) { struct drm_i915_gem_object *obj = to_intel_bo(gem); struct drm_i915_file_private *fpriv = file->driver_priv; + struct i915_lut_handle bookmark = {}; struct i915_mmap_offset *mmo, *mn; struct i915_lut_handle *lut, *ln; LIST_HEAD(close); - i915_gem_object_lock(obj); + spin_lock(&obj->lut_lock); list_for_each_entry_safe(lut, ln, &obj->lut_list, obj_link) { struct i915_gem_context *ctx = lut->ctx; - if (ctx->file_priv != fpriv) - continue; + if (ctx && ctx->file_priv == fpriv) { + i915_gem_context_get(ctx); + list_move(&lut->obj_link, &close); + } - i915_gem_context_get(ctx); - list_move(&lut->obj_link, &close); + /* Break long locks, and carefully continue on from this spot */ + if (&ln->obj_link != &obj->lut_list) { + list_add_tail(&bookmark.obj_link, &ln->obj_link); + if (cond_resched_lock(&obj->lut_lock)) + list_safe_reset_next(&bookmark, ln, obj_link); + __list_del_entry(&bookmark.obj_link); + } } - i915_gem_object_unlock(obj); + spin_unlock(&obj->lut_lock); spin_lock(&obj->mmo.lock); rbtree_postorder_for_each_entry_safe(mmo, mn, &obj->mmo.offsets, offset) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object_blt.c b/drivers/gpu/drm/i915/gem/i915_gem_object_blt.c index f457d7130491..bfdb32d46877 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object_blt.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_object_blt.c @@ -126,6 +126,17 @@ void intel_emit_vma_release(struct intel_context *ce, struct i915_vma *vma) intel_engine_pm_put(ce->engine); } +static int +move_obj_to_gpu(struct drm_i915_gem_object *obj, + struct i915_request *rq, + bool write) +{ + if (obj->cache_dirty & ~obj->cache_coherent) + i915_gem_clflush_object(obj, 0); + + return i915_request_await_object(rq, obj, write); +} + int i915_gem_object_fill_blt(struct drm_i915_gem_object *obj, struct intel_context *ce, u32 value) @@ -143,12 +154,6 @@ int i915_gem_object_fill_blt(struct drm_i915_gem_object *obj, if (unlikely(err)) return err; - if (obj->cache_dirty & ~obj->cache_coherent) { - i915_gem_object_lock(obj); - i915_gem_clflush_object(obj, 0); - i915_gem_object_unlock(obj); - } - batch = intel_emit_vma_fill_blt(ce, vma, value); if (IS_ERR(batch)) { err = PTR_ERR(batch); @@ -165,27 +170,22 @@ int i915_gem_object_fill_blt(struct drm_i915_gem_object *obj, if (unlikely(err)) goto out_request; - err = i915_request_await_object(rq, obj, true); - if (unlikely(err)) - goto out_request; - - if (ce->engine->emit_init_breadcrumb) { - err = ce->engine->emit_init_breadcrumb(rq); - if (unlikely(err)) - goto out_request; - } - i915_vma_lock(vma); - err = i915_request_await_object(rq, vma->obj, true); + err = move_obj_to_gpu(vma->obj, rq, true); if (err == 0) err = i915_vma_move_to_active(vma, rq, EXEC_OBJECT_WRITE); i915_vma_unlock(vma); if (unlikely(err)) goto out_request; - err = ce->engine->emit_bb_start(rq, - batch->node.start, batch->node.size, - 0); + if (ce->engine->emit_init_breadcrumb) + err = ce->engine->emit_init_breadcrumb(rq); + + if (likely(!err)) + err = ce->engine->emit_bb_start(rq, + batch->node.start, + batch->node.size, + 0); out_request: if (unlikely(err)) i915_request_set_error_once(rq, err); @@ -317,16 +317,6 @@ out_pm: return ERR_PTR(err); } -static int move_to_gpu(struct i915_vma *vma, struct i915_request *rq, bool write) -{ - struct drm_i915_gem_object *obj = vma->obj; - - if (obj->cache_dirty & ~obj->cache_coherent) - i915_gem_clflush_object(obj, 0); - - return i915_request_await_object(rq, obj, write); -} - int i915_gem_object_copy_blt(struct drm_i915_gem_object *src, struct drm_i915_gem_object *dst, struct intel_context *ce) @@ -375,7 +365,7 @@ int i915_gem_object_copy_blt(struct drm_i915_gem_object *src, goto out_request; for (i = 0; i < ARRAY_SIZE(vma); i++) { - err = move_to_gpu(vma[i], rq, i); + err = move_obj_to_gpu(vma[i]->obj, rq, i); if (unlikely(err)) goto out_unlock; } diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h index 54ee658bb168..5335f799b548 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h +++ b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h @@ -61,6 +61,8 @@ struct drm_i915_gem_object_ops { int (*dmabuf_export)(struct drm_i915_gem_object *obj); void (*release)(struct drm_i915_gem_object *obj); + + const char *name; /* friendly name for debug, e.g. lockdep classes */ }; enum i915_mmap_type { @@ -119,6 +121,7 @@ struct drm_i915_gem_object { * this translation from object to context->handles_vma. */ struct list_head lut_list; + spinlock_t lut_lock; /* guards lut_list */ /** Stolen memory for this object, instead of being backed by shmem. */ struct drm_mm_node *stolen; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_phys.c b/drivers/gpu/drm/i915/gem/i915_gem_phys.c index 7fe9831aa9ba..28147aab47b9 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_phys.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_phys.c @@ -27,7 +27,7 @@ static int i915_gem_object_get_pages_phys(struct drm_i915_gem_object *obj) void *dst; int i; - if (WARN_ON(i915_gem_object_needs_bit17_swizzle(obj))) + if (GEM_WARN_ON(i915_gem_object_needs_bit17_swizzle(obj))) return -EINVAL; /* @@ -140,6 +140,7 @@ static void phys_release(struct drm_i915_gem_object *obj) } static const struct drm_i915_gem_object_ops i915_gem_phys_ops = { + .name = "i915_gem_object_phys", .get_pages = i915_gem_object_get_pages_phys, .put_pages = i915_gem_object_put_pages_phys, diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c index 7aff3514d97a..38113d3c0138 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c @@ -147,8 +147,7 @@ rebuild_st: last_pfn = page_to_pfn(page); /* Check that the i965g/gm workaround works. */ - drm_WARN_ON(&i915->drm, - (gfp & __GFP_DMA32) && (last_pfn >= 0x00100000UL)); + GEM_BUG_ON(gfp & __GFP_DMA32 && last_pfn >= 0x00100000UL); } if (sg) { /* loop terminated early; short sg table */ sg_page_sizes |= sg->length; @@ -430,6 +429,7 @@ static void shmem_release(struct drm_i915_gem_object *obj) } const struct drm_i915_gem_object_ops i915_gem_shmem_ops = { + .name = "i915_gem_object_shmem", .flags = I915_GEM_OBJECT_HAS_STRUCT_PAGE | I915_GEM_OBJECT_IS_SHRINKABLE, diff --git a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c index dc250278bd2c..e0f21f12d3ce 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c @@ -566,6 +566,7 @@ i915_gem_object_release_stolen(struct drm_i915_gem_object *obj) } static const struct drm_i915_gem_object_ops i915_gem_object_stolen_ops = { + .name = "i915_gem_object_stolen", .get_pages = i915_gem_object_get_pages_stolen, .put_pages = i915_gem_object_put_pages_stolen, .release = i915_gem_object_release_stolen, diff --git a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c index c31a6744daee..e946032b13e4 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c @@ -21,7 +21,7 @@ struct i915_mm_struct { struct i915_mmu_notifier *mn; struct hlist_node node; struct kref kref; - struct work_struct work; + struct rcu_work work; }; #if defined(CONFIG_MMU_NOTIFIER) @@ -189,40 +189,31 @@ i915_gem_userptr_release__mmu_notifier(struct drm_i915_gem_object *obj) static struct i915_mmu_notifier * i915_mmu_notifier_find(struct i915_mm_struct *mm) { - struct i915_mmu_notifier *mn; - int err = 0; + struct i915_mmu_notifier *mn, *old; + int err; - mn = mm->mn; - if (mn) + mn = READ_ONCE(mm->mn); + if (likely(mn)) return mn; mn = i915_mmu_notifier_create(mm); if (IS_ERR(mn)) - err = PTR_ERR(mn); - - mmap_write_lock(mm->mm); - mutex_lock(&mm->i915->mm_lock); - if (mm->mn == NULL && !err) { - /* Protected by mmap_lock (write-lock) */ - err = __mmu_notifier_register(&mn->mn, mm->mm); - if (!err) { - /* Protected by mm_lock */ - mm->mn = fetch_and_zero(&mn); - } - } else if (mm->mn) { - /* - * Someone else raced and successfully installed the mmu - * notifier, we can cancel our own errors. - */ - err = 0; + return mn; + + err = mmu_notifier_register(&mn->mn, mm->mm); + if (err) { + kfree(mn); + return ERR_PTR(err); } - mutex_unlock(&mm->i915->mm_lock); - mmap_write_unlock(mm->mm); - if (mn && !IS_ERR(mn)) + old = cmpxchg(&mm->mn, NULL, mn); + if (old) { + mmu_notifier_unregister(&mn->mn, mm->mm); kfree(mn); + mn = old; + } - return err ? ERR_PTR(err) : mm->mn; + return mn; } static int @@ -235,7 +226,7 @@ i915_gem_userptr_init__mmu_notifier(struct drm_i915_gem_object *obj, if (flags & I915_USERPTR_UNSYNCHRONIZED) return capable(CAP_SYS_ADMIN) ? 0 : -EPERM; - if (WARN_ON(obj->userptr.mm == NULL)) + if (GEM_WARN_ON(!obj->userptr.mm)) return -EINVAL; mn = i915_mmu_notifier_find(obj->userptr.mm); @@ -301,23 +292,28 @@ i915_mmu_notifier_free(struct i915_mmu_notifier *mn, #endif static struct i915_mm_struct * -__i915_mm_struct_find(struct drm_i915_private *dev_priv, struct mm_struct *real) +__i915_mm_struct_find(struct drm_i915_private *i915, struct mm_struct *real) { - struct i915_mm_struct *mm; - - /* Protected by dev_priv->mm_lock */ - hash_for_each_possible(dev_priv->mm_structs, mm, node, (unsigned long)real) - if (mm->mm == real) - return mm; + struct i915_mm_struct *it, *mm = NULL; + + rcu_read_lock(); + hash_for_each_possible_rcu(i915->mm_structs, + it, node, + (unsigned long)real) + if (it->mm == real && kref_get_unless_zero(&it->kref)) { + mm = it; + break; + } + rcu_read_unlock(); - return NULL; + return mm; } static int i915_gem_userptr_init__mm_struct(struct drm_i915_gem_object *obj) { - struct drm_i915_private *dev_priv = to_i915(obj->base.dev); - struct i915_mm_struct *mm; + struct drm_i915_private *i915 = to_i915(obj->base.dev); + struct i915_mm_struct *mm, *new; int ret = 0; /* During release of the GEM object we hold the struct_mutex. This @@ -330,39 +326,42 @@ i915_gem_userptr_init__mm_struct(struct drm_i915_gem_object *obj) * struct_mutex, i.e. we need to schedule a worker to do the clean * up. */ - mutex_lock(&dev_priv->mm_lock); - mm = __i915_mm_struct_find(dev_priv, current->mm); - if (mm == NULL) { - mm = kmalloc(sizeof(*mm), GFP_KERNEL); - if (mm == NULL) { - ret = -ENOMEM; - goto out; - } + mm = __i915_mm_struct_find(i915, current->mm); + if (mm) + goto out; - kref_init(&mm->kref); - mm->i915 = to_i915(obj->base.dev); + new = kmalloc(sizeof(*mm), GFP_KERNEL); + if (!new) + return -ENOMEM; - mm->mm = current->mm; + kref_init(&new->kref); + new->i915 = to_i915(obj->base.dev); + new->mm = current->mm; + new->mn = NULL; + + spin_lock(&i915->mm_lock); + mm = __i915_mm_struct_find(i915, current->mm); + if (!mm) { + hash_add_rcu(i915->mm_structs, + &new->node, + (unsigned long)new->mm); mmgrab(current->mm); + mm = new; + } + spin_unlock(&i915->mm_lock); + if (mm != new) + kfree(new); - mm->mn = NULL; - - /* Protected by dev_priv->mm_lock */ - hash_add(dev_priv->mm_structs, - &mm->node, (unsigned long)mm->mm); - } else - kref_get(&mm->kref); - - obj->userptr.mm = mm; out: - mutex_unlock(&dev_priv->mm_lock); + obj->userptr.mm = mm; return ret; } static void __i915_mm_struct_free__worker(struct work_struct *work) { - struct i915_mm_struct *mm = container_of(work, typeof(*mm), work); + struct i915_mm_struct *mm = container_of(work, typeof(*mm), work.work); + i915_mmu_notifier_free(mm->mn, mm->mm); mmdrop(mm->mm); kfree(mm); @@ -373,12 +372,12 @@ __i915_mm_struct_free(struct kref *kref) { struct i915_mm_struct *mm = container_of(kref, typeof(*mm), kref); - /* Protected by dev_priv->mm_lock */ - hash_del(&mm->node); - mutex_unlock(&mm->i915->mm_lock); + spin_lock(&mm->i915->mm_lock); + hash_del_rcu(&mm->node); + spin_unlock(&mm->i915->mm_lock); - INIT_WORK(&mm->work, __i915_mm_struct_free__worker); - queue_work(mm->i915->mm.userptr_wq, &mm->work); + INIT_RCU_WORK(&mm->work, __i915_mm_struct_free__worker); + queue_rcu_work(system_wq, &mm->work); } static void @@ -387,9 +386,7 @@ i915_gem_userptr_release__mm_struct(struct drm_i915_gem_object *obj) if (obj->userptr.mm == NULL) return; - kref_put_mutex(&obj->userptr.mm->kref, - __i915_mm_struct_free, - &to_i915(obj->base.dev)->mm_lock); + kref_put(&obj->userptr.mm->kref, __i915_mm_struct_free); obj->userptr.mm = NULL; } @@ -712,6 +709,7 @@ i915_gem_userptr_dmabuf_export(struct drm_i915_gem_object *obj) } static const struct drm_i915_gem_object_ops i915_gem_userptr_ops = { + .name = "i915_gem_object_userptr", .flags = I915_GEM_OBJECT_HAS_STRUCT_PAGE | I915_GEM_OBJECT_IS_SHRINKABLE | I915_GEM_OBJECT_NO_MMAP | @@ -850,7 +848,7 @@ i915_gem_userptr_ioctl(struct drm_device *dev, int i915_gem_init_userptr(struct drm_i915_private *dev_priv) { - mutex_init(&dev_priv->mm_lock); + spin_lock_init(&dev_priv->mm_lock); hash_init(dev_priv->mm_structs); dev_priv->mm.userptr_wq = diff --git a/drivers/gpu/drm/i915/gem/selftests/huge_gem_object.c b/drivers/gpu/drm/i915/gem/selftests/huge_gem_object.c index 2b46c6530da9..a768ec61e966 100644 --- a/drivers/gpu/drm/i915/gem/selftests/huge_gem_object.c +++ b/drivers/gpu/drm/i915/gem/selftests/huge_gem_object.c @@ -88,6 +88,7 @@ static void huge_put_pages(struct drm_i915_gem_object *obj, } static const struct drm_i915_gem_object_ops huge_ops = { + .name = "huge-gem", .flags = I915_GEM_OBJECT_HAS_STRUCT_PAGE, .get_pages = huge_get_pages, .put_pages = huge_put_pages, diff --git a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c index c9988b6d5c88..8291ede6902c 100644 --- a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c +++ b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c @@ -139,6 +139,7 @@ static void put_huge_pages(struct drm_i915_gem_object *obj, } static const struct drm_i915_gem_object_ops huge_page_ops = { + .name = "huge-gem", .flags = I915_GEM_OBJECT_HAS_STRUCT_PAGE | I915_GEM_OBJECT_IS_SHRINKABLE, .get_pages = get_huge_pages, @@ -283,12 +284,14 @@ static void fake_put_huge_pages(struct drm_i915_gem_object *obj, } static const struct drm_i915_gem_object_ops fake_ops = { + .name = "fake-gem", .flags = I915_GEM_OBJECT_IS_SHRINKABLE, .get_pages = fake_get_huge_pages, .put_pages = fake_put_huge_pages, }; static const struct drm_i915_gem_object_ops fake_ops_single = { + .name = "fake-gem", .flags = I915_GEM_OBJECT_IS_SHRINKABLE, .get_pages = fake_get_huge_pages_single, .put_pages = fake_put_huge_pages, @@ -1409,147 +1412,6 @@ out: return err; } -static int igt_ppgtt_pin_update(void *arg) -{ - struct i915_gem_context *ctx = arg; - struct drm_i915_private *dev_priv = ctx->i915; - unsigned long supported = INTEL_INFO(dev_priv)->page_sizes; - struct drm_i915_gem_object *obj; - struct i915_gem_engines_iter it; - struct i915_address_space *vm; - struct intel_context *ce; - struct i915_vma *vma; - unsigned int flags = PIN_USER | PIN_OFFSET_FIXED; - unsigned int n; - int first, last; - int err = 0; - - /* - * Make sure there's no funny business when doing a PIN_UPDATE -- in the - * past we had a subtle issue with being able to incorrectly do multiple - * alloc va ranges on the same object when doing a PIN_UPDATE, which - * resulted in some pretty nasty bugs, though only when using - * huge-gtt-pages. - */ - - vm = i915_gem_context_get_vm_rcu(ctx); - if (!i915_vm_is_4lvl(vm)) { - pr_info("48b PPGTT not supported, skipping\n"); - goto out_vm; - } - - first = ilog2(I915_GTT_PAGE_SIZE_64K); - last = ilog2(I915_GTT_PAGE_SIZE_2M); - - for_each_set_bit_from(first, &supported, last + 1) { - unsigned int page_size = BIT(first); - - obj = i915_gem_object_create_internal(dev_priv, page_size); - if (IS_ERR(obj)) { - err = PTR_ERR(obj); - goto out_vm; - } - - vma = i915_vma_instance(obj, vm, NULL); - if (IS_ERR(vma)) { - err = PTR_ERR(vma); - goto out_put; - } - - err = i915_vma_pin(vma, SZ_2M, 0, flags); - if (err) - goto out_put; - - if (vma->page_sizes.sg < page_size) { - pr_info("Unable to allocate page-size %x, finishing test early\n", - page_size); - goto out_unpin; - } - - err = igt_check_page_sizes(vma); - if (err) - goto out_unpin; - - if (vma->page_sizes.gtt != page_size) { - dma_addr_t addr = i915_gem_object_get_dma_address(obj, 0); - - /* - * The only valid reason for this to ever fail would be - * if the dma-mapper screwed us over when we did the - * dma_map_sg(), since it has the final say over the dma - * address. - */ - if (IS_ALIGNED(addr, page_size)) { - pr_err("page_sizes.gtt=%u, expected=%u\n", - vma->page_sizes.gtt, page_size); - err = -EINVAL; - } else { - pr_info("dma address misaligned, finishing test early\n"); - } - - goto out_unpin; - } - - err = i915_vma_bind(vma, I915_CACHE_NONE, PIN_UPDATE, NULL); - if (err) - goto out_unpin; - - i915_vma_unpin(vma); - i915_gem_object_put(obj); - } - - obj = i915_gem_object_create_internal(dev_priv, PAGE_SIZE); - if (IS_ERR(obj)) { - err = PTR_ERR(obj); - goto out_vm; - } - - vma = i915_vma_instance(obj, vm, NULL); - if (IS_ERR(vma)) { - err = PTR_ERR(vma); - goto out_put; - } - - err = i915_vma_pin(vma, 0, 0, flags); - if (err) - goto out_put; - - /* - * Make sure we don't end up with something like where the pde is still - * pointing to the 2M page, and the pt we just filled-in is dangling -- - * we can check this by writing to the first page where it would then - * land in the now stale 2M page. - */ - - n = 0; - for_each_gem_engine(ce, i915_gem_context_lock_engines(ctx), it) { - if (!intel_engine_can_store_dword(ce->engine)) - continue; - - err = gpu_write(ce, vma, n++, 0xdeadbeaf); - if (err) - break; - } - i915_gem_context_unlock_engines(ctx); - if (err) - goto out_unpin; - - while (n--) { - err = cpu_check(obj, n, 0xdeadbeaf); - if (err) - goto out_unpin; - } - -out_unpin: - i915_vma_unpin(vma); -out_put: - i915_gem_object_put(obj); -out_vm: - i915_vm_put(vm); - - return err; -} - static int igt_tmpfs_fallback(void *arg) { struct i915_gem_context *ctx = arg; @@ -1760,7 +1622,6 @@ int i915_gem_huge_page_live_selftests(struct drm_i915_private *i915) { static const struct i915_subtest tests[] = { SUBTEST(igt_shrink_thp), - SUBTEST(igt_ppgtt_pin_update), SUBTEST(igt_tmpfs_fallback), SUBTEST(igt_ppgtt_smoke_huge), SUBTEST(igt_ppgtt_sanity_check), diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_client_blt.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_client_blt.c index 8fe3ad2ee34e..299c29e9ad86 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_client_blt.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_client_blt.c @@ -702,8 +702,5 @@ int i915_gem_client_blt_live_selftests(struct drm_i915_private *i915) if (intel_gt_is_wedged(&i915->gt)) return 0; - if (!HAS_ENGINE(i915, BCS0)) - return 0; - return i915_live_subtests(tests, i915); } diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c index a49016f8ee0d..57c14d3340cd 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c @@ -37,20 +37,14 @@ static int __igt_gpu_reloc(struct i915_execbuffer *eb, return err; /* 8-Byte aligned */ - if (!__reloc_entry_gpu(eb, vma, - offsets[0] * sizeof(u32), - 0)) { - err = -EIO; + err = __reloc_entry_gpu(eb, vma, offsets[0] * sizeof(u32), 0); + if (err) goto unpin_vma; - } /* !8-Byte aligned */ - if (!__reloc_entry_gpu(eb, vma, - offsets[1] * sizeof(u32), - 1)) { - err = -EIO; + err = __reloc_entry_gpu(eb, vma, offsets[1] * sizeof(u32), 1); + if (err) goto unpin_vma; - } /* Skip to the end of the cmd page */ i = PAGE_SIZE / sizeof(u32) - RELOC_TAIL - 1; @@ -60,12 +54,9 @@ static int __igt_gpu_reloc(struct i915_execbuffer *eb, eb->reloc_cache.rq_size += i; /* Force batch chaining */ - if (!__reloc_entry_gpu(eb, vma, - offsets[2] * sizeof(u32), - 2)) { - err = -EIO; + err = __reloc_entry_gpu(eb, vma, offsets[2] * sizeof(u32), 2); + if (err) goto unpin_vma; - } GEM_BUG_ON(!eb->reloc_cache.rq); rq = i915_request_get(eb->reloc_cache.rq); diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_object_blt.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_object_blt.c index 31549ad83fa6..23b6e11bbc3e 100644 --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_object_blt.c +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_object_blt.c @@ -193,7 +193,7 @@ err_src: } struct igt_thread_arg { - struct drm_i915_private *i915; + struct intel_engine_cs *engine; struct i915_gem_context *ctx; struct file *file; struct rnd_state prng; @@ -203,7 +203,7 @@ struct igt_thread_arg { static int igt_fill_blt_thread(void *arg) { struct igt_thread_arg *thread = arg; - struct drm_i915_private *i915 = thread->i915; + struct intel_engine_cs *engine = thread->engine; struct rnd_state *prng = &thread->prng; struct drm_i915_gem_object *obj; struct i915_gem_context *ctx; @@ -215,7 +215,7 @@ static int igt_fill_blt_thread(void *arg) ctx = thread->ctx; if (!ctx) { - ctx = live_context(i915, thread->file); + ctx = live_context_for_engine(engine, thread->file); if (IS_ERR(ctx)) return PTR_ERR(ctx); @@ -223,7 +223,7 @@ static int igt_fill_blt_thread(void *arg) ctx->sched.priority = I915_USER_PRIORITY(prio); } - ce = i915_gem_context_get_engine(ctx, BCS0); + ce = i915_gem_context_get_engine(ctx, 0); GEM_BUG_ON(IS_ERR(ce)); /* @@ -256,7 +256,7 @@ static int igt_fill_blt_thread(void *arg) pr_debug("%s with phys_sz= %x, sz=%x, val=%x\n", __func__, phys_sz, sz, val); - obj = huge_gem_object(i915, phys_sz, sz); + obj = huge_gem_object(engine->i915, phys_sz, sz); if (IS_ERR(obj)) { err = PTR_ERR(obj); goto err_flush; @@ -321,7 +321,7 @@ err_flush: static int igt_copy_blt_thread(void *arg) { struct igt_thread_arg *thread = arg; - struct drm_i915_private *i915 = thread->i915; + struct intel_engine_cs *engine = thread->engine; struct rnd_state *prng = &thread->prng; struct drm_i915_gem_object *src, *dst; struct i915_gem_context *ctx; @@ -333,7 +333,7 @@ static int igt_copy_blt_thread(void *arg) ctx = thread->ctx; if (!ctx) { - ctx = live_context(i915, thread->file); + ctx = live_context_for_engine(engine, thread->file); if (IS_ERR(ctx)) return PTR_ERR(ctx); @@ -341,7 +341,7 @@ static int igt_copy_blt_thread(void *arg) ctx->sched.priority = I915_USER_PRIORITY(prio); } - ce = i915_gem_context_get_engine(ctx, BCS0); + ce = i915_gem_context_get_engine(ctx, 0); GEM_BUG_ON(IS_ERR(ce)); /* @@ -374,7 +374,7 @@ static int igt_copy_blt_thread(void *arg) pr_debug("%s with phys_sz= %x, sz=%x, val=%x\n", __func__, phys_sz, sz, val); - src = huge_gem_object(i915, phys_sz, sz); + src = huge_gem_object(engine->i915, phys_sz, sz); if (IS_ERR(src)) { err = PTR_ERR(src); goto err_flush; @@ -394,7 +394,7 @@ static int igt_copy_blt_thread(void *arg) if (!(src->cache_coherent & I915_BO_CACHE_COHERENT_FOR_READ)) src->cache_dirty = true; - dst = huge_gem_object(i915, phys_sz, sz); + dst = huge_gem_object(engine->i915, phys_sz, sz); if (IS_ERR(dst)) { err = PTR_ERR(dst); goto err_put_src; @@ -456,7 +456,7 @@ err_flush: return err; } -static int igt_threaded_blt(struct drm_i915_private *i915, +static int igt_threaded_blt(struct intel_engine_cs *engine, int (*blt_fn)(void *arg), unsigned int flags) #define SINGLE_CTX BIT(0) @@ -477,14 +477,14 @@ static int igt_threaded_blt(struct drm_i915_private *i915, if (!thread) goto out_tsk; - thread[0].file = mock_file(i915); + thread[0].file = mock_file(engine->i915); if (IS_ERR(thread[0].file)) { err = PTR_ERR(thread[0].file); goto out_thread; } if (flags & SINGLE_CTX) { - thread[0].ctx = live_context(i915, thread[0].file); + thread[0].ctx = live_context_for_engine(engine, thread[0].file); if (IS_ERR(thread[0].ctx)) { err = PTR_ERR(thread[0].ctx); goto out_file; @@ -492,7 +492,7 @@ static int igt_threaded_blt(struct drm_i915_private *i915, } for (i = 0; i < n_cpus; ++i) { - thread[i].i915 = i915; + thread[i].engine = engine; thread[i].file = thread[0].file; thread[i].ctx = thread[0].ctx; thread[i].n_cpus = n_cpus; @@ -532,24 +532,40 @@ out_tsk: return err; } +static int test_copy_engines(struct drm_i915_private *i915, + int (*fn)(void *arg), + unsigned int flags) +{ + struct intel_engine_cs *engine; + int ret; + + for_each_uabi_class_engine(engine, I915_ENGINE_CLASS_COPY, i915) { + ret = igt_threaded_blt(engine, fn, flags); + if (ret) + return ret; + } + + return 0; +} + static int igt_fill_blt(void *arg) { - return igt_threaded_blt(arg, igt_fill_blt_thread, 0); + return test_copy_engines(arg, igt_fill_blt_thread, 0); } static int igt_fill_blt_ctx0(void *arg) { - return igt_threaded_blt(arg, igt_fill_blt_thread, SINGLE_CTX); + return test_copy_engines(arg, igt_fill_blt_thread, SINGLE_CTX); } static int igt_copy_blt(void *arg) { - return igt_threaded_blt(arg, igt_copy_blt_thread, 0); + return test_copy_engines(arg, igt_copy_blt_thread, 0); } static int igt_copy_blt_ctx0(void *arg) { - return igt_threaded_blt(arg, igt_copy_blt_thread, SINGLE_CTX); + return test_copy_engines(arg, igt_copy_blt_thread, SINGLE_CTX); } int i915_gem_object_blt_live_selftests(struct drm_i915_private *i915) @@ -564,9 +580,6 @@ int i915_gem_object_blt_live_selftests(struct drm_i915_private *i915) if (intel_gt_is_wedged(&i915->gt)) return 0; - if (!HAS_ENGINE(i915, BCS0)) - return 0; - return i915_live_subtests(tests, i915); } diff --git a/drivers/gpu/drm/i915/gem/selftests/mock_context.c b/drivers/gpu/drm/i915/gem/selftests/mock_context.c index e7e3c620f542..aa0d06cf1903 100644 --- a/drivers/gpu/drm/i915/gem/selftests/mock_context.c +++ b/drivers/gpu/drm/i915/gem/selftests/mock_context.c @@ -100,6 +100,43 @@ err_ctx: } struct i915_gem_context * +live_context_for_engine(struct intel_engine_cs *engine, struct file *file) +{ + struct i915_gem_engines *engines; + struct i915_gem_context *ctx; + struct intel_context *ce; + + engines = alloc_engines(1); + if (!engines) + return ERR_PTR(-ENOMEM); + + ctx = live_context(engine->i915, file); + if (IS_ERR(ctx)) { + __free_engines(engines, 0); + return ctx; + } + + ce = intel_context_create(engine); + if (IS_ERR(ce)) { + __free_engines(engines, 0); + return ERR_CAST(ce); + } + + intel_context_set_gem(ce, ctx); + engines->engines[0] = ce; + engines->num_engines = 1; + + mutex_lock(&ctx->engines_mutex); + i915_gem_context_set_user_engines(ctx); + engines = rcu_replace_pointer(ctx->engines, engines, 1); + mutex_unlock(&ctx->engines_mutex); + + engines_idle_release(ctx, engines); + + return ctx; +} + +struct i915_gem_context * kernel_context(struct drm_i915_private *i915) { struct i915_gem_context *ctx; diff --git a/drivers/gpu/drm/i915/gem/selftests/mock_context.h b/drivers/gpu/drm/i915/gem/selftests/mock_context.h index fb83d2f09212..2a6121d33352 100644 --- a/drivers/gpu/drm/i915/gem/selftests/mock_context.h +++ b/drivers/gpu/drm/i915/gem/selftests/mock_context.h @@ -9,6 +9,7 @@ struct file; struct drm_i915_private; +struct intel_engine_cs; void mock_init_contexts(struct drm_i915_private *i915); @@ -21,6 +22,9 @@ void mock_context_close(struct i915_gem_context *ctx); struct i915_gem_context * live_context(struct drm_i915_private *i915, struct file *file); +struct i915_gem_context * +live_context_for_engine(struct intel_engine_cs *engine, struct file *file); + struct i915_gem_context *kernel_context(struct drm_i915_private *i915); void kernel_context_close(struct i915_gem_context *ctx); diff --git a/drivers/gpu/drm/i915/gt/gen2_engine_cs.c b/drivers/gpu/drm/i915/gt/gen2_engine_cs.c new file mode 100644 index 000000000000..b491a64919c8 --- /dev/null +++ b/drivers/gpu/drm/i915/gt/gen2_engine_cs.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2020 Intel Corporation + */ + +#include "gen2_engine_cs.h" +#include "i915_drv.h" +#include "intel_engine.h" +#include "intel_gpu_commands.h" +#include "intel_gt.h" +#include "intel_gt_irq.h" +#include "intel_ring.h" + +int gen2_emit_flush(struct i915_request *rq, u32 mode) +{ + unsigned int num_store_dw = 12; + u32 cmd, *cs; + + cmd = MI_FLUSH; + if (mode & EMIT_INVALIDATE) + cmd |= MI_READ_FLUSH; + + cs = intel_ring_begin(rq, 2 + 4 * num_store_dw); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + *cs++ = cmd; + while (num_store_dw--) { + *cs++ = MI_STORE_DWORD_INDEX; + *cs++ = I915_GEM_HWS_SCRATCH * sizeof(u32); + *cs++ = 0; + *cs++ = MI_FLUSH | MI_NO_WRITE_FLUSH; + } + *cs++ = cmd; + + intel_ring_advance(rq, cs); + + return 0; +} + +int gen4_emit_flush_rcs(struct i915_request *rq, u32 mode) +{ + u32 cmd, *cs; + int i; + + /* + * read/write caches: + * + * I915_GEM_DOMAIN_RENDER is always invalidated, but is + * only flushed if MI_NO_WRITE_FLUSH is unset. On 965, it is + * also flushed at 2d versus 3d pipeline switches. + * + * read-only caches: + * + * I915_GEM_DOMAIN_SAMPLER is flushed on pre-965 if + * MI_READ_FLUSH is set, and is always flushed on 965. + * + * I915_GEM_DOMAIN_COMMAND may not exist? + * + * I915_GEM_DOMAIN_INSTRUCTION, which exists on 965, is + * invalidated when MI_EXE_FLUSH is set. + * + * I915_GEM_DOMAIN_VERTEX, which exists on 965, is + * invalidated with every MI_FLUSH. + * + * TLBs: + * + * On 965, TLBs associated with I915_GEM_DOMAIN_COMMAND + * and I915_GEM_DOMAIN_CPU in are invalidated at PTE write and + * I915_GEM_DOMAIN_RENDER and I915_GEM_DOMAIN_SAMPLER + * are flushed at any MI_FLUSH. + */ + + cmd = MI_FLUSH; + if (mode & EMIT_INVALIDATE) { + cmd |= MI_EXE_FLUSH; + if (IS_G4X(rq->engine->i915) || IS_GEN(rq->engine->i915, 5)) + cmd |= MI_INVALIDATE_ISP; + } + + i = 2; + if (mode & EMIT_INVALIDATE) + i += 20; + + cs = intel_ring_begin(rq, i); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + *cs++ = cmd; + + /* + * A random delay to let the CS invalidate take effect? Without this + * delay, the GPU relocation path fails as the CS does not see + * the updated contents. Just as important, if we apply the flushes + * to the EMIT_FLUSH branch (i.e. immediately after the relocation + * write and before the invalidate on the next batch), the relocations + * still fail. This implies that is a delay following invalidation + * that is required to reset the caches as opposed to a delay to + * ensure the memory is written. + */ + if (mode & EMIT_INVALIDATE) { + *cs++ = GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE; + *cs++ = intel_gt_scratch_offset(rq->engine->gt, + INTEL_GT_SCRATCH_FIELD_DEFAULT) | + PIPE_CONTROL_GLOBAL_GTT; + *cs++ = 0; + *cs++ = 0; + + for (i = 0; i < 12; i++) + *cs++ = MI_FLUSH; + + *cs++ = GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE; + *cs++ = intel_gt_scratch_offset(rq->engine->gt, + INTEL_GT_SCRATCH_FIELD_DEFAULT) | + PIPE_CONTROL_GLOBAL_GTT; + *cs++ = 0; + *cs++ = 0; + } + + *cs++ = cmd; + + intel_ring_advance(rq, cs); + + return 0; +} + +int gen4_emit_flush_vcs(struct i915_request *rq, u32 mode) +{ + u32 *cs; + + cs = intel_ring_begin(rq, 2); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + *cs++ = MI_FLUSH; + *cs++ = MI_NOOP; + intel_ring_advance(rq, cs); + + return 0; +} + +static u32 *__gen2_emit_breadcrumb(struct i915_request *rq, u32 *cs, + int flush, int post) +{ + GEM_BUG_ON(i915_request_active_timeline(rq)->hwsp_ggtt != rq->engine->status_page.vma); + GEM_BUG_ON(offset_in_page(i915_request_active_timeline(rq)->hwsp_offset) != I915_GEM_HWS_SEQNO_ADDR); + + *cs++ = MI_FLUSH; + + while (flush--) { + *cs++ = MI_STORE_DWORD_INDEX; + *cs++ = I915_GEM_HWS_SCRATCH * sizeof(u32); + *cs++ = rq->fence.seqno; + } + + while (post--) { + *cs++ = MI_STORE_DWORD_INDEX; + *cs++ = I915_GEM_HWS_SEQNO_ADDR; + *cs++ = rq->fence.seqno; + } + + *cs++ = MI_USER_INTERRUPT; + + rq->tail = intel_ring_offset(rq, cs); + assert_ring_tail_valid(rq->ring, rq->tail); + + return cs; +} + +u32 *gen3_emit_breadcrumb(struct i915_request *rq, u32 *cs) +{ + return __gen2_emit_breadcrumb(rq, cs, 16, 8); +} + +u32 *gen5_emit_breadcrumb(struct i915_request *rq, u32 *cs) +{ + return __gen2_emit_breadcrumb(rq, cs, 8, 8); +} + +/* Just userspace ABI convention to limit the wa batch bo to a resonable size */ +#define I830_BATCH_LIMIT SZ_256K +#define I830_TLB_ENTRIES (2) +#define I830_WA_SIZE max(I830_TLB_ENTRIES * SZ_4K, I830_BATCH_LIMIT) +int i830_emit_bb_start(struct i915_request *rq, + u64 offset, u32 len, + unsigned int dispatch_flags) +{ + u32 *cs, cs_offset = + intel_gt_scratch_offset(rq->engine->gt, + INTEL_GT_SCRATCH_FIELD_DEFAULT); + + GEM_BUG_ON(rq->engine->gt->scratch->size < I830_WA_SIZE); + + cs = intel_ring_begin(rq, 6); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + /* Evict the invalid PTE TLBs */ + *cs++ = COLOR_BLT_CMD | BLT_WRITE_RGBA; + *cs++ = BLT_DEPTH_32 | BLT_ROP_COLOR_COPY | 4096; + *cs++ = I830_TLB_ENTRIES << 16 | 4; /* load each page */ + *cs++ = cs_offset; + *cs++ = 0xdeadbeef; + *cs++ = MI_NOOP; + intel_ring_advance(rq, cs); + + if ((dispatch_flags & I915_DISPATCH_PINNED) == 0) { + if (len > I830_BATCH_LIMIT) + return -ENOSPC; + + cs = intel_ring_begin(rq, 6 + 2); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + /* + * Blit the batch (which has now all relocs applied) to the + * stable batch scratch bo area (so that the CS never + * stumbles over its tlb invalidation bug) ... + */ + *cs++ = SRC_COPY_BLT_CMD | BLT_WRITE_RGBA | (6 - 2); + *cs++ = BLT_DEPTH_32 | BLT_ROP_SRC_COPY | 4096; + *cs++ = DIV_ROUND_UP(len, 4096) << 16 | 4096; + *cs++ = cs_offset; + *cs++ = 4096; + *cs++ = offset; + + *cs++ = MI_FLUSH; + *cs++ = MI_NOOP; + intel_ring_advance(rq, cs); + + /* ... and execute it. */ + offset = cs_offset; + } + + if (!(dispatch_flags & I915_DISPATCH_SECURE)) + offset |= MI_BATCH_NON_SECURE; + + cs = intel_ring_begin(rq, 2); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + *cs++ = MI_BATCH_BUFFER_START | MI_BATCH_GTT; + *cs++ = offset; + intel_ring_advance(rq, cs); + + return 0; +} + +int gen3_emit_bb_start(struct i915_request *rq, + u64 offset, u32 len, + unsigned int dispatch_flags) +{ + u32 *cs; + + if (!(dispatch_flags & I915_DISPATCH_SECURE)) + offset |= MI_BATCH_NON_SECURE; + + cs = intel_ring_begin(rq, 2); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + *cs++ = MI_BATCH_BUFFER_START | MI_BATCH_GTT; + *cs++ = offset; + intel_ring_advance(rq, cs); + + return 0; +} + +int gen4_emit_bb_start(struct i915_request *rq, + u64 offset, u32 length, + unsigned int dispatch_flags) +{ + u32 security; + u32 *cs; + + security = MI_BATCH_NON_SECURE_I965; + if (dispatch_flags & I915_DISPATCH_SECURE) + security = 0; + + cs = intel_ring_begin(rq, 2); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + *cs++ = MI_BATCH_BUFFER_START | MI_BATCH_GTT | security; + *cs++ = offset; + intel_ring_advance(rq, cs); + + return 0; +} + +void gen2_irq_enable(struct intel_engine_cs *engine) +{ + struct drm_i915_private *i915 = engine->i915; + + i915->irq_mask &= ~engine->irq_enable_mask; + intel_uncore_write16(&i915->uncore, GEN2_IMR, i915->irq_mask); + ENGINE_POSTING_READ16(engine, RING_IMR); +} + +void gen2_irq_disable(struct intel_engine_cs *engine) +{ + struct drm_i915_private *i915 = engine->i915; + + i915->irq_mask |= engine->irq_enable_mask; + intel_uncore_write16(&i915->uncore, GEN2_IMR, i915->irq_mask); +} + +void gen3_irq_enable(struct intel_engine_cs *engine) +{ + engine->i915->irq_mask &= ~engine->irq_enable_mask; + intel_uncore_write(engine->uncore, GEN2_IMR, engine->i915->irq_mask); + intel_uncore_posting_read_fw(engine->uncore, GEN2_IMR); +} + +void gen3_irq_disable(struct intel_engine_cs *engine) +{ + engine->i915->irq_mask |= engine->irq_enable_mask; + intel_uncore_write(engine->uncore, GEN2_IMR, engine->i915->irq_mask); +} + +void gen5_irq_enable(struct intel_engine_cs *engine) +{ + gen5_gt_enable_irq(engine->gt, engine->irq_enable_mask); +} + +void gen5_irq_disable(struct intel_engine_cs *engine) +{ + gen5_gt_disable_irq(engine->gt, engine->irq_enable_mask); +} diff --git a/drivers/gpu/drm/i915/gt/gen2_engine_cs.h b/drivers/gpu/drm/i915/gt/gen2_engine_cs.h new file mode 100644 index 000000000000..a5cd64a65c9e --- /dev/null +++ b/drivers/gpu/drm/i915/gt/gen2_engine_cs.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2020 Intel Corporation + */ + +#ifndef __GEN2_ENGINE_CS_H__ +#define __GEN2_ENGINE_CS_H__ + +#include <linux/types.h> + +struct i915_request; +struct intel_engine_cs; + +int gen2_emit_flush(struct i915_request *rq, u32 mode); +int gen4_emit_flush_rcs(struct i915_request *rq, u32 mode); +int gen4_emit_flush_vcs(struct i915_request *rq, u32 mode); + +u32 *gen3_emit_breadcrumb(struct i915_request *rq, u32 *cs); +u32 *gen5_emit_breadcrumb(struct i915_request *rq, u32 *cs); + +int i830_emit_bb_start(struct i915_request *rq, + u64 offset, u32 len, + unsigned int dispatch_flags); +int gen3_emit_bb_start(struct i915_request *rq, + u64 offset, u32 len, + unsigned int dispatch_flags); +int gen4_emit_bb_start(struct i915_request *rq, + u64 offset, u32 length, + unsigned int dispatch_flags); + +void gen2_irq_enable(struct intel_engine_cs *engine); +void gen2_irq_disable(struct intel_engine_cs *engine); +void gen3_irq_enable(struct intel_engine_cs *engine); +void gen3_irq_disable(struct intel_engine_cs *engine); +void gen5_irq_enable(struct intel_engine_cs *engine); +void gen5_irq_disable(struct intel_engine_cs *engine); + +#endif /* __GEN2_ENGINE_CS_H__ */ diff --git a/drivers/gpu/drm/i915/gt/gen6_engine_cs.c b/drivers/gpu/drm/i915/gt/gen6_engine_cs.c new file mode 100644 index 000000000000..ce38d1bcaba3 --- /dev/null +++ b/drivers/gpu/drm/i915/gt/gen6_engine_cs.c @@ -0,0 +1,455 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2020 Intel Corporation + */ + +#include "gen6_engine_cs.h" +#include "intel_engine.h" +#include "intel_gpu_commands.h" +#include "intel_gt.h" +#include "intel_gt_irq.h" +#include "intel_gt_pm_irq.h" +#include "intel_ring.h" + +#define HWS_SCRATCH_ADDR (I915_GEM_HWS_SCRATCH * sizeof(u32)) + +/* + * Emits a PIPE_CONTROL with a non-zero post-sync operation, for + * implementing two workarounds on gen6. From section 1.4.7.1 + * "PIPE_CONTROL" of the Sandy Bridge PRM volume 2 part 1: + * + * [DevSNB-C+{W/A}] Before any depth stall flush (including those + * produced by non-pipelined state commands), software needs to first + * send a PIPE_CONTROL with no bits set except Post-Sync Operation != + * 0. + * + * [Dev-SNB{W/A}]: Before a PIPE_CONTROL with Write Cache Flush Enable + * =1, a PIPE_CONTROL with any non-zero post-sync-op is required. + * + * And the workaround for these two requires this workaround first: + * + * [Dev-SNB{W/A}]: Pipe-control with CS-stall bit set must be sent + * BEFORE the pipe-control with a post-sync op and no write-cache + * flushes. + * + * And this last workaround is tricky because of the requirements on + * that bit. From section 1.4.7.2.3 "Stall" of the Sandy Bridge PRM + * volume 2 part 1: + * + * "1 of the following must also be set: + * - Render Target Cache Flush Enable ([12] of DW1) + * - Depth Cache Flush Enable ([0] of DW1) + * - Stall at Pixel Scoreboard ([1] of DW1) + * - Depth Stall ([13] of DW1) + * - Post-Sync Operation ([13] of DW1) + * - Notify Enable ([8] of DW1)" + * + * The cache flushes require the workaround flush that triggered this + * one, so we can't use it. Depth stall would trigger the same. + * Post-sync nonzero is what triggered this second workaround, so we + * can't use that one either. Notify enable is IRQs, which aren't + * really our business. That leaves only stall at scoreboard. + */ +static int +gen6_emit_post_sync_nonzero_flush(struct i915_request *rq) +{ + u32 scratch_addr = + intel_gt_scratch_offset(rq->engine->gt, + INTEL_GT_SCRATCH_FIELD_RENDER_FLUSH); + u32 *cs; + + cs = intel_ring_begin(rq, 6); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + *cs++ = GFX_OP_PIPE_CONTROL(5); + *cs++ = PIPE_CONTROL_CS_STALL | PIPE_CONTROL_STALL_AT_SCOREBOARD; + *cs++ = scratch_addr | PIPE_CONTROL_GLOBAL_GTT; + *cs++ = 0; /* low dword */ + *cs++ = 0; /* high dword */ + *cs++ = MI_NOOP; + intel_ring_advance(rq, cs); + + cs = intel_ring_begin(rq, 6); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + *cs++ = GFX_OP_PIPE_CONTROL(5); + *cs++ = PIPE_CONTROL_QW_WRITE; + *cs++ = scratch_addr | PIPE_CONTROL_GLOBAL_GTT; + *cs++ = 0; + *cs++ = 0; + *cs++ = MI_NOOP; + intel_ring_advance(rq, cs); + + return 0; +} + +int gen6_emit_flush_rcs(struct i915_request *rq, u32 mode) +{ + u32 scratch_addr = + intel_gt_scratch_offset(rq->engine->gt, + INTEL_GT_SCRATCH_FIELD_RENDER_FLUSH); + u32 *cs, flags = 0; + int ret; + + /* Force SNB workarounds for PIPE_CONTROL flushes */ + ret = gen6_emit_post_sync_nonzero_flush(rq); + if (ret) + return ret; + + /* + * Just flush everything. Experiments have shown that reducing the + * number of bits based on the write domains has little performance + * impact. And when rearranging requests, the order of flushes is + * unknown. + */ + if (mode & EMIT_FLUSH) { + flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; + flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; + /* + * Ensure that any following seqno writes only happen + * when the render cache is indeed flushed. + */ + flags |= PIPE_CONTROL_CS_STALL; + } + if (mode & EMIT_INVALIDATE) { + flags |= PIPE_CONTROL_TLB_INVALIDATE; + flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE; + /* + * TLB invalidate requires a post-sync write. + */ + flags |= PIPE_CONTROL_QW_WRITE | PIPE_CONTROL_CS_STALL; + } + + cs = intel_ring_begin(rq, 4); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + *cs++ = GFX_OP_PIPE_CONTROL(4); + *cs++ = flags; + *cs++ = scratch_addr | PIPE_CONTROL_GLOBAL_GTT; + *cs++ = 0; + intel_ring_advance(rq, cs); + + return 0; +} + +u32 *gen6_emit_breadcrumb_rcs(struct i915_request *rq, u32 *cs) +{ + /* First we do the gen6_emit_post_sync_nonzero_flush w/a */ + *cs++ = GFX_OP_PIPE_CONTROL(4); + *cs++ = PIPE_CONTROL_CS_STALL | PIPE_CONTROL_STALL_AT_SCOREBOARD; + *cs++ = 0; + *cs++ = 0; + + *cs++ = GFX_OP_PIPE_CONTROL(4); + *cs++ = PIPE_CONTROL_QW_WRITE; + *cs++ = intel_gt_scratch_offset(rq->engine->gt, + INTEL_GT_SCRATCH_FIELD_DEFAULT) | + PIPE_CONTROL_GLOBAL_GTT; + *cs++ = 0; + + /* Finally we can flush and with it emit the breadcrumb */ + *cs++ = GFX_OP_PIPE_CONTROL(4); + *cs++ = (PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH | + PIPE_CONTROL_DEPTH_CACHE_FLUSH | + PIPE_CONTROL_DC_FLUSH_ENABLE | + PIPE_CONTROL_QW_WRITE | + PIPE_CONTROL_CS_STALL); + *cs++ = i915_request_active_timeline(rq)->hwsp_offset | + PIPE_CONTROL_GLOBAL_GTT; + *cs++ = rq->fence.seqno; + + *cs++ = MI_USER_INTERRUPT; + *cs++ = MI_NOOP; + + rq->tail = intel_ring_offset(rq, cs); + assert_ring_tail_valid(rq->ring, rq->tail); + + return cs; +} + +static int mi_flush_dw(struct i915_request *rq, u32 flags) +{ + u32 cmd, *cs; + + cs = intel_ring_begin(rq, 4); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + cmd = MI_FLUSH_DW; + + /* + * We always require a command barrier so that subsequent + * commands, such as breadcrumb interrupts, are strictly ordered + * wrt the contents of the write cache being flushed to memory + * (and thus being coherent from the CPU). + */ + cmd |= MI_FLUSH_DW_STORE_INDEX | MI_FLUSH_DW_OP_STOREDW; + + /* + * Bspec vol 1c.3 - blitter engine command streamer: + * "If ENABLED, all TLBs will be invalidated once the flush + * operation is complete. This bit is only valid when the + * Post-Sync Operation field is a value of 1h or 3h." + */ + cmd |= flags; + + *cs++ = cmd; + *cs++ = HWS_SCRATCH_ADDR | MI_FLUSH_DW_USE_GTT; + *cs++ = 0; + *cs++ = MI_NOOP; + + intel_ring_advance(rq, cs); + + return 0; +} + +static int gen6_flush_dw(struct i915_request *rq, u32 mode, u32 invflags) +{ + return mi_flush_dw(rq, mode & EMIT_INVALIDATE ? invflags : 0); +} + +int gen6_emit_flush_xcs(struct i915_request *rq, u32 mode) +{ + return gen6_flush_dw(rq, mode, MI_INVALIDATE_TLB); +} + +int gen6_emit_flush_vcs(struct i915_request *rq, u32 mode) +{ + return gen6_flush_dw(rq, mode, MI_INVALIDATE_TLB | MI_INVALIDATE_BSD); +} + +int gen6_emit_bb_start(struct i915_request *rq, + u64 offset, u32 len, + unsigned int dispatch_flags) +{ + u32 security; + u32 *cs; + + security = MI_BATCH_NON_SECURE_I965; + if (dispatch_flags & I915_DISPATCH_SECURE) + security = 0; + + cs = intel_ring_begin(rq, 2); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + cs = __gen6_emit_bb_start(cs, offset, security); + intel_ring_advance(rq, cs); + + return 0; +} + +int +hsw_emit_bb_start(struct i915_request *rq, + u64 offset, u32 len, + unsigned int dispatch_flags) +{ + u32 security; + u32 *cs; + + security = MI_BATCH_PPGTT_HSW | MI_BATCH_NON_SECURE_HSW; + if (dispatch_flags & I915_DISPATCH_SECURE) + security = 0; + + cs = intel_ring_begin(rq, 2); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + cs = __gen6_emit_bb_start(cs, offset, security); + intel_ring_advance(rq, cs); + + return 0; +} + +static int gen7_stall_cs(struct i915_request *rq) +{ + u32 *cs; + + cs = intel_ring_begin(rq, 4); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + *cs++ = GFX_OP_PIPE_CONTROL(4); + *cs++ = PIPE_CONTROL_CS_STALL | PIPE_CONTROL_STALL_AT_SCOREBOARD; + *cs++ = 0; + *cs++ = 0; + intel_ring_advance(rq, cs); + + return 0; +} + +int gen7_emit_flush_rcs(struct i915_request *rq, u32 mode) +{ + u32 scratch_addr = + intel_gt_scratch_offset(rq->engine->gt, + INTEL_GT_SCRATCH_FIELD_RENDER_FLUSH); + u32 *cs, flags = 0; + + /* + * Ensure that any following seqno writes only happen when the render + * cache is indeed flushed. + * + * Workaround: 4th PIPE_CONTROL command (except the ones with only + * read-cache invalidate bits set) must have the CS_STALL bit set. We + * don't try to be clever and just set it unconditionally. + */ + flags |= PIPE_CONTROL_CS_STALL; + + /* + * CS_STALL suggests at least a post-sync write. + */ + flags |= PIPE_CONTROL_QW_WRITE; + flags |= PIPE_CONTROL_GLOBAL_GTT_IVB; + + /* + * Just flush everything. Experiments have shown that reducing the + * number of bits based on the write domains has little performance + * impact. + */ + 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 (mode & EMIT_INVALIDATE) { + flags |= PIPE_CONTROL_TLB_INVALIDATE; + flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_MEDIA_STATE_CLEAR; + + /* + * Workaround: we must issue a pipe_control with CS-stall bit + * set before a pipe_control command that has the state cache + * invalidate bit set. + */ + gen7_stall_cs(rq); + } + + cs = intel_ring_begin(rq, 4); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + *cs++ = GFX_OP_PIPE_CONTROL(4); + *cs++ = flags; + *cs++ = scratch_addr; + *cs++ = 0; + intel_ring_advance(rq, cs); + + return 0; +} + +u32 *gen7_emit_breadcrumb_rcs(struct i915_request *rq, u32 *cs) +{ + *cs++ = GFX_OP_PIPE_CONTROL(4); + *cs++ = (PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH | + PIPE_CONTROL_DEPTH_CACHE_FLUSH | + PIPE_CONTROL_DC_FLUSH_ENABLE | + PIPE_CONTROL_FLUSH_ENABLE | + PIPE_CONTROL_QW_WRITE | + PIPE_CONTROL_GLOBAL_GTT_IVB | + PIPE_CONTROL_CS_STALL); + *cs++ = i915_request_active_timeline(rq)->hwsp_offset; + *cs++ = rq->fence.seqno; + + *cs++ = MI_USER_INTERRUPT; + *cs++ = MI_NOOP; + + rq->tail = intel_ring_offset(rq, cs); + assert_ring_tail_valid(rq->ring, rq->tail); + + return cs; +} + +u32 *gen6_emit_breadcrumb_xcs(struct i915_request *rq, u32 *cs) +{ + GEM_BUG_ON(i915_request_active_timeline(rq)->hwsp_ggtt != rq->engine->status_page.vma); + GEM_BUG_ON(offset_in_page(i915_request_active_timeline(rq)->hwsp_offset) != I915_GEM_HWS_SEQNO_ADDR); + + *cs++ = MI_FLUSH_DW | MI_FLUSH_DW_OP_STOREDW | MI_FLUSH_DW_STORE_INDEX; + *cs++ = I915_GEM_HWS_SEQNO_ADDR | MI_FLUSH_DW_USE_GTT; + *cs++ = rq->fence.seqno; + + *cs++ = MI_USER_INTERRUPT; + + rq->tail = intel_ring_offset(rq, cs); + assert_ring_tail_valid(rq->ring, rq->tail); + + return cs; +} + +#define GEN7_XCS_WA 32 +u32 *gen7_emit_breadcrumb_xcs(struct i915_request *rq, u32 *cs) +{ + int i; + + GEM_BUG_ON(i915_request_active_timeline(rq)->hwsp_ggtt != rq->engine->status_page.vma); + GEM_BUG_ON(offset_in_page(i915_request_active_timeline(rq)->hwsp_offset) != I915_GEM_HWS_SEQNO_ADDR); + + *cs++ = MI_FLUSH_DW | MI_INVALIDATE_TLB | + MI_FLUSH_DW_OP_STOREDW | MI_FLUSH_DW_STORE_INDEX; + *cs++ = I915_GEM_HWS_SEQNO_ADDR | MI_FLUSH_DW_USE_GTT; + *cs++ = rq->fence.seqno; + + for (i = 0; i < GEN7_XCS_WA; i++) { + *cs++ = MI_STORE_DWORD_INDEX; + *cs++ = I915_GEM_HWS_SEQNO_ADDR; + *cs++ = rq->fence.seqno; + } + + *cs++ = MI_FLUSH_DW; + *cs++ = 0; + *cs++ = 0; + + *cs++ = MI_USER_INTERRUPT; + *cs++ = MI_NOOP; + + rq->tail = intel_ring_offset(rq, cs); + assert_ring_tail_valid(rq->ring, rq->tail); + + return cs; +} +#undef GEN7_XCS_WA + +void gen6_irq_enable(struct intel_engine_cs *engine) +{ + ENGINE_WRITE(engine, RING_IMR, + ~(engine->irq_enable_mask | engine->irq_keep_mask)); + + /* Flush/delay to ensure the RING_IMR is active before the GT IMR */ + ENGINE_POSTING_READ(engine, RING_IMR); + + gen5_gt_enable_irq(engine->gt, engine->irq_enable_mask); +} + +void gen6_irq_disable(struct intel_engine_cs *engine) +{ + ENGINE_WRITE(engine, RING_IMR, ~engine->irq_keep_mask); + gen5_gt_disable_irq(engine->gt, engine->irq_enable_mask); +} + +void hsw_irq_enable_vecs(struct intel_engine_cs *engine) +{ + ENGINE_WRITE(engine, RING_IMR, ~engine->irq_enable_mask); + + /* Flush/delay to ensure the RING_IMR is active before the GT IMR */ + ENGINE_POSTING_READ(engine, RING_IMR); + + gen6_gt_pm_unmask_irq(engine->gt, engine->irq_enable_mask); +} + +void hsw_irq_disable_vecs(struct intel_engine_cs *engine) +{ + ENGINE_WRITE(engine, RING_IMR, ~0); + gen6_gt_pm_mask_irq(engine->gt, engine->irq_enable_mask); +} diff --git a/drivers/gpu/drm/i915/gt/gen6_engine_cs.h b/drivers/gpu/drm/i915/gt/gen6_engine_cs.h new file mode 100644 index 000000000000..76c6bc9f3bde --- /dev/null +++ b/drivers/gpu/drm/i915/gt/gen6_engine_cs.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2020 Intel Corporation + */ + +#ifndef __GEN6_ENGINE_CS_H__ +#define __GEN6_ENGINE_CS_H__ + +#include <linux/types.h> + +#include "intel_gpu_commands.h" + +struct i915_request; +struct intel_engine_cs; + +int gen6_emit_flush_rcs(struct i915_request *rq, u32 mode); +int gen6_emit_flush_vcs(struct i915_request *rq, u32 mode); +int gen6_emit_flush_xcs(struct i915_request *rq, u32 mode); +u32 *gen6_emit_breadcrumb_rcs(struct i915_request *rq, u32 *cs); +u32 *gen6_emit_breadcrumb_xcs(struct i915_request *rq, u32 *cs); + +int gen7_emit_flush_rcs(struct i915_request *rq, u32 mode); +u32 *gen7_emit_breadcrumb_rcs(struct i915_request *rq, u32 *cs); +u32 *gen7_emit_breadcrumb_xcs(struct i915_request *rq, u32 *cs); + +int gen6_emit_bb_start(struct i915_request *rq, + u64 offset, u32 len, + unsigned int dispatch_flags); +int hsw_emit_bb_start(struct i915_request *rq, + u64 offset, u32 len, + unsigned int dispatch_flags); + +void gen6_irq_enable(struct intel_engine_cs *engine); +void gen6_irq_disable(struct intel_engine_cs *engine); + +void hsw_irq_enable_vecs(struct intel_engine_cs *engine); +void hsw_irq_disable_vecs(struct intel_engine_cs *engine); + +#endif /* __GEN6_ENGINE_CS_H__ */ diff --git a/drivers/gpu/drm/i915/gt/intel_context_sseu.c b/drivers/gpu/drm/i915/gt/intel_context_sseu.c index 487299cb91f2..27ae48049239 100644 --- a/drivers/gpu/drm/i915/gt/intel_context_sseu.c +++ b/drivers/gpu/drm/i915/gt/intel_context_sseu.c @@ -30,7 +30,7 @@ static int gen8_emit_rpcs_config(struct i915_request *rq, *cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT; *cs++ = lower_32_bits(offset); *cs++ = upper_32_bits(offset); - *cs++ = intel_sseu_make_rpcs(rq->i915, &sseu); + *cs++ = intel_sseu_make_rpcs(rq->engine->i915, &sseu); intel_ring_advance(rq, cs); diff --git a/drivers/gpu/drm/i915/gt/intel_engine.h b/drivers/gpu/drm/i915/gt/intel_engine.h index 9bf6d4989968..a9249a23903a 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine.h +++ b/drivers/gpu/drm/i915/gt/intel_engine.h @@ -187,7 +187,6 @@ intel_write_status_page(struct intel_engine_cs *engine, int reg, u32 value) #define I915_GEM_HWS_SEQNO 0x40 #define I915_GEM_HWS_SEQNO_ADDR (I915_GEM_HWS_SEQNO * sizeof(u32)) #define I915_GEM_HWS_SCRATCH 0x80 -#define I915_GEM_HWS_SCRATCH_ADDR (I915_GEM_HWS_SCRATCH * sizeof(u32)) #define I915_HWS_CSB_BUF0_INDEX 0x10 #define I915_HWS_CSB_WRITE_INDEX 0x1f @@ -335,7 +334,8 @@ void intel_engine_dump(struct intel_engine_cs *engine, struct drm_printer *m, const char *header, ...); -ktime_t intel_engine_get_busy_time(struct intel_engine_cs *engine); +ktime_t intel_engine_get_busy_time(struct intel_engine_cs *engine, + ktime_t *now); struct i915_request * intel_engine_find_active_request(struct intel_engine_cs *engine); diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c index 8691eb61e185..7bf2f76212f0 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c +++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c @@ -414,12 +414,12 @@ void intel_engines_release(struct intel_gt *gt) /* Decouple the backend; but keep the layout for late GPU resets */ for_each_engine(engine, gt, id) { - intel_wakeref_wait_for_idle(&engine->wakeref); - GEM_BUG_ON(intel_engine_pm_is_awake(engine)); - if (!engine->release) continue; + intel_wakeref_wait_for_idle(&engine->wakeref); + GEM_BUG_ON(intel_engine_pm_is_awake(engine)); + engine->release(engine); engine->release = NULL; @@ -661,7 +661,6 @@ static int measure_breadcrumb_dw(struct intel_context *ce) if (!frame) return -ENOMEM; - frame->rq.i915 = engine->i915; frame->rq.engine = engine; frame->rq.context = ce; rcu_assign_pointer(frame->rq.timeline, ce->timeline); @@ -1095,19 +1094,21 @@ void intel_engine_flush_submission(struct intel_engine_cs *engine) { struct tasklet_struct *t = &engine->execlists.tasklet; - if (__tasklet_is_scheduled(t)) { - local_bh_disable(); - if (tasklet_trylock(t)) { - /* Must wait for any GPU reset in progress. */ - if (__tasklet_is_enabled(t)) - t->func(t->data); - tasklet_unlock(t); - } - local_bh_enable(); - } + if (!t->func) + return; - /* Otherwise flush the tasklet if it was running on another cpu */ - tasklet_unlock_wait(t); + /* Synchronise and wait for the tasklet on another CPU */ + tasklet_kill(t); + + /* Having cancelled the tasklet, ensure that is run */ + local_bh_disable(); + if (tasklet_trylock(t)) { + /* Must wait for any GPU reset in progress. */ + if (__tasklet_is_enabled(t)) + t->func(t->data); + tasklet_unlock(t); + } + local_bh_enable(); } /** @@ -1194,8 +1195,7 @@ bool intel_engine_can_store_dword(struct intel_engine_cs *engine) } } -static int print_sched_attr(struct drm_i915_private *i915, - const struct i915_sched_attr *attr, +static int print_sched_attr(const struct i915_sched_attr *attr, char *buf, int x, int len) { if (attr->priority == I915_PRIORITY_INVALID) @@ -1215,7 +1215,7 @@ static void print_request(struct drm_printer *m, char buf[80] = ""; int x = 0; - x = print_sched_attr(rq->i915, &rq->sched.attr, buf, x, sizeof(buf)); + x = print_sched_attr(&rq->sched.attr, buf, x, sizeof(buf)); drm_printf(m, "%s %llx:%llx%s%s %s @ %dms: %s\n", prefix, @@ -1423,9 +1423,11 @@ static void intel_engine_print_registers(struct intel_engine_cs *engine, int len; len = scnprintf(hdr, sizeof(hdr), - "\t\tActive[%d]: ccid:%08x, ", + "\t\tActive[%d]: ccid:%08x%s%s, ", (int)(port - execlists->active), - rq->context->lrc.ccid); + rq->context->lrc.ccid, + intel_context_is_closed(rq->context) ? "!" : "", + intel_context_is_banned(rq->context) ? "*" : ""); len += print_ring(hdr + len, sizeof(hdr) - len, rq); scnprintf(hdr + len, sizeof(hdr) - len, "rq: "); print_request(m, rq, hdr); @@ -1435,9 +1437,11 @@ static void intel_engine_print_registers(struct intel_engine_cs *engine, int len; len = scnprintf(hdr, sizeof(hdr), - "\t\tPending[%d]: ccid:%08x, ", + "\t\tPending[%d]: ccid:%08x%s%s, ", (int)(port - execlists->pending), - rq->context->lrc.ccid); + rq->context->lrc.ccid, + intel_context_is_closed(rq->context) ? "!" : "", + intel_context_is_banned(rq->context) ? "*" : ""); len += print_ring(hdr + len, sizeof(hdr) - len, rq); scnprintf(hdr + len, sizeof(hdr) - len, "rq: "); print_request(m, rq, hdr); @@ -1506,6 +1510,7 @@ void intel_engine_dump(struct intel_engine_cs *engine, struct i915_request *rq; intel_wakeref_t wakeref; unsigned long flags; + ktime_t dummy; if (header) { va_list ap; @@ -1523,6 +1528,12 @@ void intel_engine_dump(struct intel_engine_cs *engine, yesno(!llist_empty(&engine->barrier_tasks))); drm_printf(m, "\tLatency: %luus\n", ewma__engine_latency_read(&engine->latency)); + if (intel_engine_supports_stats(engine)) + drm_printf(m, "\tRuntime: %llums\n", + ktime_to_ms(intel_engine_get_busy_time(engine, + &dummy))); + drm_printf(m, "\tForcewake: %x domains, %d active\n", + engine->fw_domain, atomic_read(&engine->fw_active)); rcu_read_lock(); rq = READ_ONCE(engine->heartbeat.systole); @@ -1589,7 +1600,8 @@ void intel_engine_dump(struct intel_engine_cs *engine, intel_engine_print_breadcrumbs(engine, m); } -static ktime_t __intel_engine_get_busy_time(struct intel_engine_cs *engine) +static ktime_t __intel_engine_get_busy_time(struct intel_engine_cs *engine, + ktime_t *now) { ktime_t total = engine->stats.total; @@ -1597,9 +1609,9 @@ static ktime_t __intel_engine_get_busy_time(struct intel_engine_cs *engine) * If the engine is executing something at the moment * add it to the total. */ + *now = ktime_get(); if (atomic_read(&engine->stats.active)) - total = ktime_add(total, - ktime_sub(ktime_get(), engine->stats.start)); + total = ktime_add(total, ktime_sub(*now, engine->stats.start)); return total; } @@ -1607,17 +1619,18 @@ static ktime_t __intel_engine_get_busy_time(struct intel_engine_cs *engine) /** * intel_engine_get_busy_time() - Return current accumulated engine busyness * @engine: engine to report on + * @now: monotonic timestamp of sampling * * Returns accumulated time @engine was busy since engine stats were enabled. */ -ktime_t intel_engine_get_busy_time(struct intel_engine_cs *engine) +ktime_t intel_engine_get_busy_time(struct intel_engine_cs *engine, ktime_t *now) { unsigned int seq; ktime_t total; do { seq = read_seqbegin(&engine->stats.lock); - total = __intel_engine_get_busy_time(engine); + total = __intel_engine_get_busy_time(engine, now); } while (read_seqretry(&engine->stats.lock, seq)); return total; diff --git a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c index 5136c8bf112d..8ffdf676c0a0 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c +++ b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c @@ -4,6 +4,7 @@ * Copyright © 2019 Intel Corporation */ +#include "i915_drv.h" #include "i915_request.h" #include "intel_context.h" @@ -31,7 +32,7 @@ static bool next_heartbeat(struct intel_engine_cs *engine) delay = msecs_to_jiffies_timeout(delay); if (delay >= HZ) delay = round_jiffies_up_relative(delay); - mod_delayed_work(system_wq, &engine->heartbeat.work, delay); + mod_delayed_work(system_highpri_wq, &engine->heartbeat.work, delay); return true; } @@ -48,8 +49,10 @@ static void show_heartbeat(const struct i915_request *rq, struct drm_printer p = drm_debug_printer("heartbeat"); intel_engine_dump(engine, &p, - "%s heartbeat {prio:%d} not ticking\n", + "%s heartbeat {seqno:%llx:%lld, prio:%d} not ticking\n", engine->name, + rq->fence.context, + rq->fence.seqno, rq->sched.attr.priority); } @@ -62,6 +65,10 @@ static void heartbeat(struct work_struct *wrk) container_of(wrk, typeof(*engine), heartbeat.work.work); struct intel_context *ce = engine->kernel_context; struct i915_request *rq; + unsigned long serial; + + /* Just in case everything has gone horribly wrong, give it a kick */ + intel_engine_flush_submission(engine); rq = engine->heartbeat.systole; if (rq && i915_request_completed(rq)) { @@ -76,8 +83,19 @@ static void heartbeat(struct work_struct *wrk) goto out; if (engine->heartbeat.systole) { - if (engine->schedule && - rq->sched.attr.priority < I915_PRIORITY_BARRIER) { + if (!i915_sw_fence_signaled(&rq->submit)) { + /* + * Not yet submitted, system is stalled. + * + * This more often happens for ring submission, + * where all contexts are funnelled into a common + * ringbuffer. If one context is blocked on an + * external fence, not only is it not submitted, + * but all other contexts, including the kernel + * context are stuck waiting for the signal. + */ + } else if (engine->schedule && + rq->sched.attr.priority < I915_PRIORITY_BARRIER) { /* * Gradually raise the priority of the heartbeat to * give high priority work [which presumably desires @@ -105,10 +123,19 @@ static void heartbeat(struct work_struct *wrk) goto out; } - if (engine->wakeref_serial == engine->serial) + serial = READ_ONCE(engine->serial); + if (engine->wakeref_serial == serial) goto out; - mutex_lock(&ce->timeline->mutex); + if (!mutex_trylock(&ce->timeline->mutex)) { + /* Unable to lock the kernel timeline, is the engine stuck? */ + if (xchg(&engine->heartbeat.blocked, serial) == serial) + intel_gt_handle_error(engine->gt, engine->mask, + I915_ERROR_CAPTURE, + "no heartbeat on %s", + engine->name); + goto out; + } intel_context_enter(ce); rq = __i915_request_create(ce, GFP_NOWAIT | __GFP_NOWARN); @@ -117,7 +144,7 @@ static void heartbeat(struct work_struct *wrk) goto unlock; idle_pulse(engine, rq); - if (i915_modparams.enable_hangcheck) + if (engine->i915->params.enable_hangcheck) engine->heartbeat.systole = i915_request_get(rq); __i915_request_commit(rq); diff --git a/drivers/gpu/drm/i915/gt/intel_engine_types.h b/drivers/gpu/drm/i915/gt/intel_engine_types.h index 2b6cdf47d428..490af81bd6f3 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_types.h +++ b/drivers/gpu/drm/i915/gt/intel_engine_types.h @@ -24,6 +24,7 @@ #include "i915_selftest.h" #include "intel_sseu.h" #include "intel_timeline_types.h" +#include "intel_uncore.h" #include "intel_wakeref.h" #include "intel_workarounds_types.h" @@ -313,6 +314,16 @@ struct intel_engine_cs { u32 context_size; u32 mmio_base; + /* + * Some w/a require forcewake to be held (which prevents RC6) while + * a particular engine is active. If so, we set fw_domain to which + * domains need to be held for the duration of request activity, + * and 0 if none. We try to limit the duration of the hold as much + * as possible. + */ + enum forcewake_domains fw_domain; + atomic_t fw_active; + unsigned long context_tag; struct rb_node uabi_node; @@ -337,6 +348,7 @@ struct intel_engine_cs { struct { struct delayed_work work; struct i915_request *systole; + unsigned long blocked; } heartbeat; unsigned long serial; diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt.c b/drivers/gpu/drm/i915/gt/intel_ggtt.c index 66165b10256e..323c328d444a 100644 --- a/drivers/gpu/drm/i915/gt/intel_ggtt.c +++ b/drivers/gpu/drm/i915/gt/intel_ggtt.c @@ -108,13 +108,32 @@ static bool needs_idle_maps(struct drm_i915_private *i915) void i915_ggtt_suspend(struct i915_ggtt *ggtt) { - struct i915_vma *vma; + struct i915_vma *vma, *vn; + int open; + + mutex_lock(&ggtt->vm.mutex); + + /* Skip rewriting PTE on VMA unbind. */ + open = atomic_xchg(&ggtt->vm.open, 0); - list_for_each_entry(vma, &ggtt->vm.bound_list, vm_link) + list_for_each_entry_safe(vma, vn, &ggtt->vm.bound_list, vm_link) { + GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); i915_vma_wait_for_bind(vma); + if (i915_vma_is_pinned(vma)) + continue; + + if (!i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND)) { + __i915_vma_evict(vma); + drm_mm_remove_node(&vma->node); + } + } + ggtt->vm.clear_range(&ggtt->vm, 0, ggtt->vm.total); ggtt->invalidate(ggtt); + atomic_set(&ggtt->vm.open, open); + + mutex_unlock(&ggtt->vm.mutex); intel_gt_check_and_clear_faults(ggtt->vm.gt); } @@ -424,22 +443,17 @@ static int ggtt_bind_vma(struct i915_vma *vma, struct drm_i915_gem_object *obj = vma->obj; u32 pte_flags; + if (i915_vma_is_bound(vma, ~flags & I915_VMA_BIND_MASK)) + return 0; + /* Applicable to VLV (gen8+ do not support RO in the GGTT) */ pte_flags = 0; if (i915_gem_object_is_readonly(obj)) pte_flags |= PTE_READ_ONLY; vma->vm->insert_entries(vma->vm, vma, cache_level, pte_flags); - vma->page_sizes.gtt = I915_GTT_PAGE_SIZE; - /* - * Without aliasing PPGTT there's no difference between - * GLOBAL/LOCAL_BIND, it's all the same ptes. Hence unconditionally - * upgrade to both bound if we bind either to avoid double-binding. - */ - atomic_or(I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND, &vma->flags); - return 0; } @@ -1166,6 +1180,11 @@ void i915_ggtt_disable_guc(struct i915_ggtt *ggtt) ggtt->invalidate(ggtt); } +static unsigned int clear_bind(struct i915_vma *vma) +{ + return atomic_fetch_and(~I915_VMA_BIND_MASK, &vma->flags); +} + void i915_ggtt_resume(struct i915_ggtt *ggtt) { struct i915_vma *vma; @@ -1183,14 +1202,11 @@ void i915_ggtt_resume(struct i915_ggtt *ggtt) /* clflush objects bound into the GGTT and rebind them. */ list_for_each_entry(vma, &ggtt->vm.bound_list, vm_link) { struct drm_i915_gem_object *obj = vma->obj; + unsigned int was_bound = clear_bind(vma); - if (!i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND)) - continue; - - clear_bit(I915_VMA_GLOBAL_BIND_BIT, __i915_vma_flags(vma)); WARN_ON(i915_vma_bind(vma, obj ? obj->cache_level : 0, - PIN_GLOBAL, NULL)); + was_bound, NULL)); if (obj) { /* only used during resume => exclusive access */ flush |= fetch_and_zero(&obj->write_domain); obj->read_domains |= I915_GEM_DOMAIN_GTT; diff --git a/drivers/gpu/drm/i915/gt/intel_gt.c b/drivers/gpu/drm/i915/gt/intel_gt.c index f069551e412f..ebc29b6ee86c 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt.c +++ b/drivers/gpu/drm/i915/gt/intel_gt.c @@ -616,6 +616,11 @@ void intel_gt_driver_unregister(struct intel_gt *gt) void intel_gt_driver_release(struct intel_gt *gt) { struct i915_address_space *vm; + intel_wakeref_t wakeref; + + /* Scrub all HW state upon release */ + with_intel_runtime_pm(gt->uncore->rpm, wakeref) + __intel_gt_reset(gt, ALL_ENGINES); vm = fetch_and_zero(>->vm); if (vm) /* FIXME being called twice on error paths :( */ diff --git a/drivers/gpu/drm/i915/gt/intel_gt_buffer_pool.c b/drivers/gpu/drm/i915/gt/intel_gt_buffer_pool.c index 1495054a4305..418ae184cecf 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_buffer_pool.c +++ b/drivers/gpu/drm/i915/gt/intel_gt_buffer_pool.c @@ -212,8 +212,9 @@ void intel_gt_flush_buffer_pool(struct intel_gt *gt) { struct intel_gt_buffer_pool *pool = >->buffer_pool; - if (cancel_delayed_work_sync(&pool->work)) + do { pool_free_imm(pool); + } while (cancel_delayed_work_sync(&pool->work)); } void intel_gt_fini_buffer_pool(struct intel_gt *gt) diff --git a/drivers/gpu/drm/i915/gt/intel_gt_pm.c b/drivers/gpu/drm/i915/gt/intel_gt_pm.c index 6bdb434a442d..f1d5333f9456 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_pm.c +++ b/drivers/gpu/drm/i915/gt/intel_gt_pm.c @@ -214,8 +214,8 @@ int intel_gt_resume(struct intel_gt *gt) /* Only when the HW is re-initialised, can we replay the requests */ err = intel_gt_init_hw(gt); if (err) { - drm_err(>->i915->drm, - "Failed to initialize GPU, declaring it wedged!\n"); + i915_probe_error(gt->i915, + "Failed to initialize GPU, declaring it wedged!\n"); goto err_wedged; } diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c index 7c3d8ef4a47c..e866b8d721ed 100644 --- a/drivers/gpu/drm/i915/gt/intel_lrc.c +++ b/drivers/gpu/drm/i915/gt/intel_lrc.c @@ -446,6 +446,9 @@ static int queue_prio(const struct intel_engine_execlists *execlists) * we have to flip the index value to become priority. */ p = to_priolist(rb); + if (!I915_USER_PRIORITY_SHIFT) + return p->priority; + return ((p->priority + 1) << I915_USER_PRIORITY_SHIFT) - ffs(p->used); } @@ -1377,6 +1380,8 @@ __execlists_schedule_in(struct i915_request *rq) ce->lrc.ccid |= engine->execlists.ccid; __intel_gt_pm_get(engine->gt); + if (engine->fw_domain && !atomic_fetch_inc(&engine->fw_active)) + intel_uncore_forcewake_get(engine->uncore, engine->fw_domain); execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_IN); intel_engine_context_in(engine); @@ -1409,8 +1414,8 @@ static void kick_siblings(struct i915_request *rq, struct intel_context *ce) struct virtual_engine *ve = container_of(ce, typeof(*ve), context); struct i915_request *next = READ_ONCE(ve->request); - if (next && next->execution_mask & ~rq->execution_mask) - tasklet_schedule(&ve->base.execlists.tasklet); + if (next == rq || (next && next->execution_mask & ~rq->execution_mask)) + tasklet_hi_schedule(&ve->base.execlists.tasklet); } static inline void @@ -1445,6 +1450,8 @@ __execlists_schedule_out(struct i915_request *rq, intel_context_update_runtime(ce); intel_engine_context_out(engine); execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_OUT); + if (engine->fw_domain && !atomic_dec_return(&engine->fw_active)) + intel_uncore_forcewake_put(engine->uncore, engine->fw_domain); intel_gt_pm_put_async(engine->gt); /* @@ -1636,9 +1643,9 @@ assert_pending_valid(const struct intel_engine_execlists *execlists, ccid = ce->lrc.ccid; /* - * Sentinels are supposed to be lonely so they flush the - * current exection off the HW. Check that they are the - * only request in the pending submission. + * Sentinels are supposed to be the last request so they flush + * the current execution off the HW. Check that they are the only + * request in the pending submission. */ if (sentinel) { GEM_TRACE_ERR("%s: context:%llx after sentinel in pending[%zd]\n", @@ -1647,15 +1654,7 @@ assert_pending_valid(const struct intel_engine_execlists *execlists, port - execlists->pending); return false; } - sentinel = i915_request_has_sentinel(rq); - if (sentinel && port != execlists->pending) { - GEM_TRACE_ERR("%s: sentinel context:%llx not in prime position[%zd]\n", - engine->name, - ce->timeline->fence_context, - port - execlists->pending); - return false; - } /* Hold tightly onto the lock to prevent concurrent retires! */ if (!spin_trylock_irqsave(&rq->lock, flags)) @@ -1967,7 +1966,7 @@ static int switch_prio(struct intel_engine_cs *engine, const struct i915_request *rq) { if (list_is_last(&rq->sched.link, &engine->active.requests)) - return INT_MIN; + return engine->execlists.queue_priority_hint; return rq_prio(list_next_entry(rq, sched.link)); } @@ -2445,6 +2444,7 @@ done: set_preempt_timeout(engine, *active); execlists_submit_ports(engine); } else { + start_timeslice(engine, execlists->queue_priority_hint); skip_submit: ring_set_paused(engine, 0); } @@ -3170,13 +3170,6 @@ static void __submit_queue_imm(struct intel_engine_cs *engine) if (reset_in_progress(execlists)) return; /* defer until we restart the engine following reset */ - /* Hopefully we clear execlists->pending[] to let us through */ - if (READ_ONCE(execlists->pending[0]) && - tasklet_trylock(&execlists->tasklet)) { - process_csb(engine); - tasklet_unlock(&execlists->tasklet); - } - __execlists_submission_tasklet(engine); } @@ -3199,11 +3192,25 @@ static bool ancestor_on_hold(const struct intel_engine_cs *engine, return !list_empty(&engine->active.hold) && hold_request(rq); } +static void flush_csb(struct intel_engine_cs *engine) +{ + struct intel_engine_execlists *el = &engine->execlists; + + if (READ_ONCE(el->pending[0]) && tasklet_trylock(&el->tasklet)) { + if (!reset_in_progress(el)) + process_csb(engine); + tasklet_unlock(&el->tasklet); + } +} + static void execlists_submit_request(struct i915_request *request) { struct intel_engine_cs *engine = request->engine; unsigned long flags; + /* Hopefully we clear execlists->pending[] to let us through */ + flush_csb(engine); + /* Will be called from irq-context when using foreign fences. */ spin_lock_irqsave(&engine->active.lock, flags); @@ -3536,7 +3543,7 @@ static int emit_pdps(struct i915_request *rq) int err, i; u32 *cs; - GEM_BUG_ON(intel_vgpu_active(rq->i915)); + GEM_BUG_ON(intel_vgpu_active(rq->engine->i915)); /* * Beware ye of the dragons, this sequence is magic! @@ -4515,11 +4522,11 @@ static int gen8_emit_flush_render(struct i915_request *request, * On GEN9: before VF_CACHE_INVALIDATE we need to emit a NULL * pipe control. */ - if (IS_GEN(request->i915, 9)) + if (IS_GEN(request->engine->i915, 9)) vf_flush_wa = true; /* WaForGAMHang:kbl */ - if (IS_KBL_REVID(request->i915, 0, KBL_REVID_B0)) + if (IS_KBL_REVID(request->engine->i915, 0, KBL_REVID_B0)) dc_flush_wa = true; } @@ -5598,7 +5605,7 @@ static void virtual_submit_request(struct i915_request *rq) GEM_BUG_ON(!list_empty(virtual_queue(ve))); list_move_tail(&rq->sched.link, virtual_queue(ve)); - tasklet_schedule(&ve->base.execlists.tasklet); + tasklet_hi_schedule(&ve->base.execlists.tasklet); } spin_unlock_irqrestore(&ve->base.active.lock, flags); diff --git a/drivers/gpu/drm/i915/gt/intel_renderstate.c b/drivers/gpu/drm/i915/gt/intel_renderstate.c index f59e7875cc5e..6db23389e427 100644 --- a/drivers/gpu/drm/i915/gt/intel_renderstate.c +++ b/drivers/gpu/drm/i915/gt/intel_renderstate.c @@ -61,7 +61,7 @@ render_state_get_rodata(const struct intel_engine_cs *engine) #define OUT_BATCH(batch, i, val) \ do { \ if ((i) >= PAGE_SIZE / sizeof(u32)) \ - goto err; \ + goto out; \ (batch)[(i)++] = (val); \ } while(0) @@ -70,15 +70,12 @@ static int render_state_setup(struct intel_renderstate *so, { const struct intel_renderstate_rodata *rodata = so->rodata; unsigned int i = 0, reloc_index = 0; - unsigned int needs_clflush; + int ret = -EINVAL; u32 *d; - int ret; - ret = i915_gem_object_prepare_write(so->vma->obj, &needs_clflush); - if (ret) - return ret; - - d = kmap_atomic(i915_gem_object_get_dirty_page(so->vma->obj, 0)); + d = i915_gem_object_pin_map(so->vma->obj, I915_MAP_WB); + if (IS_ERR(d)) + return PTR_ERR(d); while (i < rodata->batch_items) { u32 s = rodata->batch[i]; @@ -89,7 +86,7 @@ static int render_state_setup(struct intel_renderstate *so, if (HAS_64BIT_RELOC(i915)) { if (i + 1 >= rodata->batch_items || rodata->batch[i + 1] != 0) - goto err; + goto out; d[i++] = s; s = upper_32_bits(r); @@ -103,7 +100,7 @@ static int render_state_setup(struct intel_renderstate *so, if (rodata->reloc[reloc_index] != -1) { drm_err(&i915->drm, "only %d relocs resolved\n", reloc_index); - goto err; + goto out; } so->batch_offset = i915_ggtt_offset(so->vma); @@ -150,19 +147,11 @@ static int render_state_setup(struct intel_renderstate *so, */ so->aux_size = ALIGN(so->aux_size, 8); - if (needs_clflush) - drm_clflush_virt_range(d, i * sizeof(u32)); - kunmap_atomic(d); - ret = 0; out: - i915_gem_object_finish_access(so->vma->obj); + __i915_gem_object_flush_map(so->vma->obj, 0, i * sizeof(u32)); + i915_gem_object_unpin_map(so->vma->obj); return ret; - -err: - kunmap_atomic(d); - ret = -EINVAL; - goto out; } #undef OUT_BATCH diff --git a/drivers/gpu/drm/i915/gt/intel_reset.c b/drivers/gpu/drm/i915/gt/intel_reset.c index 39070b514e65..0156f1f5c736 100644 --- a/drivers/gpu/drm/i915/gt/intel_reset.c +++ b/drivers/gpu/drm/i915/gt/intel_reset.c @@ -638,7 +638,7 @@ int __intel_gt_reset(struct intel_gt *gt, intel_engine_mask_t engine_mask) bool intel_has_gpu_reset(const struct intel_gt *gt) { - if (!i915_modparams.reset) + if (!gt->i915->params.reset) return NULL; return intel_get_gpu_reset(gt); @@ -646,7 +646,7 @@ bool intel_has_gpu_reset(const struct intel_gt *gt) bool intel_has_reset_engine(const struct intel_gt *gt) { - if (i915_modparams.reset < 2) + if (gt->i915->params.reset < 2) return false; return INTEL_INFO(gt->i915)->has_reset_engine; @@ -1038,7 +1038,7 @@ void intel_gt_reset(struct intel_gt *gt, awake = reset_prepare(gt); if (!intel_has_gpu_reset(gt)) { - if (i915_modparams.reset) + if (gt->i915->params.reset) drm_err(>->i915->drm, "GPU reset not supported\n"); else drm_dbg(>->i915->drm, "GPU reset disabled\n"); diff --git a/drivers/gpu/drm/i915/gt/intel_ring_submission.c b/drivers/gpu/drm/i915/gt/intel_ring_submission.c index ca7286e58409..68a08486fc87 100644 --- a/drivers/gpu/drm/i915/gt/intel_ring_submission.c +++ b/drivers/gpu/drm/i915/gt/intel_ring_submission.c @@ -27,21 +27,15 @@ * */ -#include <linux/log2.h> - -#include "gem/i915_gem_context.h" - +#include "gen2_engine_cs.h" +#include "gen6_engine_cs.h" #include "gen6_ppgtt.h" #include "gen7_renderclear.h" #include "i915_drv.h" -#include "i915_trace.h" #include "intel_context.h" #include "intel_gt.h" -#include "intel_gt_irq.h" -#include "intel_gt_pm_irq.h" #include "intel_reset.h" #include "intel_ring.h" -#include "intel_workarounds.h" #include "shmem_utils.h" /* Rough estimate of the typical request size, performing a flush, @@ -49,436 +43,6 @@ */ #define LEGACY_REQUEST_SIZE 200 -static int -gen2_render_ring_flush(struct i915_request *rq, u32 mode) -{ - unsigned int num_store_dw; - u32 cmd, *cs; - - cmd = MI_FLUSH; - num_store_dw = 0; - if (mode & EMIT_INVALIDATE) - cmd |= MI_READ_FLUSH; - if (mode & EMIT_FLUSH) - num_store_dw = 4; - - cs = intel_ring_begin(rq, 2 + 3 * num_store_dw); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - *cs++ = cmd; - while (num_store_dw--) { - *cs++ = MI_STORE_DWORD_IMM | MI_MEM_VIRTUAL; - *cs++ = intel_gt_scratch_offset(rq->engine->gt, - INTEL_GT_SCRATCH_FIELD_DEFAULT); - *cs++ = 0; - } - *cs++ = MI_FLUSH | MI_NO_WRITE_FLUSH; - - intel_ring_advance(rq, cs); - - return 0; -} - -static int -gen4_render_ring_flush(struct i915_request *rq, u32 mode) -{ - u32 cmd, *cs; - int i; - - /* - * read/write caches: - * - * I915_GEM_DOMAIN_RENDER is always invalidated, but is - * only flushed if MI_NO_WRITE_FLUSH is unset. On 965, it is - * also flushed at 2d versus 3d pipeline switches. - * - * read-only caches: - * - * I915_GEM_DOMAIN_SAMPLER is flushed on pre-965 if - * MI_READ_FLUSH is set, and is always flushed on 965. - * - * I915_GEM_DOMAIN_COMMAND may not exist? - * - * I915_GEM_DOMAIN_INSTRUCTION, which exists on 965, is - * invalidated when MI_EXE_FLUSH is set. - * - * I915_GEM_DOMAIN_VERTEX, which exists on 965, is - * invalidated with every MI_FLUSH. - * - * TLBs: - * - * On 965, TLBs associated with I915_GEM_DOMAIN_COMMAND - * and I915_GEM_DOMAIN_CPU in are invalidated at PTE write and - * I915_GEM_DOMAIN_RENDER and I915_GEM_DOMAIN_SAMPLER - * are flushed at any MI_FLUSH. - */ - - cmd = MI_FLUSH; - if (mode & EMIT_INVALIDATE) { - cmd |= MI_EXE_FLUSH; - if (IS_G4X(rq->i915) || IS_GEN(rq->i915, 5)) - cmd |= MI_INVALIDATE_ISP; - } - - i = 2; - if (mode & EMIT_INVALIDATE) - i += 20; - - cs = intel_ring_begin(rq, i); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - *cs++ = cmd; - - /* - * A random delay to let the CS invalidate take effect? Without this - * delay, the GPU relocation path fails as the CS does not see - * the updated contents. Just as important, if we apply the flushes - * to the EMIT_FLUSH branch (i.e. immediately after the relocation - * write and before the invalidate on the next batch), the relocations - * still fail. This implies that is a delay following invalidation - * that is required to reset the caches as opposed to a delay to - * ensure the memory is written. - */ - if (mode & EMIT_INVALIDATE) { - *cs++ = GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE; - *cs++ = intel_gt_scratch_offset(rq->engine->gt, - INTEL_GT_SCRATCH_FIELD_DEFAULT) | - PIPE_CONTROL_GLOBAL_GTT; - *cs++ = 0; - *cs++ = 0; - - for (i = 0; i < 12; i++) - *cs++ = MI_FLUSH; - - *cs++ = GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE; - *cs++ = intel_gt_scratch_offset(rq->engine->gt, - INTEL_GT_SCRATCH_FIELD_DEFAULT) | - PIPE_CONTROL_GLOBAL_GTT; - *cs++ = 0; - *cs++ = 0; - } - - *cs++ = cmd; - - intel_ring_advance(rq, cs); - - return 0; -} - -/* - * Emits a PIPE_CONTROL with a non-zero post-sync operation, for - * implementing two workarounds on gen6. From section 1.4.7.1 - * "PIPE_CONTROL" of the Sandy Bridge PRM volume 2 part 1: - * - * [DevSNB-C+{W/A}] Before any depth stall flush (including those - * produced by non-pipelined state commands), software needs to first - * send a PIPE_CONTROL with no bits set except Post-Sync Operation != - * 0. - * - * [Dev-SNB{W/A}]: Before a PIPE_CONTROL with Write Cache Flush Enable - * =1, a PIPE_CONTROL with any non-zero post-sync-op is required. - * - * And the workaround for these two requires this workaround first: - * - * [Dev-SNB{W/A}]: Pipe-control with CS-stall bit set must be sent - * BEFORE the pipe-control with a post-sync op and no write-cache - * flushes. - * - * And this last workaround is tricky because of the requirements on - * that bit. From section 1.4.7.2.3 "Stall" of the Sandy Bridge PRM - * volume 2 part 1: - * - * "1 of the following must also be set: - * - Render Target Cache Flush Enable ([12] of DW1) - * - Depth Cache Flush Enable ([0] of DW1) - * - Stall at Pixel Scoreboard ([1] of DW1) - * - Depth Stall ([13] of DW1) - * - Post-Sync Operation ([13] of DW1) - * - Notify Enable ([8] of DW1)" - * - * The cache flushes require the workaround flush that triggered this - * one, so we can't use it. Depth stall would trigger the same. - * Post-sync nonzero is what triggered this second workaround, so we - * can't use that one either. Notify enable is IRQs, which aren't - * really our business. That leaves only stall at scoreboard. - */ -static int -gen6_emit_post_sync_nonzero_flush(struct i915_request *rq) -{ - u32 scratch_addr = - intel_gt_scratch_offset(rq->engine->gt, - INTEL_GT_SCRATCH_FIELD_RENDER_FLUSH); - u32 *cs; - - cs = intel_ring_begin(rq, 6); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - *cs++ = GFX_OP_PIPE_CONTROL(5); - *cs++ = PIPE_CONTROL_CS_STALL | PIPE_CONTROL_STALL_AT_SCOREBOARD; - *cs++ = scratch_addr | PIPE_CONTROL_GLOBAL_GTT; - *cs++ = 0; /* low dword */ - *cs++ = 0; /* high dword */ - *cs++ = MI_NOOP; - intel_ring_advance(rq, cs); - - cs = intel_ring_begin(rq, 6); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - *cs++ = GFX_OP_PIPE_CONTROL(5); - *cs++ = PIPE_CONTROL_QW_WRITE; - *cs++ = scratch_addr | PIPE_CONTROL_GLOBAL_GTT; - *cs++ = 0; - *cs++ = 0; - *cs++ = MI_NOOP; - intel_ring_advance(rq, cs); - - return 0; -} - -static int -gen6_render_ring_flush(struct i915_request *rq, u32 mode) -{ - u32 scratch_addr = - intel_gt_scratch_offset(rq->engine->gt, - INTEL_GT_SCRATCH_FIELD_RENDER_FLUSH); - u32 *cs, flags = 0; - int ret; - - /* Force SNB workarounds for PIPE_CONTROL flushes */ - ret = gen6_emit_post_sync_nonzero_flush(rq); - if (ret) - return ret; - - /* Just flush everything. Experiments have shown that reducing the - * number of bits based on the write domains has little performance - * impact. - */ - if (mode & EMIT_FLUSH) { - flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; - flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; - /* - * Ensure that any following seqno writes only happen - * when the render cache is indeed flushed. - */ - flags |= PIPE_CONTROL_CS_STALL; - } - if (mode & EMIT_INVALIDATE) { - flags |= PIPE_CONTROL_TLB_INVALIDATE; - flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE; - flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE; - flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE; - flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE; - flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE; - /* - * TLB invalidate requires a post-sync write. - */ - flags |= PIPE_CONTROL_QW_WRITE | PIPE_CONTROL_CS_STALL; - } - - cs = intel_ring_begin(rq, 4); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - *cs++ = GFX_OP_PIPE_CONTROL(4); - *cs++ = flags; - *cs++ = scratch_addr | PIPE_CONTROL_GLOBAL_GTT; - *cs++ = 0; - intel_ring_advance(rq, cs); - - return 0; -} - -static u32 *gen6_rcs_emit_breadcrumb(struct i915_request *rq, u32 *cs) -{ - /* First we do the gen6_emit_post_sync_nonzero_flush w/a */ - *cs++ = GFX_OP_PIPE_CONTROL(4); - *cs++ = PIPE_CONTROL_CS_STALL | PIPE_CONTROL_STALL_AT_SCOREBOARD; - *cs++ = 0; - *cs++ = 0; - - *cs++ = GFX_OP_PIPE_CONTROL(4); - *cs++ = PIPE_CONTROL_QW_WRITE; - *cs++ = intel_gt_scratch_offset(rq->engine->gt, - INTEL_GT_SCRATCH_FIELD_DEFAULT) | - PIPE_CONTROL_GLOBAL_GTT; - *cs++ = 0; - - /* Finally we can flush and with it emit the breadcrumb */ - *cs++ = GFX_OP_PIPE_CONTROL(4); - *cs++ = (PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH | - PIPE_CONTROL_DEPTH_CACHE_FLUSH | - PIPE_CONTROL_DC_FLUSH_ENABLE | - PIPE_CONTROL_QW_WRITE | - PIPE_CONTROL_CS_STALL); - *cs++ = i915_request_active_timeline(rq)->hwsp_offset | - PIPE_CONTROL_GLOBAL_GTT; - *cs++ = rq->fence.seqno; - - *cs++ = MI_USER_INTERRUPT; - *cs++ = MI_NOOP; - - rq->tail = intel_ring_offset(rq, cs); - assert_ring_tail_valid(rq->ring, rq->tail); - - return cs; -} - -static int -gen7_render_ring_cs_stall_wa(struct i915_request *rq) -{ - u32 *cs; - - cs = intel_ring_begin(rq, 4); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - *cs++ = GFX_OP_PIPE_CONTROL(4); - *cs++ = PIPE_CONTROL_CS_STALL | PIPE_CONTROL_STALL_AT_SCOREBOARD; - *cs++ = 0; - *cs++ = 0; - intel_ring_advance(rq, cs); - - return 0; -} - -static int -gen7_render_ring_flush(struct i915_request *rq, u32 mode) -{ - u32 scratch_addr = - intel_gt_scratch_offset(rq->engine->gt, - INTEL_GT_SCRATCH_FIELD_RENDER_FLUSH); - u32 *cs, flags = 0; - - /* - * Ensure that any following seqno writes only happen when the render - * cache is indeed flushed. - * - * Workaround: 4th PIPE_CONTROL command (except the ones with only - * read-cache invalidate bits set) must have the CS_STALL bit set. We - * don't try to be clever and just set it unconditionally. - */ - flags |= PIPE_CONTROL_CS_STALL; - - /* - * CS_STALL suggests at least a post-sync write. - */ - flags |= PIPE_CONTROL_QW_WRITE; - flags |= PIPE_CONTROL_GLOBAL_GTT_IVB; - - /* Just flush everything. Experiments have shown that reducing the - * number of bits based on the write domains has little performance - * impact. - */ - 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 (mode & EMIT_INVALIDATE) { - flags |= PIPE_CONTROL_TLB_INVALIDATE; - flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE; - flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE; - flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE; - flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE; - flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE; - flags |= PIPE_CONTROL_MEDIA_STATE_CLEAR; - - /* Workaround: we must issue a pipe_control with CS-stall bit - * set before a pipe_control command that has the state cache - * invalidate bit set. */ - gen7_render_ring_cs_stall_wa(rq); - } - - cs = intel_ring_begin(rq, 4); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - *cs++ = GFX_OP_PIPE_CONTROL(4); - *cs++ = flags; - *cs++ = scratch_addr; - *cs++ = 0; - intel_ring_advance(rq, cs); - - return 0; -} - -static u32 *gen7_rcs_emit_breadcrumb(struct i915_request *rq, u32 *cs) -{ - *cs++ = GFX_OP_PIPE_CONTROL(4); - *cs++ = (PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH | - PIPE_CONTROL_DEPTH_CACHE_FLUSH | - PIPE_CONTROL_DC_FLUSH_ENABLE | - PIPE_CONTROL_FLUSH_ENABLE | - PIPE_CONTROL_QW_WRITE | - PIPE_CONTROL_GLOBAL_GTT_IVB | - PIPE_CONTROL_CS_STALL); - *cs++ = i915_request_active_timeline(rq)->hwsp_offset; - *cs++ = rq->fence.seqno; - - *cs++ = MI_USER_INTERRUPT; - *cs++ = MI_NOOP; - - rq->tail = intel_ring_offset(rq, cs); - assert_ring_tail_valid(rq->ring, rq->tail); - - return cs; -} - -static u32 *gen6_xcs_emit_breadcrumb(struct i915_request *rq, u32 *cs) -{ - GEM_BUG_ON(i915_request_active_timeline(rq)->hwsp_ggtt != rq->engine->status_page.vma); - GEM_BUG_ON(offset_in_page(i915_request_active_timeline(rq)->hwsp_offset) != I915_GEM_HWS_SEQNO_ADDR); - - *cs++ = MI_FLUSH_DW | MI_FLUSH_DW_OP_STOREDW | MI_FLUSH_DW_STORE_INDEX; - *cs++ = I915_GEM_HWS_SEQNO_ADDR | MI_FLUSH_DW_USE_GTT; - *cs++ = rq->fence.seqno; - - *cs++ = MI_USER_INTERRUPT; - - rq->tail = intel_ring_offset(rq, cs); - assert_ring_tail_valid(rq->ring, rq->tail); - - return cs; -} - -#define GEN7_XCS_WA 32 -static u32 *gen7_xcs_emit_breadcrumb(struct i915_request *rq, u32 *cs) -{ - int i; - - GEM_BUG_ON(i915_request_active_timeline(rq)->hwsp_ggtt != rq->engine->status_page.vma); - GEM_BUG_ON(offset_in_page(i915_request_active_timeline(rq)->hwsp_offset) != I915_GEM_HWS_SEQNO_ADDR); - - *cs++ = MI_FLUSH_DW | MI_INVALIDATE_TLB | - MI_FLUSH_DW_OP_STOREDW | MI_FLUSH_DW_STORE_INDEX; - *cs++ = I915_GEM_HWS_SEQNO_ADDR | MI_FLUSH_DW_USE_GTT; - *cs++ = rq->fence.seqno; - - for (i = 0; i < GEN7_XCS_WA; i++) { - *cs++ = MI_STORE_DWORD_INDEX; - *cs++ = I915_GEM_HWS_SEQNO_ADDR; - *cs++ = rq->fence.seqno; - } - - *cs++ = MI_FLUSH_DW; - *cs++ = 0; - *cs++ = 0; - - *cs++ = MI_USER_INTERRUPT; - *cs++ = MI_NOOP; - - rq->tail = intel_ring_offset(rq, cs); - assert_ring_tail_valid(rq->ring, rq->tail); - - return cs; -} -#undef GEN7_XCS_WA - static void set_hwstam(struct intel_engine_cs *engine, u32 mask) { /* @@ -865,32 +429,6 @@ static void reset_finish(struct intel_engine_cs *engine) { } -static int rcs_resume(struct intel_engine_cs *engine) -{ - struct drm_i915_private *i915 = engine->i915; - struct intel_uncore *uncore = engine->uncore; - - /* - * Disable CONSTANT_BUFFER before it is loaded from the context - * image. For as it is loaded, it is executed and the stored - * address may no longer be valid, leading to a GPU hang. - * - * This imposes the requirement that userspace reload their - * CONSTANT_BUFFER on every batch, fortunately a requirement - * they are already accustomed to from before contexts were - * enabled. - */ - if (IS_GEN(i915, 4)) - intel_uncore_write(uncore, ECOSKPD, - _MASKED_BIT_ENABLE(ECO_CONSTANT_BUFFER_SR_DISABLE)); - - if (IS_GEN_RANGE(i915, 6, 7)) - intel_uncore_write(uncore, INSTPM, - _MASKED_BIT_ENABLE(INSTPM_FORCE_ORDERING)); - - return xcs_resume(engine); -} - static void reset_cancel(struct intel_engine_cs *engine) { struct i915_request *request; @@ -918,255 +456,6 @@ static void i9xx_submit_request(struct i915_request *request) intel_ring_set_tail(request->ring, request->tail)); } -static u32 *i9xx_emit_breadcrumb(struct i915_request *rq, u32 *cs) -{ - GEM_BUG_ON(i915_request_active_timeline(rq)->hwsp_ggtt != rq->engine->status_page.vma); - GEM_BUG_ON(offset_in_page(i915_request_active_timeline(rq)->hwsp_offset) != I915_GEM_HWS_SEQNO_ADDR); - - *cs++ = MI_FLUSH; - - *cs++ = MI_STORE_DWORD_INDEX; - *cs++ = I915_GEM_HWS_SEQNO_ADDR; - *cs++ = rq->fence.seqno; - - *cs++ = MI_USER_INTERRUPT; - *cs++ = MI_NOOP; - - rq->tail = intel_ring_offset(rq, cs); - assert_ring_tail_valid(rq->ring, rq->tail); - - return cs; -} - -#define GEN5_WA_STORES 8 /* must be at least 1! */ -static u32 *gen5_emit_breadcrumb(struct i915_request *rq, u32 *cs) -{ - int i; - - GEM_BUG_ON(i915_request_active_timeline(rq)->hwsp_ggtt != rq->engine->status_page.vma); - GEM_BUG_ON(offset_in_page(i915_request_active_timeline(rq)->hwsp_offset) != I915_GEM_HWS_SEQNO_ADDR); - - *cs++ = MI_FLUSH; - - BUILD_BUG_ON(GEN5_WA_STORES < 1); - for (i = 0; i < GEN5_WA_STORES; i++) { - *cs++ = MI_STORE_DWORD_INDEX; - *cs++ = I915_GEM_HWS_SEQNO_ADDR; - *cs++ = rq->fence.seqno; - } - - *cs++ = MI_USER_INTERRUPT; - - rq->tail = intel_ring_offset(rq, cs); - assert_ring_tail_valid(rq->ring, rq->tail); - - return cs; -} -#undef GEN5_WA_STORES - -static void -gen5_irq_enable(struct intel_engine_cs *engine) -{ - gen5_gt_enable_irq(engine->gt, engine->irq_enable_mask); -} - -static void -gen5_irq_disable(struct intel_engine_cs *engine) -{ - gen5_gt_disable_irq(engine->gt, engine->irq_enable_mask); -} - -static void -i9xx_irq_enable(struct intel_engine_cs *engine) -{ - engine->i915->irq_mask &= ~engine->irq_enable_mask; - intel_uncore_write(engine->uncore, GEN2_IMR, engine->i915->irq_mask); - intel_uncore_posting_read_fw(engine->uncore, GEN2_IMR); -} - -static void -i9xx_irq_disable(struct intel_engine_cs *engine) -{ - engine->i915->irq_mask |= engine->irq_enable_mask; - intel_uncore_write(engine->uncore, GEN2_IMR, engine->i915->irq_mask); -} - -static void -i8xx_irq_enable(struct intel_engine_cs *engine) -{ - struct drm_i915_private *i915 = engine->i915; - - i915->irq_mask &= ~engine->irq_enable_mask; - intel_uncore_write16(&i915->uncore, GEN2_IMR, i915->irq_mask); - ENGINE_POSTING_READ16(engine, RING_IMR); -} - -static void -i8xx_irq_disable(struct intel_engine_cs *engine) -{ - struct drm_i915_private *i915 = engine->i915; - - i915->irq_mask |= engine->irq_enable_mask; - intel_uncore_write16(&i915->uncore, GEN2_IMR, i915->irq_mask); -} - -static int -bsd_ring_flush(struct i915_request *rq, u32 mode) -{ - u32 *cs; - - cs = intel_ring_begin(rq, 2); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - *cs++ = MI_FLUSH; - *cs++ = MI_NOOP; - intel_ring_advance(rq, cs); - return 0; -} - -static void -gen6_irq_enable(struct intel_engine_cs *engine) -{ - ENGINE_WRITE(engine, RING_IMR, - ~(engine->irq_enable_mask | engine->irq_keep_mask)); - - /* Flush/delay to ensure the RING_IMR is active before the GT IMR */ - ENGINE_POSTING_READ(engine, RING_IMR); - - gen5_gt_enable_irq(engine->gt, engine->irq_enable_mask); -} - -static void -gen6_irq_disable(struct intel_engine_cs *engine) -{ - ENGINE_WRITE(engine, RING_IMR, ~engine->irq_keep_mask); - gen5_gt_disable_irq(engine->gt, engine->irq_enable_mask); -} - -static void -hsw_vebox_irq_enable(struct intel_engine_cs *engine) -{ - ENGINE_WRITE(engine, RING_IMR, ~engine->irq_enable_mask); - - /* Flush/delay to ensure the RING_IMR is active before the GT IMR */ - ENGINE_POSTING_READ(engine, RING_IMR); - - gen6_gt_pm_unmask_irq(engine->gt, engine->irq_enable_mask); -} - -static void -hsw_vebox_irq_disable(struct intel_engine_cs *engine) -{ - ENGINE_WRITE(engine, RING_IMR, ~0); - gen6_gt_pm_mask_irq(engine->gt, engine->irq_enable_mask); -} - -static int -i965_emit_bb_start(struct i915_request *rq, - u64 offset, u32 length, - unsigned int dispatch_flags) -{ - u32 *cs; - - cs = intel_ring_begin(rq, 2); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - *cs++ = MI_BATCH_BUFFER_START | MI_BATCH_GTT | (dispatch_flags & - I915_DISPATCH_SECURE ? 0 : MI_BATCH_NON_SECURE_I965); - *cs++ = offset; - intel_ring_advance(rq, cs); - - return 0; -} - -/* Just userspace ABI convention to limit the wa batch bo to a resonable size */ -#define I830_BATCH_LIMIT SZ_256K -#define I830_TLB_ENTRIES (2) -#define I830_WA_SIZE max(I830_TLB_ENTRIES*4096, I830_BATCH_LIMIT) -static int -i830_emit_bb_start(struct i915_request *rq, - u64 offset, u32 len, - unsigned int dispatch_flags) -{ - u32 *cs, cs_offset = - intel_gt_scratch_offset(rq->engine->gt, - INTEL_GT_SCRATCH_FIELD_DEFAULT); - - GEM_BUG_ON(rq->engine->gt->scratch->size < I830_WA_SIZE); - - cs = intel_ring_begin(rq, 6); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - /* Evict the invalid PTE TLBs */ - *cs++ = COLOR_BLT_CMD | BLT_WRITE_RGBA; - *cs++ = BLT_DEPTH_32 | BLT_ROP_COLOR_COPY | 4096; - *cs++ = I830_TLB_ENTRIES << 16 | 4; /* load each page */ - *cs++ = cs_offset; - *cs++ = 0xdeadbeef; - *cs++ = MI_NOOP; - intel_ring_advance(rq, cs); - - if ((dispatch_flags & I915_DISPATCH_PINNED) == 0) { - if (len > I830_BATCH_LIMIT) - return -ENOSPC; - - cs = intel_ring_begin(rq, 6 + 2); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - /* Blit the batch (which has now all relocs applied) to the - * stable batch scratch bo area (so that the CS never - * stumbles over its tlb invalidation bug) ... - */ - *cs++ = SRC_COPY_BLT_CMD | BLT_WRITE_RGBA | (6 - 2); - *cs++ = BLT_DEPTH_32 | BLT_ROP_SRC_COPY | 4096; - *cs++ = DIV_ROUND_UP(len, 4096) << 16 | 4096; - *cs++ = cs_offset; - *cs++ = 4096; - *cs++ = offset; - - *cs++ = MI_FLUSH; - *cs++ = MI_NOOP; - intel_ring_advance(rq, cs); - - /* ... and execute it. */ - offset = cs_offset; - } - - cs = intel_ring_begin(rq, 2); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - *cs++ = MI_BATCH_BUFFER_START | MI_BATCH_GTT; - *cs++ = offset | (dispatch_flags & I915_DISPATCH_SECURE ? 0 : - MI_BATCH_NON_SECURE); - intel_ring_advance(rq, cs); - - return 0; -} - -static int -i915_emit_bb_start(struct i915_request *rq, - u64 offset, u32 len, - unsigned int dispatch_flags) -{ - u32 *cs; - - cs = intel_ring_begin(rq, 2); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - *cs++ = MI_BATCH_BUFFER_START | MI_BATCH_GTT; - *cs++ = offset | (dispatch_flags & I915_DISPATCH_SECURE ? 0 : - MI_BATCH_NON_SECURE); - intel_ring_advance(rq, cs); - - return 0; -} - static void __ring_context_fini(struct intel_context *ce) { i915_vma_put(ce->state); @@ -1356,8 +645,8 @@ static inline int mi_set_context(struct i915_request *rq, struct intel_context *ce, u32 flags) { - struct drm_i915_private *i915 = rq->i915; struct intel_engine_cs *engine = rq->engine; + struct drm_i915_private *i915 = engine->i915; enum intel_engine_id id; const int num_engines = IS_HASWELL(i915) ? RUNTIME_INFO(i915)->num_engines - 1 : 0; @@ -1471,7 +760,7 @@ static inline int mi_set_context(struct i915_request *rq, static int remap_l3_slice(struct i915_request *rq, int slice) { - u32 *cs, *remap_info = rq->i915->l3_parity.remap_info[slice]; + u32 *cs, *remap_info = rq->engine->i915->l3_parity.remap_info[slice]; int i; if (!remap_info) @@ -1582,7 +871,7 @@ static int switch_context(struct i915_request *rq) void **residuals = NULL; int ret; - GEM_BUG_ON(HAS_EXECLISTS(rq->i915)); + GEM_BUG_ON(HAS_EXECLISTS(engine->i915)); if (engine->wa_ctx.vma && ce != engine->kernel_context) { if (engine->wa_ctx.vma->private != ce) { @@ -1704,99 +993,6 @@ static void gen6_bsd_submit_request(struct i915_request *request) intel_uncore_forcewake_put(uncore, FORCEWAKE_ALL); } -static int mi_flush_dw(struct i915_request *rq, u32 flags) -{ - u32 cmd, *cs; - - cs = intel_ring_begin(rq, 4); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - cmd = MI_FLUSH_DW; - - /* - * We always require a command barrier so that subsequent - * commands, such as breadcrumb interrupts, are strictly ordered - * wrt the contents of the write cache being flushed to memory - * (and thus being coherent from the CPU). - */ - cmd |= MI_FLUSH_DW_STORE_INDEX | MI_FLUSH_DW_OP_STOREDW; - - /* - * Bspec vol 1c.3 - blitter engine command streamer: - * "If ENABLED, all TLBs will be invalidated once the flush - * operation is complete. This bit is only valid when the - * Post-Sync Operation field is a value of 1h or 3h." - */ - cmd |= flags; - - *cs++ = cmd; - *cs++ = I915_GEM_HWS_SCRATCH_ADDR | MI_FLUSH_DW_USE_GTT; - *cs++ = 0; - *cs++ = MI_NOOP; - - intel_ring_advance(rq, cs); - - return 0; -} - -static int gen6_flush_dw(struct i915_request *rq, u32 mode, u32 invflags) -{ - return mi_flush_dw(rq, mode & EMIT_INVALIDATE ? invflags : 0); -} - -static int gen6_bsd_ring_flush(struct i915_request *rq, u32 mode) -{ - return gen6_flush_dw(rq, mode, MI_INVALIDATE_TLB | MI_INVALIDATE_BSD); -} - -static int -hsw_emit_bb_start(struct i915_request *rq, - u64 offset, u32 len, - unsigned int dispatch_flags) -{ - u32 *cs; - - cs = intel_ring_begin(rq, 2); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - *cs++ = MI_BATCH_BUFFER_START | (dispatch_flags & I915_DISPATCH_SECURE ? - 0 : MI_BATCH_PPGTT_HSW | MI_BATCH_NON_SECURE_HSW); - /* bit0-7 is the length on GEN6+ */ - *cs++ = offset; - intel_ring_advance(rq, cs); - - return 0; -} - -static int -gen6_emit_bb_start(struct i915_request *rq, - u64 offset, u32 len, - unsigned int dispatch_flags) -{ - u32 *cs; - - cs = intel_ring_begin(rq, 2); - if (IS_ERR(cs)) - return PTR_ERR(cs); - - *cs++ = MI_BATCH_BUFFER_START | (dispatch_flags & I915_DISPATCH_SECURE ? - 0 : MI_BATCH_NON_SECURE_I965); - /* bit0-7 is the length on GEN6+ */ - *cs++ = offset; - intel_ring_advance(rq, cs); - - return 0; -} - -/* Blitter support (SandyBridge+) */ - -static int gen6_ring_flush(struct i915_request *rq, u32 mode) -{ - return gen6_flush_dw(rq, mode, MI_INVALIDATE_TLB); -} - static void i9xx_set_default_submission(struct intel_engine_cs *engine) { engine->submit_request = i9xx_submit_request; @@ -1843,11 +1039,11 @@ static void setup_irq(struct intel_engine_cs *engine) engine->irq_enable = gen5_irq_enable; engine->irq_disable = gen5_irq_disable; } else if (INTEL_GEN(i915) >= 3) { - engine->irq_enable = i9xx_irq_enable; - engine->irq_disable = i9xx_irq_disable; + engine->irq_enable = gen3_irq_enable; + engine->irq_disable = gen3_irq_disable; } else { - engine->irq_enable = i8xx_irq_enable; - engine->irq_disable = i8xx_irq_disable; + engine->irq_enable = gen2_irq_enable; + engine->irq_disable = gen2_irq_disable; } } @@ -1874,7 +1070,7 @@ static void setup_common(struct intel_engine_cs *engine) * equivalent to our next initial bread so we can elide * engine->emit_init_breadcrumb(). */ - engine->emit_fini_breadcrumb = i9xx_emit_breadcrumb; + engine->emit_fini_breadcrumb = gen3_emit_breadcrumb; if (IS_GEN(i915, 5)) engine->emit_fini_breadcrumb = gen5_emit_breadcrumb; @@ -1883,11 +1079,11 @@ static void setup_common(struct intel_engine_cs *engine) if (INTEL_GEN(i915) >= 6) engine->emit_bb_start = gen6_emit_bb_start; else if (INTEL_GEN(i915) >= 4) - engine->emit_bb_start = i965_emit_bb_start; + engine->emit_bb_start = gen4_emit_bb_start; else if (IS_I830(i915) || IS_I845G(i915)) engine->emit_bb_start = i830_emit_bb_start; else - engine->emit_bb_start = i915_emit_bb_start; + engine->emit_bb_start = gen3_emit_bb_start; } static void setup_rcs(struct intel_engine_cs *engine) @@ -1900,25 +1096,23 @@ static void setup_rcs(struct intel_engine_cs *engine) engine->irq_enable_mask = GT_RENDER_USER_INTERRUPT; if (INTEL_GEN(i915) >= 7) { - engine->emit_flush = gen7_render_ring_flush; - engine->emit_fini_breadcrumb = gen7_rcs_emit_breadcrumb; + engine->emit_flush = gen7_emit_flush_rcs; + engine->emit_fini_breadcrumb = gen7_emit_breadcrumb_rcs; } else if (IS_GEN(i915, 6)) { - engine->emit_flush = gen6_render_ring_flush; - engine->emit_fini_breadcrumb = gen6_rcs_emit_breadcrumb; + engine->emit_flush = gen6_emit_flush_rcs; + engine->emit_fini_breadcrumb = gen6_emit_breadcrumb_rcs; } else if (IS_GEN(i915, 5)) { - engine->emit_flush = gen4_render_ring_flush; + engine->emit_flush = gen4_emit_flush_rcs; } else { if (INTEL_GEN(i915) < 4) - engine->emit_flush = gen2_render_ring_flush; + engine->emit_flush = gen2_emit_flush; else - engine->emit_flush = gen4_render_ring_flush; + engine->emit_flush = gen4_emit_flush_rcs; engine->irq_enable_mask = I915_USER_INTERRUPT; } if (IS_HASWELL(i915)) engine->emit_bb_start = hsw_emit_bb_start; - - engine->resume = rcs_resume; } static void setup_vcs(struct intel_engine_cs *engine) @@ -1929,15 +1123,15 @@ static void setup_vcs(struct intel_engine_cs *engine) /* gen6 bsd needs a special wa for tail updates */ if (IS_GEN(i915, 6)) engine->set_default_submission = gen6_bsd_set_default_submission; - engine->emit_flush = gen6_bsd_ring_flush; + engine->emit_flush = gen6_emit_flush_vcs; engine->irq_enable_mask = GT_BSD_USER_INTERRUPT; if (IS_GEN(i915, 6)) - engine->emit_fini_breadcrumb = gen6_xcs_emit_breadcrumb; + engine->emit_fini_breadcrumb = gen6_emit_breadcrumb_xcs; else - engine->emit_fini_breadcrumb = gen7_xcs_emit_breadcrumb; + engine->emit_fini_breadcrumb = gen7_emit_breadcrumb_xcs; } else { - engine->emit_flush = bsd_ring_flush; + engine->emit_flush = gen4_emit_flush_vcs; if (IS_GEN(i915, 5)) engine->irq_enable_mask = ILK_BSD_USER_INTERRUPT; else @@ -1949,13 +1143,13 @@ static void setup_bcs(struct intel_engine_cs *engine) { struct drm_i915_private *i915 = engine->i915; - engine->emit_flush = gen6_ring_flush; + engine->emit_flush = gen6_emit_flush_xcs; engine->irq_enable_mask = GT_BLT_USER_INTERRUPT; if (IS_GEN(i915, 6)) - engine->emit_fini_breadcrumb = gen6_xcs_emit_breadcrumb; + engine->emit_fini_breadcrumb = gen6_emit_breadcrumb_xcs; else - engine->emit_fini_breadcrumb = gen7_xcs_emit_breadcrumb; + engine->emit_fini_breadcrumb = gen7_emit_breadcrumb_xcs; } static void setup_vecs(struct intel_engine_cs *engine) @@ -1964,12 +1158,12 @@ static void setup_vecs(struct intel_engine_cs *engine) GEM_BUG_ON(INTEL_GEN(i915) < 7); - engine->emit_flush = gen6_ring_flush; + engine->emit_flush = gen6_emit_flush_xcs; engine->irq_enable_mask = PM_VEBOX_USER_INTERRUPT; - engine->irq_enable = hsw_vebox_irq_enable; - engine->irq_disable = hsw_vebox_irq_disable; + engine->irq_enable = hsw_irq_enable_vecs; + engine->irq_disable = hsw_irq_disable_vecs; - engine->emit_fini_breadcrumb = gen7_xcs_emit_breadcrumb; + engine->emit_fini_breadcrumb = gen7_emit_breadcrumb_xcs; } static int gen7_ctx_switch_bb_setup(struct intel_engine_cs * const engine, diff --git a/drivers/gpu/drm/i915/gt/intel_rps.c b/drivers/gpu/drm/i915/gt/intel_rps.c index 2f59fc6df3c2..296391deeb94 100644 --- a/drivers/gpu/drm/i915/gt/intel_rps.c +++ b/drivers/gpu/drm/i915/gt/intel_rps.c @@ -51,15 +51,16 @@ static void rps_timer(struct timer_list *t) { struct intel_rps *rps = from_timer(rps, t, timer); struct intel_engine_cs *engine; + ktime_t dt, last, timestamp; enum intel_engine_id id; s64 max_busy[3] = {}; - ktime_t dt, last; + timestamp = 0; for_each_engine(engine, rps_to_gt(rps), id) { s64 busy; int i; - dt = intel_engine_get_busy_time(engine); + dt = intel_engine_get_busy_time(engine, ×tamp); last = engine->stats.rps; engine->stats.rps = dt; @@ -69,16 +70,14 @@ static void rps_timer(struct timer_list *t) swap(busy, max_busy[i]); } } - - dt = ktime_get(); last = rps->pm_timestamp; - rps->pm_timestamp = dt; + rps->pm_timestamp = timestamp; if (intel_rps_is_active(rps)) { s64 busy; int i; - dt = ktime_sub(dt, last); + dt = ktime_sub(timestamp, last); /* * Our goal is to evaluate each engine independently, so we run diff --git a/drivers/gpu/drm/i915/gt/intel_workarounds.c b/drivers/gpu/drm/i915/gt/intel_workarounds.c index 85d2bef51524..2da366821dda 100644 --- a/drivers/gpu/drm/i915/gt/intel_workarounds.c +++ b/drivers/gpu/drm/i915/gt/intel_workarounds.c @@ -205,6 +205,18 @@ wa_masked_dis(struct i915_wa_list *wal, i915_reg_t reg, u32 val) #define WA_SET_FIELD_MASKED(addr, mask, value) \ wa_write_masked_or(wal, (addr), 0, _MASKED_FIELD((mask), (value))) +static void gen6_ctx_workarounds_init(struct intel_engine_cs *engine, + struct i915_wa_list *wal) +{ + WA_SET_BIT_MASKED(INSTPM, INSTPM_FORCE_ORDERING); +} + +static void gen7_ctx_workarounds_init(struct intel_engine_cs *engine, + struct i915_wa_list *wal) +{ + WA_SET_BIT_MASKED(INSTPM, INSTPM_FORCE_ORDERING); +} + static void gen8_ctx_workarounds_init(struct intel_engine_cs *engine, struct i915_wa_list *wal) { @@ -355,7 +367,10 @@ static void gen9_ctx_workarounds_init(struct intel_engine_cs *engine, HDC_FORCE_NON_COHERENT); /* WaDisableSamplerPowerBypassForSOPingPong:skl,bxt,kbl,cfl */ - if (IS_SKYLAKE(i915) || IS_KABYLAKE(i915) || IS_COFFEELAKE(i915)) + if (IS_SKYLAKE(i915) || + IS_KABYLAKE(i915) || + IS_COFFEELAKE(i915) || + IS_COMETLAKE(i915)) WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3, GEN8_SAMPLER_POWER_BYPASS_DIS); @@ -600,11 +615,14 @@ static void tgl_ctx_workarounds_init(struct intel_engine_cs *engine, * Wa_1604555607:gen12 and Wa_1608008084:gen12 * FF_MODE2 register will return the wrong value when read. The default * value for this register is zero for all fields and there are no bit - * masks. So instead of doing a RMW we should just write the TDS timer - * value for Wa_1604555607. + * masks. So instead of doing a RMW we should just write the GS Timer + * and TDS timer values for Wa_1604555607 and Wa_16011163337. */ - wa_add(wal, FF_MODE2, FF_MODE2_TDS_TIMER_MASK, - FF_MODE2_TDS_TIMER_128, 0); + wa_add(wal, + FF_MODE2, + FF_MODE2_GS_TIMER_MASK | FF_MODE2_TDS_TIMER_MASK, + FF_MODE2_GS_TIMER_224 | FF_MODE2_TDS_TIMER_128, + 0); /* WaDisableGPGPUMidThreadPreemption:tgl */ WA_SET_FIELD_MASKED(GEN8_CS_CHICKEN1, @@ -630,7 +648,7 @@ __intel_engine_init_ctx_wa(struct intel_engine_cs *engine, icl_ctx_workarounds_init(engine, wal); else if (IS_CANNONLAKE(i915)) cnl_ctx_workarounds_init(engine, wal); - else if (IS_COFFEELAKE(i915)) + else if (IS_COFFEELAKE(i915) || IS_COMETLAKE(i915)) cfl_ctx_workarounds_init(engine, wal); else if (IS_GEMINILAKE(i915)) glk_ctx_workarounds_init(engine, wal); @@ -644,6 +662,10 @@ __intel_engine_init_ctx_wa(struct intel_engine_cs *engine, chv_ctx_workarounds_init(engine, wal); else if (IS_BROADWELL(i915)) bdw_ctx_workarounds_init(engine, wal); + else if (IS_GEN(i915, 7)) + gen7_ctx_workarounds_init(engine, wal); + else if (IS_GEN(i915, 6)) + gen6_ctx_workarounds_init(engine, wal); else if (INTEL_GEN(i915) < 8) return; else @@ -917,7 +939,7 @@ static void gen9_gt_workarounds_init(struct drm_i915_private *i915, struct i915_wa_list *wal) { /* WaDisableKillLogic:bxt,skl,kbl */ - if (!IS_COFFEELAKE(i915)) + if (!IS_COFFEELAKE(i915) && !IS_COMETLAKE(i915)) wa_write_or(wal, GAM_ECOCHK, ECOCHK_DIS_TLB); @@ -1180,7 +1202,7 @@ gt_init_workarounds(struct drm_i915_private *i915, struct i915_wa_list *wal) icl_gt_workarounds_init(i915, wal); else if (IS_CANNONLAKE(i915)) cnl_gt_workarounds_init(i915, wal); - else if (IS_COFFEELAKE(i915)) + else if (IS_COFFEELAKE(i915) || IS_COMETLAKE(i915)) cfl_gt_workarounds_init(i915, wal); else if (IS_GEMINILAKE(i915)) glk_gt_workarounds_init(i915, wal); @@ -1428,6 +1450,18 @@ static void cfl_whitelist_build(struct intel_engine_cs *engine) RING_FORCE_TO_NONPRIV_RANGE_4); } +static void cml_whitelist_build(struct intel_engine_cs *engine) +{ + struct i915_wa_list *w = &engine->whitelist; + + if (engine->class != RENDER_CLASS) + whitelist_reg_ext(w, + RING_CTX_TIMESTAMP(engine->mmio_base), + RING_FORCE_TO_NONPRIV_ACCESS_RD); + + cfl_whitelist_build(engine); +} + static void cnl_whitelist_build(struct intel_engine_cs *engine) { struct i915_wa_list *w = &engine->whitelist; @@ -1478,9 +1512,15 @@ static void icl_whitelist_build(struct intel_engine_cs *engine) /* hucStatus2RegOffset */ whitelist_reg_ext(w, _MMIO(0x23B0 + engine->mmio_base), RING_FORCE_TO_NONPRIV_ACCESS_RD); + whitelist_reg_ext(w, + RING_CTX_TIMESTAMP(engine->mmio_base), + RING_FORCE_TO_NONPRIV_ACCESS_RD); break; default: + whitelist_reg_ext(w, + RING_CTX_TIMESTAMP(engine->mmio_base), + RING_FORCE_TO_NONPRIV_ACCESS_RD); break; } } @@ -1512,6 +1552,9 @@ static void tgl_whitelist_build(struct intel_engine_cs *engine) whitelist_reg(w, HIZ_CHICKEN); break; default: + whitelist_reg_ext(w, + RING_CTX_TIMESTAMP(engine->mmio_base), + RING_FORCE_TO_NONPRIV_ACCESS_RD); break; } } @@ -1529,6 +1572,8 @@ void intel_engine_init_whitelist(struct intel_engine_cs *engine) icl_whitelist_build(engine); else if (IS_CANNONLAKE(i915)) cnl_whitelist_build(engine); + else if (IS_COMETLAKE(i915)) + cml_whitelist_build(engine); else if (IS_COFFEELAKE(i915)) cfl_whitelist_build(engine); else if (IS_GEMINILAKE(i915)) @@ -1725,6 +1770,12 @@ rcs_engine_wa_init(struct intel_engine_cs *engine, struct i915_wa_list *wal) wa_write_or(wal, GEN7_FF_THREAD_MODE, GEN12_FF_TESSELATION_DOP_GATE_DISABLE); + + /* Wa_22010271021:ehl */ + if (IS_ELKHARTLAKE(i915)) + wa_masked_en(wal, + GEN9_CS_DEBUG_MODE1, + FF_DOP_CLOCK_GATE_DISABLE); } if (IS_GEN_RANGE(i915, 9, 12)) { @@ -1734,7 +1785,10 @@ rcs_engine_wa_init(struct intel_engine_cs *engine, struct i915_wa_list *wal) GEN9_FFSC_PERCTX_PREEMPT_CTRL); } - if (IS_SKYLAKE(i915) || IS_KABYLAKE(i915) || IS_COFFEELAKE(i915)) { + if (IS_SKYLAKE(i915) || + IS_KABYLAKE(i915) || + IS_COFFEELAKE(i915) || + IS_COMETLAKE(i915)) { /* WaEnableGapsTsvCreditFix:skl,kbl,cfl */ wa_write_or(wal, GEN8_GARBCNTL, @@ -1818,6 +1872,21 @@ rcs_engine_wa_init(struct intel_engine_cs *engine, struct i915_wa_list *wal) 0, _MASKED_BIT_ENABLE(VS_TIMER_DISPATCH), /* XXX bit doesn't stick on Broadwater */ IS_I965G(i915) ? 0 : VS_TIMER_DISPATCH); + + if (IS_GEN(i915, 4)) + /* + * Disable CONSTANT_BUFFER before it is loaded from the context + * image. For as it is loaded, it is executed and the stored + * address may no longer be valid, leading to a GPU hang. + * + * This imposes the requirement that userspace reload their + * CONSTANT_BUFFER on every batch, fortunately a requirement + * they are already accustomed to from before contexts were + * enabled. + */ + wa_add(wal, ECOSKPD, + 0, _MASKED_BIT_ENABLE(ECO_CONSTANT_BUFFER_SR_DISABLE), + 0 /* XXX bit doesn't stick on Broadwater */); } static void @@ -1932,7 +2001,7 @@ wa_list_srm(struct i915_request *rq, const struct i915_wa_list *wal, struct i915_vma *vma) { - struct drm_i915_private *i915 = rq->i915; + struct drm_i915_private *i915 = rq->engine->i915; unsigned int i, count = 0; const struct i915_wa *wa; u32 srm, *cs; @@ -2021,7 +2090,7 @@ static int engine_wa_list_verify(struct intel_context *ce, err = 0; for (i = 0, wa = wal->list; i < wal->count; i++, wa++) { - if (mcr_range(rq->i915, i915_mmio_reg_offset(wa->reg))) + if (mcr_range(rq->engine->i915, i915_mmio_reg_offset(wa->reg))) continue; if (!wa_verify(wa, results[i], wal->name, from)) diff --git a/drivers/gpu/drm/i915/gt/selftest_engine_cs.c b/drivers/gpu/drm/i915/gt/selftest_engine_cs.c index f88e445a1cae..729c3c7b11e2 100644 --- a/drivers/gpu/drm/i915/gt/selftest_engine_cs.c +++ b/drivers/gpu/drm/i915/gt/selftest_engine_cs.c @@ -49,7 +49,7 @@ static int write_timestamp(struct i915_request *rq, int slot) return PTR_ERR(cs); cmd = MI_STORE_REGISTER_MEM | MI_USE_GGTT; - if (INTEL_GEN(rq->i915) >= 8) + if (INTEL_GEN(rq->engine->i915) >= 8) cmd++; *cs++ = cmd; *cs++ = i915_mmio_reg_offset(RING_TIMESTAMP(rq->engine->mmio_base)); diff --git a/drivers/gpu/drm/i915/gt/selftest_engine_heartbeat.c b/drivers/gpu/drm/i915/gt/selftest_engine_heartbeat.c index 697114dd1f47..73243ba59c7d 100644 --- a/drivers/gpu/drm/i915/gt/selftest_engine_heartbeat.c +++ b/drivers/gpu/drm/i915/gt/selftest_engine_heartbeat.c @@ -10,6 +10,7 @@ #include "intel_gt_requests.h" #include "i915_selftest.h" +#include "selftest_engine_heartbeat.h" static int timeline_sync(struct intel_timeline *tl) { @@ -142,24 +143,6 @@ out: return err; } -static void engine_heartbeat_disable(struct intel_engine_cs *engine, - unsigned long *saved) -{ - *saved = engine->props.heartbeat_interval_ms; - engine->props.heartbeat_interval_ms = 0; - - intel_engine_pm_get(engine); - intel_engine_park_heartbeat(engine); -} - -static void engine_heartbeat_enable(struct intel_engine_cs *engine, - unsigned long saved) -{ - intel_engine_pm_put(engine); - - engine->props.heartbeat_interval_ms = saved; -} - static int live_idle_flush(void *arg) { struct intel_gt *gt = arg; @@ -170,11 +153,9 @@ static int live_idle_flush(void *arg) /* Check that we can flush the idle barriers */ for_each_engine(engine, gt, id) { - unsigned long heartbeat; - - engine_heartbeat_disable(engine, &heartbeat); + st_engine_heartbeat_disable(engine); err = __live_idle_pulse(engine, intel_engine_flush_barriers); - engine_heartbeat_enable(engine, heartbeat); + st_engine_heartbeat_enable(engine); if (err) break; } @@ -192,11 +173,9 @@ static int live_idle_pulse(void *arg) /* Check that heartbeat pulses flush the idle barriers */ for_each_engine(engine, gt, id) { - unsigned long heartbeat; - - engine_heartbeat_disable(engine, &heartbeat); + st_engine_heartbeat_disable(engine); err = __live_idle_pulse(engine, intel_engine_pulse); - engine_heartbeat_enable(engine, heartbeat); + st_engine_heartbeat_enable(engine); if (err && err != -ENODEV) break; @@ -386,11 +365,27 @@ int intel_heartbeat_live_selftests(struct drm_i915_private *i915) if (intel_gt_is_wedged(&i915->gt)) return 0; - saved_hangcheck = i915_modparams.enable_hangcheck; - i915_modparams.enable_hangcheck = INT_MAX; + saved_hangcheck = i915->params.enable_hangcheck; + i915->params.enable_hangcheck = INT_MAX; err = intel_gt_live_subtests(tests, &i915->gt); - i915_modparams.enable_hangcheck = saved_hangcheck; + i915->params.enable_hangcheck = saved_hangcheck; return err; } + +void st_engine_heartbeat_disable(struct intel_engine_cs *engine) +{ + engine->props.heartbeat_interval_ms = 0; + + intel_engine_pm_get(engine); + intel_engine_park_heartbeat(engine); +} + +void st_engine_heartbeat_enable(struct intel_engine_cs *engine) +{ + intel_engine_pm_put(engine); + + engine->props.heartbeat_interval_ms = + engine->defaults.heartbeat_interval_ms; +} diff --git a/drivers/gpu/drm/i915/gt/selftest_engine_heartbeat.h b/drivers/gpu/drm/i915/gt/selftest_engine_heartbeat.h new file mode 100644 index 000000000000..cd27113d5400 --- /dev/null +++ b/drivers/gpu/drm/i915/gt/selftest_engine_heartbeat.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2020 Intel Corporation + */ + +#ifndef SELFTEST_ENGINE_HEARTBEAT_H +#define SELFTEST_ENGINE_HEARTBEAT_H + +struct intel_engine_cs; + +void st_engine_heartbeat_disable(struct intel_engine_cs *engine); +void st_engine_heartbeat_enable(struct intel_engine_cs *engine); + +#endif /* SELFTEST_ENGINE_HEARTBEAT_H */ diff --git a/drivers/gpu/drm/i915/gt/selftest_engine_pm.c b/drivers/gpu/drm/i915/gt/selftest_engine_pm.c index cbf6b0735272..b08fc5390e8a 100644 --- a/drivers/gpu/drm/i915/gt/selftest_engine_pm.c +++ b/drivers/gpu/drm/i915/gt/selftest_engine_pm.c @@ -6,7 +6,107 @@ #include "i915_selftest.h" #include "selftest_engine.h" +#include "selftest_engine_heartbeat.h" #include "selftests/igt_atomic.h" +#include "selftests/igt_flush_test.h" +#include "selftests/igt_spinner.h" + +static int live_engine_busy_stats(void *arg) +{ + struct intel_gt *gt = arg; + struct intel_engine_cs *engine; + enum intel_engine_id id; + struct igt_spinner spin; + int err = 0; + + /* + * Check that if an engine supports busy-stats, they tell the truth. + */ + + if (igt_spinner_init(&spin, gt)) + return -ENOMEM; + + GEM_BUG_ON(intel_gt_pm_is_awake(gt)); + for_each_engine(engine, gt, id) { + struct i915_request *rq; + ktime_t de, dt; + ktime_t t[2]; + + if (!intel_engine_supports_stats(engine)) + continue; + + if (!intel_engine_can_store_dword(engine)) + continue; + + if (intel_gt_pm_wait_for_idle(gt)) { + err = -EBUSY; + break; + } + + st_engine_heartbeat_disable(engine); + + ENGINE_TRACE(engine, "measuring idle time\n"); + preempt_disable(); + de = intel_engine_get_busy_time(engine, &t[0]); + udelay(100); + de = ktime_sub(intel_engine_get_busy_time(engine, &t[1]), de); + preempt_enable(); + dt = ktime_sub(t[1], t[0]); + if (de < 0 || de > 10) { + pr_err("%s: reported %lldns [%d%%] busyness while sleeping [for %lldns]\n", + engine->name, + de, (int)div64_u64(100 * de, dt), dt); + GEM_TRACE_DUMP(); + err = -EINVAL; + goto end; + } + + /* 100% busy */ + rq = igt_spinner_create_request(&spin, + engine->kernel_context, + MI_NOOP); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto end; + } + i915_request_add(rq); + + if (!igt_wait_for_spinner(&spin, rq)) { + intel_gt_set_wedged(engine->gt); + err = -ETIME; + goto end; + } + + ENGINE_TRACE(engine, "measuring busy time\n"); + preempt_disable(); + de = intel_engine_get_busy_time(engine, &t[0]); + udelay(100); + de = ktime_sub(intel_engine_get_busy_time(engine, &t[1]), de); + preempt_enable(); + dt = ktime_sub(t[1], t[0]); + if (100 * de < 95 * dt || 95 * de > 100 * dt) { + pr_err("%s: reported %lldns [%d%%] busyness while spinning [for %lldns]\n", + engine->name, + de, (int)div64_u64(100 * de, dt), dt); + GEM_TRACE_DUMP(); + err = -EINVAL; + goto end; + } + +end: + st_engine_heartbeat_enable(engine); + igt_spinner_end(&spin); + if (igt_flush_test(gt->i915)) + err = -EIO; + if (err) + break; + } + + igt_spinner_fini(&spin); + if (igt_flush_test(gt->i915)) + err = -EIO; + return err; +} static int live_engine_pm(void *arg) { @@ -77,6 +177,7 @@ static int live_engine_pm(void *arg) int live_engine_pm_selftests(struct intel_gt *gt) { static const struct i915_subtest tests[] = { + SUBTEST(live_engine_busy_stats), SUBTEST(live_engine_pm), }; diff --git a/drivers/gpu/drm/i915/gt/selftest_gt_pm.c b/drivers/gpu/drm/i915/gt/selftest_gt_pm.c index 242181a5214c..6180a47c1b51 100644 --- a/drivers/gpu/drm/i915/gt/selftest_gt_pm.c +++ b/drivers/gpu/drm/i915/gt/selftest_gt_pm.c @@ -5,10 +5,141 @@ * Copyright © 2019 Intel Corporation */ +#include <linux/sort.h> + +#include "intel_gt_clock_utils.h" + #include "selftest_llc.h" #include "selftest_rc6.h" #include "selftest_rps.h" +static int cmp_u64(const void *A, const void *B) +{ + const u64 *a = A, *b = B; + + if (a < b) + return -1; + else if (a > b) + return 1; + else + return 0; +} + +static int cmp_u32(const void *A, const void *B) +{ + const u32 *a = A, *b = B; + + if (a < b) + return -1; + else if (a > b) + return 1; + else + return 0; +} + +static void measure_clocks(struct intel_engine_cs *engine, + u32 *out_cycles, ktime_t *out_dt) +{ + ktime_t dt[5]; + u32 cycles[5]; + int i; + + for (i = 0; i < 5; i++) { + preempt_disable(); + cycles[i] = -ENGINE_READ_FW(engine, RING_TIMESTAMP); + dt[i] = ktime_get(); + + udelay(1000); + + dt[i] = ktime_sub(ktime_get(), dt[i]); + cycles[i] += ENGINE_READ_FW(engine, RING_TIMESTAMP); + preempt_enable(); + } + + /* Use the median of both cycle/dt; close enough */ + sort(cycles, 5, sizeof(*cycles), cmp_u32, NULL); + *out_cycles = (cycles[1] + 2 * cycles[2] + cycles[3]) / 4; + + sort(dt, 5, sizeof(*dt), cmp_u64, NULL); + *out_dt = div_u64(dt[1] + 2 * dt[2] + dt[3], 4); +} + +static int live_gt_clocks(void *arg) +{ + struct intel_gt *gt = arg; + struct intel_engine_cs *engine; + enum intel_engine_id id; + int err = 0; + + if (!RUNTIME_INFO(gt->i915)->cs_timestamp_frequency_hz) { /* unknown */ + pr_info("CS_TIMESTAMP frequency unknown\n"); + return 0; + } + + if (INTEL_GEN(gt->i915) < 4) /* Any CS_TIMESTAMP? */ + return 0; + + if (IS_GEN(gt->i915, 5)) + /* + * XXX CS_TIMESTAMP low dword is dysfunctional? + * + * Ville's experiments indicate the high dword still works, + * but at a correspondingly reduced frequency. + */ + return 0; + + if (IS_GEN(gt->i915, 4)) + /* + * XXX CS_TIMESTAMP appears gibberish + * + * Ville's experiments indicate that it mostly appears 'stuck' + * in that we see the register report the same cycle count + * for a couple of reads. + */ + return 0; + + intel_gt_pm_get(gt); + intel_uncore_forcewake_get(gt->uncore, FORCEWAKE_ALL); + + for_each_engine(engine, gt, id) { + u32 cycles; + u32 expected; + u64 time; + u64 dt; + + if (INTEL_GEN(engine->i915) < 7 && engine->id != RCS0) + continue; + + measure_clocks(engine, &cycles, &dt); + + time = i915_cs_timestamp_ticks_to_ns(engine->i915, cycles); + expected = i915_cs_timestamp_ns_to_ticks(engine->i915, dt); + + pr_info("%s: TIMESTAMP %d cycles [%lldns] in %lldns [%d cycles], using CS clock frequency of %uKHz\n", + engine->name, cycles, time, dt, expected, + RUNTIME_INFO(engine->i915)->cs_timestamp_frequency_hz / 1000); + + if (9 * time < 8 * dt || 8 * time > 9 * dt) { + pr_err("%s: CS ticks did not match walltime!\n", + engine->name); + err = -EINVAL; + break; + } + + if (9 * expected < 8 * cycles || 8 * expected > 9 * cycles) { + pr_err("%s: walltime did not match CS ticks!\n", + engine->name); + err = -EINVAL; + break; + } + } + + intel_uncore_forcewake_put(gt->uncore, FORCEWAKE_ALL); + intel_gt_pm_put(gt); + + return err; +} + static int live_gt_resume(void *arg) { struct intel_gt *gt = arg; @@ -52,6 +183,7 @@ static int live_gt_resume(void *arg) int intel_gt_pm_live_selftests(struct drm_i915_private *i915) { static const struct i915_subtest tests[] = { + SUBTEST(live_gt_clocks), SUBTEST(live_rc6_manual), SUBTEST(live_rps_clock_interval), SUBTEST(live_rps_control), diff --git a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c index 4aa4cc917d8b..fb5ebf930ab2 100644 --- a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c +++ b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c @@ -29,6 +29,7 @@ #include "intel_gt.h" #include "intel_engine_heartbeat.h" #include "intel_engine_pm.h" +#include "selftest_engine_heartbeat.h" #include "i915_selftest.h" #include "selftests/i915_random.h" @@ -203,12 +204,12 @@ hang_create_request(struct hang *h, struct intel_engine_cs *engine) *batch++ = lower_32_bits(hws_address(hws, rq)); *batch++ = upper_32_bits(hws_address(hws, rq)); *batch++ = rq->fence.seqno; - *batch++ = MI_ARB_CHECK; + *batch++ = MI_NOOP; memset(batch, 0, 1024); batch += 1024 / sizeof(*batch); - *batch++ = MI_ARB_CHECK; + *batch++ = MI_NOOP; *batch++ = MI_BATCH_BUFFER_START | 1 << 8 | 1; *batch++ = lower_32_bits(vma->node.start); *batch++ = upper_32_bits(vma->node.start); @@ -217,12 +218,12 @@ hang_create_request(struct hang *h, struct intel_engine_cs *engine) *batch++ = 0; *batch++ = lower_32_bits(hws_address(hws, rq)); *batch++ = rq->fence.seqno; - *batch++ = MI_ARB_CHECK; + *batch++ = MI_NOOP; memset(batch, 0, 1024); batch += 1024 / sizeof(*batch); - *batch++ = MI_ARB_CHECK; + *batch++ = MI_NOOP; *batch++ = MI_BATCH_BUFFER_START | 1 << 8; *batch++ = lower_32_bits(vma->node.start); } else if (INTEL_GEN(gt->i915) >= 4) { @@ -230,24 +231,24 @@ hang_create_request(struct hang *h, struct intel_engine_cs *engine) *batch++ = 0; *batch++ = lower_32_bits(hws_address(hws, rq)); *batch++ = rq->fence.seqno; - *batch++ = MI_ARB_CHECK; + *batch++ = MI_NOOP; memset(batch, 0, 1024); batch += 1024 / sizeof(*batch); - *batch++ = MI_ARB_CHECK; + *batch++ = MI_NOOP; *batch++ = MI_BATCH_BUFFER_START | 2 << 6; *batch++ = lower_32_bits(vma->node.start); } else { *batch++ = MI_STORE_DWORD_IMM | MI_MEM_VIRTUAL; *batch++ = lower_32_bits(hws_address(hws, rq)); *batch++ = rq->fence.seqno; - *batch++ = MI_ARB_CHECK; + *batch++ = MI_NOOP; memset(batch, 0, 1024); batch += 1024 / sizeof(*batch); - *batch++ = MI_ARB_CHECK; + *batch++ = MI_NOOP; *batch++ = MI_BATCH_BUFFER_START | 2 << 6; *batch++ = lower_32_bits(vma->node.start); } @@ -310,22 +311,6 @@ static bool wait_until_running(struct hang *h, struct i915_request *rq) 1000)); } -static void engine_heartbeat_disable(struct intel_engine_cs *engine) -{ - engine->props.heartbeat_interval_ms = 0; - - intel_engine_pm_get(engine); - intel_engine_park_heartbeat(engine); -} - -static void engine_heartbeat_enable(struct intel_engine_cs *engine) -{ - intel_engine_pm_put(engine); - - engine->props.heartbeat_interval_ms = - engine->defaults.heartbeat_interval_ms; -} - static int igt_hang_sanitycheck(void *arg) { struct intel_gt *gt = arg; @@ -482,7 +467,7 @@ static int igt_reset_nop_engine(void *arg) reset_engine_count = i915_reset_engine_count(global, engine); count = 0; - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); set_bit(I915_RESET_ENGINE + id, >->reset.flags); do { int i; @@ -499,6 +484,20 @@ static int igt_reset_nop_engine(void *arg) rq = intel_context_create_request(ce); if (IS_ERR(rq)) { + struct drm_printer p = + drm_info_printer(gt->i915->drm.dev); + intel_engine_dump(engine, &p, + "%s(%s): failed to submit request\n", + __func__, + engine->name); + + GEM_TRACE("%s(%s): failed to submit request\n", + __func__, + engine->name); + GEM_TRACE_DUMP(); + + intel_gt_set_wedged(gt); + err = PTR_ERR(rq); break; } @@ -526,7 +525,7 @@ static int igt_reset_nop_engine(void *arg) } } while (time_before(jiffies, end_time)); clear_bit(I915_RESET_ENGINE + id, >->reset.flags); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); pr_info("%s(%s): %d resets\n", __func__, engine->name, count); @@ -576,7 +575,7 @@ static int __igt_reset_engine(struct intel_gt *gt, bool active) reset_count = i915_reset_count(global); reset_engine_count = i915_reset_engine_count(global, engine); - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); set_bit(I915_RESET_ENGINE + id, >->reset.flags); do { if (active) { @@ -628,7 +627,7 @@ static int __igt_reset_engine(struct intel_gt *gt, bool active) } } while (time_before(jiffies, end_time)); clear_bit(I915_RESET_ENGINE + id, >->reset.flags); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); if (err) break; @@ -805,10 +804,10 @@ static int __igt_reset_engines(struct intel_gt *gt, threads[tmp].resets = i915_reset_engine_count(global, other); - if (!(flags & TEST_OTHERS)) + if (other == engine && !(flags & TEST_SELF)) continue; - if (other == engine && !(flags & TEST_SELF)) + if (other != engine && !(flags & TEST_OTHERS)) continue; threads[tmp].engine = other; @@ -827,7 +826,7 @@ static int __igt_reset_engines(struct intel_gt *gt, yield(); /* start all threads before we begin */ - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); set_bit(I915_RESET_ENGINE + id, >->reset.flags); do { struct i915_request *rq = NULL; @@ -866,13 +865,29 @@ static int __igt_reset_engines(struct intel_gt *gt, count++; if (rq) { + if (rq->fence.error != -EIO) { + pr_err("i915_reset_engine(%s:%s):" + " failed to reset request %llx:%lld\n", + engine->name, test_name, + rq->fence.context, + rq->fence.seqno); + i915_request_put(rq); + + GEM_TRACE_DUMP(); + intel_gt_set_wedged(gt); + err = -EIO; + break; + } + if (i915_request_wait(rq, 0, HZ / 5) < 0) { struct drm_printer p = drm_info_printer(gt->i915->drm.dev); pr_err("i915_reset_engine(%s:%s):" - " failed to complete request after reset\n", - engine->name, test_name); + " failed to complete request %llx:%lld after reset\n", + engine->name, test_name, + rq->fence.context, + rq->fence.seqno); intel_engine_dump(engine, &p, "%s\n", engine->name); i915_request_put(rq); @@ -901,7 +916,7 @@ static int __igt_reset_engines(struct intel_gt *gt, } } while (time_before(jiffies, end_time)); clear_bit(I915_RESET_ENGINE + id, >->reset.flags); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); pr_info("i915_reset_engine(%s:%s): %lu resets\n", engine->name, test_name, count); @@ -983,7 +998,7 @@ static int igt_reset_engines(void *arg) }, { "self-priority", - TEST_OTHERS | TEST_ACTIVE | TEST_PRIORITY | TEST_SELF, + TEST_ACTIVE | TEST_PRIORITY | TEST_SELF, }, { } }; diff --git a/drivers/gpu/drm/i915/gt/selftest_lrc.c b/drivers/gpu/drm/i915/gt/selftest_lrc.c index 924bc01ef526..daa4aabab9a7 100644 --- a/drivers/gpu/drm/i915/gt/selftest_lrc.c +++ b/drivers/gpu/drm/i915/gt/selftest_lrc.c @@ -9,6 +9,7 @@ #include "gem/i915_gem_pm.h" #include "gt/intel_engine_heartbeat.h" #include "gt/intel_reset.h" +#include "gt/selftest_engine_heartbeat.h" #include "i915_selftest.h" #include "selftests/i915_random.h" @@ -51,22 +52,6 @@ static struct i915_vma *create_scratch(struct intel_gt *gt) return vma; } -static void engine_heartbeat_disable(struct intel_engine_cs *engine) -{ - engine->props.heartbeat_interval_ms = 0; - - intel_engine_pm_get(engine); - intel_engine_park_heartbeat(engine); -} - -static void engine_heartbeat_enable(struct intel_engine_cs *engine) -{ - intel_engine_pm_put(engine); - - engine->props.heartbeat_interval_ms = - engine->defaults.heartbeat_interval_ms; -} - static bool is_active(struct i915_request *rq) { if (i915_request_is_active(rq)) @@ -75,7 +60,7 @@ static bool is_active(struct i915_request *rq) if (i915_request_on_hold(rq)) return true; - if (i915_request_started(rq)) + if (i915_request_has_initial_breadcrumb(rq) && i915_request_started(rq)) return true; return false; @@ -234,7 +219,7 @@ static int live_unlite_restore(struct intel_gt *gt, int prio) err = -EIO; break; } - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); for (n = 0; n < ARRAY_SIZE(ce); n++) { struct intel_context *tmp; @@ -332,7 +317,7 @@ static int live_unlite_restore(struct intel_gt *gt, int prio) i915_request_put(rq[0]); err_ce: - tasklet_kill(&engine->execlists.tasklet); /* flush submission */ + intel_engine_flush_submission(engine); igt_spinner_end(&spin); for (n = 0; n < ARRAY_SIZE(ce); n++) { if (IS_ERR_OR_NULL(ce[n])) @@ -342,7 +327,7 @@ err_ce: intel_context_put(ce[n]); } - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); if (igt_live_test_end(&t)) err = -EIO; if (err) @@ -363,6 +348,156 @@ static int live_unlite_preempt(void *arg) return live_unlite_restore(arg, I915_USER_PRIORITY(I915_PRIORITY_MAX)); } +static int live_unlite_ring(void *arg) +{ + struct intel_gt *gt = arg; + struct intel_engine_cs *engine; + struct igt_spinner spin; + enum intel_engine_id id; + int err = 0; + + /* + * Setup a preemption event that will cause almost the entire ring + * to be unwound, potentially fooling our intel_ring_direction() + * into emitting a forward lite-restore instead of the rollback. + */ + + if (igt_spinner_init(&spin, gt)) + return -ENOMEM; + + for_each_engine(engine, gt, id) { + struct intel_context *ce[2] = {}; + struct i915_request *rq; + struct igt_live_test t; + int n; + + if (!intel_engine_has_preemption(engine)) + continue; + + if (!intel_engine_can_store_dword(engine)) + continue; + + if (igt_live_test_begin(&t, gt->i915, __func__, engine->name)) { + err = -EIO; + break; + } + st_engine_heartbeat_disable(engine); + + for (n = 0; n < ARRAY_SIZE(ce); n++) { + struct intel_context *tmp; + + tmp = intel_context_create(engine); + if (IS_ERR(tmp)) { + err = PTR_ERR(tmp); + goto err_ce; + } + + err = intel_context_pin(tmp); + if (err) { + intel_context_put(tmp); + goto err_ce; + } + + memset32(tmp->ring->vaddr, + 0xdeadbeef, /* trigger a hang if executed */ + tmp->ring->vma->size / sizeof(u32)); + + ce[n] = tmp; + } + + /* Create max prio spinner, followed by N low prio nops */ + rq = igt_spinner_create_request(&spin, ce[0], MI_ARB_CHECK); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto err_ce; + } + + i915_request_get(rq); + rq->sched.attr.priority = I915_PRIORITY_BARRIER; + i915_request_add(rq); + + if (!igt_wait_for_spinner(&spin, rq)) { + intel_gt_set_wedged(gt); + i915_request_put(rq); + err = -ETIME; + goto err_ce; + } + + /* Fill the ring, until we will cause a wrap */ + n = 0; + while (intel_ring_direction(ce[0]->ring, + rq->wa_tail, + ce[0]->ring->tail) <= 0) { + struct i915_request *tmp; + + tmp = intel_context_create_request(ce[0]); + if (IS_ERR(tmp)) { + err = PTR_ERR(tmp); + i915_request_put(rq); + goto err_ce; + } + + i915_request_add(tmp); + intel_engine_flush_submission(engine); + n++; + } + intel_engine_flush_submission(engine); + pr_debug("%s: Filled ring with %d nop tails {size:%x, tail:%x, emit:%x, rq.tail:%x}\n", + engine->name, n, + ce[0]->ring->size, + ce[0]->ring->tail, + ce[0]->ring->emit, + rq->tail); + GEM_BUG_ON(intel_ring_direction(ce[0]->ring, + rq->tail, + ce[0]->ring->tail) <= 0); + i915_request_put(rq); + + /* Create a second ring to preempt the first ring after rq[0] */ + rq = intel_context_create_request(ce[1]); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto err_ce; + } + + rq->sched.attr.priority = I915_PRIORITY_BARRIER; + i915_request_get(rq); + i915_request_add(rq); + + err = wait_for_submit(engine, rq, HZ / 2); + i915_request_put(rq); + if (err) { + pr_err("%s: preemption request was not submitted\n", + engine->name); + err = -ETIME; + } + + pr_debug("%s: ring[0]:{ tail:%x, emit:%x }, ring[1]:{ tail:%x, emit:%x }\n", + engine->name, + ce[0]->ring->tail, ce[0]->ring->emit, + ce[1]->ring->tail, ce[1]->ring->emit); + +err_ce: + intel_engine_flush_submission(engine); + igt_spinner_end(&spin); + for (n = 0; n < ARRAY_SIZE(ce); n++) { + if (IS_ERR_OR_NULL(ce[n])) + break; + + intel_context_unpin(ce[n]); + intel_context_put(ce[n]); + } + st_engine_heartbeat_enable(engine); + if (igt_live_test_end(&t)) + err = -EIO; + if (err) + break; + } + + igt_spinner_fini(&spin); + return err; +} + static int live_pin_rewind(void *arg) { struct intel_gt *gt = arg; @@ -471,7 +606,7 @@ static int live_hold_reset(void *arg) break; } - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); rq = igt_spinner_create_request(&spin, ce, MI_ARB_CHECK); if (IS_ERR(rq)) { @@ -531,7 +666,7 @@ static int live_hold_reset(void *arg) i915_request_put(rq); out: - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); intel_context_put(ce); if (err) break; @@ -578,7 +713,7 @@ static int live_error_interrupt(void *arg) const struct error_phase *p; int err = 0; - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); for (p = phases; p->error[0] != GOOD; p++) { struct i915_request *client[ARRAY_SIZE(phases->error)]; @@ -677,7 +812,7 @@ out: } } - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); if (err) { intel_gt_set_wedged(gt); return err; @@ -845,10 +980,11 @@ static int live_timeslice_preempt(void *arg) { struct intel_gt *gt = arg; struct drm_i915_gem_object *obj; + struct intel_engine_cs *engine; + enum intel_engine_id id; struct i915_vma *vma; void *vaddr; int err = 0; - int count; /* * If a request takes too long, we would like to give other users @@ -885,26 +1021,21 @@ static int live_timeslice_preempt(void *arg) if (err) goto err_pin; - for_each_prime_number_from(count, 1, 16) { - struct intel_engine_cs *engine; - enum intel_engine_id id; - - for_each_engine(engine, gt, id) { - if (!intel_engine_has_preemption(engine)) - continue; + for_each_engine(engine, gt, id) { + if (!intel_engine_has_preemption(engine)) + continue; - memset(vaddr, 0, PAGE_SIZE); + memset(vaddr, 0, PAGE_SIZE); - engine_heartbeat_disable(engine); - err = slice_semaphore_queue(engine, vma, count); - engine_heartbeat_enable(engine); - if (err) - goto err_pin; + st_engine_heartbeat_disable(engine); + err = slice_semaphore_queue(engine, vma, 5); + st_engine_heartbeat_enable(engine); + if (err) + goto err_pin; - if (igt_flush_test(gt->i915)) { - err = -EIO; - goto err_pin; - } + if (igt_flush_test(gt->i915)) { + err = -EIO; + goto err_pin; } } @@ -1020,7 +1151,7 @@ static int live_timeslice_rewind(void *arg) * Expect execution/evaluation order XZY */ - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); timeslice = xchg(&engine->props.timeslice_duration_ms, 1); slot = memset32(engine->status_page.addr + 1000, 0, 4); @@ -1031,18 +1162,18 @@ static int live_timeslice_rewind(void *arg) goto err; } - rq[0] = create_rewinder(ce, NULL, slot, X); - if (IS_ERR(rq[0])) { + rq[A1] = create_rewinder(ce, NULL, slot, X); + if (IS_ERR(rq[A1])) { intel_context_put(ce); goto err; } - rq[1] = create_rewinder(ce, NULL, slot, Y); + rq[A2] = create_rewinder(ce, NULL, slot, Y); intel_context_put(ce); - if (IS_ERR(rq[1])) + if (IS_ERR(rq[A2])) goto err; - err = wait_for_submit(engine, rq[1], HZ / 2); + err = wait_for_submit(engine, rq[A2], HZ / 2); if (err) { pr_err("%s: failed to submit first context\n", engine->name); @@ -1055,12 +1186,12 @@ static int live_timeslice_rewind(void *arg) goto err; } - rq[2] = create_rewinder(ce, rq[0], slot, Z); + rq[B1] = create_rewinder(ce, rq[A1], slot, Z); intel_context_put(ce); if (IS_ERR(rq[2])) goto err; - err = wait_for_submit(engine, rq[2], HZ / 2); + err = wait_for_submit(engine, rq[B1], HZ / 2); if (err) { pr_err("%s: failed to submit second context\n", engine->name); @@ -1068,6 +1199,7 @@ static int live_timeslice_rewind(void *arg) } /* ELSP[] = { { A:rq1, A:rq2 }, { B:rq1 } } */ + ENGINE_TRACE(engine, "forcing tasklet for rewind\n"); if (i915_request_is_active(rq[A2])) { /* semaphore yielded! */ /* Wait for the timeslice to kick in */ del_timer(&engine->execlists.timer); @@ -1114,7 +1246,7 @@ err: wmb(); engine->props.timeslice_duration_ms = timeslice; - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); for (i = 0; i < 3; i++) i915_request_put(rq[i]); if (igt_flush_test(gt->i915)) @@ -1140,9 +1272,17 @@ static struct i915_request *nop_request(struct intel_engine_cs *engine) return rq; } -static long timeslice_threshold(const struct intel_engine_cs *engine) +static long slice_timeout(struct intel_engine_cs *engine) { - return 2 * msecs_to_jiffies_timeout(timeslice(engine)) + 1; + long timeout; + + /* Enough time for a timeslice to kick in, and kick out */ + timeout = 2 * msecs_to_jiffies_timeout(timeslice(engine)); + + /* Enough time for the nop request to complete */ + timeout += HZ / 5; + + return timeout + 1; } static int live_timeslice_queue(void *arg) @@ -1198,7 +1338,7 @@ static int live_timeslice_queue(void *arg) if (!intel_engine_has_preemption(engine)) continue; - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); memset(vaddr, 0, PAGE_SIZE); /* ELSP[0]: semaphore wait */ @@ -1243,24 +1383,8 @@ static int live_timeslice_queue(void *arg) intel_engine_flush_submission(engine); } while (READ_ONCE(engine->execlists.pending[0])); - if (!READ_ONCE(engine->execlists.timer.expires) && - execlists_active(&engine->execlists) == rq && - !i915_request_completed(rq)) { - struct drm_printer p = - drm_info_printer(gt->i915->drm.dev); - - GEM_TRACE_ERR("%s: Failed to enable timeslicing!\n", - engine->name); - intel_engine_dump(engine, &p, - "%s\n", engine->name); - GEM_TRACE_DUMP(); - - memset(vaddr, 0xff, PAGE_SIZE); - err = -EINVAL; - } - /* Timeslice every jiffy, so within 2 we should signal */ - if (i915_request_wait(rq, 0, timeslice_threshold(engine)) < 0) { + if (i915_request_wait(rq, 0, slice_timeout(engine)) < 0) { struct drm_printer p = drm_info_printer(gt->i915->drm.dev); @@ -1275,7 +1399,7 @@ static int live_timeslice_queue(void *arg) err_rq: i915_request_put(rq); err_heartbeat: - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); if (err) break; } @@ -1321,7 +1445,7 @@ static int live_timeslice_nopreempt(void *arg) break; } - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); timeslice = xchg(&engine->props.timeslice_duration_ms, 1); /* Create an unpreemptible spinner */ @@ -1349,7 +1473,7 @@ static int live_timeslice_nopreempt(void *arg) ce = intel_context_create(engine); if (IS_ERR(ce)) { - err = PTR_ERR(rq); + err = PTR_ERR(ce); goto out_spin; } @@ -1379,7 +1503,7 @@ static int live_timeslice_nopreempt(void *arg) * allow the maximum priority barrier through. Wait long * enough to see if it is timesliced in by mistake. */ - if (i915_request_wait(rq, 0, timeslice_threshold(engine)) >= 0) { + if (i915_request_wait(rq, 0, slice_timeout(engine)) >= 0) { pr_err("%s: I915_PRIORITY_BARRIER request completed, bypassing no-preempt request\n", engine->name); err = -EINVAL; @@ -1390,7 +1514,7 @@ out_spin: igt_spinner_end(&spin); out_heartbeat: xchg(&engine->props.timeslice_duration_ms, timeslice); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); if (err) break; @@ -2294,7 +2418,7 @@ static int live_suppress_self_preempt(void *arg) if (igt_flush_test(gt->i915)) goto err_wedged; - intel_engine_pm_get(engine); + st_engine_heartbeat_disable(engine); engine->execlists.preempt_hang.count = 0; rq_a = spinner_create_request(&a.spin, @@ -2302,14 +2426,14 @@ static int live_suppress_self_preempt(void *arg) MI_NOOP); if (IS_ERR(rq_a)) { err = PTR_ERR(rq_a); - intel_engine_pm_put(engine); + st_engine_heartbeat_enable(engine); goto err_client_b; } i915_request_add(rq_a); if (!igt_wait_for_spinner(&a.spin, rq_a)) { pr_err("First client failed to start\n"); - intel_engine_pm_put(engine); + st_engine_heartbeat_enable(engine); goto err_wedged; } @@ -2321,7 +2445,7 @@ static int live_suppress_self_preempt(void *arg) MI_NOOP); if (IS_ERR(rq_b)) { err = PTR_ERR(rq_b); - intel_engine_pm_put(engine); + st_engine_heartbeat_enable(engine); goto err_client_b; } i915_request_add(rq_b); @@ -2332,7 +2456,7 @@ static int live_suppress_self_preempt(void *arg) if (!igt_wait_for_spinner(&b.spin, rq_b)) { pr_err("Second client failed to start\n"); - intel_engine_pm_put(engine); + st_engine_heartbeat_enable(engine); goto err_wedged; } @@ -2346,12 +2470,12 @@ static int live_suppress_self_preempt(void *arg) engine->name, engine->execlists.preempt_hang.count, depth); - intel_engine_pm_put(engine); + st_engine_heartbeat_enable(engine); err = -EINVAL; goto err_client_b; } - intel_engine_pm_put(engine); + st_engine_heartbeat_enable(engine); if (igt_flush_test(gt->i915)) goto err_wedged; } @@ -2371,183 +2495,6 @@ err_wedged: goto err_client_b; } -static int __i915_sw_fence_call -dummy_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state) -{ - return NOTIFY_DONE; -} - -static struct i915_request *dummy_request(struct intel_engine_cs *engine) -{ - struct i915_request *rq; - - rq = kzalloc(sizeof(*rq), GFP_KERNEL); - if (!rq) - return NULL; - - rq->engine = engine; - - spin_lock_init(&rq->lock); - INIT_LIST_HEAD(&rq->fence.cb_list); - rq->fence.lock = &rq->lock; - rq->fence.ops = &i915_fence_ops; - - i915_sched_node_init(&rq->sched); - - /* mark this request as permanently incomplete */ - rq->fence.seqno = 1; - BUILD_BUG_ON(sizeof(rq->fence.seqno) != 8); /* upper 32b == 0 */ - rq->hwsp_seqno = (u32 *)&rq->fence.seqno + 1; - GEM_BUG_ON(i915_request_completed(rq)); - - i915_sw_fence_init(&rq->submit, dummy_notify); - set_bit(I915_FENCE_FLAG_ACTIVE, &rq->fence.flags); - - spin_lock_init(&rq->lock); - rq->fence.lock = &rq->lock; - INIT_LIST_HEAD(&rq->fence.cb_list); - - return rq; -} - -static void dummy_request_free(struct i915_request *dummy) -{ - /* We have to fake the CS interrupt to kick the next request */ - i915_sw_fence_commit(&dummy->submit); - - i915_request_mark_complete(dummy); - dma_fence_signal(&dummy->fence); - - i915_sched_node_fini(&dummy->sched); - i915_sw_fence_fini(&dummy->submit); - - dma_fence_free(&dummy->fence); -} - -static int live_suppress_wait_preempt(void *arg) -{ - struct intel_gt *gt = arg; - struct preempt_client client[4]; - struct i915_request *rq[ARRAY_SIZE(client)] = {}; - struct intel_engine_cs *engine; - enum intel_engine_id id; - int err = -ENOMEM; - int i; - - /* - * Waiters are given a little priority nudge, but not enough - * to actually cause any preemption. Double check that we do - * not needlessly generate preempt-to-idle cycles. - */ - - if (!HAS_LOGICAL_RING_PREEMPTION(gt->i915)) - return 0; - - if (preempt_client_init(gt, &client[0])) /* ELSP[0] */ - return -ENOMEM; - if (preempt_client_init(gt, &client[1])) /* ELSP[1] */ - goto err_client_0; - if (preempt_client_init(gt, &client[2])) /* head of queue */ - goto err_client_1; - if (preempt_client_init(gt, &client[3])) /* bystander */ - goto err_client_2; - - for_each_engine(engine, gt, id) { - int depth; - - if (!intel_engine_has_preemption(engine)) - continue; - - if (!engine->emit_init_breadcrumb) - continue; - - for (depth = 0; depth < ARRAY_SIZE(client); depth++) { - struct i915_request *dummy; - - engine->execlists.preempt_hang.count = 0; - - dummy = dummy_request(engine); - if (!dummy) - goto err_client_3; - - for (i = 0; i < ARRAY_SIZE(client); i++) { - struct i915_request *this; - - this = spinner_create_request(&client[i].spin, - client[i].ctx, engine, - MI_NOOP); - if (IS_ERR(this)) { - err = PTR_ERR(this); - goto err_wedged; - } - - /* Disable NEWCLIENT promotion */ - __i915_active_fence_set(&i915_request_timeline(this)->last_request, - &dummy->fence); - - rq[i] = i915_request_get(this); - i915_request_add(this); - } - - dummy_request_free(dummy); - - GEM_BUG_ON(i915_request_completed(rq[0])); - if (!igt_wait_for_spinner(&client[0].spin, rq[0])) { - pr_err("%s: First client failed to start\n", - engine->name); - goto err_wedged; - } - GEM_BUG_ON(!i915_request_started(rq[0])); - - if (i915_request_wait(rq[depth], - I915_WAIT_PRIORITY, - 1) != -ETIME) { - pr_err("%s: Waiter depth:%d completed!\n", - engine->name, depth); - goto err_wedged; - } - - for (i = 0; i < ARRAY_SIZE(client); i++) { - igt_spinner_end(&client[i].spin); - i915_request_put(rq[i]); - rq[i] = NULL; - } - - if (igt_flush_test(gt->i915)) - goto err_wedged; - - if (engine->execlists.preempt_hang.count) { - pr_err("%s: Preemption recorded x%d, depth %d; should have been suppressed!\n", - engine->name, - engine->execlists.preempt_hang.count, - depth); - err = -EINVAL; - goto err_client_3; - } - } - } - - err = 0; -err_client_3: - preempt_client_fini(&client[3]); -err_client_2: - preempt_client_fini(&client[2]); -err_client_1: - preempt_client_fini(&client[1]); -err_client_0: - preempt_client_fini(&client[0]); - return err; - -err_wedged: - for (i = 0; i < ARRAY_SIZE(client); i++) { - igt_spinner_end(&client[i].spin); - i915_request_put(rq[i]); - } - intel_gt_set_wedged(gt); - err = -EIO; - goto err_client_3; -} - static int live_chain_preempt(void *arg) { struct intel_gt *gt = arg; @@ -2796,6 +2743,168 @@ err_ce: return err; } +static int __live_preempt_ring(struct intel_engine_cs *engine, + struct igt_spinner *spin, + int queue_sz, int ring_sz) +{ + struct intel_context *ce[2] = {}; + struct i915_request *rq; + struct igt_live_test t; + int err = 0; + int n; + + if (igt_live_test_begin(&t, engine->i915, __func__, engine->name)) + return -EIO; + + for (n = 0; n < ARRAY_SIZE(ce); n++) { + struct intel_context *tmp; + + tmp = intel_context_create(engine); + if (IS_ERR(tmp)) { + err = PTR_ERR(tmp); + goto err_ce; + } + + tmp->ring = __intel_context_ring_size(ring_sz); + + err = intel_context_pin(tmp); + if (err) { + intel_context_put(tmp); + goto err_ce; + } + + memset32(tmp->ring->vaddr, + 0xdeadbeef, /* trigger a hang if executed */ + tmp->ring->vma->size / sizeof(u32)); + + ce[n] = tmp; + } + + rq = igt_spinner_create_request(spin, ce[0], MI_ARB_CHECK); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto err_ce; + } + + i915_request_get(rq); + rq->sched.attr.priority = I915_PRIORITY_BARRIER; + i915_request_add(rq); + + if (!igt_wait_for_spinner(spin, rq)) { + intel_gt_set_wedged(engine->gt); + i915_request_put(rq); + err = -ETIME; + goto err_ce; + } + + /* Fill the ring, until we will cause a wrap */ + n = 0; + while (ce[0]->ring->tail - rq->wa_tail <= queue_sz) { + struct i915_request *tmp; + + tmp = intel_context_create_request(ce[0]); + if (IS_ERR(tmp)) { + err = PTR_ERR(tmp); + i915_request_put(rq); + goto err_ce; + } + + i915_request_add(tmp); + intel_engine_flush_submission(engine); + n++; + } + intel_engine_flush_submission(engine); + pr_debug("%s: Filled %d with %d nop tails {size:%x, tail:%x, emit:%x, rq.tail:%x}\n", + engine->name, queue_sz, n, + ce[0]->ring->size, + ce[0]->ring->tail, + ce[0]->ring->emit, + rq->tail); + i915_request_put(rq); + + /* Create a second request to preempt the first ring */ + rq = intel_context_create_request(ce[1]); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto err_ce; + } + + rq->sched.attr.priority = I915_PRIORITY_BARRIER; + i915_request_get(rq); + i915_request_add(rq); + + err = wait_for_submit(engine, rq, HZ / 2); + i915_request_put(rq); + if (err) { + pr_err("%s: preemption request was not submited\n", + engine->name); + err = -ETIME; + } + + pr_debug("%s: ring[0]:{ tail:%x, emit:%x }, ring[1]:{ tail:%x, emit:%x }\n", + engine->name, + ce[0]->ring->tail, ce[0]->ring->emit, + ce[1]->ring->tail, ce[1]->ring->emit); + +err_ce: + intel_engine_flush_submission(engine); + igt_spinner_end(spin); + for (n = 0; n < ARRAY_SIZE(ce); n++) { + if (IS_ERR_OR_NULL(ce[n])) + break; + + intel_context_unpin(ce[n]); + intel_context_put(ce[n]); + } + if (igt_live_test_end(&t)) + err = -EIO; + return err; +} + +static int live_preempt_ring(void *arg) +{ + struct intel_gt *gt = arg; + struct intel_engine_cs *engine; + struct igt_spinner spin; + enum intel_engine_id id; + int err = 0; + + /* + * Check that we rollback large chunks of a ring in order to do a + * preemption event. Similar to live_unlite_ring, but looking at + * ring size rather than the impact of intel_ring_direction(). + */ + + if (igt_spinner_init(&spin, gt)) + return -ENOMEM; + + for_each_engine(engine, gt, id) { + int n; + + if (!intel_engine_has_preemption(engine)) + continue; + + if (!intel_engine_can_store_dword(engine)) + continue; + + st_engine_heartbeat_disable(engine); + + for (n = 0; n <= 3; n++) { + err = __live_preempt_ring(engine, &spin, + n * SZ_4K / 4, SZ_4K); + if (err) + break; + } + + st_engine_heartbeat_enable(engine); + if (err) + break; + } + + igt_spinner_fini(&spin); + return err; +} + static int live_preempt_gang(void *arg) { struct intel_gt *gt = arg; @@ -2840,16 +2949,8 @@ static int live_preempt_gang(void *arg) /* Submit each spinner at increasing priority */ engine->schedule(rq, &attr); - - if (prio <= I915_PRIORITY_MAX) - continue; - - if (prio > (INT_MAX >> I915_USER_PRIORITY_SHIFT)) - break; - - if (__igt_timeout(end_time, NULL)) - break; - } while (1); + } while (prio <= I915_PRIORITY_MAX && + !__igt_timeout(end_time, NULL)); pr_debug("%s: Preempt chain of %d requests\n", engine->name, prio); @@ -3417,7 +3518,7 @@ static int smoke_crescendo_thread(void *arg) return err; count++; - } while (!__igt_timeout(end_time, NULL)); + } while (count < smoke->ncontext && !__igt_timeout(end_time, NULL)); smoke->count = count; return 0; @@ -3493,7 +3594,7 @@ static int smoke_random(struct preempt_smoke *smoke, unsigned int flags) count++; } - } while (!__igt_timeout(end_time, NULL)); + } while (count < smoke->ncontext && !__igt_timeout(end_time, NULL)); pr_info("Submitted %lu random:%x requests across %d engines and %d contexts\n", count, flags, @@ -3506,7 +3607,7 @@ static int live_preempt_smoke(void *arg) struct preempt_smoke smoke = { .gt = arg, .prng = I915_RND_STATE_INITIALIZER(i915_selftest.random_seed), - .ncontext = 1024, + .ncontext = 256, }; const unsigned int phase[] = { 0, BATCH }; struct igt_live_test t; @@ -3706,13 +3807,43 @@ out: return err; } +static unsigned int +__select_siblings(struct intel_gt *gt, + unsigned int class, + struct intel_engine_cs **siblings, + bool (*filter)(const struct intel_engine_cs *)) +{ + unsigned int n = 0; + unsigned int inst; + + for (inst = 0; inst <= MAX_ENGINE_INSTANCE; inst++) { + if (!gt->engine_class[class][inst]) + continue; + + if (filter && !filter(gt->engine_class[class][inst])) + continue; + + siblings[n++] = gt->engine_class[class][inst]; + } + + return n; +} + +static unsigned int +select_siblings(struct intel_gt *gt, + unsigned int class, + struct intel_engine_cs **siblings) +{ + return __select_siblings(gt, class, siblings, NULL); +} + static int live_virtual_engine(void *arg) { struct intel_gt *gt = arg; struct intel_engine_cs *siblings[MAX_ENGINE_INSTANCE + 1]; struct intel_engine_cs *engine; enum intel_engine_id id; - unsigned int class, inst; + unsigned int class; int err; if (intel_uc_uses_guc_submission(>->uc)) @@ -3730,13 +3861,7 @@ static int live_virtual_engine(void *arg) for (class = 0; class <= MAX_ENGINE_CLASS; class++) { int nsibling, n; - nsibling = 0; - for (inst = 0; inst <= MAX_ENGINE_INSTANCE; inst++) { - if (!gt->engine_class[class][inst]) - continue; - - siblings[nsibling++] = gt->engine_class[class][inst]; - } + nsibling = select_siblings(gt, class, siblings); if (nsibling < 2) continue; @@ -3845,7 +3970,7 @@ static int live_virtual_mask(void *arg) { struct intel_gt *gt = arg; struct intel_engine_cs *siblings[MAX_ENGINE_INSTANCE + 1]; - unsigned int class, inst; + unsigned int class; int err; if (intel_uc_uses_guc_submission(>->uc)) @@ -3854,17 +3979,178 @@ static int live_virtual_mask(void *arg) for (class = 0; class <= MAX_ENGINE_CLASS; class++) { unsigned int nsibling; - nsibling = 0; - for (inst = 0; inst <= MAX_ENGINE_INSTANCE; inst++) { - if (!gt->engine_class[class][inst]) - break; + nsibling = select_siblings(gt, class, siblings); + if (nsibling < 2) + continue; + + err = mask_virtual_engine(gt, siblings, nsibling); + if (err) + return err; + } + + return 0; +} + +static int slicein_virtual_engine(struct intel_gt *gt, + struct intel_engine_cs **siblings, + unsigned int nsibling) +{ + const long timeout = slice_timeout(siblings[0]); + struct intel_context *ce; + struct i915_request *rq; + struct igt_spinner spin; + unsigned int n; + int err = 0; + + /* + * Virtual requests must take part in timeslicing on the target engines. + */ + + if (igt_spinner_init(&spin, gt)) + return -ENOMEM; + + for (n = 0; n < nsibling; n++) { + ce = intel_context_create(siblings[n]); + if (IS_ERR(ce)) { + err = PTR_ERR(ce); + goto out; + } + + rq = igt_spinner_create_request(&spin, ce, MI_ARB_CHECK); + intel_context_put(ce); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto out; + } + + i915_request_add(rq); + } + + ce = intel_execlists_create_virtual(siblings, nsibling); + if (IS_ERR(ce)) { + err = PTR_ERR(ce); + goto out; + } + + rq = intel_context_create_request(ce); + intel_context_put(ce); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto out; + } + + i915_request_get(rq); + i915_request_add(rq); + if (i915_request_wait(rq, 0, timeout) < 0) { + GEM_TRACE_ERR("%s(%s) failed to slice in virtual request\n", + __func__, rq->engine->name); + GEM_TRACE_DUMP(); + intel_gt_set_wedged(gt); + err = -EIO; + } + i915_request_put(rq); + +out: + igt_spinner_end(&spin); + if (igt_flush_test(gt->i915)) + err = -EIO; + igt_spinner_fini(&spin); + return err; +} + +static int sliceout_virtual_engine(struct intel_gt *gt, + struct intel_engine_cs **siblings, + unsigned int nsibling) +{ + const long timeout = slice_timeout(siblings[0]); + struct intel_context *ce; + struct i915_request *rq; + struct igt_spinner spin; + unsigned int n; + int err = 0; + + /* + * Virtual requests must allow others a fair timeslice. + */ + + if (igt_spinner_init(&spin, gt)) + return -ENOMEM; + + /* XXX We do not handle oversubscription and fairness with normal rq */ + for (n = 0; n < nsibling; n++) { + ce = intel_execlists_create_virtual(siblings, nsibling); + if (IS_ERR(ce)) { + err = PTR_ERR(ce); + goto out; + } + + rq = igt_spinner_create_request(&spin, ce, MI_ARB_CHECK); + intel_context_put(ce); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto out; + } + + i915_request_add(rq); + } + + for (n = 0; !err && n < nsibling; n++) { + ce = intel_context_create(siblings[n]); + if (IS_ERR(ce)) { + err = PTR_ERR(ce); + goto out; + } + + rq = intel_context_create_request(ce); + intel_context_put(ce); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto out; + } - siblings[nsibling++] = gt->engine_class[class][inst]; + i915_request_get(rq); + i915_request_add(rq); + if (i915_request_wait(rq, 0, timeout) < 0) { + GEM_TRACE_ERR("%s(%s) failed to slice out virtual request\n", + __func__, siblings[n]->name); + GEM_TRACE_DUMP(); + intel_gt_set_wedged(gt); + err = -EIO; } + i915_request_put(rq); + } + +out: + igt_spinner_end(&spin); + if (igt_flush_test(gt->i915)) + err = -EIO; + igt_spinner_fini(&spin); + return err; +} + +static int live_virtual_slice(void *arg) +{ + struct intel_gt *gt = arg; + struct intel_engine_cs *siblings[MAX_ENGINE_INSTANCE + 1]; + unsigned int class; + int err; + + if (intel_uc_uses_guc_submission(>->uc)) + return 0; + + for (class = 0; class <= MAX_ENGINE_CLASS; class++) { + unsigned int nsibling; + + nsibling = __select_siblings(gt, class, siblings, + intel_engine_has_timeslices); if (nsibling < 2) continue; - err = mask_virtual_engine(gt, siblings, nsibling); + err = slicein_virtual_engine(gt, siblings, nsibling); + if (err) + return err; + + err = sliceout_virtual_engine(gt, siblings, nsibling); if (err) return err; } @@ -3982,7 +4268,7 @@ static int live_virtual_preserved(void *arg) { struct intel_gt *gt = arg; struct intel_engine_cs *siblings[MAX_ENGINE_INSTANCE + 1]; - unsigned int class, inst; + unsigned int class; /* * Check that the context image retains non-privileged (user) registers @@ -4000,13 +4286,7 @@ static int live_virtual_preserved(void *arg) for (class = 0; class <= MAX_ENGINE_CLASS; class++) { int nsibling, err; - nsibling = 0; - for (inst = 0; inst <= MAX_ENGINE_INSTANCE; inst++) { - if (!gt->engine_class[class][inst]) - continue; - - siblings[nsibling++] = gt->engine_class[class][inst]; - } + nsibling = select_siblings(gt, class, siblings); if (nsibling < 2) continue; @@ -4217,7 +4497,7 @@ static int live_virtual_bond(void *arg) }; struct intel_gt *gt = arg; struct intel_engine_cs *siblings[MAX_ENGINE_INSTANCE + 1]; - unsigned int class, inst; + unsigned int class; int err; if (intel_uc_uses_guc_submission(>->uc)) @@ -4227,14 +4507,7 @@ static int live_virtual_bond(void *arg) const struct phase *p; int nsibling; - nsibling = 0; - for (inst = 0; inst <= MAX_ENGINE_INSTANCE; inst++) { - if (!gt->engine_class[class][inst]) - break; - - GEM_BUG_ON(nsibling == ARRAY_SIZE(siblings)); - siblings[nsibling++] = gt->engine_class[class][inst]; - } + nsibling = select_siblings(gt, class, siblings); if (nsibling < 2) continue; @@ -4280,7 +4553,7 @@ static int reset_virtual_engine(struct intel_gt *gt, } for (n = 0; n < nsibling; n++) - engine_heartbeat_disable(siblings[n]); + st_engine_heartbeat_disable(siblings[n]); rq = igt_spinner_create_request(&spin, ve, MI_ARB_CHECK); if (IS_ERR(rq)) { @@ -4351,7 +4624,7 @@ out_rq: i915_request_put(rq); out_heartbeat: for (n = 0; n < nsibling; n++) - engine_heartbeat_enable(siblings[n]); + st_engine_heartbeat_enable(siblings[n]); intel_context_put(ve); out_spin: @@ -4363,7 +4636,7 @@ static int live_virtual_reset(void *arg) { struct intel_gt *gt = arg; struct intel_engine_cs *siblings[MAX_ENGINE_INSTANCE + 1]; - unsigned int class, inst; + unsigned int class; /* * Check that we handle a reset event within a virtual engine. @@ -4381,13 +4654,7 @@ static int live_virtual_reset(void *arg) for (class = 0; class <= MAX_ENGINE_CLASS; class++) { int nsibling, err; - nsibling = 0; - for (inst = 0; inst <= MAX_ENGINE_INSTANCE; inst++) { - if (!gt->engine_class[class][inst]) - continue; - - siblings[nsibling++] = gt->engine_class[class][inst]; - } + nsibling = select_siblings(gt, class, siblings); if (nsibling < 2) continue; @@ -4405,6 +4672,7 @@ int intel_execlists_live_selftests(struct drm_i915_private *i915) SUBTEST(live_sanitycheck), SUBTEST(live_unlite_switch), SUBTEST(live_unlite_preempt), + SUBTEST(live_unlite_ring), SUBTEST(live_pin_rewind), SUBTEST(live_hold_reset), SUBTEST(live_error_interrupt), @@ -4418,8 +4686,8 @@ int intel_execlists_live_selftests(struct drm_i915_private *i915) SUBTEST(live_nopreempt), SUBTEST(live_preempt_cancel), SUBTEST(live_suppress_self_preempt), - SUBTEST(live_suppress_wait_preempt), SUBTEST(live_chain_preempt), + SUBTEST(live_preempt_ring), SUBTEST(live_preempt_gang), SUBTEST(live_preempt_timeout), SUBTEST(live_preempt_user), @@ -4427,6 +4695,7 @@ int intel_execlists_live_selftests(struct drm_i915_private *i915) SUBTEST(live_virtual_engine), SUBTEST(live_virtual_mask), SUBTEST(live_virtual_preserved), + SUBTEST(live_virtual_slice), SUBTEST(live_virtual_bond), SUBTEST(live_virtual_reset), }; @@ -5030,7 +5299,7 @@ static int live_lrc_gpr(void *arg) return PTR_ERR(scratch); for_each_engine(engine, gt, id) { - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); err = __live_lrc_gpr(engine, scratch, false); if (err) @@ -5041,7 +5310,7 @@ static int live_lrc_gpr(void *arg) goto err; err: - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); if (igt_flush_test(gt->i915)) err = -EIO; if (err) @@ -5190,7 +5459,7 @@ static int live_lrc_timestamp(void *arg) for_each_engine(data.engine, gt, id) { int i, err = 0; - engine_heartbeat_disable(data.engine); + st_engine_heartbeat_disable(data.engine); for (i = 0; i < ARRAY_SIZE(data.ce); i++) { struct intel_context *tmp; @@ -5223,7 +5492,7 @@ static int live_lrc_timestamp(void *arg) } err: - engine_heartbeat_enable(data.engine); + st_engine_heartbeat_enable(data.engine); for (i = 0; i < ARRAY_SIZE(data.ce); i++) { if (!data.ce[i]) break; diff --git a/drivers/gpu/drm/i915/gt/selftest_mocs.c b/drivers/gpu/drm/i915/gt/selftest_mocs.c index 63f87d8608c3..b25eba50c88e 100644 --- a/drivers/gpu/drm/i915/gt/selftest_mocs.c +++ b/drivers/gpu/drm/i915/gt/selftest_mocs.c @@ -157,7 +157,7 @@ static int read_mocs_table(struct i915_request *rq, { u32 addr; - if (HAS_GLOBAL_MOCS_REGISTERS(rq->i915)) + if (HAS_GLOBAL_MOCS_REGISTERS(rq->engine->i915)) addr = global_mocs_offset(); else addr = mocs_offset(rq->engine); diff --git a/drivers/gpu/drm/i915/gt/selftest_rc6.c b/drivers/gpu/drm/i915/gt/selftest_rc6.c index 2dc460624bbc..3c8434846fa1 100644 --- a/drivers/gpu/drm/i915/gt/selftest_rc6.c +++ b/drivers/gpu/drm/i915/gt/selftest_rc6.c @@ -132,7 +132,7 @@ static const u32 *__live_rc6_ctx(struct intel_context *ce) } cmd = MI_STORE_REGISTER_MEM | MI_USE_GGTT; - if (INTEL_GEN(rq->i915) >= 8) + if (INTEL_GEN(rq->engine->i915) >= 8) cmd++; *cs++ = cmd; @@ -197,10 +197,10 @@ int live_rc6_ctx_wa(void *arg) int pass; for (pass = 0; pass < 2; pass++) { + struct i915_gpu_error *error = >->i915->gpu_error; struct intel_context *ce; unsigned int resets = - i915_reset_engine_count(>->i915->gpu_error, - engine); + i915_reset_engine_count(error, engine); const u32 *res; /* Use a sacrifical context */ @@ -230,8 +230,7 @@ int live_rc6_ctx_wa(void *arg) engine->name, READ_ONCE(*res)); if (resets != - i915_reset_engine_count(>->i915->gpu_error, - engine)) { + i915_reset_engine_count(error, engine)) { pr_err("%s: GPU reset required\n", engine->name); add_taint_for_CI(TAINT_WARN); diff --git a/drivers/gpu/drm/i915/gt/selftest_rps.c b/drivers/gpu/drm/i915/gt/selftest_rps.c index 5049c3dd08a6..bb753f0c12eb 100644 --- a/drivers/gpu/drm/i915/gt/selftest_rps.c +++ b/drivers/gpu/drm/i915/gt/selftest_rps.c @@ -12,6 +12,7 @@ #include "intel_gt_clock_utils.h" #include "intel_gt_pm.h" #include "intel_rc6.h" +#include "selftest_engine_heartbeat.h" #include "selftest_rps.h" #include "selftests/igt_flush_test.h" #include "selftests/igt_spinner.h" @@ -20,22 +21,6 @@ /* Try to isolate the impact of cstates from determing frequency response */ #define CPU_LATENCY 0 /* -1 to disable pm_qos, 0 to disable cstates */ -static void engine_heartbeat_disable(struct intel_engine_cs *engine) -{ - engine->props.heartbeat_interval_ms = 0; - - intel_engine_pm_get(engine); - intel_engine_park_heartbeat(engine); -} - -static void engine_heartbeat_enable(struct intel_engine_cs *engine) -{ - intel_engine_pm_put(engine); - - engine->props.heartbeat_interval_ms = - engine->defaults.heartbeat_interval_ms; -} - static void dummy_rps_work(struct work_struct *wrk) { } @@ -249,13 +234,13 @@ int live_rps_clock_interval(void *arg) if (!intel_engine_can_store_dword(engine)) continue; - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); rq = igt_spinner_create_request(&spin, engine->kernel_context, MI_NOOP); if (IS_ERR(rq)) { - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); err = PTR_ERR(rq); break; } @@ -266,7 +251,7 @@ int live_rps_clock_interval(void *arg) pr_err("%s: RPS spinner did not start\n", engine->name); igt_spinner_end(&spin); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); intel_gt_set_wedged(engine->gt); err = -EIO; break; @@ -322,7 +307,7 @@ int live_rps_clock_interval(void *arg) intel_uncore_forcewake_put(gt->uncore, FORCEWAKE_ALL); igt_spinner_end(&spin); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); if (err == 0) { u64 time = intel_gt_pm_interval_to_ns(gt, cycles); @@ -408,7 +393,7 @@ int live_rps_control(void *arg) if (!intel_engine_can_store_dword(engine)) continue; - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); rq = igt_spinner_create_request(&spin, engine->kernel_context, @@ -424,7 +409,7 @@ int live_rps_control(void *arg) pr_err("%s: RPS spinner did not start\n", engine->name); igt_spinner_end(&spin); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); intel_gt_set_wedged(engine->gt); err = -EIO; break; @@ -434,7 +419,7 @@ int live_rps_control(void *arg) pr_err("%s: could not set minimum frequency [%x], only %x!\n", engine->name, rps->min_freq, read_cagf(rps)); igt_spinner_end(&spin); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); show_pstate_limits(rps); err = -EINVAL; break; @@ -451,7 +436,7 @@ int live_rps_control(void *arg) pr_err("%s: could not restore minimum frequency [%x], only %x!\n", engine->name, rps->min_freq, read_cagf(rps)); igt_spinner_end(&spin); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); show_pstate_limits(rps); err = -EINVAL; break; @@ -466,7 +451,7 @@ int live_rps_control(void *arg) min_dt = ktime_sub(ktime_get(), min_dt); igt_spinner_end(&spin); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); pr_info("%s: range:[%x:%uMHz, %x:%uMHz] limit:[%x:%uMHz], %x:%x response %lluns:%lluns\n", engine->name, @@ -637,14 +622,14 @@ int live_rps_frequency_cs(void *arg) int freq; } min, max; - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); vma = create_spin_counter(engine, engine->kernel_context->vm, false, &cancel, &cntr); if (IS_ERR(vma)) { err = PTR_ERR(vma); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); break; } @@ -725,7 +710,7 @@ err_vma: i915_vma_unpin(vma); i915_vma_put(vma); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); if (igt_flush_test(gt->i915)) err = -EIO; if (err) @@ -779,14 +764,14 @@ int live_rps_frequency_srm(void *arg) int freq; } min, max; - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); vma = create_spin_counter(engine, engine->kernel_context->vm, true, &cancel, &cntr); if (IS_ERR(vma)) { err = PTR_ERR(vma); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); break; } @@ -866,7 +851,7 @@ err_vma: i915_vma_unpin(vma); i915_vma_put(vma); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); if (igt_flush_test(gt->i915)) err = -EIO; if (err) @@ -1061,11 +1046,11 @@ int live_rps_interrupt(void *arg) intel_gt_pm_wait_for_idle(engine->gt); GEM_BUG_ON(intel_rps_is_active(rps)); - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); err = __rps_up_interrupt(rps, engine, &spin); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); if (err) goto out; @@ -1074,13 +1059,13 @@ int live_rps_interrupt(void *arg) /* Keep the engine awake but idle and check for DOWN */ if (pm_events & GEN6_PM_RP_DOWN_THRESHOLD) { - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); intel_rc6_disable(>->rc6); err = __rps_down_interrupt(rps, engine); intel_rc6_enable(>->rc6); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); if (err) goto out; } @@ -1165,13 +1150,13 @@ int live_rps_power(void *arg) if (!intel_engine_can_store_dword(engine)) continue; - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); rq = igt_spinner_create_request(&spin, engine->kernel_context, MI_NOOP); if (IS_ERR(rq)) { - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); err = PTR_ERR(rq); break; } @@ -1182,7 +1167,7 @@ int live_rps_power(void *arg) pr_err("%s: RPS spinner did not start\n", engine->name); igt_spinner_end(&spin); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); intel_gt_set_wedged(engine->gt); err = -EIO; break; @@ -1195,7 +1180,7 @@ int live_rps_power(void *arg) min.power = measure_power_at(rps, &min.freq); igt_spinner_end(&spin); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); pr_info("%s: min:%llumW @ %uMHz, max:%llumW @ %uMHz\n", engine->name, @@ -1252,6 +1237,11 @@ int live_rps_dynamic(void *arg) if (igt_spinner_init(&spin, gt)) return -ENOMEM; + if (intel_rps_has_interrupts(rps)) + pr_info("RPS has interrupt support\n"); + if (intel_rps_uses_timer(rps)) + pr_info("RPS has timer support\n"); + for_each_engine(engine, gt, id) { struct i915_request *rq; struct { diff --git a/drivers/gpu/drm/i915/gt/selftest_timeline.c b/drivers/gpu/drm/i915/gt/selftest_timeline.c index ef1c35073dc0..fcdee951579b 100644 --- a/drivers/gpu/drm/i915/gt/selftest_timeline.c +++ b/drivers/gpu/drm/i915/gt/selftest_timeline.c @@ -12,6 +12,7 @@ #include "intel_gt.h" #include "intel_gt_requests.h" #include "intel_ring.h" +#include "selftest_engine_heartbeat.h" #include "../selftests/i915_random.h" #include "../i915_selftest.h" @@ -426,12 +427,12 @@ static int emit_ggtt_store_dw(struct i915_request *rq, u32 addr, u32 value) if (IS_ERR(cs)) return PTR_ERR(cs); - if (INTEL_GEN(rq->i915) >= 8) { + if (INTEL_GEN(rq->engine->i915) >= 8) { *cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT; *cs++ = addr; *cs++ = 0; *cs++ = value; - } else if (INTEL_GEN(rq->i915) >= 4) { + } else if (INTEL_GEN(rq->engine->i915) >= 4) { *cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT; *cs++ = 0; *cs++ = addr; @@ -751,22 +752,6 @@ out_free: return err; } -static void engine_heartbeat_disable(struct intel_engine_cs *engine) -{ - engine->props.heartbeat_interval_ms = 0; - - intel_engine_pm_get(engine); - intel_engine_park_heartbeat(engine); -} - -static void engine_heartbeat_enable(struct intel_engine_cs *engine) -{ - intel_engine_pm_put(engine); - - engine->props.heartbeat_interval_ms = - engine->defaults.heartbeat_interval_ms; -} - static int live_hwsp_rollover_kernel(void *arg) { struct intel_gt *gt = arg; @@ -785,7 +770,7 @@ static int live_hwsp_rollover_kernel(void *arg) struct i915_request *rq[3] = {}; int i; - engine_heartbeat_disable(engine); + st_engine_heartbeat_disable(engine); if (intel_gt_wait_for_idle(gt, HZ / 2)) { err = -EIO; goto out; @@ -836,7 +821,7 @@ static int live_hwsp_rollover_kernel(void *arg) out: for (i = 0; i < ARRAY_SIZE(rq); i++) i915_request_put(rq[i]); - engine_heartbeat_enable(engine); + st_engine_heartbeat_enable(engine); if (err) break; } diff --git a/drivers/gpu/drm/i915/gt/selftest_workarounds.c b/drivers/gpu/drm/i915/gt/selftest_workarounds.c index 32785463ec9e..febc9e6692ba 100644 --- a/drivers/gpu/drm/i915/gt/selftest_workarounds.c +++ b/drivers/gpu/drm/i915/gt/selftest_workarounds.c @@ -417,6 +417,20 @@ static bool wo_register(struct intel_engine_cs *engine, u32 reg) return false; } +static bool timestamp(const struct intel_engine_cs *engine, u32 reg) +{ + reg = (reg - engine->mmio_base) & ~RING_FORCE_TO_NONPRIV_ACCESS_MASK; + switch (reg) { + case 0x358: + case 0x35c: + case 0x3a8: + return true; + + default: + return false; + } +} + static bool ro_register(u32 reg) { if ((reg & RING_FORCE_TO_NONPRIV_ACCESS_MASK) == @@ -497,6 +511,9 @@ static int check_dirty_whitelist(struct intel_context *ce) if (wo_register(engine, reg)) continue; + if (timestamp(engine, reg)) + continue; /* timestamps are expected to autoincrement */ + ro_reg = ro_register(reg); /* Clear non priv flags */ diff --git a/drivers/gpu/drm/i915/gt/shaders/README b/drivers/gpu/drm/i915/gt/shaders/README new file mode 100644 index 000000000000..e7e96d7073c7 --- /dev/null +++ b/drivers/gpu/drm/i915/gt/shaders/README @@ -0,0 +1,46 @@ +ASM sources for auto generated shaders +====================================== + +The i915/gt/hsw_clear_kernel.c and i915/gt/ivb_clear_kernel.c files contain +pre-compiled batch chunks that will clear any residual render cache during +context switch. + +They are generated from their respective platform ASM files present on +i915/gt/shaders/clear_kernel directory. + +The generated .c files should never be modified directly. Instead, any modification +needs to be done on the on their respective ASM files and build instructions below +needes to be followed. + +Building +======== + +Environment +----------- + +IGT GPU tool scripts and the Mesa's i965 instruction assembler tool are used +on building. + +Please make sure your Mesa tool is compiled with "-Dtools=intel" and +"-Ddri-drivers=i965", and run this script from IGT source root directory" + +The instructions bellow assume: + * IGT gpu tools source code is located on your home directory (~) as ~/igt + * Mesa source code is located on your home directory (~) as ~/mesa + and built under the ~/mesa/build directory + * Linux kernel source code is under your home directory (~) as ~/linux + +Instructions +------------ + +~ $ cp ~/linux/drivers/gpu/drm/i915/gt/shaders/clear_kernel/ivb.asm \ + ~/igt/lib/i915/shaders/clear_kernel/ivb.asm +~ $ cd ~/igt +igt $ ./scripts/generate_clear_kernel.sh -g ivb \ + -m ~/mesa/build/src/intel/tools/i965_asm + +~ $ cp ~/linux/drivers/gpu/drm/i915/gt/shaders/clear_kernel/hsw.asm \ + ~/igt/lib/i915/shaders/clear_kernel/hsw.asm +~ $ cd ~/igt +igt $ ./scripts/generate_clear_kernel.sh -g hsw \ + -m ~/mesa/build/src/intel/tools/i965_asm
\ No newline at end of file diff --git a/drivers/gpu/drm/i915/gt/shaders/clear_kernel/hsw.asm b/drivers/gpu/drm/i915/gt/shaders/clear_kernel/hsw.asm new file mode 100644 index 000000000000..5fdf384bb621 --- /dev/null +++ b/drivers/gpu/drm/i915/gt/shaders/clear_kernel/hsw.asm @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2020 Intel Corporation + */ + +/* + * Kernel for PAVP buffer clear. + * + * 1. Clear all 64 GRF registers assigned to the kernel with designated value; + * 2. Write 32x16 block of all "0" to render target buffer which indirectly clears + * 512 bytes of Render Cache. + */ + +/* Store designated "clear GRF" value */ +mov(1) f0.1<1>UW g1.2<0,1,0>UW { align1 1N }; + +/** + * Curbe Format + * + * DW 1.0 - Block Offset to write Render Cache + * DW 1.1 [15:0] - Clear Word + * DW 1.2 - Delay iterations + * DW 1.3 - Enable Instrumentation (only for debug) + * DW 1.4 - Rsvd (intended for context ID) + * DW 1.5 - [31:16]:SliceCount, [15:0]:SubSlicePerSliceCount + * DW 1.6 - Rsvd MBZ (intended for Enable Wait on Total Thread Count) + * DW 1.7 - Rsvd MBZ (inteded for Total Thread Count) + * + * Binding Table + * + * BTI 0: 2D Surface to help clear L3 (Render/Data Cache) + * BTI 1: Wait/Instrumentation Buffer + * Size : (SliceCount * SubSliceCount * 16 EUs/SubSlice) rows * (16 threads/EU) cols (Format R32_UINT) + * Expected to be initialized to 0 by driver/another kernel + * Layout: + * RowN: Histogram for EU-N: (SliceID*SubSlicePerSliceCount + SSID)*16 + EUID [assume max 16 EUs / SS] + * Col-k[DW-k]: Threads Executed on ThreadID-k for EU-N + */ +add(1) g1.2<1>UD g1.2<0,1,0>UD 0x00000001UD { align1 1N }; /* Loop count to delay kernel: Init to (g1.2 + 1) */ +cmp.z.f0.0(1) null<1>UD g1.3<0,1,0>UD 0x00000000UD { align1 1N }; +(+f0.0) jmpi(1) 352D { align1 WE_all 1N }; + +/** + * State Register has info on where this thread is running + * IVB: sr0.0 :: [15:13]: MBZ, 12: HSID (Half-Slice ID), [11:8]EUID, [2:0] ThreadSlotID + * HSW: sr0.0 :: 15: MBZ, [14:13]: SliceID, 12: HSID (Half-Slice ID), [11:8]EUID, [2:0] ThreadSlotID + */ +mov(8) g3<1>UD 0x00000000UD { align1 1Q }; +shr(1) g3<1>D sr0<0,1,0>D 12D { align1 1N }; +and(1) g3<1>D g3<0,1,0>D 1D { align1 1N }; /* g3 has HSID */ +shr(1) g3.1<1>D sr0<0,1,0>D 13D { align1 1N }; +and(1) g3.1<1>D g3.1<0,1,0>D 3D { align1 1N }; /* g3.1 has sliceID */ +mul(1) g3.5<1>D g3.1<0,1,0>D g1.10<0,1,0>UW { align1 1N }; +add(1) g3<1>D g3<0,1,0>D g3.5<0,1,0>D { align1 1N }; /* g3 = sliceID * SubSlicePerSliceCount + HSID */ +shr(1) g3.2<1>D sr0<0,1,0>D 8D { align1 1N }; +and(1) g3.2<1>D g3.2<0,1,0>D 15D { align1 1N }; /* g3.2 = EUID */ +mul(1) g3.4<1>D g3<0,1,0>D 16D { align1 1N }; +add(1) g3.2<1>D g3.2<0,1,0>D g3.4<0,1,0>D { align1 1N }; /* g3.2 now points to EU row number (Y-pixel = V address ) in instrumentation surf */ + +mov(8) g5<1>UD 0x00000000UD { align1 1Q }; +and(1) g3.3<1>D sr0<0,1,0>D 7D { align1 1N }; +mul(1) g3.3<1>D g3.3<0,1,0>D 4D { align1 1N }; + +mov(8) g4<1>UD g0<8,8,1>UD { align1 1Q }; /* Initialize message header with g0 */ +mov(1) g4<1>UD g3.3<0,1,0>UD { align1 1N }; /* Block offset */ +mov(1) g4.1<1>UD g3.2<0,1,0>UD { align1 1N }; /* Block offset */ +mov(1) g4.2<1>UD 0x00000003UD { align1 1N }; /* Block size (1 row x 4 bytes) */ +and(1) g4.3<1>UD g4.3<0,1,0>UW 0xffffffffUD { align1 1N }; + +/* Media block read to fetch current value at specified location in instrumentation buffer */ +sendc(8) g5<1>UD g4<8,8,1>F 0x02190001 + + render MsgDesc: media block read MsgCtrl = 0x0 Surface = 1 mlen 1 rlen 1 { align1 1Q }; +add(1) g5<1>D g5<0,1,0>D 1D { align1 1N }; + +/* Media block write for updated value at specified location in instrumentation buffer */ +sendc(8) g5<1>UD g4<8,8,1>F 0x040a8001 + render MsgDesc: media block write MsgCtrl = 0x0 Surface = 1 mlen 2 rlen 0 { align1 1Q }; + +/* Delay thread for specified parameter */ +add.nz.f0.0(1) g1.2<1>UD g1.2<0,1,0>UD -1D { align1 1N }; +(+f0.0) jmpi(1) -32D { align1 WE_all 1N }; + +/* Store designated "clear GRF" value */ +mov(1) f0.1<1>UW g1.2<0,1,0>UW { align1 1N }; + +/* Initialize looping parameters */ +mov(1) a0<1>D 0D { align1 1N }; /* Initialize a0.0:w=0 */ +mov(1) a0.4<1>W 127W { align1 1N }; /* Loop count. Each loop contains 16 GRF's */ + +/* Write 32x16 all "0" block */ +mov(8) g2<1>UD g0<8,8,1>UD { align1 1Q }; +mov(8) g127<1>UD g0<8,8,1>UD { align1 1Q }; +mov(2) g2<1>UD g1<2,2,1>UW { align1 1N }; +mov(1) g2.2<1>UD 0x000f000fUD { align1 1N }; /* Block size (16x16) */ +and(1) g2.3<1>UD g2.3<0,1,0>UW 0xffffffefUD { align1 1N }; +mov(16) g3<1>UD 0x00000000UD { align1 1H }; +mov(16) g4<1>UD 0x00000000UD { align1 1H }; +mov(16) g5<1>UD 0x00000000UD { align1 1H }; +mov(16) g6<1>UD 0x00000000UD { align1 1H }; +mov(16) g7<1>UD 0x00000000UD { align1 1H }; +mov(16) g8<1>UD 0x00000000UD { align1 1H }; +mov(16) g9<1>UD 0x00000000UD { align1 1H }; +mov(16) g10<1>UD 0x00000000UD { align1 1H }; +sendc(8) null<1>UD g2<8,8,1>F 0x120a8000 + render MsgDesc: media block write MsgCtrl = 0x0 Surface = 0 mlen 9 rlen 0 { align1 1Q }; +add(1) g2<1>UD g1<0,1,0>UW 0x0010UW { align1 1N }; +sendc(8) null<1>UD g2<8,8,1>F 0x120a8000 + render MsgDesc: media block write MsgCtrl = 0x0 Surface = 0 mlen 9 rlen 0 { align1 1Q }; + +/* Now, clear all GRF registers */ +add.nz.f0.0(1) a0.4<1>W a0.4<0,1,0>W -1W { align1 1N }; +mov(16) g[a0]<1>UW f0.1<0,1,0>UW { align1 1H }; +add(1) a0<1>D a0<0,1,0>D 32D { align1 1N }; +(+f0.0) jmpi(1) -64D { align1 WE_all 1N }; + +/* Terminante the thread */ +sendc(8) null<1>UD g127<8,8,1>F 0x82000010 + thread_spawner MsgDesc: mlen 1 rlen 0 { align1 1Q EOT }; diff --git a/drivers/gpu/drm/i915/gt/shaders/clear_kernel/ivb.asm b/drivers/gpu/drm/i915/gt/shaders/clear_kernel/ivb.asm new file mode 100644 index 000000000000..97c7ac9e3854 --- /dev/null +++ b/drivers/gpu/drm/i915/gt/shaders/clear_kernel/ivb.asm @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2020 Intel Corporation + */ + +/* + * Kernel for PAVP buffer clear. + * + * 1. Clear all 64 GRF registers assigned to the kernel with designated value; + * 2. Write 32x16 block of all "0" to render target buffer which indirectly clears + * 512 bytes of Render Cache. + */ + +/* Store designated "clear GRF" value */ +mov(1) f0.1<1>UW g1.2<0,1,0>UW { align1 1N }; + +/** + * Curbe Format + * + * DW 1.0 - Block Offset to write Render Cache + * DW 1.1 [15:0] - Clear Word + * DW 1.2 - Delay iterations + * DW 1.3 - Enable Instrumentation (only for debug) + * DW 1.4 - Rsvd (intended for context ID) + * DW 1.5 - [31:16]:SliceCount, [15:0]:SubSlicePerSliceCount + * DW 1.6 - Rsvd MBZ (intended for Enable Wait on Total Thread Count) + * DW 1.7 - Rsvd MBZ (inteded for Total Thread Count) + * + * Binding Table + * + * BTI 0: 2D Surface to help clear L3 (Render/Data Cache) + * BTI 1: Wait/Instrumentation Buffer + * Size : (SliceCount * SubSliceCount * 16 EUs/SubSlice) rows * (16 threads/EU) cols (Format R32_UINT) + * Expected to be initialized to 0 by driver/another kernel + * Layout : + * RowN: Histogram for EU-N: (SliceID*SubSlicePerSliceCount + SSID)*16 + EUID [assume max 16 EUs / SS] + * Col-k[DW-k]: Threads Executed on ThreadID-k for EU-N + */ +add(1) g1.2<1>UD g1.2<0,1,0>UD 0x00000001UD { align1 1N }; /* Loop count to delay kernel: Init to (g1.2 + 1) */ +cmp.z.f0.0(1) null<1>UD g1.3<0,1,0>UD 0x00000000UD { align1 1N }; +(+f0.0) jmpi(1) 44D { align1 WE_all 1N }; + +/** + * State Register has info on where this thread is running + * IVB: sr0.0 :: [15:13]: MBZ, 12: HSID (Half-Slice ID), [11:8]EUID, [2:0] ThreadSlotID + * HSW: sr0.0 :: 15: MBZ, [14:13]: SliceID, 12: HSID (Half-Slice ID), [11:8]EUID, [2:0] ThreadSlotID + */ +mov(8) g3<1>UD 0x00000000UD { align1 1Q }; +shr(1) g3<1>D sr0<0,1,0>D 12D { align1 1N }; +and(1) g3<1>D g3<0,1,0>D 1D { align1 1N }; /* g3 has HSID */ +shr(1) g3.1<1>D sr0<0,1,0>D 13D { align1 1N }; +and(1) g3.1<1>D g3.1<0,1,0>D 3D { align1 1N }; /* g3.1 has sliceID */ +mul(1) g3.5<1>D g3.1<0,1,0>D g1.10<0,1,0>UW { align1 1N }; +add(1) g3<1>D g3<0,1,0>D g3.5<0,1,0>D { align1 1N }; /* g3 = sliceID * SubSlicePerSliceCount + HSID */ +shr(1) g3.2<1>D sr0<0,1,0>D 8D { align1 1N }; +and(1) g3.2<1>D g3.2<0,1,0>D 15D { align1 1N }; /* g3.2 = EUID */ +mul(1) g3.4<1>D g3<0,1,0>D 16D { align1 1N }; +add(1) g3.2<1>D g3.2<0,1,0>D g3.4<0,1,0>D { align1 1N }; /* g3.2 now points to EU row number (Y-pixel = V address ) in instrumentation surf */ + +mov(8) g5<1>UD 0x00000000UD { align1 1Q }; +and(1) g3.3<1>D sr0<0,1,0>D 7D { align1 1N }; +mul(1) g3.3<1>D g3.3<0,1,0>D 4D { align1 1N }; + +mov(8) g4<1>UD g0<8,8,1>UD { align1 1Q }; /* Initialize message header with g0 */ +mov(1) g4<1>UD g3.3<0,1,0>UD { align1 1N }; /* Block offset */ +mov(1) g4.1<1>UD g3.2<0,1,0>UD { align1 1N }; /* Block offset */ +mov(1) g4.2<1>UD 0x00000003UD { align1 1N }; /* Block size (1 row x 4 bytes) */ +and(1) g4.3<1>UD g4.3<0,1,0>UW 0xffffffffUD { align1 1N }; + +/* Media block read to fetch current value at specified location in instrumentation buffer */ +sendc(8) g5<1>UD g4<8,8,1>F 0x02190001 + render MsgDesc: media block read MsgCtrl = 0x0 Surface = 1 mlen 1 rlen 1 { align1 1Q }; +add(1) g5<1>D g5<0,1,0>D 1D { align1 1N }; + +/* Media block write for updated value at specified location in instrumentation buffer */ +sendc(8) g5<1>UD g4<8,8,1>F 0x040a8001 + render MsgDesc: media block write MsgCtrl = 0x0 Surface = 1 mlen 2 rlen 0 { align1 1Q }; +/* Delay thread for specified parameter */ +add.nz.f0.0(1) g1.2<1>UD g1.2<0,1,0>UD -1D { align1 1N }; +(+f0.0) jmpi(1) -4D { align1 WE_all 1N }; + +/* Store designated "clear GRF" value */ +mov(1) f0.1<1>UW g1.2<0,1,0>UW { align1 1N }; + +/* Initialize looping parameters */ +mov(1) a0<1>D 0D { align1 1N }; /* Initialize a0.0:w=0 */ +mov(1) a0.4<1>W 127W { align1 1N }; /* Loop count. Each loop contains 16 GRF's */ + +/* Write 32x16 all "0" block */ +mov(8) g2<1>UD g0<8,8,1>UD { align1 1Q }; +mov(8) g127<1>UD g0<8,8,1>UD { align1 1Q }; +mov(2) g2<1>UD g1<2,2,1>UW { align1 1N }; +mov(1) g2.2<1>UD 0x000f000fUD { align1 1N }; /* Block size (16x16) */ +and(1) g2.3<1>UD g2.3<0,1,0>UW 0xffffffefUD { align1 1N }; +mov(16) g3<1>UD 0x00000000UD { align1 1H }; +mov(16) g4<1>UD 0x00000000UD { align1 1H }; +mov(16) g5<1>UD 0x00000000UD { align1 1H }; +mov(16) g6<1>UD 0x00000000UD { align1 1H }; +mov(16) g7<1>UD 0x00000000UD { align1 1H }; +mov(16) g8<1>UD 0x00000000UD { align1 1H }; +mov(16) g9<1>UD 0x00000000UD { align1 1H }; +mov(16) g10<1>UD 0x00000000UD { align1 1H }; +sendc(8) null<1>UD g2<8,8,1>F 0x120a8000 + render MsgDesc: media block write MsgCtrl = 0x0 Surface = 0 mlen 9 rlen 0 { align1 1Q }; +add(1) g2<1>UD g1<0,1,0>UW 0x0010UW { align1 1N }; +sendc(8) null<1>UD g2<8,8,1>F 0x120a8000 + render MsgDesc: media block write MsgCtrl = 0x0 Surface = 0 mlen 9 rlen 0 { align1 1Q }; + +/* Now, clear all GRF registers */ +add.nz.f0.0(1) a0.4<1>W a0.4<0,1,0>W -1W { align1 1N }; +mov(16) g[a0]<1>UW f0.1<0,1,0>UW { align1 1H }; +add(1) a0<1>D a0<0,1,0>D 32D { align1 1N }; +(+f0.0) jmpi(1) -8D { align1 WE_all 1N }; + +/* Terminante the thread */ +sendc(8) null<1>UD g127<8,8,1>F 0x82000010 + thread_spawner MsgDesc: mlen 1 rlen 0 { align1 1Q EOT }; diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c index fb10f3597ea5..9bbe8a795cb8 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_log.c @@ -424,25 +424,28 @@ static void guc_log_capture_logs(struct intel_guc_log *log) static u32 __get_default_log_level(struct intel_guc_log *log) { + struct intel_guc *guc = log_to_guc(log); + struct drm_i915_private *i915 = guc_to_gt(guc)->i915; + /* A negative value means "use platform/config default" */ - if (i915_modparams.guc_log_level < 0) { + if (i915->params.guc_log_level < 0) { return (IS_ENABLED(CONFIG_DRM_I915_DEBUG) || IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)) ? GUC_LOG_LEVEL_MAX : GUC_LOG_LEVEL_NON_VERBOSE; } - if (i915_modparams.guc_log_level > GUC_LOG_LEVEL_MAX) { + if (i915->params.guc_log_level > GUC_LOG_LEVEL_MAX) { DRM_WARN("Incompatible option detected: %s=%d, %s!\n", - "guc_log_level", i915_modparams.guc_log_level, + "guc_log_level", i915->params.guc_log_level, "verbosity too high"); return (IS_ENABLED(CONFIG_DRM_I915_DEBUG) || IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)) ? GUC_LOG_LEVEL_MAX : GUC_LOG_LEVEL_DISABLED; } - GEM_BUG_ON(i915_modparams.guc_log_level < GUC_LOG_LEVEL_DISABLED); - GEM_BUG_ON(i915_modparams.guc_log_level > GUC_LOG_LEVEL_MAX); - return i915_modparams.guc_log_level; + GEM_BUG_ON(i915->params.guc_log_level < GUC_LOG_LEVEL_DISABLED); + GEM_BUG_ON(i915->params.guc_log_level > GUC_LOG_LEVEL_MAX); + return i915->params.guc_log_level; } int intel_guc_log_create(struct intel_guc_log *log) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c index 94eb63f309ce..fdfeb4b9b0f5 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c @@ -660,10 +660,12 @@ void intel_guc_submission_disable(struct intel_guc *guc) static bool __guc_submission_selected(struct intel_guc *guc) { + struct drm_i915_private *i915 = guc_to_gt(guc)->i915; + if (!intel_guc_submission_is_supported(guc)) return false; - return i915_modparams.enable_guc & ENABLE_GUC_SUBMISSION; + return i915->params.enable_guc & ENABLE_GUC_SUBMISSION; } void intel_guc_submission_init_early(struct intel_guc *guc) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc.c b/drivers/gpu/drm/i915/gt/uc/intel_uc.c index f518fe05c6f9..1c2d6358826c 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_uc.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_uc.c @@ -47,15 +47,15 @@ static void __confirm_options(struct intel_uc *uc) drm_dbg(&i915->drm, "enable_guc=%d (guc:%s submission:%s huc:%s)\n", - i915_modparams.enable_guc, + i915->params.enable_guc, yesno(intel_uc_wants_guc(uc)), yesno(intel_uc_wants_guc_submission(uc)), yesno(intel_uc_wants_huc(uc))); - if (i915_modparams.enable_guc == -1) + if (i915->params.enable_guc == -1) return; - if (i915_modparams.enable_guc == 0) { + if (i915->params.enable_guc == 0) { GEM_BUG_ON(intel_uc_wants_guc(uc)); GEM_BUG_ON(intel_uc_wants_guc_submission(uc)); GEM_BUG_ON(intel_uc_wants_huc(uc)); @@ -65,25 +65,25 @@ static void __confirm_options(struct intel_uc *uc) if (!intel_uc_supports_guc(uc)) drm_info(&i915->drm, "Incompatible option enable_guc=%d - %s\n", - i915_modparams.enable_guc, "GuC is not supported!"); + i915->params.enable_guc, "GuC is not supported!"); - if (i915_modparams.enable_guc & ENABLE_GUC_LOAD_HUC && + if (i915->params.enable_guc & ENABLE_GUC_LOAD_HUC && !intel_uc_supports_huc(uc)) drm_info(&i915->drm, "Incompatible option enable_guc=%d - %s\n", - i915_modparams.enable_guc, "HuC is not supported!"); + i915->params.enable_guc, "HuC is not supported!"); - if (i915_modparams.enable_guc & ENABLE_GUC_SUBMISSION && + if (i915->params.enable_guc & ENABLE_GUC_SUBMISSION && !intel_uc_supports_guc_submission(uc)) drm_info(&i915->drm, "Incompatible option enable_guc=%d - %s\n", - i915_modparams.enable_guc, "GuC submission is N/A"); + i915->params.enable_guc, "GuC submission is N/A"); - if (i915_modparams.enable_guc & ~(ENABLE_GUC_SUBMISSION | + if (i915->params.enable_guc & ~(ENABLE_GUC_SUBMISSION | ENABLE_GUC_LOAD_HUC)) drm_info(&i915->drm, "Incompatible option enable_guc=%d - %s\n", - i915_modparams.enable_guc, "undocumented flag"); + i915->params.enable_guc, "undocumented flag"); } void intel_uc_init_early(struct intel_uc *uc) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c index e1caae93996d..59b27aba15c6 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c @@ -47,12 +47,15 @@ void intel_uc_fw_change_status(struct intel_uc_fw *uc_fw, * TGL 35.2 is interface-compatible with 33.0 for previous Gens. The deltas * between 33.0 and 35.2 are only related to new additions to support new Gen12 * features. + * + * Note that RKL uses the same firmware as TGL. */ #define INTEL_UC_FIRMWARE_DEFS(fw_def, guc_def, huc_def) \ + fw_def(ROCKETLAKE, 0, guc_def(tgl, 35, 2, 0), huc_def(tgl, 7, 0, 12)) \ fw_def(TIGERLAKE, 0, guc_def(tgl, 35, 2, 0), huc_def(tgl, 7, 0, 12)) \ fw_def(ELKHARTLAKE, 0, guc_def(ehl, 33, 0, 4), huc_def(ehl, 9, 0, 0)) \ fw_def(ICELAKE, 0, guc_def(icl, 33, 0, 0), huc_def(icl, 9, 0, 0)) \ - fw_def(COFFEELAKE, 5, guc_def(cml, 33, 0, 0), huc_def(cml, 4, 0, 0)) \ + fw_def(COMETLAKE, 5, guc_def(cml, 33, 0, 0), huc_def(cml, 4, 0, 0)) \ fw_def(COFFEELAKE, 0, guc_def(kbl, 33, 0, 0), huc_def(kbl, 4, 0, 0)) \ fw_def(GEMINILAKE, 0, guc_def(glk, 33, 0, 0), huc_def(glk, 4, 0, 0)) \ fw_def(KABYLAKE, 0, guc_def(kbl, 33, 0, 0), huc_def(kbl, 4, 0, 0)) \ @@ -112,11 +115,13 @@ struct __packed uc_fw_platform_requirement { }, static void -__uc_fw_auto_select(struct intel_uc_fw *uc_fw, enum intel_platform p, u8 rev) +__uc_fw_auto_select(struct drm_i915_private *i915, struct intel_uc_fw *uc_fw) { static const struct uc_fw_platform_requirement fw_blobs[] = { INTEL_UC_FIRMWARE_DEFS(MAKE_FW_LIST, GUC_FW_BLOB, HUC_FW_BLOB) }; + enum intel_platform p = INTEL_INFO(i915)->platform; + u8 rev = INTEL_REVID(i915); int i; for (i = 0; i < ARRAY_SIZE(fw_blobs) && p <= fw_blobs[i].p; i++) { @@ -151,35 +156,35 @@ __uc_fw_auto_select(struct intel_uc_fw *uc_fw, enum intel_platform p, u8 rev) } /* We don't want to enable GuC/HuC on pre-Gen11 by default */ - if (i915_modparams.enable_guc == -1 && p < INTEL_ICELAKE) + if (i915->params.enable_guc == -1 && p < INTEL_ICELAKE) uc_fw->path = NULL; } -static const char *__override_guc_firmware_path(void) +static const char *__override_guc_firmware_path(struct drm_i915_private *i915) { - if (i915_modparams.enable_guc & (ENABLE_GUC_SUBMISSION | - ENABLE_GUC_LOAD_HUC)) - return i915_modparams.guc_firmware_path; + if (i915->params.enable_guc & (ENABLE_GUC_SUBMISSION | + ENABLE_GUC_LOAD_HUC)) + return i915->params.guc_firmware_path; return ""; } -static const char *__override_huc_firmware_path(void) +static const char *__override_huc_firmware_path(struct drm_i915_private *i915) { - if (i915_modparams.enable_guc & ENABLE_GUC_LOAD_HUC) - return i915_modparams.huc_firmware_path; + if (i915->params.enable_guc & ENABLE_GUC_LOAD_HUC) + return i915->params.huc_firmware_path; return ""; } -static void __uc_fw_user_override(struct intel_uc_fw *uc_fw) +static void __uc_fw_user_override(struct drm_i915_private *i915, struct intel_uc_fw *uc_fw) { const char *path = NULL; switch (uc_fw->type) { case INTEL_UC_FW_TYPE_GUC: - path = __override_guc_firmware_path(); + path = __override_guc_firmware_path(i915); break; case INTEL_UC_FW_TYPE_HUC: - path = __override_huc_firmware_path(); + path = __override_huc_firmware_path(i915); break; } @@ -213,10 +218,8 @@ void intel_uc_fw_init_early(struct intel_uc_fw *uc_fw, uc_fw->type = type; if (HAS_GT_UC(i915)) { - __uc_fw_auto_select(uc_fw, - INTEL_INFO(i915)->platform, - INTEL_REVID(i915)); - __uc_fw_user_override(uc_fw); + __uc_fw_auto_select(i915, uc_fw); + __uc_fw_user_override(i915, uc_fw); } intel_uc_fw_change_status(uc_fw, uc_fw->path ? *uc_fw->path ? diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c index 8b87f130f7f1..f1940939260a 100644 --- a/drivers/gpu/drm/i915/gvt/cmd_parser.c +++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c @@ -1904,19 +1904,10 @@ static int perform_bb_shadow(struct parser_exec_state *s) goto err_free_bb; } - ret = i915_gem_object_prepare_write(bb->obj, &bb->clflush); - if (ret) - goto err_free_obj; - bb->va = i915_gem_object_pin_map(bb->obj, I915_MAP_WB); if (IS_ERR(bb->va)) { ret = PTR_ERR(bb->va); - goto err_finish_shmem_access; - } - - if (bb->clflush & CLFLUSH_BEFORE) { - drm_clflush_virt_range(bb->va, bb->obj->base.size); - bb->clflush &= ~CLFLUSH_BEFORE; + goto err_free_obj; } ret = copy_gma_to_hva(s->vgpu, mm, @@ -1935,7 +1926,6 @@ static int perform_bb_shadow(struct parser_exec_state *s) INIT_LIST_HEAD(&bb->list); list_add(&bb->list, &s->workload->shadow_bb); - bb->accessing = true; bb->bb_start_cmd_va = s->ip_va; if ((s->buf_type == BATCH_BUFFER_INSTRUCTION) && (!s->is_ctx_wa)) @@ -1956,8 +1946,6 @@ static int perform_bb_shadow(struct parser_exec_state *s) return 0; err_unmap: i915_gem_object_unpin_map(bb->obj); -err_finish_shmem_access: - i915_gem_object_finish_access(bb->obj); err_free_obj: i915_gem_object_put(bb->obj); err_free_bb: diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c index a1696e9ce4b6..7ba16ddfe75f 100644 --- a/drivers/gpu/drm/i915/gvt/display.c +++ b/drivers/gpu/drm/i915/gvt/display.c @@ -199,8 +199,10 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu) SDE_PORTC_HOTPLUG_CPT | SDE_PORTD_HOTPLUG_CPT); - if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) || - IS_COFFEELAKE(dev_priv)) { + if (IS_SKYLAKE(dev_priv) || + IS_KABYLAKE(dev_priv) || + IS_COFFEELAKE(dev_priv) || + IS_COMETLAKE(dev_priv)) { vgpu_vreg_t(vgpu, SDEISR) &= ~(SDE_PORTA_HOTPLUG_SPT | SDE_PORTE_HOTPLUG_SPT); vgpu_vreg_t(vgpu, SKL_FUSE_STATUS) |= @@ -314,8 +316,10 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu) vgpu_vreg_t(vgpu, SFUSE_STRAP) |= SFUSE_STRAP_DDID_DETECTED; } - if ((IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) || - IS_COFFEELAKE(dev_priv)) && + if ((IS_SKYLAKE(dev_priv) || + IS_KABYLAKE(dev_priv) || + IS_COFFEELAKE(dev_priv) || + IS_COMETLAKE(dev_priv)) && intel_vgpu_has_monitor_on_port(vgpu, PORT_E)) { vgpu_vreg_t(vgpu, SDEISR) |= SDE_PORTE_HOTPLUG_SPT; } @@ -498,8 +502,10 @@ void intel_vgpu_emulate_hotplug(struct intel_vgpu *vgpu, bool connected) struct drm_i915_private *i915 = vgpu->gvt->gt->i915; /* TODO: add more platforms support */ - if (IS_SKYLAKE(i915) || IS_KABYLAKE(i915) || - IS_COFFEELAKE(i915)) { + if (IS_SKYLAKE(i915) || + IS_KABYLAKE(i915) || + IS_COFFEELAKE(i915) || + IS_COMETLAKE(i915)) { if (connected) { vgpu_vreg_t(vgpu, SFUSE_STRAP) |= SFUSE_STRAP_DDID_DETECTED; @@ -527,8 +533,10 @@ void intel_vgpu_clean_display(struct intel_vgpu *vgpu) { struct drm_i915_private *dev_priv = vgpu->gvt->gt->i915; - if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) || - IS_COFFEELAKE(dev_priv)) + if (IS_SKYLAKE(dev_priv) || + IS_KABYLAKE(dev_priv) || + IS_COFFEELAKE(dev_priv) || + IS_COMETLAKE(dev_priv)) clean_virtual_dp_monitor(vgpu, PORT_D); else clean_virtual_dp_monitor(vgpu, PORT_B); @@ -551,8 +559,10 @@ int intel_vgpu_init_display(struct intel_vgpu *vgpu, u64 resolution) intel_vgpu_init_i2c_edid(vgpu); - if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) || - IS_COFFEELAKE(dev_priv)) + if (IS_SKYLAKE(dev_priv) || + IS_KABYLAKE(dev_priv) || + IS_COFFEELAKE(dev_priv) || + IS_COMETLAKE(dev_priv)) return setup_virtual_dp_monitor(vgpu, PORT_D, GVT_DP_D, resolution); else diff --git a/drivers/gpu/drm/i915/gvt/dmabuf.c b/drivers/gpu/drm/i915/gvt/dmabuf.c index 37fc460414a8..c3eb3838fe88 100644 --- a/drivers/gpu/drm/i915/gvt/dmabuf.c +++ b/drivers/gpu/drm/i915/gvt/dmabuf.c @@ -198,6 +198,7 @@ static void vgpu_gem_release(struct drm_i915_gem_object *gem_obj) } static const struct drm_i915_gem_object_ops intel_vgpu_gem_ops = { + .name = "i915_gem_object_vgpu", .flags = I915_GEM_OBJECT_IS_PROXY, .get_pages = vgpu_gem_get_pages, .put_pages = vgpu_gem_put_pages, diff --git a/drivers/gpu/drm/i915/gvt/edid.c b/drivers/gpu/drm/i915/gvt/edid.c index 190651df5db1..22247805c345 100644 --- a/drivers/gpu/drm/i915/gvt/edid.c +++ b/drivers/gpu/drm/i915/gvt/edid.c @@ -149,7 +149,7 @@ static int gmbus0_mmio_write(struct intel_vgpu *vgpu, if (IS_BROXTON(i915)) port = bxt_get_port_from_gmbus0(pin_select); - else if (IS_COFFEELAKE(i915)) + else if (IS_COFFEELAKE(i915) || IS_COMETLAKE(i915)) port = cnp_get_port_from_gmbus0(pin_select); else port = get_port_from_gmbus0(pin_select); diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 3e88e3b5c43a..26cae4846c82 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -59,7 +59,7 @@ unsigned long intel_gvt_get_device_type(struct intel_gvt *gvt) return D_KBL; else if (IS_BROXTON(i915)) return D_BXT; - else if (IS_COFFEELAKE(i915)) + else if (IS_COFFEELAKE(i915) || IS_COMETLAKE(i915)) return D_CFL; return 0; @@ -1435,7 +1435,8 @@ static int mailbox_write(struct intel_vgpu *vgpu, unsigned int offset, case GEN9_PCODE_READ_MEM_LATENCY: if (IS_SKYLAKE(vgpu->gvt->gt->i915) || IS_KABYLAKE(vgpu->gvt->gt->i915) || - IS_COFFEELAKE(vgpu->gvt->gt->i915)) { + IS_COFFEELAKE(vgpu->gvt->gt->i915) || + IS_COMETLAKE(vgpu->gvt->gt->i915)) { /** * "Read memory latency" command on gen9. * Below memory latency values are read @@ -1460,7 +1461,8 @@ static int mailbox_write(struct intel_vgpu *vgpu, unsigned int offset, case SKL_PCODE_CDCLK_CONTROL: if (IS_SKYLAKE(vgpu->gvt->gt->i915) || IS_KABYLAKE(vgpu->gvt->gt->i915) || - IS_COFFEELAKE(vgpu->gvt->gt->i915)) + IS_COFFEELAKE(vgpu->gvt->gt->i915) || + IS_COMETLAKE(vgpu->gvt->gt->i915)) *data0 = SKL_CDCLK_READY_FOR_CHANGE; break; case GEN6_PCODE_READ_RC6VIDS: @@ -1722,7 +1724,8 @@ static int ring_mode_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, int ret; (*(u32 *)p_data) &= ~_MASKED_BIT_ENABLE(1); - if (IS_COFFEELAKE(vgpu->gvt->gt->i915)) + if (IS_COFFEELAKE(vgpu->gvt->gt->i915) || + IS_COMETLAKE(vgpu->gvt->gt->i915)) (*(u32 *)p_data) &= ~_MASKED_BIT_ENABLE(2); write_vreg(vgpu, offset, p_data, bytes); @@ -1731,7 +1734,8 @@ static int ring_mode_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, return 0; } - if (IS_COFFEELAKE(vgpu->gvt->gt->i915) && + if ((IS_COFFEELAKE(vgpu->gvt->gt->i915) || + IS_COMETLAKE(vgpu->gvt->gt->i915)) && data & _MASKED_BIT_ENABLE(2)) { enter_failsafe_mode(vgpu, GVT_FAILSAFE_UNSUPPORTED_GUEST); return 0; @@ -3393,7 +3397,8 @@ int intel_gvt_setup_mmio_info(struct intel_gvt *gvt) goto err; } else if (IS_SKYLAKE(i915) || IS_KABYLAKE(i915) || - IS_COFFEELAKE(i915)) { + IS_COFFEELAKE(i915) || + IS_COMETLAKE(i915)) { ret = init_bdw_mmio_info(gvt); if (ret) goto err; diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index 0fb1df71c637..3c3b9842bbbd 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -348,7 +348,7 @@ static int copy_workload_to_ring_buffer(struct intel_vgpu_workload *workload) u32 *cs; int err; - if (IS_GEN(req->i915, 9) && is_inhibit_context(req->context)) + if (IS_GEN(req->engine->i915, 9) && is_inhibit_context(req->context)) intel_vgpu_restore_inhibit_context(vgpu, req); /* @@ -509,26 +509,18 @@ static int prepare_shadow_batch_buffer(struct intel_vgpu_workload *workload) bb->bb_start_cmd_va = workload->shadow_ring_buffer_va + bb->bb_offset; - if (bb->ppgtt) { - /* for non-priv bb, scan&shadow is only for - * debugging purpose, so the content of shadow bb - * is the same as original bb. Therefore, - * here, rather than switch to shadow bb's gma - * address, we directly use original batch buffer's - * gma address, and send original bb to hardware - * directly - */ - if (bb->clflush & CLFLUSH_AFTER) { - drm_clflush_virt_range(bb->va, - bb->obj->base.size); - bb->clflush &= ~CLFLUSH_AFTER; - } - i915_gem_object_finish_access(bb->obj); - bb->accessing = false; - - } else { + /* + * For non-priv bb, scan&shadow is only for + * debugging purpose, so the content of shadow bb + * is the same as original bb. Therefore, + * here, rather than switch to shadow bb's gma + * address, we directly use original batch buffer's + * gma address, and send original bb to hardware + * directly + */ + if (!bb->ppgtt) { bb->vma = i915_gem_object_ggtt_pin(bb->obj, - NULL, 0, 0, 0); + NULL, 0, 0, 0); if (IS_ERR(bb->vma)) { ret = PTR_ERR(bb->vma); goto err; @@ -539,27 +531,15 @@ static int prepare_shadow_batch_buffer(struct intel_vgpu_workload *workload) if (gmadr_bytes == 8) bb->bb_start_cmd_va[2] = 0; - /* No one is going to touch shadow bb from now on. */ - if (bb->clflush & CLFLUSH_AFTER) { - drm_clflush_virt_range(bb->va, - bb->obj->base.size); - bb->clflush &= ~CLFLUSH_AFTER; - } - - ret = i915_gem_object_set_to_gtt_domain(bb->obj, - false); - if (ret) - goto err; - ret = i915_vma_move_to_active(bb->vma, workload->req, 0); if (ret) goto err; - - i915_gem_object_finish_access(bb->obj); - bb->accessing = false; } + + /* No one is going to touch shadow bb from now on. */ + i915_gem_object_flush_map(bb->obj); } return 0; err: @@ -630,9 +610,6 @@ static void release_shadow_batch_buffer(struct intel_vgpu_workload *workload) list_for_each_entry_safe(bb, pos, &workload->shadow_bb, list) { if (bb->obj) { - if (bb->accessing) - i915_gem_object_finish_access(bb->obj); - if (bb->va && !IS_ERR(bb->va)) i915_gem_object_unpin_map(bb->obj); @@ -939,7 +916,7 @@ static void update_guest_context(struct intel_vgpu_workload *workload) context_page_num = rq->engine->context_size; context_page_num = context_page_num >> PAGE_SHIFT; - if (IS_BROADWELL(rq->i915) && rq->engine->id == RCS0) + if (IS_BROADWELL(rq->engine->i915) && rq->engine->id == RCS0) context_page_num = 19; context_base = (void *) ctx->lrc_reg_state - diff --git a/drivers/gpu/drm/i915/gvt/scheduler.h b/drivers/gpu/drm/i915/gvt/scheduler.h index 15d317f2a4a4..64e7a0b791c3 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.h +++ b/drivers/gpu/drm/i915/gvt/scheduler.h @@ -124,8 +124,6 @@ struct intel_vgpu_shadow_bb { struct i915_vma *vma; void *va; u32 *bb_start_cmd_va; - unsigned int clflush; - bool accessing; unsigned long bb_offset; bool ppgtt; }; diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index bca036ac6621..9ca94a435b75 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -64,7 +64,7 @@ static int i915_capabilities(struct seq_file *m, void *data) intel_driver_caps_print(&i915->caps, &p); kernel_param_lock(THIS_MODULE); - i915_params_dump(&i915_modparams, &p); + i915_params_dump(&i915->params, &p); kernel_param_unlock(THIS_MODULE); return 0; @@ -230,7 +230,7 @@ static int per_file_stats(int id, void *ptr, void *data) struct file_stats *stats = data; struct i915_vma *vma; - if (!kref_get_unless_zero(&obj->base.refcount)) + if (IS_ERR_OR_NULL(obj) || !kref_get_unless_zero(&obj->base.refcount)) return 0; stats->count++; diff --git a/drivers/gpu/drm/i915/i915_debugfs_params.c b/drivers/gpu/drm/i915/i915_debugfs_params.c index 62b2c5f0495d..4e2b077692cb 100644 --- a/drivers/gpu/drm/i915/i915_debugfs_params.c +++ b/drivers/gpu/drm/i915/i915_debugfs_params.c @@ -138,9 +138,6 @@ static ssize_t i915_param_charp_write(struct file *file, char **s = m->private; char *new, *old; - /* FIXME: remove locking after params aren't the module params */ - kernel_param_lock(THIS_MODULE); - old = *s; new = strndup_user(ubuf, PAGE_SIZE); if (IS_ERR(new)) { @@ -152,8 +149,6 @@ static ssize_t i915_param_charp_write(struct file *file, kfree(old); out: - kernel_param_unlock(THIS_MODULE); - return len; } @@ -229,7 +224,7 @@ _i915_param_create_file(struct dentry *parent, const char *name, struct dentry *i915_debugfs_params(struct drm_i915_private *i915) { struct drm_minor *minor = i915->drm.primary; - struct i915_params *params = &i915_modparams; + struct i915_params *params = &i915->params; struct dentry *dir; dir = debugfs_create_dir("i915_params", minor->debugfs_root); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 34ee12f3f02d..67102dc26fce 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -501,6 +501,8 @@ static void i915_driver_late_release(struct drm_i915_private *dev_priv) cpu_latency_qos_remove_request(&dev_priv->sb_qos); mutex_destroy(&dev_priv->sb_lock); + + i915_params_free(&dev_priv->params); } /** @@ -915,6 +917,9 @@ i915_driver_create(struct pci_dev *pdev, const struct pci_device_id *ent) i915->drm.pdev = pdev; pci_set_drvdata(pdev, i915); + /* Device parameters start as a copy of module parameters. */ + i915_params_copy(&i915->params, &i915_modparams); + /* Setup the write-once "constant" device info */ device_info = mkwrite_device_info(i915); memcpy(device_info, match_info, sizeof(*device_info)); @@ -948,7 +953,7 @@ int i915_driver_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return PTR_ERR(i915); /* Disable nuclear pageflip by default on pre-ILK */ - if (!i915_modparams.nuclear_pageflip && match_info->gen < 5) + if (!i915->params.nuclear_pageflip && match_info->gen < 5) i915->drm.driver_features &= ~DRIVER_ATOMIC; /* @@ -958,7 +963,7 @@ int i915_driver_probe(struct pci_dev *pdev, const struct pci_device_id *ent) #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) if (IS_ENABLED(CONFIG_DRM_I915_UNSTABLE_FAKE_LMEM)) { if (INTEL_GEN(i915) >= 9 && i915_selftest.live < 0 && - i915_modparams.fake_lmem_start) { + i915->params.fake_lmem_start) { mkwrite_device_info(i915)->memory_regions = REGION_SMEM | REGION_LMEM | REGION_STOLEN; mkwrite_device_info(i915)->is_dgfx = true; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index adb9bf34cf97..2c2e88d49f3e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -108,8 +108,8 @@ #define DRIVER_NAME "i915" #define DRIVER_DESC "Intel Graphics" -#define DRIVER_DATE "20200515" -#define DRIVER_TIMESTAMP 1589543364 +#define DRIVER_DATE "20200702" +#define DRIVER_TIMESTAMP 1593714328 struct drm_i915_gem_object; @@ -273,6 +273,7 @@ struct drm_i915_display_funcs { void (*set_cdclk)(struct drm_i915_private *dev_priv, const struct intel_cdclk_config *cdclk_config, enum pipe pipe); + int (*bw_calc_min_cdclk)(struct intel_atomic_state *state); int (*get_fifo_size)(struct drm_i915_private *dev_priv, enum i9xx_plane_id i9xx_plane); int (*compute_pipe_wm)(struct intel_crtc_state *crtc_state); @@ -410,8 +411,6 @@ struct intel_fbc { int adjusted_x; int adjusted_y; - int y; - u16 pixel_blend_mode; } plane; @@ -420,7 +419,10 @@ struct intel_fbc { unsigned int stride; u64 modifier; } fb; + + unsigned int fence_y_offset; u16 gen9_wa_cfb_stride; + u16 interval; s8 fence_id; } state_cache; @@ -435,7 +437,6 @@ struct intel_fbc { struct { enum pipe pipe; enum i9xx_plane_id i9xx_plane; - unsigned int fence_y_offset; } crtc; struct { @@ -444,7 +445,9 @@ struct intel_fbc { } fb; int cfb_size; + unsigned int fence_y_offset; u16 gen9_wa_cfb_stride; + u16 interval; s8 fence_id; bool plane_visible; } params; @@ -829,6 +832,9 @@ struct drm_i915_private { /* FIXME: Device release actions should all be moved to drmm_ */ bool do_release; + /* i915 device parameters */ + struct i915_params params; + const struct intel_device_info __info; /* Use INTEL_INFO() to access. */ struct intel_runtime_info __runtime; /* Use RUNTIME_INFO() to access. */ struct intel_driver_caps caps; @@ -950,6 +956,13 @@ struct drm_i915_private { struct intel_global_obj obj; } cdclk; + struct { + /* The current hardware dbuf configuration */ + u8 enabled_slices; + + struct intel_global_obj obj; + } dbuf; + /** * wq - Driver workqueue for GEM. * @@ -980,7 +993,7 @@ struct drm_i915_private { struct i915_gem_mm mm; DECLARE_HASHTABLE(mm_structs, 7); - struct mutex mm_lock; + spinlock_t mm_lock; /* Kernel Modesetting */ @@ -1126,12 +1139,12 @@ struct drm_i915_private { * Set during HW readout of watermarks/DDB. Some platforms * need to know when we're still using BIOS-provided values * (which we don't fully trust). + * + * FIXME get rid of this. */ bool distrust_bios_wm; } wm; - u8 enabled_dbuf_slices_mask; /* GEN11 has configurable 2 slices */ - struct dram_info { bool valid; bool is_16gb_dimm; @@ -1256,6 +1269,11 @@ static inline struct drm_i915_private *pdev_to_i915(struct pci_dev *pdev) (engine__); \ (engine__) = rb_to_uabi_engine(rb_next(&(engine__)->uabi_node))) +#define for_each_uabi_class_engine(engine__, class__, i915__) \ + for ((engine__) = intel_engine_lookup_user((i915__), (class__), 0); \ + (engine__) && (engine__)->uabi_class == (class__); \ + (engine__) = rb_to_uabi_engine(rb_next(&(engine__)->uabi_node))) + #define I915_GTT_OFFSET_NONE ((u32)-1) /* @@ -1406,10 +1424,12 @@ IS_SUBPLATFORM(const struct drm_i915_private *i915, #define IS_KABYLAKE(dev_priv) IS_PLATFORM(dev_priv, INTEL_KABYLAKE) #define IS_GEMINILAKE(dev_priv) IS_PLATFORM(dev_priv, INTEL_GEMINILAKE) #define IS_COFFEELAKE(dev_priv) IS_PLATFORM(dev_priv, INTEL_COFFEELAKE) +#define IS_COMETLAKE(dev_priv) IS_PLATFORM(dev_priv, INTEL_COMETLAKE) #define IS_CANNONLAKE(dev_priv) IS_PLATFORM(dev_priv, INTEL_CANNONLAKE) #define IS_ICELAKE(dev_priv) IS_PLATFORM(dev_priv, INTEL_ICELAKE) #define IS_ELKHARTLAKE(dev_priv) IS_PLATFORM(dev_priv, INTEL_ELKHARTLAKE) #define IS_TIGERLAKE(dev_priv) IS_PLATFORM(dev_priv, INTEL_TIGERLAKE) +#define IS_ROCKETLAKE(dev_priv) IS_PLATFORM(dev_priv, INTEL_ROCKETLAKE) #define IS_HSW_EARLY_SDV(dev_priv) (IS_HASWELL(dev_priv) && \ (INTEL_DEVID(dev_priv) & 0xFF00) == 0x0C00) #define IS_BDW_ULT(dev_priv) \ @@ -1453,6 +1473,14 @@ IS_SUBPLATFORM(const struct drm_i915_private *i915, INTEL_INFO(dev_priv)->gt == 2) #define IS_CFL_GT3(dev_priv) (IS_COFFEELAKE(dev_priv) && \ INTEL_INFO(dev_priv)->gt == 3) + +#define IS_CML_ULT(dev_priv) \ + IS_SUBPLATFORM(dev_priv, INTEL_COMETLAKE, INTEL_SUBPLATFORM_ULT) +#define IS_CML_ULX(dev_priv) \ + IS_SUBPLATFORM(dev_priv, INTEL_COMETLAKE, INTEL_SUBPLATFORM_ULX) +#define IS_CML_GT2(dev_priv) (IS_COMETLAKE(dev_priv) && \ + INTEL_INFO(dev_priv)->gt == 2) + #define IS_CNL_WITH_PORT_F(dev_priv) \ IS_SUBPLATFORM(dev_priv, INTEL_CANNONLAKE, INTEL_SUBPLATFORM_PORTF) #define IS_ICL_WITH_PORT_F(dev_priv) \ @@ -1523,6 +1551,13 @@ IS_SUBPLATFORM(const struct drm_i915_private *i915, #define IS_TGL_REVID(p, since, until) \ (IS_TIGERLAKE(p) && IS_REVID(p, since, until)) +#define RKL_REVID_A0 0x0 +#define RKL_REVID_B0 0x1 +#define RKL_REVID_C0 0x4 + +#define IS_RKL_REVID(p, since, until) \ + (IS_ROCKETLAKE(p) && IS_REVID(p, since, until)) + #define IS_LP(dev_priv) (INTEL_INFO(dev_priv)->is_lp) #define IS_GEN9_LP(dev_priv) (IS_GEN(dev_priv, 9) && IS_LP(dev_priv)) #define IS_GEN9_BC(dev_priv) (IS_GEN(dev_priv, 9) && !IS_LP(dev_priv)) @@ -1616,6 +1651,8 @@ IS_SUBPLATFORM(const struct drm_i915_private *i915, #define HAS_DDI(dev_priv) (INTEL_INFO(dev_priv)->display.has_ddi) #define HAS_FPGA_DBG_UNCLAIMED(dev_priv) (INTEL_INFO(dev_priv)->has_fpga_dbg) #define HAS_PSR(dev_priv) (INTEL_INFO(dev_priv)->display.has_psr) +#define HAS_PSR_HW_TRACKING(dev_priv) \ + (INTEL_INFO(dev_priv)->display.has_psr_hw_tracking) #define HAS_TRANSCODER(dev_priv, trans) ((INTEL_INFO(dev_priv)->cpu_transcoder_mask & BIT(trans)) != 0) #define HAS_RC6(dev_priv) (INTEL_INFO(dev_priv)->has_rc6) @@ -1658,7 +1695,8 @@ IS_SUBPLATFORM(const struct drm_i915_private *i915, #define HAS_DISPLAY(dev_priv) (INTEL_INFO(dev_priv)->pipe_mask != 0) /* Only valid when HAS_DISPLAY() is true */ -#define INTEL_DISPLAY_ENABLED(dev_priv) (WARN_ON(!HAS_DISPLAY(dev_priv)), !i915_modparams.disable_display) +#define INTEL_DISPLAY_ENABLED(dev_priv) \ + (drm_WARN_ON(&(dev_priv)->drm, !HAS_DISPLAY(dev_priv)), !(dev_priv)->params.disable_display) static inline bool intel_vtd_active(void) { diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 0cbcb9f54e7d..9aa3066cb75d 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -933,6 +933,18 @@ void i915_gem_runtime_suspend(struct drm_i915_private *i915) } } +static void discard_ggtt_vma(struct i915_vma *vma) +{ + struct drm_i915_gem_object *obj = vma->obj; + + spin_lock(&obj->vma.lock); + if (!RB_EMPTY_NODE(&vma->obj_node)) { + rb_erase(&vma->obj_node, &obj->vma.tree); + RB_CLEAR_NODE(&vma->obj_node); + } + spin_unlock(&obj->vma.lock); +} + struct i915_vma * i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj, const struct i915_ggtt_view *view, @@ -979,6 +991,7 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj, return ERR_PTR(-ENOSPC); } +new_vma: vma = i915_vma_instance(obj, &ggtt->vm, view); if (IS_ERR(vma)) return vma; @@ -993,6 +1006,11 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj, return ERR_PTR(-ENOSPC); } + if (i915_vma_is_pinned(vma) || i915_vma_is_active(vma)) { + discard_ggtt_vma(vma); + goto new_vma; + } + ret = i915_vma_unbind(vma); if (ret) return ERR_PTR(ret); diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index f6226df9f972..c9b0ee5e1d23 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -42,7 +42,6 @@ int i915_gem_gtt_insert(struct i915_address_space *vm, #define PIN_OFFSET_BIAS BIT_ULL(6) #define PIN_OFFSET_FIXED BIT_ULL(7) -#define PIN_UPDATE BIT_ULL(9) #define PIN_GLOBAL BIT_ULL(10) /* I915_VMA_GLOBAL_BIND */ #define PIN_USER BIT_ULL(11) /* I915_VMA_LOCAL_BIND */ diff --git a/drivers/gpu/drm/i915/i915_getparam.c b/drivers/gpu/drm/i915/i915_getparam.c index d042644b9cd2..40390b2352b1 100644 --- a/drivers/gpu/drm/i915/i915_getparam.c +++ b/drivers/gpu/drm/i915/i915_getparam.c @@ -80,7 +80,7 @@ int i915_getparam_ioctl(struct drm_device *dev, void *data, return -ENODEV; break; case I915_PARAM_HAS_GPU_RESET: - value = i915_modparams.enable_hangcheck && + value = i915->params.enable_hangcheck && intel_has_gpu_reset(&i915->gt); if (value && intel_has_reset_engine(&i915->gt)) value = 2; diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index eec292d06f11..866166ada10e 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -1698,7 +1698,7 @@ static void capture_gen(struct i915_gpu_coredump *error) error->reset_count = i915_reset_count(&i915->gpu_error); error->suspend_count = i915->suspend_count; - i915_params_copy(&error->params, &i915_modparams); + i915_params_copy(&error->params, &i915->params); memcpy(&error->device_info, INTEL_INFO(i915), sizeof(error->device_info)); @@ -1713,7 +1713,7 @@ i915_gpu_coredump_alloc(struct drm_i915_private *i915, gfp_t gfp) { struct i915_gpu_coredump *error; - if (!i915_modparams.error_capture) + if (!i915->params.error_capture) return NULL; error = kzalloc(sizeof(*error), gfp); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 284cf078135a..562b43ed077f 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -777,7 +777,7 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc) vblank = &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)]; mode = &vblank->hwmode; - if (mode->private_flags & I915_MODE_FLAG_GET_SCANLINE_FROM_TIMESTAMP) + if (crtc->mode_flags & I915_MODE_FLAG_GET_SCANLINE_FROM_TIMESTAMP) return __intel_get_crtc_scanline_from_timestamp(crtc); vtotal = mode->crtc_vtotal; @@ -836,7 +836,7 @@ static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, unsigned long irqflags; bool use_scanline_counter = INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv) || IS_GEN(dev_priv, 2) || - mode->private_flags & I915_MODE_FLAG_USE_SCANLINE_COUNTER; + crtc->mode_flags & I915_MODE_FLAG_USE_SCANLINE_COUNTER; if (drm_WARN_ON(&dev_priv->drm, !mode->crtc_clock)) { drm_dbg(&dev_priv->drm, @@ -2097,67 +2097,68 @@ static void ivb_display_irq_handler(struct drm_i915_private *dev_priv, */ static irqreturn_t ilk_irq_handler(int irq, void *arg) { - struct drm_i915_private *dev_priv = arg; + struct drm_i915_private *i915 = arg; + void __iomem * const regs = i915->uncore.regs; u32 de_iir, gt_iir, de_ier, sde_ier = 0; irqreturn_t ret = IRQ_NONE; - if (!intel_irqs_enabled(dev_priv)) + if (unlikely(!intel_irqs_enabled(i915))) return IRQ_NONE; /* IRQs are synced during runtime_suspend, we don't require a wakeref */ - disable_rpm_wakeref_asserts(&dev_priv->runtime_pm); + disable_rpm_wakeref_asserts(&i915->runtime_pm); /* disable master interrupt before clearing iir */ - de_ier = I915_READ(DEIER); - I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL); + de_ier = raw_reg_read(regs, DEIER); + raw_reg_write(regs, DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL); /* Disable south interrupts. We'll only write to SDEIIR once, so further * interrupts will will be stored on its back queue, and then we'll be * able to process them after we restore SDEIER (as soon as we restore * it, we'll get an interrupt if SDEIIR still has something to process * due to its back queue). */ - if (!HAS_PCH_NOP(dev_priv)) { - sde_ier = I915_READ(SDEIER); - I915_WRITE(SDEIER, 0); + if (!HAS_PCH_NOP(i915)) { + sde_ier = raw_reg_read(regs, SDEIER); + raw_reg_write(regs, SDEIER, 0); } /* Find, clear, then process each source of interrupt */ - gt_iir = I915_READ(GTIIR); + gt_iir = raw_reg_read(regs, GTIIR); if (gt_iir) { - I915_WRITE(GTIIR, gt_iir); - ret = IRQ_HANDLED; - if (INTEL_GEN(dev_priv) >= 6) - gen6_gt_irq_handler(&dev_priv->gt, gt_iir); + raw_reg_write(regs, GTIIR, gt_iir); + if (INTEL_GEN(i915) >= 6) + gen6_gt_irq_handler(&i915->gt, gt_iir); else - gen5_gt_irq_handler(&dev_priv->gt, gt_iir); + gen5_gt_irq_handler(&i915->gt, gt_iir); + ret = IRQ_HANDLED; } - de_iir = I915_READ(DEIIR); + de_iir = raw_reg_read(regs, DEIIR); if (de_iir) { - I915_WRITE(DEIIR, de_iir); - ret = IRQ_HANDLED; - if (INTEL_GEN(dev_priv) >= 7) - ivb_display_irq_handler(dev_priv, de_iir); + raw_reg_write(regs, DEIIR, de_iir); + if (INTEL_GEN(i915) >= 7) + ivb_display_irq_handler(i915, de_iir); else - ilk_display_irq_handler(dev_priv, de_iir); + ilk_display_irq_handler(i915, de_iir); + ret = IRQ_HANDLED; } - if (INTEL_GEN(dev_priv) >= 6) { - u32 pm_iir = I915_READ(GEN6_PMIIR); + if (INTEL_GEN(i915) >= 6) { + u32 pm_iir = raw_reg_read(regs, GEN6_PMIIR); if (pm_iir) { - I915_WRITE(GEN6_PMIIR, pm_iir); + raw_reg_write(regs, GEN6_PMIIR, pm_iir); + gen6_rps_irq_handler(&i915->gt.rps, pm_iir); ret = IRQ_HANDLED; - gen6_rps_irq_handler(&dev_priv->gt.rps, pm_iir); } } - I915_WRITE(DEIER, de_ier); - if (!HAS_PCH_NOP(dev_priv)) - I915_WRITE(SDEIER, sde_ier); + raw_reg_write(regs, DEIER, de_ier); + if (sde_ier) + raw_reg_write(regs, SDEIER, sde_ier); /* IRQs are synced during runtime_suspend, we don't require a wakeref */ - enable_rpm_wakeref_asserts(&dev_priv->runtime_pm); + enable_rpm_wakeref_asserts(&i915->runtime_pm); return ret; } @@ -2254,7 +2255,9 @@ static u32 gen8_de_port_aux_mask(struct drm_i915_private *dev_priv) static u32 gen8_de_pipe_fault_mask(struct drm_i915_private *dev_priv) { - if (INTEL_GEN(dev_priv) >= 11) + if (IS_ROCKETLAKE(dev_priv)) + return RKL_DE_PIPE_IRQ_FAULT_ERRORS; + else if (INTEL_GEN(dev_priv) >= 11) return GEN11_DE_PIPE_IRQ_FAULT_ERRORS; else if (INTEL_GEN(dev_priv) >= 9) return GEN9_DE_PIPE_IRQ_FAULT_ERRORS; @@ -2869,13 +2872,15 @@ static void gen11_display_irq_reset(struct drm_i915_private *dev_priv) { struct intel_uncore *uncore = &dev_priv->uncore; enum pipe pipe; + u32 trans_mask = BIT(TRANSCODER_A) | BIT(TRANSCODER_B) | + BIT(TRANSCODER_C) | BIT(TRANSCODER_D); intel_uncore_write(uncore, GEN11_DISPLAY_INT_CTL, 0); if (INTEL_GEN(dev_priv) >= 12) { enum transcoder trans; - for (trans = TRANSCODER_A; trans <= TRANSCODER_D; trans++) { + for_each_cpu_transcoder_masked(dev_priv, trans, trans_mask) { enum intel_display_power_domain domain; domain = POWER_DOMAIN_TRANSCODER(trans); @@ -2902,8 +2907,8 @@ static void gen11_display_irq_reset(struct drm_i915_private *dev_priv) if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP) GEN3_IRQ_RESET(uncore, SDE); - /* Wa_14010685332:icl */ - if (INTEL_PCH_TYPE(dev_priv) == PCH_ICP) { + /* Wa_14010685332:icl,jsl,ehl,tgl,rkl */ + if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP) { intel_uncore_rmw(uncore, SOUTH_CHICKEN1, SBCLK_RUN_REFCLK_DIS, SBCLK_RUN_REFCLK_DIS); intel_uncore_rmw(uncore, SOUTH_CHICKEN1, @@ -3396,6 +3401,8 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) u32 de_port_masked = gen8_de_port_aux_mask(dev_priv); u32 de_port_enables; u32 de_misc_masked = GEN8_DE_EDP_PSR; + u32 trans_mask = BIT(TRANSCODER_A) | BIT(TRANSCODER_B) | + BIT(TRANSCODER_C) | BIT(TRANSCODER_D); enum pipe pipe; if (INTEL_GEN(dev_priv) <= 10) @@ -3416,7 +3423,7 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) if (INTEL_GEN(dev_priv) >= 12) { enum transcoder trans; - for (trans = TRANSCODER_A; trans <= TRANSCODER_D; trans++) { + for_each_cpu_transcoder_masked(dev_priv, trans, trans_mask) { enum intel_display_power_domain domain; domain = POWER_DOMAIN_TRANSCODER(trans); diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 02559da61e6e..8d8db9ff0a48 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -40,6 +40,15 @@ struct i915_params i915_modparams __read_mostly = { #undef MEMBER }; +/* + * Note: As a rule, keep module parameter sysfs permissions read-only + * 0400. Runtime changes are only supported through i915 debugfs. + * + * For any exceptions requiring write access and runtime changes through module + * parameter sysfs, prevent debugfs file creation by setting the parameter's + * debugfs mode to 0. + */ + i915_param_named(modeset, int, 0400, "Use kernel modesetting [KMS] (0=disable, " "1=on, -1=force vga console preference [default])"); @@ -49,7 +58,7 @@ i915_param_named_unsafe(enable_dc, int, 0400, "(-1=auto [default]; 0=disable; 1=up to DC5; 2=up to DC6; " "3=up to DC5 with DC3CO; 4=up to DC6 with DC3CO)"); -i915_param_named_unsafe(enable_fbc, int, 0600, +i915_param_named_unsafe(enable_fbc, int, 0400, "Enable frame buffer compression for power savings " "(default: -1 (use per-chip default))"); @@ -57,7 +66,7 @@ i915_param_named_unsafe(lvds_channel_mode, int, 0400, "Specify LVDS channel mode " "(0=probe BIOS [default], 1=single-channel, 2=dual-channel)"); -i915_param_named_unsafe(panel_use_ssc, int, 0600, +i915_param_named_unsafe(panel_use_ssc, int, 0400, "Use Spread Spectrum Clock with panels [LVDS/eDP] " "(default: auto from VBT)"); @@ -65,29 +74,34 @@ i915_param_named_unsafe(vbt_sdvo_panel_type, int, 0400, "Override/Ignore selection of SDVO panel mode in the VBT " "(-2=ignore, -1=auto [default], index in VBT BIOS table)"); -i915_param_named_unsafe(reset, uint, 0600, +i915_param_named_unsafe(reset, uint, 0400, "Attempt GPU resets (0=disabled, 1=full gpu reset, 2=engine reset [default])"); i915_param_named_unsafe(vbt_firmware, charp, 0400, "Load VBT from specified file under /lib/firmware"); #if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR) -i915_param_named(error_capture, bool, 0600, +i915_param_named(error_capture, bool, 0400, "Record the GPU state following a hang. " "This information in /sys/class/drm/card<N>/error is vital for " "triaging and debugging hangs."); #endif -i915_param_named_unsafe(enable_hangcheck, bool, 0600, +i915_param_named_unsafe(enable_hangcheck, bool, 0400, "Periodically check GPU activity for detecting hangs. " "WARNING: Disabling this can cause system wide hangs. " "(default: true)"); -i915_param_named_unsafe(enable_psr, int, 0600, +i915_param_named_unsafe(enable_psr, int, 0400, "Enable PSR " "(0=disabled, 1=enabled) " "Default: -1 (use per-chip default)"); +i915_param_named(psr_safest_params, bool, 0400, + "Replace PSR VBT parameters by the safest and not optimal ones. This " + "is helpful to detect if PSR issues are related to bad values set in " + " VBT. (0=use VBT parameters, 1=use safest parameters)"); + i915_param_named_unsafe(force_probe, charp, 0400, "Force probe the driver for specified devices. " "See CONFIG_DRM_I915_FORCE_PROBE for details."); @@ -96,22 +110,22 @@ i915_param_named_unsafe(disable_power_well, int, 0400, "Disable display power wells when possible " "(-1=auto [default], 0=power wells always on, 1=power wells disabled when possible)"); -i915_param_named_unsafe(enable_ips, int, 0600, "Enable IPS (default: true)"); +i915_param_named_unsafe(enable_ips, int, 0400, "Enable IPS (default: true)"); -i915_param_named(fastboot, int, 0600, +i915_param_named(fastboot, int, 0400, "Try to skip unnecessary mode sets at boot time " "(0=disabled, 1=enabled) " "Default: -1 (use per-chip default)"); -i915_param_named_unsafe(load_detect_test, bool, 0600, +i915_param_named_unsafe(load_detect_test, bool, 0400, "Force-enable the VGA load detect code for testing (default:false). " "For developers only."); -i915_param_named_unsafe(force_reset_modeset_test, bool, 0600, +i915_param_named_unsafe(force_reset_modeset_test, bool, 0400, "Force a modeset during gpu reset for testing (default:false). " "For developers only."); -i915_param_named_unsafe(invert_brightness, int, 0600, +i915_param_named_unsafe(invert_brightness, int, 0400, "Invert backlight brightness " "(-1 force normal, 0 machine defaults, 1 force inversion), please " "report PCI device ID, subsystem vendor and subsystem device ID " @@ -121,10 +135,11 @@ i915_param_named_unsafe(invert_brightness, int, 0600, i915_param_named(disable_display, bool, 0400, "Disable display (default: false)"); -i915_param_named(mmio_debug, int, 0600, +i915_param_named(mmio_debug, int, 0400, "Enable the MMIO debug code for the first N failures (default: off). " "This may negatively affect performance."); +/* Special case writable file */ i915_param_named(verbose_state_checks, bool, 0600, "Enable verbose logs (ie. WARN_ON()) in case of unexpected hw state conditions."); @@ -155,7 +170,7 @@ i915_param_named_unsafe(huc_firmware_path, charp, 0400, i915_param_named_unsafe(dmc_firmware_path, charp, 0400, "DMC firmware path to use instead of the default one"); -i915_param_named_unsafe(enable_dp_mst, bool, 0600, +i915_param_named_unsafe(enable_dp_mst, bool, 0400, "Enable multi-stream transport (MST) for new DisplayPort sinks. (default: true)"); #if IS_ENABLED(CONFIG_DRM_I915_DEBUG) @@ -163,7 +178,7 @@ i915_param_named_unsafe(inject_probe_failure, uint, 0400, "Force an error after a number of failure check points (0:disabled (default), N:force failure at the Nth failure check point)"); #endif -i915_param_named(enable_dpcd_backlight, int, 0600, +i915_param_named(enable_dpcd_backlight, int, 0400, "Enable support for DPCD backlight control" "(-1=use per-VBT LFP backlight type setting [default], 0=disabled, 1=enabled)"); diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 4f21bfffbf0e..53fb5ba8fbed 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -53,6 +53,7 @@ struct drm_printer; param(int, enable_dc, -1, 0400) \ param(int, enable_fbc, -1, 0600) \ param(int, enable_psr, -1, 0600) \ + param(bool, psr_safest_params, false, 0600) \ param(int, disable_power_well, -1, 0400) \ param(int, enable_ips, 1, 0600) \ param(int, invert_brightness, 0, 0600) \ diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c index eb0b5be7c35d..e5fdf17cd9cd 100644 --- a/drivers/gpu/drm/i915/i915_pci.c +++ b/drivers/gpu/drm/i915/i915_pci.c @@ -536,6 +536,7 @@ static const struct intel_device_info vlv_info = { .display.has_ddi = 1, \ .has_fpga_dbg = 1, \ .display.has_psr = 1, \ + .display.has_psr_hw_tracking = 1, \ .display.has_dp_mst = 1, \ .has_rc6p = 0 /* RC6p removed-by HSW */, \ HSW_PIPE_OFFSETS, \ @@ -690,6 +691,7 @@ static const struct intel_device_info skl_gt4_info = { .display.has_fbc = 1, \ .display.has_hdcp = 1, \ .display.has_psr = 1, \ + .display.has_psr_hw_tracking = 1, \ .has_runtime_pm = 1, \ .display.has_csr = 1, \ .has_rc6 = 1, \ @@ -766,6 +768,20 @@ static const struct intel_device_info cfl_gt3_info = { BIT(RCS0) | BIT(VCS0) | BIT(BCS0) | BIT(VECS0) | BIT(VCS1), }; +#define CML_PLATFORM \ + GEN9_FEATURES, \ + PLATFORM(INTEL_COMETLAKE) + +static const struct intel_device_info cml_gt1_info = { + CML_PLATFORM, + .gt = 1, +}; + +static const struct intel_device_info cml_gt2_info = { + CML_PLATFORM, + .gt = 2, +}; + #define GEN10_FEATURES \ GEN9_FEATURES, \ GEN(10), \ @@ -788,6 +804,7 @@ static const struct intel_device_info cnl_info = { #define GEN11_FEATURES \ GEN10_FEATURES, \ GEN11_DEFAULT_PAGE_SIZES, \ + .abox_mask = BIT(0), \ .cpu_transcoder_mask = BIT(TRANSCODER_A) | BIT(TRANSCODER_B) | \ BIT(TRANSCODER_C) | BIT(TRANSCODER_EDP) | \ BIT(TRANSCODER_DSI_0) | BIT(TRANSCODER_DSI_1), \ @@ -831,6 +848,7 @@ static const struct intel_device_info ehl_info = { #define GEN12_FEATURES \ GEN11_FEATURES, \ GEN(12), \ + .abox_mask = GENMASK(2, 1), \ .pipe_mask = BIT(PIPE_A) | BIT(PIPE_B) | BIT(PIPE_C) | BIT(PIPE_D), \ .cpu_transcoder_mask = BIT(TRANSCODER_A) | BIT(TRANSCODER_B) | \ BIT(TRANSCODER_C) | BIT(TRANSCODER_D) | \ @@ -863,6 +881,19 @@ static const struct intel_device_info tgl_info = { BIT(RCS0) | BIT(BCS0) | BIT(VECS0) | BIT(VCS0) | BIT(VCS2), }; +static const struct intel_device_info rkl_info = { + GEN12_FEATURES, + PLATFORM(INTEL_ROCKETLAKE), + .abox_mask = BIT(0), + .pipe_mask = BIT(PIPE_A) | BIT(PIPE_B) | BIT(PIPE_C), + .cpu_transcoder_mask = BIT(TRANSCODER_A) | BIT(TRANSCODER_B) | + BIT(TRANSCODER_C), + .require_force_probe = 1, + .display.has_psr_hw_tracking = 0, + .engine_mask = + BIT(RCS0) | BIT(BCS0) | BIT(VECS0) | BIT(VCS0), +}; + #define GEN12_DGFX_FEATURES \ GEN12_FEATURES, \ .is_dgfx = 1 @@ -933,14 +964,15 @@ static const struct pci_device_id pciidlist[] = { INTEL_WHL_U_GT2_IDS(&cfl_gt2_info), INTEL_AML_CFL_GT2_IDS(&cfl_gt2_info), INTEL_WHL_U_GT3_IDS(&cfl_gt3_info), - INTEL_CML_GT1_IDS(&cfl_gt1_info), - INTEL_CML_GT2_IDS(&cfl_gt2_info), - INTEL_CML_U_GT1_IDS(&cfl_gt1_info), - INTEL_CML_U_GT2_IDS(&cfl_gt2_info), + INTEL_CML_GT1_IDS(&cml_gt1_info), + INTEL_CML_GT2_IDS(&cml_gt2_info), + INTEL_CML_U_GT1_IDS(&cml_gt1_info), + INTEL_CML_U_GT2_IDS(&cml_gt2_info), INTEL_CNL_IDS(&cnl_info), INTEL_ICL_11_IDS(&icl_info), INTEL_EHL_IDS(&ehl_info), INTEL_TGL_12_IDS(&tgl_info), + INTEL_RKL_IDS(&rkl_info), {0, 0, 0} }; MODULE_DEVICE_TABLE(pci, pciidlist); diff --git a/drivers/gpu/drm/i915/i915_pmu.c b/drivers/gpu/drm/i915/i915_pmu.c index 962ded9ce73f..28bc5f13ae52 100644 --- a/drivers/gpu/drm/i915/i915_pmu.c +++ b/drivers/gpu/drm/i915/i915_pmu.c @@ -441,7 +441,11 @@ static u64 count_interrupts(struct drm_i915_private *i915) static void i915_pmu_event_destroy(struct perf_event *event) { - WARN_ON(event->parent); + struct drm_i915_private *i915 = + container_of(event->pmu, typeof(*i915), pmu.base); + + drm_WARN_ON(&i915->drm, event->parent); + module_put(THIS_MODULE); } @@ -561,7 +565,10 @@ static u64 __i915_pmu_event_read(struct perf_event *event) /* Do nothing */ } else if (sample == I915_SAMPLE_BUSY && intel_engine_supports_stats(engine)) { - val = ktime_to_ns(intel_engine_get_busy_time(engine)); + ktime_t unused; + + val = ktime_to_ns(intel_engine_get_busy_time(engine, + &unused)); } else { val = engine->pmu.sample[sample].cur; } @@ -1058,8 +1065,10 @@ static int i915_pmu_register_cpuhp_state(struct i915_pmu *pmu) static void i915_pmu_unregister_cpuhp_state(struct i915_pmu *pmu) { - WARN_ON(pmu->cpuhp.slot == CPUHP_INVALID); - WARN_ON(cpuhp_state_remove_instance(pmu->cpuhp.slot, &pmu->cpuhp.node)); + struct drm_i915_private *i915 = container_of(pmu, typeof(*i915), pmu); + + drm_WARN_ON(&i915->drm, pmu->cpuhp.slot == CPUHP_INVALID); + drm_WARN_ON(&i915->drm, cpuhp_state_remove_instance(pmu->cpuhp.slot, &pmu->cpuhp.node)); cpuhp_remove_multi_state(pmu->cpuhp.slot); pmu->cpuhp.slot = CPUHP_INVALID; } diff --git a/drivers/gpu/drm/i915/i915_query.c b/drivers/gpu/drm/i915/i915_query.c index e75c528ebbe0..c1ebda9b5627 100644 --- a/drivers/gpu/drm/i915/i915_query.c +++ b/drivers/gpu/drm/i915/i915_query.c @@ -109,8 +109,7 @@ query_engine_info(struct drm_i915_private *i915, for_each_uabi_engine(engine, i915) num_uabi_engines++; - len = sizeof(struct drm_i915_query_engine_info) + - num_uabi_engines * sizeof(struct drm_i915_engine_info); + len = struct_size(query_ptr, engines, num_uabi_engines); ret = copy_query_item(&query, sizeof(query), len, query_item); if (ret != 0) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 06cd1d28a176..9d6536afc94b 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1869,9 +1869,11 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) #define _ICL_COMBOPHY_A 0x162000 #define _ICL_COMBOPHY_B 0x6C000 #define _EHL_COMBOPHY_C 0x160000 +#define _RKL_COMBOPHY_D 0x161000 #define _ICL_COMBOPHY(phy) _PICK(phy, _ICL_COMBOPHY_A, \ _ICL_COMBOPHY_B, \ - _EHL_COMBOPHY_C) + _EHL_COMBOPHY_C, \ + _RKL_COMBOPHY_D) /* CNL/ICL Port CL_DW registers */ #define _ICL_PORT_CL_DW(dw, phy) (_ICL_COMBOPHY(phy) + \ @@ -2877,9 +2879,12 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) #define LM_FIFO_WATERMARK 0x0000001F #define MI_ARB_STATE _MMIO(0x20e4) /* 915+ only */ -#define MBUS_ABOX_CTL _MMIO(0x45038) -#define MBUS_ABOX1_CTL _MMIO(0x45048) -#define MBUS_ABOX2_CTL _MMIO(0x4504C) +#define _MBUS_ABOX0_CTL 0x45038 +#define _MBUS_ABOX1_CTL 0x45048 +#define _MBUS_ABOX2_CTL 0x4504C +#define MBUS_ABOX_CTL(x) _MMIO(_PICK(x, _MBUS_ABOX0_CTL, \ + _MBUS_ABOX1_CTL, \ + _MBUS_ABOX2_CTL)) #define MBUS_ABOX_BW_CREDIT_MASK (3 << 20) #define MBUS_ABOX_BW_CREDIT(x) ((x) << 20) #define MBUS_ABOX_B_CREDIT_MASK (0xF << 16) @@ -3203,13 +3208,17 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) #define FBC_CFB_BASE _MMIO(0x3200) /* 4k page aligned */ #define FBC_LL_BASE _MMIO(0x3204) /* 4k page aligned */ #define FBC_CONTROL _MMIO(0x3208) -#define FBC_CTL_EN (1 << 31) -#define FBC_CTL_PERIODIC (1 << 30) -#define FBC_CTL_INTERVAL_SHIFT (16) -#define FBC_CTL_UNCOMPRESSIBLE (1 << 14) -#define FBC_CTL_C3_IDLE (1 << 13) -#define FBC_CTL_STRIDE_SHIFT (5) -#define FBC_CTL_FENCENO_SHIFT (0) +#define FBC_CTL_EN REG_BIT(31) +#define FBC_CTL_PERIODIC REG_BIT(30) +#define FBC_CTL_INTERVAL_MASK REG_GENMASK(29, 16) +#define FBC_CTL_INTERVAL(x) REG_FIELD_PREP(FBC_CTL_INTERVAL_MASK, (x)) +#define FBC_CTL_STOP_ON_MOD REG_BIT(15) +#define FBC_CTL_UNCOMPRESSIBLE REG_BIT(14) /* i915+ */ +#define FBC_CTL_C3_IDLE REG_BIT(13) /* i945gm */ +#define FBC_CTL_STRIDE_MASK REG_GENMASK(12, 5) +#define FBC_CTL_STRIDE(x) REG_FIELD_PREP(FBC_CTL_STRIDE_MASK, (x)) +#define FBC_CTL_FENCENO_MASK REG_GENMASK(3, 0) +#define FBC_CTL_FENCENO(x) REG_FIELD_PREP(FBC_CTL_FENCENO_MASK, (x)) #define FBC_COMMAND _MMIO(0x320c) #define FBC_CMD_COMPRESS (1 << 0) #define FBC_STATUS _MMIO(0x3210) @@ -3768,19 +3777,16 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) /* Clocking configuration register */ #define CLKCFG _MMIO(MCHBAR_MIRROR_BASE + 0xc00) -#define CLKCFG_FSB_400 (5 << 0) /* hrawclk 100 */ +#define CLKCFG_FSB_400 (0 << 0) /* hrawclk 100 */ +#define CLKCFG_FSB_400_ALT (5 << 0) /* hrawclk 100 */ #define CLKCFG_FSB_533 (1 << 0) /* hrawclk 133 */ #define CLKCFG_FSB_667 (3 << 0) /* hrawclk 166 */ #define CLKCFG_FSB_800 (2 << 0) /* hrawclk 200 */ #define CLKCFG_FSB_1067 (6 << 0) /* hrawclk 266 */ #define CLKCFG_FSB_1067_ALT (0 << 0) /* hrawclk 266 */ #define CLKCFG_FSB_1333 (7 << 0) /* hrawclk 333 */ -/* - * Note that on at least on ELK the below value is reported for both - * 333 and 400 MHz BIOS FSB setting, but given that the gmch datasheet - * lists only 200/266/333 MHz FSB as supported let's decode it as 333 MHz. - */ #define CLKCFG_FSB_1333_ALT (4 << 0) /* hrawclk 333 */ +#define CLKCFG_FSB_1600_ALT (6 << 0) /* hrawclk 400 */ #define CLKCFG_FSB_MASK (7 << 0) #define CLKCFG_MEM_533 (1 << 4) #define CLKCFG_MEM_667 (2 << 4) @@ -4512,25 +4518,39 @@ enum { #define EDP_PSR_DEBUG_MASK_DISP_REG_WRITE (1 << 16) /* Reserved in ICL+ */ #define EDP_PSR_DEBUG_EXIT_ON_PIXEL_UNDERRUN (1 << 15) /* SKL+ */ -#define _PSR2_CTL_A 0x60900 -#define _PSR2_CTL_EDP 0x6f900 -#define EDP_PSR2_CTL(tran) _MMIO_TRANS2(tran, _PSR2_CTL_A) -#define EDP_PSR2_ENABLE (1 << 31) -#define EDP_SU_TRACK_ENABLE (1 << 30) -#define EDP_Y_COORDINATE_VALID (1 << 26) /* GLK and CNL+ */ -#define EDP_Y_COORDINATE_ENABLE (1 << 25) /* GLK and CNL+ */ -#define EDP_MAX_SU_DISABLE_TIME(t) ((t) << 20) -#define EDP_MAX_SU_DISABLE_TIME_MASK (0x1f << 20) -#define EDP_PSR2_TP2_TIME_500us (0 << 8) -#define EDP_PSR2_TP2_TIME_100us (1 << 8) -#define EDP_PSR2_TP2_TIME_2500us (2 << 8) -#define EDP_PSR2_TP2_TIME_50us (3 << 8) -#define EDP_PSR2_TP2_TIME_MASK (3 << 8) -#define EDP_PSR2_FRAME_BEFORE_SU_SHIFT 4 -#define EDP_PSR2_FRAME_BEFORE_SU_MASK (0xf << 4) -#define EDP_PSR2_FRAME_BEFORE_SU(a) ((a) << 4) -#define EDP_PSR2_IDLE_FRAME_MASK 0xf -#define EDP_PSR2_IDLE_FRAME_SHIFT 0 +#define _PSR2_CTL_A 0x60900 +#define _PSR2_CTL_EDP 0x6f900 +#define EDP_PSR2_CTL(tran) _MMIO_TRANS2(tran, _PSR2_CTL_A) +#define EDP_PSR2_ENABLE (1 << 31) +#define EDP_SU_TRACK_ENABLE (1 << 30) +#define TGL_EDP_PSR2_BLOCK_COUNT_NUM_2 (0 << 28) +#define TGL_EDP_PSR2_BLOCK_COUNT_NUM_3 (1 << 28) +#define EDP_Y_COORDINATE_VALID (1 << 26) /* GLK and CNL+ */ +#define EDP_Y_COORDINATE_ENABLE (1 << 25) /* GLK and CNL+ */ +#define EDP_MAX_SU_DISABLE_TIME(t) ((t) << 20) +#define EDP_MAX_SU_DISABLE_TIME_MASK (0x1f << 20) +#define EDP_PSR2_IO_BUFFER_WAKE_MAX_LINES 8 +#define EDP_PSR2_IO_BUFFER_WAKE(lines) ((EDP_PSR2_IO_BUFFER_WAKE_MAX_LINES - (lines)) << 13) +#define EDP_PSR2_IO_BUFFER_WAKE_MASK (3 << 13) +#define TGL_EDP_PSR2_IO_BUFFER_WAKE_MIN_LINES 5 +#define TGL_EDP_PSR2_IO_BUFFER_WAKE(lines) (((lines) - TGL_EDP_PSR2_IO_BUFFER_WAKE_MIN_LINES) << 13) +#define TGL_EDP_PSR2_IO_BUFFER_WAKE_MASK (7 << 13) +#define EDP_PSR2_FAST_WAKE_MAX_LINES 8 +#define EDP_PSR2_FAST_WAKE(lines) ((EDP_PSR2_FAST_WAKE_MAX_LINES - (lines)) << 11) +#define EDP_PSR2_FAST_WAKE_MASK (3 << 11) +#define TGL_EDP_PSR2_FAST_WAKE_MIN_LINES 5 +#define TGL_EDP_PSR2_FAST_WAKE(lines) (((lines) - TGL_EDP_PSR2_FAST_WAKE_MIN_LINES) << 10) +#define TGL_EDP_PSR2_FAST_WAKE_MASK (7 << 10) +#define EDP_PSR2_TP2_TIME_500us (0 << 8) +#define EDP_PSR2_TP2_TIME_100us (1 << 8) +#define EDP_PSR2_TP2_TIME_2500us (2 << 8) +#define EDP_PSR2_TP2_TIME_50us (3 << 8) +#define EDP_PSR2_TP2_TIME_MASK (3 << 8) +#define EDP_PSR2_FRAME_BEFORE_SU_SHIFT 4 +#define EDP_PSR2_FRAME_BEFORE_SU_MASK (0xf << 4) +#define EDP_PSR2_FRAME_BEFORE_SU(a) ((a) << 4) +#define EDP_PSR2_IDLE_FRAME_MASK 0xf +#define EDP_PSR2_IDLE_FRAME_SHIFT 0 #define _PSR_EVENT_TRANS_A 0x60848 #define _PSR_EVENT_TRANS_B 0x61848 @@ -4569,6 +4589,18 @@ enum { #define PSR2_SU_STATUS_MASK(frame) (0x3ff << PSR2_SU_STATUS_SHIFT(frame)) #define PSR2_SU_STATUS_FRAMES 8 +#define _PSR2_MAN_TRK_CTL_A 0x60910 +#define _PSR2_MAN_TRK_CTL_EDP 0x6f910 +#define PSR2_MAN_TRK_CTL(tran) _MMIO_TRANS2(tran, _PSR2_MAN_TRK_CTL_A) +#define PSR2_MAN_TRK_CTL_ENABLE REG_BIT(31) +#define PSR2_MAN_TRK_CTL_SU_REGION_START_ADDR_MASK REG_GENMASK(30, 21) +#define PSR2_MAN_TRK_CTL_SU_REGION_START_ADDR(val) REG_FIELD_PREP(PSR2_MAN_TRK_CTL_SU_REGION_START_ADDR_MASK, val) +#define PSR2_MAN_TRK_CTL_SU_REGION_END_ADDR_MASK REG_GENMASK(20, 11) +#define PSR2_MAN_TRK_CTL_SU_REGION_END_ADDR(val) REG_FIELD_PREP(PSR2_MAN_TRK_CTL_SU_REGION_END_ADDR_MASK, val) +#define PSR2_MAN_TRK_CTL_SF_SINGLE_FULL_FRAME REG_BIT(3) +#define PSR2_MAN_TRK_CTL_SF_CONTINUOS_FULL_FRAME REG_BIT(2) +#define PSR2_MAN_TRK_CTL_SF_PARTIAL_FRAME_UPDATE REG_BIT(1) + /* VGA port control */ #define ADPA _MMIO(0x61100) #define PCH_ADPA _MMIO(0xe1100) @@ -6915,6 +6947,8 @@ enum { #define _PLANE_CUS_CTL_1_A 0x701c8 #define _PLANE_CUS_CTL_2_A 0x702c8 #define PLANE_CUS_ENABLE (1 << 31) +#define PLANE_CUS_PLANE_4_RKL (0 << 30) +#define PLANE_CUS_PLANE_5_RKL (1 << 30) #define PLANE_CUS_PLANE_6 (0 << 30) #define PLANE_CUS_PLANE_7 (1 << 30) #define PLANE_CUS_HPHASE_SIGN_NEGATIVE (1 << 19) @@ -6933,7 +6967,7 @@ enum { #define PLANE_COLOR_INPUT_CSC_ENABLE (1 << 20) /* ICL+ */ #define PLANE_COLOR_PIPE_CSC_ENABLE (1 << 23) /* Pre-ICL */ #define PLANE_COLOR_CSC_MODE_BYPASS (0 << 17) -#define PLANE_COLOR_CSC_MODE_YUV601_TO_RGB709 (1 << 17) +#define PLANE_COLOR_CSC_MODE_YUV601_TO_RGB601 (1 << 17) #define PLANE_COLOR_CSC_MODE_YUV709_TO_RGB709 (2 << 17) #define PLANE_COLOR_CSC_MODE_YUV2020_TO_RGB2020 (3 << 17) #define PLANE_COLOR_CSC_MODE_RGB709_TO_RGB2020 (4 << 17) @@ -7130,7 +7164,52 @@ enum { #define PLANE_COLOR_CTL(pipe, plane) \ _MMIO_PLANE(plane, _PLANE_COLOR_CTL_1(pipe), _PLANE_COLOR_CTL_2(pipe)) -#/* SKL new cursor registers */ +#define _SEL_FETCH_PLANE_BASE_1_A 0x70890 +#define _SEL_FETCH_PLANE_BASE_2_A 0x708B0 +#define _SEL_FETCH_PLANE_BASE_3_A 0x708D0 +#define _SEL_FETCH_PLANE_BASE_4_A 0x708F0 +#define _SEL_FETCH_PLANE_BASE_5_A 0x70920 +#define _SEL_FETCH_PLANE_BASE_6_A 0x70940 +#define _SEL_FETCH_PLANE_BASE_7_A 0x70960 +#define _SEL_FETCH_PLANE_BASE_CUR_A 0x70880 +#define _SEL_FETCH_PLANE_BASE_1_B 0x70990 + +#define _SEL_FETCH_PLANE_BASE_A(plane) _PICK(plane, \ + _SEL_FETCH_PLANE_BASE_1_A, \ + _SEL_FETCH_PLANE_BASE_2_A, \ + _SEL_FETCH_PLANE_BASE_3_A, \ + _SEL_FETCH_PLANE_BASE_4_A, \ + _SEL_FETCH_PLANE_BASE_5_A, \ + _SEL_FETCH_PLANE_BASE_6_A, \ + _SEL_FETCH_PLANE_BASE_7_A, \ + _SEL_FETCH_PLANE_BASE_CUR_A) +#define _SEL_FETCH_PLANE_BASE_1(pipe) _PIPE(pipe, _SEL_FETCH_PLANE_BASE_1_A, _SEL_FETCH_PLANE_BASE_1_B) +#define _SEL_FETCH_PLANE_BASE(pipe, plane) (_SEL_FETCH_PLANE_BASE_1(pipe) - \ + _SEL_FETCH_PLANE_BASE_1_A + \ + _SEL_FETCH_PLANE_BASE_A(plane)) + +#define _SEL_FETCH_PLANE_CTL_1_A 0x70890 +#define PLANE_SEL_FETCH_CTL(pipe, plane) _MMIO(_SEL_FETCH_PLANE_BASE(pipe, plane) + \ + _SEL_FETCH_PLANE_CTL_1_A - \ + _SEL_FETCH_PLANE_BASE_1_A) +#define PLANE_SEL_FETCH_CTL_ENABLE REG_BIT(31) + +#define _SEL_FETCH_PLANE_POS_1_A 0x70894 +#define PLANE_SEL_FETCH_POS(pipe, plane) _MMIO(_SEL_FETCH_PLANE_BASE(pipe, plane) + \ + _SEL_FETCH_PLANE_POS_1_A - \ + _SEL_FETCH_PLANE_BASE_1_A) + +#define _SEL_FETCH_PLANE_SIZE_1_A 0x70898 +#define PLANE_SEL_FETCH_SIZE(pipe, plane) _MMIO(_SEL_FETCH_PLANE_BASE(pipe, plane) + \ + _SEL_FETCH_PLANE_SIZE_1_A - \ + _SEL_FETCH_PLANE_BASE_1_A) + +#define _SEL_FETCH_PLANE_OFFSET_1_A 0x7089C +#define PLANE_SEL_FETCH_OFFSET(pipe, plane) _MMIO(_SEL_FETCH_PLANE_BASE(pipe, plane) + \ + _SEL_FETCH_PLANE_OFFSET_1_A - \ + _SEL_FETCH_PLANE_BASE_1_A) + +/* SKL new cursor registers */ #define _CUR_BUF_CFG_A 0x7017c #define _CUR_BUF_CFG_B 0x7117c #define CUR_BUF_CFG(pipe) _MMIO_PIPE(pipe, _CUR_BUF_CFG_A, _CUR_BUF_CFG_B) @@ -7581,6 +7660,9 @@ enum { GEN11_PIPE_PLANE7_FAULT | \ GEN11_PIPE_PLANE6_FAULT | \ GEN11_PIPE_PLANE5_FAULT) +#define RKL_DE_PIPE_IRQ_FAULT_ERRORS \ + (GEN9_DE_PIPE_IRQ_FAULT_ERRORS | \ + GEN11_PIPE_PLANE5_FAULT) #define GEN8_DE_PORT_ISR _MMIO(0x44440) #define GEN8_DE_PORT_IMR _MMIO(0x44444) @@ -7773,11 +7855,12 @@ enum { # define CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE (1 << 5) # define CHICKEN3_DGMG_DONE_FIX_DISABLE (1 << 2) -#define CHICKEN_PAR1_1 _MMIO(0x42080) +#define CHICKEN_PAR1_1 _MMIO(0x42080) #define SKL_DE_COMPRESSED_HASH_MODE (1 << 15) -#define DPA_MASK_VBLANK_SRD (1 << 15) -#define FORCE_ARB_IDLE_PLANES (1 << 14) -#define SKL_EDP_PSR_FIX_RDWRAP (1 << 3) +#define DPA_MASK_VBLANK_SRD (1 << 15) +#define FORCE_ARB_IDLE_PLANES (1 << 14) +#define SKL_EDP_PSR_FIX_RDWRAP (1 << 3) +#define IGNORE_PSR2_HW_TRACKING (1 << 1) #define CHICKEN_PAR2_1 _MMIO(0x42090) #define KVM_CONFIG_CHANGE_NOTIFICATION_SELECT (1 << 14) @@ -7835,13 +7918,20 @@ enum { #define WAIT_FOR_PCH_RESET_ACK (1 << 1) #define WAIT_FOR_PCH_FLR_ACK (1 << 0) -#define BW_BUDDY1_CTL _MMIO(0x45140) -#define BW_BUDDY2_CTL _MMIO(0x45150) +#define _BW_BUDDY0_CTL 0x45130 +#define _BW_BUDDY1_CTL 0x45140 +#define BW_BUDDY_CTL(x) _MMIO(_PICK_EVEN(x, \ + _BW_BUDDY0_CTL, \ + _BW_BUDDY1_CTL)) #define BW_BUDDY_DISABLE REG_BIT(31) #define BW_BUDDY_TLB_REQ_TIMER_MASK REG_GENMASK(21, 16) +#define BW_BUDDY_TLB_REQ_TIMER(x) REG_FIELD_PREP(BW_BUDDY_TLB_REQ_TIMER_MASK, x) -#define BW_BUDDY1_PAGE_MASK _MMIO(0x45144) -#define BW_BUDDY2_PAGE_MASK _MMIO(0x45154) +#define _BW_BUDDY0_PAGE_MASK 0x45134 +#define _BW_BUDDY1_PAGE_MASK 0x45144 +#define BW_BUDDY_PAGE_MASK(x) _MMIO(_PICK_EVEN(x, \ + _BW_BUDDY0_PAGE_MASK, \ + _BW_BUDDY1_PAGE_MASK)) #define HSW_NDE_RSTWRN_OPT _MMIO(0x46408) #define RESET_PCH_HANDSHAKE_ENABLE (1 << 4) @@ -7852,6 +7942,12 @@ enum { #define MASK_WAKEMEM (1 << 13) #define CNL_DDI_CLOCK_REG_ACCESS_ON (1 << 7) +#define GEN11_CHICKEN_DCPR_2 _MMIO(0x46434) +#define DCPR_MASK_MAXLATENCY_MEMUP_CLR REG_BIT(27) +#define DCPR_MASK_LPMODE REG_BIT(26) +#define DCPR_SEND_RESP_IMM REG_BIT(25) +#define DCPR_CLEAR_MEMSTAT_DIS REG_BIT(24) + #define SKL_DFSM _MMIO(0x51000) #define SKL_DFSM_DISPLAY_PM_DISABLE (1 << 27) #define SKL_DFSM_DISPLAY_HDCP_DISABLE (1 << 25) @@ -8002,6 +8098,8 @@ enum { #define PER_PIXEL_ALPHA_BYPASS_EN (1 << 7) #define FF_MODE2 _MMIO(0x6604) +#define FF_MODE2_GS_TIMER_MASK REG_GENMASK(31, 24) +#define FF_MODE2_GS_TIMER_224 REG_FIELD_PREP(FF_MODE2_GS_TIMER_MASK, 224) #define FF_MODE2_TDS_TIMER_MASK REG_GENMASK(23, 16) #define FF_MODE2_TDS_TIMER_128 REG_FIELD_PREP(FF_MODE2_TDS_TIMER_MASK, 4) diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index def62100e666..3bb7320249ae 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -42,7 +42,6 @@ #include "intel_pm.h" struct execute_cb { - struct list_head link; struct irq_work work; struct i915_sw_fence *fence; void (*hook)(struct i915_request *rq, struct dma_fence *signal); @@ -57,7 +56,7 @@ static struct i915_global_request { static const char *i915_fence_get_driver_name(struct dma_fence *fence) { - return dev_name(to_request(fence)->i915->drm.dev); + return dev_name(to_request(fence)->engine->i915->drm.dev); } static const char *i915_fence_get_timeline_name(struct dma_fence *fence) @@ -189,14 +188,15 @@ static void irq_execute_cb_hook(struct irq_work *wrk) static void __notify_execute_cb(struct i915_request *rq) { - struct execute_cb *cb; + struct execute_cb *cb, *cn; lockdep_assert_held(&rq->lock); - if (list_empty(&rq->execute_cb)) + GEM_BUG_ON(!i915_request_is_active(rq)); + if (llist_empty(&rq->execute_cb)) return; - list_for_each_entry(cb, &rq->execute_cb, link) + llist_for_each_entry_safe(cb, cn, rq->execute_cb.first, work.llnode) irq_work_queue(&cb->work); /* @@ -209,7 +209,7 @@ static void __notify_execute_cb(struct i915_request *rq) * preempt-to-idle cycle on the target engine, all the while the * master execute_cb may refire. */ - INIT_LIST_HEAD(&rq->execute_cb); + init_llist_head(&rq->execute_cb); } static inline void @@ -327,7 +327,7 @@ bool i915_request_retire(struct i915_request *rq) set_bit(I915_FENCE_FLAG_ACTIVE, &rq->fence.flags); __notify_execute_cb(rq); } - GEM_BUG_ON(!list_empty(&rq->execute_cb)); + GEM_BUG_ON(!llist_empty(&rq->execute_cb)); spin_unlock_irq(&rq->lock); remove_from_client(rq); @@ -357,6 +357,12 @@ void i915_request_retire_upto(struct i915_request *rq) } while (i915_request_retire(tmp) && tmp != rq); } +static void __llist_add(struct llist_node *node, struct llist_head *head) +{ + node->next = head->first; + head->first = node; +} + static struct i915_request * const * __engine_active(struct intel_engine_cs *engine) { @@ -442,7 +448,7 @@ __await_execution(struct i915_request *rq, i915_sw_fence_complete(cb->fence); kmem_cache_free(global.slab_execute_cbs, cb); } else { - list_add_tail(&cb->link, &signal->execute_cb); + __llist_add(&cb->work.llnode, &signal->execute_cb); } spin_unlock_irq(&signal->lock); @@ -560,15 +566,15 @@ xfer: /* We may be recursing from the signal callback of another i915 fence */ if (!test_and_set_bit(I915_FENCE_FLAG_ACTIVE, &request->fence.flags)) { list_move_tail(&request->sched.link, &engine->active.requests); clear_bit(I915_FENCE_FLAG_PQUEUE, &request->fence.flags); + __notify_execute_cb(request); } + GEM_BUG_ON(!llist_empty(&request->execute_cb)); if (test_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, &request->fence.flags) && !test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &request->fence.flags) && !i915_request_enable_breadcrumb(request)) intel_engine_signal_breadcrumbs(engine); - __notify_execute_cb(request); - spin_unlock(&request->lock); return result; @@ -751,7 +757,7 @@ static void __i915_request_ctor(void *arg) rq->file_priv = NULL; rq->capture_list = NULL; - INIT_LIST_HEAD(&rq->execute_cb); + init_llist_head(&rq->execute_cb); } struct i915_request * @@ -806,7 +812,6 @@ __i915_request_create(struct intel_context *ce, gfp_t gfp) } } - rq->i915 = ce->engine->i915; rq->context = ce; rq->engine = ce->engine; rq->ring = ce->ring; @@ -841,7 +846,7 @@ __i915_request_create(struct intel_context *ce, gfp_t gfp) rq->batch = NULL; GEM_BUG_ON(rq->file_priv); GEM_BUG_ON(rq->capture_list); - GEM_BUG_ON(!list_empty(&rq->execute_cb)); + GEM_BUG_ON(!llist_empty(&rq->execute_cb)); /* * Reserve space in the ring buffer for all the commands required to @@ -1005,12 +1010,12 @@ __emit_semaphore_wait(struct i915_request *to, struct i915_request *from, u32 seqno) { - const int has_token = INTEL_GEN(to->i915) >= 12; + const int has_token = INTEL_GEN(to->engine->i915) >= 12; u32 hwsp_offset; int len, err; u32 *cs; - GEM_BUG_ON(INTEL_GEN(to->i915) < 8); + GEM_BUG_ON(INTEL_GEN(to->engine->i915) < 8); GEM_BUG_ON(i915_request_has_initial_breadcrumb(to)); /* We need to pin the signaler's HWSP until we are finished reading. */ @@ -1205,7 +1210,7 @@ __i915_request_await_external(struct i915_request *rq, struct dma_fence *fence) { mark_external(rq); return i915_sw_fence_await_dma_fence(&rq->submit, fence, - i915_fence_context_timeout(rq->i915, + i915_fence_context_timeout(rq->engine->i915, fence->context), I915_FENCE_GFP); } @@ -1776,7 +1781,8 @@ long i915_request_wait(struct i915_request *rq, * (bad for battery). */ if (flags & I915_WAIT_PRIORITY) { - if (!i915_request_started(rq) && INTEL_GEN(rq->i915) >= 6) + if (!i915_request_started(rq) && + INTEL_GEN(rq->engine->i915) >= 6) intel_rps_boost(rq); } diff --git a/drivers/gpu/drm/i915/i915_request.h b/drivers/gpu/drm/i915/i915_request.h index 8ec7ee4dbadc..590762820761 100644 --- a/drivers/gpu/drm/i915/i915_request.h +++ b/drivers/gpu/drm/i915/i915_request.h @@ -162,9 +162,6 @@ struct i915_request { struct dma_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 @@ -214,7 +211,7 @@ struct i915_request { ktime_t emitted; } duration; }; - struct list_head execute_cb; + struct llist_head execute_cb; struct i915_sw_fence semaphore; /* @@ -564,7 +561,7 @@ static inline void i915_request_clear_hold(struct i915_request *rq) } static inline struct intel_timeline * -i915_request_timeline(struct i915_request *rq) +i915_request_timeline(const struct i915_request *rq) { /* Valid only while the request is being constructed (or retired). */ return rcu_dereference_protected(rq->timeline, @@ -572,14 +569,14 @@ i915_request_timeline(struct i915_request *rq) } static inline struct i915_gem_context * -i915_request_gem_context(struct i915_request *rq) +i915_request_gem_context(const struct i915_request *rq) { /* Valid only while the request is being constructed (or retired). */ return rcu_dereference_protected(rq->context->gem_context, true); } static inline struct intel_timeline * -i915_request_active_timeline(struct i915_request *rq) +i915_request_active_timeline(const struct i915_request *rq) { /* * When in use during submission, we are protected by a guarantee that diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index bc854ad60954..a4addcc64978 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -735,7 +735,7 @@ TRACE_EVENT(i915_request_queue, ), TP_fast_assign( - __entry->dev = rq->i915->drm.primary->index; + __entry->dev = rq->engine->i915->drm.primary->index; __entry->class = rq->engine->uabi_class; __entry->instance = rq->engine->uabi_instance; __entry->ctx = rq->fence.context; @@ -761,7 +761,7 @@ DECLARE_EVENT_CLASS(i915_request, ), TP_fast_assign( - __entry->dev = rq->i915->drm.primary->index; + __entry->dev = rq->engine->i915->drm.primary->index; __entry->class = rq->engine->uabi_class; __entry->instance = rq->engine->uabi_instance; __entry->ctx = rq->fence.context; @@ -804,7 +804,7 @@ TRACE_EVENT(i915_request_in, ), TP_fast_assign( - __entry->dev = rq->i915->drm.primary->index; + __entry->dev = rq->engine->i915->drm.primary->index; __entry->class = rq->engine->uabi_class; __entry->instance = rq->engine->uabi_instance; __entry->ctx = rq->fence.context; @@ -833,7 +833,7 @@ TRACE_EVENT(i915_request_out, ), TP_fast_assign( - __entry->dev = rq->i915->drm.primary->index; + __entry->dev = rq->engine->i915->drm.primary->index; __entry->class = rq->engine->uabi_class; __entry->instance = rq->engine->uabi_instance; __entry->ctx = rq->fence.context; @@ -895,7 +895,7 @@ TRACE_EVENT(i915_request_wait_begin, * less desirable. */ TP_fast_assign( - __entry->dev = rq->i915->drm.primary->index; + __entry->dev = rq->engine->i915->drm.primary->index; __entry->class = rq->engine->uabi_class; __entry->instance = rq->engine->uabi_instance; __entry->ctx = rq->fence.context; diff --git a/drivers/gpu/drm/i915/i915_utils.c b/drivers/gpu/drm/i915/i915_utils.c index e28eae4a8f70..f42a9e9a0b4f 100644 --- a/drivers/gpu/drm/i915/i915_utils.c +++ b/drivers/gpu/drm/i915/i915_utils.c @@ -91,7 +91,7 @@ void set_timer_ms(struct timer_list *t, unsigned long timeout) return; } - timeout = msecs_to_jiffies_timeout(timeout); + timeout = msecs_to_jiffies(timeout); /* * Paranoia to make sure the compiler computes the timeout before diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c index fc14ebf9a0b7..7fe1f317cd2b 100644 --- a/drivers/gpu/drm/i915/i915_vma.c +++ b/drivers/gpu/drm/i915/i915_vma.c @@ -198,6 +198,7 @@ vma_create(struct drm_i915_gem_object *obj, cmp = i915_vma_compare(pos, vm, view); if (cmp == 0) { spin_unlock(&obj->vma.lock); + i915_vm_put(vm); i915_vma_free(vma); return pos; } @@ -397,17 +398,15 @@ int i915_vma_bind(struct i915_vma *vma, vma_flags = atomic_read(&vma->flags); vma_flags &= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND; - if (flags & PIN_UPDATE) - bind_flags |= vma_flags; - else - bind_flags &= ~vma_flags; + + bind_flags &= ~vma_flags; if (bind_flags == 0) return 0; GEM_BUG_ON(!vma->pages); trace_i915_vma_bind(vma, bind_flags); - if (work && (bind_flags & ~vma_flags) & vma->vm->bind_async_flags) { + if (work && bind_flags & vma->vm->bind_async_flags) { struct dma_fence *prev; work->vma = vma; @@ -868,7 +867,6 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags) BUILD_BUG_ON(PIN_GLOBAL != I915_VMA_GLOBAL_BIND); BUILD_BUG_ON(PIN_USER != I915_VMA_LOCAL_BIND); - GEM_BUG_ON(flags & PIN_UPDATE); GEM_BUG_ON(!(flags & (PIN_USER | PIN_GLOBAL))); /* First try and grab the pin without rebinding the vma */ @@ -1090,7 +1088,8 @@ void i915_vma_release(struct kref *ref) spin_lock(&obj->vma.lock); list_del(&vma->obj_link); - rb_erase(&vma->obj_node, &obj->vma.tree); + if (!RB_EMPTY_NODE(&vma->obj_node)) + rb_erase(&vma->obj_node, &obj->vma.tree); spin_unlock(&obj->vma.lock); } @@ -1232,31 +1231,9 @@ int i915_vma_move_to_active(struct i915_vma *vma, return 0; } -int __i915_vma_unbind(struct i915_vma *vma) +void __i915_vma_evict(struct i915_vma *vma) { - int ret; - - lockdep_assert_held(&vma->vm->mutex); - - if (i915_vma_is_pinned(vma)) { - vma_print_allocator(vma, "is pinned"); - return -EAGAIN; - } - - /* - * After confirming that no one else is pinning this vma, wait for - * any laggards who may have crept in during the wait (through - * a residual pin skipping the vm->mutex) to complete. - */ - ret = i915_vma_sync(vma); - if (ret) - return ret; - - if (!drm_mm_node_allocated(&vma->node)) - return 0; - GEM_BUG_ON(i915_vma_is_pinned(vma)); - GEM_BUG_ON(i915_vma_is_active(vma)); if (i915_vma_is_map_and_fenceable(vma)) { /* Force a pagefault for domain tracking on next user access */ @@ -1295,6 +1272,33 @@ int __i915_vma_unbind(struct i915_vma *vma) i915_vma_detach(vma); vma_unbind_pages(vma); +} + +int __i915_vma_unbind(struct i915_vma *vma) +{ + int ret; + + lockdep_assert_held(&vma->vm->mutex); + + if (!drm_mm_node_allocated(&vma->node)) + return 0; + + if (i915_vma_is_pinned(vma)) { + vma_print_allocator(vma, "is pinned"); + return -EAGAIN; + } + + /* + * After confirming that no one else is pinning this vma, wait for + * any laggards who may have crept in during the wait (through + * a residual pin skipping the vm->mutex) to complete. + */ + ret = i915_vma_sync(vma); + if (ret) + return ret; + + GEM_BUG_ON(i915_vma_is_active(vma)); + __i915_vma_evict(vma); drm_mm_remove_node(&vma->node); /* pairs with i915_vma_release() */ return 0; @@ -1306,13 +1310,13 @@ int i915_vma_unbind(struct i915_vma *vma) intel_wakeref_t wakeref = 0; int err; - if (!drm_mm_node_allocated(&vma->node)) - return 0; - /* Optimistic wait before taking the mutex */ err = i915_vma_sync(vma); if (err) - goto out_rpm; + return err; + + if (!drm_mm_node_allocated(&vma->node)) + return 0; if (i915_vma_is_pinned(vma)) { vma_print_allocator(vma, "is pinned"); diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h index 8ad1daabcd58..d0d01f909548 100644 --- a/drivers/gpu/drm/i915/i915_vma.h +++ b/drivers/gpu/drm/i915/i915_vma.h @@ -203,6 +203,7 @@ bool i915_vma_misplaced(const struct i915_vma *vma, u64 size, u64 alignment, u64 flags); void __i915_vma_set_map_and_fenceable(struct i915_vma *vma); void i915_vma_revoke_mmap(struct i915_vma *vma); +void __i915_vma_evict(struct i915_vma *vma); int __i915_vma_unbind(struct i915_vma *vma); int __must_check i915_vma_unbind(struct i915_vma *vma); void i915_vma_unlink_ctx(struct i915_vma *vma); diff --git a/drivers/gpu/drm/i915/intel_device_info.c b/drivers/gpu/drm/i915/intel_device_info.c index 8a635bd4d5d8..544ac61fbc36 100644 --- a/drivers/gpu/drm/i915/intel_device_info.c +++ b/drivers/gpu/drm/i915/intel_device_info.c @@ -57,10 +57,12 @@ static const char * const platform_names[] = { PLATFORM_NAME(KABYLAKE), PLATFORM_NAME(GEMINILAKE), PLATFORM_NAME(COFFEELAKE), + PLATFORM_NAME(COMETLAKE), PLATFORM_NAME(CANNONLAKE), PLATFORM_NAME(ICELAKE), PLATFORM_NAME(ELKHARTLAKE), PLATFORM_NAME(TIGERLAKE), + PLATFORM_NAME(ROCKETLAKE), }; #undef PLATFORM_NAME @@ -933,7 +935,10 @@ void intel_device_info_runtime_init(struct drm_i915_private *dev_priv) BUILD_BUG_ON(BITS_PER_TYPE(intel_engine_mask_t) < I915_NUM_ENGINES); - if (INTEL_GEN(dev_priv) >= 11) + if (IS_ROCKETLAKE(dev_priv)) + for_each_pipe(dev_priv, pipe) + runtime->num_sprites[pipe] = 4; + else if (INTEL_GEN(dev_priv) >= 11) for_each_pipe(dev_priv, pipe) runtime->num_sprites[pipe] = 6; else if (IS_GEN(dev_priv, 10) || IS_GEMINILAKE(dev_priv)) diff --git a/drivers/gpu/drm/i915/intel_device_info.h b/drivers/gpu/drm/i915/intel_device_info.h index 62e03ffa377e..8d62b8538585 100644 --- a/drivers/gpu/drm/i915/intel_device_info.h +++ b/drivers/gpu/drm/i915/intel_device_info.h @@ -73,6 +73,7 @@ enum intel_platform { INTEL_KABYLAKE, INTEL_GEMINILAKE, INTEL_COFFEELAKE, + INTEL_COMETLAKE, /* gen10 */ INTEL_CANNONLAKE, /* gen11 */ @@ -80,6 +81,7 @@ enum intel_platform { INTEL_ELKHARTLAKE, /* gen12 */ INTEL_TIGERLAKE, + INTEL_ROCKETLAKE, INTEL_MAX_PLATFORMS }; @@ -146,6 +148,7 @@ enum intel_ppgtt_type { func(has_modular_fia); \ func(has_overlay); \ func(has_psr); \ + func(has_psr_hw_tracking); \ func(overlay_needs_physical); \ func(supports_tv); @@ -172,6 +175,8 @@ struct intel_device_info { u8 pipe_mask; u8 cpu_transcoder_mask; + u8 abox_mask; + #define DEFINE_FLAG(name) u8 name:1 DEV_INFO_FOR_EACH_FLAG(DEFINE_FLAG); #undef DEFINE_FLAG diff --git a/drivers/gpu/drm/i915/intel_gvt.c b/drivers/gpu/drm/i915/intel_gvt.c index 21b91313cc5d..99fe8aef1c67 100644 --- a/drivers/gpu/drm/i915/intel_gvt.c +++ b/drivers/gpu/drm/i915/intel_gvt.c @@ -52,6 +52,8 @@ static bool is_supported_device(struct drm_i915_private *dev_priv) return true; if (IS_COFFEELAKE(dev_priv)) return true; + if (IS_COMETLAKE(dev_priv)) + return true; return false; } @@ -64,7 +66,7 @@ static bool is_supported_device(struct drm_i915_private *dev_priv) */ void intel_gvt_sanitize_options(struct drm_i915_private *dev_priv) { - if (!i915_modparams.enable_gvt) + if (!dev_priv->params.enable_gvt) return; if (intel_vgpu_active(dev_priv)) { @@ -80,7 +82,7 @@ void intel_gvt_sanitize_options(struct drm_i915_private *dev_priv) return; bail: - i915_modparams.enable_gvt = 0; + dev_priv->params.enable_gvt = 0; } /** @@ -100,7 +102,7 @@ int intel_gvt_init(struct drm_i915_private *dev_priv) if (i915_inject_probe_failure(dev_priv)) return -ENODEV; - if (!i915_modparams.enable_gvt) { + if (!dev_priv->params.enable_gvt) { drm_dbg(&dev_priv->drm, "GVT-g is disabled by kernel params\n"); return 0; @@ -121,7 +123,7 @@ int intel_gvt_init(struct drm_i915_private *dev_priv) return 0; bail: - i915_modparams.enable_gvt = 0; + dev_priv->params.enable_gvt = 0; return 0; } diff --git a/drivers/gpu/drm/i915/intel_pch.c b/drivers/gpu/drm/i915/intel_pch.c index 20ab9a5023b5..c668e99eb2e4 100644 --- a/drivers/gpu/drm/i915/intel_pch.c +++ b/drivers/gpu/drm/i915/intel_pch.c @@ -64,36 +64,49 @@ intel_pch_type(const struct drm_i915_private *dev_priv, unsigned short id) case INTEL_PCH_SPT_LP_DEVICE_ID_TYPE: drm_dbg_kms(&dev_priv->drm, "Found SunrisePoint LP PCH\n"); drm_WARN_ON(&dev_priv->drm, - !IS_SKYLAKE(dev_priv) && !IS_KABYLAKE(dev_priv) && - !IS_COFFEELAKE(dev_priv)); + !IS_SKYLAKE(dev_priv) && + !IS_KABYLAKE(dev_priv) && + !IS_COFFEELAKE(dev_priv) && + !IS_COMETLAKE(dev_priv)); return PCH_SPT; case INTEL_PCH_KBP_DEVICE_ID_TYPE: drm_dbg_kms(&dev_priv->drm, "Found Kaby Lake PCH (KBP)\n"); drm_WARN_ON(&dev_priv->drm, - !IS_SKYLAKE(dev_priv) && !IS_KABYLAKE(dev_priv) && - !IS_COFFEELAKE(dev_priv)); + !IS_SKYLAKE(dev_priv) && + !IS_KABYLAKE(dev_priv) && + !IS_COFFEELAKE(dev_priv) && + !IS_COMETLAKE(dev_priv)); /* KBP is SPT compatible */ return PCH_SPT; case INTEL_PCH_CNP_DEVICE_ID_TYPE: drm_dbg_kms(&dev_priv->drm, "Found Cannon Lake PCH (CNP)\n"); - drm_WARN_ON(&dev_priv->drm, !IS_CANNONLAKE(dev_priv) && - !IS_COFFEELAKE(dev_priv)); + drm_WARN_ON(&dev_priv->drm, + !IS_CANNONLAKE(dev_priv) && + !IS_COFFEELAKE(dev_priv) && + !IS_COMETLAKE(dev_priv)); return PCH_CNP; case INTEL_PCH_CNP_LP_DEVICE_ID_TYPE: drm_dbg_kms(&dev_priv->drm, "Found Cannon Lake LP PCH (CNP-LP)\n"); - drm_WARN_ON(&dev_priv->drm, !IS_CANNONLAKE(dev_priv) && - !IS_COFFEELAKE(dev_priv)); + drm_WARN_ON(&dev_priv->drm, + !IS_CANNONLAKE(dev_priv) && + !IS_COFFEELAKE(dev_priv) && + !IS_COMETLAKE(dev_priv)); return PCH_CNP; case INTEL_PCH_CMP_DEVICE_ID_TYPE: case INTEL_PCH_CMP2_DEVICE_ID_TYPE: drm_dbg_kms(&dev_priv->drm, "Found Comet Lake PCH (CMP)\n"); - drm_WARN_ON(&dev_priv->drm, !IS_COFFEELAKE(dev_priv)); + drm_WARN_ON(&dev_priv->drm, + !IS_COFFEELAKE(dev_priv) && + !IS_COMETLAKE(dev_priv) && + !IS_ROCKETLAKE(dev_priv)); /* CometPoint is CNP Compatible */ return PCH_CNP; case INTEL_PCH_CMP_V_DEVICE_ID_TYPE: drm_dbg_kms(&dev_priv->drm, "Found Comet Lake V PCH (CMP-V)\n"); - drm_WARN_ON(&dev_priv->drm, !IS_COFFEELAKE(dev_priv)); + drm_WARN_ON(&dev_priv->drm, + !IS_COFFEELAKE(dev_priv) && + !IS_COMETLAKE(dev_priv)); /* Comet Lake V PCH is based on KBP, which is SPT compatible */ return PCH_SPT; case INTEL_PCH_ICP_DEVICE_ID_TYPE: @@ -107,7 +120,8 @@ intel_pch_type(const struct drm_i915_private *dev_priv, unsigned short id) case INTEL_PCH_TGP_DEVICE_ID_TYPE: case INTEL_PCH_TGP2_DEVICE_ID_TYPE: drm_dbg_kms(&dev_priv->drm, "Found Tiger Lake LP PCH\n"); - drm_WARN_ON(&dev_priv->drm, !IS_TIGERLAKE(dev_priv)); + drm_WARN_ON(&dev_priv->drm, !IS_TIGERLAKE(dev_priv) && + !IS_ROCKETLAKE(dev_priv)); return PCH_TGP; case INTEL_PCH_JSP_DEVICE_ID_TYPE: case INTEL_PCH_JSP2_DEVICE_ID_TYPE: @@ -141,13 +155,15 @@ intel_virt_detect_pch(const struct drm_i915_private *dev_priv) * make an educated guess as to which PCH is really there. */ - if (IS_TIGERLAKE(dev_priv)) + if (IS_TIGERLAKE(dev_priv) || IS_ROCKETLAKE(dev_priv)) id = INTEL_PCH_TGP_DEVICE_ID_TYPE; else if (IS_ELKHARTLAKE(dev_priv)) id = INTEL_PCH_MCC_DEVICE_ID_TYPE; else if (IS_ICELAKE(dev_priv)) id = INTEL_PCH_ICP_DEVICE_ID_TYPE; - else if (IS_CANNONLAKE(dev_priv) || IS_COFFEELAKE(dev_priv)) + else if (IS_CANNONLAKE(dev_priv) || + IS_COFFEELAKE(dev_priv) || + IS_COMETLAKE(dev_priv)) id = INTEL_PCH_CNP_DEVICE_ID_TYPE; else if (IS_KABYLAKE(dev_priv) || IS_SKYLAKE(dev_priv)) id = INTEL_PCH_SPT_DEVICE_ID_TYPE; diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 07f663cd2d1c..565a2b9da3b3 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -33,6 +33,7 @@ #include <drm/drm_plane_helper.h> #include "display/intel_atomic.h" +#include "display/intel_bw.h" #include "display/intel_display_types.h" #include "display/intel_fbc.h" #include "display/intel_sprite.h" @@ -43,7 +44,6 @@ #include "i915_fixed.h" #include "i915_irq.h" #include "i915_trace.h" -#include "display/intel_bw.h" #include "intel_pm.h" #include "intel_sideband.h" #include "../../../platform/x86/intel_ips.h" @@ -1345,6 +1345,23 @@ static void g4x_invalidate_wms(struct intel_crtc *crtc, } } +static bool g4x_compute_fbc_en(const struct g4x_wm_state *wm_state, + int level) +{ + if (level < G4X_WM_LEVEL_SR) + return false; + + if (level >= G4X_WM_LEVEL_SR && + wm_state->sr.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_SR)) + return false; + + if (level >= G4X_WM_LEVEL_HPLL && + wm_state->hpll.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_HPLL)) + return false; + + return true; +} + static int g4x_compute_pipe_wm(struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); @@ -1384,7 +1401,6 @@ static int g4x_compute_pipe_wm(struct intel_crtc_state *crtc_state) wm_state->wm.plane[plane_id] = raw->plane[plane_id]; level = G4X_WM_LEVEL_SR; - if (!g4x_raw_crtc_wm_is_valid(crtc_state, level)) goto out; @@ -1396,7 +1412,6 @@ static int g4x_compute_pipe_wm(struct intel_crtc_state *crtc_state) wm_state->cxsr = num_active_planes == BIT(PLANE_PRIMARY); level = G4X_WM_LEVEL_HPLL; - if (!g4x_raw_crtc_wm_is_valid(crtc_state, level)) goto out; @@ -1419,17 +1434,11 @@ static int g4x_compute_pipe_wm(struct intel_crtc_state *crtc_state) /* * Determine if the FBC watermark(s) can be used. IF * this isn't the case we prefer to disable the FBC - ( watermark(s) rather than disable the SR/HPLL - * level(s) entirely. + * watermark(s) rather than disable the SR/HPLL + * level(s) entirely. 'level-1' is the highest valid + * level here. */ - wm_state->fbc_en = level > G4X_WM_LEVEL_NORMAL; - - if (level >= G4X_WM_LEVEL_SR && - wm_state->sr.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_SR)) - wm_state->fbc_en = false; - else if (level >= G4X_WM_LEVEL_HPLL && - wm_state->hpll.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_HPLL)) - wm_state->fbc_en = false; + wm_state->fbc_en = g4x_compute_fbc_en(wm_state, level - 1); return 0; } @@ -1437,6 +1446,7 @@ static int g4x_compute_pipe_wm(struct intel_crtc_state *crtc_state) static int g4x_compute_intermediate_wm(struct intel_crtc_state *new_crtc_state) { struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->uapi.crtc); + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); struct g4x_wm_state *intermediate = &new_crtc_state->wm.g4x.intermediate; const struct g4x_wm_state *optimal = &new_crtc_state->wm.g4x.optimal; struct intel_atomic_state *intel_state = @@ -1465,8 +1475,8 @@ static int g4x_compute_intermediate_wm(struct intel_crtc_state *new_crtc_state) max(optimal->wm.plane[plane_id], active->wm.plane[plane_id]); - WARN_ON(intermediate->wm.plane[plane_id] > - g4x_plane_fifo_size(plane_id, G4X_WM_LEVEL_NORMAL)); + drm_WARN_ON(&dev_priv->drm, intermediate->wm.plane[plane_id] > + g4x_plane_fifo_size(plane_id, G4X_WM_LEVEL_NORMAL)); } intermediate->sr.plane = max(optimal->sr.plane, @@ -1483,21 +1493,25 @@ static int g4x_compute_intermediate_wm(struct intel_crtc_state *new_crtc_state) intermediate->hpll.fbc = max(optimal->hpll.fbc, active->hpll.fbc); - WARN_ON((intermediate->sr.plane > - g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_SR) || - intermediate->sr.cursor > - g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_SR)) && - intermediate->cxsr); - WARN_ON((intermediate->sr.plane > - g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_HPLL) || - intermediate->sr.cursor > - g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_HPLL)) && - intermediate->hpll_en); - - WARN_ON(intermediate->sr.fbc > g4x_fbc_fifo_size(1) && - intermediate->fbc_en && intermediate->cxsr); - WARN_ON(intermediate->hpll.fbc > g4x_fbc_fifo_size(2) && - intermediate->fbc_en && intermediate->hpll_en); + drm_WARN_ON(&dev_priv->drm, + (intermediate->sr.plane > + g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_SR) || + intermediate->sr.cursor > + g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_SR)) && + intermediate->cxsr); + drm_WARN_ON(&dev_priv->drm, + (intermediate->sr.plane > + g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_HPLL) || + intermediate->sr.cursor > + g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_HPLL)) && + intermediate->hpll_en); + + drm_WARN_ON(&dev_priv->drm, + intermediate->sr.fbc > g4x_fbc_fifo_size(1) && + intermediate->fbc_en && intermediate->cxsr); + drm_WARN_ON(&dev_priv->drm, + intermediate->hpll.fbc > g4x_fbc_fifo_size(2) && + intermediate->fbc_en && intermediate->hpll_en); out: /* @@ -1681,6 +1695,7 @@ static bool vlv_need_sprite0_fifo_workaround(unsigned int active_planes) static int vlv_compute_fifo(struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); const struct g4x_pipe_wm *raw = &crtc_state->wm.vlv.raw[VLV_WM_LEVEL_PM2]; struct vlv_fifo_state *fifo_state = &crtc_state->wm.vlv.fifo_state; @@ -1749,11 +1764,11 @@ static int vlv_compute_fifo(struct intel_crtc_state *crtc_state) fifo_left -= plane_extra; } - WARN_ON(active_planes != 0 && fifo_left != 0); + drm_WARN_ON(&dev_priv->drm, active_planes != 0 && fifo_left != 0); /* give it all to the first plane if none are active */ if (active_planes == 0) { - WARN_ON(fifo_left != fifo_size); + drm_WARN_ON(&dev_priv->drm, fifo_left != fifo_size); fifo_state->plane[PLANE_PRIMARY] = fifo_left; } @@ -4025,10 +4040,9 @@ icl_get_first_dbuf_slice_offset(u32 dbuf_slice_mask, return offset; } -static u16 intel_get_ddb_size(struct drm_i915_private *dev_priv) +u16 intel_get_ddb_size(struct drm_i915_private *dev_priv) { u16 ddb_size = INTEL_INFO(dev_priv)->ddb_size; - drm_WARN_ON(&dev_priv->drm, ddb_size == 0); if (INTEL_GEN(dev_priv) < 11) @@ -4037,10 +4051,38 @@ static u16 intel_get_ddb_size(struct drm_i915_private *dev_priv) return ddb_size; } +u32 skl_ddb_dbuf_slice_mask(struct drm_i915_private *dev_priv, + const struct skl_ddb_entry *entry) +{ + u32 slice_mask = 0; + u16 ddb_size = intel_get_ddb_size(dev_priv); + u16 num_supported_slices = INTEL_INFO(dev_priv)->num_supported_dbuf_slices; + u16 slice_size = ddb_size / num_supported_slices; + u16 start_slice; + u16 end_slice; + + if (!skl_ddb_entry_size(entry)) + return 0; + + start_slice = entry->start / slice_size; + end_slice = (entry->end - 1) / slice_size; + + /* + * Per plane DDB entry can in a really worst case be on multiple slices + * but single entry is anyway contigious. + */ + while (start_slice <= end_slice) { + slice_mask |= BIT(start_slice); + start_slice++; + } + + return slice_mask; +} + static u8 skl_compute_dbuf_slices(const struct intel_crtc_state *crtc_state, u8 active_pipes); -static void +static int skl_ddb_get_pipe_allocation_limits(struct drm_i915_private *dev_priv, const struct intel_crtc_state *crtc_state, const u64 total_data_rate, @@ -4053,30 +4095,29 @@ skl_ddb_get_pipe_allocation_limits(struct drm_i915_private *dev_priv, const struct intel_crtc *crtc; u32 pipe_width = 0, total_width_in_range = 0, width_before_pipe_in_range = 0; enum pipe for_pipe = to_intel_crtc(for_crtc)->pipe; + struct intel_dbuf_state *new_dbuf_state = + intel_atomic_get_new_dbuf_state(intel_state); + const struct intel_dbuf_state *old_dbuf_state = + intel_atomic_get_old_dbuf_state(intel_state); + u8 active_pipes = new_dbuf_state->active_pipes; u16 ddb_size; u32 ddb_range_size; u32 i; u32 dbuf_slice_mask; - u32 active_pipes; u32 offset; u32 slice_size; u32 total_slice_mask; u32 start, end; + int ret; - if (drm_WARN_ON(&dev_priv->drm, !state) || !crtc_state->hw.active) { + *num_active = hweight8(active_pipes); + + if (!crtc_state->hw.active) { alloc->start = 0; alloc->end = 0; - *num_active = hweight8(dev_priv->active_pipes); - return; + return 0; } - if (intel_state->active_pipe_changes) - active_pipes = intel_state->active_pipes; - else - active_pipes = dev_priv->active_pipes; - - *num_active = hweight8(active_pipes); - ddb_size = intel_get_ddb_size(dev_priv); slice_size = ddb_size / INTEL_INFO(dev_priv)->num_supported_dbuf_slices; @@ -4089,13 +4130,16 @@ skl_ddb_get_pipe_allocation_limits(struct drm_i915_private *dev_priv, * that changes the active CRTC list or do modeset would need to * grab _all_ crtc locks, including the one we currently hold. */ - if (!intel_state->active_pipe_changes && !intel_state->modeset) { + if (old_dbuf_state->active_pipes == new_dbuf_state->active_pipes && + !dev_priv->wm.distrust_bios_wm) { /* * alloc may be cleared by clear_intel_crtc_state, * copy from old state to be sure + * + * FIXME get rid of this mess */ *alloc = to_intel_crtc_state(for_crtc->state)->wm.skl.ddb; - return; + return 0; } /* @@ -4103,10 +4147,6 @@ skl_ddb_get_pipe_allocation_limits(struct drm_i915_private *dev_priv, */ dbuf_slice_mask = skl_compute_dbuf_slices(crtc_state, active_pipes); - DRM_DEBUG_KMS("DBuf slice mask %x pipe %c active pipes %x\n", - dbuf_slice_mask, - pipe_name(for_pipe), active_pipes); - /* * Figure out at which DBuf slice we start, i.e if we start at Dbuf S2 * and slice size is 1024, the offset would be 1024 @@ -4174,7 +4214,13 @@ skl_ddb_get_pipe_allocation_limits(struct drm_i915_private *dev_priv, * FIXME: For now we always enable slice S1 as per * the Bspec display initialization sequence. */ - intel_state->enabled_dbuf_slices_mask = total_slice_mask | BIT(DBUF_S1); + new_dbuf_state->enabled_slices = total_slice_mask | BIT(DBUF_S1); + + if (old_dbuf_state->enabled_slices != new_dbuf_state->enabled_slices) { + ret = intel_atomic_serialize_global_state(&new_dbuf_state->base); + if (ret) + return ret; + } start = ddb_range_size * width_before_pipe_in_range / total_width_in_range; end = ddb_range_size * @@ -4183,11 +4229,12 @@ skl_ddb_get_pipe_allocation_limits(struct drm_i915_private *dev_priv, alloc->start = offset + start; alloc->end = offset + end; - DRM_DEBUG_KMS("Pipe %d ddb %d-%d\n", for_pipe, - alloc->start, alloc->end); - DRM_DEBUG_KMS("Enabled ddb slices mask %x num supported %d\n", - intel_state->enabled_dbuf_slices_mask, - INTEL_INFO(dev_priv)->num_supported_dbuf_slices); + drm_dbg_kms(&dev_priv->drm, + "[CRTC:%d:%s] dbuf slices 0x%x, ddb (%d - %d), active pipes 0x%x\n", + for_crtc->base.id, for_crtc->name, + dbuf_slice_mask, alloc->start, alloc->end, active_pipes); + + return 0; } static int skl_compute_wm_params(const struct intel_crtc_state *crtc_state, @@ -4308,12 +4355,6 @@ void skl_pipe_ddb_get_hw_state(struct intel_crtc *crtc, intel_display_power_put(dev_priv, power_domain, wakeref); } -void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv) -{ - dev_priv->enabled_dbuf_slices_mask = - intel_enabled_dbuf_slices_mask(dev_priv); -} - /* * Determines the downscale amount of a plane for the purposes of watermark calculations. * The bspec defines downscale amount as: @@ -4334,11 +4375,13 @@ static uint_fixed_16_16_t skl_plane_downscale_amount(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { + struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); u32 src_w, src_h, dst_w, dst_h; uint_fixed_16_16_t fp_w_ratio, fp_h_ratio; uint_fixed_16_16_t downscale_h, downscale_w; - if (WARN_ON(!intel_wm_plane_visible(crtc_state, plane_state))) + if (drm_WARN_ON(&dev_priv->drm, + !intel_wm_plane_visible(crtc_state, plane_state))) return u32_to_fixed16(0); /* @@ -4606,7 +4649,7 @@ static u8 skl_compute_dbuf_slices(const struct intel_crtc_state *crtc_state, * For anything else just return one slice yet. * Should be extended for other platforms. */ - return BIT(DBUF_S1); + return active_pipes & BIT(pipe) ? BIT(DBUF_S1) : 0; } static u64 @@ -4758,12 +4801,37 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *crtc_state) u64 uv_plane_data_rate[I915_MAX_PLANES] = {}; u32 blocks; int level; + int ret; /* Clear the partitioning for disabled planes. */ memset(crtc_state->wm.skl.plane_ddb_y, 0, sizeof(crtc_state->wm.skl.plane_ddb_y)); memset(crtc_state->wm.skl.plane_ddb_uv, 0, sizeof(crtc_state->wm.skl.plane_ddb_uv)); if (!crtc_state->hw.active) { + struct intel_atomic_state *state = + to_intel_atomic_state(crtc_state->uapi.state); + struct intel_dbuf_state *new_dbuf_state = + intel_atomic_get_new_dbuf_state(state); + const struct intel_dbuf_state *old_dbuf_state = + intel_atomic_get_old_dbuf_state(state); + + /* + * FIXME hack to make sure we compute this sensibly when + * turning off all the pipes. Otherwise we leave it at + * whatever we had previously, and then runtime PM will + * mess it up by turning off all but S1. Remove this + * once the dbuf state computation flow becomes sane. + */ + if (new_dbuf_state->active_pipes == 0) { + new_dbuf_state->enabled_slices = BIT(DBUF_S1); + + if (old_dbuf_state->enabled_slices != new_dbuf_state->enabled_slices) { + ret = intel_atomic_serialize_global_state(&new_dbuf_state->base); + if (ret) + return ret; + } + } + alloc->start = alloc->end = 0; return 0; } @@ -4778,8 +4846,12 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *crtc_state) plane_data_rate, uv_plane_data_rate); - skl_ddb_get_pipe_allocation_limits(dev_priv, crtc_state, total_data_rate, - alloc, &num_active); + ret = skl_ddb_get_pipe_allocation_limits(dev_priv, crtc_state, + total_data_rate, + alloc, &num_active); + if (ret) + return ret; + alloc_size = skl_ddb_entry_size(alloc); if (alloc_size == 0) return 0; @@ -5003,6 +5075,7 @@ skl_wm_method2(u32 pixel_rate, u32 pipe_htotal, u32 latency, static uint_fixed_16_16_t intel_get_linetime_us(const struct intel_crtc_state *crtc_state) { + struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); u32 pixel_rate; u32 crtc_htotal; uint_fixed_16_16_t linetime_us; @@ -5012,7 +5085,7 @@ intel_get_linetime_us(const struct intel_crtc_state *crtc_state) pixel_rate = crtc_state->pixel_rate; - if (WARN_ON(pixel_rate == 0)) + if (drm_WARN_ON(&dev_priv->drm, pixel_rate == 0)) return u32_to_fixed16(0); crtc_htotal = crtc_state->hw.adjusted_mode.crtc_htotal; @@ -5025,11 +5098,13 @@ static u32 skl_adjusted_plane_pixel_rate(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { + struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); u64 adjusted_pixel_rate; uint_fixed_16_16_t downscale_amount; /* Shouldn't reach here on disabled planes... */ - if (WARN_ON(!intel_wm_plane_visible(crtc_state, plane_state))) + if (drm_WARN_ON(&dev_priv->drm, + !intel_wm_plane_visible(crtc_state, plane_state))) return 0; /* @@ -5190,7 +5265,9 @@ static void skl_compute_plane_wm(const struct intel_crtc_state *crtc_state, * WaIncreaseLatencyIPCEnabled: kbl,cfl * Display WA #1141: kbl,cfl */ - if ((IS_KABYLAKE(dev_priv) || IS_COFFEELAKE(dev_priv)) && + if ((IS_KABYLAKE(dev_priv) || + IS_COFFEELAKE(dev_priv) || + IS_COMETLAKE(dev_priv)) && dev_priv->ipc_enabled) latency += 4; @@ -5465,6 +5542,7 @@ static int skl_build_plane_wm(struct intel_crtc_state *crtc_state, static int icl_build_plane_wm(struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { + struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); enum plane_id plane_id = to_intel_plane(plane_state->uapi.plane)->id; int ret; @@ -5476,9 +5554,10 @@ static int icl_build_plane_wm(struct intel_crtc_state *crtc_state, const struct drm_framebuffer *fb = plane_state->hw.fb; enum plane_id y_plane_id = plane_state->planar_linked_plane->id; - WARN_ON(!intel_wm_plane_visible(crtc_state, plane_state)); - WARN_ON(!fb->format->is_yuv || - fb->format->num_planes == 1); + drm_WARN_ON(&dev_priv->drm, + !intel_wm_plane_visible(crtc_state, plane_state)); + drm_WARN_ON(&dev_priv->drm, !fb->format->is_yuv || + fb->format->num_planes == 1); ret = skl_build_plane_wm_single(crtc_state, plane_state, y_plane_id, 0); @@ -5701,13 +5780,13 @@ static int skl_compute_ddb(struct intel_atomic_state *state) { struct drm_i915_private *dev_priv = to_i915(state->base.dev); - struct intel_crtc_state *old_crtc_state; + const struct intel_dbuf_state *old_dbuf_state; + const struct intel_dbuf_state *new_dbuf_state; + const struct intel_crtc_state *old_crtc_state; struct intel_crtc_state *new_crtc_state; struct intel_crtc *crtc; int ret, i; - state->enabled_dbuf_slices_mask = dev_priv->enabled_dbuf_slices_mask; - for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { ret = skl_allocate_pipe_ddb(new_crtc_state); @@ -5720,6 +5799,17 @@ skl_compute_ddb(struct intel_atomic_state *state) return ret; } + old_dbuf_state = intel_atomic_get_old_dbuf_state(state); + new_dbuf_state = intel_atomic_get_new_dbuf_state(state); + + if (new_dbuf_state && + new_dbuf_state->enabled_slices != old_dbuf_state->enabled_slices) + drm_dbg_kms(&dev_priv->drm, + "Enabled dbuf slices 0x%x -> 0x%x (out of %d dbuf slices)\n", + old_dbuf_state->enabled_slices, + new_dbuf_state->enabled_slices, + INTEL_INFO(dev_priv)->num_supported_dbuf_slices); + return 0; } @@ -5855,7 +5945,8 @@ skl_print_wm_changes(struct intel_atomic_state *state) } } -static int intel_add_all_pipes(struct intel_atomic_state *state) +static int intel_add_affected_pipes(struct intel_atomic_state *state, + u8 pipe_mask) { struct drm_i915_private *dev_priv = to_i915(state->base.dev); struct intel_crtc *crtc; @@ -5863,6 +5954,9 @@ static int intel_add_all_pipes(struct intel_atomic_state *state) for_each_intel_crtc(&dev_priv->drm, crtc) { struct intel_crtc_state *crtc_state; + if ((pipe_mask & BIT(crtc->pipe)) == 0) + continue; + crtc_state = intel_atomic_get_crtc_state(&state->base, crtc); if (IS_ERR(crtc_state)) return PTR_ERR(crtc_state); @@ -5875,49 +5969,54 @@ static int skl_ddb_add_affected_pipes(struct intel_atomic_state *state) { struct drm_i915_private *dev_priv = to_i915(state->base.dev); - int ret; + struct intel_crtc_state *crtc_state; + struct intel_crtc *crtc; + int i, ret; - /* - * If this is our first atomic update following hardware readout, - * we can't trust the DDB that the BIOS programmed for us. Let's - * pretend that all pipes switched active status so that we'll - * ensure a full DDB recompute. - */ if (dev_priv->wm.distrust_bios_wm) { - ret = drm_modeset_lock(&dev_priv->drm.mode_config.connection_mutex, - state->base.acquire_ctx); + /* + * skl_ddb_get_pipe_allocation_limits() currently requires + * all active pipes to be included in the state so that + * it can redistribute the dbuf among them, and it really + * wants to recompute things when distrust_bios_wm is set + * so we add all the pipes to the state. + */ + ret = intel_add_affected_pipes(state, ~0); if (ret) return ret; + } + + for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) { + struct intel_dbuf_state *new_dbuf_state; + const struct intel_dbuf_state *old_dbuf_state; - state->active_pipe_changes = INTEL_INFO(dev_priv)->pipe_mask; + new_dbuf_state = intel_atomic_get_dbuf_state(state); + if (IS_ERR(new_dbuf_state)) + return PTR_ERR(new_dbuf_state); + + old_dbuf_state = intel_atomic_get_old_dbuf_state(state); + + new_dbuf_state->active_pipes = + intel_calc_active_pipes(state, old_dbuf_state->active_pipes); + + if (old_dbuf_state->active_pipes == new_dbuf_state->active_pipes) + break; + + ret = intel_atomic_lock_global_state(&new_dbuf_state->base); + if (ret) + return ret; /* - * We usually only initialize state->active_pipes 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. + * skl_ddb_get_pipe_allocation_limits() currently requires + * all active pipes to be included in the state so that + * it can redistribute the dbuf among them. */ - if (!state->modeset) - state->active_pipes = dev_priv->active_pipes; - } - - /* - * If the modeset changes which CRTC's are active, we need to - * recompute the DDB allocation for *all* active pipes, even - * those that weren't otherwise being modified in any way by this - * atomic commit. Due to the shrinking of the per-pipe allocations - * when new active CRTC's are added, it's possible for a pipe that - * we were already using and aren't changing at all here to suddenly - * become invalid if its DDB needs exceeds its new allocation. - * - * Note that if we wind up doing a full DDB recompute, we can't let - * any other display updates race with this transaction, so we need - * to grab the lock on *all* CRTC's. - */ - if (state->active_pipe_changes || state->modeset) { - ret = intel_add_all_pipes(state); + ret = intel_add_affected_pipes(state, + new_dbuf_state->active_pipes); if (ret) return ret; + + break; } return 0; @@ -6163,7 +6262,6 @@ void skl_wm_get_hw_state(struct drm_i915_private *dev_priv) struct intel_crtc *crtc; struct intel_crtc_state *crtc_state; - skl_ddb_get_hw_state(dev_priv); for_each_intel_crtc(&dev_priv->drm, crtc) { crtc_state = to_intel_crtc_state(crtc->base.state); @@ -6735,7 +6833,9 @@ static bool intel_can_enable_ipc(struct drm_i915_private *dev_priv) return false; /* Display WA #1141: SKL:all KBL:all CFL */ - if (IS_KABYLAKE(dev_priv) || IS_COFFEELAKE(dev_priv)) + if (IS_KABYLAKE(dev_priv) || + IS_COFFEELAKE(dev_priv) || + IS_COMETLAKE(dev_priv)) return dev_priv->dram_info.symmetric_memory; return true; @@ -7414,7 +7514,7 @@ void intel_init_clock_gating_hooks(struct drm_i915_private *dev_priv) dev_priv->display.init_clock_gating = icl_init_clock_gating; else if (IS_CANNONLAKE(dev_priv)) dev_priv->display.init_clock_gating = cnl_init_clock_gating; - else if (IS_COFFEELAKE(dev_priv)) + else if (IS_COFFEELAKE(dev_priv) || IS_COMETLAKE(dev_priv)) dev_priv->display.init_clock_gating = cfl_init_clock_gating; else if (IS_SKYLAKE(dev_priv)) dev_priv->display.init_clock_gating = skl_init_clock_gating; @@ -7544,3 +7644,89 @@ void intel_pm_setup(struct drm_i915_private *dev_priv) dev_priv->runtime_pm.suspended = false; atomic_set(&dev_priv->runtime_pm.wakeref_count, 0); } + +static struct intel_global_state *intel_dbuf_duplicate_state(struct intel_global_obj *obj) +{ + struct intel_dbuf_state *dbuf_state; + + dbuf_state = kmemdup(obj->state, sizeof(*dbuf_state), GFP_KERNEL); + if (!dbuf_state) + return NULL; + + return &dbuf_state->base; +} + +static void intel_dbuf_destroy_state(struct intel_global_obj *obj, + struct intel_global_state *state) +{ + kfree(state); +} + +static const struct intel_global_state_funcs intel_dbuf_funcs = { + .atomic_duplicate_state = intel_dbuf_duplicate_state, + .atomic_destroy_state = intel_dbuf_destroy_state, +}; + +struct intel_dbuf_state * +intel_atomic_get_dbuf_state(struct intel_atomic_state *state) +{ + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct intel_global_state *dbuf_state; + + dbuf_state = intel_atomic_get_global_obj_state(state, &dev_priv->dbuf.obj); + if (IS_ERR(dbuf_state)) + return ERR_CAST(dbuf_state); + + return to_intel_dbuf_state(dbuf_state); +} + +int intel_dbuf_init(struct drm_i915_private *dev_priv) +{ + struct intel_dbuf_state *dbuf_state; + + dbuf_state = kzalloc(sizeof(*dbuf_state), GFP_KERNEL); + if (!dbuf_state) + return -ENOMEM; + + intel_atomic_global_obj_init(dev_priv, &dev_priv->dbuf.obj, + &dbuf_state->base, &intel_dbuf_funcs); + + return 0; +} + +void intel_dbuf_pre_plane_update(struct intel_atomic_state *state) +{ + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + const struct intel_dbuf_state *new_dbuf_state = + intel_atomic_get_new_dbuf_state(state); + const struct intel_dbuf_state *old_dbuf_state = + intel_atomic_get_old_dbuf_state(state); + + if (!new_dbuf_state || + new_dbuf_state->enabled_slices == old_dbuf_state->enabled_slices) + return; + + WARN_ON(!new_dbuf_state->base.changed); + + gen9_dbuf_slices_update(dev_priv, + old_dbuf_state->enabled_slices | + new_dbuf_state->enabled_slices); +} + +void intel_dbuf_post_plane_update(struct intel_atomic_state *state) +{ + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + const struct intel_dbuf_state *new_dbuf_state = + intel_atomic_get_new_dbuf_state(state); + const struct intel_dbuf_state *old_dbuf_state = + intel_atomic_get_old_dbuf_state(state); + + if (!new_dbuf_state || + new_dbuf_state->enabled_slices == old_dbuf_state->enabled_slices) + return; + + WARN_ON(!new_dbuf_state->base.changed); + + gen9_dbuf_slices_update(dev_priv, + new_dbuf_state->enabled_slices); +} diff --git a/drivers/gpu/drm/i915/intel_pm.h b/drivers/gpu/drm/i915/intel_pm.h index 614ac7f8d4cc..a2473594c2db 100644 --- a/drivers/gpu/drm/i915/intel_pm.h +++ b/drivers/gpu/drm/i915/intel_pm.h @@ -8,8 +8,10 @@ #include <linux/types.h> -#include "i915_reg.h" #include "display/intel_bw.h" +#include "display/intel_global_state.h" + +#include "i915_reg.h" struct drm_device; struct drm_i915_private; @@ -38,6 +40,9 @@ void skl_pipe_ddb_get_hw_state(struct intel_crtc *crtc, struct skl_ddb_entry *ddb_y, struct skl_ddb_entry *ddb_uv); void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv); +u16 intel_get_ddb_size(struct drm_i915_private *dev_priv); +u32 skl_ddb_dbuf_slice_mask(struct drm_i915_private *dev_priv, + const struct skl_ddb_entry *entry); void skl_pipe_wm_get_hw_state(struct intel_crtc *crtc, struct skl_pipe_wm *out); void g4x_wm_sanitize(struct drm_i915_private *dev_priv); @@ -63,4 +68,26 @@ void intel_enable_ipc(struct drm_i915_private *dev_priv); bool intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable); +struct intel_dbuf_state { + struct intel_global_state base; + + u8 enabled_slices; + u8 active_pipes; +}; + +int intel_dbuf_init(struct drm_i915_private *dev_priv); + +struct intel_dbuf_state * +intel_atomic_get_dbuf_state(struct intel_atomic_state *state); + +#define to_intel_dbuf_state(x) container_of((x), struct intel_dbuf_state, base) +#define intel_atomic_get_old_dbuf_state(state) \ + to_intel_dbuf_state(intel_atomic_get_old_global_obj_state(state, &to_i915(state->base.dev)->dbuf.obj)) +#define intel_atomic_get_new_dbuf_state(state) \ + to_intel_dbuf_state(intel_atomic_get_new_global_obj_state(state, &to_i915(state->base.dev)->dbuf.obj)) + +int intel_dbuf_init(struct drm_i915_private *dev_priv); +void intel_dbuf_pre_plane_update(struct intel_atomic_state *state); +void intel_dbuf_post_plane_update(struct intel_atomic_state *state); + #endif /* __INTEL_PM_H__ */ diff --git a/drivers/gpu/drm/i915/intel_region_lmem.c b/drivers/gpu/drm/i915/intel_region_lmem.c index 14b59b899c9b..40d8f1a95df6 100644 --- a/drivers/gpu/drm/i915/intel_region_lmem.c +++ b/drivers/gpu/drm/i915/intel_region_lmem.c @@ -76,7 +76,7 @@ region_lmem_init(struct intel_memory_region *mem) { int ret; - if (i915_modparams.fake_lmem_start) { + if (mem->i915->params.fake_lmem_start) { ret = init_fake_lmem_bar(mem); GEM_BUG_ON(ret); } @@ -111,12 +111,12 @@ intel_setup_fake_lmem(struct drm_i915_private *i915) resource_size_t start; GEM_BUG_ON(i915_ggtt_has_aperture(&i915->ggtt)); - GEM_BUG_ON(!i915_modparams.fake_lmem_start); + GEM_BUG_ON(!i915->params.fake_lmem_start); /* Your mappable aperture belongs to me now! */ mappable_end = pci_resource_len(pdev, 2); io_start = pci_resource_start(pdev, 2), - start = i915_modparams.fake_lmem_start; + start = i915->params.fake_lmem_start; mem = intel_memory_region_create(i915, start, diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index 9cb2d7548daa..153ca9e65382 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -116,6 +116,9 @@ track_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm) static void untrack_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm, depot_stack_handle_t stack) { + struct drm_i915_private *i915 = container_of(rpm, + struct drm_i915_private, + runtime_pm); unsigned long flags, n; bool found = false; @@ -134,9 +137,9 @@ static void untrack_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm, } spin_unlock_irqrestore(&rpm->debug.lock, flags); - if (WARN(!found, - "Unmatched wakeref (tracking %lu), count %u\n", - rpm->debug.count, atomic_read(&rpm->wakeref_count))) { + if (drm_WARN(&i915->drm, !found, + "Unmatched wakeref (tracking %lu), count %u\n", + rpm->debug.count, atomic_read(&rpm->wakeref_count))) { char *buf; buf = kmalloc(PAGE_SIZE, GFP_NOWAIT | __GFP_NOWARN); @@ -355,10 +358,14 @@ intel_runtime_pm_release(struct intel_runtime_pm *rpm, int wakelock) static intel_wakeref_t __intel_runtime_pm_get(struct intel_runtime_pm *rpm, bool wakelock) { + struct drm_i915_private *i915 = container_of(rpm, + struct drm_i915_private, + runtime_pm); int ret; ret = pm_runtime_get_sync(rpm->kdev); - WARN_ONCE(ret < 0, "pm_runtime_get_sync() failed: %d\n", ret); + drm_WARN_ONCE(&i915->drm, ret < 0, + "pm_runtime_get_sync() failed: %d\n", ret); intel_runtime_pm_acquire(rpm, wakelock); @@ -539,6 +546,9 @@ void intel_runtime_pm_put(struct intel_runtime_pm *rpm, intel_wakeref_t wref) */ void intel_runtime_pm_enable(struct intel_runtime_pm *rpm) { + struct drm_i915_private *i915 = container_of(rpm, + struct drm_i915_private, + runtime_pm); struct device *kdev = rpm->kdev; /* @@ -565,7 +575,8 @@ void intel_runtime_pm_enable(struct intel_runtime_pm *rpm) pm_runtime_dont_use_autosuspend(kdev); ret = pm_runtime_get_sync(kdev); - WARN(ret < 0, "pm_runtime_get_sync() failed: %d\n", ret); + drm_WARN(&i915->drm, ret < 0, + "pm_runtime_get_sync() failed: %d\n", ret); } else { pm_runtime_use_autosuspend(kdev); } @@ -580,11 +591,14 @@ void intel_runtime_pm_enable(struct intel_runtime_pm *rpm) void intel_runtime_pm_disable(struct intel_runtime_pm *rpm) { + struct drm_i915_private *i915 = container_of(rpm, + struct drm_i915_private, + runtime_pm); struct device *kdev = rpm->kdev; /* Transfer rpm ownership back to core */ - WARN(pm_runtime_get_sync(kdev) < 0, - "Failed to pass rpm ownership back to core\n"); + drm_WARN(&i915->drm, pm_runtime_get_sync(kdev) < 0, + "Failed to pass rpm ownership back to core\n"); pm_runtime_dont_use_autosuspend(kdev); @@ -594,12 +608,15 @@ void intel_runtime_pm_disable(struct intel_runtime_pm *rpm) void intel_runtime_pm_driver_release(struct intel_runtime_pm *rpm) { + struct drm_i915_private *i915 = container_of(rpm, + struct drm_i915_private, + runtime_pm); int count = atomic_read(&rpm->wakeref_count); - WARN(count, - "i915 raw-wakerefs=%d wakelocks=%d on cleanup\n", - intel_rpm_raw_wakeref_count(count), - intel_rpm_wakelock_count(count)); + drm_WARN(&i915->drm, count, + "i915 raw-wakerefs=%d wakelocks=%d on cleanup\n", + intel_rpm_raw_wakeref_count(count), + intel_rpm_wakelock_count(count)); untrack_all_intel_runtime_pm_wakerefs(rpm); } diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index a61cb8ca4d50..592364aed2da 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -709,7 +709,7 @@ static void __intel_uncore_forcewake_put(struct intel_uncore *uncore, continue; } - fw_domain_arm_timer(domain); + uncore->funcs.force_wake_put(uncore, domain->mask); } } @@ -1185,7 +1185,7 @@ __unclaimed_reg_debug(struct intel_uncore *uncore, read ? "read from" : "write to", i915_mmio_reg_offset(reg))) /* Only report the first N failures */ - i915_modparams.mmio_debug--; + uncore->i915->params.mmio_debug--; } static inline void @@ -1194,7 +1194,7 @@ unclaimed_reg_debug(struct intel_uncore *uncore, const bool read, const bool before) { - if (likely(!i915_modparams.mmio_debug)) + if (likely(!uncore->i915->params.mmio_debug)) return; /* interrupts are disabled and re-enabled around uncore->lock usage */ @@ -2093,12 +2093,12 @@ intel_uncore_arm_unclaimed_mmio_detection(struct intel_uncore *uncore) goto out; if (unlikely(check_for_unclaimed_mmio(uncore))) { - if (!i915_modparams.mmio_debug) { + if (!uncore->i915->params.mmio_debug) { drm_dbg(&uncore->i915->drm, "Unclaimed register detected, " "enabling oneshot unclaimed register reporting. " "Please use i915.mmio_debug=N for more information.\n"); - i915_modparams.mmio_debug++; + uncore->i915->params.mmio_debug++; } uncore->debug->unclaimed_mmio_check--; ret = true; diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c index 2e471500a646..0016ffc7d914 100644 --- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c @@ -97,6 +97,7 @@ static void fake_put_pages(struct drm_i915_gem_object *obj, } static const struct drm_i915_gem_object_ops fake_ops = { + .name = "fake-gem", .flags = I915_GEM_OBJECT_IS_SHRINKABLE, .get_pages = fake_get_pages, .put_pages = fake_put_pages, diff --git a/drivers/gpu/drm/i915/selftests/i915_live_selftests.h b/drivers/gpu/drm/i915/selftests/i915_live_selftests.h index 5dd5d81646c4..a92c0e9b7e6b 100644 --- a/drivers/gpu/drm/i915/selftests/i915_live_selftests.h +++ b/drivers/gpu/drm/i915/selftests/i915_live_selftests.h @@ -11,9 +11,9 @@ * a module parameter. It must be unique and legal for a C identifier. * * The function should be of type int function(void). It may be conditionally - * compiled using #if IS_ENABLED(DRM_I915_SELFTEST). + * compiled using #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST). * - * Tests are executed in order by igt/drv_selftest + * Tests are executed in order by igt/i915_selftest */ selftest(sanitycheck, i915_live_sanitycheck) /* keep first (igt selfcheck) */ selftest(uncore, intel_uncore_live_selftests) diff --git a/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h b/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h index 6090ce35226b..3db34d3eea58 100644 --- a/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h +++ b/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h @@ -11,9 +11,9 @@ * a module parameter. It must be unique and legal for a C identifier. * * The function should be of type int function(void). It may be conditionally - * compiled using #if IS_ENABLED(DRM_I915_SELFTEST). + * compiled using #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST). * - * Tests are executed in order by igt/drv_selftest + * Tests are executed in order by igt/i915_selftest */ selftest(sanitycheck, i915_mock_sanitycheck) /* keep first (igt selfcheck) */ selftest(shmem, shmem_utils_mock_selftests) diff --git a/drivers/gpu/drm/i915/selftests/i915_perf.c b/drivers/gpu/drm/i915/selftests/i915_perf.c index 8eb3108f1767..be54570c407c 100644 --- a/drivers/gpu/drm/i915/selftests/i915_perf.c +++ b/drivers/gpu/drm/i915/selftests/i915_perf.c @@ -162,7 +162,7 @@ static int write_timestamp(struct i915_request *rq, int slot) return PTR_ERR(cs); len = 5; - if (INTEL_GEN(rq->i915) >= 8) + if (INTEL_GEN(rq->engine->i915) >= 8) len++; *cs++ = GFX_OP_PIPE_CONTROL(len); diff --git a/drivers/gpu/drm/i915/selftests/i915_perf_selftests.h b/drivers/gpu/drm/i915/selftests/i915_perf_selftests.h index d8da142985eb..c2389f8a257d 100644 --- a/drivers/gpu/drm/i915/selftests/i915_perf_selftests.h +++ b/drivers/gpu/drm/i915/selftests/i915_perf_selftests.h @@ -11,7 +11,7 @@ * a module parameter. It must be unique and legal for a C identifier. * * The function should be of type int function(void). It may be conditionally - * compiled using #if IS_ENABLED(DRM_I915_SELFTEST). + * compiled using #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST). * * Tests are executed in order by igt/i915_selftest */ diff --git a/drivers/gpu/drm/i915/selftests/i915_request.c b/drivers/gpu/drm/i915/selftests/i915_request.c index 6014e8dfcbb1..9271aad7f779 100644 --- a/drivers/gpu/drm/i915/selftests/i915_request.c +++ b/drivers/gpu/drm/i915/selftests/i915_request.c @@ -24,16 +24,21 @@ #include <linux/prime_numbers.h> #include <linux/pm_qos.h> +#include <linux/sort.h> #include "gem/i915_gem_pm.h" #include "gem/selftests/mock_context.h" +#include "gt/intel_engine_heartbeat.h" #include "gt/intel_engine_pm.h" #include "gt/intel_engine_user.h" #include "gt/intel_gt.h" +#include "gt/intel_gt_requests.h" +#include "gt/selftest_engine_heartbeat.h" #include "i915_random.h" #include "i915_selftest.h" +#include "igt_flush_test.h" #include "igt_live_test.h" #include "igt_spinner.h" #include "lib_sw_fence.h" @@ -1524,6 +1529,808 @@ struct perf_series { struct intel_context *ce[]; }; +static int cmp_u32(const void *A, const void *B) +{ + const u32 *a = A, *b = B; + + return *a - *b; +} + +static u32 trifilter(u32 *a) +{ + u64 sum; + +#define TF_COUNT 5 + sort(a, TF_COUNT, sizeof(*a), cmp_u32, NULL); + + sum = mul_u32_u32(a[2], 2); + sum += a[1]; + sum += a[3]; + + GEM_BUG_ON(sum > U32_MAX); + return sum; +#define TF_BIAS 2 +} + +static u64 cycles_to_ns(struct intel_engine_cs *engine, u32 cycles) +{ + u64 ns = i915_cs_timestamp_ticks_to_ns(engine->i915, cycles); + + return DIV_ROUND_CLOSEST(ns, 1 << TF_BIAS); +} + +static u32 *emit_timestamp_store(u32 *cs, struct intel_context *ce, u32 offset) +{ + *cs++ = MI_STORE_REGISTER_MEM_GEN8 | MI_USE_GGTT; + *cs++ = i915_mmio_reg_offset(RING_TIMESTAMP((ce->engine->mmio_base))); + *cs++ = offset; + *cs++ = 0; + + return cs; +} + +static u32 *emit_store_dw(u32 *cs, u32 offset, u32 value) +{ + *cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT; + *cs++ = offset; + *cs++ = 0; + *cs++ = value; + + return cs; +} + +static u32 *emit_semaphore_poll(u32 *cs, u32 mode, u32 value, u32 offset) +{ + *cs++ = MI_SEMAPHORE_WAIT | + MI_SEMAPHORE_GLOBAL_GTT | + MI_SEMAPHORE_POLL | + mode; + *cs++ = value; + *cs++ = offset; + *cs++ = 0; + + return cs; +} + +static u32 *emit_semaphore_poll_until(u32 *cs, u32 offset, u32 value) +{ + return emit_semaphore_poll(cs, MI_SEMAPHORE_SAD_EQ_SDD, value, offset); +} + +static void semaphore_set(u32 *sema, u32 value) +{ + WRITE_ONCE(*sema, value); + wmb(); /* flush the update to the cache, and beyond */ +} + +static u32 *hwsp_scratch(const struct intel_context *ce) +{ + return memset32(ce->engine->status_page.addr + 1000, 0, 21); +} + +static u32 hwsp_offset(const struct intel_context *ce, u32 *dw) +{ + return (i915_ggtt_offset(ce->engine->status_page.vma) + + offset_in_page(dw)); +} + +static int measure_semaphore_response(struct intel_context *ce) +{ + u32 *sema = hwsp_scratch(ce); + const u32 offset = hwsp_offset(ce, sema); + u32 elapsed[TF_COUNT], cycles; + struct i915_request *rq; + u32 *cs; + int err; + int i; + + /* + * Measure how many cycles it takes for the HW to detect the change + * in a semaphore value. + * + * A: read CS_TIMESTAMP from CPU + * poke semaphore + * B: read CS_TIMESTAMP on GPU + * + * Semaphore latency: B - A + */ + + semaphore_set(sema, -1); + + rq = i915_request_create(ce); + if (IS_ERR(rq)) + return PTR_ERR(rq); + + cs = intel_ring_begin(rq, 4 + 12 * ARRAY_SIZE(elapsed)); + if (IS_ERR(cs)) { + i915_request_add(rq); + err = PTR_ERR(cs); + goto err; + } + + cs = emit_store_dw(cs, offset, 0); + for (i = 1; i <= ARRAY_SIZE(elapsed); i++) { + cs = emit_semaphore_poll_until(cs, offset, i); + cs = emit_timestamp_store(cs, ce, offset + i * sizeof(u32)); + cs = emit_store_dw(cs, offset, 0); + } + + intel_ring_advance(rq, cs); + i915_request_add(rq); + + if (wait_for(READ_ONCE(*sema) == 0, 50)) { + err = -EIO; + goto err; + } + + for (i = 1; i <= ARRAY_SIZE(elapsed); i++) { + preempt_disable(); + cycles = ENGINE_READ_FW(ce->engine, RING_TIMESTAMP); + semaphore_set(sema, i); + preempt_enable(); + + if (wait_for(READ_ONCE(*sema) == 0, 50)) { + err = -EIO; + goto err; + } + + elapsed[i - 1] = sema[i] - cycles; + } + + cycles = trifilter(elapsed); + pr_info("%s: semaphore response %d cycles, %lluns\n", + ce->engine->name, cycles >> TF_BIAS, + cycles_to_ns(ce->engine, cycles)); + + return intel_gt_wait_for_idle(ce->engine->gt, HZ); + +err: + intel_gt_set_wedged(ce->engine->gt); + return err; +} + +static int measure_idle_dispatch(struct intel_context *ce) +{ + u32 *sema = hwsp_scratch(ce); + const u32 offset = hwsp_offset(ce, sema); + u32 elapsed[TF_COUNT], cycles; + u32 *cs; + int err; + int i; + + /* + * Measure how long it takes for us to submit a request while the + * engine is idle, but is resting in our context. + * + * A: read CS_TIMESTAMP from CPU + * submit request + * B: read CS_TIMESTAMP on GPU + * + * Submission latency: B - A + */ + + for (i = 0; i < ARRAY_SIZE(elapsed); i++) { + struct i915_request *rq; + + err = intel_gt_wait_for_idle(ce->engine->gt, HZ / 2); + if (err) + return err; + + rq = i915_request_create(ce); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto err; + } + + cs = intel_ring_begin(rq, 4); + if (IS_ERR(cs)) { + i915_request_add(rq); + err = PTR_ERR(cs); + goto err; + } + + cs = emit_timestamp_store(cs, ce, offset + i * sizeof(u32)); + + intel_ring_advance(rq, cs); + + preempt_disable(); + local_bh_disable(); + elapsed[i] = ENGINE_READ_FW(ce->engine, RING_TIMESTAMP); + i915_request_add(rq); + local_bh_enable(); + preempt_enable(); + } + + err = intel_gt_wait_for_idle(ce->engine->gt, HZ / 2); + if (err) + goto err; + + for (i = 0; i < ARRAY_SIZE(elapsed); i++) + elapsed[i] = sema[i] - elapsed[i]; + + cycles = trifilter(elapsed); + pr_info("%s: idle dispatch latency %d cycles, %lluns\n", + ce->engine->name, cycles >> TF_BIAS, + cycles_to_ns(ce->engine, cycles)); + + return intel_gt_wait_for_idle(ce->engine->gt, HZ); + +err: + intel_gt_set_wedged(ce->engine->gt); + return err; +} + +static int measure_busy_dispatch(struct intel_context *ce) +{ + u32 *sema = hwsp_scratch(ce); + const u32 offset = hwsp_offset(ce, sema); + u32 elapsed[TF_COUNT + 1], cycles; + u32 *cs; + int err; + int i; + + /* + * Measure how long it takes for us to submit a request while the + * engine is busy, polling on a semaphore in our context. With + * direct submission, this will include the cost of a lite restore. + * + * A: read CS_TIMESTAMP from CPU + * submit request + * B: read CS_TIMESTAMP on GPU + * + * Submission latency: B - A + */ + + for (i = 1; i <= ARRAY_SIZE(elapsed); i++) { + struct i915_request *rq; + + rq = i915_request_create(ce); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto err; + } + + cs = intel_ring_begin(rq, 12); + if (IS_ERR(cs)) { + i915_request_add(rq); + err = PTR_ERR(cs); + goto err; + } + + cs = emit_store_dw(cs, offset + i * sizeof(u32), -1); + cs = emit_semaphore_poll_until(cs, offset, i); + cs = emit_timestamp_store(cs, ce, offset + i * sizeof(u32)); + + intel_ring_advance(rq, cs); + + if (i > 1 && wait_for(READ_ONCE(sema[i - 1]), 500)) { + err = -EIO; + goto err; + } + + preempt_disable(); + local_bh_disable(); + elapsed[i - 1] = ENGINE_READ_FW(ce->engine, RING_TIMESTAMP); + i915_request_add(rq); + local_bh_enable(); + semaphore_set(sema, i - 1); + preempt_enable(); + } + + wait_for(READ_ONCE(sema[i - 1]), 500); + semaphore_set(sema, i - 1); + + for (i = 1; i <= TF_COUNT; i++) { + GEM_BUG_ON(sema[i] == -1); + elapsed[i - 1] = sema[i] - elapsed[i]; + } + + cycles = trifilter(elapsed); + pr_info("%s: busy dispatch latency %d cycles, %lluns\n", + ce->engine->name, cycles >> TF_BIAS, + cycles_to_ns(ce->engine, cycles)); + + return intel_gt_wait_for_idle(ce->engine->gt, HZ); + +err: + intel_gt_set_wedged(ce->engine->gt); + return err; +} + +static int plug(struct intel_engine_cs *engine, u32 *sema, u32 mode, int value) +{ + const u32 offset = + i915_ggtt_offset(engine->status_page.vma) + + offset_in_page(sema); + struct i915_request *rq; + u32 *cs; + + rq = i915_request_create(engine->kernel_context); + if (IS_ERR(rq)) + return PTR_ERR(rq); + + cs = intel_ring_begin(rq, 4); + if (IS_ERR(cs)) { + i915_request_add(rq); + return PTR_ERR(cs); + } + + cs = emit_semaphore_poll(cs, mode, value, offset); + + intel_ring_advance(rq, cs); + i915_request_add(rq); + + return 0; +} + +static int measure_inter_request(struct intel_context *ce) +{ + u32 *sema = hwsp_scratch(ce); + const u32 offset = hwsp_offset(ce, sema); + u32 elapsed[TF_COUNT + 1], cycles; + struct i915_sw_fence *submit; + int i, err; + + /* + * Measure how long it takes to advance from one request into the + * next. Between each request we flush the GPU caches to memory, + * update the breadcrumbs, and then invalidate those caches. + * We queue up all the requests to be submitted in one batch so + * it should be one set of contiguous measurements. + * + * A: read CS_TIMESTAMP on GPU + * advance request + * B: read CS_TIMESTAMP on GPU + * + * Request latency: B - A + */ + + err = plug(ce->engine, sema, MI_SEMAPHORE_SAD_NEQ_SDD, 0); + if (err) + return err; + + submit = heap_fence_create(GFP_KERNEL); + if (!submit) { + semaphore_set(sema, 1); + return -ENOMEM; + } + + intel_engine_flush_submission(ce->engine); + for (i = 1; i <= ARRAY_SIZE(elapsed); i++) { + struct i915_request *rq; + u32 *cs; + + rq = i915_request_create(ce); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto err_submit; + } + + err = i915_sw_fence_await_sw_fence_gfp(&rq->submit, + submit, + GFP_KERNEL); + if (err < 0) { + i915_request_add(rq); + goto err_submit; + } + + cs = intel_ring_begin(rq, 4); + if (IS_ERR(cs)) { + i915_request_add(rq); + err = PTR_ERR(cs); + goto err_submit; + } + + cs = emit_timestamp_store(cs, ce, offset + i * sizeof(u32)); + + intel_ring_advance(rq, cs); + i915_request_add(rq); + } + local_bh_disable(); + i915_sw_fence_commit(submit); + local_bh_enable(); + intel_engine_flush_submission(ce->engine); + heap_fence_put(submit); + + semaphore_set(sema, 1); + err = intel_gt_wait_for_idle(ce->engine->gt, HZ / 2); + if (err) + goto err; + + for (i = 1; i <= TF_COUNT; i++) + elapsed[i - 1] = sema[i + 1] - sema[i]; + + cycles = trifilter(elapsed); + pr_info("%s: inter-request latency %d cycles, %lluns\n", + ce->engine->name, cycles >> TF_BIAS, + cycles_to_ns(ce->engine, cycles)); + + return intel_gt_wait_for_idle(ce->engine->gt, HZ); + +err_submit: + i915_sw_fence_commit(submit); + heap_fence_put(submit); + semaphore_set(sema, 1); +err: + intel_gt_set_wedged(ce->engine->gt); + return err; +} + +static int measure_context_switch(struct intel_context *ce) +{ + u32 *sema = hwsp_scratch(ce); + const u32 offset = hwsp_offset(ce, sema); + struct i915_request *fence = NULL; + u32 elapsed[TF_COUNT + 1], cycles; + int i, j, err; + u32 *cs; + + /* + * Measure how long it takes to advance from one request in one + * context to a request in another context. This allows us to + * measure how long the context save/restore take, along with all + * the inter-context setup we require. + * + * A: read CS_TIMESTAMP on GPU + * switch context + * B: read CS_TIMESTAMP on GPU + * + * Context switch latency: B - A + */ + + err = plug(ce->engine, sema, MI_SEMAPHORE_SAD_NEQ_SDD, 0); + if (err) + return err; + + for (i = 1; i <= ARRAY_SIZE(elapsed); i++) { + struct intel_context *arr[] = { + ce, ce->engine->kernel_context + }; + u32 addr = offset + ARRAY_SIZE(arr) * i * sizeof(u32); + + for (j = 0; j < ARRAY_SIZE(arr); j++) { + struct i915_request *rq; + + rq = i915_request_create(arr[j]); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto err_fence; + } + + if (fence) { + err = i915_request_await_dma_fence(rq, + &fence->fence); + if (err) { + i915_request_add(rq); + goto err_fence; + } + } + + cs = intel_ring_begin(rq, 4); + if (IS_ERR(cs)) { + i915_request_add(rq); + err = PTR_ERR(cs); + goto err_fence; + } + + cs = emit_timestamp_store(cs, ce, addr); + addr += sizeof(u32); + + intel_ring_advance(rq, cs); + + i915_request_put(fence); + fence = i915_request_get(rq); + + i915_request_add(rq); + } + } + i915_request_put(fence); + intel_engine_flush_submission(ce->engine); + + semaphore_set(sema, 1); + err = intel_gt_wait_for_idle(ce->engine->gt, HZ / 2); + if (err) + goto err; + + for (i = 1; i <= TF_COUNT; i++) + elapsed[i - 1] = sema[2 * i + 2] - sema[2 * i + 1]; + + cycles = trifilter(elapsed); + pr_info("%s: context switch latency %d cycles, %lluns\n", + ce->engine->name, cycles >> TF_BIAS, + cycles_to_ns(ce->engine, cycles)); + + return intel_gt_wait_for_idle(ce->engine->gt, HZ); + +err_fence: + i915_request_put(fence); + semaphore_set(sema, 1); +err: + intel_gt_set_wedged(ce->engine->gt); + return err; +} + +static int measure_preemption(struct intel_context *ce) +{ + u32 *sema = hwsp_scratch(ce); + const u32 offset = hwsp_offset(ce, sema); + u32 elapsed[TF_COUNT], cycles; + u32 *cs; + int err; + int i; + + /* + * We measure two latencies while triggering preemption. The first + * latency is how long it takes for us to submit a preempting request. + * The second latency is how it takes for us to return from the + * preemption back to the original context. + * + * A: read CS_TIMESTAMP from CPU + * submit preemption + * B: read CS_TIMESTAMP on GPU (in preempting context) + * context switch + * C: read CS_TIMESTAMP on GPU (in original context) + * + * Preemption dispatch latency: B - A + * Preemption switch latency: C - B + */ + + if (!intel_engine_has_preemption(ce->engine)) + return 0; + + for (i = 1; i <= ARRAY_SIZE(elapsed); i++) { + u32 addr = offset + 2 * i * sizeof(u32); + struct i915_request *rq; + + rq = i915_request_create(ce); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto err; + } + + cs = intel_ring_begin(rq, 12); + if (IS_ERR(cs)) { + i915_request_add(rq); + err = PTR_ERR(cs); + goto err; + } + + cs = emit_store_dw(cs, addr, -1); + cs = emit_semaphore_poll_until(cs, offset, i); + cs = emit_timestamp_store(cs, ce, addr + sizeof(u32)); + + intel_ring_advance(rq, cs); + i915_request_add(rq); + + if (wait_for(READ_ONCE(sema[2 * i]) == -1, 500)) { + err = -EIO; + goto err; + } + + rq = i915_request_create(ce->engine->kernel_context); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto err; + } + + cs = intel_ring_begin(rq, 8); + if (IS_ERR(cs)) { + i915_request_add(rq); + err = PTR_ERR(cs); + goto err; + } + + cs = emit_timestamp_store(cs, ce, addr); + cs = emit_store_dw(cs, offset, i); + + intel_ring_advance(rq, cs); + rq->sched.attr.priority = I915_PRIORITY_BARRIER; + + elapsed[i - 1] = ENGINE_READ_FW(ce->engine, RING_TIMESTAMP); + i915_request_add(rq); + } + + if (wait_for(READ_ONCE(sema[2 * i - 2]) != -1, 500)) { + err = -EIO; + goto err; + } + + for (i = 1; i <= TF_COUNT; i++) + elapsed[i - 1] = sema[2 * i + 0] - elapsed[i - 1]; + + cycles = trifilter(elapsed); + pr_info("%s: preemption dispatch latency %d cycles, %lluns\n", + ce->engine->name, cycles >> TF_BIAS, + cycles_to_ns(ce->engine, cycles)); + + for (i = 1; i <= TF_COUNT; i++) + elapsed[i - 1] = sema[2 * i + 1] - sema[2 * i + 0]; + + cycles = trifilter(elapsed); + pr_info("%s: preemption switch latency %d cycles, %lluns\n", + ce->engine->name, cycles >> TF_BIAS, + cycles_to_ns(ce->engine, cycles)); + + return intel_gt_wait_for_idle(ce->engine->gt, HZ); + +err: + intel_gt_set_wedged(ce->engine->gt); + return err; +} + +struct signal_cb { + struct dma_fence_cb base; + bool seen; +}; + +static void signal_cb(struct dma_fence *fence, struct dma_fence_cb *cb) +{ + struct signal_cb *s = container_of(cb, typeof(*s), base); + + smp_store_mb(s->seen, true); /* be safe, be strong */ +} + +static int measure_completion(struct intel_context *ce) +{ + u32 *sema = hwsp_scratch(ce); + const u32 offset = hwsp_offset(ce, sema); + u32 elapsed[TF_COUNT], cycles; + u32 *cs; + int err; + int i; + + /* + * Measure how long it takes for the signal (interrupt) to be + * sent from the GPU to be processed by the CPU. + * + * A: read CS_TIMESTAMP on GPU + * signal + * B: read CS_TIMESTAMP from CPU + * + * Completion latency: B - A + */ + + for (i = 1; i <= ARRAY_SIZE(elapsed); i++) { + struct signal_cb cb = { .seen = false }; + struct i915_request *rq; + + rq = i915_request_create(ce); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto err; + } + + cs = intel_ring_begin(rq, 12); + if (IS_ERR(cs)) { + i915_request_add(rq); + err = PTR_ERR(cs); + goto err; + } + + cs = emit_store_dw(cs, offset + i * sizeof(u32), -1); + cs = emit_semaphore_poll_until(cs, offset, i); + cs = emit_timestamp_store(cs, ce, offset + i * sizeof(u32)); + + intel_ring_advance(rq, cs); + + dma_fence_add_callback(&rq->fence, &cb.base, signal_cb); + + local_bh_disable(); + i915_request_add(rq); + local_bh_enable(); + + if (wait_for(READ_ONCE(sema[i]) == -1, 50)) { + err = -EIO; + goto err; + } + + preempt_disable(); + semaphore_set(sema, i); + while (!READ_ONCE(cb.seen)) + cpu_relax(); + + elapsed[i - 1] = ENGINE_READ_FW(ce->engine, RING_TIMESTAMP); + preempt_enable(); + } + + err = intel_gt_wait_for_idle(ce->engine->gt, HZ / 2); + if (err) + goto err; + + for (i = 0; i < ARRAY_SIZE(elapsed); i++) { + GEM_BUG_ON(sema[i + 1] == -1); + elapsed[i] = elapsed[i] - sema[i + 1]; + } + + cycles = trifilter(elapsed); + pr_info("%s: completion latency %d cycles, %lluns\n", + ce->engine->name, cycles >> TF_BIAS, + cycles_to_ns(ce->engine, cycles)); + + return intel_gt_wait_for_idle(ce->engine->gt, HZ); + +err: + intel_gt_set_wedged(ce->engine->gt); + return err; +} + +static void rps_pin(struct intel_gt *gt) +{ + /* Pin the frequency to max */ + atomic_inc(>->rps.num_waiters); + intel_uncore_forcewake_get(gt->uncore, FORCEWAKE_ALL); + + mutex_lock(>->rps.lock); + intel_rps_set(>->rps, gt->rps.max_freq); + mutex_unlock(>->rps.lock); +} + +static void rps_unpin(struct intel_gt *gt) +{ + intel_uncore_forcewake_put(gt->uncore, FORCEWAKE_ALL); + atomic_dec(>->rps.num_waiters); +} + +static int perf_request_latency(void *arg) +{ + struct drm_i915_private *i915 = arg; + struct intel_engine_cs *engine; + struct pm_qos_request qos; + int err = 0; + + if (INTEL_GEN(i915) < 8) /* per-engine CS timestamp, semaphores */ + return 0; + + cpu_latency_qos_add_request(&qos, 0); /* disable cstates */ + + for_each_uabi_engine(engine, i915) { + struct intel_context *ce; + + ce = intel_context_create(engine); + if (IS_ERR(ce)) + goto out; + + err = intel_context_pin(ce); + if (err) { + intel_context_put(ce); + goto out; + } + + st_engine_heartbeat_disable(engine); + rps_pin(engine->gt); + + if (err == 0) + err = measure_semaphore_response(ce); + if (err == 0) + err = measure_idle_dispatch(ce); + if (err == 0) + err = measure_busy_dispatch(ce); + if (err == 0) + err = measure_inter_request(ce); + if (err == 0) + err = measure_context_switch(ce); + if (err == 0) + err = measure_preemption(ce); + if (err == 0) + err = measure_completion(ce); + + rps_unpin(engine->gt); + st_engine_heartbeat_enable(engine); + + intel_context_unpin(ce); + intel_context_put(ce); + if (err) + goto out; + } + +out: + if (igt_flush_test(i915)) + err = -EIO; + + cpu_latency_qos_remove_request(&qos); + return err; +} + static int s_sync0(void *arg) { struct perf_series *ps = arg; @@ -1685,9 +2492,11 @@ static int perf_series_engines(void *arg) intel_engine_pm_get(p->engine); if (intel_engine_supports_stats(p->engine)) - p->busy = intel_engine_get_busy_time(p->engine) + 1; + p->busy = intel_engine_get_busy_time(p->engine, + &p->time) + 1; + else + p->time = ktime_get(); p->runtime = -intel_context_get_total_runtime_ns(ce); - p->time = ktime_get(); } err = (*fn)(ps); @@ -1698,13 +2507,15 @@ static int perf_series_engines(void *arg) struct perf_stats *p = &stats[idx]; struct intel_context *ce = ps->ce[idx]; int integer, decimal; - u64 busy, dt; + u64 busy, dt, now; - p->time = ktime_sub(ktime_get(), p->time); - if (p->busy) { - p->busy = ktime_sub(intel_engine_get_busy_time(p->engine), + if (p->busy) + p->busy = ktime_sub(intel_engine_get_busy_time(p->engine, + &now), p->busy - 1); - } + else + now = ktime_get(); + p->time = ktime_sub(now, p->time); err = switch_to_kernel_sync(ce, err); p->runtime += intel_context_get_total_runtime_ns(ce); @@ -1764,13 +2575,14 @@ static int p_sync0(void *arg) return err; } - busy = false; if (intel_engine_supports_stats(engine)) { - p->busy = intel_engine_get_busy_time(engine); + p->busy = intel_engine_get_busy_time(engine, &p->time); busy = true; + } else { + p->time = ktime_get(); + busy = false; } - p->time = ktime_get(); count = 0; do { struct i915_request *rq; @@ -1793,11 +2605,15 @@ static int p_sync0(void *arg) count++; } while (!__igt_timeout(end_time, NULL)); - p->time = ktime_sub(ktime_get(), p->time); if (busy) { - p->busy = ktime_sub(intel_engine_get_busy_time(engine), + ktime_t now; + + p->busy = ktime_sub(intel_engine_get_busy_time(engine, &now), p->busy); + p->time = ktime_sub(now, p->time); + } else { + p->time = ktime_sub(ktime_get(), p->time); } err = switch_to_kernel_sync(ce, err); @@ -1830,13 +2646,14 @@ static int p_sync1(void *arg) return err; } - busy = false; if (intel_engine_supports_stats(engine)) { - p->busy = intel_engine_get_busy_time(engine); + p->busy = intel_engine_get_busy_time(engine, &p->time); busy = true; + } else { + p->time = ktime_get(); + busy = false; } - p->time = ktime_get(); count = 0; do { struct i915_request *rq; @@ -1861,11 +2678,15 @@ static int p_sync1(void *arg) count++; } while (!__igt_timeout(end_time, NULL)); i915_request_put(prev); - p->time = ktime_sub(ktime_get(), p->time); if (busy) { - p->busy = ktime_sub(intel_engine_get_busy_time(engine), + ktime_t now; + + p->busy = ktime_sub(intel_engine_get_busy_time(engine, &now), p->busy); + p->time = ktime_sub(now, p->time); + } else { + p->time = ktime_sub(ktime_get(), p->time); } err = switch_to_kernel_sync(ce, err); @@ -1897,14 +2718,15 @@ static int p_many(void *arg) return err; } - busy = false; if (intel_engine_supports_stats(engine)) { - p->busy = intel_engine_get_busy_time(engine); + p->busy = intel_engine_get_busy_time(engine, &p->time); busy = true; + } else { + p->time = ktime_get(); + busy = false; } count = 0; - p->time = ktime_get(); do { struct i915_request *rq; @@ -1917,11 +2739,15 @@ static int p_many(void *arg) i915_request_add(rq); count++; } while (!__igt_timeout(end_time, NULL)); - p->time = ktime_sub(ktime_get(), p->time); if (busy) { - p->busy = ktime_sub(intel_engine_get_busy_time(engine), + ktime_t now; + + p->busy = ktime_sub(intel_engine_get_busy_time(engine, &now), p->busy); + p->time = ktime_sub(now, p->time); + } else { + p->time = ktime_sub(ktime_get(), p->time); } err = switch_to_kernel_sync(ce, err); @@ -2042,6 +2868,7 @@ static int perf_parallel_engines(void *arg) int i915_request_perf_selftests(struct drm_i915_private *i915) { static const struct i915_subtest tests[] = { + SUBTEST(perf_request_latency), SUBTEST(perf_series_engines), SUBTEST(perf_parallel_engines), }; diff --git a/drivers/gpu/drm/i915/selftests/igt_spinner.c b/drivers/gpu/drm/i915/selftests/igt_spinner.c index e35ba5f9e73f..ec0ecb4e4ca6 100644 --- a/drivers/gpu/drm/i915/selftests/igt_spinner.c +++ b/drivers/gpu/drm/i915/selftests/igt_spinner.c @@ -134,15 +134,15 @@ igt_spinner_create_request(struct igt_spinner *spin, batch = spin->batch; - if (INTEL_GEN(rq->i915) >= 8) { + if (INTEL_GEN(rq->engine->i915) >= 8) { *batch++ = MI_STORE_DWORD_IMM_GEN4; *batch++ = lower_32_bits(hws_address(hws, rq)); *batch++ = upper_32_bits(hws_address(hws, rq)); - } else if (INTEL_GEN(rq->i915) >= 6) { + } else if (INTEL_GEN(rq->engine->i915) >= 6) { *batch++ = MI_STORE_DWORD_IMM_GEN4; *batch++ = 0; *batch++ = hws_address(hws, rq); - } else if (INTEL_GEN(rq->i915) >= 4) { + } else if (INTEL_GEN(rq->engine->i915) >= 4) { *batch++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT; *batch++ = 0; *batch++ = hws_address(hws, rq); @@ -154,11 +154,11 @@ igt_spinner_create_request(struct igt_spinner *spin, *batch++ = arbitration_command; - if (INTEL_GEN(rq->i915) >= 8) + if (INTEL_GEN(rq->engine->i915) >= 8) *batch++ = MI_BATCH_BUFFER_START | BIT(8) | 1; - else if (IS_HASWELL(rq->i915)) + else if (IS_HASWELL(rq->engine->i915)) *batch++ = MI_BATCH_BUFFER_START | MI_BATCH_PPGTT_HSW; - else if (INTEL_GEN(rq->i915) >= 6) + else if (INTEL_GEN(rq->engine->i915) >= 6) *batch++ = MI_BATCH_BUFFER_START; else *batch++ = MI_BATCH_BUFFER_START | MI_BATCH_GTT; @@ -176,7 +176,7 @@ igt_spinner_create_request(struct igt_spinner *spin, } flags = 0; - if (INTEL_GEN(rq->i915) <= 5) + if (INTEL_GEN(rq->engine->i915) <= 5) flags |= I915_DISPATCH_SECURE; err = engine->emit_bb_start(rq, vma->node.start, PAGE_SIZE, flags); @@ -221,8 +221,8 @@ bool igt_wait_for_spinner(struct igt_spinner *spin, struct i915_request *rq) { return !(wait_for_us(i915_seqno_passed(hws_seqno(spin, rq), rq->fence.seqno), - 10) && + 100) && wait_for(i915_seqno_passed(hws_seqno(spin, rq), rq->fence.seqno), - 1000)); + 50)); } diff --git a/drivers/gpu/drm/i915/selftests/mock_region.c b/drivers/gpu/drm/i915/selftests/mock_region.c index b2ad41c27e67..09660f5a0a4c 100644 --- a/drivers/gpu/drm/i915/selftests/mock_region.c +++ b/drivers/gpu/drm/i915/selftests/mock_region.c @@ -9,6 +9,7 @@ #include "mock_region.h" static const struct drm_i915_gem_object_ops mock_region_obj_ops = { + .name = "mock-region", .get_pages = i915_gem_object_get_pages_buddy, .put_pages = i915_gem_object_put_pages_buddy, .release = i915_gem_object_release_memory_region, diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c index 2dc93fa6ecb6..71d84c7a5378 100644 --- a/drivers/gpu/drm/imx/dw_hdmi-imx.c +++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c @@ -211,9 +211,8 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master, if (!pdev->dev.of_node) return -ENODEV; - hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return -ENOMEM; + hdmi = dev_get_drvdata(dev); + memset(hdmi, 0, sizeof(*hdmi)); match = of_match_node(dw_hdmi_imx_dt_ids, pdev->dev.of_node); plat_data = match->data; @@ -237,8 +236,6 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master, drm_encoder_helper_add(encoder, &dw_hdmi_imx_encoder_helper_funcs); drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); - platform_set_drvdata(pdev, hdmi); - hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data); /* @@ -268,6 +265,14 @@ static const struct component_ops dw_hdmi_imx_ops = { static int dw_hdmi_imx_probe(struct platform_device *pdev) { + struct imx_hdmi *hdmi; + + hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) + return -ENOMEM; + + platform_set_drvdata(pdev, hdmi); + return component_add(&pdev->dev, &dw_hdmi_imx_ops); } diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 36037b2e6564..7d00c49fd5a5 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -265,9 +265,10 @@ static void imx_drm_unbind(struct device *dev) drm_kms_helper_poll_fini(drm); + component_unbind_all(drm->dev, drm); + drm_mode_config_cleanup(drm); - component_unbind_all(drm->dev, drm); dev_set_drvdata(dev, NULL); drm_dev_put(drm); diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c index 66ea68e8da87..8791d60be92e 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/imx-ldb.c @@ -156,14 +156,6 @@ static int imx_ldb_connector_get_modes(struct drm_connector *connector) return num_modes; } -static struct drm_encoder *imx_ldb_connector_best_encoder( - struct drm_connector *connector) -{ - struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector); - - return &imx_ldb_ch->encoder; -} - static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno, unsigned long serial_clk, unsigned long di_clk) { @@ -304,18 +296,19 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder) { struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); struct imx_ldb *ldb = imx_ldb_ch->ldb; + int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; int mux, ret; drm_panel_disable(imx_ldb_ch->panel); - if (imx_ldb_ch == &ldb->channel[0]) + if (imx_ldb_ch == &ldb->channel[0] || dual) ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK; - else if (imx_ldb_ch == &ldb->channel[1]) + if (imx_ldb_ch == &ldb->channel[1] || dual) ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK; regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl); - if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { + if (dual) { clk_disable_unprepare(ldb->clk[0]); clk_disable_unprepare(ldb->clk[1]); } @@ -391,7 +384,6 @@ static const struct drm_connector_funcs imx_ldb_connector_funcs = { static const struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = { .get_modes = imx_ldb_connector_get_modes, - .best_encoder = imx_ldb_connector_best_encoder, }; static const struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = { @@ -473,11 +465,6 @@ static int imx_ldb_register(struct drm_device *drm, return 0; } -enum { - LVDS_BIT_MAP_SPWG, - LVDS_BIT_MAP_JEIDA -}; - struct imx_ldb_bit_mapping { u32 bus_format; u32 datawidth; @@ -590,9 +577,8 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) int ret; int i; - imx_ldb = devm_kzalloc(dev, sizeof(*imx_ldb), GFP_KERNEL); - if (!imx_ldb) - return -ENOMEM; + imx_ldb = dev_get_drvdata(dev); + memset(imx_ldb, 0, sizeof(*imx_ldb)); imx_ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr"); if (IS_ERR(imx_ldb->regmap)) { @@ -700,8 +686,6 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) } } - dev_set_drvdata(dev, imx_ldb); - return 0; free_child: @@ -733,6 +717,14 @@ static const struct component_ops imx_ldb_ops = { static int imx_ldb_probe(struct platform_device *pdev) { + struct imx_ldb *imx_ldb; + + imx_ldb = devm_kzalloc(&pdev->dev, sizeof(*imx_ldb), GFP_KERNEL); + if (!imx_ldb) + return -ENOMEM; + + platform_set_drvdata(pdev, imx_ldb); + return component_add(&pdev->dev, &imx_ldb_ops); } diff --git a/drivers/gpu/drm/imx/imx-tve.c b/drivers/gpu/drm/imx/imx-tve.c index ee63782c77e9..813bb6156a68 100644 --- a/drivers/gpu/drm/imx/imx-tve.c +++ b/drivers/gpu/drm/imx/imx-tve.c @@ -260,14 +260,6 @@ static int imx_tve_connector_mode_valid(struct drm_connector *connector, return MODE_BAD; } -static struct drm_encoder *imx_tve_connector_best_encoder( - struct drm_connector *connector) -{ - struct imx_tve *tve = con_to_tve(connector); - - return &tve->encoder; -} - static void imx_tve_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *orig_mode, struct drm_display_mode *mode) @@ -345,7 +337,6 @@ static const struct drm_connector_funcs imx_tve_connector_funcs = { static const struct drm_connector_helper_funcs imx_tve_connector_helper_funcs = { .get_modes = imx_tve_connector_get_modes, - .best_encoder = imx_tve_connector_best_encoder, .mode_valid = imx_tve_connector_mode_valid, }; @@ -490,6 +481,13 @@ static int imx_tve_register(struct drm_device *drm, struct imx_tve *tve) return 0; } +static void imx_tve_disable_regulator(void *data) +{ + struct imx_tve *tve = data; + + regulator_disable(tve->dac_reg); +} + static bool imx_tve_readable_reg(struct device *dev, unsigned int reg) { return (reg % 4 == 0) && (reg <= 0xdc); @@ -542,9 +540,8 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data) int irq; int ret; - tve = devm_kzalloc(dev, sizeof(*tve), GFP_KERNEL); - if (!tve) - return -ENOMEM; + tve = dev_get_drvdata(dev); + memset(tve, 0, sizeof(*tve)); tve->dev = dev; spin_lock_init(&tve->lock); @@ -594,10 +591,8 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "failed to get irq\n"); + if (irq < 0) return irq; - } ret = devm_request_threaded_irq(dev, irq, NULL, imx_tve_irq_handler, IRQF_ONESHOT, @@ -614,6 +609,9 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data) ret = regulator_enable(tve->dac_reg); if (ret) return ret; + ret = devm_add_action_or_reset(dev, imx_tve_disable_regulator, tve); + if (ret) + return ret; } tve->clk = devm_clk_get(dev, "tve"); @@ -655,27 +653,23 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data) if (ret) return ret; - dev_set_drvdata(dev, tve); - return 0; } -static void imx_tve_unbind(struct device *dev, struct device *master, - void *data) -{ - struct imx_tve *tve = dev_get_drvdata(dev); - - if (!IS_ERR(tve->dac_reg)) - regulator_disable(tve->dac_reg); -} - static const struct component_ops imx_tve_ops = { .bind = imx_tve_bind, - .unbind = imx_tve_unbind, }; static int imx_tve_probe(struct platform_device *pdev) { + struct imx_tve *tve; + + tve = devm_kzalloc(&pdev->dev, sizeof(*tve), GFP_KERNEL); + if (!tve) + return -ENOMEM; + + platform_set_drvdata(pdev, tve); + return component_add(&pdev->dev, &imx_tve_ops); } diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c index 63c0284f8b3c..d412fc265395 100644 --- a/drivers/gpu/drm/imx/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c @@ -109,20 +109,15 @@ static void imx_drm_crtc_reset(struct drm_crtc *crtc) { struct imx_crtc_state *state; - if (crtc->state) { - if (crtc->state->mode_blob) - drm_property_blob_put(crtc->state->mode_blob); - - state = to_imx_crtc_state(crtc->state); - memset(state, 0, sizeof(*state)); - } else { - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (!state) - return; - crtc->state = &state->base; - } + if (crtc->state) + __drm_atomic_helper_crtc_destroy_state(crtc->state); - state->base.crtc = crtc; + kfree(to_imx_crtc_state(crtc->state)); + crtc->state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_crtc_reset(crtc, &state->base); } static struct drm_crtc_state *imx_drm_crtc_duplicate_state(struct drm_crtc *crtc) @@ -438,21 +433,13 @@ static int ipu_drm_bind(struct device *dev, struct device *master, void *data) struct ipu_client_platformdata *pdata = dev->platform_data; struct drm_device *drm = data; struct ipu_crtc *ipu_crtc; - int ret; - ipu_crtc = devm_kzalloc(dev, sizeof(*ipu_crtc), GFP_KERNEL); - if (!ipu_crtc) - return -ENOMEM; + ipu_crtc = dev_get_drvdata(dev); + memset(ipu_crtc, 0, sizeof(*ipu_crtc)); ipu_crtc->dev = dev; - ret = ipu_crtc_init(ipu_crtc, pdata, drm); - if (ret) - return ret; - - dev_set_drvdata(dev, ipu_crtc); - - return 0; + return ipu_crtc_init(ipu_crtc, pdata, drm); } static void ipu_drm_unbind(struct device *dev, struct device *master, @@ -474,6 +461,7 @@ static const struct component_ops ipu_crtc_ops = { static int ipu_drm_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct ipu_crtc *ipu_crtc; int ret; if (!dev->platform_data) @@ -483,6 +471,12 @@ static int ipu_drm_probe(struct platform_device *pdev) if (ret) return ret; + ipu_crtc = devm_kzalloc(dev, sizeof(*ipu_crtc), GFP_KERNEL); + if (!ipu_crtc) + return -ENOMEM; + + dev_set_drvdata(dev, ipu_crtc); + return component_add(dev, &ipu_crtc_ops); } diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index ac916c84a631..a831b5bd1613 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -88,14 +88,6 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector) return num_modes; } -static struct drm_encoder *imx_pd_connector_best_encoder( - struct drm_connector *connector) -{ - struct imx_parallel_display *imxpd = con_to_imxpd(connector); - - return &imxpd->encoder; -} - static void imx_pd_bridge_enable(struct drm_bridge *bridge) { struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge); @@ -217,7 +209,7 @@ static int imx_pd_bridge_atomic_check(struct drm_bridge *bridge, if (next_bridge_state) bus_flags = next_bridge_state->input_bus_cfg.flags; - else if (!imxpd->bus_format && di->num_bus_formats) + else if (di->num_bus_formats) bus_flags = di->bus_flags; else bus_flags = imxpd->bus_flags; @@ -254,7 +246,6 @@ static const struct drm_connector_funcs imx_pd_connector_funcs = { static const struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = { .get_modes = imx_pd_connector_get_modes, - .best_encoder = imx_pd_connector_best_encoder, }; static const struct drm_bridge_funcs imx_pd_bridge_funcs = { @@ -326,9 +317,14 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) u32 bus_format = 0; const char *fmt; - imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL); - if (!imxpd) - return -ENOMEM; + imxpd = dev_get_drvdata(dev); + memset(imxpd, 0, sizeof(*imxpd)); + + /* port@1 is the output port */ + ret = drm_of_find_panel_or_bridge(np, 1, 0, &imxpd->panel, + &imxpd->next_bridge); + if (ret && ret != -ENODEV) + return ret; edidp = of_get_property(np, "edid", &imxpd->edid_len); if (edidp) @@ -347,20 +343,12 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) } imxpd->bus_format = bus_format; - /* port@1 is the output port */ - ret = drm_of_find_panel_or_bridge(np, 1, 0, &imxpd->panel, - &imxpd->next_bridge); - if (ret && ret != -ENODEV) - return ret; - imxpd->dev = dev; ret = imx_pd_register(drm, imxpd); if (ret) return ret; - dev_set_drvdata(dev, imxpd); - return 0; } @@ -382,6 +370,14 @@ static const struct component_ops imx_pd_ops = { static int imx_pd_probe(struct platform_device *pdev) { + struct imx_parallel_display *imxpd; + + imxpd = devm_kzalloc(&pdev->dev, sizeof(*imxpd), GFP_KERNEL); + if (!imxpd) + return -ENOMEM; + + platform_set_drvdata(pdev, imxpd); + return component_add(&pdev->dev, &imx_pd_ops); } diff --git a/drivers/gpu/drm/ingenic/Kconfig b/drivers/gpu/drm/ingenic/Kconfig index d82c3d37ec9c..477d5387e43e 100644 --- a/drivers/gpu/drm/ingenic/Kconfig +++ b/drivers/gpu/drm/ingenic/Kconfig @@ -14,3 +14,14 @@ config DRM_INGENIC Choose this option for DRM support for the Ingenic SoCs. If M is selected the module will be called ingenic-drm. + +if DRM_INGENIC + +config DRM_INGENIC_IPU + bool "IPU support for Ingenic SoCs" + help + Choose this option to enable support for the IPU found in Ingenic SoCs. + + The Image Processing Unit (IPU) will appear as a second primary plane. + +endif diff --git a/drivers/gpu/drm/ingenic/Makefile b/drivers/gpu/drm/ingenic/Makefile index 11cac42ce0bb..d313326bdddb 100644 --- a/drivers/gpu/drm/ingenic/Makefile +++ b/drivers/gpu/drm/ingenic/Makefile @@ -1 +1,3 @@ obj-$(CONFIG_DRM_INGENIC) += ingenic-drm.o +ingenic-drm-y = ingenic-drm-drv.o +ingenic-drm-$(CONFIG_DRM_INGENIC_IPU) += ingenic-ipu.o diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index 16f0740df507..ada990a7f911 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -4,6 +4,9 @@ // // Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net> +#include "ingenic-drm.h" + +#include <linux/component.h> #include <linux/clk.h> #include <linux/dma-mapping.h> #include <linux/module.h> @@ -32,120 +35,6 @@ #include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> -#define JZ_REG_LCD_CFG 0x00 -#define JZ_REG_LCD_VSYNC 0x04 -#define JZ_REG_LCD_HSYNC 0x08 -#define JZ_REG_LCD_VAT 0x0C -#define JZ_REG_LCD_DAH 0x10 -#define JZ_REG_LCD_DAV 0x14 -#define JZ_REG_LCD_PS 0x18 -#define JZ_REG_LCD_CLS 0x1C -#define JZ_REG_LCD_SPL 0x20 -#define JZ_REG_LCD_REV 0x24 -#define JZ_REG_LCD_CTRL 0x30 -#define JZ_REG_LCD_STATE 0x34 -#define JZ_REG_LCD_IID 0x38 -#define JZ_REG_LCD_DA0 0x40 -#define JZ_REG_LCD_SA0 0x44 -#define JZ_REG_LCD_FID0 0x48 -#define JZ_REG_LCD_CMD0 0x4C -#define JZ_REG_LCD_DA1 0x50 -#define JZ_REG_LCD_SA1 0x54 -#define JZ_REG_LCD_FID1 0x58 -#define JZ_REG_LCD_CMD1 0x5C - -#define JZ_LCD_CFG_SLCD BIT(31) -#define JZ_LCD_CFG_PS_DISABLE BIT(23) -#define JZ_LCD_CFG_CLS_DISABLE BIT(22) -#define JZ_LCD_CFG_SPL_DISABLE BIT(21) -#define JZ_LCD_CFG_REV_DISABLE BIT(20) -#define JZ_LCD_CFG_HSYNCM BIT(19) -#define JZ_LCD_CFG_PCLKM BIT(18) -#define JZ_LCD_CFG_INV BIT(17) -#define JZ_LCD_CFG_SYNC_DIR BIT(16) -#define JZ_LCD_CFG_PS_POLARITY BIT(15) -#define JZ_LCD_CFG_CLS_POLARITY BIT(14) -#define JZ_LCD_CFG_SPL_POLARITY BIT(13) -#define JZ_LCD_CFG_REV_POLARITY BIT(12) -#define JZ_LCD_CFG_HSYNC_ACTIVE_LOW BIT(11) -#define JZ_LCD_CFG_PCLK_FALLING_EDGE BIT(10) -#define JZ_LCD_CFG_DE_ACTIVE_LOW BIT(9) -#define JZ_LCD_CFG_VSYNC_ACTIVE_LOW BIT(8) -#define JZ_LCD_CFG_18_BIT BIT(7) -#define JZ_LCD_CFG_PDW (BIT(5) | BIT(4)) - -#define JZ_LCD_CFG_MODE_GENERIC_16BIT 0 -#define JZ_LCD_CFG_MODE_GENERIC_18BIT BIT(7) -#define JZ_LCD_CFG_MODE_GENERIC_24BIT BIT(6) - -#define JZ_LCD_CFG_MODE_SPECIAL_TFT_1 1 -#define JZ_LCD_CFG_MODE_SPECIAL_TFT_2 2 -#define JZ_LCD_CFG_MODE_SPECIAL_TFT_3 3 - -#define JZ_LCD_CFG_MODE_TV_OUT_P 4 -#define JZ_LCD_CFG_MODE_TV_OUT_I 6 - -#define JZ_LCD_CFG_MODE_SINGLE_COLOR_STN 8 -#define JZ_LCD_CFG_MODE_SINGLE_MONOCHROME_STN 9 -#define JZ_LCD_CFG_MODE_DUAL_COLOR_STN 10 -#define JZ_LCD_CFG_MODE_DUAL_MONOCHROME_STN 11 - -#define JZ_LCD_CFG_MODE_8BIT_SERIAL 12 -#define JZ_LCD_CFG_MODE_LCM 13 - -#define JZ_LCD_VSYNC_VPS_OFFSET 16 -#define JZ_LCD_VSYNC_VPE_OFFSET 0 - -#define JZ_LCD_HSYNC_HPS_OFFSET 16 -#define JZ_LCD_HSYNC_HPE_OFFSET 0 - -#define JZ_LCD_VAT_HT_OFFSET 16 -#define JZ_LCD_VAT_VT_OFFSET 0 - -#define JZ_LCD_DAH_HDS_OFFSET 16 -#define JZ_LCD_DAH_HDE_OFFSET 0 - -#define JZ_LCD_DAV_VDS_OFFSET 16 -#define JZ_LCD_DAV_VDE_OFFSET 0 - -#define JZ_LCD_CTRL_BURST_4 (0x0 << 28) -#define JZ_LCD_CTRL_BURST_8 (0x1 << 28) -#define JZ_LCD_CTRL_BURST_16 (0x2 << 28) -#define JZ_LCD_CTRL_RGB555 BIT(27) -#define JZ_LCD_CTRL_OFUP BIT(26) -#define JZ_LCD_CTRL_FRC_GRAYSCALE_16 (0x0 << 24) -#define JZ_LCD_CTRL_FRC_GRAYSCALE_4 (0x1 << 24) -#define JZ_LCD_CTRL_FRC_GRAYSCALE_2 (0x2 << 24) -#define JZ_LCD_CTRL_PDD_MASK (0xff << 16) -#define JZ_LCD_CTRL_EOF_IRQ BIT(13) -#define JZ_LCD_CTRL_SOF_IRQ BIT(12) -#define JZ_LCD_CTRL_OFU_IRQ BIT(11) -#define JZ_LCD_CTRL_IFU0_IRQ BIT(10) -#define JZ_LCD_CTRL_IFU1_IRQ BIT(9) -#define JZ_LCD_CTRL_DD_IRQ BIT(8) -#define JZ_LCD_CTRL_QDD_IRQ BIT(7) -#define JZ_LCD_CTRL_REVERSE_ENDIAN BIT(6) -#define JZ_LCD_CTRL_LSB_FISRT BIT(5) -#define JZ_LCD_CTRL_DISABLE BIT(4) -#define JZ_LCD_CTRL_ENABLE BIT(3) -#define JZ_LCD_CTRL_BPP_1 0x0 -#define JZ_LCD_CTRL_BPP_2 0x1 -#define JZ_LCD_CTRL_BPP_4 0x2 -#define JZ_LCD_CTRL_BPP_8 0x3 -#define JZ_LCD_CTRL_BPP_15_16 0x4 -#define JZ_LCD_CTRL_BPP_18_24 0x5 -#define JZ_LCD_CTRL_BPP_MASK (JZ_LCD_CTRL_RGB555 | (0x7 << 0)) - -#define JZ_LCD_CMD_SOF_IRQ BIT(31) -#define JZ_LCD_CMD_EOF_IRQ BIT(30) -#define JZ_LCD_CMD_ENABLE_PAL BIT(28) - -#define JZ_LCD_SYNC_MASK 0x3ff - -#define JZ_LCD_STATE_EOF_IRQ BIT(5) -#define JZ_LCD_STATE_SOF_IRQ BIT(4) -#define JZ_LCD_STATE_DISABLED BIT(0) - struct ingenic_dma_hwdesc { u32 next; u32 addr; @@ -155,24 +44,30 @@ struct ingenic_dma_hwdesc { struct jz_soc_info { bool needs_dev_clk; + bool has_osd; unsigned int max_width, max_height; }; struct ingenic_drm { struct drm_device drm; - struct drm_plane primary; + /* + * f1 (aka. foreground1) is our primary plane, on top of which + * f0 (aka. foreground0) can be overlayed. Z-order is fixed in + * hardware and cannot be changed. + */ + struct drm_plane f0, f1, *ipu_plane; struct drm_crtc crtc; - struct drm_encoder encoder; struct device *dev; struct regmap *map; struct clk *lcd_clk, *pix_clk; const struct jz_soc_info *soc_info; - struct ingenic_dma_hwdesc *dma_hwdesc; - dma_addr_t dma_hwdesc_phys; + struct ingenic_dma_hwdesc *dma_hwdesc_f0, *dma_hwdesc_f1; + dma_addr_t dma_hwdesc_phys_f0, dma_hwdesc_phys_f1; bool panel_is_sharp; + bool no_vblank; }; static const u32 ingenic_drm_primary_formats[] = { @@ -202,7 +97,7 @@ static const struct regmap_config ingenic_drm_regmap_config = { .val_bits = 32, .reg_stride = 4, - .max_register = JZ_REG_LCD_CMD1, + .max_register = JZ_REG_LCD_SIZE1, .writeable_reg = ingenic_drm_writeable_reg, }; @@ -216,17 +111,6 @@ static inline struct ingenic_drm *drm_crtc_get_priv(struct drm_crtc *crtc) return container_of(crtc, struct ingenic_drm, crtc); } -static inline struct ingenic_drm * -drm_encoder_get_priv(struct drm_encoder *encoder) -{ - return container_of(encoder, struct ingenic_drm, encoder); -} - -static inline struct ingenic_drm *drm_plane_get_priv(struct drm_plane *plane) -{ - return container_of(plane, struct ingenic_drm, primary); -} - static void ingenic_drm_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *state) { @@ -297,34 +181,24 @@ static void ingenic_drm_crtc_update_timings(struct ingenic_drm *priv, regmap_write(priv->map, JZ_REG_LCD_SPL, hpe << 16 | (hpe + 1)); regmap_write(priv->map, JZ_REG_LCD_REV, mode->htotal << 16); } -} -static void ingenic_drm_crtc_update_ctrl(struct ingenic_drm *priv, - const struct drm_format_info *finfo) -{ - unsigned int ctrl = JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16; + regmap_set_bits(priv->map, JZ_REG_LCD_CTRL, + JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16); - switch (finfo->format) { - case DRM_FORMAT_XRGB1555: - ctrl |= JZ_LCD_CTRL_RGB555; - /* fall-through */ - case DRM_FORMAT_RGB565: - ctrl |= JZ_LCD_CTRL_BPP_15_16; - break; - case DRM_FORMAT_XRGB8888: - ctrl |= JZ_LCD_CTRL_BPP_18_24; - break; - } - - regmap_update_bits(priv->map, JZ_REG_LCD_CTRL, - JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16 | - JZ_LCD_CTRL_BPP_MASK, ctrl); + /* + * IPU restart - specify how much time the LCDC will wait before + * transferring a new frame from the IPU. The value is the one + * suggested in the programming manual. + */ + regmap_write(priv->map, JZ_REG_LCD_IPUR, JZ_LCD_IPUR_IPUREN | + (ht * vpe / 3) << JZ_LCD_IPUR_IPUR_LSB); } static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct ingenic_drm *priv = drm_crtc_get_priv(crtc); + struct drm_plane_state *f1_state, *f0_state, *ipu_state = NULL; long rate; if (!drm_atomic_crtc_needs_modeset(state)) @@ -339,27 +213,59 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc, if (rate < 0) return rate; + if (priv->soc_info->has_osd) { + f1_state = drm_atomic_get_plane_state(state->state, &priv->f1); + f0_state = drm_atomic_get_plane_state(state->state, &priv->f0); + + if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU) && priv->ipu_plane) { + ipu_state = drm_atomic_get_plane_state(state->state, priv->ipu_plane); + + /* IPU and F1 planes cannot be enabled at the same time. */ + if (f1_state->fb && ipu_state->fb) { + dev_dbg(priv->dev, "Cannot enable both F1 and IPU\n"); + return -EINVAL; + } + } + + /* If all the planes are disabled, we won't get a VBLANK IRQ */ + priv->no_vblank = !f1_state->fb && !f0_state->fb && + !(ipu_state && ipu_state->fb); + } + return 0; } +static void ingenic_drm_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *oldstate) +{ + struct ingenic_drm *priv = drm_crtc_get_priv(crtc); + u32 ctrl = 0; + + if (priv->soc_info->has_osd && + drm_atomic_crtc_needs_modeset(crtc->state)) { + /* + * If IPU plane is enabled, enable IPU as source for the F1 + * plane; otherwise use regular DMA. + */ + if (priv->ipu_plane && priv->ipu_plane->state->fb) + ctrl |= JZ_LCD_OSDCTRL_IPU; + + regmap_update_bits(priv->map, JZ_REG_LCD_OSDCTRL, + JZ_LCD_OSDCTRL_IPU, ctrl); + } +} + static void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *oldstate) { struct ingenic_drm *priv = drm_crtc_get_priv(crtc); struct drm_crtc_state *state = crtc->state; struct drm_pending_vblank_event *event = state->event; - struct drm_framebuffer *drm_fb = crtc->primary->state->fb; - const struct drm_format_info *finfo; if (drm_atomic_crtc_needs_modeset(state)) { - finfo = drm_format_info(drm_fb->format->format); - ingenic_drm_crtc_update_timings(priv, &state->mode); - ingenic_drm_crtc_update_ctrl(priv, finfo); clk_set_rate(priv->pix_clk, state->adjusted_mode.clock * 1000); - - regmap_write(priv->map, JZ_REG_LCD_DA0, priv->dma_hwdesc->next); } if (event) { @@ -374,11 +280,160 @@ static void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc, } } +static int ingenic_drm_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct ingenic_drm *priv = drm_device_get_priv(plane->dev); + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc = state->crtc ?: plane->state->crtc; + int ret; + + if (!crtc) + return 0; + + crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + ret = drm_atomic_helper_check_plane_state(state, crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + priv->soc_info->has_osd, + true); + if (ret) + return ret; + + /* + * If OSD is not available, check that the width/height match. + * Note that state->src_* are in 16.16 fixed-point format. + */ + if (!priv->soc_info->has_osd && + (state->src_x != 0 || + (state->src_w >> 16) != state->crtc_w || + (state->src_h >> 16) != state->crtc_h)) + return -EINVAL; + + /* + * Require full modeset if enabling or disabling a plane, or changing + * its position, size or depth. + */ + if (priv->soc_info->has_osd && + (!plane->state->fb || !state->fb || + plane->state->crtc_x != state->crtc_x || + plane->state->crtc_y != state->crtc_y || + plane->state->crtc_w != state->crtc_w || + plane->state->crtc_h != state->crtc_h || + plane->state->fb->format->format != state->fb->format->format)) + crtc_state->mode_changed = true; + + return 0; +} + +static void ingenic_drm_plane_enable(struct ingenic_drm *priv, + struct drm_plane *plane) +{ + unsigned int en_bit; + + if (priv->soc_info->has_osd) { + if (plane->type == DRM_PLANE_TYPE_PRIMARY) + en_bit = JZ_LCD_OSDC_F1EN; + else + en_bit = JZ_LCD_OSDC_F0EN; + + regmap_set_bits(priv->map, JZ_REG_LCD_OSDC, en_bit); + } +} + +void ingenic_drm_plane_disable(struct device *dev, struct drm_plane *plane) +{ + struct ingenic_drm *priv = dev_get_drvdata(dev); + unsigned int en_bit; + + if (priv->soc_info->has_osd) { + if (plane->type == DRM_PLANE_TYPE_PRIMARY) + en_bit = JZ_LCD_OSDC_F1EN; + else + en_bit = JZ_LCD_OSDC_F0EN; + + regmap_clear_bits(priv->map, JZ_REG_LCD_OSDC, en_bit); + } +} + +static void ingenic_drm_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct ingenic_drm *priv = drm_device_get_priv(plane->dev); + + ingenic_drm_plane_disable(priv->dev, plane); +} + +void ingenic_drm_plane_config(struct device *dev, + struct drm_plane *plane, u32 fourcc) +{ + struct ingenic_drm *priv = dev_get_drvdata(dev); + struct drm_plane_state *state = plane->state; + unsigned int xy_reg, size_reg; + unsigned int ctrl = 0; + + ingenic_drm_plane_enable(priv, plane); + + if (priv->soc_info->has_osd && + plane->type == DRM_PLANE_TYPE_PRIMARY) { + switch (fourcc) { + case DRM_FORMAT_XRGB1555: + ctrl |= JZ_LCD_OSDCTRL_RGB555; + fallthrough; + case DRM_FORMAT_RGB565: + ctrl |= JZ_LCD_OSDCTRL_BPP_15_16; + break; + case DRM_FORMAT_XRGB8888: + ctrl |= JZ_LCD_OSDCTRL_BPP_18_24; + break; + } + + regmap_update_bits(priv->map, JZ_REG_LCD_OSDCTRL, + JZ_LCD_OSDCTRL_BPP_MASK, ctrl); + } else { + switch (fourcc) { + case DRM_FORMAT_XRGB1555: + ctrl |= JZ_LCD_CTRL_RGB555; + fallthrough; + case DRM_FORMAT_RGB565: + ctrl |= JZ_LCD_CTRL_BPP_15_16; + break; + case DRM_FORMAT_XRGB8888: + ctrl |= JZ_LCD_CTRL_BPP_18_24; + break; + } + + regmap_update_bits(priv->map, JZ_REG_LCD_CTRL, + JZ_LCD_CTRL_BPP_MASK, ctrl); + } + + if (priv->soc_info->has_osd) { + if (plane->type == DRM_PLANE_TYPE_PRIMARY) { + xy_reg = JZ_REG_LCD_XYP1; + size_reg = JZ_REG_LCD_SIZE1; + } else { + xy_reg = JZ_REG_LCD_XYP0; + size_reg = JZ_REG_LCD_SIZE0; + } + + regmap_write(priv->map, xy_reg, + state->crtc_x << JZ_LCD_XYP01_XPOS_LSB | + state->crtc_y << JZ_LCD_XYP01_YPOS_LSB); + regmap_write(priv->map, size_reg, + state->crtc_w << JZ_LCD_SIZE01_WIDTH_LSB | + state->crtc_h << JZ_LCD_SIZE01_HEIGHT_LSB); + } +} + static void ingenic_drm_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *oldstate) { - struct ingenic_drm *priv = drm_plane_get_priv(plane); + struct ingenic_drm *priv = drm_device_get_priv(plane->dev); struct drm_plane_state *state = plane->state; + struct ingenic_dma_hwdesc *hwdesc; unsigned int width, height, cpp; dma_addr_t addr; @@ -386,11 +441,19 @@ static void ingenic_drm_plane_atomic_update(struct drm_plane *plane, addr = drm_fb_cma_get_gem_addr(state->fb, state, 0); width = state->src_w >> 16; height = state->src_h >> 16; - cpp = state->fb->format->cpp[plane->index]; + cpp = state->fb->format->cpp[0]; + + if (priv->soc_info->has_osd && plane->type == DRM_PLANE_TYPE_OVERLAY) + hwdesc = priv->dma_hwdesc_f0; + else + hwdesc = priv->dma_hwdesc_f1; - priv->dma_hwdesc->addr = addr; - priv->dma_hwdesc->cmd = width * height * cpp / 4; - priv->dma_hwdesc->cmd |= JZ_LCD_CMD_EOF_IRQ; + hwdesc->addr = addr; + hwdesc->cmd = JZ_LCD_CMD_EOF_IRQ | (width * height * cpp / 4); + + if (drm_atomic_crtc_needs_modeset(state->crtc->state)) + ingenic_drm_plane_config(priv->dev, plane, + state->fb->format->format); } } @@ -398,7 +461,7 @@ static void ingenic_drm_encoder_atomic_mode_set(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { - struct ingenic_drm *priv = drm_encoder_get_priv(encoder); + struct ingenic_drm *priv = drm_device_get_priv(encoder->dev); struct drm_display_mode *mode = &crtc_state->adjusted_mode; struct drm_connector *conn = conn_state->connector; struct drm_display_info *info = &conn->display_info; @@ -419,7 +482,7 @@ static void ingenic_drm_encoder_atomic_mode_set(struct drm_encoder *encoder, cfg |= JZ_LCD_CFG_VSYNC_ACTIVE_LOW; if (info->bus_flags & DRM_BUS_FLAG_DE_LOW) cfg |= JZ_LCD_CFG_DE_ACTIVE_LOW; - if (info->bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE) + if (info->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) cfg |= JZ_LCD_CFG_PCLK_FALLING_EDGE; if (!priv->panel_is_sharp) { @@ -474,6 +537,29 @@ static int ingenic_drm_encoder_atomic_check(struct drm_encoder *encoder, } } +static void ingenic_drm_atomic_helper_commit_tail(struct drm_atomic_state *old_state) +{ + /* + * Just your regular drm_atomic_helper_commit_tail(), but only calls + * drm_atomic_helper_wait_for_vblanks() if priv->no_vblank. + */ + struct drm_device *dev = old_state->dev; + struct ingenic_drm *priv = drm_device_get_priv(dev); + + drm_atomic_helper_commit_modeset_disables(dev, old_state); + + drm_atomic_helper_commit_planes(dev, old_state, 0); + + drm_atomic_helper_commit_modeset_enables(dev, old_state); + + drm_atomic_helper_commit_hw_done(old_state); + + if (!priv->no_vblank) + drm_atomic_helper_wait_for_vblanks(dev, old_state); + + drm_atomic_helper_cleanup_planes(dev, old_state); +} + static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg) { struct ingenic_drm *priv = drm_device_get_priv(arg); @@ -513,9 +599,9 @@ static struct drm_driver ingenic_drm_driver_data = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, .name = "ingenic-drm", .desc = "DRM module for Ingenic SoCs", - .date = "20190422", + .date = "20200716", .major = 1, - .minor = 0, + .minor = 1, .patchlevel = 0, .fops = &ingenic_drm_fops, @@ -551,12 +637,15 @@ static const struct drm_crtc_funcs ingenic_drm_crtc_funcs = { static const struct drm_plane_helper_funcs ingenic_drm_plane_helper_funcs = { .atomic_update = ingenic_drm_plane_atomic_update, + .atomic_check = ingenic_drm_plane_atomic_check, + .atomic_disable = ingenic_drm_plane_atomic_disable, .prepare_fb = drm_gem_fb_prepare_fb, }; static const struct drm_crtc_helper_funcs ingenic_drm_crtc_helper_funcs = { .atomic_enable = ingenic_drm_crtc_atomic_enable, .atomic_disable = ingenic_drm_crtc_atomic_disable, + .atomic_begin = ingenic_drm_crtc_atomic_begin, .atomic_flush = ingenic_drm_crtc_atomic_flush, .atomic_check = ingenic_drm_crtc_atomic_check, }; @@ -573,25 +662,30 @@ static const struct drm_mode_config_funcs ingenic_drm_mode_config_funcs = { .atomic_commit = drm_atomic_helper_commit, }; -static void ingenic_drm_free_dma_hwdesc(void *d) +static struct drm_mode_config_helper_funcs ingenic_drm_mode_config_helpers = { + .atomic_commit_tail = ingenic_drm_atomic_helper_commit_tail, +}; + +static void ingenic_drm_unbind_all(void *d) { struct ingenic_drm *priv = d; - dma_free_coherent(priv->dev, sizeof(*priv->dma_hwdesc), - priv->dma_hwdesc, priv->dma_hwdesc_phys); + component_unbind_all(priv->dev, &priv->drm); } -static int ingenic_drm_probe(struct platform_device *pdev) +static int ingenic_drm_bind(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); const struct jz_soc_info *soc_info; - struct device *dev = &pdev->dev; struct ingenic_drm *priv; struct clk *parent_clk; struct drm_bridge *bridge; struct drm_panel *panel; + struct drm_encoder *encoder; struct drm_device *drm; void __iomem *base; long parent_rate; + unsigned int i, clone_mask = 0; int ret, irq; soc_info = of_device_get_match_data(dev); @@ -620,17 +714,18 @@ static int ingenic_drm_probe(struct platform_device *pdev) drm->mode_config.max_width = soc_info->max_width; drm->mode_config.max_height = 4095; drm->mode_config.funcs = &ingenic_drm_mode_config_funcs; + drm->mode_config.helper_private = &ingenic_drm_mode_config_helpers; base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) { - dev_err(dev, "Failed to get memory resource"); + dev_err(dev, "Failed to get memory resource\n"); return PTR_ERR(base); } priv->map = devm_regmap_init_mmio(dev, base, &ingenic_drm_regmap_config); if (IS_ERR(priv->map)) { - dev_err(dev, "Failed to create regmap"); + dev_err(dev, "Failed to create regmap\n"); return PTR_ERR(priv->map); } @@ -641,89 +736,150 @@ static int ingenic_drm_probe(struct platform_device *pdev) if (soc_info->needs_dev_clk) { priv->lcd_clk = devm_clk_get(dev, "lcd"); if (IS_ERR(priv->lcd_clk)) { - dev_err(dev, "Failed to get lcd clock"); + dev_err(dev, "Failed to get lcd clock\n"); return PTR_ERR(priv->lcd_clk); } } priv->pix_clk = devm_clk_get(dev, "lcd_pclk"); if (IS_ERR(priv->pix_clk)) { - dev_err(dev, "Failed to get pixel clock"); + dev_err(dev, "Failed to get pixel clock\n"); return PTR_ERR(priv->pix_clk); } - ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, &bridge); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get panel handle"); - return ret; - } + priv->dma_hwdesc_f1 = dmam_alloc_coherent(dev, sizeof(*priv->dma_hwdesc_f1), + &priv->dma_hwdesc_phys_f1, + GFP_KERNEL); + if (!priv->dma_hwdesc_f1) + return -ENOMEM; - if (panel) - bridge = devm_drm_panel_bridge_add_typed(dev, panel, - DRM_MODE_CONNECTOR_DPI); + priv->dma_hwdesc_f1->next = priv->dma_hwdesc_phys_f1; + priv->dma_hwdesc_f1->id = 0xf1; - priv->dma_hwdesc = dma_alloc_coherent(dev, sizeof(*priv->dma_hwdesc), - &priv->dma_hwdesc_phys, - GFP_KERNEL); - if (!priv->dma_hwdesc) - return -ENOMEM; + if (priv->soc_info->has_osd) { + priv->dma_hwdesc_f0 = dmam_alloc_coherent(dev, + sizeof(*priv->dma_hwdesc_f0), + &priv->dma_hwdesc_phys_f0, + GFP_KERNEL); + if (!priv->dma_hwdesc_f0) + return -ENOMEM; - ret = devm_add_action_or_reset(dev, ingenic_drm_free_dma_hwdesc, priv); - if (ret) - return ret; + priv->dma_hwdesc_f0->next = priv->dma_hwdesc_phys_f0; + priv->dma_hwdesc_f0->id = 0xf0; + } - priv->dma_hwdesc->next = priv->dma_hwdesc_phys; - priv->dma_hwdesc->id = 0xdeafbead; + if (soc_info->has_osd) + priv->ipu_plane = drm_plane_from_index(drm, 0); - drm_plane_helper_add(&priv->primary, &ingenic_drm_plane_helper_funcs); + drm_plane_helper_add(&priv->f1, &ingenic_drm_plane_helper_funcs); - ret = drm_universal_plane_init(drm, &priv->primary, - 0, &ingenic_drm_primary_plane_funcs, + ret = drm_universal_plane_init(drm, &priv->f1, 1, + &ingenic_drm_primary_plane_funcs, ingenic_drm_primary_formats, ARRAY_SIZE(ingenic_drm_primary_formats), NULL, DRM_PLANE_TYPE_PRIMARY, NULL); if (ret) { - dev_err(dev, "Failed to register primary plane: %i", ret); + dev_err(dev, "Failed to register plane: %i\n", ret); return ret; } drm_crtc_helper_add(&priv->crtc, &ingenic_drm_crtc_helper_funcs); - ret = drm_crtc_init_with_planes(drm, &priv->crtc, &priv->primary, + ret = drm_crtc_init_with_planes(drm, &priv->crtc, &priv->f1, NULL, &ingenic_drm_crtc_funcs, NULL); if (ret) { - dev_err(dev, "Failed to init CRTC: %i", ret); + dev_err(dev, "Failed to init CRTC: %i\n", ret); return ret; } - priv->encoder.possible_crtcs = 1; + if (soc_info->has_osd) { + drm_plane_helper_add(&priv->f0, + &ingenic_drm_plane_helper_funcs); - drm_encoder_helper_add(&priv->encoder, - &ingenic_drm_encoder_helper_funcs); + ret = drm_universal_plane_init(drm, &priv->f0, 1, + &ingenic_drm_primary_plane_funcs, + ingenic_drm_primary_formats, + ARRAY_SIZE(ingenic_drm_primary_formats), + NULL, DRM_PLANE_TYPE_OVERLAY, + NULL); + if (ret) { + dev_err(dev, "Failed to register overlay plane: %i\n", + ret); + return ret; + } - ret = drm_simple_encoder_init(drm, &priv->encoder, - DRM_MODE_ENCODER_DPI); - if (ret) { - dev_err(dev, "Failed to init encoder: %i", ret); - return ret; + if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) { + ret = component_bind_all(dev, drm); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to bind components: %i\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(dev, ingenic_drm_unbind_all, priv); + if (ret) + return ret; + + priv->ipu_plane = drm_plane_from_index(drm, 2); + if (!priv->ipu_plane) { + dev_err(dev, "Failed to retrieve IPU plane\n"); + return -EINVAL; + } + } } - ret = drm_bridge_attach(&priv->encoder, bridge, NULL, 0); - if (ret) { - dev_err(dev, "Unable to attach bridge"); - return ret; + for (i = 0; ; i++) { + ret = drm_of_find_panel_or_bridge(dev->of_node, 0, i, &panel, &bridge); + if (ret) { + if (ret == -ENODEV) + break; /* we're done */ + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get bridge handle\n"); + return ret; + } + + if (panel) + bridge = devm_drm_panel_bridge_add_typed(dev, panel, + DRM_MODE_CONNECTOR_DPI); + + encoder = devm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL); + if (!encoder) + return -ENOMEM; + + encoder->possible_crtcs = 1; + + drm_encoder_helper_add(encoder, &ingenic_drm_encoder_helper_funcs); + + ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_DPI); + if (ret) { + dev_err(dev, "Failed to init encoder: %d\n", ret); + return ret; + } + + ret = drm_bridge_attach(encoder, bridge, NULL, 0); + if (ret) { + dev_err(dev, "Unable to attach bridge\n"); + return ret; + } + } + + drm_for_each_encoder(encoder, drm) { + clone_mask |= BIT(drm_encoder_index(encoder)); + } + + drm_for_each_encoder(encoder, drm) { + encoder->possible_clones = clone_mask; } ret = drm_irq_install(drm, irq); if (ret) { - dev_err(dev, "Unable to install IRQ handler"); + dev_err(dev, "Unable to install IRQ handler\n"); return ret; } ret = drm_vblank_init(drm, 1); if (ret) { - dev_err(dev, "Failed calling drm_vblank_init()"); + dev_err(dev, "Failed calling drm_vblank_init()\n"); return ret; } @@ -731,7 +887,7 @@ static int ingenic_drm_probe(struct platform_device *pdev) ret = clk_prepare_enable(priv->pix_clk); if (ret) { - dev_err(dev, "Unable to start pixel clock"); + dev_err(dev, "Unable to start pixel clock\n"); return ret; } @@ -746,20 +902,28 @@ static int ingenic_drm_probe(struct platform_device *pdev) */ ret = clk_set_rate(priv->lcd_clk, parent_rate); if (ret) { - dev_err(dev, "Unable to set LCD clock rate"); + dev_err(dev, "Unable to set LCD clock rate\n"); goto err_pixclk_disable; } ret = clk_prepare_enable(priv->lcd_clk); if (ret) { - dev_err(dev, "Unable to start lcd clock"); + dev_err(dev, "Unable to start lcd clock\n"); goto err_pixclk_disable; } } + /* Set address of our DMA descriptor chain */ + regmap_write(priv->map, JZ_REG_LCD_DA0, priv->dma_hwdesc_phys_f0); + regmap_write(priv->map, JZ_REG_LCD_DA1, priv->dma_hwdesc_phys_f1); + + /* Enable OSD if available */ + if (soc_info->has_osd) + regmap_write(priv->map, JZ_REG_LCD_OSDC, JZ_LCD_OSDC_OSDEN); + ret = drm_dev_register(drm, 0); if (ret) { - dev_err(dev, "Failed to register DRM driver"); + dev_err(dev, "Failed to register DRM driver\n"); goto err_devclk_disable; } @@ -775,9 +939,14 @@ err_pixclk_disable: return ret; } -static int ingenic_drm_remove(struct platform_device *pdev) +static int compare_of(struct device *dev, void *data) { - struct ingenic_drm *priv = platform_get_drvdata(pdev); + return dev->of_node == data; +} + +static void ingenic_drm_unbind(struct device *dev) +{ + struct ingenic_drm *priv = dev_get_drvdata(dev); if (priv->lcd_clk) clk_disable_unprepare(priv->lcd_clk); @@ -785,24 +954,63 @@ static int ingenic_drm_remove(struct platform_device *pdev) drm_dev_unregister(&priv->drm); drm_atomic_helper_shutdown(&priv->drm); +} + +static const struct component_master_ops ingenic_master_ops = { + .bind = ingenic_drm_bind, + .unbind = ingenic_drm_unbind, +}; + +static int ingenic_drm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct component_match *match = NULL; + struct device_node *np; + + if (!IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) + return ingenic_drm_bind(dev); + + /* IPU is at port address 8 */ + np = of_graph_get_remote_node(dev->of_node, 8, 0); + if (!np) { + dev_err(dev, "Unable to get IPU node\n"); + return -EINVAL; + } + + drm_of_component_match_add(dev, &match, compare_of, np); + + return component_master_add_with_match(dev, &ingenic_master_ops, match); +} + +static int ingenic_drm_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + if (!IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) + ingenic_drm_unbind(dev); + else + component_master_del(dev, &ingenic_master_ops); return 0; } static const struct jz_soc_info jz4740_soc_info = { .needs_dev_clk = true, + .has_osd = false, .max_width = 800, .max_height = 600, }; static const struct jz_soc_info jz4725b_soc_info = { .needs_dev_clk = false, + .has_osd = true, .max_width = 800, .max_height = 600, }; static const struct jz_soc_info jz4770_soc_info = { .needs_dev_clk = false, + .has_osd = true, .max_width = 1280, .max_height = 720, }; @@ -823,7 +1031,29 @@ static struct platform_driver ingenic_drm_driver = { .probe = ingenic_drm_probe, .remove = ingenic_drm_remove, }; -module_platform_driver(ingenic_drm_driver); + +static int ingenic_drm_init(void) +{ + int err; + + if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) { + err = platform_driver_register(ingenic_ipu_driver_ptr); + if (err) + return err; + } + + return platform_driver_register(&ingenic_drm_driver); +} +module_init(ingenic_drm_init); + +static void ingenic_drm_exit(void) +{ + platform_driver_unregister(&ingenic_drm_driver); + + if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) + platform_driver_unregister(ingenic_ipu_driver_ptr); +} +module_exit(ingenic_drm_exit); MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); MODULE_DESCRIPTION("DRM driver for the Ingenic SoCs\n"); diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.h b/drivers/gpu/drm/ingenic/ingenic-drm.h new file mode 100644 index 000000000000..43f7d959cff7 --- /dev/null +++ b/drivers/gpu/drm/ingenic/ingenic-drm.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// +// Ingenic JZ47xx KMS driver - Register definitions and private API +// +// Copyright (C) 2020, Paul Cercueil <paul@crapouillou.net> + +#ifndef DRIVERS_GPU_DRM_INGENIC_INGENIC_DRM_H +#define DRIVERS_GPU_DRM_INGENIC_INGENIC_DRM_H + +#include <linux/bitops.h> +#include <linux/types.h> + +#define JZ_REG_LCD_CFG 0x00 +#define JZ_REG_LCD_VSYNC 0x04 +#define JZ_REG_LCD_HSYNC 0x08 +#define JZ_REG_LCD_VAT 0x0C +#define JZ_REG_LCD_DAH 0x10 +#define JZ_REG_LCD_DAV 0x14 +#define JZ_REG_LCD_PS 0x18 +#define JZ_REG_LCD_CLS 0x1C +#define JZ_REG_LCD_SPL 0x20 +#define JZ_REG_LCD_REV 0x24 +#define JZ_REG_LCD_CTRL 0x30 +#define JZ_REG_LCD_STATE 0x34 +#define JZ_REG_LCD_IID 0x38 +#define JZ_REG_LCD_DA0 0x40 +#define JZ_REG_LCD_SA0 0x44 +#define JZ_REG_LCD_FID0 0x48 +#define JZ_REG_LCD_CMD0 0x4C +#define JZ_REG_LCD_DA1 0x50 +#define JZ_REG_LCD_SA1 0x54 +#define JZ_REG_LCD_FID1 0x58 +#define JZ_REG_LCD_CMD1 0x5C +#define JZ_REG_LCD_OSDC 0x100 +#define JZ_REG_LCD_OSDCTRL 0x104 +#define JZ_REG_LCD_OSDS 0x108 +#define JZ_REG_LCD_BGC 0x10c +#define JZ_REG_LCD_KEY0 0x110 +#define JZ_REG_LCD_KEY1 0x114 +#define JZ_REG_LCD_ALPHA 0x118 +#define JZ_REG_LCD_IPUR 0x11c +#define JZ_REG_LCD_XYP0 0x120 +#define JZ_REG_LCD_XYP1 0x124 +#define JZ_REG_LCD_SIZE0 0x128 +#define JZ_REG_LCD_SIZE1 0x12c + +#define JZ_LCD_CFG_SLCD BIT(31) +#define JZ_LCD_CFG_PS_DISABLE BIT(23) +#define JZ_LCD_CFG_CLS_DISABLE BIT(22) +#define JZ_LCD_CFG_SPL_DISABLE BIT(21) +#define JZ_LCD_CFG_REV_DISABLE BIT(20) +#define JZ_LCD_CFG_HSYNCM BIT(19) +#define JZ_LCD_CFG_PCLKM BIT(18) +#define JZ_LCD_CFG_INV BIT(17) +#define JZ_LCD_CFG_SYNC_DIR BIT(16) +#define JZ_LCD_CFG_PS_POLARITY BIT(15) +#define JZ_LCD_CFG_CLS_POLARITY BIT(14) +#define JZ_LCD_CFG_SPL_POLARITY BIT(13) +#define JZ_LCD_CFG_REV_POLARITY BIT(12) +#define JZ_LCD_CFG_HSYNC_ACTIVE_LOW BIT(11) +#define JZ_LCD_CFG_PCLK_FALLING_EDGE BIT(10) +#define JZ_LCD_CFG_DE_ACTIVE_LOW BIT(9) +#define JZ_LCD_CFG_VSYNC_ACTIVE_LOW BIT(8) +#define JZ_LCD_CFG_18_BIT BIT(7) +#define JZ_LCD_CFG_PDW (BIT(5) | BIT(4)) + +#define JZ_LCD_CFG_MODE_GENERIC_16BIT 0 +#define JZ_LCD_CFG_MODE_GENERIC_18BIT BIT(7) +#define JZ_LCD_CFG_MODE_GENERIC_24BIT BIT(6) + +#define JZ_LCD_CFG_MODE_SPECIAL_TFT_1 1 +#define JZ_LCD_CFG_MODE_SPECIAL_TFT_2 2 +#define JZ_LCD_CFG_MODE_SPECIAL_TFT_3 3 + +#define JZ_LCD_CFG_MODE_TV_OUT_P 4 +#define JZ_LCD_CFG_MODE_TV_OUT_I 6 + +#define JZ_LCD_CFG_MODE_SINGLE_COLOR_STN 8 +#define JZ_LCD_CFG_MODE_SINGLE_MONOCHROME_STN 9 +#define JZ_LCD_CFG_MODE_DUAL_COLOR_STN 10 +#define JZ_LCD_CFG_MODE_DUAL_MONOCHROME_STN 11 + +#define JZ_LCD_CFG_MODE_8BIT_SERIAL 12 +#define JZ_LCD_CFG_MODE_LCM 13 + +#define JZ_LCD_VSYNC_VPS_OFFSET 16 +#define JZ_LCD_VSYNC_VPE_OFFSET 0 + +#define JZ_LCD_HSYNC_HPS_OFFSET 16 +#define JZ_LCD_HSYNC_HPE_OFFSET 0 + +#define JZ_LCD_VAT_HT_OFFSET 16 +#define JZ_LCD_VAT_VT_OFFSET 0 + +#define JZ_LCD_DAH_HDS_OFFSET 16 +#define JZ_LCD_DAH_HDE_OFFSET 0 + +#define JZ_LCD_DAV_VDS_OFFSET 16 +#define JZ_LCD_DAV_VDE_OFFSET 0 + +#define JZ_LCD_CTRL_BURST_4 (0x0 << 28) +#define JZ_LCD_CTRL_BURST_8 (0x1 << 28) +#define JZ_LCD_CTRL_BURST_16 (0x2 << 28) +#define JZ_LCD_CTRL_RGB555 BIT(27) +#define JZ_LCD_CTRL_OFUP BIT(26) +#define JZ_LCD_CTRL_FRC_GRAYSCALE_16 (0x0 << 24) +#define JZ_LCD_CTRL_FRC_GRAYSCALE_4 (0x1 << 24) +#define JZ_LCD_CTRL_FRC_GRAYSCALE_2 (0x2 << 24) +#define JZ_LCD_CTRL_PDD_MASK (0xff << 16) +#define JZ_LCD_CTRL_EOF_IRQ BIT(13) +#define JZ_LCD_CTRL_SOF_IRQ BIT(12) +#define JZ_LCD_CTRL_OFU_IRQ BIT(11) +#define JZ_LCD_CTRL_IFU0_IRQ BIT(10) +#define JZ_LCD_CTRL_IFU1_IRQ BIT(9) +#define JZ_LCD_CTRL_DD_IRQ BIT(8) +#define JZ_LCD_CTRL_QDD_IRQ BIT(7) +#define JZ_LCD_CTRL_REVERSE_ENDIAN BIT(6) +#define JZ_LCD_CTRL_LSB_FISRT BIT(5) +#define JZ_LCD_CTRL_DISABLE BIT(4) +#define JZ_LCD_CTRL_ENABLE BIT(3) +#define JZ_LCD_CTRL_BPP_1 0x0 +#define JZ_LCD_CTRL_BPP_2 0x1 +#define JZ_LCD_CTRL_BPP_4 0x2 +#define JZ_LCD_CTRL_BPP_8 0x3 +#define JZ_LCD_CTRL_BPP_15_16 0x4 +#define JZ_LCD_CTRL_BPP_18_24 0x5 +#define JZ_LCD_CTRL_BPP_MASK (JZ_LCD_CTRL_RGB555 | 0x7) + +#define JZ_LCD_CMD_SOF_IRQ BIT(31) +#define JZ_LCD_CMD_EOF_IRQ BIT(30) +#define JZ_LCD_CMD_ENABLE_PAL BIT(28) + +#define JZ_LCD_SYNC_MASK 0x3ff + +#define JZ_LCD_STATE_EOF_IRQ BIT(5) +#define JZ_LCD_STATE_SOF_IRQ BIT(4) +#define JZ_LCD_STATE_DISABLED BIT(0) + +#define JZ_LCD_OSDC_OSDEN BIT(0) +#define JZ_LCD_OSDC_F0EN BIT(3) +#define JZ_LCD_OSDC_F1EN BIT(4) + +#define JZ_LCD_OSDCTRL_IPU BIT(15) +#define JZ_LCD_OSDCTRL_RGB555 BIT(4) +#define JZ_LCD_OSDCTRL_CHANGE BIT(3) +#define JZ_LCD_OSDCTRL_BPP_15_16 0x4 +#define JZ_LCD_OSDCTRL_BPP_18_24 0x5 +#define JZ_LCD_OSDCTRL_BPP_30 0x7 +#define JZ_LCD_OSDCTRL_BPP_MASK (JZ_LCD_OSDCTRL_RGB555 | 0x7) + +#define JZ_LCD_OSDS_READY BIT(0) + +#define JZ_LCD_IPUR_IPUREN BIT(31) +#define JZ_LCD_IPUR_IPUR_LSB 0 + +#define JZ_LCD_XYP01_XPOS_LSB 0 +#define JZ_LCD_XYP01_YPOS_LSB 16 + +#define JZ_LCD_SIZE01_WIDTH_LSB 0 +#define JZ_LCD_SIZE01_HEIGHT_LSB 16 + +struct device; +struct drm_plane; +struct drm_plane_state; +struct platform_driver; + +void ingenic_drm_plane_config(struct device *dev, + struct drm_plane *plane, u32 fourcc); +void ingenic_drm_plane_disable(struct device *dev, struct drm_plane *plane); + +extern struct platform_driver *ingenic_ipu_driver_ptr; + +#endif /* DRIVERS_GPU_DRM_INGENIC_INGENIC_DRM_H */ diff --git a/drivers/gpu/drm/ingenic/ingenic-ipu.c b/drivers/gpu/drm/ingenic/ingenic-ipu.c new file mode 100644 index 000000000000..7a0a8bd865d3 --- /dev/null +++ b/drivers/gpu/drm/ingenic/ingenic-ipu.c @@ -0,0 +1,853 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Ingenic JZ47xx IPU driver +// +// Copyright (C) 2020, Paul Cercueil <paul@crapouillou.net> +// Copyright (C) 2020, Daniel Silsby <dansilsby@gmail.com> + +#include "ingenic-drm.h" +#include "ingenic-ipu.h" + +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/gcd.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/time.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_plane.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_property.h> +#include <drm/drm_vblank.h> + +struct ingenic_ipu; + +struct soc_info { + const u32 *formats; + size_t num_formats; + bool has_bicubic; + + void (*set_coefs)(struct ingenic_ipu *ipu, unsigned int reg, + unsigned int sharpness, bool downscale, + unsigned int weight, unsigned int offset); +}; + +struct ingenic_ipu { + struct drm_plane plane; + struct drm_device *drm; + struct device *dev, *master; + struct regmap *map; + struct clk *clk; + const struct soc_info *soc_info; + + unsigned int num_w, num_h, denom_w, denom_h; + + dma_addr_t addr_y, addr_u, addr_v; + + struct drm_property *sharpness_prop; + unsigned int sharpness; +}; + +/* Signed 15.16 fixed-point math (for bicubic scaling coefficients) */ +#define I2F(i) ((s32)(i) * 65536) +#define F2I(f) ((f) / 65536) +#define FMUL(fa, fb) ((s32)(((s64)(fa) * (s64)(fb)) / 65536)) +#define SHARPNESS_INCR (I2F(-1) / 8) + +static inline struct ingenic_ipu *plane_to_ingenic_ipu(struct drm_plane *plane) +{ + return container_of(plane, struct ingenic_ipu, plane); +} + +/* + * Apply conventional cubic convolution kernel. Both parameters + * and return value are 15.16 signed fixed-point. + * + * @f_a: Sharpness factor, typically in range [-4.0, -0.25]. + * A larger magnitude increases perceived sharpness, but going past + * -2.0 might cause ringing artifacts to outweigh any improvement. + * Nice values on a 320x240 LCD are between -0.75 and -2.0. + * + * @f_x: Absolute distance in pixels from 'pixel 0' sample position + * along horizontal (or vertical) source axis. Range is [0, +2.0]. + * + * returns: Weight of this pixel within 4-pixel sample group. Range is + * [-2.0, +2.0]. For moderate (i.e. > -3.0) sharpness factors, + * range is within [-1.0, +1.0]. + */ +static inline s32 cubic_conv(s32 f_a, s32 f_x) +{ + const s32 f_1 = I2F(1); + const s32 f_2 = I2F(2); + const s32 f_3 = I2F(3); + const s32 f_4 = I2F(4); + const s32 f_x2 = FMUL(f_x, f_x); + const s32 f_x3 = FMUL(f_x, f_x2); + + if (f_x <= f_1) + return FMUL((f_a + f_2), f_x3) - FMUL((f_a + f_3), f_x2) + f_1; + else if (f_x <= f_2) + return FMUL(f_a, (f_x3 - 5 * f_x2 + 8 * f_x - f_4)); + else + return 0; +} + +/* + * On entry, "weight" is a coefficient suitable for bilinear mode, + * which is converted to a set of four suitable for bicubic mode. + * + * "weight 512" means all of pixel 0; + * "weight 256" means half of pixel 0 and half of pixel 1; + * "weight 0" means all of pixel 1; + * + * "offset" is increment to next source pixel sample location. + */ +static void jz4760_set_coefs(struct ingenic_ipu *ipu, unsigned int reg, + unsigned int sharpness, bool downscale, + unsigned int weight, unsigned int offset) +{ + u32 val; + s32 w0, w1, w2, w3; /* Pixel weights at X (or Y) offsets -1,0,1,2 */ + + weight = clamp_val(weight, 0, 512); + + if (sharpness < 2) { + /* + * When sharpness setting is 0, emulate nearest-neighbor. + * When sharpness setting is 1, emulate bilinear. + */ + + if (sharpness == 0) + weight = weight >= 256 ? 512 : 0; + w0 = 0; + w1 = weight; + w2 = 512 - weight; + w3 = 0; + } else { + const s32 f_a = SHARPNESS_INCR * sharpness; + const s32 f_h = I2F(1) / 2; /* Round up 0.5 */ + + /* + * Note that always rounding towards +infinity here is intended. + * The resulting coefficients match a round-to-nearest-int + * double floating-point implementation. + */ + + weight = 512 - weight; + w0 = F2I(f_h + 512 * cubic_conv(f_a, I2F(512 + weight) / 512)); + w1 = F2I(f_h + 512 * cubic_conv(f_a, I2F(0 + weight) / 512)); + w2 = F2I(f_h + 512 * cubic_conv(f_a, I2F(512 - weight) / 512)); + w3 = F2I(f_h + 512 * cubic_conv(f_a, I2F(1024 - weight) / 512)); + w0 = clamp_val(w0, -1024, 1023); + w1 = clamp_val(w1, -1024, 1023); + w2 = clamp_val(w2, -1024, 1023); + w3 = clamp_val(w3, -1024, 1023); + } + + val = ((w1 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF31_LSB) | + ((w0 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF20_LSB); + regmap_write(ipu->map, reg, val); + + val = ((w3 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF31_LSB) | + ((w2 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF20_LSB) | + ((offset & JZ4760_IPU_RSZ_OFFSET_MASK) << JZ4760_IPU_RSZ_OFFSET_LSB); + regmap_write(ipu->map, reg, val); +} + +static void jz4725b_set_coefs(struct ingenic_ipu *ipu, unsigned int reg, + unsigned int sharpness, bool downscale, + unsigned int weight, unsigned int offset) +{ + u32 val = JZ4725B_IPU_RSZ_LUT_OUT_EN; + unsigned int i; + + weight = clamp_val(weight, 0, 512); + + if (sharpness == 0) + weight = weight >= 256 ? 512 : 0; + + val |= (weight & JZ4725B_IPU_RSZ_LUT_COEF_MASK) << JZ4725B_IPU_RSZ_LUT_COEF_LSB; + if (downscale || !!offset) + val |= JZ4725B_IPU_RSZ_LUT_IN_EN; + + regmap_write(ipu->map, reg, val); + + if (downscale) { + for (i = 1; i < offset; i++) + regmap_write(ipu->map, reg, JZ4725B_IPU_RSZ_LUT_IN_EN); + } +} + +static void ingenic_ipu_set_downscale_coefs(struct ingenic_ipu *ipu, + unsigned int reg, + unsigned int num, + unsigned int denom) +{ + unsigned int i, offset, weight, weight_num = denom; + + for (i = 0; i < num; i++) { + weight_num = num + (weight_num - num) % (num * 2); + weight = 512 - 512 * (weight_num - num) / (num * 2); + weight_num += denom * 2; + offset = (weight_num - num) / (num * 2); + + ipu->soc_info->set_coefs(ipu, reg, ipu->sharpness, + true, weight, offset); + } +} + +static void ingenic_ipu_set_integer_upscale_coefs(struct ingenic_ipu *ipu, + unsigned int reg, + unsigned int num) +{ + /* + * Force nearest-neighbor scaling and use simple math when upscaling + * by an integer ratio. It looks better, and fixes a few problem cases. + */ + unsigned int i; + + for (i = 0; i < num; i++) + ipu->soc_info->set_coefs(ipu, reg, 0, false, 512, i == num - 1); +} + +static void ingenic_ipu_set_upscale_coefs(struct ingenic_ipu *ipu, + unsigned int reg, + unsigned int num, + unsigned int denom) +{ + unsigned int i, offset, weight, weight_num = 0; + + for (i = 0; i < num; i++) { + weight = 512 - 512 * weight_num / num; + weight_num += denom; + offset = weight_num >= num; + + if (offset) + weight_num -= num; + + ipu->soc_info->set_coefs(ipu, reg, ipu->sharpness, + false, weight, offset); + } +} + +static void ingenic_ipu_set_coefs(struct ingenic_ipu *ipu, unsigned int reg, + unsigned int num, unsigned int denom) +{ + /* Begin programming the LUT */ + regmap_write(ipu->map, reg, -1); + + if (denom > num) + ingenic_ipu_set_downscale_coefs(ipu, reg, num, denom); + else if (denom == 1) + ingenic_ipu_set_integer_upscale_coefs(ipu, reg, num); + else + ingenic_ipu_set_upscale_coefs(ipu, reg, num, denom); +} + +static int reduce_fraction(unsigned int *num, unsigned int *denom) +{ + unsigned long d = gcd(*num, *denom); + + /* The scaling table has only 31 entries */ + if (*num > 31 * d) + return -EINVAL; + + *num /= d; + *denom /= d; + return 0; +} + +static inline bool osd_changed(struct drm_plane_state *state, + struct drm_plane_state *oldstate) +{ + return state->src_x != oldstate->src_x || + state->src_y != oldstate->src_y || + state->src_w != oldstate->src_w || + state->src_h != oldstate->src_h || + state->crtc_x != oldstate->crtc_x || + state->crtc_y != oldstate->crtc_y || + state->crtc_w != oldstate->crtc_w || + state->crtc_h != oldstate->crtc_h; +} + +static void ingenic_ipu_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *oldstate) +{ + struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); + struct drm_plane_state *state = plane->state; + const struct drm_format_info *finfo; + u32 ctrl, stride = 0, coef_index = 0, format = 0; + bool needs_modeset, upscaling_w, upscaling_h; + + if (!state || !state->fb) + return; + + finfo = drm_format_info(state->fb->format->format); + + /* Reset all the registers if needed */ + needs_modeset = drm_atomic_crtc_needs_modeset(state->crtc->state); + if (needs_modeset) { + regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_RST); + + /* Enable the chip */ + regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, + JZ_IPU_CTRL_CHIP_EN | JZ_IPU_CTRL_LCDC_SEL); + } + + /* New addresses will be committed in vblank handler... */ + ipu->addr_y = drm_fb_cma_get_gem_addr(state->fb, state, 0); + if (finfo->num_planes > 1) + ipu->addr_u = drm_fb_cma_get_gem_addr(state->fb, state, 1); + if (finfo->num_planes > 2) + ipu->addr_v = drm_fb_cma_get_gem_addr(state->fb, state, 2); + + if (!needs_modeset) + return; + + /* Or right here if we're doing a full modeset. */ + regmap_write(ipu->map, JZ_REG_IPU_Y_ADDR, ipu->addr_y); + regmap_write(ipu->map, JZ_REG_IPU_U_ADDR, ipu->addr_u); + regmap_write(ipu->map, JZ_REG_IPU_V_ADDR, ipu->addr_v); + + if (finfo->num_planes == 1) + regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_SPKG_SEL); + + ingenic_drm_plane_config(ipu->master, plane, DRM_FORMAT_XRGB8888); + + /* Set the input height/width/strides */ + if (finfo->num_planes > 2) + stride = ((state->src_w >> 16) * finfo->cpp[2] / finfo->hsub) + << JZ_IPU_UV_STRIDE_V_LSB; + + if (finfo->num_planes > 1) + stride |= ((state->src_w >> 16) * finfo->cpp[1] / finfo->hsub) + << JZ_IPU_UV_STRIDE_U_LSB; + + regmap_write(ipu->map, JZ_REG_IPU_UV_STRIDE, stride); + + stride = ((state->src_w >> 16) * finfo->cpp[0]) << JZ_IPU_Y_STRIDE_Y_LSB; + regmap_write(ipu->map, JZ_REG_IPU_Y_STRIDE, stride); + + regmap_write(ipu->map, JZ_REG_IPU_IN_GS, + (stride << JZ_IPU_IN_GS_W_LSB) | + ((state->src_h >> 16) << JZ_IPU_IN_GS_H_LSB)); + + switch (finfo->format) { + case DRM_FORMAT_XRGB1555: + format = JZ_IPU_D_FMT_IN_FMT_RGB555 | + JZ_IPU_D_FMT_RGB_OUT_OFT_RGB; + break; + case DRM_FORMAT_XBGR1555: + format = JZ_IPU_D_FMT_IN_FMT_RGB555 | + JZ_IPU_D_FMT_RGB_OUT_OFT_BGR; + break; + case DRM_FORMAT_RGB565: + format = JZ_IPU_D_FMT_IN_FMT_RGB565 | + JZ_IPU_D_FMT_RGB_OUT_OFT_RGB; + break; + case DRM_FORMAT_BGR565: + format = JZ_IPU_D_FMT_IN_FMT_RGB565 | + JZ_IPU_D_FMT_RGB_OUT_OFT_BGR; + break; + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XYUV8888: + format = JZ_IPU_D_FMT_IN_FMT_RGB888 | + JZ_IPU_D_FMT_RGB_OUT_OFT_RGB; + break; + case DRM_FORMAT_XBGR8888: + format = JZ_IPU_D_FMT_IN_FMT_RGB888 | + JZ_IPU_D_FMT_RGB_OUT_OFT_BGR; + break; + case DRM_FORMAT_YUYV: + format = JZ_IPU_D_FMT_IN_FMT_YUV422 | + JZ_IPU_D_FMT_YUV_VY1UY0; + break; + case DRM_FORMAT_YVYU: + format = JZ_IPU_D_FMT_IN_FMT_YUV422 | + JZ_IPU_D_FMT_YUV_UY1VY0; + break; + case DRM_FORMAT_UYVY: + format = JZ_IPU_D_FMT_IN_FMT_YUV422 | + JZ_IPU_D_FMT_YUV_Y1VY0U; + break; + case DRM_FORMAT_VYUY: + format = JZ_IPU_D_FMT_IN_FMT_YUV422 | + JZ_IPU_D_FMT_YUV_Y1UY0V; + break; + case DRM_FORMAT_YUV411: + format = JZ_IPU_D_FMT_IN_FMT_YUV411; + break; + case DRM_FORMAT_YUV420: + format = JZ_IPU_D_FMT_IN_FMT_YUV420; + break; + case DRM_FORMAT_YUV422: + format = JZ_IPU_D_FMT_IN_FMT_YUV422; + break; + case DRM_FORMAT_YUV444: + format = JZ_IPU_D_FMT_IN_FMT_YUV444; + break; + default: + WARN_ONCE(1, "Unsupported format"); + break; + } + + /* Fix output to RGB888 */ + format |= JZ_IPU_D_FMT_OUT_FMT_RGB888; + + /* Set pixel format */ + regmap_write(ipu->map, JZ_REG_IPU_D_FMT, format); + + /* Set the output height/width/stride */ + regmap_write(ipu->map, JZ_REG_IPU_OUT_GS, + ((state->crtc_w * 4) << JZ_IPU_OUT_GS_W_LSB) + | state->crtc_h << JZ_IPU_OUT_GS_H_LSB); + regmap_write(ipu->map, JZ_REG_IPU_OUT_STRIDE, state->crtc_w * 4); + + if (finfo->is_yuv) { + regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_CSC_EN); + + /* + * Offsets for Chroma/Luma. + * y = source Y - LUMA, + * u = source Cb - CHROMA, + * v = source Cr - CHROMA + */ + regmap_write(ipu->map, JZ_REG_IPU_CSC_OFFSET, + 128 << JZ_IPU_CSC_OFFSET_CHROMA_LSB | + 0 << JZ_IPU_CSC_OFFSET_LUMA_LSB); + + /* + * YUV422 to RGB conversion table. + * R = C0 / 0x400 * y + C1 / 0x400 * v + * G = C0 / 0x400 * y - C2 / 0x400 * u - C3 / 0x400 * v + * B = C0 / 0x400 * y + C4 / 0x400 * u + */ + regmap_write(ipu->map, JZ_REG_IPU_CSC_C0_COEF, 0x4a8); + regmap_write(ipu->map, JZ_REG_IPU_CSC_C1_COEF, 0x662); + regmap_write(ipu->map, JZ_REG_IPU_CSC_C2_COEF, 0x191); + regmap_write(ipu->map, JZ_REG_IPU_CSC_C3_COEF, 0x341); + regmap_write(ipu->map, JZ_REG_IPU_CSC_C4_COEF, 0x811); + } + + ctrl = 0; + + /* + * Must set ZOOM_SEL before programming bicubic LUTs. + * If the IPU supports bicubic, we enable it unconditionally, since it + * can do anything bilinear can and more. + */ + if (ipu->soc_info->has_bicubic) + ctrl |= JZ_IPU_CTRL_ZOOM_SEL; + + upscaling_w = ipu->num_w > ipu->denom_w; + if (upscaling_w) + ctrl |= JZ_IPU_CTRL_HSCALE; + + if (ipu->num_w != 1 || ipu->denom_w != 1) { + if (!ipu->soc_info->has_bicubic && !upscaling_w) + coef_index |= (ipu->denom_w - 1) << 16; + else + coef_index |= (ipu->num_w - 1) << 16; + ctrl |= JZ_IPU_CTRL_HRSZ_EN; + } + + upscaling_h = ipu->num_h > ipu->denom_h; + if (upscaling_h) + ctrl |= JZ_IPU_CTRL_VSCALE; + + if (ipu->num_h != 1 || ipu->denom_h != 1) { + if (!ipu->soc_info->has_bicubic && !upscaling_h) + coef_index |= ipu->denom_h - 1; + else + coef_index |= ipu->num_h - 1; + ctrl |= JZ_IPU_CTRL_VRSZ_EN; + } + + regmap_update_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_ZOOM_SEL | + JZ_IPU_CTRL_HRSZ_EN | JZ_IPU_CTRL_VRSZ_EN | + JZ_IPU_CTRL_HSCALE | JZ_IPU_CTRL_VSCALE, ctrl); + + /* Set the LUT index register */ + regmap_write(ipu->map, JZ_REG_IPU_RSZ_COEF_INDEX, coef_index); + + if (ipu->num_w != 1 || ipu->denom_w != 1) + ingenic_ipu_set_coefs(ipu, JZ_REG_IPU_HRSZ_COEF_LUT, + ipu->num_w, ipu->denom_w); + + if (ipu->num_h != 1 || ipu->denom_h != 1) + ingenic_ipu_set_coefs(ipu, JZ_REG_IPU_VRSZ_COEF_LUT, + ipu->num_h, ipu->denom_h); + + /* Clear STATUS register */ + regmap_write(ipu->map, JZ_REG_IPU_STATUS, 0); + + /* Start IPU */ + regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, + JZ_IPU_CTRL_RUN | JZ_IPU_CTRL_FM_IRQ_EN); + + dev_dbg(ipu->dev, "Scaling %ux%u to %ux%u (%u:%u horiz, %u:%u vert)\n", + state->src_w >> 16, state->src_h >> 16, + state->crtc_w, state->crtc_h, + ipu->num_w, ipu->denom_w, ipu->num_h, ipu->denom_h); +} + +static int ingenic_ipu_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + unsigned int num_w, denom_w, num_h, denom_h, xres, yres; + struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); + struct drm_crtc *crtc = state->crtc ?: plane->state->crtc; + struct drm_crtc_state *crtc_state; + + if (!crtc) + return 0; + + crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + /* Request a full modeset if we are enabling or disabling the IPU. */ + if (!plane->state->crtc ^ !state->crtc) + crtc_state->mode_changed = true; + + if (!state->crtc || + !crtc_state->mode.hdisplay || !crtc_state->mode.vdisplay) + return 0; + + /* Plane must be fully visible */ + if (state->crtc_x < 0 || state->crtc_y < 0 || + state->crtc_x + state->crtc_w > crtc_state->mode.hdisplay || + state->crtc_y + state->crtc_h > crtc_state->mode.vdisplay) + return -EINVAL; + + /* Minimum size is 4x4 */ + if ((state->src_w >> 16) < 4 || (state->src_h >> 16) < 4) + return -EINVAL; + + /* Input and output lines must have an even number of pixels. */ + if (((state->src_w >> 16) & 1) || (state->crtc_w & 1)) + return -EINVAL; + + if (!osd_changed(state, plane->state)) + return 0; + + crtc_state->mode_changed = true; + + xres = state->src_w >> 16; + yres = state->src_h >> 16; + + /* Adjust the coefficients until we find a valid configuration */ + for (denom_w = xres, num_w = state->crtc_w; + num_w <= crtc_state->mode.hdisplay; num_w++) + if (!reduce_fraction(&num_w, &denom_w)) + break; + if (num_w > crtc_state->mode.hdisplay) + return -EINVAL; + + for (denom_h = yres, num_h = state->crtc_h; + num_h <= crtc_state->mode.vdisplay; num_h++) + if (!reduce_fraction(&num_h, &denom_h)) + break; + if (num_h > crtc_state->mode.vdisplay) + return -EINVAL; + + ipu->num_w = num_w; + ipu->num_h = num_h; + ipu->denom_w = denom_w; + ipu->denom_h = denom_h; + + return 0; +} + +static void ingenic_ipu_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); + + regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_STOP); + regmap_clear_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_CHIP_EN); + + ingenic_drm_plane_disable(ipu->master, plane); +} + +static const struct drm_plane_helper_funcs ingenic_ipu_plane_helper_funcs = { + .atomic_update = ingenic_ipu_plane_atomic_update, + .atomic_check = ingenic_ipu_plane_atomic_check, + .atomic_disable = ingenic_ipu_plane_atomic_disable, + .prepare_fb = drm_gem_fb_prepare_fb, +}; + +static int +ingenic_ipu_plane_atomic_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, + struct drm_property *property, u64 *val) +{ + struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); + + if (property != ipu->sharpness_prop) + return -EINVAL; + + *val = ipu->sharpness; + + return 0; +} + +static int +ingenic_ipu_plane_atomic_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, u64 val) +{ + struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); + struct drm_crtc_state *crtc_state; + + if (property != ipu->sharpness_prop) + return -EINVAL; + + ipu->sharpness = val; + + if (state->crtc) { + crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + crtc_state->mode_changed = true; + } + + return 0; +} + +static const struct drm_plane_funcs ingenic_ipu_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .reset = drm_atomic_helper_plane_reset, + .destroy = drm_plane_cleanup, + + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + + .atomic_get_property = ingenic_ipu_plane_atomic_get_property, + .atomic_set_property = ingenic_ipu_plane_atomic_set_property, +}; + +static irqreturn_t ingenic_ipu_irq_handler(int irq, void *arg) +{ + struct ingenic_ipu *ipu = arg; + struct drm_crtc *crtc = drm_crtc_from_index(ipu->drm, 0); + unsigned int dummy; + + /* dummy read allows CPU to reconfigure IPU */ + regmap_read(ipu->map, JZ_REG_IPU_STATUS, &dummy); + + /* ACK interrupt */ + regmap_write(ipu->map, JZ_REG_IPU_STATUS, 0); + + /* Set previously cached addresses */ + regmap_write(ipu->map, JZ_REG_IPU_Y_ADDR, ipu->addr_y); + regmap_write(ipu->map, JZ_REG_IPU_U_ADDR, ipu->addr_u); + regmap_write(ipu->map, JZ_REG_IPU_V_ADDR, ipu->addr_v); + + /* Run IPU for the new frame */ + regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_RUN); + + drm_crtc_handle_vblank(crtc); + + return IRQ_HANDLED; +} + +static const struct regmap_config ingenic_ipu_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + + .max_register = JZ_REG_IPU_OUT_PHY_T_ADDR, +}; + +static int ingenic_ipu_bind(struct device *dev, struct device *master, void *d) +{ + struct platform_device *pdev = to_platform_device(dev); + const struct soc_info *soc_info; + struct drm_device *drm = d; + struct drm_plane *plane; + struct ingenic_ipu *ipu; + void __iomem *base; + unsigned int sharpness_max; + int err, irq; + + ipu = devm_kzalloc(dev, sizeof(*ipu), GFP_KERNEL); + if (!ipu) + return -ENOMEM; + + soc_info = of_device_get_match_data(dev); + if (!soc_info) { + dev_err(dev, "Missing platform data\n"); + return -EINVAL; + } + + ipu->dev = dev; + ipu->drm = drm; + ipu->master = master; + ipu->soc_info = soc_info; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) { + dev_err(dev, "Failed to get memory resource\n"); + return PTR_ERR(base); + } + + ipu->map = devm_regmap_init_mmio(dev, base, &ingenic_ipu_regmap_config); + if (IS_ERR(ipu->map)) { + dev_err(dev, "Failed to create regmap\n"); + return PTR_ERR(ipu->map); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ipu->clk = devm_clk_get(dev, "ipu"); + if (IS_ERR(ipu->clk)) { + dev_err(dev, "Failed to get pixel clock\n"); + return PTR_ERR(ipu->clk); + } + + err = devm_request_irq(dev, irq, ingenic_ipu_irq_handler, 0, + dev_name(dev), ipu); + if (err) { + dev_err(dev, "Unable to request IRQ\n"); + return err; + } + + plane = &ipu->plane; + dev_set_drvdata(dev, plane); + + drm_plane_helper_add(plane, &ingenic_ipu_plane_helper_funcs); + + err = drm_universal_plane_init(drm, plane, 1, &ingenic_ipu_plane_funcs, + soc_info->formats, soc_info->num_formats, + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); + if (err) { + dev_err(dev, "Failed to init plane: %i\n", err); + return err; + } + + /* + * Sharpness settings range is [0,32] + * 0 : nearest-neighbor + * 1 : bilinear + * 2 .. 32 : bicubic (translated to sharpness factor -0.25 .. -4.0) + */ + sharpness_max = soc_info->has_bicubic ? 32 : 1; + ipu->sharpness_prop = drm_property_create_range(drm, 0, "sharpness", + 0, sharpness_max); + if (!ipu->sharpness_prop) { + dev_err(dev, "Unable to create sharpness property\n"); + return -ENOMEM; + } + + /* Default sharpness factor: -0.125 * 8 = -1.0 */ + ipu->sharpness = soc_info->has_bicubic ? 8 : 1; + drm_object_attach_property(&plane->base, ipu->sharpness_prop, + ipu->sharpness); + + err = clk_prepare_enable(ipu->clk); + if (err) { + dev_err(dev, "Unable to enable clock\n"); + return err; + } + + return 0; +} + +static void ingenic_ipu_unbind(struct device *dev, + struct device *master, void *d) +{ + struct ingenic_ipu *ipu = dev_get_drvdata(dev); + + clk_disable_unprepare(ipu->clk); +} + +static const struct component_ops ingenic_ipu_ops = { + .bind = ingenic_ipu_bind, + .unbind = ingenic_ipu_unbind, +}; + +static int ingenic_ipu_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &ingenic_ipu_ops); +} + +static int ingenic_ipu_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &ingenic_ipu_ops); + return 0; +} + +static const u32 jz4725b_ipu_formats[] = { + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_YUV411, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV422, + DRM_FORMAT_YUV444, +}; + +static const struct soc_info jz4725b_soc_info = { + .formats = jz4725b_ipu_formats, + .num_formats = ARRAY_SIZE(jz4725b_ipu_formats), + .has_bicubic = false, + .set_coefs = jz4725b_set_coefs, +}; + +static const u32 jz4760_ipu_formats[] = { + DRM_FORMAT_XRGB1555, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_YUV411, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV422, + DRM_FORMAT_YUV444, + DRM_FORMAT_XYUV8888, +}; + +static const struct soc_info jz4760_soc_info = { + .formats = jz4760_ipu_formats, + .num_formats = ARRAY_SIZE(jz4760_ipu_formats), + .has_bicubic = true, + .set_coefs = jz4760_set_coefs, +}; + +static const struct of_device_id ingenic_ipu_of_match[] = { + { .compatible = "ingenic,jz4725b-ipu", .data = &jz4725b_soc_info }, + { .compatible = "ingenic,jz4760-ipu", .data = &jz4760_soc_info }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ingenic_ipu_of_match); + +static struct platform_driver ingenic_ipu_driver = { + .driver = { + .name = "ingenic-ipu", + .of_match_table = ingenic_ipu_of_match, + }, + .probe = ingenic_ipu_probe, + .remove = ingenic_ipu_remove, +}; + +struct platform_driver *ingenic_ipu_driver_ptr = &ingenic_ipu_driver; diff --git a/drivers/gpu/drm/ingenic/ingenic-ipu.h b/drivers/gpu/drm/ingenic/ingenic-ipu.h new file mode 100644 index 000000000000..eab6fab8c7f6 --- /dev/null +++ b/drivers/gpu/drm/ingenic/ingenic-ipu.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// +// Ingenic JZ47xx IPU - Register definitions and private API +// +// Copyright (C) 2020, Paul Cercueil <paul@crapouillou.net> + +#ifndef DRIVERS_GPU_DRM_INGENIC_INGENIC_IPU_H +#define DRIVERS_GPU_DRM_INGENIC_INGENIC_IPU_H + +#include <linux/bitops.h> + +#define JZ_REG_IPU_CTRL 0x00 +#define JZ_REG_IPU_STATUS 0x04 +#define JZ_REG_IPU_D_FMT 0x08 +#define JZ_REG_IPU_Y_ADDR 0x0c +#define JZ_REG_IPU_U_ADDR 0x10 +#define JZ_REG_IPU_V_ADDR 0x14 +#define JZ_REG_IPU_IN_GS 0x18 +#define JZ_REG_IPU_Y_STRIDE 0x1c +#define JZ_REG_IPU_UV_STRIDE 0x20 +#define JZ_REG_IPU_OUT_ADDR 0x24 +#define JZ_REG_IPU_OUT_GS 0x28 +#define JZ_REG_IPU_OUT_STRIDE 0x2c +#define JZ_REG_IPU_RSZ_COEF_INDEX 0x30 +#define JZ_REG_IPU_CSC_C0_COEF 0x34 +#define JZ_REG_IPU_CSC_C1_COEF 0x38 +#define JZ_REG_IPU_CSC_C2_COEF 0x3c +#define JZ_REG_IPU_CSC_C3_COEF 0x40 +#define JZ_REG_IPU_CSC_C4_COEF 0x44 +#define JZ_REG_IPU_HRSZ_COEF_LUT 0x48 +#define JZ_REG_IPU_VRSZ_COEF_LUT 0x4c +#define JZ_REG_IPU_CSC_OFFSET 0x50 +#define JZ_REG_IPU_Y_PHY_T_ADDR 0x54 +#define JZ_REG_IPU_U_PHY_T_ADDR 0x58 +#define JZ_REG_IPU_V_PHY_T_ADDR 0x5c +#define JZ_REG_IPU_OUT_PHY_T_ADDR 0x60 + +#define JZ_IPU_CTRL_ADDR_SEL BIT(20) +#define JZ_IPU_CTRL_ZOOM_SEL BIT(18) +#define JZ_IPU_CTRL_DFIX_SEL BIT(17) +#define JZ_IPU_CTRL_LCDC_SEL BIT(11) +#define JZ_IPU_CTRL_SPKG_SEL BIT(10) +#define JZ_IPU_CTRL_VSCALE BIT(9) +#define JZ_IPU_CTRL_HSCALE BIT(8) +#define JZ_IPU_CTRL_STOP BIT(7) +#define JZ_IPU_CTRL_RST BIT(6) +#define JZ_IPU_CTRL_FM_IRQ_EN BIT(5) +#define JZ_IPU_CTRL_CSC_EN BIT(4) +#define JZ_IPU_CTRL_VRSZ_EN BIT(3) +#define JZ_IPU_CTRL_HRSZ_EN BIT(2) +#define JZ_IPU_CTRL_RUN BIT(1) +#define JZ_IPU_CTRL_CHIP_EN BIT(0) + +#define JZ_IPU_STATUS_OUT_END BIT(0) + +#define JZ_IPU_IN_GS_H_LSB 0x0 +#define JZ_IPU_IN_GS_W_LSB 0x10 +#define JZ_IPU_OUT_GS_H_LSB 0x0 +#define JZ_IPU_OUT_GS_W_LSB 0x10 + +#define JZ_IPU_Y_STRIDE_Y_LSB 0 +#define JZ_IPU_UV_STRIDE_U_LSB 16 +#define JZ_IPU_UV_STRIDE_V_LSB 0 + +#define JZ_IPU_D_FMT_IN_FMT_LSB 0 +#define JZ_IPU_D_FMT_IN_FMT_RGB555 (0x0 << JZ_IPU_D_FMT_IN_FMT_LSB) +#define JZ_IPU_D_FMT_IN_FMT_YUV420 (0x0 << JZ_IPU_D_FMT_IN_FMT_LSB) +#define JZ_IPU_D_FMT_IN_FMT_YUV422 (0x1 << JZ_IPU_D_FMT_IN_FMT_LSB) +#define JZ_IPU_D_FMT_IN_FMT_RGB888 (0x2 << JZ_IPU_D_FMT_IN_FMT_LSB) +#define JZ_IPU_D_FMT_IN_FMT_YUV444 (0x2 << JZ_IPU_D_FMT_IN_FMT_LSB) +#define JZ_IPU_D_FMT_IN_FMT_RGB565 (0x3 << JZ_IPU_D_FMT_IN_FMT_LSB) + +#define JZ_IPU_D_FMT_YUV_FMT_LSB 2 +#define JZ_IPU_D_FMT_YUV_Y1UY0V (0x0 << JZ_IPU_D_FMT_YUV_FMT_LSB) +#define JZ_IPU_D_FMT_YUV_Y1VY0U (0x1 << JZ_IPU_D_FMT_YUV_FMT_LSB) +#define JZ_IPU_D_FMT_YUV_UY1VY0 (0x2 << JZ_IPU_D_FMT_YUV_FMT_LSB) +#define JZ_IPU_D_FMT_YUV_VY1UY0 (0x3 << JZ_IPU_D_FMT_YUV_FMT_LSB) +#define JZ_IPU_D_FMT_IN_FMT_YUV411 (0x3 << JZ_IPU_D_FMT_IN_FMT_LSB) + +#define JZ_IPU_D_FMT_OUT_FMT_LSB 19 +#define JZ_IPU_D_FMT_OUT_FMT_RGB555 (0x0 << JZ_IPU_D_FMT_OUT_FMT_LSB) +#define JZ_IPU_D_FMT_OUT_FMT_RGB565 (0x1 << JZ_IPU_D_FMT_OUT_FMT_LSB) +#define JZ_IPU_D_FMT_OUT_FMT_RGB888 (0x2 << JZ_IPU_D_FMT_OUT_FMT_LSB) +#define JZ_IPU_D_FMT_OUT_FMT_YUV422 (0x3 << JZ_IPU_D_FMT_OUT_FMT_LSB) +#define JZ_IPU_D_FMT_OUT_FMT_RGBAAA (0x4 << JZ_IPU_D_FMT_OUT_FMT_LSB) + +#define JZ_IPU_D_FMT_RGB_OUT_OFT_LSB 22 +#define JZ_IPU_D_FMT_RGB_OUT_OFT_RGB (0x0 << JZ_IPU_D_FMT_RGB_OUT_OFT_LSB) +#define JZ_IPU_D_FMT_RGB_OUT_OFT_RBG (0x1 << JZ_IPU_D_FMT_RGB_OUT_OFT_LSB) +#define JZ_IPU_D_FMT_RGB_OUT_OFT_GBR (0x2 << JZ_IPU_D_FMT_RGB_OUT_OFT_LSB) +#define JZ_IPU_D_FMT_RGB_OUT_OFT_GRB (0x3 << JZ_IPU_D_FMT_RGB_OUT_OFT_LSB) +#define JZ_IPU_D_FMT_RGB_OUT_OFT_BRG (0x4 << JZ_IPU_D_FMT_RGB_OUT_OFT_LSB) +#define JZ_IPU_D_FMT_RGB_OUT_OFT_BGR (0x5 << JZ_IPU_D_FMT_RGB_OUT_OFT_LSB) + +#define JZ4725B_IPU_RSZ_LUT_COEF_LSB 2 +#define JZ4725B_IPU_RSZ_LUT_COEF_MASK 0x7ff +#define JZ4725B_IPU_RSZ_LUT_IN_EN BIT(1) +#define JZ4725B_IPU_RSZ_LUT_OUT_EN BIT(0) + +#define JZ4760_IPU_RSZ_COEF20_LSB 6 +#define JZ4760_IPU_RSZ_COEF31_LSB 17 +#define JZ4760_IPU_RSZ_COEF_MASK 0x7ff +#define JZ4760_IPU_RSZ_OFFSET_LSB 1 +#define JZ4760_IPU_RSZ_OFFSET_MASK 0x1f + +#define JZ_IPU_CSC_OFFSET_CHROMA_LSB 16 +#define JZ_IPU_CSC_OFFSET_LUMA_LSB 16 + +#endif /* DRIVERS_GPU_DRM_INGENIC_INGENIC_IPU_H */ diff --git a/drivers/gpu/drm/lima/lima_drv.c b/drivers/gpu/drm/lima/lima_drv.c index a831565af813..ab460121fd52 100644 --- a/drivers/gpu/drm/lima/lima_drv.c +++ b/drivers/gpu/drm/lima/lima_drv.c @@ -19,6 +19,7 @@ int lima_sched_timeout_ms; uint lima_heap_init_nr_pages = 8; uint lima_max_error_tasks; +uint lima_job_hang_limit; MODULE_PARM_DESC(sched_timeout_ms, "task run timeout in ms"); module_param_named(sched_timeout_ms, lima_sched_timeout_ms, int, 0444); @@ -29,6 +30,9 @@ module_param_named(heap_init_nr_pages, lima_heap_init_nr_pages, uint, 0444); MODULE_PARM_DESC(max_error_tasks, "max number of error tasks to save"); module_param_named(max_error_tasks, lima_max_error_tasks, uint, 0644); +MODULE_PARM_DESC(job_hang_limit, "number of times to allow a job to hang before dropping it (default 0)"); +module_param_named(job_hang_limit, lima_job_hang_limit, uint, 0444); + static int lima_ioctl_get_param(struct drm_device *dev, void *data, struct drm_file *file) { struct drm_lima_get_param *args = data; diff --git a/drivers/gpu/drm/lima/lima_drv.h b/drivers/gpu/drm/lima/lima_drv.h index fdbd4077c768..c738d288547b 100644 --- a/drivers/gpu/drm/lima/lima_drv.h +++ b/drivers/gpu/drm/lima/lima_drv.h @@ -11,6 +11,7 @@ extern int lima_sched_timeout_ms; extern uint lima_heap_init_nr_pages; extern uint lima_max_error_tasks; +extern uint lima_job_hang_limit; struct lima_vm; struct lima_bo; diff --git a/drivers/gpu/drm/lima/lima_sched.c b/drivers/gpu/drm/lima/lima_sched.c index 64ced6d0e6cf..dc6df9e9a40d 100644 --- a/drivers/gpu/drm/lima/lima_sched.c +++ b/drivers/gpu/drm/lima/lima_sched.c @@ -503,8 +503,9 @@ int lima_sched_pipe_init(struct lima_sched_pipe *pipe, const char *name) INIT_WORK(&pipe->recover_work, lima_sched_recover_work); - return drm_sched_init(&pipe->base, &lima_sched_ops, 1, 0, - msecs_to_jiffies(timeout), name); + return drm_sched_init(&pipe->base, &lima_sched_ops, 1, + lima_job_hang_limit, msecs_to_jiffies(timeout), + name); } void lima_sched_pipe_fini(struct lima_sched_pipe *pipe) diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c index e56e47aa707b..f6adc5b147c9 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c @@ -113,19 +113,15 @@ static void mtk_drm_crtc_reset(struct drm_crtc *crtc) { struct mtk_crtc_state *state; - if (crtc->state) { + if (crtc->state) __drm_atomic_helper_crtc_destroy_state(crtc->state); - state = to_mtk_crtc_state(crtc->state); - memset(state, 0, sizeof(*state)); - } else { - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (!state) - return; - crtc->state = &state->base; - } + kfree(to_mtk_crtc_state(crtc->state)); + crtc->state = NULL; - state->base.crtc = crtc; + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_crtc_reset(crtc, &state->base); } static struct drm_crtc_state *mtk_drm_crtc_duplicate_state(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c index e66b6271ff58..2854272dc2d9 100644 --- a/drivers/gpu/drm/meson/meson_crtc.c +++ b/drivers/gpu/drm/meson/meson_crtc.c @@ -291,6 +291,10 @@ static void meson_crtc_enable_vd1(struct meson_drm *priv) VPP_VD1_PREBLEND | VPP_VD1_POSTBLEND | VPP_COLOR_MNG_ENABLE, priv->io_base + _REG(VPP_MISC)); + + writel_bits_relaxed(VIU_CTRL0_AFBC_TO_VD1, + priv->viu.vd1_afbc ? VIU_CTRL0_AFBC_TO_VD1 : 0, + priv->io_base + _REG(VIU_MISC_CTRL0)); } static void meson_g12a_crtc_enable_vd1(struct meson_drm *priv) @@ -300,6 +304,10 @@ static void meson_g12a_crtc_enable_vd1(struct meson_drm *priv) VD_BLEND_POSTBLD_SRC_VD1 | VD_BLEND_POSTBLD_PREMULT_EN, priv->io_base + _REG(VD1_BLEND_SRC_CTRL)); + + writel_relaxed(priv->viu.vd1_afbc ? + (VD1_AXI_SEL_AFBC | AFBC_VD1_SEL) : 0, + priv->io_base + _REG(VD1_AFBCD0_MISC_CTRL)); } void meson_crtc_irq(struct meson_drm *priv) @@ -383,36 +391,86 @@ void meson_crtc_irq(struct meson_drm *priv) /* Update the VD1 registers */ if (priv->viu.vd1_enabled && priv->viu.vd1_commit) { - switch (priv->viu.vd1_planes) { - case 3: - meson_canvas_config(priv->canvas, - priv->canvas_id_vd1_2, - priv->viu.vd1_addr2, - priv->viu.vd1_stride2, - priv->viu.vd1_height2, - MESON_CANVAS_WRAP_NONE, - MESON_CANVAS_BLKMODE_LINEAR, - MESON_CANVAS_ENDIAN_SWAP64); - /* fallthrough */ - case 2: - meson_canvas_config(priv->canvas, - priv->canvas_id_vd1_1, - priv->viu.vd1_addr1, - priv->viu.vd1_stride1, - priv->viu.vd1_height1, - MESON_CANVAS_WRAP_NONE, - MESON_CANVAS_BLKMODE_LINEAR, - MESON_CANVAS_ENDIAN_SWAP64); - /* fallthrough */ - case 1: - meson_canvas_config(priv->canvas, - priv->canvas_id_vd1_0, - priv->viu.vd1_addr0, - priv->viu.vd1_stride0, - priv->viu.vd1_height0, - MESON_CANVAS_WRAP_NONE, - MESON_CANVAS_BLKMODE_LINEAR, - MESON_CANVAS_ENDIAN_SWAP64); + if (priv->viu.vd1_afbc) { + writel_relaxed(priv->viu.vd1_afbc_head_addr, + priv->io_base + + _REG(AFBC_HEAD_BADDR)); + writel_relaxed(priv->viu.vd1_afbc_body_addr, + priv->io_base + + _REG(AFBC_BODY_BADDR)); + writel_relaxed(priv->viu.vd1_afbc_en, + priv->io_base + + _REG(AFBC_ENABLE)); + writel_relaxed(priv->viu.vd1_afbc_mode, + priv->io_base + + _REG(AFBC_MODE)); + writel_relaxed(priv->viu.vd1_afbc_size_in, + priv->io_base + + _REG(AFBC_SIZE_IN)); + writel_relaxed(priv->viu.vd1_afbc_dec_def_color, + priv->io_base + + _REG(AFBC_DEC_DEF_COLOR)); + writel_relaxed(priv->viu.vd1_afbc_conv_ctrl, + priv->io_base + + _REG(AFBC_CONV_CTRL)); + writel_relaxed(priv->viu.vd1_afbc_size_out, + priv->io_base + + _REG(AFBC_SIZE_OUT)); + writel_relaxed(priv->viu.vd1_afbc_vd_cfmt_ctrl, + priv->io_base + + _REG(AFBC_VD_CFMT_CTRL)); + writel_relaxed(priv->viu.vd1_afbc_vd_cfmt_w, + priv->io_base + + _REG(AFBC_VD_CFMT_W)); + writel_relaxed(priv->viu.vd1_afbc_mif_hor_scope, + priv->io_base + + _REG(AFBC_MIF_HOR_SCOPE)); + writel_relaxed(priv->viu.vd1_afbc_mif_ver_scope, + priv->io_base + + _REG(AFBC_MIF_VER_SCOPE)); + writel_relaxed(priv->viu.vd1_afbc_pixel_hor_scope, + priv->io_base+ + _REG(AFBC_PIXEL_HOR_SCOPE)); + writel_relaxed(priv->viu.vd1_afbc_pixel_ver_scope, + priv->io_base + + _REG(AFBC_PIXEL_VER_SCOPE)); + writel_relaxed(priv->viu.vd1_afbc_vd_cfmt_h, + priv->io_base + + _REG(AFBC_VD_CFMT_H)); + } else { + switch (priv->viu.vd1_planes) { + case 3: + meson_canvas_config(priv->canvas, + priv->canvas_id_vd1_2, + priv->viu.vd1_addr2, + priv->viu.vd1_stride2, + priv->viu.vd1_height2, + MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + fallthrough; + case 2: + meson_canvas_config(priv->canvas, + priv->canvas_id_vd1_1, + priv->viu.vd1_addr1, + priv->viu.vd1_stride1, + priv->viu.vd1_height1, + MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + fallthrough; + case 1: + meson_canvas_config(priv->canvas, + priv->canvas_id_vd1_0, + priv->viu.vd1_addr0, + priv->viu.vd1_stride0, + priv->viu.vd1_height0, + MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + } + + writel_relaxed(0, priv->io_base + _REG(AFBC_ENABLE)); } writel_relaxed(priv->viu.vd1_if0_gen_reg, diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h index 5b23704a80d6..177dac3ca3be 100644 --- a/drivers/gpu/drm/meson/meson_drv.h +++ b/drivers/gpu/drm/meson/meson_drv.h @@ -86,6 +86,7 @@ struct meson_drm { bool vd1_enabled; bool vd1_commit; + bool vd1_afbc; unsigned int vd1_planes; uint32_t vd1_if0_gen_reg; uint32_t vd1_if0_luma_x0; @@ -111,6 +112,21 @@ struct meson_drm { uint32_t vd1_height0; uint32_t vd1_height1; uint32_t vd1_height2; + uint32_t vd1_afbc_mode; + uint32_t vd1_afbc_en; + uint32_t vd1_afbc_head_addr; + uint32_t vd1_afbc_body_addr; + uint32_t vd1_afbc_conv_ctrl; + uint32_t vd1_afbc_dec_def_color; + uint32_t vd1_afbc_vd_cfmt_ctrl; + uint32_t vd1_afbc_vd_cfmt_w; + uint32_t vd1_afbc_vd_cfmt_h; + uint32_t vd1_afbc_mif_hor_scope; + uint32_t vd1_afbc_mif_ver_scope; + uint32_t vd1_afbc_size_out; + uint32_t vd1_afbc_pixel_hor_scope; + uint32_t vd1_afbc_pixel_ver_scope; + uint32_t vd1_afbc_size_in; uint32_t vpp_pic_in_height; uint32_t vpp_postblend_vd1_h_start_end; uint32_t vpp_postblend_vd1_v_start_end; diff --git a/drivers/gpu/drm/meson/meson_overlay.c b/drivers/gpu/drm/meson/meson_overlay.c index 2468b0212d52..a8bcc70644df 100644 --- a/drivers/gpu/drm/meson/meson_overlay.c +++ b/drivers/gpu/drm/meson/meson_overlay.c @@ -58,7 +58,8 @@ /* VPP_POSTBLEND_VD1_H_START_END */ #define VD_H_END(value) FIELD_PREP(GENMASK(11, 0), value) -#define VD_H_START(value) FIELD_PREP(GENMASK(27, 16), value) +#define VD_H_START(value) FIELD_PREP(GENMASK(27, 16), \ + ((value) & GENMASK(13, 0))) /* VPP_POSTBLEND_VD1_V_START_END */ #define VD_V_END(value) FIELD_PREP(GENMASK(11, 0), value) @@ -76,6 +77,85 @@ #define VD_REGION24_START(value) FIELD_PREP(GENMASK(11, 0), value) #define VD_REGION13_END(value) FIELD_PREP(GENMASK(27, 16), value) +/* AFBC_ENABLE */ +#define AFBC_DEC_ENABLE BIT(8) +#define AFBC_FRM_START BIT(0) + +/* AFBC_MODE */ +#define AFBC_HORZ_SKIP_UV(value) FIELD_PREP(GENMASK(1, 0), value) +#define AFBC_VERT_SKIP_UV(value) FIELD_PREP(GENMASK(3, 2), value) +#define AFBC_HORZ_SKIP_Y(value) FIELD_PREP(GENMASK(5, 4), value) +#define AFBC_VERT_SKIP_Y(value) FIELD_PREP(GENMASK(7, 6), value) +#define AFBC_COMPBITS_YUV(value) FIELD_PREP(GENMASK(13, 8), value) +#define AFBC_COMPBITS_8BIT 0 +#define AFBC_COMPBITS_10BIT (2 | (2 << 2) | (2 << 4)) +#define AFBC_BURST_LEN(value) FIELD_PREP(GENMASK(15, 14), value) +#define AFBC_HOLD_LINE_NUM(value) FIELD_PREP(GENMASK(22, 16), value) +#define AFBC_MIF_URGENT(value) FIELD_PREP(GENMASK(25, 24), value) +#define AFBC_REV_MODE(value) FIELD_PREP(GENMASK(27, 26), value) +#define AFBC_BLK_MEM_MODE BIT(28) +#define AFBC_SCATTER_MODE BIT(29) +#define AFBC_SOFT_RESET BIT(31) + +/* AFBC_SIZE_IN */ +#define AFBC_HSIZE_IN(value) FIELD_PREP(GENMASK(28, 16), value) +#define AFBC_VSIZE_IN(value) FIELD_PREP(GENMASK(12, 0), value) + +/* AFBC_DEC_DEF_COLOR */ +#define AFBC_DEF_COLOR_Y(value) FIELD_PREP(GENMASK(29, 20), value) +#define AFBC_DEF_COLOR_U(value) FIELD_PREP(GENMASK(19, 10), value) +#define AFBC_DEF_COLOR_V(value) FIELD_PREP(GENMASK(9, 0), value) + +/* AFBC_CONV_CTRL */ +#define AFBC_CONV_LBUF_LEN(value) FIELD_PREP(GENMASK(11, 0), value) + +/* AFBC_LBUF_DEPTH */ +#define AFBC_DEC_LBUF_DEPTH(value) FIELD_PREP(GENMASK(27, 16), value) +#define AFBC_MIF_LBUF_DEPTH(value) FIELD_PREP(GENMASK(11, 0), value) + +/* AFBC_OUT_XSCOPE/AFBC_SIZE_OUT */ +#define AFBC_HSIZE_OUT(value) FIELD_PREP(GENMASK(28, 16), value) +#define AFBC_VSIZE_OUT(value) FIELD_PREP(GENMASK(12, 0), value) +#define AFBC_OUT_HORZ_BGN(value) FIELD_PREP(GENMASK(28, 16), value) +#define AFBC_OUT_HORZ_END(value) FIELD_PREP(GENMASK(12, 0), value) + +/* AFBC_OUT_YSCOPE */ +#define AFBC_OUT_VERT_BGN(value) FIELD_PREP(GENMASK(28, 16), value) +#define AFBC_OUT_VERT_END(value) FIELD_PREP(GENMASK(12, 0), value) + +/* AFBC_VD_CFMT_CTRL */ +#define AFBC_HORZ_RPT_PIXEL0 BIT(23) +#define AFBC_HORZ_Y_C_RATIO(value) FIELD_PREP(GENMASK(22, 21), value) +#define AFBC_HORZ_FMT_EN BIT(20) +#define AFBC_VERT_RPT_LINE0 BIT(16) +#define AFBC_VERT_INITIAL_PHASE(value) FIELD_PREP(GENMASK(11, 8), value) +#define AFBC_VERT_PHASE_STEP(value) FIELD_PREP(GENMASK(7, 1), value) +#define AFBC_VERT_FMT_EN BIT(0) + +/* AFBC_VD_CFMT_W */ +#define AFBC_VD_V_WIDTH(value) FIELD_PREP(GENMASK(11, 0), value) +#define AFBC_VD_H_WIDTH(value) FIELD_PREP(GENMASK(27, 16), value) + +/* AFBC_MIF_HOR_SCOPE */ +#define AFBC_MIF_BLK_BGN_H(value) FIELD_PREP(GENMASK(25, 16), value) +#define AFBC_MIF_BLK_END_H(value) FIELD_PREP(GENMASK(9, 0), value) + +/* AFBC_MIF_VER_SCOPE */ +#define AFBC_MIF_BLK_BGN_V(value) FIELD_PREP(GENMASK(27, 16), value) +#define AFBC_MIF_BLK_END_V(value) FIELD_PREP(GENMASK(11, 0), value) + +/* AFBC_PIXEL_HOR_SCOPE */ +#define AFBC_DEC_PIXEL_BGN_H(value) FIELD_PREP(GENMASK(28, 16), \ + ((value) & GENMASK(12, 0))) +#define AFBC_DEC_PIXEL_END_H(value) FIELD_PREP(GENMASK(12, 0), value) + +/* AFBC_PIXEL_VER_SCOPE */ +#define AFBC_DEC_PIXEL_BGN_V(value) FIELD_PREP(GENMASK(28, 16), value) +#define AFBC_DEC_PIXEL_END_V(value) FIELD_PREP(GENMASK(12, 0), value) + +/* AFBC_VD_CFMT_H */ +#define AFBC_VD_HEIGHT(value) FIELD_PREP(GENMASK(12, 0), value) + struct meson_overlay { struct drm_plane base; struct meson_drm *priv; @@ -157,6 +237,9 @@ static void meson_overlay_setup_scaler_params(struct meson_drm *priv, unsigned int ratio_x, ratio_y; int temp_height, temp_width; unsigned int w_in, h_in; + int afbc_left, afbc_right; + int afbc_top_src, afbc_bottom_src; + int afbc_top, afbc_bottom; int temp, start, end; if (!crtc_state) { @@ -169,7 +252,7 @@ static void meson_overlay_setup_scaler_params(struct meson_drm *priv, w_in = fixed16_to_int(state->src_w); h_in = fixed16_to_int(state->src_h); - crop_top = fixed16_to_int(state->src_x); + crop_top = fixed16_to_int(state->src_y); crop_left = fixed16_to_int(state->src_x); video_top = state->crtc_y; @@ -243,6 +326,14 @@ static void meson_overlay_setup_scaler_params(struct meson_drm *priv, DRM_DEBUG("vsc startp %d endp %d start_lines %d end_lines %d\n", vsc_startp, vsc_endp, vd_start_lines, vd_end_lines); + afbc_top = round_down(vd_start_lines, 4); + afbc_bottom = round_up(vd_end_lines + 1, 4); + afbc_top_src = 0; + afbc_bottom_src = round_up(h_in + 1, 4); + + DRM_DEBUG("afbc top %d (src %d) bottom %d (src %d)\n", + afbc_top, afbc_top_src, afbc_bottom, afbc_bottom_src); + /* Horizontal */ start = video_left + video_width / 2 - ((w_in << 17) / ratio_x); @@ -278,6 +369,16 @@ static void meson_overlay_setup_scaler_params(struct meson_drm *priv, DRM_DEBUG("hsc startp %d endp %d start_lines %d end_lines %d\n", hsc_startp, hsc_endp, hd_start_lines, hd_end_lines); + if (hd_start_lines > 0 || (hd_end_lines < w_in)) { + afbc_left = 0; + afbc_right = round_up(w_in, 32); + } else { + afbc_left = round_down(hd_start_lines, 32); + afbc_right = round_up(hd_end_lines + 1, 32); + } + + DRM_DEBUG("afbc left %d right %d\n", afbc_left, afbc_right); + priv->viu.vpp_vsc_start_phase_step = ratio_y << 6; priv->viu.vpp_vsc_ini_phase = vphase << 8; @@ -293,6 +394,35 @@ static void meson_overlay_setup_scaler_params(struct meson_drm *priv, VD_H_WIDTH(hd_end_lines - hd_start_lines + 1) | VD_V_WIDTH(hd_end_lines/2 - hd_start_lines/2 + 1); + priv->viu.vd1_afbc_vd_cfmt_w = + AFBC_VD_H_WIDTH(afbc_right - afbc_left) | + AFBC_VD_V_WIDTH(afbc_right / 2 - afbc_left / 2); + + priv->viu.vd1_afbc_vd_cfmt_h = + AFBC_VD_HEIGHT((afbc_bottom - afbc_top) / 2); + + priv->viu.vd1_afbc_mif_hor_scope = AFBC_MIF_BLK_BGN_H(afbc_left / 32) | + AFBC_MIF_BLK_END_H((afbc_right / 32) - 1); + + priv->viu.vd1_afbc_mif_ver_scope = AFBC_MIF_BLK_BGN_V(afbc_top / 4) | + AFBC_MIF_BLK_END_H((afbc_bottom / 4) - 1); + + priv->viu.vd1_afbc_size_out = + AFBC_HSIZE_OUT(afbc_right - afbc_left) | + AFBC_VSIZE_OUT(afbc_bottom - afbc_top); + + priv->viu.vd1_afbc_pixel_hor_scope = + AFBC_DEC_PIXEL_BGN_H(hd_start_lines - afbc_left) | + AFBC_DEC_PIXEL_END_H(hd_end_lines - afbc_left); + + priv->viu.vd1_afbc_pixel_ver_scope = + AFBC_DEC_PIXEL_BGN_V(vd_start_lines - afbc_top) | + AFBC_DEC_PIXEL_END_V(vd_end_lines - afbc_top); + + priv->viu.vd1_afbc_size_in = + AFBC_HSIZE_IN(afbc_right - afbc_left) | + AFBC_VSIZE_IN(afbc_bottom_src - afbc_top_src); + priv->viu.vd1_if0_luma_y0 = VD_Y_START(vd_start_lines) | VD_Y_END(vd_end_lines); @@ -350,11 +480,65 @@ static void meson_overlay_atomic_update(struct drm_plane *plane, spin_lock_irqsave(&priv->drm->event_lock, flags); - priv->viu.vd1_if0_gen_reg = VD_URGENT_CHROMA | - VD_URGENT_LUMA | - VD_HOLD_LINES(9) | - VD_CHRO_RPT_LASTL_CTRL | - VD_ENABLE; + if ((fb->modifier & DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) == + DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) { + priv->viu.vd1_afbc = true; + + priv->viu.vd1_afbc_mode = AFBC_MIF_URGENT(3) | + AFBC_HOLD_LINE_NUM(8) | + AFBC_BURST_LEN(2); + + if (fb->modifier & DRM_FORMAT_MOD_AMLOGIC_FBC(0, + AMLOGIC_FBC_OPTION_MEM_SAVING)) + priv->viu.vd1_afbc_mode |= AFBC_BLK_MEM_MODE; + + if ((fb->modifier & __fourcc_mod_amlogic_layout_mask) == + AMLOGIC_FBC_LAYOUT_SCATTER) + priv->viu.vd1_afbc_mode |= AFBC_SCATTER_MODE; + + priv->viu.vd1_afbc_en = 0x1600 | AFBC_DEC_ENABLE; + + priv->viu.vd1_afbc_conv_ctrl = AFBC_CONV_LBUF_LEN(256); + + priv->viu.vd1_afbc_dec_def_color = AFBC_DEF_COLOR_Y(1023); + + /* 420: horizontal / 2, vertical / 4 */ + priv->viu.vd1_afbc_vd_cfmt_ctrl = AFBC_HORZ_RPT_PIXEL0 | + AFBC_HORZ_Y_C_RATIO(1) | + AFBC_HORZ_FMT_EN | + AFBC_VERT_RPT_LINE0 | + AFBC_VERT_INITIAL_PHASE(12) | + AFBC_VERT_PHASE_STEP(8) | + AFBC_VERT_FMT_EN; + + switch (fb->format->format) { + /* AFBC Only formats */ + case DRM_FORMAT_YUV420_10BIT: + priv->viu.vd1_afbc_mode |= + AFBC_COMPBITS_YUV(AFBC_COMPBITS_10BIT); + priv->viu.vd1_afbc_dec_def_color |= + AFBC_DEF_COLOR_U(512) | + AFBC_DEF_COLOR_V(512); + break; + case DRM_FORMAT_YUV420_8BIT: + priv->viu.vd1_afbc_dec_def_color |= + AFBC_DEF_COLOR_U(128) | + AFBC_DEF_COLOR_V(128); + break; + } + + priv->viu.vd1_if0_gen_reg = 0; + priv->viu.vd1_if0_canvas0 = 0; + priv->viu.viu_vd1_fmt_ctrl = 0; + } else { + priv->viu.vd1_afbc = false; + + priv->viu.vd1_if0_gen_reg = VD_URGENT_CHROMA | + VD_URGENT_LUMA | + VD_HOLD_LINES(9) | + VD_CHRO_RPT_LASTL_CTRL | + VD_ENABLE; + } /* Setup scaler params */ meson_overlay_setup_scaler_params(priv, plane, interlace_mode); @@ -370,6 +554,7 @@ static void meson_overlay_atomic_update(struct drm_plane *plane, priv->viu.vd1_if0_gen_reg2 = 0; priv->viu.viu_vd1_fmt_ctrl = 0; + /* None will match for AFBC Only formats */ switch (fb->format->format) { /* TOFIX DRM_FORMAT_RGB888 should be supported */ case DRM_FORMAT_YUYV: @@ -488,13 +673,42 @@ static void meson_overlay_atomic_update(struct drm_plane *plane, priv->viu.vd1_stride0 = fb->pitches[0]; priv->viu.vd1_height0 = drm_format_info_plane_height(fb->format, - fb->height, 0); + fb->height, 0); DRM_DEBUG("plane 0 addr 0x%x stride %d height %d\n", priv->viu.vd1_addr0, priv->viu.vd1_stride0, priv->viu.vd1_height0); } + if (priv->viu.vd1_afbc) { + if (priv->viu.vd1_afbc_mode & AFBC_SCATTER_MODE) { + /* + * In Scatter mode, the header contains the physical + * body content layout, thus the body content + * size isn't needed. + */ + priv->viu.vd1_afbc_head_addr = priv->viu.vd1_addr0 >> 4; + priv->viu.vd1_afbc_body_addr = 0; + } else { + /* Default mode is 4k per superblock */ + unsigned long block_size = 4096; + unsigned long body_size; + + /* 8bit mem saving mode is 3072bytes per superblock */ + if (priv->viu.vd1_afbc_mode & AFBC_BLK_MEM_MODE) + block_size = 3072; + + body_size = (ALIGN(priv->viu.vd1_stride0, 64) / 64) * + (ALIGN(priv->viu.vd1_height0, 32) / 32) * + block_size; + + priv->viu.vd1_afbc_body_addr = priv->viu.vd1_addr0 >> 4; + /* Header is after body content */ + priv->viu.vd1_afbc_head_addr = (priv->viu.vd1_addr0 + + body_size) >> 4; + } + } + priv->viu.vd1_enabled = true; spin_unlock_irqrestore(&priv->drm->event_lock, flags); @@ -531,6 +745,53 @@ static const struct drm_plane_helper_funcs meson_overlay_helper_funcs = { .prepare_fb = drm_gem_fb_prepare_fb, }; +static bool meson_overlay_format_mod_supported(struct drm_plane *plane, + u32 format, u64 modifier) +{ + if (modifier == DRM_FORMAT_MOD_LINEAR && + format != DRM_FORMAT_YUV420_8BIT && + format != DRM_FORMAT_YUV420_10BIT) + return true; + + if ((modifier & DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) == + DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) { + unsigned int layout = modifier & + DRM_FORMAT_MOD_AMLOGIC_FBC( + __fourcc_mod_amlogic_layout_mask, 0); + unsigned int options = + (modifier >> __fourcc_mod_amlogic_options_shift) & + __fourcc_mod_amlogic_options_mask; + + if (format != DRM_FORMAT_YUV420_8BIT && + format != DRM_FORMAT_YUV420_10BIT) { + DRM_DEBUG_KMS("%llx invalid format 0x%08x\n", + modifier, format); + return false; + } + + if (layout != AMLOGIC_FBC_LAYOUT_BASIC && + layout != AMLOGIC_FBC_LAYOUT_SCATTER) { + DRM_DEBUG_KMS("%llx invalid layout %x\n", + modifier, layout); + return false; + } + + if (options && + options != AMLOGIC_FBC_OPTION_MEM_SAVING) { + DRM_DEBUG_KMS("%llx invalid layout %x\n", + modifier, layout); + return false; + } + + return true; + } + + DRM_DEBUG_KMS("invalid modifier %llx for format 0x%08x\n", + modifier, format); + + return false; +} + static const struct drm_plane_funcs meson_overlay_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, @@ -538,6 +799,7 @@ static const struct drm_plane_funcs meson_overlay_funcs = { .reset = drm_atomic_helper_plane_reset, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .format_mod_supported = meson_overlay_format_mod_supported, }; static const uint32_t supported_drm_formats[] = { @@ -549,6 +811,19 @@ static const uint32_t supported_drm_formats[] = { DRM_FORMAT_YUV420, DRM_FORMAT_YUV411, DRM_FORMAT_YUV410, + DRM_FORMAT_YUV420_8BIT, /* Amlogic FBC Only */ + DRM_FORMAT_YUV420_10BIT, /* Amlogic FBC Only */ +}; + +static const uint64_t format_modifiers[] = { + DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_SCATTER, + AMLOGIC_FBC_OPTION_MEM_SAVING), + DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_BASIC, + AMLOGIC_FBC_OPTION_MEM_SAVING), + DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_SCATTER, 0), + DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_BASIC, 0), + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID, }; int meson_overlay_create(struct meson_drm *priv) @@ -570,7 +845,7 @@ int meson_overlay_create(struct meson_drm *priv) &meson_overlay_funcs, supported_drm_formats, ARRAY_SIZE(supported_drm_formats), - NULL, + format_modifiers, DRM_PLANE_TYPE_OVERLAY, "meson_overlay_plane"); drm_plane_helper_add(plane, &meson_overlay_helper_funcs); diff --git a/drivers/gpu/drm/meson/meson_registers.h b/drivers/gpu/drm/meson/meson_registers.h index 8ea00546cd4e..08631fdfe4b9 100644 --- a/drivers/gpu/drm/meson/meson_registers.h +++ b/drivers/gpu/drm/meson/meson_registers.h @@ -144,10 +144,15 @@ #define VIU_SW_RESET_OSD1 BIT(0) #define VIU_MISC_CTRL0 0x1a06 #define VIU_CTRL0_VD1_AFBC_MASK 0x170000 +#define VIU_CTRL0_AFBC_TO_VD1 BIT(20) #define VIU_MISC_CTRL1 0x1a07 #define MALI_AFBC_MISC GENMASK(15, 8) #define D2D3_INTF_LENGTH 0x1a08 #define D2D3_INTF_CTRL0 0x1a09 +#define VD1_AFBCD0_MISC_CTRL 0x1a0a +#define VD1_AXI_SEL_AFBC (1 << 12) +#define AFBC_VD1_SEL (1 << 10) +#define VD2_AFBCD1_MISC_CTRL 0x1a0b #define VIU_OSD1_CTRL_STAT 0x1a10 #define VIU_OSD1_OSD_BLK_ENABLE BIT(0) #define VIU_OSD1_OSD_MEM_MODE_LINEAR BIT(2) @@ -365,6 +370,23 @@ #define VIU_OSD1_OETF_LUT_ADDR_PORT 0x1add #define VIU_OSD1_OETF_LUT_DATA_PORT 0x1ade #define AFBC_ENABLE 0x1ae0 +#define AFBC_MODE 0x1ae1 +#define AFBC_SIZE_IN 0x1ae2 +#define AFBC_DEC_DEF_COLOR 0x1ae3 +#define AFBC_CONV_CTRL 0x1ae4 +#define AFBC_LBUF_DEPTH 0x1ae5 +#define AFBC_HEAD_BADDR 0x1ae6 +#define AFBC_BODY_BADDR 0x1ae7 +#define AFBC_SIZE_OUT 0x1ae8 +#define AFBC_OUT_YSCOPE 0x1ae9 +#define AFBC_STAT 0x1aea +#define AFBC_VD_CFMT_CTRL 0x1aeb +#define AFBC_VD_CFMT_W 0x1aec +#define AFBC_MIF_HOR_SCOPE 0x1aed +#define AFBC_MIF_VER_SCOPE 0x1aee +#define AFBC_PIXEL_HOR_SCOPE 0x1aef +#define AFBC_PIXEL_VER_SCOPE 0x1af0 +#define AFBC_VD_CFMT_H 0x1af1 /* vpp */ #define VPP_DUMMY_DATA 0x1d00 diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h index 270c2f9a6766..3817520bfefc 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.h +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -60,6 +60,12 @@ WREG8(MGAREG_SEQ_DATA, v); \ } while (0) \ +#define RREG_CRT(reg, v) \ + do { \ + WREG8(MGAREG_CRTC_INDEX, reg); \ + v = RREG8(MGAREG_CRTC_DATA); \ + } while (0) \ + #define WREG_CRT(reg, v) \ do { \ WREG8(MGAREG_CRTC_INDEX, reg); \ diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index f16bd278ab7e..e0d037a7413c 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -712,7 +712,7 @@ static int mga_g200er_set_plls(struct mga_device *mdev, long clock) return 0; } -static int mga_crtc_set_plls(struct mga_device *mdev, long clock) +static int mgag200_crtc_set_plls(struct mga_device *mdev, long clock) { u8 misc; @@ -745,9 +745,8 @@ static int mga_crtc_set_plls(struct mga_device *mdev, long clock) return 0; } -static void mga_g200wb_prepare(struct drm_crtc *crtc) +static void mgag200_g200wb_hold_bmc(struct mga_device *mdev) { - struct mga_device *mdev = to_mga_device(crtc->dev); u8 tmp; int iter_max; @@ -799,10 +798,9 @@ static void mga_g200wb_prepare(struct drm_crtc *crtc) } } -static void mga_g200wb_commit(struct drm_crtc *crtc) +static void mgag200_g200wb_release_bmc(struct mga_device *mdev) { u8 tmp; - struct mga_device *mdev = to_mga_device(crtc->dev); /* 1- The first step is to ensure that the vrsten and hrsten are set */ WREG8(MGAREG_CRTCEXT_INDEX, 1); @@ -988,7 +986,7 @@ static void mgag200_set_dac_regs(struct mga_device *mdev) static void mgag200_init_regs(struct mga_device *mdev) { - u8 crtcext3, crtcext4, misc; + u8 crtc11, crtcext3, crtcext4, misc; mgag200_set_pci_regs(mdev); mgag200_set_dac_regs(mdev); @@ -1012,6 +1010,12 @@ static void mgag200_init_regs(struct mga_device *mdev) WREG_ECRT(0x03, crtcext3); WREG_ECRT(0x04, crtcext4); + RREG_CRT(0x11, crtc11); + crtc11 &= ~(MGAREG_CRTC11_CRTCPROTECT | + MGAREG_CRTC11_VINTEN | + MGAREG_CRTC11_VINTCLR); + WREG_CRT(0x11, crtc11); + if (mdev->type == G200_ER) WREG_ECRT(0x24, 0x5); @@ -1104,8 +1108,6 @@ static void mgag200_set_mode_regs(struct mga_device *mdev, WREG_ECRT(0x05, crtcext5); WREG8(MGA_MISC_OUT, misc); - - mga_crtc_set_plls(mdev, mode->clock); } static u8 mgag200_get_bpp_shift(struct mga_device *mdev, @@ -1215,14 +1217,8 @@ static void mgag200_set_format_regs(struct mga_device *mdev, static void mgag200_g200er_reset_tagfifo(struct mga_device *mdev) { static uint32_t RESET_FLAG = 0x00200000; /* undocumented magic value */ - u8 seq1; u32 memctl; - /* screen off */ - RREG_SEQ(0x01, seq1); - seq1 |= MGAREG_SEQ1_SCROFF; - WREG_SEQ(0x01, seq1); - memctl = RREG32(MGAREG_MEMCTL); memctl |= RESET_FLAG; @@ -1232,11 +1228,6 @@ static void mgag200_g200er_reset_tagfifo(struct mga_device *mdev) memctl &= ~RESET_FLAG; WREG32(MGAREG_MEMCTL, memctl); - - /* screen on */ - RREG_SEQ(0x01, seq1); - seq1 &= ~MGAREG_SEQ1_SCROFF; - WREG_SEQ(0x01, seq1); } static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev, @@ -1289,107 +1280,59 @@ static void mgag200_g200ev_set_hiprilvl(struct mga_device *mdev) WREG_ECRT(0x06, 0x00); } -static void mga_crtc_dpms(struct drm_crtc *crtc, int mode) +static void mgag200_enable_display(struct mga_device *mdev) { - struct drm_device *dev = crtc->dev; - struct mga_device *mdev = to_mga_device(dev); - u8 seq1 = 0, crtcext1 = 0; + u8 seq0, seq1, crtcext1; - switch (mode) { - case DRM_MODE_DPMS_ON: - seq1 = 0; - crtcext1 = 0; - mga_crtc_load_lut(crtc); - break; - case DRM_MODE_DPMS_STANDBY: - seq1 = 0x20; - crtcext1 = 0x10; - break; - case DRM_MODE_DPMS_SUSPEND: - seq1 = 0x20; - crtcext1 = 0x20; - break; - case DRM_MODE_DPMS_OFF: - seq1 = 0x20; - crtcext1 = 0x30; - break; - } + RREG_SEQ(0x00, seq0); + seq0 |= MGAREG_SEQ0_SYNCRST | + MGAREG_SEQ0_ASYNCRST; + WREG_SEQ(0x00, seq0); - WREG8(MGAREG_SEQ_INDEX, 0x01); - seq1 |= RREG8(MGAREG_SEQ_DATA) & ~0x20; + /* + * TODO: replace busy waiting with vblank IRQ; put + * msleep(50) before changing SCROFF + */ mga_wait_vsync(mdev); mga_wait_busy(mdev); - WREG8(MGAREG_SEQ_DATA, seq1); - msleep(20); - WREG8(MGAREG_CRTCEXT_INDEX, 0x01); - crtcext1 |= RREG8(MGAREG_CRTCEXT_DATA) & ~0x30; - WREG8(MGAREG_CRTCEXT_DATA, crtcext1); -} - -/* - * This is called before a mode is programmed. A typical use might be to - * enable DPMS during the programming to avoid seeing intermediate stages, - * but that's not relevant to us - */ -static void mga_crtc_prepare(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct mga_device *mdev = to_mga_device(dev); - u8 tmp; - - /* mga_resume(crtc);*/ - - WREG8(MGAREG_CRTC_INDEX, 0x11); - tmp = RREG8(MGAREG_CRTC_DATA); - WREG_CRT(0x11, tmp | 0x80); - - if (mdev->type == G200_SE_A || mdev->type == G200_SE_B) { - WREG_SEQ(0, 1); - msleep(50); - WREG_SEQ(1, 0x20); - msleep(20); - } else { - WREG8(MGAREG_SEQ_INDEX, 0x1); - tmp = RREG8(MGAREG_SEQ_DATA); - /* start sync reset */ - WREG_SEQ(0, 1); - WREG_SEQ(1, tmp | 0x20); - } + RREG_SEQ(0x01, seq1); + seq1 &= ~MGAREG_SEQ1_SCROFF; + WREG_SEQ(0x01, seq1); - if (mdev->type == G200_WB || mdev->type == G200_EW3) - mga_g200wb_prepare(crtc); + msleep(20); - WREG_CRT(17, 0); + RREG_ECRT(0x01, crtcext1); + crtcext1 &= ~MGAREG_CRTCEXT1_VSYNCOFF; + crtcext1 &= ~MGAREG_CRTCEXT1_HSYNCOFF; + WREG_ECRT(0x01, crtcext1); } -/* - * This is called after a mode is programmed. It should reverse anything done - * by the prepare function - */ -static void mga_crtc_commit(struct drm_crtc *crtc) +static void mgag200_disable_display(struct mga_device *mdev) { - struct drm_device *dev = crtc->dev; - struct mga_device *mdev = to_mga_device(dev); - u8 tmp; + u8 seq0, seq1, crtcext1; - if (mdev->type == G200_WB || mdev->type == G200_EW3) - mga_g200wb_commit(crtc); + RREG_SEQ(0x00, seq0); + seq0 &= ~MGAREG_SEQ0_SYNCRST; + WREG_SEQ(0x00, seq0); - if (mdev->type == G200_SE_A || mdev->type == G200_SE_B) { - msleep(50); - WREG_SEQ(1, 0x0); - msleep(20); - WREG_SEQ(0, 0x3); - } else { - WREG8(MGAREG_SEQ_INDEX, 0x1); - tmp = RREG8(MGAREG_SEQ_DATA); + /* + * TODO: replace busy waiting with vblank IRQ; put + * msleep(50) before changing SCROFF + */ + mga_wait_vsync(mdev); + mga_wait_busy(mdev); - tmp &= ~0x20; - WREG_SEQ(0x1, tmp); - WREG_SEQ(0, 3); - } - mga_crtc_dpms(crtc, DRM_MODE_DPMS_ON); + RREG_SEQ(0x01, seq1); + seq1 |= MGAREG_SEQ1_SCROFF; + WREG_SEQ(0x01, seq1); + + msleep(20); + + RREG_ECRT(0x01, crtcext1); + crtcext1 |= MGAREG_CRTCEXT1_VSYNCOFF | + MGAREG_CRTCEXT1_HSYNCOFF; + WREG_ECRT(0x01, crtcext1); } /* @@ -1612,10 +1555,12 @@ mgag200_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe, .y2 = fb->height, }; - mga_crtc_prepare(crtc); + if (mdev->type == G200_WB || mdev->type == G200_EW3) + mgag200_g200wb_hold_bmc(mdev); mgag200_set_format_regs(mdev, fb); mgag200_set_mode_regs(mdev, adjusted_mode); + mgag200_crtc_set_plls(mdev, adjusted_mode->clock); if (mdev->type == G200_ER) mgag200_g200er_reset_tagfifo(mdev); @@ -1625,7 +1570,11 @@ mgag200_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe, else if (mdev->type == G200_EV) mgag200_g200ev_set_hiprilvl(mdev); - mga_crtc_commit(crtc); + if (mdev->type == G200_WB || mdev->type == G200_EW3) + mgag200_g200wb_release_bmc(mdev); + + mga_crtc_load_lut(crtc); + mgag200_enable_display(mdev); mgag200_handle_damage(mdev, fb, &fullscreen); } @@ -1634,8 +1583,9 @@ static void mgag200_simple_display_pipe_disable(struct drm_simple_display_pipe *pipe) { struct drm_crtc *crtc = &pipe->crtc; + struct mga_device *mdev = to_mga_device(crtc->dev); - mga_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + mgag200_disable_display(mdev); } static int diff --git a/drivers/gpu/drm/mgag200/mgag200_reg.h b/drivers/gpu/drm/mgag200/mgag200_reg.h index 29f7194faadc..c3b7bcad52ed 100644 --- a/drivers/gpu/drm/mgag200/mgag200_reg.h +++ b/drivers/gpu/drm/mgag200/mgag200_reg.h @@ -236,15 +236,26 @@ #define MGAREG_SEQ_INDEX 0x1fc4 #define MGAREG_SEQ_DATA 0x1fc5 +#define MGAREG_SEQ0_ASYNCRST BIT(0) +#define MGAREG_SEQ0_SYNCRST BIT(1) + #define MGAREG_SEQ1_SCROFF BIT(5) #define MGAREG_CRTC_INDEX 0x1fd4 #define MGAREG_CRTC_DATA 0x1fd5 + +#define MGAREG_CRTC11_VINTCLR BIT(4) +#define MGAREG_CRTC11_VINTEN BIT(5) +#define MGAREG_CRTC11_CRTCPROTECT BIT(7) + #define MGAREG_CRTCEXT_INDEX 0x1fde #define MGAREG_CRTCEXT_DATA 0x1fdf #define MGAREG_CRTCEXT0_OFFSET_MASK GENMASK(5, 4) +#define MGAREG_CRTCEXT1_VSYNCOFF BIT(5) +#define MGAREG_CRTCEXT1_HSYNCOFF BIT(4) + /* Cursor X and Y position */ #define MGA_CURPOSXL 0x3c0c #define MGA_CURPOSXH 0x3c0d diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c index e152016a6a7d..c39dad151bb6 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c @@ -1117,8 +1117,6 @@ static void mdp5_crtc_reset(struct drm_crtc *crtc) mdp5_crtc_destroy_state(crtc, crtc->state); __drm_atomic_helper_crtc_reset(crtc, &mdp5_cstate->base); - - drm_crtc_vblank_reset(crtc); } static const struct drm_crtc_funcs mdp5_crtc_funcs = { diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 47c7dce03da4..508764fccd27 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -69,6 +69,11 @@ static const uint32_t mxsfb_formats[] = { DRM_FORMAT_RGB565 }; +static const uint64_t mxsfb_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + static struct mxsfb_drm_private * drm_pipe_to_mxsfb_drm_private(struct drm_simple_display_pipe *pipe) { @@ -191,7 +196,7 @@ static struct drm_simple_display_pipe_funcs mxsfb_funcs = { .disable_vblank = mxsfb_pipe_disable_vblank, }; -static int mxsfb_load(struct drm_device *drm, unsigned long flags) +static int mxsfb_load(struct drm_device *drm) { struct platform_device *pdev = to_platform_device(drm->dev); struct mxsfb_drm_private *mxsfb; @@ -244,8 +249,8 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) } ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs, - mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL, - mxsfb->connector); + mxsfb_formats, ARRAY_SIZE(mxsfb_formats), + mxsfb_modifiers, mxsfb->connector); if (ret < 0) { dev_err(drm->dev, "Cannot setup simple display pipe\n"); goto err_vblank; @@ -398,7 +403,7 @@ static int mxsfb_probe(struct platform_device *pdev) if (IS_ERR(drm)) return PTR_ERR(drm); - ret = mxsfb_load(drm, 0); + ret = mxsfb_load(drm); if (ret) goto err_free; diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index 640738f3196c..4989627b7802 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -44,6 +44,9 @@ #include <subdev/bios/pll.h> #include <subdev/clk.h> +#include <nvif/event.h> +#include <nvif/cl0046.h> + static int nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb); @@ -756,6 +759,7 @@ static void nv_crtc_destroy(struct drm_crtc *crtc) nouveau_bo_unmap(nv_crtc->cursor.nvbo); nouveau_bo_unpin(nv_crtc->cursor.nvbo); nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); + nvif_notify_fini(&nv_crtc->vblank); kfree(nv_crtc); } @@ -845,7 +849,7 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, } nvbo = nouveau_gem_object(drm_fb->obj[0]); - nv_crtc->fb.offset = nvbo->bo.offset; + nv_crtc->fb.offset = nvbo->offset; if (nv_crtc->lut.depth != drm_fb->format->depth) { nv_crtc->lut.depth = drm_fb->format->depth; @@ -1013,7 +1017,7 @@ nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, nv04_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo); nouveau_bo_unmap(cursor); - nv_crtc->cursor.offset = nv_crtc->cursor.nvbo->bo.offset; + nv_crtc->cursor.offset = nv_crtc->cursor.nvbo->offset; nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset); nv_crtc->cursor.show(nv_crtc, true); out: @@ -1192,7 +1196,7 @@ nv04_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, /* Initialize a page flip struct */ *s = (struct nv04_page_flip_state) { { }, event, crtc, fb->format->cpp[0] * 8, fb->pitches[0], - new_bo->bo.offset }; + new_bo->offset }; /* Keep vblanks on during flip, for the target crtc of this flip */ drm_crtc_vblank_get(crtc); @@ -1297,9 +1301,19 @@ create_primary_plane(struct drm_device *dev) return primary; } +static int nv04_crtc_vblank_handler(struct nvif_notify *notify) +{ + struct nouveau_crtc *nv_crtc = + container_of(notify, struct nouveau_crtc, vblank); + + drm_crtc_handle_vblank(&nv_crtc->base); + return NVIF_NOTIFY_KEEP; +} + int nv04_crtc_create(struct drm_device *dev, int crtc_num) { + struct nouveau_display *disp = nouveau_display(dev); struct nouveau_crtc *nv_crtc; int ret; @@ -1337,5 +1351,14 @@ nv04_crtc_create(struct drm_device *dev, int crtc_num) nv04_cursor_init(nv_crtc); - return 0; + ret = nvif_notify_init(&disp->disp.object, nv04_crtc_vblank_handler, + false, NV04_DISP_NTFY_VBLANK, + &(struct nvif_notify_head_req_v0) { + .head = nv_crtc->index, + }, + sizeof(struct nvif_notify_head_req_v0), + sizeof(struct nvif_notify_head_rep_v0), + &nv_crtc->vblank); + + return ret; } diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.c b/drivers/gpu/drm/nouveau/dispnv04/disp.c index 0f4ebefed1fd..76be805fc488 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c @@ -152,7 +152,8 @@ nv04_display_init(struct drm_device *dev, bool resume, bool runtime) continue; if (nv_crtc->cursor.set_offset) - nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.offset); + nv_crtc->cursor.set_offset(nv_crtc, + nv_crtc->cursor.nvbo->offset); nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, nv_crtc->cursor_saved_y); } diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c index 6248fd1dbc6d..193ba9498f3d 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c +++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c @@ -152,7 +152,7 @@ nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, nvif_mask(dev, NV_PCRTC_ENGINE_CTRL + soff2, NV_CRTC_FSEL_OVERLAY, 0); nvif_wr32(dev, NV_PVIDEO_BASE(flip), 0); - nvif_wr32(dev, NV_PVIDEO_OFFSET_BUFF(flip), nvbo->bo.offset); + nvif_wr32(dev, NV_PVIDEO_OFFSET_BUFF(flip), nvbo->offset); nvif_wr32(dev, NV_PVIDEO_SIZE_IN(flip), src_h << 16 | src_w); nvif_wr32(dev, NV_PVIDEO_POINT_IN(flip), src_y << 16 | src_x); nvif_wr32(dev, NV_PVIDEO_DS_DX(flip), (src_w << 20) / crtc_w); @@ -174,7 +174,7 @@ nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, if (format & NV_PVIDEO_FORMAT_PLANAR) { nvif_wr32(dev, NV_PVIDEO_UVPLANE_BASE(flip), 0); nvif_wr32(dev, NV_PVIDEO_UVPLANE_OFFSET_BUFF(flip), - nvbo->bo.offset + fb->offsets[1]); + nvbo->offset + fb->offsets[1]); } nvif_wr32(dev, NV_PVIDEO_FORMAT(flip), format | fb->pitches[0]); nvif_wr32(dev, NV_PVIDEO_STOP, 0); @@ -399,7 +399,7 @@ nv04_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, for (i = 0; i < 2; i++) { nvif_wr32(dev, NV_PVIDEO_BUFF0_START_ADDRESS + 4 * i, - nvbo->bo.offset); + nvbo->offset); nvif_wr32(dev, NV_PVIDEO_BUFF0_PITCH_LENGTH + 4 * i, fb->pitches[0]); nvif_wr32(dev, NV_PVIDEO_BUFF0_OFFSET + 4 * i, 0); diff --git a/drivers/gpu/drm/nouveau/dispnv50/Kbuild b/drivers/gpu/drm/nouveau/dispnv50/Kbuild index e0c435eae664..6fdddb266fb1 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/Kbuild +++ b/drivers/gpu/drm/nouveau/dispnv50/Kbuild @@ -10,6 +10,10 @@ nouveau-y += dispnv50/core917d.o nouveau-y += dispnv50/corec37d.o nouveau-y += dispnv50/corec57d.o +nouveau-$(CONFIG_DEBUG_FS) += dispnv50/crc.o +nouveau-$(CONFIG_DEBUG_FS) += dispnv50/crc907d.o +nouveau-$(CONFIG_DEBUG_FS) += dispnv50/crcc37d.o + nouveau-y += dispnv50/dac507d.o nouveau-y += dispnv50/dac907d.o diff --git a/drivers/gpu/drm/nouveau/dispnv50/atom.h b/drivers/gpu/drm/nouveau/dispnv50/atom.h index 24f7700768da..3d82b3c67dec 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/atom.h +++ b/drivers/gpu/drm/nouveau/dispnv50/atom.h @@ -2,6 +2,9 @@ #define __NV50_KMS_ATOM_H__ #define nv50_atom(p) container_of((p), struct nv50_atom, state) #include <drm/drm_atomic.h> +#include "crc.h" + +struct nouveau_encoder; struct nv50_atom { struct drm_atomic_state state; @@ -18,6 +21,7 @@ struct nv50_head_atom { struct { u32 mask; + u32 owned; u32 olut; } wndw; @@ -114,9 +118,12 @@ struct nv50_head_atom { u8 nhsync:1; u8 nvsync:1; u8 depth:4; + u8 crc_raster:2; u8 bpc; } or; + struct nv50_crc_atom crc; + /* Currently only used for MST */ struct { int pbn; @@ -134,6 +141,7 @@ struct nv50_head_atom { bool ovly:1; bool dither:1; bool procamp:1; + bool crc:1; bool or:1; }; u16 mask; @@ -149,6 +157,19 @@ nv50_head_atom_get(struct drm_atomic_state *state, struct drm_crtc *crtc) return nv50_head_atom(statec); } +static inline struct drm_encoder * +nv50_head_atom_get_encoder(struct nv50_head_atom *atom) +{ + struct drm_encoder *encoder = NULL; + + /* We only ever have a single encoder */ + drm_for_each_encoder_mask(encoder, atom->state.crtc->dev, + atom->state.encoder_mask) + break; + + return encoder; +} + #define nv50_wndw_atom(p) container_of((p), struct nv50_wndw_atom, state) struct nv50_wndw_atom { diff --git a/drivers/gpu/drm/nouveau/dispnv50/base507c.c b/drivers/gpu/drm/nouveau/dispnv50/base507c.c index 511258bfbcbc..ba20a7722900 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/base507c.c +++ b/drivers/gpu/drm/nouveau/dispnv50/base507c.c @@ -276,7 +276,7 @@ base507c_new_(const struct nv50_wndw_func *func, const u32 *format, ret = nv50_dmac_create(&drm->client.device, &disp->disp.object, &oclass, head, &args, sizeof(args), - disp50->sync->bo.offset, &wndw->wndw); + disp50->sync->offset, &wndw->wndw); if (ret) { NV_ERROR(drm, "base%04x allocation failed: %d\n", oclass, ret); return ret; diff --git a/drivers/gpu/drm/nouveau/dispnv50/core.h b/drivers/gpu/drm/nouveau/dispnv50/core.h index 99157dc94d23..e021cb340569 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core.h +++ b/drivers/gpu/drm/nouveau/dispnv50/core.h @@ -2,6 +2,7 @@ #define __NV50_KMS_CORE_H__ #include "disp.h" #include "atom.h" +#include "crc.h" #include <nouveau_encoder.h> struct nv50_core { @@ -26,6 +27,9 @@ struct nv50_core_func { } wndw; const struct nv50_head_func *head; +#if IS_ENABLED(CONFIG_DEBUG_FS) + const struct nv50_crc_func *crc; +#endif const struct nv50_outp_func { void (*ctrl)(struct nv50_core *, int or, u32 ctrl, struct nv50_head_atom *); diff --git a/drivers/gpu/drm/nouveau/dispnv50/core507d.c b/drivers/gpu/drm/nouveau/dispnv50/core507d.c index e341f572c269..1d66f694b945 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core507d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core507d.c @@ -115,7 +115,7 @@ core507d_new_(const struct nv50_core_func *func, struct nouveau_drm *drm, ret = nv50_dmac_create(&drm->client.device, &disp->disp->object, &oclass, 0, &args, sizeof(args), - disp->sync->bo.offset, &core->chan); + disp->sync->offset, &core->chan); if (ret) { NV_ERROR(drm, "core%04x allocation failed: %d\n", oclass, ret); return ret; diff --git a/drivers/gpu/drm/nouveau/dispnv50/core907d.c b/drivers/gpu/drm/nouveau/dispnv50/core907d.c index 271629832629..b17c03529c78 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core907d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core907d.c @@ -30,6 +30,9 @@ core907d = { .ntfy_wait_done = core507d_ntfy_wait_done, .update = core507d_update, .head = &head907d, +#if IS_ENABLED(CONFIG_DEBUG_FS) + .crc = &crc907d, +#endif .dac = &dac907d, .sor = &sor907d, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/core917d.c b/drivers/gpu/drm/nouveau/dispnv50/core917d.c index 5cc072d4c30f..66846f372080 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core917d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core917d.c @@ -30,6 +30,9 @@ core917d = { .ntfy_wait_done = core507d_ntfy_wait_done, .update = core507d_update, .head = &head917d, +#if IS_ENABLED(CONFIG_DEBUG_FS) + .crc = &crc907d, +#endif .dac = &dac907d, .sor = &sor907d, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/corec37d.c b/drivers/gpu/drm/nouveau/dispnv50/corec37d.c index e0c8811fb8e4..ec83189a1d48 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/corec37d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/corec37d.c @@ -142,6 +142,9 @@ corec37d = { .wndw.owner = corec37d_wndw_owner, .head = &headc37d, .sor = &sorc37d, +#if IS_ENABLED(CONFIG_DEBUG_FS) + .crc = &crcc37d, +#endif }; int diff --git a/drivers/gpu/drm/nouveau/dispnv50/corec57d.c b/drivers/gpu/drm/nouveau/dispnv50/corec57d.c index 10ba9e9e4ae6..e1c11eba0ce1 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/corec57d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/corec57d.c @@ -52,6 +52,9 @@ corec57d = { .wndw.owner = corec37d_wndw_owner, .head = &headc57d, .sor = &sorc37d, +#if IS_ENABLED(CONFIG_DEBUG_FS) + .crc = &crcc37d, +#endif }; int diff --git a/drivers/gpu/drm/nouveau/dispnv50/crc.c b/drivers/gpu/drm/nouveau/dispnv50/crc.c new file mode 100644 index 000000000000..f17fb6d56757 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv50/crc.c @@ -0,0 +1,751 @@ +// SPDX-License-Identifier: MIT +#include <linux/string.h> +#include <drm/drm_crtc.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_vblank.h> +#include <drm/drm_vblank_work.h> + +#include <nvif/class.h> +#include <nvif/cl0002.h> +#include <nvif/timer.h> + +#include "nouveau_drv.h" +#include "core.h" +#include "head.h" +#include "wndw.h" +#include "handles.h" +#include "crc.h" + +static const char * const nv50_crc_sources[] = { + [NV50_CRC_SOURCE_NONE] = "none", + [NV50_CRC_SOURCE_AUTO] = "auto", + [NV50_CRC_SOURCE_RG] = "rg", + [NV50_CRC_SOURCE_OUTP_ACTIVE] = "outp-active", + [NV50_CRC_SOURCE_OUTP_COMPLETE] = "outp-complete", + [NV50_CRC_SOURCE_OUTP_INACTIVE] = "outp-inactive", +}; + +static int nv50_crc_parse_source(const char *buf, enum nv50_crc_source *s) +{ + int i; + + if (!buf) { + *s = NV50_CRC_SOURCE_NONE; + return 0; + } + + i = match_string(nv50_crc_sources, ARRAY_SIZE(nv50_crc_sources), buf); + if (i < 0) + return i; + + *s = i; + return 0; +} + +int +nv50_crc_verify_source(struct drm_crtc *crtc, const char *source_name, + size_t *values_cnt) +{ + struct nouveau_drm *drm = nouveau_drm(crtc->dev); + enum nv50_crc_source source; + + if (nv50_crc_parse_source(source_name, &source) < 0) { + NV_DEBUG(drm, "unknown source %s\n", source_name); + return -EINVAL; + } + + *values_cnt = 1; + return 0; +} + +const char *const *nv50_crc_get_sources(struct drm_crtc *crtc, size_t *count) +{ + *count = ARRAY_SIZE(nv50_crc_sources); + return nv50_crc_sources; +} + +static void +nv50_crc_program_ctx(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx) +{ + struct nv50_disp *disp = nv50_disp(head->base.base.dev); + struct nv50_core *core = disp->core; + u32 interlock[NV50_DISP_INTERLOCK__SIZE] = { 0 }; + + core->func->crc->set_ctx(head, ctx); + core->func->update(core, interlock, false); +} + +static void nv50_crc_ctx_flip_work(struct kthread_work *base) +{ + struct drm_vblank_work *work = to_drm_vblank_work(base); + struct nv50_crc *crc = container_of(work, struct nv50_crc, flip_work); + struct nv50_head *head = container_of(crc, struct nv50_head, crc); + struct drm_crtc *crtc = &head->base.base; + struct nv50_disp *disp = nv50_disp(crtc->dev); + u8 new_idx = crc->ctx_idx ^ 1; + + /* + * We don't want to accidentally wait for longer then the vblank, so + * try again for the next vblank if we don't grab the lock + */ + if (!mutex_trylock(&disp->mutex)) { + DRM_DEV_DEBUG_KMS(crtc->dev->dev, + "Lock contended, delaying CRC ctx flip for head-%d\n", + head->base.index); + drm_vblank_work_schedule(work, + drm_crtc_vblank_count(crtc) + 1, + true); + return; + } + + DRM_DEV_DEBUG_KMS(crtc->dev->dev, + "Flipping notifier ctx for head %d (%d -> %d)\n", + drm_crtc_index(crtc), crc->ctx_idx, new_idx); + + nv50_crc_program_ctx(head, NULL); + nv50_crc_program_ctx(head, &crc->ctx[new_idx]); + mutex_unlock(&disp->mutex); + + spin_lock_irq(&crc->lock); + crc->ctx_changed = true; + spin_unlock_irq(&crc->lock); +} + +static inline void nv50_crc_reset_ctx(struct nv50_crc_notifier_ctx *ctx) +{ + memset_io(ctx->mem.object.map.ptr, 0, ctx->mem.object.map.size); +} + +static void +nv50_crc_get_entries(struct nv50_head *head, + const struct nv50_crc_func *func, + enum nv50_crc_source source) +{ + struct drm_crtc *crtc = &head->base.base; + struct nv50_crc *crc = &head->crc; + u32 output_crc; + + while (crc->entry_idx < func->num_entries) { + /* + * While Nvidia's documentation says CRCs are written on each + * subsequent vblank after being enabled, in practice they + * aren't written immediately. + */ + output_crc = func->get_entry(head, &crc->ctx[crc->ctx_idx], + source, crc->entry_idx); + if (!output_crc) + return; + + drm_crtc_add_crc_entry(crtc, true, crc->frame, &output_crc); + crc->frame++; + crc->entry_idx++; + } +} + +void nv50_crc_handle_vblank(struct nv50_head *head) +{ + struct drm_crtc *crtc = &head->base.base; + struct nv50_crc *crc = &head->crc; + const struct nv50_crc_func *func = + nv50_disp(head->base.base.dev)->core->func->crc; + struct nv50_crc_notifier_ctx *ctx; + bool need_reschedule = false; + + if (!func) + return; + + /* + * We don't lose events if we aren't able to report CRCs until the + * next vblank, so only report CRCs if the locks we need aren't + * contended to prevent missing an actual vblank event + */ + if (!spin_trylock(&crc->lock)) + return; + + if (!crc->src) + goto out; + + ctx = &crc->ctx[crc->ctx_idx]; + if (crc->ctx_changed && func->ctx_finished(head, ctx)) { + nv50_crc_get_entries(head, func, crc->src); + + crc->ctx_idx ^= 1; + crc->entry_idx = 0; + crc->ctx_changed = false; + + /* + * Unfortunately when notifier contexts are changed during CRC + * capture, we will inevitably lose the CRC entry for the + * frame where the hardware actually latched onto the first + * UPDATE. According to Nvidia's hardware engineers, there's + * no workaround for this. + * + * Now, we could try to be smart here and calculate the number + * of missed CRCs based on audit timestamps, but those were + * removed starting with volta. Since we always flush our + * updates back-to-back without waiting, we'll just be + * optimistic and assume we always miss exactly one frame. + */ + DRM_DEV_DEBUG_KMS(head->base.base.dev->dev, + "Notifier ctx flip for head-%d finished, lost CRC for frame %llu\n", + head->base.index, crc->frame); + crc->frame++; + + nv50_crc_reset_ctx(ctx); + need_reschedule = true; + } + + nv50_crc_get_entries(head, func, crc->src); + + if (need_reschedule) + drm_vblank_work_schedule(&crc->flip_work, + drm_crtc_vblank_count(crtc) + + crc->flip_threshold + - crc->entry_idx, + true); + +out: + spin_unlock(&crc->lock); +} + +static void nv50_crc_wait_ctx_finished(struct nv50_head *head, + const struct nv50_crc_func *func, + struct nv50_crc_notifier_ctx *ctx) +{ + struct drm_device *dev = head->base.base.dev; + struct nouveau_drm *drm = nouveau_drm(dev); + s64 ret; + + ret = nvif_msec(&drm->client.device, 50, + if (func->ctx_finished(head, ctx)) break;); + if (ret == -ETIMEDOUT) + NV_ERROR(drm, + "CRC notifier ctx for head %d not finished after 50ms\n", + head->base.index); + else if (ret) + NV_ATOMIC(drm, + "CRC notifier ctx for head-%d finished after %lldns\n", + head->base.index, ret); +} + +void nv50_crc_atomic_stop_reporting(struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + int i; + + for_each_new_crtc_in_state(state, crtc, crtc_state, i) { + struct nv50_head *head = nv50_head(crtc); + struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); + struct nv50_crc *crc = &head->crc; + + if (!asyh->clr.crc) + continue; + + spin_lock_irq(&crc->lock); + crc->src = NV50_CRC_SOURCE_NONE; + spin_unlock_irq(&crc->lock); + + drm_crtc_vblank_put(crtc); + drm_vblank_work_cancel_sync(&crc->flip_work); + + NV_ATOMIC(nouveau_drm(crtc->dev), + "CRC reporting on vblank for head-%d disabled\n", + head->base.index); + + /* CRC generation is still enabled in hw, we'll just report + * any remaining CRC entries ourselves after it gets disabled + * in hardware + */ + } +} + +void nv50_crc_atomic_init_notifier_contexts(struct drm_atomic_state *state) +{ + struct drm_crtc_state *new_crtc_state; + struct drm_crtc *crtc; + int i; + + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { + struct nv50_head *head = nv50_head(crtc); + struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); + struct nv50_crc *crc = &head->crc; + int i; + + if (!asyh->set.crc) + continue; + + crc->entry_idx = 0; + crc->ctx_changed = false; + for (i = 0; i < ARRAY_SIZE(crc->ctx); i++) + nv50_crc_reset_ctx(&crc->ctx[i]); + } +} + +void nv50_crc_atomic_release_notifier_contexts(struct drm_atomic_state *state) +{ + const struct nv50_crc_func *func = + nv50_disp(state->dev)->core->func->crc; + struct drm_crtc_state *new_crtc_state; + struct drm_crtc *crtc; + int i; + + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { + struct nv50_head *head = nv50_head(crtc); + struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); + struct nv50_crc *crc = &head->crc; + struct nv50_crc_notifier_ctx *ctx = &crc->ctx[crc->ctx_idx]; + + if (!asyh->clr.crc) + continue; + + if (crc->ctx_changed) { + nv50_crc_wait_ctx_finished(head, func, ctx); + ctx = &crc->ctx[crc->ctx_idx ^ 1]; + } + nv50_crc_wait_ctx_finished(head, func, ctx); + } +} + +void nv50_crc_atomic_start_reporting(struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + int i; + + for_each_new_crtc_in_state(state, crtc, crtc_state, i) { + struct nv50_head *head = nv50_head(crtc); + struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); + struct nv50_crc *crc = &head->crc; + u64 vbl_count; + + if (!asyh->set.crc) + continue; + + drm_crtc_vblank_get(crtc); + + spin_lock_irq(&crc->lock); + vbl_count = drm_crtc_vblank_count(crtc); + crc->frame = vbl_count; + crc->src = asyh->crc.src; + drm_vblank_work_schedule(&crc->flip_work, + vbl_count + crc->flip_threshold, + true); + spin_unlock_irq(&crc->lock); + + NV_ATOMIC(nouveau_drm(crtc->dev), + "CRC reporting on vblank for head-%d enabled\n", + head->base.index); + } +} + +int nv50_crc_atomic_check_head(struct nv50_head *head, + struct nv50_head_atom *asyh, + struct nv50_head_atom *armh) +{ + struct nv50_atom *atom = nv50_atom(asyh->state.state); + struct drm_device *dev = head->base.base.dev; + struct nv50_disp *disp = nv50_disp(dev); + bool changed = armh->crc.src != asyh->crc.src; + + if (!armh->crc.src && !asyh->crc.src) { + asyh->set.crc = false; + asyh->clr.crc = false; + return 0; + } + + /* While we don't care about entry tags, Volta+ hw always needs the + * controlling wndw channel programmed to a wndw that's owned by our + * head + */ + if (asyh->crc.src && disp->disp->object.oclass >= GV100_DISP && + !(BIT(asyh->crc.wndw) & asyh->wndw.owned)) { + if (!asyh->wndw.owned) { + /* TODO: once we support flexible channel ownership, + * we should write some code here to handle attempting + * to "steal" a plane: e.g. take a plane that is + * currently not-visible and owned by another head, + * and reassign it to this head. If we fail to do so, + * we shuld reject the mode outright as CRC capture + * then becomes impossible. + */ + NV_ATOMIC(nouveau_drm(dev), + "No available wndws for CRC readback\n"); + return -EINVAL; + } + asyh->crc.wndw = ffs(asyh->wndw.owned) - 1; + } + + if (drm_atomic_crtc_needs_modeset(&asyh->state) || changed || + armh->crc.wndw != asyh->crc.wndw) { + asyh->clr.crc = armh->crc.src && armh->state.active; + asyh->set.crc = asyh->crc.src && asyh->state.active; + if (changed) + asyh->set.or |= armh->or.crc_raster != + asyh->or.crc_raster; + + if (asyh->clr.crc && asyh->set.crc) + atom->flush_disable = true; + } else { + asyh->set.crc = false; + asyh->clr.crc = false; + } + + return 0; +} + +void nv50_crc_atomic_check_outp(struct nv50_atom *atom) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + int i; + + if (atom->flush_disable) + return; + + for_each_oldnew_crtc_in_state(&atom->state, crtc, old_crtc_state, + new_crtc_state, i) { + struct nv50_head_atom *armh = nv50_head_atom(old_crtc_state); + struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); + struct nv50_outp_atom *outp_atom; + struct nouveau_encoder *outp = + nv50_real_outp(nv50_head_atom_get_encoder(armh)); + struct drm_encoder *encoder = &outp->base.base; + + if (!asyh->clr.crc) + continue; + + /* + * Re-programming ORs can't be done in the same flush as + * disabling CRCs + */ + list_for_each_entry(outp_atom, &atom->outp, head) { + if (outp_atom->encoder == encoder) { + if (outp_atom->set.mask) { + atom->flush_disable = true; + return; + } else { + break; + } + } + } + } +} + +static enum nv50_crc_source_type +nv50_crc_source_type(struct nouveau_encoder *outp, + enum nv50_crc_source source) +{ + struct dcb_output *dcbe = outp->dcb; + + switch (source) { + case NV50_CRC_SOURCE_NONE: return NV50_CRC_SOURCE_TYPE_NONE; + case NV50_CRC_SOURCE_RG: return NV50_CRC_SOURCE_TYPE_RG; + default: break; + } + + if (dcbe->location != DCB_LOC_ON_CHIP) + return NV50_CRC_SOURCE_TYPE_PIOR; + + switch (dcbe->type) { + case DCB_OUTPUT_DP: return NV50_CRC_SOURCE_TYPE_SF; + case DCB_OUTPUT_ANALOG: return NV50_CRC_SOURCE_TYPE_DAC; + default: return NV50_CRC_SOURCE_TYPE_SOR; + } +} + +void nv50_crc_atomic_set(struct nv50_head *head, + struct nv50_head_atom *asyh) +{ + struct drm_crtc *crtc = &head->base.base; + struct drm_device *dev = crtc->dev; + struct nv50_crc *crc = &head->crc; + const struct nv50_crc_func *func = nv50_disp(dev)->core->func->crc; + struct nouveau_encoder *outp = + nv50_real_outp(nv50_head_atom_get_encoder(asyh)); + + func->set_src(head, outp->or, + nv50_crc_source_type(outp, asyh->crc.src), + &crc->ctx[crc->ctx_idx], asyh->crc.wndw); +} + +void nv50_crc_atomic_clr(struct nv50_head *head) +{ + const struct nv50_crc_func *func = + nv50_disp(head->base.base.dev)->core->func->crc; + + func->set_src(head, 0, NV50_CRC_SOURCE_TYPE_NONE, NULL, 0); +} + +#define NV50_CRC_RASTER_ACTIVE 0 +#define NV50_CRC_RASTER_COMPLETE 1 +#define NV50_CRC_RASTER_INACTIVE 2 + +static inline int +nv50_crc_raster_type(enum nv50_crc_source source) +{ + switch (source) { + case NV50_CRC_SOURCE_NONE: + case NV50_CRC_SOURCE_AUTO: + case NV50_CRC_SOURCE_RG: + case NV50_CRC_SOURCE_OUTP_ACTIVE: + return NV50_CRC_RASTER_ACTIVE; + case NV50_CRC_SOURCE_OUTP_COMPLETE: + return NV50_CRC_RASTER_COMPLETE; + case NV50_CRC_SOURCE_OUTP_INACTIVE: + return NV50_CRC_RASTER_INACTIVE; + } + + return 0; +} + +/* We handle mapping the memory for CRC notifiers ourselves, since each + * notifier needs it's own handle + */ +static inline int +nv50_crc_ctx_init(struct nv50_head *head, struct nvif_mmu *mmu, + struct nv50_crc_notifier_ctx *ctx, size_t len, int idx) +{ + struct nv50_core *core = nv50_disp(head->base.base.dev)->core; + int ret; + + ret = nvif_mem_init_map(mmu, NVIF_MEM_VRAM, len, &ctx->mem); + if (ret) + return ret; + + ret = nvif_object_init(&core->chan.base.user, + NV50_DISP_HANDLE_CRC_CTX(head, idx), + NV_DMA_IN_MEMORY, + &(struct nv_dma_v0) { + .target = NV_DMA_V0_TARGET_VRAM, + .access = NV_DMA_V0_ACCESS_RDWR, + .start = ctx->mem.addr, + .limit = ctx->mem.addr + + ctx->mem.size - 1, + }, sizeof(struct nv_dma_v0), + &ctx->ntfy); + if (ret) + goto fail_fini; + + return 0; + +fail_fini: + nvif_mem_fini(&ctx->mem); + return ret; +} + +static inline void +nv50_crc_ctx_fini(struct nv50_crc_notifier_ctx *ctx) +{ + nvif_object_fini(&ctx->ntfy); + nvif_mem_fini(&ctx->mem); +} + +int nv50_crc_set_source(struct drm_crtc *crtc, const char *source_str) +{ + struct drm_device *dev = crtc->dev; + struct drm_atomic_state *state; + struct drm_modeset_acquire_ctx ctx; + struct nv50_head *head = nv50_head(crtc); + struct nv50_crc *crc = &head->crc; + const struct nv50_crc_func *func = nv50_disp(dev)->core->func->crc; + struct nvif_mmu *mmu = &nouveau_drm(dev)->client.mmu; + struct nv50_head_atom *asyh; + struct drm_crtc_state *crtc_state; + enum nv50_crc_source source; + int ret = 0, ctx_flags = 0, i; + + ret = nv50_crc_parse_source(source_str, &source); + if (ret) + return ret; + + /* + * Since we don't want the user to accidentally interrupt us as we're + * disabling CRCs + */ + if (source) + ctx_flags |= DRM_MODESET_ACQUIRE_INTERRUPTIBLE; + drm_modeset_acquire_init(&ctx, ctx_flags); + + state = drm_atomic_state_alloc(dev); + if (!state) { + ret = -ENOMEM; + goto out_acquire_fini; + } + state->acquire_ctx = &ctx; + + if (source) { + for (i = 0; i < ARRAY_SIZE(head->crc.ctx); i++) { + ret = nv50_crc_ctx_init(head, mmu, &crc->ctx[i], + func->notifier_len, i); + if (ret) + goto out_ctx_fini; + } + } + +retry: + crtc_state = drm_atomic_get_crtc_state(state, &head->base.base); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + if (ret == -EDEADLK) + goto deadlock; + else if (ret) + goto out_drop_locks; + } + asyh = nv50_head_atom(crtc_state); + asyh->crc.src = source; + asyh->or.crc_raster = nv50_crc_raster_type(source); + + ret = drm_atomic_commit(state); + if (ret == -EDEADLK) + goto deadlock; + else if (ret) + goto out_drop_locks; + + if (!source) { + /* + * If the user specified a custom flip threshold through + * debugfs, reset it + */ + crc->flip_threshold = func->flip_threshold; + } + +out_drop_locks: + drm_modeset_drop_locks(&ctx); +out_ctx_fini: + if (!source || ret) { + for (i = 0; i < ARRAY_SIZE(crc->ctx); i++) + nv50_crc_ctx_fini(&crc->ctx[i]); + } + drm_atomic_state_put(state); +out_acquire_fini: + drm_modeset_acquire_fini(&ctx); + return ret; + +deadlock: + drm_atomic_state_clear(state); + drm_modeset_backoff(&ctx); + goto retry; +} + +static int +nv50_crc_debugfs_flip_threshold_get(struct seq_file *m, void *data) +{ + struct nv50_head *head = m->private; + struct drm_crtc *crtc = &head->base.base; + struct nv50_crc *crc = &head->crc; + int ret; + + ret = drm_modeset_lock_single_interruptible(&crtc->mutex); + if (ret) + return ret; + + seq_printf(m, "%d\n", crc->flip_threshold); + + drm_modeset_unlock(&crtc->mutex); + return ret; +} + +static int +nv50_crc_debugfs_flip_threshold_open(struct inode *inode, struct file *file) +{ + return single_open(file, nv50_crc_debugfs_flip_threshold_get, + inode->i_private); +} + +static ssize_t +nv50_crc_debugfs_flip_threshold_set(struct file *file, + const char __user *ubuf, size_t len, + loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct nv50_head *head = m->private; + struct nv50_head_atom *armh; + struct drm_crtc *crtc = &head->base.base; + struct nouveau_drm *drm = nouveau_drm(crtc->dev); + struct nv50_crc *crc = &head->crc; + const struct nv50_crc_func *func = + nv50_disp(crtc->dev)->core->func->crc; + int value, ret; + + ret = kstrtoint_from_user(ubuf, len, 10, &value); + if (ret) + return ret; + + if (value > func->flip_threshold) + return -EINVAL; + else if (value == -1) + value = func->flip_threshold; + else if (value < -1) + return -EINVAL; + + ret = drm_modeset_lock_single_interruptible(&crtc->mutex); + if (ret) + return ret; + + armh = nv50_head_atom(crtc->state); + if (armh->crc.src) { + ret = -EBUSY; + goto out; + } + + NV_DEBUG(drm, + "Changing CRC flip threshold for next capture on head-%d to %d\n", + head->base.index, value); + crc->flip_threshold = value; + ret = len; + +out: + drm_modeset_unlock(&crtc->mutex); + return ret; +} + +static const struct file_operations nv50_crc_flip_threshold_fops = { + .owner = THIS_MODULE, + .open = nv50_crc_debugfs_flip_threshold_open, + .read = seq_read, + .write = nv50_crc_debugfs_flip_threshold_set, +}; + +int nv50_head_crc_late_register(struct nv50_head *head) +{ + struct drm_crtc *crtc = &head->base.base; + const struct nv50_crc_func *func = + nv50_disp(crtc->dev)->core->func->crc; + struct dentry *root; + + if (!func || !crtc->debugfs_entry) + return 0; + + root = debugfs_create_dir("nv_crc", crtc->debugfs_entry); + debugfs_create_file("flip_threshold", 0644, root, head, + &nv50_crc_flip_threshold_fops); + + return 0; +} + +static inline void +nv50_crc_init_head(struct nv50_disp *disp, const struct nv50_crc_func *func, + struct nv50_head *head) +{ + struct nv50_crc *crc = &head->crc; + + crc->flip_threshold = func->flip_threshold; + spin_lock_init(&crc->lock); + drm_vblank_work_init(&crc->flip_work, &head->base.base, + nv50_crc_ctx_flip_work); +} + +void nv50_crc_init(struct drm_device *dev) +{ + struct nv50_disp *disp = nv50_disp(dev); + struct drm_crtc *crtc; + const struct nv50_crc_func *func = disp->core->func->crc; + + if (!func) + return; + + drm_for_each_crtc(crtc, dev) + nv50_crc_init_head(disp, func, nv50_head(crtc)); +} diff --git a/drivers/gpu/drm/nouveau/dispnv50/crc.h b/drivers/gpu/drm/nouveau/dispnv50/crc.h new file mode 100644 index 000000000000..4bc59e779315 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv50/crc.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV50_CRC_H__ +#define __NV50_CRC_H__ + +#include <linux/mutex.h> +#include <drm/drm_crtc.h> +#include <drm/drm_vblank_work.h> + +#include <nvif/mem.h> +#include <nvkm/subdev/bios.h> +#include "nouveau_encoder.h" + +struct nv50_atom; +struct nv50_disp; +struct nv50_head; + +#if IS_ENABLED(CONFIG_DEBUG_FS) +enum nv50_crc_source { + NV50_CRC_SOURCE_NONE = 0, + NV50_CRC_SOURCE_AUTO, + NV50_CRC_SOURCE_RG, + NV50_CRC_SOURCE_OUTP_ACTIVE, + NV50_CRC_SOURCE_OUTP_COMPLETE, + NV50_CRC_SOURCE_OUTP_INACTIVE, +}; + +/* RG -> SF (DP only) + * -> SOR + * -> PIOR + * -> DAC + */ +enum nv50_crc_source_type { + NV50_CRC_SOURCE_TYPE_NONE = 0, + NV50_CRC_SOURCE_TYPE_SOR, + NV50_CRC_SOURCE_TYPE_PIOR, + NV50_CRC_SOURCE_TYPE_DAC, + NV50_CRC_SOURCE_TYPE_RG, + NV50_CRC_SOURCE_TYPE_SF, +}; + +struct nv50_crc_notifier_ctx { + struct nvif_mem mem; + struct nvif_object ntfy; +}; + +struct nv50_crc_atom { + enum nv50_crc_source src; + /* Only used for gv100+ */ + u8 wndw : 4; +}; + +struct nv50_crc_func { + void (*set_src)(struct nv50_head *, int or, enum nv50_crc_source_type, + struct nv50_crc_notifier_ctx *, u32 wndw); + void (*set_ctx)(struct nv50_head *, struct nv50_crc_notifier_ctx *); + u32 (*get_entry)(struct nv50_head *, struct nv50_crc_notifier_ctx *, + enum nv50_crc_source, int idx); + bool (*ctx_finished)(struct nv50_head *, + struct nv50_crc_notifier_ctx *); + short flip_threshold; + short num_entries; + size_t notifier_len; +}; + +struct nv50_crc { + spinlock_t lock; + struct nv50_crc_notifier_ctx ctx[2]; + struct drm_vblank_work flip_work; + enum nv50_crc_source src; + + u64 frame; + short entry_idx; + short flip_threshold; + u8 ctx_idx : 1; + bool ctx_changed : 1; +}; + +void nv50_crc_init(struct drm_device *dev); +int nv50_head_crc_late_register(struct nv50_head *); +void nv50_crc_handle_vblank(struct nv50_head *head); + +int nv50_crc_verify_source(struct drm_crtc *, const char *, size_t *); +const char *const *nv50_crc_get_sources(struct drm_crtc *, size_t *); +int nv50_crc_set_source(struct drm_crtc *, const char *); + +int nv50_crc_atomic_check_head(struct nv50_head *, struct nv50_head_atom *, + struct nv50_head_atom *); +void nv50_crc_atomic_check_outp(struct nv50_atom *atom); +void nv50_crc_atomic_stop_reporting(struct drm_atomic_state *); +void nv50_crc_atomic_init_notifier_contexts(struct drm_atomic_state *); +void nv50_crc_atomic_release_notifier_contexts(struct drm_atomic_state *); +void nv50_crc_atomic_start_reporting(struct drm_atomic_state *); +void nv50_crc_atomic_set(struct nv50_head *, struct nv50_head_atom *); +void nv50_crc_atomic_clr(struct nv50_head *); + +extern const struct nv50_crc_func crc907d; +extern const struct nv50_crc_func crcc37d; + +#else /* IS_ENABLED(CONFIG_DEBUG_FS) */ +struct nv50_crc {}; +struct nv50_crc_func {}; +struct nv50_crc_atom {}; + +#define nv50_crc_verify_source NULL +#define nv50_crc_get_sources NULL +#define nv50_crc_set_source NULL + +static inline void nv50_crc_init(struct drm_device *dev) {} +static inline int nv50_head_crc_late_register(struct nv50_head *) {} +static inline void +nv50_crc_handle_vblank(struct nv50_head *head) { return 0; } + +static inline int +nv50_crc_atomic_check_head(struct nv50_head *, struct nv50_head_atom *, + struct nv50_head_atom *) {} +static inline void nv50_crc_atomic_check_outp(struct nv50_atom *atom) {} +static inline void +nv50_crc_atomic_stop_reporting(struct drm_atomic_state *) {} +static inline void +nv50_crc_atomic_init_notifier_contexts(struct drm_atomic_state *) {} +static inline void +nv50_crc_atomic_release_notifier_contexts(struct drm_atomic_state *) {} +static inline void +nv50_crc_atomic_start_reporting(struct drm_atomic_state *) {} +static inline void +nv50_crc_atomic_set(struct nv50_head *, struct nv50_head_atom *) {} +static inline void +nv50_crc_atomic_clr(struct nv50_head *) {} + +#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */ +#endif /* !__NV50_CRC_H__ */ diff --git a/drivers/gpu/drm/nouveau/dispnv50/crc907d.c b/drivers/gpu/drm/nouveau/dispnv50/crc907d.c new file mode 100644 index 000000000000..92e907de7645 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv50/crc907d.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: MIT +#include <drm/drm_crtc.h> + +#include "crc.h" +#include "core.h" +#include "disp.h" +#include "head.h" + +#define CRC907D_MAX_ENTRIES 255 + +struct crc907d_notifier { + u32 status; + u32 :32; /* reserved */ + struct crc907d_entry { + u32 status; + u32 compositor_crc; + u32 output_crc[2]; + } entries[CRC907D_MAX_ENTRIES]; +} __packed; + +static void +crc907d_set_src(struct nv50_head *head, int or, + enum nv50_crc_source_type source, + struct nv50_crc_notifier_ctx *ctx, u32 wndw) +{ + struct drm_crtc *crtc = &head->base.base; + struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + const u32 hoff = head->base.index * 0x300; + u32 *push; + u32 crc_args = 0xfff00000; + + switch (source) { + case NV50_CRC_SOURCE_TYPE_SOR: + crc_args |= (0x00000f0f + or * 16) << 8; + break; + case NV50_CRC_SOURCE_TYPE_PIOR: + crc_args |= (0x000000ff + or * 256) << 8; + break; + case NV50_CRC_SOURCE_TYPE_DAC: + crc_args |= (0x00000ff0 + or) << 8; + break; + case NV50_CRC_SOURCE_TYPE_RG: + crc_args |= (0x00000ff8 + drm_crtc_index(crtc)) << 8; + break; + case NV50_CRC_SOURCE_TYPE_SF: + crc_args |= (0x00000f8f + drm_crtc_index(crtc) * 16) << 8; + break; + case NV50_CRC_SOURCE_NONE: + crc_args |= 0x000fff00; + break; + } + + push = evo_wait(core, 4); + if (!push) + return; + + if (source) { + evo_mthd(push, 0x0438 + hoff, 1); + evo_data(push, ctx->ntfy.handle); + evo_mthd(push, 0x0430 + hoff, 1); + evo_data(push, crc_args); + } else { + evo_mthd(push, 0x0430 + hoff, 1); + evo_data(push, crc_args); + evo_mthd(push, 0x0438 + hoff, 1); + evo_data(push, 0); + } + evo_kick(push, core); +} + +static void crc907d_set_ctx(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx) +{ + struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + u32 *push = evo_wait(core, 2); + + if (!push) + return; + + evo_mthd(push, 0x0438 + (head->base.index * 0x300), 1); + evo_data(push, ctx ? ctx->ntfy.handle : 0); + evo_kick(push, core); +} + +static u32 crc907d_get_entry(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx, + enum nv50_crc_source source, int idx) +{ + struct crc907d_notifier __iomem *notifier = ctx->mem.object.map.ptr; + + return ioread32_native(¬ifier->entries[idx].output_crc[0]); +} + +static bool crc907d_ctx_finished(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx) +{ + struct nouveau_drm *drm = nouveau_drm(head->base.base.dev); + struct crc907d_notifier __iomem *notifier = ctx->mem.object.map.ptr; + const u32 status = ioread32_native(¬ifier->status); + const u32 overflow = status & 0x0000003e; + + if (!(status & 0x00000001)) + return false; + + if (overflow) { + const char *engine = NULL; + + switch (overflow) { + case 0x00000004: engine = "DSI"; break; + case 0x00000008: engine = "Compositor"; break; + case 0x00000010: engine = "CRC output 1"; break; + case 0x00000020: engine = "CRC output 2"; break; + } + + if (engine) + NV_ERROR(drm, + "CRC notifier context for head %d overflowed on %s: %x\n", + head->base.index, engine, status); + else + NV_ERROR(drm, + "CRC notifier context for head %d overflowed: %x\n", + head->base.index, status); + } + + NV_DEBUG(drm, "Head %d CRC context status: %x\n", + head->base.index, status); + + return true; +} + +const struct nv50_crc_func crc907d = { + .set_src = crc907d_set_src, + .set_ctx = crc907d_set_ctx, + .get_entry = crc907d_get_entry, + .ctx_finished = crc907d_ctx_finished, + .flip_threshold = CRC907D_MAX_ENTRIES - 10, + .num_entries = CRC907D_MAX_ENTRIES, + .notifier_len = sizeof(struct crc907d_notifier), +}; diff --git a/drivers/gpu/drm/nouveau/dispnv50/crcc37d.c b/drivers/gpu/drm/nouveau/dispnv50/crcc37d.c new file mode 100644 index 000000000000..940cefd5517d --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv50/crcc37d.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: MIT +#include <drm/drm_crtc.h> + +#include "crc.h" +#include "core.h" +#include "disp.h" +#include "head.h" + +#define CRCC37D_MAX_ENTRIES 2047 + +struct crcc37d_notifier { + u32 status; + + /* reserved */ + u32 :32; + u32 :32; + u32 :32; + u32 :32; + u32 :32; + u32 :32; + u32 :32; + + struct crcc37d_entry { + u32 status[2]; + u32 :32; /* reserved */ + u32 compositor_crc; + u32 rg_crc; + u32 output_crc[2]; + u32 :32; /* reserved */ + } entries[CRCC37D_MAX_ENTRIES]; +} __packed; + +static void +crcc37d_set_src(struct nv50_head *head, int or, + enum nv50_crc_source_type source, + struct nv50_crc_notifier_ctx *ctx, u32 wndw) +{ + struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + const u32 hoff = head->base.index * 0x400; + u32 *push; + u32 crc_args; + + switch (source) { + case NV50_CRC_SOURCE_TYPE_SOR: + crc_args = (0x00000050 + or) << 12; + break; + case NV50_CRC_SOURCE_TYPE_PIOR: + crc_args = (0x00000060 + or) << 12; + break; + case NV50_CRC_SOURCE_TYPE_SF: + crc_args = 0x00000030 << 12; + break; + default: + crc_args = 0; + break; + } + + push = evo_wait(core, 4); + if (!push) + return; + + if (source) { + evo_mthd(push, 0x2180 + hoff, 1); + evo_data(push, ctx->ntfy.handle); + evo_mthd(push, 0x2184 + hoff, 1); + evo_data(push, crc_args | wndw); + } else { + evo_mthd(push, 0x2184 + hoff, 1); + evo_data(push, 0); + evo_mthd(push, 0x2180 + hoff, 1); + evo_data(push, 0); + } + + evo_kick(push, core); +} + +static void crcc37d_set_ctx(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx) +{ + struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + u32 *push = evo_wait(core, 2); + + if (!push) + return; + + evo_mthd(push, 0x2180 + (head->base.index * 0x400), 1); + evo_data(push, ctx ? ctx->ntfy.handle : 0); + evo_kick(push, core); +} + +static u32 crcc37d_get_entry(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx, + enum nv50_crc_source source, int idx) +{ + struct crcc37d_notifier __iomem *notifier = ctx->mem.object.map.ptr; + struct crcc37d_entry __iomem *entry = ¬ifier->entries[idx]; + u32 __iomem *crc_addr; + + if (source == NV50_CRC_SOURCE_RG) + crc_addr = &entry->rg_crc; + else + crc_addr = &entry->output_crc[0]; + + return ioread32_native(crc_addr); +} + +static bool crcc37d_ctx_finished(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx) +{ + struct nouveau_drm *drm = nouveau_drm(head->base.base.dev); + struct crcc37d_notifier __iomem *notifier = ctx->mem.object.map.ptr; + const u32 status = ioread32_native(¬ifier->status); + const u32 overflow = status & 0x0000007e; + + if (!(status & 0x00000001)) + return false; + + if (overflow) { + const char *engine = NULL; + + switch (overflow) { + case 0x00000004: engine = "Front End"; break; + case 0x00000008: engine = "Compositor"; break; + case 0x00000010: engine = "RG"; break; + case 0x00000020: engine = "CRC output 1"; break; + case 0x00000040: engine = "CRC output 2"; break; + } + + if (engine) + NV_ERROR(drm, + "CRC notifier context for head %d overflowed on %s: %x\n", + head->base.index, engine, status); + else + NV_ERROR(drm, + "CRC notifier context for head %d overflowed: %x\n", + head->base.index, status); + } + + NV_DEBUG(drm, "Head %d CRC context status: %x\n", + head->base.index, status); + + return true; +} + +const struct nv50_crc_func crcc37d = { + .set_src = crcc37d_set_src, + .set_ctx = crcc37d_set_ctx, + .get_entry = crcc37d_get_entry, + .ctx_finished = crcc37d_ctx_finished, + .flip_threshold = CRCC37D_MAX_ENTRIES - 30, + .num_entries = CRCC37D_MAX_ENTRIES, + .notifier_len = sizeof(struct crcc37d_notifier), +}; diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index d472942102f5..cd71b9876c8a 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -26,6 +26,7 @@ #include "core.h" #include "head.h" #include "wndw.h" +#include "handles.h" #include <linux/dma-mapping.h> #include <linux/hdmi.h> @@ -58,24 +59,6 @@ #include <subdev/bios/dp.h> /****************************************************************************** - * Atomic state - *****************************************************************************/ - -struct nv50_outp_atom { - struct list_head head; - - struct drm_encoder *encoder; - bool flush_disable; - - union nv50_outp_atom_mask { - struct { - bool ctrl:1; - }; - u8 mask; - } set, clr; -}; - -/****************************************************************************** * EVO channel *****************************************************************************/ @@ -172,7 +155,8 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, if (!syncbuf) return 0; - ret = nvif_object_init(&dmac->base.user, 0xf0000000, NV_DMA_IN_MEMORY, + ret = nvif_object_init(&dmac->base.user, NV50_DISP_HANDLE_SYNCBUF, + NV_DMA_IN_MEMORY, &(struct nv_dma_v0) { .target = NV_DMA_V0_TARGET_VRAM, .access = NV_DMA_V0_ACCESS_RDWR, @@ -183,7 +167,8 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, if (ret) return ret; - ret = nvif_object_init(&dmac->base.user, 0xf0000001, NV_DMA_IN_MEMORY, + ret = nvif_object_init(&dmac->base.user, NV50_DISP_HANDLE_VRAM, + NV_DMA_IN_MEMORY, &(struct nv_dma_v0) { .target = NV_DMA_V0_TARGET_VRAM, .access = NV_DMA_V0_ACCESS_RDWR, @@ -798,6 +783,19 @@ struct nv50_msto { bool disabled; }; +struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder) +{ + struct nv50_msto *msto; + + if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) + return nouveau_encoder(encoder); + + msto = nv50_msto(encoder); + if (!msto->mstc) + return NULL; + return msto->mstc->mstm->outp; +} + static struct drm_dp_payload * nv50_msto_payload(struct nv50_msto *msto) { @@ -1945,8 +1943,10 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) struct nv50_outp_atom *outp, *outt; u32 interlock[NV50_DISP_INTERLOCK__SIZE] = {}; int i; + bool flushed = false; NV_ATOMIC(drm, "commit %d %d\n", atom->lock_core, atom->flush_disable); + nv50_crc_atomic_stop_reporting(state); drm_atomic_helper_wait_for_fences(dev, state, false); drm_atomic_helper_wait_for_dependencies(state); drm_atomic_helper_update_legacy_modeset_state(dev, state); @@ -2004,6 +2004,8 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) nv50_disp_atomic_commit_wndw(state, interlock); nv50_disp_atomic_commit_core(state, interlock); memset(interlock, 0x00, sizeof(interlock)); + + flushed = true; } } } @@ -2014,9 +2016,15 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) nv50_disp_atomic_commit_wndw(state, interlock); nv50_disp_atomic_commit_core(state, interlock); memset(interlock, 0x00, sizeof(interlock)); + + flushed = true; } } + if (flushed) + nv50_crc_atomic_release_notifier_contexts(state); + nv50_crc_atomic_init_notifier_contexts(state); + /* Update output path(s). */ list_for_each_entry_safe(outp, outt, &atom->outp, head) { const struct drm_encoder_helper_funcs *help; @@ -2130,6 +2138,9 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) } } + nv50_crc_atomic_start_reporting(state); + if (!flushed) + nv50_crc_atomic_release_notifier_contexts(state); drm_atomic_helper_commit_hw_done(state); drm_atomic_helper_cleanup_planes(dev, state); drm_atomic_helper_commit_cleanup_done(state); @@ -2287,12 +2298,28 @@ static int nv50_disp_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { struct nv50_atom *atom = nv50_atom(state); + struct nv50_core *core = nv50_disp(dev)->core; struct drm_connector_state *old_connector_state, *new_connector_state; struct drm_connector *connector; struct drm_crtc_state *new_crtc_state; struct drm_crtc *crtc; + struct nv50_head *head; + struct nv50_head_atom *asyh; int ret, i; + if (core->assign_windows && core->func->head->static_wndw_map) { + drm_for_each_crtc(crtc, dev) { + new_crtc_state = drm_atomic_get_crtc_state(state, + crtc); + if (IS_ERR(new_crtc_state)) + return PTR_ERR(new_crtc_state); + + head = nv50_head(crtc); + asyh = nv50_head_atom(new_crtc_state); + core->func->head->static_wndw_map(head, asyh); + } + } + /* We need to handle colour management on a per-plane basis. */ for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { if (new_crtc_state->color_mgmt_changed) { @@ -2320,6 +2347,8 @@ nv50_disp_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) if (ret) return ret; + nv50_crc_atomic_check_outp(atom); + return 0; } diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.h b/drivers/gpu/drm/nouveau/dispnv50/disp.h index 696e70a6b98b..1968c6921f9e 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.h +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.h @@ -1,10 +1,12 @@ #ifndef __NV50_KMS_H__ #define __NV50_KMS_H__ +#include <linux/workqueue.h> #include <nvif/mem.h> #include "nouveau_display.h" struct nv50_msto; +struct nouveau_encoder; struct nv50_disp { struct nvif_disp *disp; @@ -71,11 +73,33 @@ struct nv50_dmac { struct mutex lock; }; +struct nv50_outp_atom { + struct list_head head; + + struct drm_encoder *encoder; + bool flush_disable; + + union nv50_outp_atom_mask { + struct { + bool ctrl:1; + }; + u8 mask; + } set, clr; +}; + int nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, const s32 *oclass, u8 head, void *data, u32 size, u64 syncbuf, struct nv50_dmac *dmac); void nv50_dmac_destroy(struct nv50_dmac *); +/* + * For normal encoders this just returns the encoder. For active MST encoders, + * this returns the real outp that's driving displays on the topology. + * Inactive MST encoders return NULL, since they would have no real outp to + * return anyway. + */ +struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder); + u32 *evo_wait(struct nv50_dmac *, int nr); void evo_kick(u32 *, struct nv50_dmac *); diff --git a/drivers/gpu/drm/nouveau/dispnv50/handles.h b/drivers/gpu/drm/nouveau/dispnv50/handles.h new file mode 100644 index 000000000000..a97a7bd29243 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv50/handles.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV50_KMS_HANDLES_H__ +#define __NV50_KMS_HANDLES_H__ + +/* + * Various hard-coded object handles that nouveau uses. These are made-up by + * nouveau developers, not Nvidia. The only significance of the handles chosen + * is that they must all be unique. + */ +#define NV50_DISP_HANDLE_SYNCBUF 0xf0000000 +#define NV50_DISP_HANDLE_VRAM 0xf0000001 + +#define NV50_DISP_HANDLE_WNDW_CTX(kind) (0xfb000000 | kind) +#define NV50_DISP_HANDLE_CRC_CTX(head, i) (0xfc000000 | head->base.index << 1 | i) + +#endif /* !__NV50_KMS_HANDLES_H__ */ diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.c b/drivers/gpu/drm/nouveau/dispnv50/head.c index 8f6455697ba7..9a10ec267d1f 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head.c +++ b/drivers/gpu/drm/nouveau/dispnv50/head.c @@ -24,13 +24,17 @@ #include "core.h" #include "curs.h" #include "ovly.h" +#include "crc.h" #include <nvif/class.h> +#include <nvif/event.h> +#include <nvif/cl0046.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_vblank.h> #include "nouveau_connector.h" + void nv50_head_flush_clr(struct nv50_head *head, struct nv50_head_atom *asyh, bool flush) @@ -38,6 +42,7 @@ nv50_head_flush_clr(struct nv50_head *head, union nv50_head_atom_mask clr = { .mask = asyh->clr.mask & ~(flush ? 0 : asyh->set.mask), }; + if (clr.crc) nv50_crc_atomic_clr(head); if (clr.olut) head->func->olut_clr(head); if (clr.core) head->func->core_clr(head); if (clr.curs) head->func->curs_clr(head); @@ -61,6 +66,7 @@ nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh) if (asyh->set.ovly ) head->func->ovly (head, asyh); if (asyh->set.dither ) head->func->dither (head, asyh); if (asyh->set.procamp) head->func->procamp (head, asyh); + if (asyh->set.crc ) nv50_crc_atomic_set (head, asyh); if (asyh->set.or ) head->func->or (head, asyh); } @@ -84,18 +90,20 @@ nv50_head_atomic_check_dither(struct nv50_head_atom *armh, { u32 mode = 0x00; - if (asyc->dither.mode == DITHERING_MODE_AUTO) { - if (asyh->base.depth > asyh->or.bpc * 3) - mode = DITHERING_MODE_DYNAMIC2X2; - } else { - mode = asyc->dither.mode; - } + if (asyc->dither.mode) { + if (asyc->dither.mode == DITHERING_MODE_AUTO) { + if (asyh->base.depth > asyh->or.bpc * 3) + mode = DITHERING_MODE_DYNAMIC2X2; + } else { + mode = asyc->dither.mode; + } - if (asyc->dither.depth == DITHERING_DEPTH_AUTO) { - if (asyh->or.bpc >= 8) - mode |= DITHERING_DEPTH_8BPC; - } else { - mode |= asyc->dither.depth; + if (asyc->dither.depth == DITHERING_DEPTH_AUTO) { + if (asyh->or.bpc >= 8) + mode |= DITHERING_DEPTH_8BPC; + } else { + mode |= asyc->dither.depth; + } } asyh->dither.enable = mode; @@ -311,7 +319,7 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) struct nouveau_conn_atom *asyc = NULL; struct drm_connector_state *conns; struct drm_connector *conn; - int i; + int i, ret; NV_ATOMIC(drm, "%s atomic_check %d\n", crtc->name, asyh->state.active); if (asyh->state.active) { @@ -406,6 +414,10 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) asyh->set.curs = asyh->curs.visible; } + ret = nv50_crc_atomic_check_head(head, asyh, armh); + if (ret) + return ret; + if (asyh->clr.mask || asyh->set.mask) nv50_atom(asyh->state.state)->lock_core = true; return 0; @@ -444,6 +456,7 @@ nv50_head_atomic_duplicate_state(struct drm_crtc *crtc) asyh->ovly = armh->ovly; asyh->dither = armh->dither; asyh->procamp = armh->procamp; + asyh->crc = armh->crc; asyh->or = armh->or; asyh->dp = armh->dp; asyh->clr.mask = 0; @@ -465,10 +478,18 @@ nv50_head_reset(struct drm_crtc *crtc) __drm_atomic_helper_crtc_reset(crtc, &asyh->state); } +static int +nv50_head_late_register(struct drm_crtc *crtc) +{ + return nv50_head_crc_late_register(nv50_head(crtc)); +} + static void nv50_head_destroy(struct drm_crtc *crtc) { struct nv50_head *head = nv50_head(crtc); + + nvif_notify_fini(&head->base.vblank); nv50_lut_fini(&head->olut); drm_crtc_cleanup(crtc); kfree(head); @@ -486,8 +507,38 @@ nv50_head_func = { .enable_vblank = nouveau_display_vblank_enable, .disable_vblank = nouveau_display_vblank_disable, .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, + .late_register = nv50_head_late_register, +}; + +static const struct drm_crtc_funcs +nvd9_head_func = { + .reset = nv50_head_reset, + .gamma_set = drm_atomic_helper_legacy_gamma_set, + .destroy = nv50_head_destroy, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = nv50_head_atomic_duplicate_state, + .atomic_destroy_state = nv50_head_atomic_destroy_state, + .enable_vblank = nouveau_display_vblank_enable, + .disable_vblank = nouveau_display_vblank_disable, + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, + .verify_crc_source = nv50_crc_verify_source, + .get_crc_sources = nv50_crc_get_sources, + .set_crc_source = nv50_crc_set_source, + .late_register = nv50_head_late_register, }; +static int nv50_head_vblank_handler(struct nvif_notify *notify) +{ + struct nouveau_crtc *nv_crtc = + container_of(notify, struct nouveau_crtc, vblank); + + if (drm_crtc_handle_vblank(&nv_crtc->base)) + nv50_crc_handle_vblank(nv50_head(&nv_crtc->base)); + + return NVIF_NOTIFY_KEEP; +} + struct nv50_head * nv50_head_create(struct drm_device *dev, int index) { @@ -495,7 +546,9 @@ nv50_head_create(struct drm_device *dev, int index) struct nv50_disp *disp = nv50_disp(dev); struct nv50_head *head; struct nv50_wndw *base, *ovly, *curs; + struct nouveau_crtc *nv_crtc; struct drm_crtc *crtc; + const struct drm_crtc_funcs *funcs; int ret; head = kzalloc(sizeof(*head), GFP_KERNEL); @@ -505,6 +558,11 @@ nv50_head_create(struct drm_device *dev, int index) head->func = disp->core->func->head; head->base.index = index; + if (disp->disp->object.oclass < GF110_DISP) + funcs = &nv50_head_func; + else + funcs = &nvd9_head_func; + if (disp->disp->object.oclass < GV100_DISP) { ret = nv50_base_new(drm, head->base.index, &base); ret = nv50_ovly_new(drm, head->base.index, &ovly); @@ -521,9 +579,10 @@ nv50_head_create(struct drm_device *dev, int index) return ERR_PTR(ret); } - crtc = &head->base.base; + nv_crtc = &head->base; + crtc = &nv_crtc->base; drm_crtc_init_with_planes(dev, crtc, &base->plane, &curs->plane, - &nv50_head_func, "head-%d", head->base.index); + funcs, "head-%d", head->base.index); drm_crtc_helper_add(crtc, &nv50_head_help); /* Keep the legacy gamma size at 256 to avoid compatibility issues */ drm_mode_crtc_set_gamma_size(crtc, 256); @@ -539,5 +598,16 @@ nv50_head_create(struct drm_device *dev, int index) } } + ret = nvif_notify_init(&disp->disp->object, nv50_head_vblank_handler, + false, NV04_DISP_NTFY_VBLANK, + &(struct nvif_notify_head_req_v0) { + .head = nv_crtc->index, + }, + sizeof(struct nvif_notify_head_req_v0), + sizeof(struct nvif_notify_head_rep_v0), + &nv_crtc->vblank); + if (ret) + return ERR_PTR(ret); + return head; } diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.h b/drivers/gpu/drm/nouveau/dispnv50/head.h index c32b27cdaefc..30501ad1824e 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head.h +++ b/drivers/gpu/drm/nouveau/dispnv50/head.h @@ -1,22 +1,28 @@ #ifndef __NV50_KMS_HEAD_H__ #define __NV50_KMS_HEAD_H__ #define nv50_head(c) container_of((c), struct nv50_head, base.base) +#include <linux/workqueue.h> + #include "disp.h" #include "atom.h" +#include "crc.h" #include "lut.h" #include "nouveau_crtc.h" +#include "nouveau_encoder.h" struct nv50_head { const struct nv50_head_func *func; struct nouveau_crtc base; + struct nv50_crc crc; struct nv50_lut olut; struct nv50_msto *msto; }; struct nv50_head *nv50_head_create(struct drm_device *, int index); -void nv50_head_flush_set(struct nv50_head *, struct nv50_head_atom *); -void nv50_head_flush_clr(struct nv50_head *, struct nv50_head_atom *, bool y); +void nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh); +void nv50_head_flush_clr(struct nv50_head *head, + struct nv50_head_atom *asyh, bool flush); struct nv50_head_func { void (*view)(struct nv50_head *, struct nv50_head_atom *); @@ -40,6 +46,7 @@ struct nv50_head_func { void (*dither)(struct nv50_head *, struct nv50_head_atom *); void (*procamp)(struct nv50_head *, struct nv50_head_atom *); void (*or)(struct nv50_head *, struct nv50_head_atom *); + void (*static_wndw_map)(struct nv50_head *, struct nv50_head_atom *); }; extern const struct nv50_head_func head507d; @@ -86,6 +93,7 @@ int headc37d_curs_format(struct nv50_head *, struct nv50_wndw_atom *, void headc37d_curs_set(struct nv50_head *, struct nv50_head_atom *); void headc37d_curs_clr(struct nv50_head *); void headc37d_dither(struct nv50_head *, struct nv50_head_atom *); +void headc37d_static_wndw_map(struct nv50_head *, struct nv50_head_atom *); extern const struct nv50_head_func headc57d; #endif diff --git a/drivers/gpu/drm/nouveau/dispnv50/head907d.c b/drivers/gpu/drm/nouveau/dispnv50/head907d.c index 3002ec23d7a6..63a0b45d96d6 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head907d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/head907d.c @@ -19,8 +19,15 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ +#include <drm/drm_connector.h> +#include <drm/drm_mode_config.h> +#include <drm/drm_vblank.h> +#include "nouveau_drv.h" +#include "nouveau_bios.h" +#include "nouveau_connector.h" #include "head.h" #include "core.h" +#include "crc.h" void head907d_or(struct nv50_head *head, struct nv50_head_atom *asyh) @@ -29,9 +36,10 @@ head907d_or(struct nv50_head *head, struct nv50_head_atom *asyh) u32 *push; if ((push = evo_wait(core, 3))) { evo_mthd(push, 0x0404 + (head->base.index * 0x300), 2); - evo_data(push, 0x00000001 | asyh->or.depth << 6 | - asyh->or.nvsync << 4 | - asyh->or.nhsync << 3); + evo_data(push, asyh->or.depth << 6 | + asyh->or.nvsync << 4 | + asyh->or.nhsync << 3 | + asyh->or.crc_raster); evo_data(push, 0x31ec6000 | head->base.index << 25 | asyh->mode.interlace); evo_kick(push, core); diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc37d.c b/drivers/gpu/drm/nouveau/dispnv50/headc37d.c index 4a9a32b89f74..35fcdf8825b5 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/headc37d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/headc37d.c @@ -27,26 +27,29 @@ static void headc37d_or(struct nv50_head *head, struct nv50_head_atom *asyh) { struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + u8 depth; u32 *push; + if ((push = evo_wait(core, 2))) { /*XXX: This is a dirty hack until OR depth handling is * improved later for deep colour etc. */ switch (asyh->or.depth) { - case 6: asyh->or.depth = 5; break; - case 5: asyh->or.depth = 4; break; - case 2: asyh->or.depth = 1; break; - case 0: asyh->or.depth = 4; break; + case 6: depth = 5; break; + case 5: depth = 4; break; + case 2: depth = 1; break; + case 0: depth = 4; break; default: + depth = asyh->or.depth; WARN_ON(1); break; } evo_mthd(push, 0x2004 + (head->base.index * 0x400), 1); - evo_data(push, 0x00000001 | - asyh->or.depth << 4 | + evo_data(push, depth << 4 | asyh->or.nvsync << 3 | - asyh->or.nhsync << 2); + asyh->or.nhsync << 2 | + asyh->or.crc_raster); evo_kick(push, core); } } @@ -201,6 +204,15 @@ headc37d_view(struct nv50_head *head, struct nv50_head_atom *asyh) } } +void +headc37d_static_wndw_map(struct nv50_head *head, struct nv50_head_atom *asyh) +{ + int i, end; + + for (i = head->base.index * 2, end = i + 2; i < end; i++) + asyh->wndw.owned |= BIT(i); +} + const struct nv50_head_func headc37d = { .view = headc37d_view, @@ -216,4 +228,5 @@ headc37d = { .dither = headc37d_dither, .procamp = headc37d_procamp, .or = headc37d_or, + .static_wndw_map = headc37d_static_wndw_map, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c index 859131a8bc3c..c7d04dd935fd 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c @@ -27,26 +27,30 @@ static void headc57d_or(struct nv50_head *head, struct nv50_head_atom *asyh) { struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + u8 depth; u32 *push; + if ((push = evo_wait(core, 2))) { /*XXX: This is a dirty hack until OR depth handling is * improved later for deep colour etc. */ switch (asyh->or.depth) { - case 6: asyh->or.depth = 5; break; - case 5: asyh->or.depth = 4; break; - case 2: asyh->or.depth = 1; break; - case 0: asyh->or.depth = 4; break; + case 6: depth = 5; break; + case 5: depth = 4; break; + case 2: depth = 1; break; + case 0: depth = 4; break; default: + depth = asyh->or.depth; WARN_ON(1); break; } evo_mthd(push, 0x2004 + (head->base.index * 0x400), 1); - evo_data(push, 0xfc000001 | - asyh->or.depth << 4 | + evo_data(push, 0xfc000000 | + depth << 4 | asyh->or.nvsync << 3 | - asyh->or.nhsync << 2); + asyh->or.nhsync << 2 | + asyh->or.crc_raster); evo_kick(push, core); } } @@ -208,4 +212,6 @@ headc57d = { .dither = headc37d_dither, .procamp = headc57d_procamp, .or = headc57d_or, + /* TODO: flexible window mappings */ + .static_wndw_map = headc37d_static_wndw_map, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/ovly507e.c b/drivers/gpu/drm/nouveau/dispnv50/ovly507e.c index 8ccd96113bad..4cce1078140a 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/ovly507e.c +++ b/drivers/gpu/drm/nouveau/dispnv50/ovly507e.c @@ -186,7 +186,7 @@ ovly507e_new_(const struct nv50_wndw_func *func, const u32 *format, ret = nv50_dmac_create(&drm->client.device, &disp->disp->object, &oclass, 0, &args, sizeof(args), - disp->sync->bo.offset, &wndw->wndw); + disp->sync->offset, &wndw->wndw); if (ret) { NV_ERROR(drm, "ovly%04x allocation failed: %d\n", oclass, ret); return ret; diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.c b/drivers/gpu/drm/nouveau/dispnv50/wndw.c index 99b9b681736d..293ccfdba17e 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/wndw.c +++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.c @@ -21,6 +21,7 @@ */ #include "wndw.h" #include "wimm.h" +#include "handles.h" #include <nvif/class.h> #include <nvif/cl0002.h> @@ -59,7 +60,7 @@ nv50_wndw_ctxdma_new(struct nv50_wndw *wndw, struct drm_framebuffer *fb) int ret; nouveau_framebuffer_get_layout(fb, &unused, &kind); - handle = 0xfb000000 | kind; + handle = NV50_DISP_HANDLE_WNDW_CTX(kind); list_for_each_entry(ctxdma, &wndw->ctxdma.list, head) { if (ctxdma->object.handle == handle) @@ -526,7 +527,7 @@ nv50_wndw_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state) } asyw->state.fence = dma_resv_get_excl_rcu(nvbo->bo.base.resv); - asyw->image.offset[0] = nvbo->bo.offset; + asyw->image.offset[0] = nvbo->offset; if (wndw->func->prepare) { asyh = nv50_head_atom_get(asyw->state.state, asyw->state.crtc); diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c b/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c index b92dc3461bbd..bb84e4d54a33 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c +++ b/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c @@ -298,7 +298,7 @@ wndwc37e_new_(const struct nv50_wndw_func *func, struct nouveau_drm *drm, ret = nv50_dmac_create(&drm->client.device, &disp->disp->object, &oclass, 0, &args, sizeof(args), - disp->sync->bo.offset, &wndw->wndw); + disp->sync->offset, &wndw->wndw); if (ret) { NV_ERROR(drm, "qndw%04x allocation failed: %d\n", oclass, ret); return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c index 72c91991b96a..5b2406950e53 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.c +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -558,13 +558,13 @@ nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS) if (drm->agp.bridge) { args.target = NV_DMA_V0_TARGET_AGP; args.access = NV_DMA_V0_ACCESS_RDWR; - args.start += drm->agp.base + chan->ntfy->bo.offset; - args.limit += drm->agp.base + chan->ntfy->bo.offset; + args.start += drm->agp.base + chan->ntfy->offset; + args.limit += drm->agp.base + chan->ntfy->offset; } else { args.target = NV_DMA_V0_TARGET_VM; args.access = NV_DMA_V0_ACCESS_RDWR; - args.start += chan->ntfy->bo.offset; - args.limit += chan->ntfy->bo.offset; + args.start += chan->ntfy->offset; + args.limit += chan->ntfy->offset; } client->route = NVDRM_OBJECT_ABI16; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index c40f127de3d0..4ccf937df0d0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -655,13 +655,12 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, switch (type) { case TTM_PL_SYSTEM: - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = 0; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; break; case TTM_PL_VRAM: - man->flags = TTM_MEMTYPE_FLAG_FIXED | - TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = TTM_MEMTYPE_FLAG_FIXED; man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; man->default_caching = TTM_PL_FLAG_WC; @@ -675,7 +674,6 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, } man->func = &nouveau_vram_manager; - man->io_reserve_fastpath = false; man->use_io_reserve_lru = true; } else { man->func = &ttm_bo_manager_func; @@ -691,13 +689,12 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, man->func = &ttm_bo_manager_func; if (drm->agp.bridge) { - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = 0; man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; man->default_caching = TTM_PL_FLAG_WC; } else { - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | - TTM_MEMTYPE_FLAG_CMA; + man->flags = 0; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; } @@ -1317,6 +1314,14 @@ nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, bool evict, nouveau_vma_unmap(vma); } } + + if (new_reg) { + if (new_reg->mm_node) + nvbo->offset = (new_reg->start << PAGE_SHIFT); + else + nvbo->offset = 0; + } + } static int @@ -1431,7 +1436,6 @@ nouveau_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp) static int nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *reg) { - struct ttm_mem_type_manager *man = &bdev->man[reg->mem_type]; struct nouveau_drm *drm = nouveau_bdev(bdev); struct nvkm_device *device = nvxx_device(&drm->client.device); struct nouveau_mem *mem = nouveau_mem(reg); @@ -1441,8 +1445,7 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *reg) reg->bus.size = reg->num_pages << PAGE_SHIFT; reg->bus.base = 0; reg->bus.is_iomem = false; - if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) - return -EINVAL; + switch (reg->mem_type) { case TTM_PL_SYSTEM: /* System memory */ @@ -1497,8 +1500,6 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *reg) if (ret != 1) { if (WARN_ON(ret == 0)) return -EINVAL; - if (ret == -ENOSPC) - return -EAGAIN; return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h index 38f9d8350963..e944b4aa5547 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.h +++ b/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -24,6 +24,9 @@ struct nouveau_bo { int pbbo_index; bool validate_mapped; + /* GPU address space is independent of CPU word size */ + uint64_t offset; + struct list_head vma_list; unsigned contig:1; diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c index d9381a053169..3d71dfcb2fde 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.c +++ b/drivers/gpu/drm/nouveau/nouveau_chan.c @@ -162,7 +162,7 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nvif_device *device, * pushbuf lives in, this is because the GEM code requires that * we be able to call out to other (indirect) push buffers */ - chan->push.addr = chan->push.buffer->bo.offset; + chan->push.addr = chan->push.buffer->offset; if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) { ret = nouveau_vma_new(chan->push.buffer, chan->vmm, diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 901ac55506d6..657554cf011e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -44,15 +44,7 @@ #include <nvif/class.h> #include <nvif/cl0046.h> #include <nvif/event.h> - -static int -nouveau_display_vblank_handler(struct nvif_notify *notify) -{ - struct nouveau_crtc *nv_crtc = - container_of(notify, typeof(*nv_crtc), vblank); - drm_crtc_handle_vblank(&nv_crtc->base); - return NVIF_NOTIFY_KEEP; -} +#include <dispnv50/crc.h> int nouveau_display_vblank_enable(struct drm_crtc *crtc) @@ -136,50 +128,6 @@ nouveau_display_scanoutpos(struct drm_crtc *crtc, stime, etime); } -static void -nouveau_display_vblank_fini(struct drm_device *dev) -{ - struct drm_crtc *crtc; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - nvif_notify_fini(&nv_crtc->vblank); - } -} - -static int -nouveau_display_vblank_init(struct drm_device *dev) -{ - struct nouveau_display *disp = nouveau_display(dev); - struct drm_crtc *crtc; - int ret; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - ret = nvif_notify_init(&disp->disp.object, - nouveau_display_vblank_handler, false, - NV04_DISP_NTFY_VBLANK, - &(struct nvif_notify_head_req_v0) { - .head = nv_crtc->index, - }, - sizeof(struct nvif_notify_head_req_v0), - sizeof(struct nvif_notify_head_rep_v0), - &nv_crtc->vblank); - if (ret) { - nouveau_display_vblank_fini(dev); - return ret; - } - } - - ret = drm_vblank_init(dev, dev->mode_config.num_crtc); - if (ret) { - nouveau_display_vblank_fini(dev); - return ret; - } - - return 0; -} - static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = { .destroy = drm_gem_fb_destroy, .create_handle = drm_gem_fb_create_handle, @@ -705,9 +653,12 @@ nouveau_display_create(struct drm_device *dev) drm_mode_config_reset(dev); if (dev->mode_config.num_crtc) { - ret = nouveau_display_vblank_init(dev); + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); if (ret) goto vblank_err; + + if (disp->disp.object.oclass >= NV50_DISP) + nv50_crc_init(dev); } INIT_WORK(&drm->hpd_work, nouveau_display_hpd_work); @@ -734,7 +685,6 @@ nouveau_display_destroy(struct drm_device *dev) #ifdef CONFIG_ACPI unregister_acpi_notifier(&nouveau_drm(dev)->acpi_nb); #endif - nouveau_display_vblank_fini(dev); drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c b/drivers/gpu/drm/nouveau/nouveau_dmem.c index e5c230d9ae24..af870671195a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dmem.c +++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c @@ -100,7 +100,7 @@ unsigned long nouveau_dmem_page_addr(struct page *page) unsigned long off = (page_to_pfn(page) << PAGE_SHIFT) - chunk->pagemap.res.start; - return chunk->bo->bo.offset + off; + return chunk->bo->offset + off; } static void nouveau_dmem_page_free(struct page *page) diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 3d11b84d4cf9..f9f5a13fdb80 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -393,7 +393,7 @@ nouveau_fbcon_create(struct drm_fb_helper *helper, /* To allow resizeing without swapping buffers */ NV_INFO(drm, "allocated %dx%d fb: 0x%llx, bo %p\n", - fb->width, fb->height, nvbo->bo.offset, nvbo); + fb->width, fb->height, nvbo->offset, nvbo); vga_switcheroo_client_fb_set(dev->pdev, info); return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index db61f3db96ea..63b832585390 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -234,7 +234,7 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem, rep->domain = NOUVEAU_GEM_DOMAIN_GART; else rep->domain = NOUVEAU_GEM_DOMAIN_VRAM; - rep->offset = nvbo->bo.offset; + rep->offset = nvbo->offset; if (vmm->vmm.object.oclass >= NVIF_CLASS_VMM_NV50) { vma = nouveau_vma_find(nvbo, vmm); if (!vma) @@ -518,7 +518,7 @@ validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli, } if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) { - if (nvbo->bo.offset == b->presumed.offset && + if (nvbo->offset == b->presumed.offset && ((nvbo->bo.mem.mem_type == TTM_PL_VRAM && b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) || (nvbo->bo.mem.mem_type == TTM_PL_TT && @@ -529,7 +529,7 @@ validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli, b->presumed.domain = NOUVEAU_GEM_DOMAIN_GART; else b->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM; - b->presumed.offset = nvbo->bo.offset; + b->presumed.offset = nvbo->offset; b->presumed.valid = 0; relocs++; } @@ -807,7 +807,7 @@ revalidate: struct nouveau_bo *nvbo = (void *)(unsigned long) bo[push[i].bo_index].user_priv; - OUT_RING(chan, (nvbo->bo.offset + push[i].offset) | 2); + OUT_RING(chan, (nvbo->offset + push[i].offset) | 2); OUT_RING(chan, 0); } } else { @@ -842,7 +842,7 @@ revalidate: } OUT_RING(chan, 0x20000000 | - (nvbo->bo.offset + push[i].offset)); + (nvbo->offset + push[i].offset)); OUT_RING(chan, 0); for (j = 0; j < NOUVEAU_DMA_SKIPS; j++) OUT_RING(chan, 0); diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c index 7ca0a2498532..e89ea052cf71 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ttm.c +++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c @@ -75,10 +75,6 @@ nouveau_vram_manager_new(struct ttm_mem_type_manager *man, ret = nouveau_mem_vram(reg, nvbo->contig, nvbo->page); if (ret) { nouveau_mem_del(reg); - if (ret == -ENOSPC) { - reg->mm_node = NULL; - return 0; - } return ret; } @@ -139,10 +135,6 @@ nv04_gart_manager_new(struct ttm_mem_type_manager *man, reg->num_pages << PAGE_SHIFT, &mem->vma[0]); if (ret) { nouveau_mem_del(reg); - if (ret == -ENOSPC) { - reg->mm_node = NULL; - return 0; - } return ret; } diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index fce7e944a280..6d40914675da 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -697,14 +697,16 @@ static int omap_crtc_atomic_get_property(struct drm_crtc *crtc, static void omap_crtc_reset(struct drm_crtc *crtc) { + struct omap_crtc_state *state; + if (crtc->state) __drm_atomic_helper_crtc_destroy_state(crtc->state); kfree(crtc->state); - crtc->state = kzalloc(sizeof(struct omap_crtc_state), GFP_KERNEL); - if (crtc->state) - crtc->state->crtc = crtc; + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_crtc_reset(crtc, &state->base); } static struct drm_crtc_state * diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 242d28281784..4526967978b7 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -595,7 +595,6 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev) { const struct soc_device_attribute *soc; struct drm_device *ddev; - unsigned int i; int ret; DBG("%s", dev_name(dev)); @@ -642,9 +641,6 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev) goto err_cleanup_modeset; } - for (i = 0; i < priv->num_pipes; i++) - drm_crtc_vblank_off(priv->pipes[i].crtc); - omap_fbdev_init(ddev); drm_kms_helper_poll_init(ddev); diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 39055c1f0e2f..de2f2a452be5 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -283,19 +283,6 @@ config DRM_PANEL_RAYDIUM_RM68200 Say Y here if you want to enable support for Raydium RM68200 720x1280 DSI video mode panel. -config DRM_PANEL_ROCKTECH_JH057N00900 - tristate "Rocktech JH057N00900 MIPI touchscreen 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 Rocktech JH057N00900 - MIPI DSI panel as e.g. used in the Librem 5 devkit. It has a - resolution of 720x1440 pixels, a built in backlight and touch - controller. - Touch input support is provided by the goodix driver and needs to be - selected separately. - config DRM_PANEL_RONBO_RB070D30 tristate "Ronbo Electronics RB070D30 panel" depends on OF @@ -395,6 +382,19 @@ config DRM_PANEL_SITRONIX_ST7701 ST7701 controller for 480X864 LCD panels with MIPI/RGB/SPI system interfaces. +config DRM_PANEL_SITRONIX_ST7703 + tristate "Sitronix ST7703 based MIPI touchscreen panels" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for Sitronix ST7703 based + panels, souch as Rocktech JH057N00900 MIPI DSI panel as e.g. used in + the Librem 5 devkit. It has a resolution of 720x1440 pixels, a built + in backlight and touch controller. + Touch input support is provided by the goodix driver and needs to be + selected separately. + config DRM_PANEL_SITRONIX_ST7789V tristate "Sitronix ST7789V panel" depends on OF && SPI diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index de74f282c433..e45ceac6286f 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -27,7 +27,6 @@ obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67191) += panel-raydium-rm67191.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o -obj-$(CONFIG_DRM_PANEL_ROCKTECH_JH057N00900) += panel-rocktech-jh057n00900.o obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o @@ -41,6 +40,7 @@ obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o +obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o obj-$(CONFIG_DRM_PANEL_SONY_ACX424AKP) += panel-sony-acx424akp.o obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o diff --git a/drivers/gpu/drm/panel/panel-elida-kd35t133.c b/drivers/gpu/drm/panel/panel-elida-kd35t133.c index 2338d22e23b1..e9675514d77b 100644 --- a/drivers/gpu/drm/panel/panel-elida-kd35t133.c +++ b/drivers/gpu/drm/panel/panel-elida-kd35t133.c @@ -52,9 +52,9 @@ static inline struct kd35t133 *panel_to_kd35t133(struct drm_panel *panel) } #define dsi_dcs_write_seq(dsi, cmd, seq...) do { \ - static const u8 d[] = { seq }; \ + static const u8 b[] = { cmd, seq }; \ int ret; \ - ret = mipi_dsi_dcs_write(dsi, cmd, d, ARRAY_SIZE(d)); \ + ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \ if (ret < 0) \ return ret; \ } while (0) diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c index 5a7a31c8513e..eaa9da3ebbea 100644 --- a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c +++ b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c @@ -246,9 +246,9 @@ struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel) } #define dsi_dcs_write_seq(dsi, cmd, seq...) do { \ - static const u8 d[] = { seq }; \ + static const u8 b[] = { cmd, seq }; \ int ret; \ - ret = mipi_dsi_dcs_write(dsi, cmd, d, ARRAY_SIZE(d)); \ + ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \ if (ret < 0) \ return ret; \ } while (0) diff --git a/drivers/gpu/drm/panel/panel-novatek-nt39016.c b/drivers/gpu/drm/panel/panel-novatek-nt39016.c index 79be3dc4e817..91df050ba3f6 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt39016.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt39016.c @@ -357,7 +357,7 @@ static const struct nt39016_panel_info kd035g6_info = { .width_mm = 71, .height_mm = 53, .bus_format = MEDIA_BUS_FMT_RGB888_1X24, - .bus_flags = DRM_BUS_FLAG_PIXDATA_NEGEDGE, + .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE, }; static const struct of_device_id nt39016_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c b/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c index 11b3d01aca56..83e5aa47f0d6 100644 --- a/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c +++ b/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> */ diff --git a/drivers/gpu/drm/panel/panel-raydium-rm67191.c b/drivers/gpu/drm/panel/panel-raydium-rm67191.c index d001c52e0ca9..57ff2b1f6361 100644 --- a/drivers/gpu/drm/panel/panel-raydium-rm67191.c +++ b/drivers/gpu/drm/panel/panel-raydium-rm67191.c @@ -192,7 +192,7 @@ static const u32 rad_bus_formats[] = { }; static const u32 rad_bus_flags = DRM_BUS_FLAG_DE_LOW | - DRM_BUS_FLAG_PIXDATA_NEGEDGE; + DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE; struct rad_panel { struct drm_panel panel; diff --git a/drivers/gpu/drm/panel/panel-rocktech-jh057n00900.c b/drivers/gpu/drm/panel/panel-rocktech-jh057n00900.c deleted file mode 100644 index da4e373291f9..000000000000 --- a/drivers/gpu/drm/panel/panel-rocktech-jh057n00900.c +++ /dev/null @@ -1,423 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Rockteck jh057n00900 5.5" MIPI-DSI panel driver - * - * Copyright (C) Purism SPC 2019 - */ - -#include <linux/debugfs.h> -#include <linux/delay.h> -#include <linux/gpio/consumer.h> -#include <linux/media-bus-format.h> -#include <linux/mod_devicetable.h> -#include <linux/module.h> -#include <linux/regulator/consumer.h> - -#include <video/display_timing.h> -#include <video/mipi_display.h> - -#include <drm/drm_mipi_dsi.h> -#include <drm/drm_modes.h> -#include <drm/drm_panel.h> -#include <drm/drm_print.h> - -#define DRV_NAME "panel-rocktech-jh057n00900" - -/* Manufacturer specific Commands send via DSI */ -#define ST7703_CMD_ALL_PIXEL_OFF 0x22 -#define ST7703_CMD_ALL_PIXEL_ON 0x23 -#define ST7703_CMD_SETDISP 0xB2 -#define ST7703_CMD_SETRGBIF 0xB3 -#define ST7703_CMD_SETCYC 0xB4 -#define ST7703_CMD_SETBGP 0xB5 -#define ST7703_CMD_SETVCOM 0xB6 -#define ST7703_CMD_SETOTP 0xB7 -#define ST7703_CMD_SETPOWER_EXT 0xB8 -#define ST7703_CMD_SETEXTC 0xB9 -#define ST7703_CMD_SETMIPI 0xBA -#define ST7703_CMD_SETVDC 0xBC -#define ST7703_CMD_UNKNOWN0 0xBF -#define ST7703_CMD_SETSCR 0xC0 -#define ST7703_CMD_SETPOWER 0xC1 -#define ST7703_CMD_SETPANEL 0xCC -#define ST7703_CMD_SETGAMMA 0xE0 -#define ST7703_CMD_SETEQ 0xE3 -#define ST7703_CMD_SETGIP1 0xE9 -#define ST7703_CMD_SETGIP2 0xEA - -struct jh057n { - struct device *dev; - struct drm_panel panel; - struct gpio_desc *reset_gpio; - struct regulator *vcc; - struct regulator *iovcc; - bool prepared; - - struct dentry *debugfs; -}; - -static inline struct jh057n *panel_to_jh057n(struct drm_panel *panel) -{ - return container_of(panel, struct jh057n, panel); -} - -#define dsi_generic_write_seq(dsi, seq...) do { \ - static const u8 d[] = { seq }; \ - int ret; \ - ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)); \ - if (ret < 0) \ - return ret; \ - } while (0) - -static int jh057n_init_sequence(struct jh057n *ctx) -{ - struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); - struct device *dev = ctx->dev; - int ret; - - /* - * Init sequence was supplied by the panel vendor. Most of the commands - * resemble the ST7703 but the number of parameters often don't match - * so it's likely a clone. - */ - dsi_generic_write_seq(dsi, ST7703_CMD_SETEXTC, - 0xF1, 0x12, 0x83); - dsi_generic_write_seq(dsi, ST7703_CMD_SETRGBIF, - 0x10, 0x10, 0x05, 0x05, 0x03, 0xFF, 0x00, 0x00, - 0x00, 0x00); - dsi_generic_write_seq(dsi, ST7703_CMD_SETSCR, - 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70, - 0x00); - dsi_generic_write_seq(dsi, ST7703_CMD_SETVDC, 0x4E); - dsi_generic_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0B); - dsi_generic_write_seq(dsi, ST7703_CMD_SETCYC, 0x80); - dsi_generic_write_seq(dsi, ST7703_CMD_SETDISP, 0xF0, 0x12, 0x30); - dsi_generic_write_seq(dsi, ST7703_CMD_SETEQ, - 0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00, - 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10); - dsi_generic_write_seq(dsi, ST7703_CMD_SETBGP, 0x08, 0x08); - msleep(20); - - dsi_generic_write_seq(dsi, ST7703_CMD_SETVCOM, 0x3F, 0x3F); - dsi_generic_write_seq(dsi, ST7703_CMD_UNKNOWN0, 0x02, 0x11, 0x00); - dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP1, - 0x82, 0x10, 0x06, 0x05, 0x9E, 0x0A, 0xA5, 0x12, - 0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38, - 0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64, - 0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); - dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP2, - 0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13, - 0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0A, - 0xA5, 0x00, 0x00, 0x00, 0x00); - dsi_generic_write_seq(dsi, ST7703_CMD_SETGAMMA, - 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, 0x37, - 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, 0x11, - 0x18, 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, - 0x37, 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, - 0x11, 0x18); - msleep(20); - - ret = mipi_dsi_dcs_exit_sleep_mode(dsi); - if (ret < 0) { - DRM_DEV_ERROR(dev, "Failed to exit sleep mode: %d\n", ret); - return ret; - } - /* Panel is operational 120 msec after reset */ - msleep(60); - ret = mipi_dsi_dcs_set_display_on(dsi); - if (ret) - return ret; - - DRM_DEV_DEBUG_DRIVER(dev, "Panel init sequence done\n"); - return 0; -} - -static int jh057n_enable(struct drm_panel *panel) -{ - struct jh057n *ctx = panel_to_jh057n(panel); - int ret; - - ret = jh057n_init_sequence(ctx); - if (ret < 0) { - DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n", - ret); - return ret; - } - - return 0; -} - -static int jh057n_disable(struct drm_panel *panel) -{ - struct jh057n *ctx = panel_to_jh057n(panel); - struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); - - return mipi_dsi_dcs_set_display_off(dsi); -} - -static int jh057n_unprepare(struct drm_panel *panel) -{ - struct jh057n *ctx = panel_to_jh057n(panel); - - if (!ctx->prepared) - return 0; - - regulator_disable(ctx->iovcc); - regulator_disable(ctx->vcc); - ctx->prepared = false; - - return 0; -} - -static int jh057n_prepare(struct drm_panel *panel) -{ - struct jh057n *ctx = panel_to_jh057n(panel); - int ret; - - if (ctx->prepared) - return 0; - - DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n"); - ret = regulator_enable(ctx->vcc); - if (ret < 0) { - DRM_DEV_ERROR(ctx->dev, - "Failed to enable vcc supply: %d\n", ret); - return ret; - } - ret = regulator_enable(ctx->iovcc); - if (ret < 0) { - DRM_DEV_ERROR(ctx->dev, - "Failed to enable iovcc supply: %d\n", ret); - goto disable_vcc; - } - - gpiod_set_value_cansleep(ctx->reset_gpio, 1); - usleep_range(20, 40); - gpiod_set_value_cansleep(ctx->reset_gpio, 0); - msleep(20); - - ctx->prepared = true; - - return 0; - -disable_vcc: - regulator_disable(ctx->vcc); - return ret; -} - -static const struct drm_display_mode default_mode = { - .hdisplay = 720, - .hsync_start = 720 + 90, - .hsync_end = 720 + 90 + 20, - .htotal = 720 + 90 + 20 + 20, - .vdisplay = 1440, - .vsync_start = 1440 + 20, - .vsync_end = 1440 + 20 + 4, - .vtotal = 1440 + 20 + 4 + 12, - .clock = 75276, - .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, - .width_mm = 65, - .height_mm = 130, -}; - -static int jh057n_get_modes(struct drm_panel *panel, - struct drm_connector *connector) -{ - struct jh057n *ctx = panel_to_jh057n(panel); - struct drm_display_mode *mode; - - mode = drm_mode_duplicate(connector->dev, &default_mode); - if (!mode) { - DRM_DEV_ERROR(ctx->dev, "Failed to add mode %ux%u@%u\n", - default_mode.hdisplay, default_mode.vdisplay, - drm_mode_vrefresh(&default_mode)); - return -ENOMEM; - } - - drm_mode_set_name(mode); - - mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; - connector->display_info.width_mm = mode->width_mm; - connector->display_info.height_mm = mode->height_mm; - drm_mode_probed_add(connector, mode); - - return 1; -} - -static const struct drm_panel_funcs jh057n_drm_funcs = { - .disable = jh057n_disable, - .unprepare = jh057n_unprepare, - .prepare = jh057n_prepare, - .enable = jh057n_enable, - .get_modes = jh057n_get_modes, -}; - -static int allpixelson_set(void *data, u64 val) -{ - struct jh057n *ctx = data; - struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); - - DRM_DEV_DEBUG_DRIVER(ctx->dev, "Setting all pixels on\n"); - dsi_generic_write_seq(dsi, ST7703_CMD_ALL_PIXEL_ON); - msleep(val * 1000); - /* Reset the panel to get video back */ - drm_panel_disable(&ctx->panel); - drm_panel_unprepare(&ctx->panel); - drm_panel_prepare(&ctx->panel); - drm_panel_enable(&ctx->panel); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(allpixelson_fops, NULL, - allpixelson_set, "%llu\n"); - -static void jh057n_debugfs_init(struct jh057n *ctx) -{ - ctx->debugfs = debugfs_create_dir(DRV_NAME, NULL); - - debugfs_create_file("allpixelson", 0600, ctx->debugfs, ctx, - &allpixelson_fops); -} - -static void jh057n_debugfs_remove(struct jh057n *ctx) -{ - debugfs_remove_recursive(ctx->debugfs); - ctx->debugfs = NULL; -} - -static int jh057n_probe(struct mipi_dsi_device *dsi) -{ - struct device *dev = &dsi->dev; - struct jh057n *ctx; - int ret; - - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(ctx->reset_gpio)) { - DRM_DEV_ERROR(dev, "cannot get reset gpio\n"); - return PTR_ERR(ctx->reset_gpio); - } - - mipi_dsi_set_drvdata(dsi, ctx); - - ctx->dev = dev; - - dsi->lanes = 4; - dsi->format = MIPI_DSI_FMT_RGB888; - dsi->mode_flags = MIPI_DSI_MODE_VIDEO | - MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; - - ctx->vcc = devm_regulator_get(dev, "vcc"); - if (IS_ERR(ctx->vcc)) { - ret = PTR_ERR(ctx->vcc); - if (ret != -EPROBE_DEFER) - DRM_DEV_ERROR(dev, - "Failed to request vcc regulator: %d\n", - ret); - return ret; - } - ctx->iovcc = devm_regulator_get(dev, "iovcc"); - if (IS_ERR(ctx->iovcc)) { - ret = PTR_ERR(ctx->iovcc); - if (ret != -EPROBE_DEFER) - DRM_DEV_ERROR(dev, - "Failed to request iovcc regulator: %d\n", - ret); - return ret; - } - - drm_panel_init(&ctx->panel, dev, &jh057n_drm_funcs, - DRM_MODE_CONNECTOR_DSI); - - ret = drm_panel_of_backlight(&ctx->panel); - if (ret) - return ret; - - drm_panel_add(&ctx->panel); - - ret = mipi_dsi_attach(dsi); - if (ret < 0) { - DRM_DEV_ERROR(dev, - "mipi_dsi_attach failed (%d). Is host ready?\n", - ret); - drm_panel_remove(&ctx->panel); - return ret; - } - - DRM_DEV_INFO(dev, "%ux%u@%u %ubpp dsi %udl - ready\n", - default_mode.hdisplay, default_mode.vdisplay, - drm_mode_vrefresh(&default_mode), - mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes); - - jh057n_debugfs_init(ctx); - return 0; -} - -static void jh057n_shutdown(struct mipi_dsi_device *dsi) -{ - struct jh057n *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = drm_panel_unprepare(&ctx->panel); - if (ret < 0) - DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n", - ret); - - ret = drm_panel_disable(&ctx->panel); - if (ret < 0) - DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n", - ret); -} - -static int jh057n_remove(struct mipi_dsi_device *dsi) -{ - struct jh057n *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - jh057n_shutdown(dsi); - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n", - ret); - - drm_panel_remove(&ctx->panel); - - jh057n_debugfs_remove(ctx); - - return 0; -} - -static const struct of_device_id jh057n_of_match[] = { - { .compatible = "rocktech,jh057n00900" }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, jh057n_of_match); - -static struct mipi_dsi_driver jh057n_driver = { - .probe = jh057n_probe, - .remove = jh057n_remove, - .shutdown = jh057n_shutdown, - .driver = { - .name = DRV_NAME, - .of_match_table = jh057n_of_match, - }, -}; -module_mipi_dsi_driver(jh057n_driver); - -MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>"); -MODULE_DESCRIPTION("DRM driver for Rocktech JH057N00900 MIPI DSI panel"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 95eb2122a767..88493538a147 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -549,6 +549,23 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc) panel_simple_parse_panel_timing_node(dev, panel, &dt); } + if (desc->connector_type == DRM_MODE_CONNECTOR_LVDS) { + /* Catch common mistakes for LVDS panels. */ + WARN_ON(desc->bus_flags & + ~(DRM_BUS_FLAG_DE_LOW | + DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_DATA_MSB_TO_LSB | + DRM_BUS_FLAG_DATA_LSB_TO_MSB)); + WARN_ON(desc->bus_format != MEDIA_BUS_FMT_RGB666_1X7X3_SPWG && + desc->bus_format != MEDIA_BUS_FMT_RGB888_1X7X4_SPWG && + desc->bus_format != MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA); + WARN_ON(desc->bus_format == MEDIA_BUS_FMT_RGB666_1X7X3_SPWG && + desc->bpc != 6); + WARN_ON((desc->bus_format == MEDIA_BUS_FMT_RGB888_1X7X4_SPWG || + desc->bus_format == MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA) && + desc->bpc != 8); + } + drm_panel_init(&panel->base, dev, &panel_simple_funcs, desc->connector_type); @@ -664,7 +681,7 @@ static const struct panel_desc armadeus_st0700_adapt = { .height = 86, }, .bus_format = MEDIA_BUS_FMT_RGB666_1X18, - .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE, }; static const struct drm_display_mode auo_b101aw03_mode = { @@ -687,6 +704,8 @@ static const struct panel_desc auo_b101aw03 = { .width = 223, .height = 125, }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, .connector_type = DRM_MODE_CONNECTOR_LVDS, }; @@ -773,6 +792,7 @@ static const struct drm_display_mode auo_b116xw03_mode = { .vsync_start = 768 + 10, .vsync_end = 768 + 10 + 12, .vtotal = 768 + 10 + 12 + 6, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, }; static const struct panel_desc auo_b116xw03 = { @@ -783,6 +803,12 @@ static const struct panel_desc auo_b116xw03 = { .width = 256, .height = 144, }, + .delay = { + .enable = 400, + }, + .bus_flags = DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE, + .bus_format = MEDIA_BUS_FMT_RGB666_1X18, + .connector_type = DRM_MODE_CONNECTOR_eDP, }; static const struct drm_display_mode auo_b133xtn01_mode = { @@ -1320,7 +1346,7 @@ static const struct panel_desc cdtech_s070pws19hp_fc21 = { .height = 86, }, .bus_format = MEDIA_BUS_FMT_RGB666_1X18, - .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE, .connector_type = DRM_MODE_CONNECTOR_DPI, }; @@ -1347,7 +1373,7 @@ static const struct panel_desc cdtech_s070swv29hg_dc44 = { .height = 86, }, .bus_format = MEDIA_BUS_FMT_RGB666_1X18, - .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE, .connector_type = DRM_MODE_CONNECTOR_DPI, }; @@ -1395,6 +1421,8 @@ static const struct panel_desc chunghwa_claa070wp03xg = { .width = 94, .height = 150, }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, .connector_type = DRM_MODE_CONNECTOR_LVDS, }; @@ -1418,6 +1446,8 @@ static const struct panel_desc chunghwa_claa101wa01a = { .width = 220, .height = 120, }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, .connector_type = DRM_MODE_CONNECTOR_LVDS, }; @@ -1441,6 +1471,8 @@ static const struct panel_desc chunghwa_claa101wb01 = { .width = 223, .height = 125, }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, .connector_type = DRM_MODE_CONNECTOR_LVDS, }; @@ -1552,7 +1584,7 @@ static const struct panel_desc edt_et035012dm6 = { .height = 52, }, .bus_format = MEDIA_BUS_FMT_RGB888_1X24, - .bus_flags = DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_PIXDATA_NEGEDGE, + .bus_flags = DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE, }; static const struct drm_display_mode edt_etm043080dh6gp_mode = { @@ -1631,7 +1663,7 @@ static const struct panel_desc edt_et057090dhu = { }, .bus_format = MEDIA_BUS_FMT_RGB666_1X18, .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, - .connector_type = DRM_MODE_CONNECTOR_LVDS, + .connector_type = DRM_MODE_CONNECTOR_DPI, }; static const struct drm_display_mode edt_etm0700g0dh6_mode = { @@ -1695,7 +1727,7 @@ static const struct panel_desc evervision_vgg804821 = { .height = 64, }, .bus_format = MEDIA_BUS_FMT_RGB888_1X24, - .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_NEGEDGE, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE, }; static const struct drm_display_mode foxlink_fl500wvr00_a0t_mode = { @@ -1721,29 +1753,43 @@ static const struct panel_desc foxlink_fl500wvr00_a0t = { .bus_format = MEDIA_BUS_FMT_RGB888_1X24, }; -static const struct drm_display_mode frida_frd350h54004_mode = { - .clock = 6000, - .hdisplay = 320, - .hsync_start = 320 + 44, - .hsync_end = 320 + 44 + 16, - .htotal = 320 + 44 + 16 + 20, - .vdisplay = 240, - .vsync_start = 240 + 2, - .vsync_end = 240 + 2 + 6, - .vtotal = 240 + 2 + 6 + 2, - .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC, +static const struct drm_display_mode frida_frd350h54004_modes[] = { + { /* 60 Hz */ + .clock = 6000, + .hdisplay = 320, + .hsync_start = 320 + 44, + .hsync_end = 320 + 44 + 16, + .htotal = 320 + 44 + 16 + 20, + .vdisplay = 240, + .vsync_start = 240 + 2, + .vsync_end = 240 + 2 + 6, + .vtotal = 240 + 2 + 6 + 2, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + }, + { /* 50 Hz */ + .clock = 5400, + .hdisplay = 320, + .hsync_start = 320 + 56, + .hsync_end = 320 + 56 + 16, + .htotal = 320 + 56 + 16 + 40, + .vdisplay = 240, + .vsync_start = 240 + 2, + .vsync_end = 240 + 2 + 6, + .vtotal = 240 + 2 + 6 + 2, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + }, }; static const struct panel_desc frida_frd350h54004 = { - .modes = &frida_frd350h54004_mode, - .num_modes = 1, + .modes = frida_frd350h54004_modes, + .num_modes = ARRAY_SIZE(frida_frd350h54004_modes), .bpc = 8, .size = { .width = 77, .height = 64, }, .bus_format = MEDIA_BUS_FMT_RGB888_1X24, - .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE, .connector_type = DRM_MODE_CONNECTOR_DPI, }; @@ -1814,7 +1860,7 @@ static const struct panel_desc giantplus_gpm940b0 = { .height = 45, }, .bus_format = MEDIA_BUS_FMT_RGB888_3X8, - .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_NEGEDGE, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE, }; static const struct display_timing hannstar_hsd070pww1_timing = { @@ -2114,6 +2160,8 @@ static const struct panel_desc innolux_n156bge_l21 = { .width = 344, .height = 193, }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, .connector_type = DRM_MODE_CONNECTOR_LVDS, }; @@ -2246,7 +2294,7 @@ static const struct panel_desc koe_tx26d202vm0bwa = { .disable = 1000, }, .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, - .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, .connector_type = DRM_MODE_CONNECTOR_LVDS, }; @@ -2338,7 +2386,7 @@ static const struct drm_display_mode lg_lb070wv8_mode = { static const struct panel_desc lg_lb070wv8 = { .modes = &lg_lb070wv8_mode, .num_modes = 1, - .bpc = 16, + .bpc = 8, .size = { .width = 151, .height = 91, @@ -2487,9 +2535,7 @@ static const struct panel_desc logictechno_lt170410_2whc = { .height = 136, }, .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, - .bus_flags = DRM_BUS_FLAG_DE_HIGH | - DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE | - DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, .connector_type = DRM_MODE_CONNECTOR_LVDS, }; @@ -2860,7 +2906,7 @@ static const struct panel_desc ortustech_com37h3m = { .height = 75, /* 74.88mm */ }, .bus_format = MEDIA_BUS_FMT_RGB888_1X24, - .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE | + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE | DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE, }; @@ -3061,6 +3107,8 @@ static const struct panel_desc samsung_ltn101nt05 = { .width = 223, .height = 125, }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, .connector_type = DRM_MODE_CONNECTOR_LVDS, }; @@ -3106,7 +3154,7 @@ static const struct panel_desc satoz_sat050at40h12r2 = { .width = 108, .height = 65, }, - .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, .connector_type = DRM_MODE_CONNECTOR_LVDS, }; @@ -3157,7 +3205,7 @@ static const struct panel_desc sharp_lq070y3dg3b = { .height = 91, /* 91.4mm */ }, .bus_format = MEDIA_BUS_FMT_RGB888_1X24, - .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE | + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE | DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE, }; @@ -3260,7 +3308,7 @@ static const struct panel_desc sharp_ls020b1dd01d = { }, .bus_format = MEDIA_BUS_FMT_RGB565_1X16, .bus_flags = DRM_BUS_FLAG_DE_HIGH - | DRM_BUS_FLAG_PIXDATA_NEGEDGE + | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE | DRM_BUS_FLAG_SHARP_SIGNALS, }; @@ -3308,7 +3356,7 @@ static const struct panel_desc starry_kr070pe2t = { }, .bus_format = MEDIA_BUS_FMT_RGB888_1X24, .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, - .connector_type = DRM_MODE_CONNECTOR_LVDS, + .connector_type = DRM_MODE_CONNECTOR_DPI, }; static const struct drm_display_mode starry_kr122ea0sra_mode = { @@ -3359,7 +3407,7 @@ static const struct panel_desc tfc_s9700rtwv43tr_01b = { .height = 90, }, .bus_format = MEDIA_BUS_FMT_RGB888_1X24, - .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE, }; static const struct display_timing tianma_tm070jdhg30_timing = { @@ -3448,7 +3496,7 @@ static const struct panel_desc ti_nspire_cx_lcd_panel = { .height = 49, }, .bus_format = MEDIA_BUS_FMT_RGB888_1X24, - .bus_flags = DRM_BUS_FLAG_PIXDATA_NEGEDGE, + .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE, }; static const struct drm_display_mode ti_nspire_classic_lcd_mode[] = { @@ -3477,7 +3525,7 @@ static const struct panel_desc ti_nspire_classic_lcd_panel = { }, /* This is the grayscale bus format */ .bus_format = MEDIA_BUS_FMT_Y8_1X8, - .bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE, + .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE, }; static const struct drm_display_mode toshiba_lt089ac29000_mode = { @@ -3500,7 +3548,7 @@ static const struct panel_desc toshiba_lt089ac29000 = { .height = 116, }, .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, - .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, .connector_type = DRM_MODE_CONNECTOR_LVDS, }; @@ -3606,7 +3654,7 @@ static const struct panel_desc vl050_8048nt_c01 = { .height = 76, }, .bus_format = MEDIA_BUS_FMT_RGB888_1X24, - .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE, }; static const struct drm_display_mode winstar_wf35ltiacd_mode = { diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7703.c b/drivers/gpu/drm/panel/panel-sitronix-st7703.c new file mode 100644 index 000000000000..8996ced2b721 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-sitronix-st7703.c @@ -0,0 +1,654 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for panels based on Sitronix ST7703 controller, souch as: + * + * - Rocktech jh057n00900 5.5" MIPI-DSI panel + * + * Copyright (C) Purism SPC 2019 + */ + +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/media-bus-format.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> + +#include <video/display_timing.h> +#include <video/mipi_display.h> + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> + +#define DRV_NAME "panel-sitronix-st7703" + +/* Manufacturer specific Commands send via DSI */ +#define ST7703_CMD_ALL_PIXEL_OFF 0x22 +#define ST7703_CMD_ALL_PIXEL_ON 0x23 +#define ST7703_CMD_SETDISP 0xB2 +#define ST7703_CMD_SETRGBIF 0xB3 +#define ST7703_CMD_SETCYC 0xB4 +#define ST7703_CMD_SETBGP 0xB5 +#define ST7703_CMD_SETVCOM 0xB6 +#define ST7703_CMD_SETOTP 0xB7 +#define ST7703_CMD_SETPOWER_EXT 0xB8 +#define ST7703_CMD_SETEXTC 0xB9 +#define ST7703_CMD_SETMIPI 0xBA +#define ST7703_CMD_SETVDC 0xBC +#define ST7703_CMD_UNKNOWN_BF 0xBF +#define ST7703_CMD_SETSCR 0xC0 +#define ST7703_CMD_SETPOWER 0xC1 +#define ST7703_CMD_SETPANEL 0xCC +#define ST7703_CMD_UNKNOWN_C6 0xC6 +#define ST7703_CMD_SETGAMMA 0xE0 +#define ST7703_CMD_SETEQ 0xE3 +#define ST7703_CMD_SETGIP1 0xE9 +#define ST7703_CMD_SETGIP2 0xEA + +struct st7703 { + struct device *dev; + struct drm_panel panel; + struct gpio_desc *reset_gpio; + struct regulator *vcc; + struct regulator *iovcc; + bool prepared; + + struct dentry *debugfs; + const struct st7703_panel_desc *desc; +}; + +struct st7703_panel_desc { + const struct drm_display_mode *mode; + unsigned int lanes; + unsigned long mode_flags; + enum mipi_dsi_pixel_format format; + int (*init_sequence)(struct st7703 *ctx); +}; + +static inline struct st7703 *panel_to_st7703(struct drm_panel *panel) +{ + return container_of(panel, struct st7703, panel); +} + +#define dsi_generic_write_seq(dsi, seq...) do { \ + static const u8 d[] = { seq }; \ + int ret; \ + ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)); \ + if (ret < 0) \ + return ret; \ + } while (0) + +static int jh057n_init_sequence(struct st7703 *ctx) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + + /* + * Init sequence was supplied by the panel vendor. Most of the commands + * resemble the ST7703 but the number of parameters often don't match + * so it's likely a clone. + */ + dsi_generic_write_seq(dsi, ST7703_CMD_SETEXTC, + 0xF1, 0x12, 0x83); + dsi_generic_write_seq(dsi, ST7703_CMD_SETRGBIF, + 0x10, 0x10, 0x05, 0x05, 0x03, 0xFF, 0x00, 0x00, + 0x00, 0x00); + dsi_generic_write_seq(dsi, ST7703_CMD_SETSCR, + 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70, + 0x00); + dsi_generic_write_seq(dsi, ST7703_CMD_SETVDC, 0x4E); + dsi_generic_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0B); + dsi_generic_write_seq(dsi, ST7703_CMD_SETCYC, 0x80); + dsi_generic_write_seq(dsi, ST7703_CMD_SETDISP, 0xF0, 0x12, 0x30); + dsi_generic_write_seq(dsi, ST7703_CMD_SETEQ, + 0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10); + dsi_generic_write_seq(dsi, ST7703_CMD_SETBGP, 0x08, 0x08); + msleep(20); + + dsi_generic_write_seq(dsi, ST7703_CMD_SETVCOM, 0x3F, 0x3F); + dsi_generic_write_seq(dsi, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00); + dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP1, + 0x82, 0x10, 0x06, 0x05, 0x9E, 0x0A, 0xA5, 0x12, + 0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38, + 0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64, + 0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP2, + 0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13, + 0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0A, + 0xA5, 0x00, 0x00, 0x00, 0x00); + dsi_generic_write_seq(dsi, ST7703_CMD_SETGAMMA, + 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, 0x37, + 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, 0x11, + 0x18, 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, + 0x37, 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, + 0x11, 0x18); + + return 0; +} + +static const struct drm_display_mode jh057n00900_mode = { + .hdisplay = 720, + .hsync_start = 720 + 90, + .hsync_end = 720 + 90 + 20, + .htotal = 720 + 90 + 20 + 20, + .vdisplay = 1440, + .vsync_start = 1440 + 20, + .vsync_end = 1440 + 20 + 4, + .vtotal = 1440 + 20 + 4 + 12, + .clock = 75276, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + .width_mm = 65, + .height_mm = 130, +}; + +struct st7703_panel_desc jh057n00900_panel_desc = { + .mode = &jh057n00900_mode, + .lanes = 4, + .mode_flags = MIPI_DSI_MODE_VIDEO | + MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE, + .format = MIPI_DSI_FMT_RGB888, + .init_sequence = jh057n_init_sequence, +}; + +#define dsi_dcs_write_seq(dsi, cmd, seq...) do { \ + static const u8 d[] = { seq }; \ + int ret; \ + ret = mipi_dsi_dcs_write(dsi, cmd, d, ARRAY_SIZE(d)); \ + if (ret < 0) \ + return ret; \ + } while (0) + + +static int xbd599_init_sequence(struct st7703 *ctx) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + + /* + * Init sequence was supplied by the panel vendor. + */ + + /* Magic sequence to unlock user commands below. */ + dsi_dcs_write_seq(dsi, ST7703_CMD_SETEXTC, 0xF1, 0x12, 0x83); + + dsi_dcs_write_seq(dsi, ST7703_CMD_SETMIPI, + 0x33, /* VC_main = 0, Lane_Number = 3 (4 lanes) */ + 0x81, /* DSI_LDO_SEL = 1.7V, RTERM = 90 Ohm */ + 0x05, /* IHSRX = x6 (Low High Speed driving ability) */ + 0xF9, /* TX_CLK_SEL = fDSICLK/16 */ + 0x0E, /* HFP_OSC (min. HFP number in DSI mode) */ + 0x0E, /* HBP_OSC (min. HBP number in DSI mode) */ + /* The rest is undocumented in ST7703 datasheet */ + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x25, 0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, + 0x4F, 0x11, 0x00, 0x00, 0x37); + + dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER_EXT, + 0x25, /* PCCS = 2, ECP_DC_DIV = 1/4 HSYNC */ + 0x22, /* DT = 15ms XDK_ECP = x2 */ + 0x20, /* PFM_DC_DIV = /1 */ + 0x03 /* ECP_SYNC_EN = 1, VGX_SYNC_EN = 1 */); + + /* RGB I/F porch timing */ + dsi_dcs_write_seq(dsi, ST7703_CMD_SETRGBIF, + 0x10, /* VBP_RGB_GEN */ + 0x10, /* VFP_RGB_GEN */ + 0x05, /* DE_BP_RGB_GEN */ + 0x05, /* DE_FP_RGB_GEN */ + /* The rest is undocumented in ST7703 datasheet */ + 0x03, 0xFF, + 0x00, 0x00, + 0x00, 0x00); + + /* Source driving settings. */ + dsi_dcs_write_seq(dsi, ST7703_CMD_SETSCR, + 0x73, /* N_POPON */ + 0x73, /* N_NOPON */ + 0x50, /* I_POPON */ + 0x50, /* I_NOPON */ + 0x00, /* SCR[31,24] */ + 0xC0, /* SCR[23,16] */ + 0x08, /* SCR[15,8] */ + 0x70, /* SCR[7,0] */ + 0x00 /* Undocumented */); + + /* NVDDD_SEL = -1.8V, VDDD_SEL = out of range (possibly 1.9V?) */ + dsi_dcs_write_seq(dsi, ST7703_CMD_SETVDC, 0x4E); + + /* + * SS_PANEL = 1 (reverse scan), GS_PANEL = 0 (normal scan) + * REV_PANEL = 1 (normally black panel), BGR_PANEL = 1 (BGR) + */ + dsi_dcs_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0B); + + /* Zig-Zag Type C column inversion. */ + dsi_dcs_write_seq(dsi, ST7703_CMD_SETCYC, 0x80); + + /* Set display resolution. */ + dsi_dcs_write_seq(dsi, ST7703_CMD_SETDISP, + 0xF0, /* NL = 240 */ + 0x12, /* RES_V_LSB = 0, BLK_CON = VSSD, + * RESO_SEL = 720RGB + */ + 0xF0 /* WHITE_GND_EN = 1 (GND), + * WHITE_FRAME_SEL = 7 frames, + * ISC = 0 frames + */); + + dsi_dcs_write_seq(dsi, ST7703_CMD_SETEQ, + 0x00, /* PNOEQ */ + 0x00, /* NNOEQ */ + 0x0B, /* PEQGND */ + 0x0B, /* NEQGND */ + 0x10, /* PEQVCI */ + 0x10, /* NEQVCI */ + 0x00, /* PEQVCI1 */ + 0x00, /* NEQVCI1 */ + 0x00, /* reserved */ + 0x00, /* reserved */ + 0xFF, /* reserved */ + 0x00, /* reserved */ + 0xC0, /* ESD_DET_DATA_WHITE = 1, ESD_WHITE_EN = 1 */ + 0x10 /* SLPIN_OPTION = 1 (no need vsync after sleep-in) + * VEDIO_NO_CHECK_EN = 0 + * ESD_WHITE_GND_EN = 0 + * ESD_DET_TIME_SEL = 0 frames + */); + + /* Undocumented command. */ + dsi_dcs_write_seq(dsi, ST7703_CMD_UNKNOWN_C6, 0x01, 0x00, 0xFF, 0xFF, 0x00); + + dsi_dcs_write_seq(dsi, ST7703_CMD_SETPOWER, + 0x74, /* VBTHS, VBTLS: VGH = 17V, VBL = -11V */ + 0x00, /* FBOFF_VGH = 0, FBOFF_VGL = 0 */ + 0x32, /* VRP */ + 0x32, /* VRN */ + 0x77, /* reserved */ + 0xF1, /* APS = 1 (small), + * VGL_DET_EN = 1, VGH_DET_EN = 1, + * VGL_TURBO = 1, VGH_TURBO = 1 + */ + 0xFF, /* VGH1_L_DIV, VGL1_L_DIV (1.5MHz) */ + 0xFF, /* VGH1_R_DIV, VGL1_R_DIV (1.5MHz) */ + 0xCC, /* VGH2_L_DIV, VGL2_L_DIV (2.6MHz) */ + 0xCC, /* VGH2_R_DIV, VGL2_R_DIV (2.6MHz) */ + 0x77, /* VGH3_L_DIV, VGL3_L_DIV (4.5MHz) */ + 0x77 /* VGH3_R_DIV, VGL3_R_DIV (4.5MHz) */); + + /* Reference voltage. */ + dsi_dcs_write_seq(dsi, ST7703_CMD_SETBGP, + 0x07, /* VREF_SEL = 4.2V */ + 0x07 /* NVREF_SEL = 4.2V */); + msleep(20); + + dsi_dcs_write_seq(dsi, ST7703_CMD_SETVCOM, + 0x2C, /* VCOMDC_F = -0.67V */ + 0x2C /* VCOMDC_B = -0.67V */); + + /* Undocumented command. */ + dsi_dcs_write_seq(dsi, ST7703_CMD_UNKNOWN_BF, 0x02, 0x11, 0x00); + + /* This command is to set forward GIP timing. */ + dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP1, + 0x82, 0x10, 0x06, 0x05, 0xA2, 0x0A, 0xA5, 0x12, + 0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38, + 0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64, + 0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + + /* This command is to set backward GIP timing. */ + dsi_dcs_write_seq(dsi, ST7703_CMD_SETGIP2, + 0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13, + 0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0A, + 0xA5, 0x00, 0x00, 0x00, 0x00); + + /* Adjust the gamma characteristics of the panel. */ + dsi_dcs_write_seq(dsi, ST7703_CMD_SETGAMMA, + 0x00, 0x09, 0x0D, 0x23, 0x27, 0x3C, 0x41, 0x35, + 0x07, 0x0D, 0x0E, 0x12, 0x13, 0x10, 0x12, 0x12, + 0x18, 0x00, 0x09, 0x0D, 0x23, 0x27, 0x3C, 0x41, + 0x35, 0x07, 0x0D, 0x0E, 0x12, 0x13, 0x10, 0x12, + 0x12, 0x18); + + return 0; +} + +static const struct drm_display_mode xbd599_mode = { + .hdisplay = 720, + .hsync_start = 720 + 40, + .hsync_end = 720 + 40 + 40, + .htotal = 720 + 40 + 40 + 40, + .vdisplay = 1440, + .vsync_start = 1440 + 18, + .vsync_end = 1440 + 18 + 10, + .vtotal = 1440 + 18 + 10 + 17, + .clock = 69000, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + .width_mm = 68, + .height_mm = 136, +}; + +static const struct st7703_panel_desc xbd599_desc = { + .mode = &xbd599_mode, + .lanes = 4, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE, + .format = MIPI_DSI_FMT_RGB888, + .init_sequence = xbd599_init_sequence, +}; + +static int st7703_enable(struct drm_panel *panel) +{ + struct st7703 *ctx = panel_to_st7703(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + int ret; + + ret = ctx->desc->init_sequence(ctx); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n", + ret); + return ret; + } + + msleep(20); + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, "Failed to exit sleep mode: %d\n", ret); + return ret; + } + + /* Panel is operational 120 msec after reset */ + msleep(60); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret) + return ret; + + DRM_DEV_DEBUG_DRIVER(ctx->dev, "Panel init sequence done\n"); + + return 0; +} + +static int st7703_disable(struct drm_panel *panel) +{ + struct st7703 *ctx = panel_to_st7703(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + int ret; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) + DRM_DEV_ERROR(ctx->dev, + "Failed to turn off the display: %d\n", ret); + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) + DRM_DEV_ERROR(ctx->dev, + "Failed to enter sleep mode: %d\n", ret); + + return 0; +} + +static int st7703_unprepare(struct drm_panel *panel) +{ + struct st7703 *ctx = panel_to_st7703(panel); + + if (!ctx->prepared) + return 0; + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_disable(ctx->iovcc); + regulator_disable(ctx->vcc); + ctx->prepared = false; + + return 0; +} + +static int st7703_prepare(struct drm_panel *panel) +{ + struct st7703 *ctx = panel_to_st7703(panel); + int ret; + + if (ctx->prepared) + return 0; + + DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n"); + ret = regulator_enable(ctx->vcc); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, + "Failed to enable vcc supply: %d\n", ret); + return ret; + } + ret = regulator_enable(ctx->iovcc); + if (ret < 0) { + DRM_DEV_ERROR(ctx->dev, + "Failed to enable iovcc supply: %d\n", ret); + goto disable_vcc; + } + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + usleep_range(20, 40); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + msleep(20); + + ctx->prepared = true; + + return 0; + +disable_vcc: + regulator_disable(ctx->vcc); + return ret; +} + +static int st7703_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct st7703 *ctx = panel_to_st7703(panel); + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, ctx->desc->mode); + if (!mode) { + DRM_DEV_ERROR(ctx->dev, "Failed to add mode %ux%u@%u\n", + ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay, + drm_mode_vrefresh(ctx->desc->mode)); + return -ENOMEM; + } + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_panel_funcs st7703_drm_funcs = { + .disable = st7703_disable, + .unprepare = st7703_unprepare, + .prepare = st7703_prepare, + .enable = st7703_enable, + .get_modes = st7703_get_modes, +}; + +static int allpixelson_set(void *data, u64 val) +{ + struct st7703 *ctx = data; + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + + DRM_DEV_DEBUG_DRIVER(ctx->dev, "Setting all pixels on\n"); + dsi_generic_write_seq(dsi, ST7703_CMD_ALL_PIXEL_ON); + msleep(val * 1000); + /* Reset the panel to get video back */ + drm_panel_disable(&ctx->panel); + drm_panel_unprepare(&ctx->panel); + drm_panel_prepare(&ctx->panel); + drm_panel_enable(&ctx->panel); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(allpixelson_fops, NULL, + allpixelson_set, "%llu\n"); + +static void st7703_debugfs_init(struct st7703 *ctx) +{ + ctx->debugfs = debugfs_create_dir(DRV_NAME, NULL); + + debugfs_create_file("allpixelson", 0600, ctx->debugfs, ctx, + &allpixelson_fops); +} + +static void st7703_debugfs_remove(struct st7703 *ctx) +{ + debugfs_remove_recursive(ctx->debugfs); + ctx->debugfs = NULL; +} + +static int st7703_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct st7703 *ctx; + int ret; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset_gpio)) { + DRM_DEV_ERROR(dev, "cannot get reset gpio\n"); + return PTR_ERR(ctx->reset_gpio); + } + + mipi_dsi_set_drvdata(dsi, ctx); + + ctx->dev = dev; + ctx->desc = of_device_get_match_data(dev); + + dsi->mode_flags = ctx->desc->mode_flags; + dsi->format = ctx->desc->format; + dsi->lanes = ctx->desc->lanes; + + ctx->vcc = devm_regulator_get(dev, "vcc"); + if (IS_ERR(ctx->vcc)) { + ret = PTR_ERR(ctx->vcc); + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(dev, + "Failed to request vcc regulator: %d\n", + ret); + return ret; + } + ctx->iovcc = devm_regulator_get(dev, "iovcc"); + if (IS_ERR(ctx->iovcc)) { + ret = PTR_ERR(ctx->iovcc); + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(dev, + "Failed to request iovcc regulator: %d\n", + ret); + return ret; + } + + drm_panel_init(&ctx->panel, dev, &st7703_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) + return ret; + + drm_panel_add(&ctx->panel); + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + DRM_DEV_ERROR(dev, + "mipi_dsi_attach failed (%d). Is host ready?\n", + ret); + drm_panel_remove(&ctx->panel); + return ret; + } + + DRM_DEV_INFO(dev, "%ux%u@%u %ubpp dsi %udl - ready\n", + ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay, + drm_mode_vrefresh(ctx->desc->mode), + mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes); + + st7703_debugfs_init(ctx); + return 0; +} + +static void st7703_shutdown(struct mipi_dsi_device *dsi) +{ + struct st7703 *ctx = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = drm_panel_unprepare(&ctx->panel); + if (ret < 0) + DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n", + ret); + + ret = drm_panel_disable(&ctx->panel); + if (ret < 0) + DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n", + ret); +} + +static int st7703_remove(struct mipi_dsi_device *dsi) +{ + struct st7703 *ctx = mipi_dsi_get_drvdata(dsi); + int ret; + + st7703_shutdown(dsi); + + ret = mipi_dsi_detach(dsi); + if (ret < 0) + DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n", + ret); + + drm_panel_remove(&ctx->panel); + + st7703_debugfs_remove(ctx); + + return 0; +} + +static const struct of_device_id st7703_of_match[] = { + { .compatible = "rocktech,jh057n00900", .data = &jh057n00900_panel_desc }, + { .compatible = "xingbangda,xbd599", .data = &xbd599_desc }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, st7703_of_match); + +static struct mipi_dsi_driver st7703_driver = { + .probe = st7703_probe, + .remove = st7703_remove, + .shutdown = st7703_shutdown, + .driver = { + .name = DRV_NAME, + .of_match_table = st7703_of_match, + }, +}; +module_mipi_dsi_driver(st7703_driver); + +MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>"); +MODULE_DESCRIPTION("DRM driver for Sitronix ST7703 based MIPI DSI panels"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c b/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c index 8a3b2f906e63..06341deb60ca 100644 --- a/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c +++ b/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c @@ -62,9 +62,9 @@ static inline struct xpp055c272 *panel_to_xpp055c272(struct drm_panel *panel) } #define dsi_generic_write_seq(dsi, cmd, seq...) do { \ - static const u8 d[] = { seq }; \ + static const u8 b[] = { cmd, seq }; \ int ret; \ - ret = mipi_dsi_dcs_write(dsi, cmd, d, ARRAY_SIZE(d)); \ + ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \ if (ret < 0) \ return ret; \ } while (0) diff --git a/drivers/gpu/drm/pl111/pl111_debugfs.c b/drivers/gpu/drm/pl111/pl111_debugfs.c index 26ca8cdf3e60..317f68abf18b 100644 --- a/drivers/gpu/drm/pl111/pl111_debugfs.c +++ b/drivers/gpu/drm/pl111/pl111_debugfs.c @@ -3,7 +3,6 @@ * Copyright © 2017 Broadcom */ -#include <linux/amba/clcd-regs.h> #include <linux/seq_file.h> #include <drm/drm_debugfs.h> diff --git a/drivers/gpu/drm/pl111/pl111_display.c b/drivers/gpu/drm/pl111/pl111_display.c index 703ddc803c55..b3e8697cafcf 100644 --- a/drivers/gpu/drm/pl111/pl111_display.c +++ b/drivers/gpu/drm/pl111/pl111_display.c @@ -9,7 +9,6 @@ * Copyright (C) 2011 Texas Instruments */ -#include <linux/amba/clcd-regs.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/version.h> diff --git a/drivers/gpu/drm/pl111/pl111_drm.h b/drivers/gpu/drm/pl111/pl111_drm.h index ba399bcb792f..2a46b5bd8576 100644 --- a/drivers/gpu/drm/pl111/pl111_drm.h +++ b/drivers/gpu/drm/pl111/pl111_drm.h @@ -23,6 +23,79 @@ #include <drm/drm_panel.h> #include <drm/drm_simple_kms_helper.h> +/* + * CLCD Controller Internal Register addresses + */ +#define CLCD_TIM0 0x00000000 +#define CLCD_TIM1 0x00000004 +#define CLCD_TIM2 0x00000008 +#define CLCD_TIM3 0x0000000c +#define CLCD_UBAS 0x00000010 +#define CLCD_LBAS 0x00000014 + +#define CLCD_PL110_IENB 0x00000018 +#define CLCD_PL110_CNTL 0x0000001c +#define CLCD_PL110_STAT 0x00000020 +#define CLCD_PL110_INTR 0x00000024 +#define CLCD_PL110_UCUR 0x00000028 +#define CLCD_PL110_LCUR 0x0000002C + +#define CLCD_PL111_CNTL 0x00000018 +#define CLCD_PL111_IENB 0x0000001c +#define CLCD_PL111_RIS 0x00000020 +#define CLCD_PL111_MIS 0x00000024 +#define CLCD_PL111_ICR 0x00000028 +#define CLCD_PL111_UCUR 0x0000002c +#define CLCD_PL111_LCUR 0x00000030 + +#define CLCD_PALL 0x00000200 +#define CLCD_PALETTE 0x00000200 + +#define TIM2_PCD_LO_MASK GENMASK(4, 0) +#define TIM2_PCD_LO_BITS 5 +#define TIM2_CLKSEL (1 << 5) +#define TIM2_ACB_MASK GENMASK(10, 6) +#define TIM2_IVS (1 << 11) +#define TIM2_IHS (1 << 12) +#define TIM2_IPC (1 << 13) +#define TIM2_IOE (1 << 14) +#define TIM2_BCD (1 << 26) +#define TIM2_PCD_HI_MASK GENMASK(31, 27) +#define TIM2_PCD_HI_BITS 5 +#define TIM2_PCD_HI_SHIFT 27 + +#define CNTL_LCDEN (1 << 0) +#define CNTL_LCDBPP1 (0 << 1) +#define CNTL_LCDBPP2 (1 << 1) +#define CNTL_LCDBPP4 (2 << 1) +#define CNTL_LCDBPP8 (3 << 1) +#define CNTL_LCDBPP16 (4 << 1) +#define CNTL_LCDBPP16_565 (6 << 1) +#define CNTL_LCDBPP16_444 (7 << 1) +#define CNTL_LCDBPP24 (5 << 1) +#define CNTL_LCDBW (1 << 4) +#define CNTL_LCDTFT (1 << 5) +#define CNTL_LCDMONO8 (1 << 6) +#define CNTL_LCDDUAL (1 << 7) +#define CNTL_BGR (1 << 8) +#define CNTL_BEBO (1 << 9) +#define CNTL_BEPO (1 << 10) +#define CNTL_LCDPWR (1 << 11) +#define CNTL_LCDVCOMP(x) ((x) << 12) +#define CNTL_LDMAFIFOTIME (1 << 15) +#define CNTL_WATERMARK (1 << 16) + +/* ST Microelectronics variant bits */ +#define CNTL_ST_1XBPP_444 0x0 +#define CNTL_ST_1XBPP_5551 (1 << 17) +#define CNTL_ST_1XBPP_565 (1 << 18) +#define CNTL_ST_CDWID_12 0x0 +#define CNTL_ST_CDWID_16 (1 << 19) +#define CNTL_ST_CDWID_18 (1 << 20) +#define CNTL_ST_CDWID_24 ((1 << 19) | (1 << 20)) +#define CNTL_ST_CEAEN (1 << 21) +#define CNTL_ST_LCDBPP24_PACKED (6 << 1) + #define CLCD_IRQ_NEXTBASE_UPDATE BIT(2) struct drm_minor; diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c index da0c39dae874..96e58fda75d8 100644 --- a/drivers/gpu/drm/pl111/pl111_drv.c +++ b/drivers/gpu/drm/pl111/pl111_drv.c @@ -47,7 +47,6 @@ */ #include <linux/amba/bus.h> -#include <linux/amba/clcd-regs.h> #include <linux/dma-buf.h> #include <linux/module.h> #include <linux/of.h> diff --git a/drivers/gpu/drm/pl111/pl111_versatile.c b/drivers/gpu/drm/pl111/pl111_versatile.c index 64f01a4e6767..bdd883f4f0da 100644 --- a/drivers/gpu/drm/pl111/pl111_versatile.c +++ b/drivers/gpu/drm/pl111/pl111_versatile.c @@ -1,6 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-only -#include <linux/amba/clcd-regs.h> +/* + * Versatile family (ARM reference designs) handling for the PL11x. + * This is based on code and know-how in the previous frame buffer + * driver in drivers/video/fbdev/amba-clcd.c: + * Copyright (C) 2001 ARM Limited, by David A Rusling + * Updated to 2.5 by Deep Blue Solutions Ltd. + * Major contributions and discoveries by Russell King. + */ + #include <linux/bitops.h> #include <linux/device.h> #include <linux/mfd/syscon.h> diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c index 52eaa2d22745..1d8e07b8b19e 100644 --- a/drivers/gpu/drm/qxl/qxl_ttm.c +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -54,7 +54,7 @@ static int qxl_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, switch (type) { case TTM_PL_SYSTEM: /* System memory */ - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = 0; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; break; @@ -62,8 +62,7 @@ static int qxl_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, case TTM_PL_PRIV: /* "On-card" video ram */ man->func = &ttm_bo_manager_func; - man->flags = TTM_MEMTYPE_FLAG_FIXED | - TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = TTM_MEMTYPE_FLAG_FIXED; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; break; @@ -99,7 +98,6 @@ static void qxl_evict_flags(struct ttm_buffer_object *bo, int qxl_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) { - struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; struct qxl_device *qdev = qxl_get_qdev(bdev); mem->bus.addr = NULL; @@ -107,8 +105,7 @@ int qxl_ttm_io_mem_reserve(struct ttm_bo_device *bdev, mem->bus.size = mem->num_pages << PAGE_SHIFT; mem->bus.base = 0; mem->bus.is_iomem = false; - if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) - return -EINVAL; + switch (mem->mem_type) { case TTM_PL_SYSTEM: /* system memory */ @@ -129,11 +126,6 @@ int qxl_ttm_io_mem_reserve(struct ttm_bo_device *bdev, return 0; } -static void qxl_ttm_io_mem_free(struct ttm_bo_device *bdev, - struct ttm_mem_reg *mem) -{ -} - /* * TTM backend functions. */ @@ -247,7 +239,6 @@ static struct ttm_bo_driver qxl_bo_driver = { .evict_flags = &qxl_evict_flags, .move = &qxl_bo_move, .io_mem_reserve = &qxl_ttm_io_mem_reserve, - .io_mem_free = &qxl_ttm_io_mem_free, .move_notify = &qxl_bo_move_notify, }; diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 357e8e98cca9..54af06df865b 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -84,7 +84,7 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, man->func = &ttm_bo_manager_func; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | TTM_MEMTYPE_FLAG_CMA; + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; #if IS_ENABLED(CONFIG_AGP) if (rdev->flags & RADEON_IS_AGP) { if (!rdev->ddev->agp) { @@ -457,10 +457,6 @@ static int radeon_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_ return 0; } -static void radeon_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) -{ -} - /* * TTM backend functions. */ @@ -479,7 +475,7 @@ static int radeon_ttm_tt_pin_userptr(struct ttm_tt *ttm) { struct radeon_device *rdev = radeon_get_rdev(ttm->bdev); struct radeon_ttm_tt *gtt = (void *)ttm; - unsigned pinned = 0, nents; + unsigned pinned = 0; int r; int write = !(gtt->userflags & RADEON_GEM_USERPTR_READONLY); @@ -519,9 +515,8 @@ static int radeon_ttm_tt_pin_userptr(struct ttm_tt *ttm) if (r) goto release_sg; - r = -ENOMEM; - nents = dma_map_sg(rdev->dev, ttm->sg->sgl, ttm->sg->nents, direction); - if (nents == 0) + r = dma_map_sgtable(rdev->dev, ttm->sg, direction, 0); + if (r) goto release_sg; drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages, @@ -552,9 +547,9 @@ static void radeon_ttm_tt_unpin_userptr(struct ttm_tt *ttm) return; /* free the sg table and pages again */ - dma_unmap_sg(rdev->dev, ttm->sg->sgl, ttm->sg->nents, direction); + dma_unmap_sgtable(rdev->dev, ttm->sg, direction, 0); - for_each_sg_page(ttm->sg->sgl, &sg_iter, ttm->sg->nents, 0) { + for_each_sgtable_page(ttm->sg, &sg_iter, 0) { struct page *page = sg_page_iter_page(&sg_iter); if (!(gtt->userflags & RADEON_GEM_USERPTR_READONLY)) set_page_dirty(page); @@ -775,7 +770,6 @@ static struct ttm_bo_driver radeon_bo_driver = { .move_notify = &radeon_bo_move_notify, .fault_reserve_notify = &radeon_bo_fault_reserve_notify, .io_mem_reserve = &radeon_ttm_io_mem_reserve, - .io_mem_free = &radeon_ttm_io_mem_free, }; int radeon_ttm_init(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index d73e88ddecd0..fe86a3e67757 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -975,8 +975,7 @@ static void rcar_du_crtc_reset(struct drm_crtc *crtc) state->crc.source = VSP1_DU_CRC_NONE; state->crc.index = 0; - crtc->state = &state->state; - crtc->state->crtc = crtc; + __drm_atomic_helper_crtc_reset(crtc, &state->state); } static int rcar_du_crtc_enable_vblank(struct drm_crtc *crtc) @@ -1271,9 +1270,6 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex, drm_crtc_helper_add(crtc, &crtc_helper_funcs); - /* Start with vertical blanking interrupt reporting disabled. */ - drm_crtc_vblank_off(crtc); - /* Register the interrupt handler. */ if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) { /* The IRQ's are associated with the CRTC (sw)index. */ diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index c803e14eed91..146380118962 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -486,7 +486,7 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job, bool first; trace_drm_sched_job(sched_job, entity); - atomic_inc(&entity->rq->sched->num_jobs); + atomic_inc(&entity->rq->sched->score); WRITE_ONCE(entity->last_user, current->group_leader); first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node); diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 2f319102ae9f..d6eaa23ad746 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -92,6 +92,7 @@ void drm_sched_rq_add_entity(struct drm_sched_rq *rq, if (!list_empty(&entity->list)) return; spin_lock(&rq->lock); + atomic_inc(&rq->sched->score); list_add_tail(&entity->list, &rq->entities); spin_unlock(&rq->lock); } @@ -110,6 +111,7 @@ void drm_sched_rq_remove_entity(struct drm_sched_rq *rq, if (list_empty(&entity->list)) return; spin_lock(&rq->lock); + atomic_dec(&rq->sched->score); list_del_init(&entity->list); if (rq->current_entity == entity) rq->current_entity = NULL; @@ -647,7 +649,7 @@ static void drm_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb) struct drm_gpu_scheduler *sched = s_fence->sched; atomic_dec(&sched->hw_rq_count); - atomic_dec(&sched->num_jobs); + atomic_dec(&sched->score); trace_drm_sched_process_job(s_fence); @@ -712,7 +714,7 @@ drm_sched_pick_best(struct drm_gpu_scheduler **sched_list, { struct drm_gpu_scheduler *sched, *picked_sched = NULL; int i; - unsigned int min_jobs = UINT_MAX, num_jobs; + unsigned int min_score = UINT_MAX, num_score; for (i = 0; i < num_sched_list; ++i) { sched = sched_list[i]; @@ -723,9 +725,9 @@ drm_sched_pick_best(struct drm_gpu_scheduler **sched_list, continue; } - num_jobs = atomic_read(&sched->num_jobs); - if (num_jobs < min_jobs) { - min_jobs = num_jobs; + num_score = atomic_read(&sched->score); + if (num_score < min_score) { + min_score = num_score; picked_sched = sched; } } @@ -860,7 +862,7 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, spin_lock_init(&sched->job_list_lock); atomic_set(&sched->hw_rq_count, 0); INIT_DELAYED_WORK(&sched->work_tdr, drm_sched_job_timedout); - atomic_set(&sched->num_jobs, 0); + atomic_set(&sched->score, 0); atomic64_set(&sched->job_id_count, 0); /* Each scheduler will run on a seperate kernel thread */ diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index f894968d6e45..6e28f707092f 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -423,9 +423,12 @@ static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { struct ltdc_device *ldev = crtc_to_ltdc(crtc); + struct drm_device *ddev = crtc->dev; DRM_DEBUG_DRIVER("\n"); + pm_runtime_get_sync(ddev->dev); + /* Sets the background color value */ reg_write(ldev->regs, LTDC_BCCR, BCCR_BCBLACK); @@ -503,15 +506,7 @@ static bool ltdc_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *adjusted_mode) { struct ltdc_device *ldev = crtc_to_ltdc(crtc); - struct drm_device *ddev = crtc->dev; int rate = mode->clock * 1000; - bool runtime_active; - int ret; - - runtime_active = pm_runtime_active(ddev->dev); - - if (runtime_active) - pm_runtime_put_sync(ddev->dev); if (clk_set_rate(ldev->pixel_clk, rate) < 0) { DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate); @@ -520,14 +515,6 @@ static bool ltdc_crtc_mode_fixup(struct drm_crtc *crtc, adjusted_mode->clock = clk_get_rate(ldev->pixel_clk) / 1000; - if (runtime_active) { - ret = pm_runtime_get_sync(ddev->dev); - if (ret) { - DRM_ERROR("Failed to fixup mode, cannot get sync\n"); - return false; - } - } - DRM_DEBUG_DRIVER("requested clock %dkHz, adjusted clock %dkHz\n", mode->clock, adjusted_mode->clock); diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 83f31c6e891c..9b308b572eac 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1168,7 +1168,6 @@ static void tegra_crtc_reset(struct drm_crtc *crtc) tegra_crtc_atomic_destroy_state(crtc, crtc->state); __drm_atomic_helper_crtc_reset(crtc, &state->base); - drm_crtc_vblank_reset(crtc); } static struct drm_crtc_state * diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tidss_crtc.c index 89a226912de8..3c5744a91d4a 100644 --- a/drivers/gpu/drm/tidss/tidss_crtc.c +++ b/drivers/gpu/drm/tidss/tidss_crtc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ @@ -352,8 +352,7 @@ static void tidss_crtc_reset(struct drm_crtc *crtc) return; } - crtc->state = &tcrtc->base; - crtc->state->crtc = crtc; + __drm_atomic_helper_crtc_reset(crtc, &tcrtc->base); } static struct drm_crtc_state *tidss_crtc_duplicate_state(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/tidss/tidss_crtc.h b/drivers/gpu/drm/tidss/tidss_crtc.h index 09e773666228..040d1205496b 100644 --- a/drivers/gpu/drm/tidss/tidss_crtc.h +++ b/drivers/gpu/drm/tidss/tidss_crtc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c index 629dd06393f6..c3ece2c9d1c8 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.c +++ b/drivers/gpu/drm/tidss/tidss_dispc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2016-2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Jyri Sarha <jsarha@ti.com> */ @@ -997,12 +997,12 @@ void dispc_vp_enable(struct dispc_device *dispc, u32 hw_videoport, ieo = !!(tstate->bus_flags & DRM_BUS_FLAG_DE_LOW); - ipc = !!(tstate->bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE); + ipc = !!(tstate->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE); /* always use the 'rf' setting */ onoff = true; - rf = !!(tstate->bus_flags & DRM_BUS_FLAG_SYNC_POSEDGE); + rf = !!(tstate->bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE); /* always use aligned syncs */ align = true; diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h index 902e612ff7ac..5984e0de2cd9 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.h +++ b/drivers/gpu/drm/tidss/tidss_dispc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ diff --git a/drivers/gpu/drm/tidss/tidss_dispc_regs.h b/drivers/gpu/drm/tidss/tidss_dispc_regs.h index 88a83a41b6e3..13feedfe5d6d 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc_regs.h +++ b/drivers/gpu/drm/tidss/tidss_dispc_regs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2016-2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Jyri Sarha <jsarha@ti.com> */ diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c index fee2f6fa3506..9179ea18f625 100644 --- a/drivers/gpu/drm/tidss/tidss_drv.c +++ b/drivers/gpu/drm/tidss/tidss_drv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ diff --git a/drivers/gpu/drm/tidss/tidss_drv.h b/drivers/gpu/drm/tidss/tidss_drv.h index 3b0a3d87b7c4..7de4bba52e6f 100644 --- a/drivers/gpu/drm/tidss/tidss_drv.h +++ b/drivers/gpu/drm/tidss/tidss_drv.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ diff --git a/drivers/gpu/drm/tidss/tidss_encoder.c b/drivers/gpu/drm/tidss/tidss_encoder.c index 30bf2a65949c..e278a9c89476 100644 --- a/drivers/gpu/drm/tidss/tidss_encoder.c +++ b/drivers/gpu/drm/tidss/tidss_encoder.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ diff --git a/drivers/gpu/drm/tidss/tidss_encoder.h b/drivers/gpu/drm/tidss/tidss_encoder.h index 06854d66e7e6..ace877c0e0fd 100644 --- a/drivers/gpu/drm/tidss/tidss_encoder.h +++ b/drivers/gpu/drm/tidss/tidss_encoder.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ diff --git a/drivers/gpu/drm/tidss/tidss_irq.c b/drivers/gpu/drm/tidss/tidss_irq.c index 1b80f2d62e0a..a5ec7931ef6b 100644 --- a/drivers/gpu/drm/tidss/tidss_irq.c +++ b/drivers/gpu/drm/tidss/tidss_irq.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ diff --git a/drivers/gpu/drm/tidss/tidss_irq.h b/drivers/gpu/drm/tidss/tidss_irq.h index aa92db403cca..4aaad5dfd7c2 100644 --- a/drivers/gpu/drm/tidss/tidss_irq.h +++ b/drivers/gpu/drm/tidss/tidss_irq.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c index 4b99e9fa84a5..808c8af58fd5 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.c +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ @@ -253,7 +253,6 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss) int tidss_modeset_init(struct tidss_device *tidss) { struct drm_device *ddev = &tidss->ddev; - unsigned int i; int ret; dev_dbg(tidss->dev, "%s\n", __func__); @@ -278,10 +277,6 @@ int tidss_modeset_init(struct tidss_device *tidss) if (ret) return ret; - /* Start with vertical blanking interrupt reporting disabled. */ - for (i = 0; i < tidss->num_crtcs; ++i) - drm_crtc_vblank_reset(tidss->crtcs[i]); - drm_mode_config_reset(ddev); dev_dbg(tidss->dev, "%s done\n", __func__); diff --git a/drivers/gpu/drm/tidss/tidss_kms.h b/drivers/gpu/drm/tidss/tidss_kms.h index 99aaff099f22..632d79f5983f 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.h +++ b/drivers/gpu/drm/tidss/tidss_kms.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ diff --git a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/tidss_plane.c index 0a563eabcbb9..43e72d0b2d84 100644 --- a/drivers/gpu/drm/tidss/tidss_plane.c +++ b/drivers/gpu/drm/tidss/tidss_plane.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ diff --git a/drivers/gpu/drm/tidss/tidss_plane.h b/drivers/gpu/drm/tidss/tidss_plane.h index 80ff1c5a2535..e933e158b617 100644 --- a/drivers/gpu/drm/tidss/tidss_plane.h +++ b/drivers/gpu/drm/tidss/tidss_plane.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ diff --git a/drivers/gpu/drm/tidss/tidss_scale_coefs.c b/drivers/gpu/drm/tidss/tidss_scale_coefs.c index 5ec68389cc68..c2b84fea89a5 100644 --- a/drivers/gpu/drm/tidss/tidss_scale_coefs.c +++ b/drivers/gpu/drm/tidss/tidss_scale_coefs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Jyri Sarha <jsarha@ti.com> */ diff --git a/drivers/gpu/drm/tidss/tidss_scale_coefs.h b/drivers/gpu/drm/tidss/tidss_scale_coefs.h index 64b5af5b5361..9c560d0fdac0 100644 --- a/drivers/gpu/drm/tidss/tidss_scale_coefs.h +++ b/drivers/gpu/drm/tidss/tidss_scale_coefs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Jyri Sarha <jsarha@ti.com> */ diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index e9dd5e5cb4e7..1856962411c7 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -537,6 +537,18 @@ static void tilcdc_crtc_atomic_disable(struct drm_crtc *crtc, tilcdc_crtc_disable(crtc); } +static void tilcdc_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + if (!crtc->state->event) + return; + + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + spin_unlock_irq(&crtc->dev->event_lock); +} + void tilcdc_crtc_shutdown(struct drm_crtc *crtc) { tilcdc_crtc_off(crtc, true); @@ -822,6 +834,7 @@ static const struct drm_crtc_helper_funcs tilcdc_crtc_helper_funcs = { .atomic_check = tilcdc_crtc_atomic_check, .atomic_enable = tilcdc_crtc_atomic_enable, .atomic_disable = tilcdc_crtc_atomic_disable, + .atomic_flush = tilcdc_crtc_atomic_flush, }; void tilcdc_crtc_set_panel_info(struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 0d74a6443263..4f5fc3e87383 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -87,55 +87,10 @@ static int tilcdc_atomic_check(struct drm_device *dev, 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; - - ret = drm_atomic_helper_swap_state(state, true); - if (ret) { - drm_atomic_helper_cleanup_planes(dev, state); - return ret; - } - - /* - * 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. - */ - - drm_atomic_helper_commit_modeset_disables(dev, state); - - drm_atomic_helper_commit_planes(dev, state, 0); - - drm_atomic_helper_commit_modeset_enables(dev, state); - - drm_atomic_helper_wait_for_vblanks(dev, state); - - drm_atomic_helper_cleanup_planes(dev, state); - - return 0; -} - static const struct drm_mode_config_funcs mode_config_funcs = { .fb_create = drm_gem_fb_create, .atomic_check = tilcdc_atomic_check, - .atomic_commit = tilcdc_commit, + .atomic_commit = drm_atomic_helper_commit, }; static void modeset_init(struct drm_device *dev) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_plane.c b/drivers/gpu/drm/tilcdc/tilcdc_plane.c index 0d09b31ae759..2f681a713815 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_plane.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_plane.c @@ -83,9 +83,11 @@ static void tilcdc_plane_atomic_update(struct drm_plane *plane, if (WARN_ON(!state->fb || !state->crtc->state)) return; - tilcdc_crtc_update_fb(state->crtc, - state->fb, - state->crtc->state->event); + if (tilcdc_crtc_update_fb(state->crtc, + state->fb, + state->crtc->state->event) == 0) { + state->crtc->state->event = NULL; + } } static const struct drm_plane_helper_funcs plane_helper_funcs = { diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index f73b81c2576e..0768a054a916 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -85,7 +85,6 @@ static void ttm_mem_type_debug(struct ttm_bo_device *bdev, struct drm_printer *p drm_printf(p, " has_type: %d\n", man->has_type); drm_printf(p, " use_type: %d\n", man->use_type); drm_printf(p, " flags: 0x%08X\n", man->flags); - drm_printf(p, " gpu_offset: 0x%08llX\n", man->gpu_offset); drm_printf(p, " size: %llu\n", man->size); drm_printf(p, " available_caching: 0x%08X\n", man->available_caching); drm_printf(p, " default_caching: 0x%08X\n", man->default_caching); @@ -273,32 +272,26 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx) { struct ttm_bo_device *bdev = bo->bdev; - bool old_is_pci = ttm_mem_reg_is_pci(bdev, &bo->mem); - bool new_is_pci = ttm_mem_reg_is_pci(bdev, mem); struct ttm_mem_type_manager *old_man = &bdev->man[bo->mem.mem_type]; struct ttm_mem_type_manager *new_man = &bdev->man[mem->mem_type]; - int ret = 0; + int ret; - if (old_is_pci || new_is_pci || - ((mem->placement & bo->mem.placement & TTM_PL_MASK_CACHING) == 0)) { - ret = ttm_mem_io_lock(old_man, true); - if (unlikely(ret != 0)) - goto out_err; - ttm_bo_unmap_virtual_locked(bo); - ttm_mem_io_unlock(old_man); - } + ret = ttm_mem_io_lock(old_man, true); + if (unlikely(ret != 0)) + goto out_err; + ttm_bo_unmap_virtual_locked(bo); + ttm_mem_io_unlock(old_man); /* * Create and bind a ttm if required. */ if (!(new_man->flags & TTM_MEMTYPE_FLAG_FIXED)) { - if (bo->ttm == NULL) { - bool zero = !(old_man->flags & TTM_MEMTYPE_FLAG_FIXED); - ret = ttm_tt_create(bo, zero); - if (ret) - goto out_err; - } + bool zero = !(old_man->flags & TTM_MEMTYPE_FLAG_FIXED); + + ret = ttm_tt_create(bo, zero); + if (ret) + goto out_err; ret = ttm_tt_set_placement_caching(bo->ttm, mem->placement); if (ret) @@ -314,7 +307,6 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, if (bdev->driver->move_notify) bdev->driver->move_notify(bo, evict, mem); bo->mem = *mem; - mem->mm_node = NULL; goto moved; } } @@ -343,12 +335,6 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, moved: bo->evicted = false; - if (bo->mem.mm_node) - bo->offset = (bo->mem.start << PAGE_SHIFT) + - bdev->man[bo->mem.mem_type].gpu_offset; - else - bo->offset = 0; - ctx->bytes_moved += bo->num_pages << PAGE_SHIFT; return 0; @@ -624,7 +610,6 @@ static void ttm_bo_release(struct kref *kref) ttm_bo_cleanup_memtype_use(bo); dma_resv_unlock(bo->base.resv); - BUG_ON(bo->mem.mm_node != NULL); atomic_dec(&ttm_bo_glob.bo_count); dma_fence_put(bo->moving); if (!ttm_bo_uses_embedded_gem_object(bo)) @@ -667,13 +652,8 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, placement.num_busy_placement = 0; bdev->driver->evict_flags(bo, &placement); - if (!placement.num_placement && !placement.num_busy_placement) { - ret = ttm_bo_pipeline_gutting(bo); - if (ret) - return ret; - - return ttm_tt_create(bo, false); - } + if (!placement.num_placement && !placement.num_busy_placement) + return ttm_bo_pipeline_gutting(bo); evict_mem = bo->mem; evict_mem.mm_node = NULL; @@ -856,12 +836,29 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev, return ret; } +static int ttm_bo_mem_get(struct ttm_buffer_object *bo, + const struct ttm_place *place, + struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = &bo->bdev->man[mem->mem_type]; + + mem->mm_node = NULL; + if (!man->func || !man->func->get_node) + return 0; + + return man->func->get_node(man, bo, place, mem); +} + void ttm_bo_mem_put(struct ttm_buffer_object *bo, struct ttm_mem_reg *mem) { struct ttm_mem_type_manager *man = &bo->bdev->man[mem->mem_type]; - if (mem->mm_node) - (*man->func->put_node)(man, mem); + if (!man->func || !man->func->put_node) + return; + + man->func->put_node(man, mem); + mem->mm_node = NULL; + mem->mem_type = TTM_PL_SYSTEM; } EXPORT_SYMBOL(ttm_bo_mem_put); @@ -915,11 +912,11 @@ static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo, ticket = dma_resv_locking_ctx(bo->base.resv); do { - ret = (*man->func->get_node)(man, bo, place, mem); - if (unlikely(ret != 0)) - return ret; - if (mem->mm_node) + ret = ttm_bo_mem_get(bo, place, mem); + if (likely(!ret)) break; + if (unlikely(ret != -ENOSPC)) + return ret; ret = ttm_mem_evict_first(bdev, mem->mem_type, place, ctx, ticket); if (unlikely(ret != 0)) @@ -1045,7 +1042,6 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, if (unlikely(ret)) return ret; - mem->mm_node = NULL; for (i = 0; i < placement->num_placement; ++i) { const struct ttm_place *place = &placement->placement[i]; struct ttm_mem_type_manager *man; @@ -1057,21 +1053,16 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, goto error; type_found = true; - mem->mm_node = NULL; - if (mem->mem_type == TTM_PL_SYSTEM) - return 0; - - man = &bdev->man[mem->mem_type]; - ret = (*man->func->get_node)(man, bo, place, mem); + ret = ttm_bo_mem_get(bo, place, mem); + if (ret == -ENOSPC) + continue; if (unlikely(ret)) goto error; - if (!mem->mm_node) - continue; - + man = &bdev->man[mem->mem_type]; ret = ttm_bo_add_move_fence(bo, man, mem, ctx->no_wait_gpu); if (unlikely(ret)) { - (*man->func->put_node)(man, mem); + ttm_bo_mem_put(bo, mem); if (ret == -EBUSY) continue; @@ -1090,12 +1081,8 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, goto error; type_found = true; - mem->mm_node = NULL; - if (mem->mem_type == TTM_PL_SYSTEM) - return 0; - ret = ttm_bo_mem_force_space(bo, place, mem, ctx); - if (ret == 0 && mem->mm_node) + if (likely(!ret)) return 0; if (ret && ret != -EBUSY) @@ -1133,6 +1120,8 @@ static int ttm_bo_move_buffer(struct ttm_buffer_object *bo, mem.page_alignment = bo->mem.page_alignment; mem.bus.io_reserved_vm = false; mem.bus.io_reserved_count = 0; + mem.mm_node = NULL; + /* * Determine where to move the buffer. */ @@ -1141,7 +1130,7 @@ static int ttm_bo_move_buffer(struct ttm_buffer_object *bo, goto out_unlock; ret = ttm_bo_handle_move_mem(bo, &mem, false, ctx); out_unlock: - if (ret && mem.mm_node) + if (ret) ttm_bo_mem_put(bo, &mem); return ret; } @@ -1156,7 +1145,7 @@ static bool ttm_bo_places_compat(const struct ttm_place *places, for (i = 0; i < num_placement; i++) { const struct ttm_place *heap = &places[i]; - if (mem->mm_node && (mem->start < heap->fpfn || + if ((mem->start < heap->fpfn || (heap->lpfn != 0 && (mem->start + mem->num_pages) > heap->lpfn))) continue; @@ -1201,13 +1190,8 @@ int ttm_bo_validate(struct ttm_buffer_object *bo, /* * Remove the backing store if no placement is given. */ - if (!placement->num_placement && !placement->num_busy_placement) { - ret = ttm_bo_pipeline_gutting(bo); - if (ret) - return ret; - - return ttm_tt_create(bo, false); - } + if (!placement->num_placement && !placement->num_busy_placement) + return ttm_bo_pipeline_gutting(bo); /* * Check whether we need to move buffer. @@ -1224,14 +1208,6 @@ int ttm_bo_validate(struct ttm_buffer_object *bo, ttm_flag_masked(&bo->mem.placement, new_flags, ~TTM_PL_MASK_MEMTYPE); } - /* - * We might need to add a TTM. - */ - if (bo->mem.mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) { - ret = ttm_tt_create(bo, true); - if (ret) - return ret; - } return 0; } EXPORT_SYMBOL(ttm_bo_validate); @@ -1540,7 +1516,6 @@ int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type, BUG_ON(type >= TTM_NUM_MEM_TYPES); man = &bdev->man[type]; BUG_ON(man->has_type); - man->io_reserve_fastpath = true; man->use_io_reserve_lru = false; mutex_init(&man->io_reserve_mutex); spin_lock_init(&man->move_lock); @@ -1718,23 +1693,6 @@ EXPORT_SYMBOL(ttm_bo_device_init); * buffer object vm functions. */ -bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) -{ - struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; - - if (!(man->flags & TTM_MEMTYPE_FLAG_FIXED)) { - if (mem->mem_type == TTM_PL_SYSTEM) - return false; - - if (man->flags & TTM_MEMTYPE_FLAG_CMA) - return false; - - if (mem->placement & TTM_PL_FLAG_CACHED) - return false; - } - return true; -} - void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo) { struct ttm_bo_device *bdev = bo->bdev; diff --git a/drivers/gpu/drm/ttm/ttm_bo_manager.c b/drivers/gpu/drm/ttm/ttm_bo_manager.c index 18d3debcc949..facd3049c3aa 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_manager.c +++ b/drivers/gpu/drm/ttm/ttm_bo_manager.c @@ -86,7 +86,7 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, mem->start = node->start; } - return 0; + return ret; } static void ttm_bo_man_put_node(struct ttm_mem_type_manager *man, diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index f09b096ba4fd..7fb3e0bcbab4 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -93,7 +93,7 @@ EXPORT_SYMBOL(ttm_bo_move_ttm); int ttm_mem_io_lock(struct ttm_mem_type_manager *man, bool interruptible) { - if (likely(man->io_reserve_fastpath)) + if (likely(!man->use_io_reserve_lru)) return 0; if (interruptible) @@ -105,7 +105,7 @@ int ttm_mem_io_lock(struct ttm_mem_type_manager *man, bool interruptible) void ttm_mem_io_unlock(struct ttm_mem_type_manager *man) { - if (likely(man->io_reserve_fastpath)) + if (likely(!man->use_io_reserve_lru)) return; mutex_unlock(&man->io_reserve_mutex); @@ -115,39 +115,35 @@ static int ttm_mem_io_evict(struct ttm_mem_type_manager *man) { struct ttm_buffer_object *bo; - if (!man->use_io_reserve_lru || list_empty(&man->io_reserve_lru)) - return -EAGAIN; + bo = list_first_entry_or_null(&man->io_reserve_lru, + struct ttm_buffer_object, + io_reserve_lru); + if (!bo) + return -ENOSPC; - bo = list_first_entry(&man->io_reserve_lru, - struct ttm_buffer_object, - io_reserve_lru); list_del_init(&bo->io_reserve_lru); ttm_bo_unmap_virtual_locked(bo); - return 0; } - int ttm_mem_io_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) { struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; - int ret = 0; + int ret; + + if (mem->bus.io_reserved_count++) + return 0; if (!bdev->driver->io_mem_reserve) return 0; - if (likely(man->io_reserve_fastpath)) - return bdev->driver->io_mem_reserve(bdev, mem); - if (bdev->driver->io_mem_reserve && - mem->bus.io_reserved_count++ == 0) { retry: - ret = bdev->driver->io_mem_reserve(bdev, mem); - if (ret == -EAGAIN) { - ret = ttm_mem_io_evict(man); - if (ret == 0) - goto retry; - } + ret = bdev->driver->io_mem_reserve(bdev, mem); + if (ret == -ENOSPC) { + ret = ttm_mem_io_evict(man); + if (ret == 0) + goto retry; } return ret; } @@ -155,35 +151,31 @@ retry: void ttm_mem_io_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) { - struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; - - if (likely(man->io_reserve_fastpath)) + if (--mem->bus.io_reserved_count) return; - if (bdev->driver->io_mem_reserve && - --mem->bus.io_reserved_count == 0 && - bdev->driver->io_mem_free) - bdev->driver->io_mem_free(bdev, mem); + if (!bdev->driver->io_mem_free) + return; + bdev->driver->io_mem_free(bdev, mem); } int ttm_mem_io_reserve_vm(struct ttm_buffer_object *bo) { + struct ttm_mem_type_manager *man = &bo->bdev->man[bo->mem.mem_type]; struct ttm_mem_reg *mem = &bo->mem; int ret; - if (!mem->bus.io_reserved_vm) { - struct ttm_mem_type_manager *man = - &bo->bdev->man[mem->mem_type]; + if (mem->bus.io_reserved_vm) + return 0; - ret = ttm_mem_io_reserve(bo->bdev, mem); - if (unlikely(ret != 0)) - return ret; - mem->bus.io_reserved_vm = true; - if (man->use_io_reserve_lru) - list_add_tail(&bo->io_reserve_lru, - &man->io_reserve_lru); - } + ret = ttm_mem_io_reserve(bo->bdev, mem); + if (unlikely(ret != 0)) + return ret; + mem->bus.io_reserved_vm = true; + if (man->use_io_reserve_lru) + list_add_tail(&bo->io_reserve_lru, + &man->io_reserve_lru); return 0; } @@ -191,15 +183,17 @@ void ttm_mem_io_free_vm(struct ttm_buffer_object *bo) { struct ttm_mem_reg *mem = &bo->mem; - if (mem->bus.io_reserved_vm) { - mem->bus.io_reserved_vm = false; - list_del_init(&bo->io_reserve_lru); - ttm_mem_io_free(bo->bdev, mem); - } + if (!mem->bus.io_reserved_vm) + return; + + mem->bus.io_reserved_vm = false; + list_del_init(&bo->io_reserve_lru); + ttm_mem_io_free(bo->bdev, mem); } -static int ttm_mem_reg_ioremap(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem, - void **virtual) +static int ttm_mem_reg_ioremap(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem, + void **virtual) { struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; int ret; @@ -216,9 +210,11 @@ static int ttm_mem_reg_ioremap(struct ttm_bo_device *bdev, struct ttm_mem_reg *m addr = mem->bus.addr; } else { if (mem->placement & TTM_PL_FLAG_WC) - addr = ioremap_wc(mem->bus.base + mem->bus.offset, mem->bus.size); + addr = ioremap_wc(mem->bus.base + mem->bus.offset, + mem->bus.size); else - addr = ioremap(mem->bus.base + mem->bus.offset, mem->bus.size); + addr = ioremap(mem->bus.base + mem->bus.offset, + mem->bus.size); if (!addr) { (void) ttm_mem_io_lock(man, false); ttm_mem_io_free(bdev, mem); @@ -230,8 +226,9 @@ static int ttm_mem_reg_ioremap(struct ttm_bo_device *bdev, struct ttm_mem_reg *m return 0; } -static void ttm_mem_reg_iounmap(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem, - void *virtual) +static void ttm_mem_reg_iounmap(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem, + void *virtual) { struct ttm_mem_type_manager *man; @@ -513,11 +510,13 @@ static int ttm_bo_ioremap(struct ttm_buffer_object *bo, } else { map->bo_kmap_type = ttm_bo_map_iomap; if (mem->placement & TTM_PL_FLAG_WC) - map->virtual = ioremap_wc(bo->mem.bus.base + bo->mem.bus.offset + offset, + map->virtual = ioremap_wc(bo->mem.bus.base + + bo->mem.bus.offset + offset, size); else - map->virtual = ioremap(bo->mem.bus.base + bo->mem.bus.offset + offset, - size); + map->virtual = ioremap(bo->mem.bus.base + + bo->mem.bus.offset + offset, + size); } return (!map->virtual) ? -ENOMEM : 0; } @@ -532,12 +531,15 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo, .interruptible = false, .no_wait_gpu = false }; - struct ttm_tt *ttm = bo->ttm; + struct ttm_tt *ttm; pgprot_t prot; int ret; - BUG_ON(!ttm); + ret = ttm_tt_create(bo, true); + if (ret) + return ret; + ttm = bo->ttm; ret = ttm_tt_populate(ttm, &ctx); if (ret) return ret; diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index a43aa7275f12..82b893d4249f 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -349,6 +349,11 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf, }; + if (ttm_tt_create(bo, true)) { + ret = VM_FAULT_OOM; + goto out_io_unlock; + } + ttm = bo->ttm; if (ttm_tt_populate(bo->ttm, &ctx)) { ret = VM_FAULT_OOM; diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index 2ec448e1d663..e25d4097aa16 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -50,6 +50,9 @@ int ttm_tt_create(struct ttm_buffer_object *bo, bool zero_alloc) dma_resv_assert_held(bo->base.resv); + if (bo->ttm) + return 0; + if (bdev->need_dma32) page_flags |= TTM_PAGE_FLAG_DMA32; @@ -67,7 +70,6 @@ int ttm_tt_create(struct ttm_buffer_object *bo, bool zero_alloc) page_flags |= TTM_PAGE_FLAG_SG; break; default: - bo->ttm = NULL; pr_err("Illegal buffer object type\n"); return -EINVAL; } diff --git a/drivers/gpu/drm/vboxvideo/hgsmi_defs.h b/drivers/gpu/drm/vboxvideo/hgsmi_defs.h index 6c8df1cdb087..3cb52f2b2274 100644 --- a/drivers/gpu/drm/vboxvideo/hgsmi_defs.h +++ b/drivers/gpu/drm/vboxvideo/hgsmi_defs.h @@ -58,7 +58,7 @@ struct hgsmi_buffer_tail { /* Reserved, must be initialized to 0. */ u32 reserved; /* - * One-at-a-Time Hash: http://www.burtleburtle.net/bob/hash/doobs.html + * One-at-a-Time Hash: https://www.burtleburtle.net/bob/hash/doobs.html * Over the header, offset and for first 4 bytes of the tail. */ u32 checksum; diff --git a/drivers/gpu/drm/vboxvideo/vbox_hgsmi.c b/drivers/gpu/drm/vboxvideo/vbox_hgsmi.c index 94b60654a012..a9ca4d0c3eca 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_hgsmi.c +++ b/drivers/gpu/drm/vboxvideo/vbox_hgsmi.c @@ -8,7 +8,7 @@ #include "vboxvideo_vbe.h" #include "hgsmi_defs.h" -/* One-at-a-Time Hash from http://www.burtleburtle.net/bob/hash/doobs.html */ +/* One-at-a-Time Hash from https://www.burtleburtle.net/bob/hash/doobs.html */ static u32 hgsmi_hash_process(u32 hash, const u8 *data, int size) { while (size--) { diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 29131409a4de..6d8fa6118fc1 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -184,52 +184,11 @@ static bool vc4_crtc_get_scanout_position(struct drm_crtc *crtc, return ret; } -static void vc4_crtc_destroy(struct drm_crtc *crtc) +void vc4_crtc_destroy(struct drm_crtc *crtc) { drm_crtc_cleanup(crtc); } -static void -vc4_crtc_lut_load(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); - u32 i; - - /* The LUT memory is laid out with each HVS channel in order, - * each of which takes 256 writes for R, 256 for G, then 256 - * for B. - */ - HVS_WRITE(SCALER_GAMADDR, - SCALER_GAMADDR_AUTOINC | - (vc4_crtc->channel * 3 * crtc->gamma_size)); - - for (i = 0; i < crtc->gamma_size; i++) - HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_r[i]); - for (i = 0; i < crtc->gamma_size; i++) - HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_g[i]); - for (i = 0; i < crtc->gamma_size; i++) - HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_b[i]); -} - -static void -vc4_crtc_update_gamma_lut(struct drm_crtc *crtc) -{ - struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); - struct drm_color_lut *lut = crtc->state->gamma_lut->data; - u32 length = drm_color_lut_size(crtc->state->gamma_lut); - u32 i; - - for (i = 0; i < length; i++) { - vc4_crtc->lut_r[i] = drm_color_lut_extract(lut[i].red, 8); - vc4_crtc->lut_g[i] = drm_color_lut_extract(lut[i].green, 8); - vc4_crtc->lut_b[i] = drm_color_lut_extract(lut[i].blue, 8); - } - - vc4_crtc_lut_load(crtc); -} - static u32 vc4_get_fifo_full_level(u32 format) { static const u32 fifo_len_bytes = 64; @@ -363,12 +322,7 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc) static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) { - struct drm_device *dev = crtc->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); - struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); - struct drm_display_mode *mode = &crtc->state->adjusted_mode; - bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE; bool debug_dump_regs = false; if (debug_dump_regs) { @@ -378,42 +332,9 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) drm_print_regset32(&p, &vc4_crtc->regset); } - if (vc4_crtc->channel == 2) { - u32 dispctrl; - u32 dsp3_mux; + vc4_crtc_config_pv(crtc); - /* - * SCALER_DISPCTRL_DSP3 = X, where X < 2 means 'connect DSP3 to - * FIFO X'. - * SCALER_DISPCTRL_DSP3 = 3 means 'disable DSP 3'. - * - * DSP3 is connected to FIFO2 unless the transposer is - * enabled. In this case, FIFO 2 is directly accessed by the - * TXP IP, and we need to disable the FIFO2 -> pixelvalve1 - * route. - */ - if (vc4_state->feed_txp) - dsp3_mux = VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX); - else - dsp3_mux = VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX); - - dispctrl = HVS_READ(SCALER_DISPCTRL) & - ~SCALER_DISPCTRL_DSP3_MUX_MASK; - HVS_WRITE(SCALER_DISPCTRL, dispctrl | dsp3_mux); - } - - if (!vc4_state->feed_txp) - vc4_crtc_config_pv(crtc); - - HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), - SCALER_DISPBKGND_AUTOHS | - SCALER_DISPBKGND_GAMMA | - (interlace ? SCALER_DISPBKGND_INTERLACE : 0)); - - /* Reload the LUT, since the SRAMs would have been disabled if - * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once. - */ - vc4_crtc_lut_load(crtc); + vc4_hvs_mode_set_nofb(crtc); if (debug_dump_regs) { struct drm_printer p = drm_info_printer(&vc4_crtc->pdev->dev); @@ -435,10 +356,9 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { struct drm_device *dev = crtc->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); - u32 chan = vc4_crtc->channel; int ret; + require_hvs_enabled(dev); /* Disable vblank irq handling before crtc is disabled. */ @@ -449,28 +369,7 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, ret = wait_for(!(CRTC_READ(PV_V_CONTROL) & PV_VCONTROL_VIDEN), 1); WARN_ONCE(ret, "Timeout waiting for !PV_VCONTROL_VIDEN\n"); - if (HVS_READ(SCALER_DISPCTRLX(chan)) & - SCALER_DISPCTRLX_ENABLE) { - HVS_WRITE(SCALER_DISPCTRLX(chan), - SCALER_DISPCTRLX_RESET); - - /* While the docs say that reset is self-clearing, it - * seems it doesn't actually. - */ - HVS_WRITE(SCALER_DISPCTRLX(chan), 0); - } - - /* Once we leave, the scaler should be disabled and its fifo empty. */ - - WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_RESET); - - WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(chan)), - SCALER_DISPSTATX_MODE) != - SCALER_DISPSTATX_MODE_DISABLED); - - WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) & - (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) != - SCALER_DISPSTATX_EMPTY); + vc4_hvs_atomic_disable(crtc, old_state); /* * Make sure we issue a vblank event after disabling the CRTC if @@ -486,52 +385,11 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, } } -void vc4_crtc_txp_armed(struct drm_crtc_state *state) -{ - struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); - - vc4_state->txp_armed = true; -} - -static void vc4_crtc_update_dlist(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); - struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); - - if (crtc->state->event) { - unsigned long flags; - - crtc->state->event->pipe = drm_crtc_index(crtc); - - WARN_ON(drm_crtc_vblank_get(crtc) != 0); - - spin_lock_irqsave(&dev->event_lock, flags); - - if (!vc4_state->feed_txp || vc4_state->txp_armed) { - vc4_crtc->event = crtc->state->event; - crtc->state->event = NULL; - } - - HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), - vc4_state->mm.start); - - spin_unlock_irqrestore(&dev->event_lock, flags); - } else { - HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), - vc4_state->mm.start); - } -} - static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { struct drm_device *dev = crtc->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); - struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); - struct drm_display_mode *mode = &crtc->state->adjusted_mode; require_hvs_enabled(dev); @@ -539,25 +397,14 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, * drm_crtc_get_vblank() fails in vc4_crtc_update_dlist(). */ drm_crtc_vblank_on(crtc); - vc4_crtc_update_dlist(crtc); - /* Turn on the scaler, which will wait for vstart to start - * compositing. - * When feeding the transposer, we should operate in oneshot - * mode. - */ - HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), - VC4_SET_FIELD(mode->hdisplay, SCALER_DISPCTRLX_WIDTH) | - VC4_SET_FIELD(mode->vdisplay, SCALER_DISPCTRLX_HEIGHT) | - SCALER_DISPCTRLX_ENABLE | - (vc4_state->feed_txp ? SCALER_DISPCTRLX_ONESHOT : 0)); + vc4_hvs_atomic_enable(crtc, old_state); /* When feeding the transposer block the pixelvalve is unneeded and * should not be enabled. */ - if (!vc4_state->feed_txp) - CRTC_WRITE(PV_V_CONTROL, - CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN); + CRTC_WRITE(PV_V_CONTROL, + CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN); } static enum drm_mode_status vc4_crtc_mode_valid(struct drm_crtc *crtc, @@ -608,31 +455,11 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); - struct drm_device *dev = crtc->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct drm_plane *plane; - unsigned long flags; - const struct drm_plane_state *plane_state; struct drm_connector *conn; struct drm_connector_state *conn_state; - u32 dlist_count = 0; int ret, i; - /* The pixelvalve can only feed one encoder (and encoders are - * 1:1 with connectors.) - */ - if (hweight32(state->connector_mask) > 1) - return -EINVAL; - - drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, state) - dlist_count += vc4_plane_dlist_size(plane_state); - - dlist_count++; /* Account for SCALER_CTL0_END. */ - - spin_lock_irqsave(&vc4->hvs->mm_lock, flags); - ret = drm_mm_insert_node(&vc4->hvs->dlist_mm, &vc4_state->mm, - dlist_count); - spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags); + ret = vc4_hvs_atomic_check(crtc, state); if (ret) return ret; @@ -640,17 +467,6 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, if (conn_state->crtc != crtc) continue; - /* The writeback connector is implemented using the transposer - * block which is directly taking its data from the HVS FIFO. - */ - if (conn->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) { - state->no_vblank = true; - vc4_state->feed_txp = true; - } else { - state->no_vblank = false; - vc4_state->feed_txp = false; - } - vc4_state->margins.left = conn_state->tv.margins.left; vc4_state->margins.right = conn_state->tv.margins.right; vc4_state->margins.top = conn_state->tv.margins.top; @@ -661,89 +477,6 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, return 0; } -static void vc4_crtc_atomic_flush(struct drm_crtc *crtc, - struct drm_crtc_state *old_state) -{ - struct drm_device *dev = crtc->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); - struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); - struct drm_plane *plane; - struct vc4_plane_state *vc4_plane_state; - bool debug_dump_regs = false; - bool enable_bg_fill = false; - u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start; - u32 __iomem *dlist_next = dlist_start; - - if (debug_dump_regs) { - DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc)); - vc4_hvs_dump_state(dev); - } - - /* Copy all the active planes' dlist contents to the hardware dlist. */ - drm_atomic_crtc_for_each_plane(plane, crtc) { - /* Is this the first active plane? */ - if (dlist_next == dlist_start) { - /* We need to enable background fill when a plane - * could be alpha blending from the background, i.e. - * where no other plane is underneath. It suffices to - * consider the first active plane here since we set - * needs_bg_fill such that either the first plane - * already needs it or all planes on top blend from - * the first or a lower plane. - */ - vc4_plane_state = to_vc4_plane_state(plane->state); - enable_bg_fill = vc4_plane_state->needs_bg_fill; - } - - dlist_next += vc4_plane_write_dlist(plane, dlist_next); - } - - writel(SCALER_CTL0_END, dlist_next); - dlist_next++; - - WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm.size); - - if (enable_bg_fill) - /* This sets a black background color fill, as is the case - * with other DRM drivers. - */ - HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), - HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel)) | - SCALER_DISPBKGND_FILL); - - /* Only update DISPLIST if the CRTC was already running and is not - * being disabled. - * vc4_crtc_enable() takes care of updating the dlist just after - * re-enabling VBLANK interrupts and before enabling the engine. - * If the CRTC is being disabled, there's no point in updating this - * information. - */ - if (crtc->state->active && old_state->active) - vc4_crtc_update_dlist(crtc); - - if (crtc->state->color_mgmt_changed) { - u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel)); - - if (crtc->state->gamma_lut) { - vc4_crtc_update_gamma_lut(crtc); - dispbkgndx |= SCALER_DISPBKGND_GAMMA; - } else { - /* Unsetting DISPBKGND_GAMMA skips the gamma lut step - * in hardware, which is the same as a linear lut that - * DRM expects us to use in absence of a user lut. - */ - dispbkgndx &= ~SCALER_DISPBKGND_GAMMA; - } - HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), dispbkgndx); - } - - if (debug_dump_regs) { - DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc)); - vc4_hvs_dump_state(dev); - } -} - static int vc4_enable_vblank(struct drm_crtc *crtc) { struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); @@ -942,11 +675,11 @@ static int vc4_async_page_flip(struct drm_crtc *crtc, return 0; } -static int vc4_page_flip(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event, - uint32_t flags, - struct drm_modeset_acquire_ctx *ctx) +int vc4_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t flags, + struct drm_modeset_acquire_ctx *ctx) { if (flags & DRM_MODE_PAGE_FLIP_ASYNC) return vc4_async_page_flip(crtc, fb, event, flags); @@ -954,7 +687,7 @@ static int vc4_page_flip(struct drm_crtc *crtc, return drm_atomic_helper_page_flip(crtc, fb, event, flags, ctx); } -static struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc) +struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc) { struct vc4_crtc_state *vc4_state, *old_vc4_state; @@ -970,8 +703,8 @@ static struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc) return &vc4_state->base; } -static void vc4_crtc_destroy_state(struct drm_crtc *crtc, - struct drm_crtc_state *state) +void vc4_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) { struct vc4_dev *vc4 = to_vc4_dev(crtc->dev); struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); @@ -988,15 +721,13 @@ static void vc4_crtc_destroy_state(struct drm_crtc *crtc, drm_atomic_helper_crtc_destroy_state(crtc, state); } -static void -vc4_crtc_reset(struct drm_crtc *crtc) +void vc4_crtc_reset(struct drm_crtc *crtc) { if (crtc->state) vc4_crtc_destroy_state(crtc, crtc->state); - crtc->state = kzalloc(sizeof(struct vc4_crtc_state), GFP_KERNEL); if (crtc->state) - crtc->state->crtc = crtc; + __drm_atomic_helper_crtc_reset(crtc, crtc->state); } static const struct drm_crtc_funcs vc4_crtc_funcs = { @@ -1019,14 +750,16 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { .mode_set_nofb = vc4_crtc_mode_set_nofb, .mode_valid = vc4_crtc_mode_valid, .atomic_check = vc4_crtc_atomic_check, - .atomic_flush = vc4_crtc_atomic_flush, + .atomic_flush = vc4_hvs_atomic_flush, .atomic_enable = vc4_crtc_atomic_enable, .atomic_disable = vc4_crtc_atomic_disable, .get_scanout_position = vc4_crtc_get_scanout_position, }; -static const struct vc4_crtc_data bcm2835_pv0_data = { - .hvs_channel = 0, +static const struct vc4_pv_data bcm2835_pv0_data = { + .base = { + .hvs_channel = 0, + }, .debugfs_name = "crtc0_regs", .encoder_types = { [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI0, @@ -1034,8 +767,10 @@ static const struct vc4_crtc_data bcm2835_pv0_data = { }, }; -static const struct vc4_crtc_data bcm2835_pv1_data = { - .hvs_channel = 2, +static const struct vc4_pv_data bcm2835_pv1_data = { + .base = { + .hvs_channel = 2, + }, .debugfs_name = "crtc1_regs", .encoder_types = { [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI1, @@ -1043,8 +778,10 @@ static const struct vc4_crtc_data bcm2835_pv1_data = { }, }; -static const struct vc4_crtc_data bcm2835_pv2_data = { - .hvs_channel = 1, +static const struct vc4_pv_data bcm2835_pv2_data = { + .base = { + .hvs_channel = 1, + }, .debugfs_name = "crtc2_regs", .encoder_types = { [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_HDMI, @@ -1063,23 +800,16 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm, struct drm_crtc *crtc) { struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); - const struct vc4_crtc_data *crtc_data = vc4_crtc->data; - const enum vc4_encoder_type *encoder_types = crtc_data->encoder_types; + const struct vc4_pv_data *pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc); + const enum vc4_encoder_type *encoder_types = pv_data->encoder_types; struct drm_encoder *encoder; drm_for_each_encoder(encoder, drm) { struct vc4_encoder *vc4_encoder; int i; - /* HVS FIFO2 can feed the TXP IP. */ - if (crtc_data->hvs_channel == 2 && - encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) { - encoder->possible_crtcs |= drm_crtc_mask(crtc); - continue; - } - vc4_encoder = to_vc4_encoder(encoder); - for (i = 0; i < ARRAY_SIZE(crtc_data->encoder_types); i++) { + for (i = 0; i < ARRAY_SIZE(pv_data->encoder_types); i++) { if (vc4_encoder->type == encoder_types[i]) { vc4_encoder->clock_select = i; encoder->possible_crtcs |= drm_crtc_mask(crtc); @@ -1105,15 +835,57 @@ vc4_crtc_get_cob_allocation(struct vc4_crtc *vc4_crtc) vc4_crtc->cob_size = top - base + 4; } +int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc, + const struct drm_crtc_funcs *crtc_funcs, + const struct drm_crtc_helper_funcs *crtc_helper_funcs) +{ + struct drm_crtc *crtc = &vc4_crtc->base; + struct drm_plane *primary_plane; + unsigned int i; + + /* For now, we create just the primary and the legacy cursor + * planes. We should be able to stack more planes on easily, + * but to do that we would need to compute the bandwidth + * requirement of the plane configuration, and reject ones + * that will take too much. + */ + primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY); + if (IS_ERR(primary_plane)) { + dev_err(drm->dev, "failed to construct primary plane\n"); + return PTR_ERR(primary_plane); + } + + drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, + crtc_funcs, NULL); + drm_crtc_helper_add(crtc, crtc_helper_funcs); + vc4_crtc->channel = vc4_crtc->data->hvs_channel; + drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); + drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size); + + /* We support CTM, but only for one CRTC at a time. It's therefore + * implemented as private driver state in vc4_kms, not here. + */ + drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size); + vc4_crtc_get_cob_allocation(vc4_crtc); + + for (i = 0; i < crtc->gamma_size; i++) { + vc4_crtc->lut_r[i] = i; + vc4_crtc->lut_g[i] = i; + vc4_crtc->lut_b[i] = i; + } + + return 0; +} + static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = dev_get_drvdata(master); - const struct vc4_crtc_data *pv_data; + const struct vc4_pv_data *pv_data; struct vc4_crtc *vc4_crtc; struct drm_crtc *crtc; - struct drm_plane *primary_plane, *destroy_plane, *temp; - int ret, i; + struct drm_plane *destroy_plane, *temp; + int ret; vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL); if (!vc4_crtc) @@ -1123,7 +895,7 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) pv_data = of_device_get_match_data(dev); if (!pv_data) return -ENODEV; - vc4_crtc->data = pv_data; + vc4_crtc->data = &pv_data->base; vc4_crtc->pdev = pdev; vc4_crtc->regs = vc4_ioremap_regs(pdev, 0); @@ -1134,32 +906,11 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) vc4_crtc->regset.regs = crtc_regs; vc4_crtc->regset.nregs = ARRAY_SIZE(crtc_regs); - /* For now, we create just the primary and the legacy cursor - * planes. We should be able to stack more planes on easily, - * but to do that we would need to compute the bandwidth - * requirement of the plane configuration, and reject ones - * that will take too much. - */ - primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY); - if (IS_ERR(primary_plane)) { - dev_err(dev, "failed to construct primary plane\n"); - ret = PTR_ERR(primary_plane); - goto err; - } - - drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, - &vc4_crtc_funcs, NULL); - drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs); - vc4_crtc->channel = vc4_crtc->data->hvs_channel; - drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); - drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size); - - /* We support CTM, but only for one CRTC at a time. It's therefore - * implemented as private driver state in vc4_kms, not here. - */ - drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size); - - vc4_crtc_get_cob_allocation(vc4_crtc); + ret = vc4_crtc_init(drm, vc4_crtc, + &vc4_crtc_funcs, &vc4_crtc_helper_funcs); + if (ret) + return ret; + vc4_set_crtc_possible_masks(drm, crtc); CRTC_WRITE(PV_INTEN, 0); CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START); @@ -1168,14 +919,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) if (ret) goto err_destroy_planes; - vc4_set_crtc_possible_masks(drm, crtc); - - for (i = 0; i < crtc->gamma_size; i++) { - vc4_crtc->lut_r[i] = i; - vc4_crtc->lut_g[i] = i; - vc4_crtc->lut_b[i] = i; - } - platform_set_drvdata(pdev, vc4_crtc); vc4_debugfs_add_regset32(drm, pv_data->debugfs_name, @@ -1189,7 +932,7 @@ err_destroy_planes: if (destroy_plane->possible_crtcs == drm_crtc_mask(crtc)) destroy_plane->funcs->destroy(destroy_plane); } -err: + return ret; } diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 7792c97d4303..38343d2fb4fb 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -343,8 +343,8 @@ static struct platform_driver *const component_drivers[] = { &vc4_vec_driver, &vc4_dpi_driver, &vc4_dsi_driver, - &vc4_txp_driver, &vc4_hvs_driver, + &vc4_txp_driver, &vc4_crtc_driver, &vc4_v3d_driver, }; diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 9866d61bfa88..fa19160c801f 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -443,9 +443,14 @@ to_vc4_encoder(struct drm_encoder *encoder) struct vc4_crtc_data { /* Which channel of the HVS this pixelvalve sources from. */ int hvs_channel; +}; + +struct vc4_pv_data { + struct vc4_crtc_data base; enum vc4_encoder_type encoder_types[4]; const char *debugfs_name; + }; struct vc4_crtc { @@ -477,6 +482,20 @@ to_vc4_crtc(struct drm_crtc *crtc) return (struct vc4_crtc *)crtc; } +static inline const struct vc4_crtc_data * +vc4_crtc_to_vc4_crtc_data(const struct vc4_crtc *crtc) +{ + return crtc->data; +} + +static inline const struct vc4_pv_data * +vc4_crtc_to_vc4_pv_data(const struct vc4_crtc *crtc) +{ + const struct vc4_crtc_data *data = vc4_crtc_to_vc4_crtc_data(crtc); + + return container_of(data, struct vc4_pv_data, base); +} + struct vc4_crtc_state { struct drm_crtc_state base; /* Dlist area for this CRTC configuration. */ @@ -775,8 +794,20 @@ void vc4_bo_remove_from_purgeable_pool(struct vc4_bo *bo); /* vc4_crtc.c */ extern struct platform_driver vc4_crtc_driver; +int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc, + const struct drm_crtc_funcs *crtc_funcs, + const struct drm_crtc_helper_funcs *crtc_helper_funcs); +void vc4_crtc_destroy(struct drm_crtc *crtc); +int vc4_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t flags, + struct drm_modeset_acquire_ctx *ctx); +struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc); +void vc4_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); +void vc4_crtc_reset(struct drm_crtc *crtc); void vc4_crtc_handle_vblank(struct vc4_crtc *crtc); -void vc4_crtc_txp_armed(struct drm_crtc_state *state); void vc4_crtc_get_margins(struct drm_crtc_state *state, unsigned int *right, unsigned int *left, unsigned int *top, unsigned int *bottom); @@ -857,6 +888,11 @@ void vc4_irq_reset(struct drm_device *dev); /* vc4_hvs.c */ extern struct platform_driver vc4_hvs_driver; +int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state); +void vc4_hvs_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state); +void vc4_hvs_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state); +void vc4_hvs_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *state); +void vc4_hvs_mode_set_nofb(struct drm_crtc *crtc); void vc4_hvs_dump_state(struct drm_device *dev); void vc4_hvs_unmask_underrun(struct drm_device *dev, int channel); void vc4_hvs_mask_underrun(struct drm_device *dev, int channel); diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index 5a43659da319..2d2bf59c0503 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -23,6 +23,7 @@ #include <linux/platform_device.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_vblank.h> #include "vc4_drv.h" #include "vc4_regs.h" @@ -154,6 +155,296 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs, return 0; } +static void vc4_hvs_lut_load(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + u32 i; + + /* The LUT memory is laid out with each HVS channel in order, + * each of which takes 256 writes for R, 256 for G, then 256 + * for B. + */ + HVS_WRITE(SCALER_GAMADDR, + SCALER_GAMADDR_AUTOINC | + (vc4_crtc->channel * 3 * crtc->gamma_size)); + + for (i = 0; i < crtc->gamma_size; i++) + HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_r[i]); + for (i = 0; i < crtc->gamma_size; i++) + HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_g[i]); + for (i = 0; i < crtc->gamma_size; i++) + HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_b[i]); +} + +static void vc4_hvs_update_gamma_lut(struct drm_crtc *crtc) +{ + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct drm_color_lut *lut = crtc->state->gamma_lut->data; + u32 length = drm_color_lut_size(crtc->state->gamma_lut); + u32 i; + + for (i = 0; i < length; i++) { + vc4_crtc->lut_r[i] = drm_color_lut_extract(lut[i].red, 8); + vc4_crtc->lut_g[i] = drm_color_lut_extract(lut[i].green, 8); + vc4_crtc->lut_b[i] = drm_color_lut_extract(lut[i].blue, 8); + } + + vc4_hvs_lut_load(crtc); +} + +int vc4_hvs_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct drm_plane *plane; + unsigned long flags; + const struct drm_plane_state *plane_state; + u32 dlist_count = 0; + int ret; + + /* The pixelvalve can only feed one encoder (and encoders are + * 1:1 with connectors.) + */ + if (hweight32(state->connector_mask) > 1) + return -EINVAL; + + drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, state) + dlist_count += vc4_plane_dlist_size(plane_state); + + dlist_count++; /* Account for SCALER_CTL0_END. */ + + spin_lock_irqsave(&vc4->hvs->mm_lock, flags); + ret = drm_mm_insert_node(&vc4->hvs->dlist_mm, &vc4_state->mm, + dlist_count); + spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags); + if (ret) + return ret; + + return 0; +} + +static void vc4_hvs_update_dlist(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + + if (crtc->state->event) { + unsigned long flags; + + crtc->state->event->pipe = drm_crtc_index(crtc); + + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + spin_lock_irqsave(&dev->event_lock, flags); + + if (!vc4_state->feed_txp || vc4_state->txp_armed) { + vc4_crtc->event = crtc->state->event; + crtc->state->event = NULL; + } + + HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), + vc4_state->mm.start); + + spin_unlock_irqrestore(&dev->event_lock, flags); + } else { + HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), + vc4_state->mm.start); + } +} + +void vc4_hvs_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + bool oneshot = vc4_state->feed_txp; + u32 dispctrl; + + vc4_hvs_update_dlist(crtc); + + /* Turn on the scaler, which will wait for vstart to start + * compositing. + * When feeding the transposer, we should operate in oneshot + * mode. + */ + dispctrl = SCALER_DISPCTRLX_ENABLE; + dispctrl |= VC4_SET_FIELD(mode->hdisplay, + SCALER_DISPCTRLX_WIDTH) | + VC4_SET_FIELD(mode->vdisplay, + SCALER_DISPCTRLX_HEIGHT) | + (oneshot ? SCALER_DISPCTRLX_ONESHOT : 0); + + HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), dispctrl); +} + +void vc4_hvs_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + u32 chan = vc4_crtc->channel; + + if (HVS_READ(SCALER_DISPCTRLX(chan)) & + SCALER_DISPCTRLX_ENABLE) { + HVS_WRITE(SCALER_DISPCTRLX(chan), + SCALER_DISPCTRLX_RESET); + + /* While the docs say that reset is self-clearing, it + * seems it doesn't actually. + */ + HVS_WRITE(SCALER_DISPCTRLX(chan), 0); + } + + /* Once we leave, the scaler should be disabled and its fifo empty. */ + + WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_RESET); + + WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(chan)), + SCALER_DISPSTATX_MODE) != + SCALER_DISPSTATX_MODE_DISABLED); + + WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) & + (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) != + SCALER_DISPSTATX_EMPTY); +} + +void vc4_hvs_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + struct drm_plane *plane; + struct vc4_plane_state *vc4_plane_state; + bool debug_dump_regs = false; + bool enable_bg_fill = false; + u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start; + u32 __iomem *dlist_next = dlist_start; + + if (debug_dump_regs) { + DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc)); + vc4_hvs_dump_state(dev); + } + + /* Copy all the active planes' dlist contents to the hardware dlist. */ + drm_atomic_crtc_for_each_plane(plane, crtc) { + /* Is this the first active plane? */ + if (dlist_next == dlist_start) { + /* We need to enable background fill when a plane + * could be alpha blending from the background, i.e. + * where no other plane is underneath. It suffices to + * consider the first active plane here since we set + * needs_bg_fill such that either the first plane + * already needs it or all planes on top blend from + * the first or a lower plane. + */ + vc4_plane_state = to_vc4_plane_state(plane->state); + enable_bg_fill = vc4_plane_state->needs_bg_fill; + } + + dlist_next += vc4_plane_write_dlist(plane, dlist_next); + } + + writel(SCALER_CTL0_END, dlist_next); + dlist_next++; + + WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm.size); + + if (enable_bg_fill) + /* This sets a black background color fill, as is the case + * with other DRM drivers. + */ + HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), + HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel)) | + SCALER_DISPBKGND_FILL); + + /* Only update DISPLIST if the CRTC was already running and is not + * being disabled. + * vc4_crtc_enable() takes care of updating the dlist just after + * re-enabling VBLANK interrupts and before enabling the engine. + * If the CRTC is being disabled, there's no point in updating this + * information. + */ + if (crtc->state->active && old_state->active) + vc4_hvs_update_dlist(crtc); + + if (crtc->state->color_mgmt_changed) { + u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel)); + + if (crtc->state->gamma_lut) { + vc4_hvs_update_gamma_lut(crtc); + dispbkgndx |= SCALER_DISPBKGND_GAMMA; + } else { + /* Unsetting DISPBKGND_GAMMA skips the gamma lut step + * in hardware, which is the same as a linear lut that + * DRM expects us to use in absence of a user lut. + */ + dispbkgndx &= ~SCALER_DISPBKGND_GAMMA; + } + HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), dispbkgndx); + } + + if (debug_dump_regs) { + DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc)); + vc4_hvs_dump_state(dev); + } +} + +void vc4_hvs_mode_set_nofb(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE; + + if (vc4_crtc->data->hvs_channel == 2) { + u32 dispctrl; + u32 dsp3_mux; + + /* + * SCALER_DISPCTRL_DSP3 = X, where X < 2 means 'connect DSP3 to + * FIFO X'. + * SCALER_DISPCTRL_DSP3 = 3 means 'disable DSP 3'. + * + * DSP3 is connected to FIFO2 unless the transposer is + * enabled. In this case, FIFO 2 is directly accessed by the + * TXP IP, and we need to disable the FIFO2 -> pixelvalve1 + * route. + */ + if (vc4_state->feed_txp) + dsp3_mux = VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX); + else + dsp3_mux = VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX); + + dispctrl = HVS_READ(SCALER_DISPCTRL) & + ~SCALER_DISPCTRL_DSP3_MUX_MASK; + HVS_WRITE(SCALER_DISPCTRL, dispctrl | dsp3_mux); + } + + HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), + SCALER_DISPBKGND_AUTOHS | + SCALER_DISPBKGND_GAMMA | + (interlace ? SCALER_DISPBKGND_INTERLACE : 0)); + + /* Reload the LUT, since the SRAMs would have been disabled if + * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once. + */ + vc4_hvs_lut_load(crtc); +} + void vc4_hvs_mask_underrun(struct drm_device *dev, int channel) { struct vc4_dev *vc4 = to_vc4_dev(dev); diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index b5a6b4cdd332..324462cc9cd4 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -6,19 +6,18 @@ #ifndef VC4_REGS_H #define VC4_REGS_H +#include <linux/bitfield.h> #include <linux/bitops.h> #define VC4_MASK(high, low) ((u32)GENMASK(high, low)) /* Using the GNU statement expression extension */ #define VC4_SET_FIELD(value, field) \ ({ \ - uint32_t fieldval = (value) << field##_SHIFT; \ - WARN_ON((fieldval & ~field##_MASK) != 0); \ - fieldval & field##_MASK; \ + WARN_ON(!FIELD_FIT(field##_MASK, value)); \ + FIELD_PREP(field##_MASK, value); \ }) -#define VC4_GET_FIELD(word, field) (((word) & field##_MASK) >> \ - field##_SHIFT) +#define VC4_GET_FIELD(word, field) FIELD_GET(field##_MASK, word) #define V3D_IDENT0 0x00000 # define V3D_EXPECTED_IDENT0 \ diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index bf720206727f..a7c3af0005a0 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -19,6 +19,7 @@ #include <drm/drm_fourcc.h> #include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h> #include <drm/drm_writeback.h> #include "vc4_drv.h" @@ -145,6 +146,8 @@ #define TXP_WRITE(offset, val) writel(val, txp->regs + (offset)) struct vc4_txp { + struct vc4_crtc base; + struct platform_device *pdev; struct drm_writeback_connector connector; @@ -222,6 +225,13 @@ static const u32 txp_fmts[] = { TXP_FORMAT_BGRA8888, }; +static void vc4_txp_armed(struct drm_crtc_state *state) +{ + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); + + vc4_state->txp_armed = true; +} + static int vc4_txp_connector_atomic_check(struct drm_connector *conn, struct drm_atomic_state *state) { @@ -256,7 +266,7 @@ static int vc4_txp_connector_atomic_check(struct drm_connector *conn, if (fb->pitches[0] & GENMASK(3, 0)) return -EINVAL; - vc4_crtc_txp_armed(crtc_state); + vc4_txp_armed(crtc_state); return 0; } @@ -355,23 +365,105 @@ static const struct drm_encoder_helper_funcs vc4_txp_encoder_helper_funcs = { .disable = vc4_txp_encoder_disable, }; +static int vc4_txp_enable_vblank(struct drm_crtc *crtc) +{ + return 0; +} + +static void vc4_txp_disable_vblank(struct drm_crtc *crtc) {} + +static const struct drm_crtc_funcs vc4_txp_crtc_funcs = { + .set_config = drm_atomic_helper_set_config, + .destroy = vc4_crtc_destroy, + .page_flip = vc4_page_flip, + .reset = vc4_crtc_reset, + .atomic_duplicate_state = vc4_crtc_duplicate_state, + .atomic_destroy_state = vc4_crtc_destroy_state, + .gamma_set = drm_atomic_helper_legacy_gamma_set, + .enable_vblank = vc4_txp_enable_vblank, + .disable_vblank = vc4_txp_disable_vblank, +}; + +static int vc4_txp_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); + int ret; + + ret = vc4_hvs_atomic_check(crtc, state); + if (ret) + return ret; + + state->no_vblank = true; + vc4_state->feed_txp = true; + + return 0; +} + +static void vc4_txp_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + drm_crtc_vblank_on(crtc); + vc4_hvs_atomic_enable(crtc, old_state); +} + +static void vc4_txp_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct drm_device *dev = crtc->dev; + + /* Disable vblank irq handling before crtc is disabled. */ + drm_crtc_vblank_off(crtc); + + vc4_hvs_atomic_disable(crtc, old_state); + + /* + * Make sure we issue a vblank event after disabling the CRTC if + * someone was waiting it. + */ + if (crtc->state->event) { + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + spin_unlock_irqrestore(&dev->event_lock, flags); + } +} + +static const struct drm_crtc_helper_funcs vc4_txp_crtc_helper_funcs = { + .atomic_check = vc4_txp_atomic_check, + .atomic_flush = vc4_hvs_atomic_flush, + .atomic_enable = vc4_txp_atomic_enable, + .atomic_disable = vc4_txp_atomic_disable, + .mode_set_nofb = vc4_hvs_mode_set_nofb, +}; + static irqreturn_t vc4_txp_interrupt(int irq, void *data) { struct vc4_txp *txp = data; + struct vc4_crtc *vc4_crtc = &txp->base; TXP_WRITE(TXP_DST_CTRL, TXP_READ(TXP_DST_CTRL) & ~TXP_EI); - vc4_crtc_handle_vblank(to_vc4_crtc(txp->connector.base.state->crtc)); + vc4_crtc_handle_vblank(vc4_crtc); drm_writeback_signal_completion(&txp->connector, 0); return IRQ_HANDLED; } +static const struct vc4_crtc_data vc4_txp_crtc_data = { + .hvs_channel = 2, +}; + static int vc4_txp_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = dev_get_drvdata(master); struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_crtc *vc4_crtc; struct vc4_txp *txp; + struct drm_crtc *crtc; + struct drm_encoder *encoder; int ret, irq; irq = platform_get_irq(pdev, 0); @@ -381,6 +473,11 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) txp = devm_kzalloc(dev, sizeof(*txp), GFP_KERNEL); if (!txp) return -ENOMEM; + vc4_crtc = &txp->base; + crtc = &vc4_crtc->base; + + vc4_crtc->pdev = pdev; + vc4_crtc->data = &vc4_txp_crtc_data; txp->pdev = pdev; @@ -400,6 +497,14 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) if (ret) return ret; + ret = vc4_crtc_init(drm, vc4_crtc, + &vc4_txp_crtc_funcs, &vc4_txp_crtc_helper_funcs); + if (ret) + return ret; + + encoder = &txp->connector.encoder; + encoder->possible_crtcs |= drm_crtc_mask(crtc); + ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0, dev_name(dev), txp); if (ret) diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c index e4dc7b267a0b..a775feda1cc7 100644 --- a/drivers/gpu/drm/vgem/vgem_drv.c +++ b/drivers/gpu/drm/vgem/vgem_drv.c @@ -230,32 +230,6 @@ static int vgem_gem_dumb_create(struct drm_file *file, struct drm_device *dev, return 0; } -static int vgem_gem_dumb_map(struct drm_file *file, struct drm_device *dev, - uint32_t handle, uint64_t *offset) -{ - struct drm_gem_object *obj; - int ret; - - obj = drm_gem_object_lookup(file, handle); - if (!obj) - return -ENOENT; - - if (!obj->filp) { - ret = -EINVAL; - goto unref; - } - - ret = drm_gem_create_mmap_offset(obj); - if (ret) - goto unref; - - *offset = drm_vma_node_offset_addr(&obj->vma_node); -unref: - drm_gem_object_put(obj); - - return ret; -} - static struct drm_ioctl_desc vgem_ioctls[] = { DRM_IOCTL_DEF_DRV(VGEM_FENCE_ATTACH, vgem_fence_attach_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(VGEM_FENCE_SIGNAL, vgem_fence_signal_ioctl, DRM_RENDER_ALLOW), @@ -446,7 +420,6 @@ static struct drm_driver vgem_driver = { .fops = &vgem_driver_fops, .dumb_create = vgem_gem_dumb_create, - .dumb_map_offset = vgem_gem_dumb_map, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index f3ce49c5a34c..af55b334be2f 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -314,25 +314,6 @@ virtio_gpu_user_framebuffer_create(struct drm_device *dev, return &virtio_gpu_fb->base; } -static void vgdev_atomic_commit_tail(struct drm_atomic_state *state) -{ - struct drm_device *dev = state->dev; - - drm_atomic_helper_commit_modeset_disables(dev, state); - drm_atomic_helper_commit_modeset_enables(dev, state); - drm_atomic_helper_commit_planes(dev, state, 0); - - drm_atomic_helper_fake_vblank(state); - drm_atomic_helper_commit_hw_done(state); - - drm_atomic_helper_wait_for_vblanks(dev, state); - drm_atomic_helper_cleanup_planes(dev, state); -} - -static const struct drm_mode_config_helper_funcs virtio_mode_config_helpers = { - .atomic_commit_tail = vgdev_atomic_commit_tail, -}; - static const struct drm_mode_config_funcs virtio_gpu_mode_funcs = { .fb_create = virtio_gpu_user_framebuffer_create, .atomic_check = drm_atomic_helper_check, @@ -346,7 +327,6 @@ void virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev) drm_mode_config_init(vgdev->ddev); vgdev->ddev->mode_config.quirk_addfb_prefer_host_byte_order = true; vgdev->ddev->mode_config.funcs = &virtio_gpu_mode_funcs; - vgdev->ddev->mode_config.helper_private = &virtio_mode_config_helpers; /* modes will be validated against the framebuffer size */ vgdev->ddev->mode_config.min_width = XRES_MIN; diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index 1e8b2169d834..57a8a397d5e8 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -133,6 +133,8 @@ static int vkms_modeset_init(struct vkms_device *vkmsdev) dev->mode_config.min_height = YRES_MIN; dev->mode_config.max_width = XRES_MAX; dev->mode_config.max_height = YRES_MAX; + dev->mode_config.cursor_width = 512; + dev->mode_config.cursor_height = 512; dev->mode_config.preferred_depth = 24; dev->mode_config.helper_private = &vkms_mode_config_helpers; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c index 8b71bf6b58ef..1e59c019affa 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c @@ -258,7 +258,7 @@ int vmw_bo_pin_in_start_of_vram(struct vmw_private *dev_priv, ret = ttm_bo_validate(bo, &placement, &ctx); /* For some reason we didn't end up at the start of vram */ - WARN_ON(ret == 0 && bo->offset != 0); + WARN_ON(ret == 0 && bo->mem.start != 0); if (!ret) vmw_bo_pin_reserved(buf, true); @@ -317,7 +317,7 @@ void vmw_bo_get_guest_ptr(const struct ttm_buffer_object *bo, { if (bo->mem.mem_type == TTM_PL_VRAM) { ptr->gmrId = SVGA_GMR_FRAMEBUFFER; - ptr->offset = bo->offset; + ptr->offset = bo->mem.start << PAGE_SHIFT; } else { ptr->gmrId = bo->mem.start; ptr->offset = 0; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 367d5b87ee6a..4284c4bd444d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -3696,7 +3696,7 @@ static void vmw_apply_relocations(struct vmw_sw_context *sw_context) bo = &reloc->vbo->base; switch (bo->mem.mem_type) { case TTM_PL_VRAM: - reloc->location->offset += bo->offset; + reloc->location->offset += bo->mem.start << PAGE_SHIFT; reloc->location->gmrId = SVGA_GMR_FRAMEBUFFER; break; case VMW_PL_GMR: diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c index 6941689085ed..a95156fc5db7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c @@ -610,7 +610,7 @@ static int vmw_fifo_emit_dummy_legacy_query(struct vmw_private *dev_priv, if (bo->mem.mem_type == TTM_PL_VRAM) { cmd->body.guestResult.gmrId = SVGA_GMR_FRAMEBUFFER; - cmd->body.guestResult.offset = bo->offset; + cmd->body.guestResult.offset = bo->mem.start << PAGE_SHIFT; } else { cmd->body.guestResult.gmrId = bo->mem.start; cmd->body.guestResult.offset = 0; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c index 7da752ca1c34..4a76fc7114ad 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c @@ -53,8 +53,6 @@ static int vmw_gmrid_man_get_node(struct ttm_mem_type_manager *man, (struct vmwgfx_gmrid_man *)man->priv; int id; - mem->mm_node = NULL; - id = ida_alloc_max(&gman->gmr_ida, gman->max_gmr_ids - 1, GFP_KERNEL); if (id < 0) return (id != -ENOMEM ? 0 : id); @@ -78,7 +76,7 @@ nospace: gman->used_gmr_pages -= bo->num_pages; spin_unlock(&gman->lock); ida_free(&gman->gmr_ida, id); - return 0; + return -ENOSPC; } static void vmw_gmrid_man_put_node(struct ttm_mem_type_manager *man, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 3c97654b5a43..bbce45d142aa 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -629,8 +629,7 @@ void vmw_du_crtc_reset(struct drm_crtc *crtc) return; } - crtc->state = &vcs->base; - crtc->state->crtc = crtc; + __drm_atomic_helper_crtc_reset(crtc, &vcs->base); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c index bf0bc4697959..1d78187eaba6 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c @@ -742,16 +742,13 @@ static int vmw_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, switch (type) { case TTM_PL_SYSTEM: /* System memory */ - - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; man->available_caching = TTM_PL_FLAG_CACHED; man->default_caching = TTM_PL_FLAG_CACHED; break; case TTM_PL_VRAM: /* "On-card" video ram */ man->func = &vmw_thp_func; - man->gpu_offset = 0; - man->flags = TTM_MEMTYPE_FLAG_FIXED | TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = TTM_MEMTYPE_FLAG_FIXED; man->available_caching = TTM_PL_FLAG_CACHED; man->default_caching = TTM_PL_FLAG_CACHED; break; @@ -763,8 +760,6 @@ static int vmw_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, * slots as well as the bo size. */ man->func = &vmw_gmrid_manager_func; - man->gpu_offset = 0; - man->flags = TTM_MEMTYPE_FLAG_CMA | TTM_MEMTYPE_FLAG_MAPPABLE; man->available_caching = TTM_PL_FLAG_CACHED; man->default_caching = TTM_PL_FLAG_CACHED; break; @@ -791,7 +786,6 @@ static int vmw_verify_access(struct ttm_buffer_object *bo, struct file *filp) static int vmw_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) { - struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; struct vmw_private *dev_priv = container_of(bdev, struct vmw_private, bdev); mem->bus.addr = NULL; @@ -799,8 +793,7 @@ static int vmw_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg mem->bus.offset = 0; mem->bus.size = mem->num_pages << PAGE_SHIFT; mem->bus.base = 0; - if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) - return -EINVAL; + switch (mem->mem_type) { case TTM_PL_SYSTEM: case VMW_PL_GMR: @@ -817,15 +810,6 @@ static int vmw_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg return 0; } -static void vmw_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) -{ -} - -static int vmw_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) -{ - return 0; -} - /** * vmw_move_notify - TTM move_notify_callback * @@ -868,7 +852,5 @@ struct ttm_bo_driver vmw_bo_driver = { .verify_access = vmw_verify_access, .move_notify = vmw_move_notify, .swap_notify = vmw_swap_notify, - .fault_reserve_notify = &vmw_ttm_fault_reserve_notify, .io_mem_reserve = &vmw_ttm_io_mem_reserve, - .io_mem_free = &vmw_ttm_io_mem_free, }; diff --git a/drivers/gpu/drm/xlnx/Kconfig b/drivers/gpu/drm/xlnx/Kconfig new file mode 100644 index 000000000000..aa6cd889bd11 --- /dev/null +++ b/drivers/gpu/drm/xlnx/Kconfig @@ -0,0 +1,13 @@ +config DRM_ZYNQMP_DPSUB + tristate "ZynqMP DisplayPort Controller Driver" + depends on ARCH_ZYNQMP || COMPILE_TEST + depends on COMMON_CLK && DRM && OF + select DMA_ENGINE + select DRM_GEM_CMA_HELPER + select DRM_KMS_CMA_HELPER + select DRM_KMS_HELPER + select GENERIC_PHY + help + This is a DRM/KMS driver for ZynqMP DisplayPort controller. Choose + this option if you have a Xilinx ZynqMP SoC with DisplayPort + subsystem. diff --git a/drivers/gpu/drm/xlnx/Makefile b/drivers/gpu/drm/xlnx/Makefile new file mode 100644 index 000000000000..51c24b72217b --- /dev/null +++ b/drivers/gpu/drm/xlnx/Makefile @@ -0,0 +1,2 @@ +zynqmp-dpsub-y := zynqmp_disp.o zynqmp_dpsub.o zynqmp_dp.o +obj-$(CONFIG_DRM_ZYNQMP_DPSUB) += zynqmp-dpsub.o diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c new file mode 100644 index 000000000000..a455cfc1bee5 --- /dev/null +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -0,0 +1,1697 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ZynqMP Display Controller Driver + * + * Copyright (C) 2017 - 2020 Xilinx, Inc. + * + * Authors: + * - Hyun Woo Kwon <hyun.kwon@xilinx.com> + * - Laurent Pinchart <laurent.pinchart@ideasonboard.com> + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_atomic_uapi.h> +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_managed.h> +#include <drm/drm_plane.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_vblank.h> + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_dma.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/spinlock.h> + +#include "zynqmp_disp.h" +#include "zynqmp_disp_regs.h" +#include "zynqmp_dp.h" +#include "zynqmp_dpsub.h" + +/* + * Overview + * -------- + * + * The display controller part of ZynqMP DP subsystem, made of the Audio/Video + * Buffer Manager, the Video Rendering Pipeline (blender) and the Audio Mixer. + * + * +------------------------------------------------------------+ + * +--------+ | +----------------+ +-----------+ | + * | DPDMA | --->| | --> | Video | Video +-------------+ | + * | 4x vid | | | | | Rendering | -+--> | | | +------+ + * | 2x aud | | | Audio/Video | --> | Pipeline | | | DisplayPort |---> | PHY0 | + * +--------+ | | Buffer Manager | +-----------+ | | Source | | +------+ + * | | and STC | +-----------+ | | Controller | | +------+ + * Live Video --->| | --> | Audio | Audio | |---> | PHY1 | + * | | | | Mixer | --+-> | | | +------+ + * Live Audio --->| | --> | | || +-------------+ | + * | +----------------+ +-----------+ || | + * +---------------------------------------||-------------------+ + * vv + * Blended Video and + * Mixed Audio to PL + * + * Only non-live input from the DPDMA and output to the DisplayPort Source + * Controller are currently supported. Interface with the programmable logic + * for live streams is not implemented. + * + * The display controller code creates planes for the DPDMA video and graphics + * layers, and a CRTC for the Video Rendering Pipeline. + */ + +#define ZYNQMP_DISP_AV_BUF_NUM_VID_GFX_BUFFERS 4 +#define ZYNQMP_DISP_AV_BUF_NUM_BUFFERS 6 + +#define ZYNQMP_DISP_NUM_LAYERS 2 +#define ZYNQMP_DISP_MAX_NUM_SUB_PLANES 3 + +/** + * struct zynqmp_disp_format - Display subsystem format information + * @drm_fmt: DRM format (4CC) + * @buf_fmt: AV buffer format + * @bus_fmt: Media bus formats (live formats) + * @swap: Flag to swap R & B for RGB formats, and U & V for YUV formats + * @sf: Scaling factors for color components + */ +struct zynqmp_disp_format { + u32 drm_fmt; + u32 buf_fmt; + u32 bus_fmt; + bool swap; + const u32 *sf; +}; + +/** + * enum zynqmp_disp_id - Layer identifier + * @ZYNQMP_DISP_LAYER_VID: Video layer + * @ZYNQMP_DISP_LAYER_GFX: Graphics layer + */ +enum zynqmp_disp_layer_id { + ZYNQMP_DISP_LAYER_VID, + ZYNQMP_DISP_LAYER_GFX +}; + +/** + * enum zynqmp_disp_layer_mode - Layer mode + * @ZYNQMP_DISP_LAYER_NONLIVE: non-live (memory) mode + * @ZYNQMP_DISP_LAYER_LIVE: live (stream) mode + */ +enum zynqmp_disp_layer_mode { + ZYNQMP_DISP_LAYER_NONLIVE, + ZYNQMP_DISP_LAYER_LIVE +}; + +/** + * struct zynqmp_disp_layer_dma - DMA channel for one data plane of a layer + * @chan: DMA channel + * @xt: Interleaved DMA descriptor template + * @sgl: Data chunk for dma_interleaved_template + */ +struct zynqmp_disp_layer_dma { + struct dma_chan *chan; + struct dma_interleaved_template xt; + struct data_chunk sgl; +}; + +/** + * struct zynqmp_disp_layer_info - Static layer information + * @formats: Array of supported formats + * @num_formats: Number of formats in @formats array + * @num_channels: Number of DMA channels + */ +struct zynqmp_disp_layer_info { + const struct zynqmp_disp_format *formats; + unsigned int num_formats; + unsigned int num_channels; +}; + +/** + * struct zynqmp_disp_layer - Display layer (DRM plane) + * @plane: DRM plane + * @id: Layer ID + * @disp: Back pointer to struct zynqmp_disp + * @info: Static layer information + * @dmas: DMA channels + * @disp_fmt: Current format information + * @drm_fmt: Current DRM format information + * @mode: Current operation mode + */ +struct zynqmp_disp_layer { + struct drm_plane plane; + enum zynqmp_disp_layer_id id; + struct zynqmp_disp *disp; + const struct zynqmp_disp_layer_info *info; + + struct zynqmp_disp_layer_dma dmas[ZYNQMP_DISP_MAX_NUM_SUB_PLANES]; + + const struct zynqmp_disp_format *disp_fmt; + const struct drm_format_info *drm_fmt; + enum zynqmp_disp_layer_mode mode; +}; + +/** + * struct zynqmp_disp_blend - Blender + * @base: Registers I/O base address + */ +struct zynqmp_disp_blend { + void __iomem *base; +}; + +/** + * struct zynqmp_disp_avbuf - Audio/video buffer manager + * @base: Registers I/O base address + */ +struct zynqmp_disp_avbuf { + void __iomem *base; +}; + +/** + * struct zynqmp_disp_audio - Audio mixer + * @base: Registers I/O base address + * @clk: Audio clock + * @clk_from_ps: True of the audio clock comes from PS, false from PL + */ +struct zynqmp_disp_audio { + void __iomem *base; + struct clk *clk; + bool clk_from_ps; +}; + +/** + * struct zynqmp_disp - Display controller + * @dev: Device structure + * @drm: DRM core + * @dpsub: Display subsystem + * @crtc: DRM CRTC + * @blend: Blender (video rendering pipeline) + * @avbuf: Audio/video buffer manager + * @audio: Audio mixer + * @layers: Layers (planes) + * @event: Pending vblank event request + * @pclk: Pixel clock + * @pclk_from_ps: True of the video clock comes from PS, false from PL + */ +struct zynqmp_disp { + struct device *dev; + struct drm_device *drm; + struct zynqmp_dpsub *dpsub; + + struct drm_crtc crtc; + + struct zynqmp_disp_blend blend; + struct zynqmp_disp_avbuf avbuf; + struct zynqmp_disp_audio audio; + + struct zynqmp_disp_layer layers[ZYNQMP_DISP_NUM_LAYERS]; + + struct drm_pending_vblank_event *event; + + struct clk *pclk; + bool pclk_from_ps; +}; + +/* ----------------------------------------------------------------------------- + * Audio/Video Buffer Manager + */ + +static const u32 scaling_factors_444[] = { + ZYNQMP_DISP_AV_BUF_4BIT_SF, + ZYNQMP_DISP_AV_BUF_4BIT_SF, + ZYNQMP_DISP_AV_BUF_4BIT_SF, +}; + +static const u32 scaling_factors_555[] = { + ZYNQMP_DISP_AV_BUF_5BIT_SF, + ZYNQMP_DISP_AV_BUF_5BIT_SF, + ZYNQMP_DISP_AV_BUF_5BIT_SF, +}; + +static const u32 scaling_factors_565[] = { + ZYNQMP_DISP_AV_BUF_5BIT_SF, + ZYNQMP_DISP_AV_BUF_6BIT_SF, + ZYNQMP_DISP_AV_BUF_5BIT_SF, +}; + +static const u32 scaling_factors_666[] = { + ZYNQMP_DISP_AV_BUF_6BIT_SF, + ZYNQMP_DISP_AV_BUF_6BIT_SF, + ZYNQMP_DISP_AV_BUF_6BIT_SF, +}; + +static const u32 scaling_factors_888[] = { + ZYNQMP_DISP_AV_BUF_8BIT_SF, + ZYNQMP_DISP_AV_BUF_8BIT_SF, + ZYNQMP_DISP_AV_BUF_8BIT_SF, +}; + +static const u32 scaling_factors_101010[] = { + ZYNQMP_DISP_AV_BUF_10BIT_SF, + ZYNQMP_DISP_AV_BUF_10BIT_SF, + ZYNQMP_DISP_AV_BUF_10BIT_SF, +}; + +/* List of video layer formats */ +static const struct zynqmp_disp_format avbuf_vid_fmts[] = { + { + .drm_fmt = DRM_FORMAT_VYUY, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_VYUY, + .swap = true, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_UYVY, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_VYUY, + .swap = false, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_YUYV, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUYV, + .swap = false, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_YVYU, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUYV, + .swap = true, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_YUV422, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16, + .swap = false, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_YVU422, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16, + .swap = true, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_YUV444, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24, + .swap = false, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_YVU444, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24, + .swap = true, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_NV16, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI, + .swap = false, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_NV61, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI, + .swap = true, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_BGR888, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888, + .swap = false, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_RGB888, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888, + .swap = true, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_XBGR8888, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGBA8880, + .swap = false, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_XRGB8888, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGBA8880, + .swap = true, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_XBGR2101010, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888_10, + .swap = false, + .sf = scaling_factors_101010, + }, { + .drm_fmt = DRM_FORMAT_XRGB2101010, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888_10, + .swap = true, + .sf = scaling_factors_101010, + }, { + .drm_fmt = DRM_FORMAT_YUV420, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420, + .swap = false, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_YVU420, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420, + .swap = true, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_NV12, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420, + .swap = false, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_NV21, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420, + .swap = true, + .sf = scaling_factors_888, + }, +}; + +/* List of graphics layer formats */ +static const struct zynqmp_disp_format avbuf_gfx_fmts[] = { + { + .drm_fmt = DRM_FORMAT_ABGR8888, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA8888, + .swap = false, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_ARGB8888, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA8888, + .swap = true, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_RGBA8888, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_ABGR8888, + .swap = false, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_BGRA8888, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_ABGR8888, + .swap = true, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_BGR888, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB888, + .swap = false, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_RGB888, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_BGR888, + .swap = false, + .sf = scaling_factors_888, + }, { + .drm_fmt = DRM_FORMAT_RGBA5551, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA5551, + .swap = false, + .sf = scaling_factors_555, + }, { + .drm_fmt = DRM_FORMAT_BGRA5551, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA5551, + .swap = true, + .sf = scaling_factors_555, + }, { + .drm_fmt = DRM_FORMAT_RGBA4444, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA4444, + .swap = false, + .sf = scaling_factors_444, + }, { + .drm_fmt = DRM_FORMAT_BGRA4444, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA4444, + .swap = true, + .sf = scaling_factors_444, + }, { + .drm_fmt = DRM_FORMAT_RGB565, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB565, + .swap = false, + .sf = scaling_factors_565, + }, { + .drm_fmt = DRM_FORMAT_BGR565, + .buf_fmt = ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB565, + .swap = true, + .sf = scaling_factors_565, + }, +}; + +static u32 zynqmp_disp_avbuf_read(struct zynqmp_disp_avbuf *avbuf, int reg) +{ + return readl(avbuf->base + reg); +} + +static void zynqmp_disp_avbuf_write(struct zynqmp_disp_avbuf *avbuf, + int reg, u32 val) +{ + writel(val, avbuf->base + reg); +} + +/** + * zynqmp_disp_avbuf_set_format - Set the input format for a layer + * @avbuf: Audio/video buffer manager + * @layer: The layer ID + * @fmt: The format information + * + * Set the video buffer manager format for @layer to @fmt. + */ +static void zynqmp_disp_avbuf_set_format(struct zynqmp_disp_avbuf *avbuf, + enum zynqmp_disp_layer_id layer, + const struct zynqmp_disp_format *fmt) +{ + unsigned int i; + u32 val; + + val = zynqmp_disp_avbuf_read(avbuf, ZYNQMP_DISP_AV_BUF_FMT); + val &= layer == ZYNQMP_DISP_LAYER_VID + ? ~ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK + : ~ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK; + val |= fmt->buf_fmt; + zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_FMT, val); + + for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_SF; i++) { + unsigned int reg = layer == ZYNQMP_DISP_LAYER_VID + ? ZYNQMP_DISP_AV_BUF_VID_COMP_SF(i) + : ZYNQMP_DISP_AV_BUF_GFX_COMP_SF(i); + + zynqmp_disp_avbuf_write(avbuf, reg, fmt->sf[i]); + } +} + +/** + * zynqmp_disp_avbuf_set_clocks_sources - Set the clocks sources + * @avbuf: Audio/video buffer manager + * @video_from_ps: True if the video clock originates from the PS + * @audio_from_ps: True if the audio clock originates from the PS + * @timings_internal: True if video timings are generated internally + * + * Set the source for the video and audio clocks, as well as for the video + * timings. Clocks can originate from the PS or PL, and timings can be + * generated internally or externally. + */ +static void +zynqmp_disp_avbuf_set_clocks_sources(struct zynqmp_disp_avbuf *avbuf, + bool video_from_ps, bool audio_from_ps, + bool timings_internal) +{ + u32 val = 0; + + if (video_from_ps) + val |= ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_FROM_PS; + if (audio_from_ps) + val |= ZYNQMP_DISP_AV_BUF_CLK_SRC_AUD_FROM_PS; + if (timings_internal) + val |= ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_INTERNAL_TIMING; + + zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_CLK_SRC, val); +} + +/** + * zynqmp_disp_avbuf_enable_channels - Enable buffer channels + * @avbuf: Audio/video buffer manager + * + * Enable all (video and audio) buffer channels. + */ +static void zynqmp_disp_avbuf_enable_channels(struct zynqmp_disp_avbuf *avbuf) +{ + unsigned int i; + u32 val; + + val = ZYNQMP_DISP_AV_BUF_CHBUF_EN | + (ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_MAX << + ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT); + + for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_VID_GFX_BUFFERS; i++) + zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_CHBUF(i), + val); + + val = ZYNQMP_DISP_AV_BUF_CHBUF_EN | + (ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_AUD_MAX << + ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT); + + for (; i < ZYNQMP_DISP_AV_BUF_NUM_BUFFERS; i++) + zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_CHBUF(i), + val); +} + +/** + * zynqmp_disp_avbuf_disable_channels - Disable buffer channels + * @avbuf: Audio/video buffer manager + * + * Disable all (video and audio) buffer channels. + */ +static void zynqmp_disp_avbuf_disable_channels(struct zynqmp_disp_avbuf *avbuf) +{ + unsigned int i; + + for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_BUFFERS; i++) + zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_CHBUF(i), + ZYNQMP_DISP_AV_BUF_CHBUF_FLUSH); +} + +/** + * zynqmp_disp_avbuf_enable_audio - Enable audio + * @avbuf: Audio/video buffer manager + * + * Enable all audio buffers with a non-live (memory) source. + */ +static void zynqmp_disp_avbuf_enable_audio(struct zynqmp_disp_avbuf *avbuf) +{ + u32 val; + + val = zynqmp_disp_avbuf_read(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT); + val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK; + val |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MEM; + val |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN; + zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT, val); +} + +/** + * zynqmp_disp_avbuf_disable_audio - Disable audio + * @avbuf: Audio/video buffer manager + * + * Disable all audio buffers. + */ +static void zynqmp_disp_avbuf_disable_audio(struct zynqmp_disp_avbuf *avbuf) +{ + u32 val; + + val = zynqmp_disp_avbuf_read(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT); + val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK; + val |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_DISABLE; + val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN; + zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT, val); +} + +/** + * zynqmp_disp_avbuf_enable_video - Enable a video layer + * @avbuf: Audio/video buffer manager + * @layer: The layer ID + * @mode: Operating mode of layer + * + * Enable the video/graphics buffer for @layer. + */ +static void zynqmp_disp_avbuf_enable_video(struct zynqmp_disp_avbuf *avbuf, + enum zynqmp_disp_layer_id layer, + enum zynqmp_disp_layer_mode mode) +{ + u32 val; + + val = zynqmp_disp_avbuf_read(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT); + if (layer == ZYNQMP_DISP_LAYER_VID) { + val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; + if (mode == ZYNQMP_DISP_LAYER_NONLIVE) + val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MEM; + else + val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_LIVE; + } else { + val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK; + val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM; + if (mode == ZYNQMP_DISP_LAYER_NONLIVE) + val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM; + else + val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_LIVE; + } + zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT, val); +} + +/** + * zynqmp_disp_avbuf_disable_video - Disable a video layer + * @avbuf: Audio/video buffer manager + * @layer: The layer ID + * + * Disable the video/graphics buffer for @layer. + */ +static void zynqmp_disp_avbuf_disable_video(struct zynqmp_disp_avbuf *avbuf, + enum zynqmp_disp_layer_id layer) +{ + u32 val; + + val = zynqmp_disp_avbuf_read(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT); + if (layer == ZYNQMP_DISP_LAYER_VID) { + val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; + val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_NONE; + } else { + val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK; + val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_DISABLE; + } + zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_OUTPUT, val); +} + +/** + * zynqmp_disp_avbuf_enable - Enable the video pipe + * @avbuf: Audio/video buffer manager + * + * De-assert the video pipe reset. + */ +static void zynqmp_disp_avbuf_enable(struct zynqmp_disp_avbuf *avbuf) +{ + zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_SRST_REG, 0); +} + +/** + * zynqmp_disp_avbuf_disable - Disable the video pipe + * @avbuf: Audio/video buffer manager + * + * Assert the video pipe reset. + */ +static void zynqmp_disp_avbuf_disable(struct zynqmp_disp_avbuf *avbuf) +{ + zynqmp_disp_avbuf_write(avbuf, ZYNQMP_DISP_AV_BUF_SRST_REG, + ZYNQMP_DISP_AV_BUF_SRST_REG_VID_RST); +} + +/* ----------------------------------------------------------------------------- + * Blender (Video Pipeline) + */ + +static void zynqmp_disp_blend_write(struct zynqmp_disp_blend *blend, + int reg, u32 val) +{ + writel(val, blend->base + reg); +} + +/* + * Colorspace conversion matrices. + * + * Hardcode RGB <-> YUV conversion to full-range SDTV for now. + */ +static const u16 csc_zero_matrix[] = { + 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0 +}; + +static const u16 csc_identity_matrix[] = { + 0x1000, 0x0, 0x0, + 0x0, 0x1000, 0x0, + 0x0, 0x0, 0x1000 +}; + +static const u32 csc_zero_offsets[] = { + 0, 0, 0 +}; + +static const u16 csc_rgb_to_sdtv_matrix[] = { + 0x4c9, 0x864, 0x1d3, + 0x7d4d, 0x7ab3, 0x800, + 0x800, 0x794d, 0x7eb3 +}; + +static const u32 csc_rgb_to_sdtv_offsets[] = { + 0x0, 0x8000000, 0x8000000 +}; + +static const u16 csc_sdtv_to_rgb_matrix[] = { + 0x1000, 0x166f, 0x0, + 0x1000, 0x7483, 0x7a7f, + 0x1000, 0x0, 0x1c5a +}; + +static const u32 csc_sdtv_to_rgb_offsets[] = { + 0x0, 0x1800, 0x1800 +}; + +/** + * zynqmp_disp_blend_set_output_format - Set the output format of the blender + * @blend: Blender object + * @format: Output format + * + * Set the output format of the blender to @format. + */ +static void zynqmp_disp_blend_set_output_format(struct zynqmp_disp_blend *blend, + enum zynqmp_dpsub_format format) +{ + static const unsigned int blend_output_fmts[] = { + [ZYNQMP_DPSUB_FORMAT_RGB] = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB, + [ZYNQMP_DPSUB_FORMAT_YCRCB444] = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR444, + [ZYNQMP_DPSUB_FORMAT_YCRCB422] = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR422 + | ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_EN_DOWNSAMPLE, + [ZYNQMP_DPSUB_FORMAT_YONLY] = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YONLY, + }; + + u32 fmt = blend_output_fmts[format]; + const u16 *coeffs; + const u32 *offsets; + unsigned int i; + + zynqmp_disp_blend_write(blend, ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT, fmt); + if (fmt == ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB) { + coeffs = csc_identity_matrix; + offsets = csc_zero_offsets; + } else { + coeffs = csc_rgb_to_sdtv_matrix; + offsets = csc_rgb_to_sdtv_offsets; + } + + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i++) + zynqmp_disp_blend_write(blend, + ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF(i), + coeffs[i]); + + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) + zynqmp_disp_blend_write(blend, + ZYNQMP_DISP_V_BLEND_OUTCSC_OFFSET(i), + offsets[i]); +} + +/** + * zynqmp_disp_blend_set_bg_color - Set the background color + * @blend: Blender object + * @rcr: Red/Cr color component + * @gy: Green/Y color component + * @bcb: Blue/Cb color component + * + * Set the background color to (@rcr, @gy, @bcb), corresponding to the R, G and + * B or Cr, Y and Cb components respectively depending on the selected output + * format. + */ +static void zynqmp_disp_blend_set_bg_color(struct zynqmp_disp_blend *blend, + u32 rcr, u32 gy, u32 bcb) +{ + zynqmp_disp_blend_write(blend, ZYNQMP_DISP_V_BLEND_BG_CLR_0, rcr); + zynqmp_disp_blend_write(blend, ZYNQMP_DISP_V_BLEND_BG_CLR_1, gy); + zynqmp_disp_blend_write(blend, ZYNQMP_DISP_V_BLEND_BG_CLR_2, bcb); +} + +/** + * zynqmp_disp_blend_set_global_alpha - Configure global alpha blending + * @blend: Blender object + * @enable: True to enable global alpha blending + * @alpha: Global alpha value (ignored if @enabled is false) + */ +static void zynqmp_disp_blend_set_global_alpha(struct zynqmp_disp_blend *blend, + bool enable, u32 alpha) +{ + zynqmp_disp_blend_write(blend, ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA, + ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_VALUE(alpha) | + (enable ? ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_EN : 0)); +} + +/** + * zynqmp_disp_blend_layer_set_csc - Configure colorspace conversion for layer + * @blend: Blender object + * @layer: The layer + * @coeffs: Colorspace conversion matrix + * @offsets: Colorspace conversion offsets + * + * Configure the input colorspace conversion matrix and offsets for the @layer. + * Columns of the matrix are automatically swapped based on the input format to + * handle RGB and YCrCb components permutations. + */ +static void zynqmp_disp_blend_layer_set_csc(struct zynqmp_disp_blend *blend, + struct zynqmp_disp_layer *layer, + const u16 *coeffs, + const u32 *offsets) +{ + unsigned int swap[3] = { 0, 1, 2 }; + unsigned int reg; + unsigned int i; + + if (layer->disp_fmt->swap) { + if (layer->drm_fmt->is_yuv) { + /* Swap U and V. */ + swap[1] = 2; + swap[2] = 1; + } else { + /* Swap R and B. */ + swap[0] = 2; + swap[2] = 0; + } + } + + if (layer->id == ZYNQMP_DISP_LAYER_VID) + reg = ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF(0); + else + reg = ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF(0); + + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i += 3, reg += 12) { + zynqmp_disp_blend_write(blend, reg + 0, coeffs[i + swap[0]]); + zynqmp_disp_blend_write(blend, reg + 4, coeffs[i + swap[1]]); + zynqmp_disp_blend_write(blend, reg + 8, coeffs[i + swap[2]]); + } + + if (layer->id == ZYNQMP_DISP_LAYER_VID) + reg = ZYNQMP_DISP_V_BLEND_IN1CSC_OFFSET(0); + else + reg = ZYNQMP_DISP_V_BLEND_IN2CSC_OFFSET(0); + + for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++) + zynqmp_disp_blend_write(blend, reg + i * 4, offsets[i]); +} + +/** + * zynqmp_disp_blend_layer_enable - Enable a layer + * @blend: Blender object + * @layer: The layer + */ +static void zynqmp_disp_blend_layer_enable(struct zynqmp_disp_blend *blend, + struct zynqmp_disp_layer *layer) +{ + const u16 *coeffs; + const u32 *offsets; + u32 val; + + val = (layer->drm_fmt->is_yuv ? + 0 : ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_RGB) | + (layer->drm_fmt->hsub > 1 ? + ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_EN_US : 0); + + zynqmp_disp_blend_write(blend, + ZYNQMP_DISP_V_BLEND_LAYER_CONTROL(layer->id), + val); + + if (layer->drm_fmt->is_yuv) { + coeffs = csc_sdtv_to_rgb_matrix; + offsets = csc_sdtv_to_rgb_offsets; + } else { + coeffs = csc_identity_matrix; + offsets = csc_zero_offsets; + } + + zynqmp_disp_blend_layer_set_csc(blend, layer, coeffs, offsets); +} + +/** + * zynqmp_disp_blend_layer_disable - Disable a layer + * @blend: Blender object + * @layer: The layer + */ +static void zynqmp_disp_blend_layer_disable(struct zynqmp_disp_blend *blend, + struct zynqmp_disp_layer *layer) +{ + zynqmp_disp_blend_write(blend, + ZYNQMP_DISP_V_BLEND_LAYER_CONTROL(layer->id), + 0); + + zynqmp_disp_blend_layer_set_csc(blend, layer, csc_zero_matrix, + csc_zero_offsets); +} + +/* ----------------------------------------------------------------------------- + * Audio Mixer + */ + +static void zynqmp_disp_audio_write(struct zynqmp_disp_audio *audio, + int reg, u32 val) +{ + writel(val, audio->base + reg); +} + +/** + * zynqmp_disp_audio_enable - Enable the audio mixer + * @audio: Audio mixer + * + * Enable the audio mixer by de-asserting the soft reset. The audio state is set to + * default values by the reset, set the default mixer volume explicitly. + */ +static void zynqmp_disp_audio_enable(struct zynqmp_disp_audio *audio) +{ + /* Clear the audio soft reset register as it's an non-reset flop. */ + zynqmp_disp_audio_write(audio, ZYNQMP_DISP_AUD_SOFT_RESET, 0); + zynqmp_disp_audio_write(audio, ZYNQMP_DISP_AUD_MIXER_VOLUME, + ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE); +} + +/** + * zynqmp_disp_audio_disable - Disable the audio mixer + * @audio: Audio mixer + * + * Disable the audio mixer by asserting its soft reset. + */ +static void zynqmp_disp_audio_disable(struct zynqmp_disp_audio *audio) +{ + zynqmp_disp_audio_write(audio, ZYNQMP_DISP_AUD_SOFT_RESET, + ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST); +} + +static void zynqmp_disp_audio_init(struct device *dev, + struct zynqmp_disp_audio *audio) +{ + /* Try the live PL audio clock. */ + audio->clk = devm_clk_get(dev, "dp_live_audio_aclk"); + if (!IS_ERR(audio->clk)) { + audio->clk_from_ps = false; + return; + } + + /* If the live PL audio clock is not valid, fall back to PS clock. */ + audio->clk = devm_clk_get(dev, "dp_aud_clk"); + if (!IS_ERR(audio->clk)) { + audio->clk_from_ps = true; + return; + } + + dev_err(dev, "audio disabled due to missing clock\n"); +} + +/* ----------------------------------------------------------------------------- + * ZynqMP Display external functions for zynqmp_dp + */ + +/** + * zynqmp_disp_handle_vblank - Handle the vblank event + * @disp: Display controller + * + * This function handles the vblank interrupt, and sends an event to + * CRTC object. This will be called by the DP vblank interrupt handler. + */ +void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp) +{ + struct drm_crtc *crtc = &disp->crtc; + + drm_crtc_handle_vblank(crtc); +} + +/** + * zynqmp_disp_audio_enabled - If the audio is enabled + * @disp: Display controller + * + * Return if the audio is enabled depending on the audio clock. + * + * Return: true if audio is enabled, or false. + */ +bool zynqmp_disp_audio_enabled(struct zynqmp_disp *disp) +{ + return !!disp->audio.clk; +} + +/** + * zynqmp_disp_get_audio_clk_rate - Get the current audio clock rate + * @disp: Display controller + * + * Return: the current audio clock rate. + */ +unsigned int zynqmp_disp_get_audio_clk_rate(struct zynqmp_disp *disp) +{ + if (zynqmp_disp_audio_enabled(disp)) + return 0; + return clk_get_rate(disp->audio.clk); +} + +/** + * zynqmp_disp_get_crtc_mask - Return the CRTC bit mask + * @disp: Display controller + * + * Return: the crtc mask of the zyqnmp_disp CRTC. + */ +uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp) +{ + return drm_crtc_mask(&disp->crtc); +} + +/* ----------------------------------------------------------------------------- + * ZynqMP Display Layer & DRM Plane + */ + +/** + * zynqmp_disp_layer_find_format - Find format information for a DRM format + * @layer: The layer + * @drm_fmt: DRM format to search + * + * Search display subsystem format information corresponding to the given DRM + * format @drm_fmt for the @layer, and return a pointer to the format + * descriptor. + * + * Return: A pointer to the format descriptor if found, NULL otherwise + */ +static const struct zynqmp_disp_format * +zynqmp_disp_layer_find_format(struct zynqmp_disp_layer *layer, + u32 drm_fmt) +{ + unsigned int i; + + for (i = 0; i < layer->info->num_formats; i++) { + if (layer->info->formats[i].drm_fmt == drm_fmt) + return &layer->info->formats[i]; + } + + return NULL; +} + +/** + * zynqmp_disp_layer_enable - Enable a layer + * @layer: The layer + * + * Enable the @layer in the audio/video buffer manager and the blender. DMA + * channels are started separately by zynqmp_disp_layer_update(). + */ +static void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer) +{ + zynqmp_disp_avbuf_enable_video(&layer->disp->avbuf, layer->id, + ZYNQMP_DISP_LAYER_NONLIVE); + zynqmp_disp_blend_layer_enable(&layer->disp->blend, layer); + + layer->mode = ZYNQMP_DISP_LAYER_NONLIVE; +} + +/** + * zynqmp_disp_layer_disable - Disable the layer + * @layer: The layer + * + * Disable the layer by stopping its DMA channels and disabling it in the + * audio/video buffer manager and the blender. + */ +static void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer) +{ + unsigned int i; + + for (i = 0; i < layer->drm_fmt->num_planes; i++) + dmaengine_terminate_sync(layer->dmas[i].chan); + + zynqmp_disp_avbuf_disable_video(&layer->disp->avbuf, layer->id); + zynqmp_disp_blend_layer_disable(&layer->disp->blend, layer); +} + +/** + * zynqmp_disp_layer_set_format - Set the layer format + * @layer: The layer + * @state: The plane state + * + * Set the format for @layer based on @state->fb->format. The layer must be + * disabled. + */ +static void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, + struct drm_plane_state *state) +{ + const struct drm_format_info *info = state->fb->format; + unsigned int i; + + layer->disp_fmt = zynqmp_disp_layer_find_format(layer, info->format); + layer->drm_fmt = info; + + zynqmp_disp_avbuf_set_format(&layer->disp->avbuf, layer->id, + layer->disp_fmt); + + /* + * Set slave_id for each DMA channel to indicate they're part of a + * video group. + */ + for (i = 0; i < info->num_planes; i++) { + struct zynqmp_disp_layer_dma *dma = &layer->dmas[i]; + struct dma_slave_config config = { + .direction = DMA_MEM_TO_DEV, + .slave_id = 1, + }; + + dmaengine_slave_config(dma->chan, &config); + } +} + +/** + * zynqmp_disp_layer_update - Update the layer framebuffer + * @layer: The layer + * @state: The plane state + * + * Update the framebuffer for the layer by issuing a new DMA engine transaction + * for the new framebuffer. + * + * Return: 0 on success, or the DMA descriptor failure error otherwise + */ +static int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer, + struct drm_plane_state *state) +{ + const struct drm_format_info *info = layer->drm_fmt; + unsigned int i; + + for (i = 0; i < layer->drm_fmt->num_planes; i++) { + unsigned int width = state->crtc_w / (i ? info->hsub : 1); + unsigned int height = state->crtc_h / (i ? info->vsub : 1); + struct zynqmp_disp_layer_dma *dma = &layer->dmas[i]; + struct dma_async_tx_descriptor *desc; + dma_addr_t paddr; + + paddr = drm_fb_cma_get_gem_addr(state->fb, state, i); + + dma->xt.numf = height; + dma->sgl.size = width * info->cpp[i]; + dma->sgl.icg = state->fb->pitches[i] - dma->sgl.size; + dma->xt.src_start = paddr; + dma->xt.frame_size = 1; + dma->xt.dir = DMA_MEM_TO_DEV; + dma->xt.src_sgl = true; + dma->xt.dst_sgl = false; + + desc = dmaengine_prep_interleaved_dma(dma->chan, &dma->xt, + DMA_CTRL_ACK | + DMA_PREP_REPEAT | + DMA_PREP_LOAD_EOT); + if (!desc) { + dev_err(layer->disp->dev, + "failed to prepare DMA descriptor\n"); + return -ENOMEM; + } + + dmaengine_submit(desc); + dma_async_issue_pending(dma->chan); + } + + return 0; +} + +static inline struct zynqmp_disp_layer *plane_to_layer(struct drm_plane *plane) +{ + return container_of(plane, struct zynqmp_disp_layer, plane); +} + +static int +zynqmp_disp_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_crtc_state *crtc_state; + + if (!state->crtc) + return 0; + + crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + return drm_atomic_helper_check_plane_state(state, crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, false); +} + +static void +zynqmp_disp_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct zynqmp_disp_layer *layer = plane_to_layer(plane); + + if (!old_state->fb) + return; + + zynqmp_disp_layer_disable(layer); +} + +static void +zynqmp_disp_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct zynqmp_disp_layer *layer = plane_to_layer(plane); + bool format_changed = false; + + if (!old_state->fb || + old_state->fb->format->format != plane->state->fb->format->format) + format_changed = true; + + /* + * If the format has changed (including going from a previously + * disabled state to any format), reconfigure the format. Disable the + * plane first if needed. + */ + if (format_changed) { + if (old_state->fb) + zynqmp_disp_layer_disable(layer); + + zynqmp_disp_layer_set_format(layer, plane->state); + } + + zynqmp_disp_layer_update(layer, plane->state); + + /* Enable or re-enable the plane is the format has changed. */ + if (format_changed) + zynqmp_disp_layer_enable(layer); +} + +static const struct drm_plane_helper_funcs zynqmp_disp_plane_helper_funcs = { + .atomic_check = zynqmp_disp_plane_atomic_check, + .atomic_update = zynqmp_disp_plane_atomic_update, + .atomic_disable = zynqmp_disp_plane_atomic_disable, +}; + +static const struct drm_plane_funcs zynqmp_disp_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .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 zynqmp_disp_create_planes(struct zynqmp_disp *disp) +{ + unsigned int i, j; + int ret; + + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { + struct zynqmp_disp_layer *layer = &disp->layers[i]; + enum drm_plane_type type; + u32 *drm_formats; + + drm_formats = drmm_kcalloc(disp->drm, sizeof(*drm_formats), + layer->info->num_formats, + GFP_KERNEL); + if (!drm_formats) + return -ENOMEM; + + for (j = 0; j < layer->info->num_formats; ++j) + drm_formats[j] = layer->info->formats[j].drm_fmt; + + /* Graphics layer is primary, and video layer is overlay. */ + type = i == ZYNQMP_DISP_LAYER_GFX + ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; + ret = drm_universal_plane_init(disp->drm, &layer->plane, 0, + &zynqmp_disp_plane_funcs, + drm_formats, + layer->info->num_formats, + NULL, type, NULL); + if (ret) + return ret; + + drm_plane_helper_add(&layer->plane, + &zynqmp_disp_plane_helper_funcs); + } + + return 0; +} + +/** + * zynqmp_disp_layer_release_dma - Release DMA channels for a layer + * @disp: Display controller + * @layer: The layer + * + * Release the DMA channels associated with @layer. + */ +static void zynqmp_disp_layer_release_dma(struct zynqmp_disp *disp, + struct zynqmp_disp_layer *layer) +{ + unsigned int i; + + if (!layer->info) + return; + + for (i = 0; i < layer->info->num_channels; i++) { + struct zynqmp_disp_layer_dma *dma = &layer->dmas[i]; + + if (!dma->chan) + continue; + + /* Make sure the channel is terminated before release. */ + dmaengine_terminate_sync(dma->chan); + dma_release_channel(dma->chan); + } +} + +/** + * zynqmp_disp_destroy_layers - Destroy all layers + * @disp: Display controller + */ +static void zynqmp_disp_destroy_layers(struct zynqmp_disp *disp) +{ + unsigned int i; + + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) + zynqmp_disp_layer_release_dma(disp, &disp->layers[i]); +} + +/** + * zynqmp_disp_layer_request_dma - Request DMA channels for a layer + * @disp: Display controller + * @layer: The layer + * + * Request all DMA engine channels needed by @layer. + * + * Return: 0 on success, or the DMA channel request error otherwise + */ +static int zynqmp_disp_layer_request_dma(struct zynqmp_disp *disp, + struct zynqmp_disp_layer *layer) +{ + static const char * const dma_names[] = { "vid", "gfx" }; + unsigned int i; + int ret; + + for (i = 0; i < layer->info->num_channels; i++) { + struct zynqmp_disp_layer_dma *dma = &layer->dmas[i]; + char dma_channel_name[16]; + + snprintf(dma_channel_name, sizeof(dma_channel_name), + "%s%u", dma_names[layer->id], i); + dma->chan = of_dma_request_slave_channel(disp->dev->of_node, + dma_channel_name); + if (IS_ERR(dma->chan)) { + dev_err(disp->dev, "failed to request dma channel\n"); + ret = PTR_ERR(dma->chan); + dma->chan = NULL; + return ret; + } + } + + return 0; +} + +/** + * zynqmp_disp_create_layers - Create and initialize all layers + * @disp: Display controller + * + * Return: 0 on success, or the DMA channel request error otherwise + */ +static int zynqmp_disp_create_layers(struct zynqmp_disp *disp) +{ + static const struct zynqmp_disp_layer_info layer_info[] = { + [ZYNQMP_DISP_LAYER_VID] = { + .formats = avbuf_vid_fmts, + .num_formats = ARRAY_SIZE(avbuf_vid_fmts), + .num_channels = 3, + }, + [ZYNQMP_DISP_LAYER_GFX] = { + .formats = avbuf_gfx_fmts, + .num_formats = ARRAY_SIZE(avbuf_gfx_fmts), + .num_channels = 1, + }, + }; + + unsigned int i; + int ret; + + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) { + struct zynqmp_disp_layer *layer = &disp->layers[i]; + + layer->id = i; + layer->disp = disp; + layer->info = &layer_info[i]; + + ret = zynqmp_disp_layer_request_dma(disp, layer); + if (ret) + goto err; + } + + return 0; + +err: + zynqmp_disp_destroy_layers(disp); + return ret; +} + +/* ----------------------------------------------------------------------------- + * ZynqMP Display & DRM CRTC + */ + +/** + * zynqmp_disp_enable - Enable the display controller + * @disp: Display controller + */ +static void zynqmp_disp_enable(struct zynqmp_disp *disp) +{ + zynqmp_disp_avbuf_enable(&disp->avbuf); + /* Choose clock source based on the DT clock handle. */ + zynqmp_disp_avbuf_set_clocks_sources(&disp->avbuf, disp->pclk_from_ps, + disp->audio.clk_from_ps, true); + zynqmp_disp_avbuf_enable_channels(&disp->avbuf); + zynqmp_disp_avbuf_enable_audio(&disp->avbuf); + + zynqmp_disp_audio_enable(&disp->audio); +} + +/** + * zynqmp_disp_disable - Disable the display controller + * @disp: Display controller + */ +static void zynqmp_disp_disable(struct zynqmp_disp *disp) +{ + struct drm_crtc *crtc = &disp->crtc; + + zynqmp_disp_audio_disable(&disp->audio); + + zynqmp_disp_avbuf_disable_audio(&disp->avbuf); + zynqmp_disp_avbuf_disable_channels(&disp->avbuf); + zynqmp_disp_avbuf_disable(&disp->avbuf); + + /* Mark the flip is done as crtc is disabled anyway */ + if (crtc->state->event) { + complete_all(crtc->state->event->base.completion); + crtc->state->event = NULL; + } +} + +static inline struct zynqmp_disp *crtc_to_disp(struct drm_crtc *crtc) +{ + return container_of(crtc, struct zynqmp_disp, crtc); +} + +static int zynqmp_disp_crtc_setup_clock(struct drm_crtc *crtc, + struct drm_display_mode *adjusted_mode) +{ + struct zynqmp_disp *disp = crtc_to_disp(crtc); + unsigned long mode_clock = adjusted_mode->clock * 1000; + unsigned long rate; + long diff; + int ret; + + ret = clk_set_rate(disp->pclk, mode_clock); + if (ret) { + dev_err(disp->dev, "failed to set a pixel clock\n"); + return ret; + } + + rate = clk_get_rate(disp->pclk); + diff = rate - mode_clock; + if (abs(diff) > mode_clock / 20) + dev_info(disp->dev, + "requested pixel rate: %lu actual rate: %lu\n", + mode_clock, rate); + else + dev_dbg(disp->dev, + "requested pixel rate: %lu actual rate: %lu\n", + mode_clock, rate); + + return 0; +} + +static void +zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct zynqmp_disp *disp = crtc_to_disp(crtc); + struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; + int ret, vrefresh; + + zynqmp_disp_crtc_setup_clock(crtc, adjusted_mode); + + pm_runtime_get_sync(disp->dev); + ret = clk_prepare_enable(disp->pclk); + if (ret) { + dev_err(disp->dev, "failed to enable a pixel clock\n"); + pm_runtime_put_sync(disp->dev); + return; + } + + zynqmp_disp_blend_set_output_format(&disp->blend, + ZYNQMP_DPSUB_FORMAT_RGB); + zynqmp_disp_blend_set_bg_color(&disp->blend, 0, 0, 0); + zynqmp_disp_blend_set_global_alpha(&disp->blend, false, 0); + + zynqmp_disp_enable(disp); + + /* Delay of 3 vblank intervals for timing gen to be stable */ + vrefresh = (adjusted_mode->clock * 1000) / + (adjusted_mode->vtotal * adjusted_mode->htotal); + msleep(3 * 1000 / vrefresh); +} + +static void +zynqmp_disp_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct zynqmp_disp *disp = crtc_to_disp(crtc); + struct drm_plane_state *old_plane_state; + + /* + * Disable the plane if active. The old plane state can be NULL in the + * .shutdown() path if the plane is already disabled, skip + * zynqmp_disp_plane_atomic_disable() in that case. + */ + old_plane_state = drm_atomic_get_old_plane_state(old_crtc_state->state, + crtc->primary); + if (old_plane_state) + zynqmp_disp_plane_atomic_disable(crtc->primary, old_plane_state); + + zynqmp_disp_disable(disp); + + drm_crtc_vblank_off(&disp->crtc); + + clk_disable_unprepare(disp->pclk); + pm_runtime_put_sync(disp->dev); +} + +static int zynqmp_disp_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + return drm_atomic_add_affected_planes(state->state, crtc); +} + +static void +zynqmp_disp_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + drm_crtc_vblank_on(crtc); +} + +static void +zynqmp_disp_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + if (crtc->state->event) { + struct drm_pending_vblank_event *event; + + /* Consume the flip_done event from atomic helper. */ + event = crtc->state->event; + crtc->state->event = NULL; + + event->pipe = drm_crtc_index(crtc); + + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_arm_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + } +} + +static const struct drm_crtc_helper_funcs zynqmp_disp_crtc_helper_funcs = { + .atomic_enable = zynqmp_disp_crtc_atomic_enable, + .atomic_disable = zynqmp_disp_crtc_atomic_disable, + .atomic_check = zynqmp_disp_crtc_atomic_check, + .atomic_begin = zynqmp_disp_crtc_atomic_begin, + .atomic_flush = zynqmp_disp_crtc_atomic_flush, +}; + +static int zynqmp_disp_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct zynqmp_disp *disp = crtc_to_disp(crtc); + + zynqmp_dp_enable_vblank(disp->dpsub->dp); + + return 0; +} + +static void zynqmp_disp_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct zynqmp_disp *disp = crtc_to_disp(crtc); + + zynqmp_dp_disable_vblank(disp->dpsub->dp); +} + +static const struct drm_crtc_funcs zynqmp_disp_crtc_funcs = { + .destroy = drm_crtc_cleanup, + .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, + .enable_vblank = zynqmp_disp_crtc_enable_vblank, + .disable_vblank = zynqmp_disp_crtc_disable_vblank, +}; + +static int zynqmp_disp_create_crtc(struct zynqmp_disp *disp) +{ + struct drm_plane *plane = &disp->layers[ZYNQMP_DISP_LAYER_GFX].plane; + int ret; + + ret = drm_crtc_init_with_planes(disp->drm, &disp->crtc, plane, + NULL, &zynqmp_disp_crtc_funcs, NULL); + if (ret < 0) + return ret; + + drm_crtc_helper_add(&disp->crtc, &zynqmp_disp_crtc_helper_funcs); + + /* Start with vertical blanking interrupt reporting disabled. */ + drm_crtc_vblank_off(&disp->crtc); + + return 0; +} + +static void zynqmp_disp_map_crtc_to_plane(struct zynqmp_disp *disp) +{ + u32 possible_crtcs = drm_crtc_mask(&disp->crtc); + unsigned int i; + + for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) + disp->layers[i].plane.possible_crtcs = possible_crtcs; +} + +/* ----------------------------------------------------------------------------- + * Initialization & Cleanup + */ + +int zynqmp_disp_drm_init(struct zynqmp_dpsub *dpsub) +{ + struct zynqmp_disp *disp = dpsub->disp; + int ret; + + ret = zynqmp_disp_create_planes(disp); + if (ret) + return ret; + + ret = zynqmp_disp_create_crtc(disp); + if (ret < 0) + return ret; + + zynqmp_disp_map_crtc_to_plane(disp); + + return 0; +} + +int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) +{ + struct platform_device *pdev = to_platform_device(dpsub->dev); + struct zynqmp_disp *disp; + struct zynqmp_disp_layer *layer; + struct resource *res; + int ret; + + disp = drmm_kzalloc(drm, sizeof(*disp), GFP_KERNEL); + if (!disp) + return -ENOMEM; + + disp->dev = &pdev->dev; + disp->dpsub = dpsub; + disp->drm = drm; + + dpsub->disp = disp; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "blend"); + disp->blend.base = devm_ioremap_resource(disp->dev, res); + if (IS_ERR(disp->blend.base)) + return PTR_ERR(disp->blend.base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "av_buf"); + disp->avbuf.base = devm_ioremap_resource(disp->dev, res); + if (IS_ERR(disp->avbuf.base)) + return PTR_ERR(disp->avbuf.base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aud"); + disp->audio.base = devm_ioremap_resource(disp->dev, res); + if (IS_ERR(disp->audio.base)) + return PTR_ERR(disp->audio.base); + + /* Try the live PL video clock */ + disp->pclk = devm_clk_get(disp->dev, "dp_live_video_in_clk"); + if (!IS_ERR(disp->pclk)) + disp->pclk_from_ps = false; + else if (PTR_ERR(disp->pclk) == -EPROBE_DEFER) + return PTR_ERR(disp->pclk); + + /* If the live PL video clock is not valid, fall back to PS clock */ + if (IS_ERR_OR_NULL(disp->pclk)) { + disp->pclk = devm_clk_get(disp->dev, "dp_vtc_pixel_clk_in"); + if (IS_ERR(disp->pclk)) { + dev_err(disp->dev, "failed to init any video clock\n"); + return PTR_ERR(disp->pclk); + } + disp->pclk_from_ps = true; + } + + zynqmp_disp_audio_init(disp->dev, &disp->audio); + + ret = zynqmp_disp_create_layers(disp); + if (ret) + return ret; + + layer = &disp->layers[ZYNQMP_DISP_LAYER_VID]; + dpsub->dma_align = 1 << layer->dmas[0].chan->device->copy_align; + + return 0; +} + +void zynqmp_disp_remove(struct zynqmp_dpsub *dpsub) +{ + struct zynqmp_disp *disp = dpsub->disp; + + zynqmp_disp_destroy_layers(disp); +} diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.h b/drivers/gpu/drm/xlnx/zynqmp_disp.h new file mode 100644 index 000000000000..f402901afb23 --- /dev/null +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ZynqMP Display Driver + * + * Copyright (C) 2017 - 2020 Xilinx, Inc. + * + * Authors: + * - Hyun Woo Kwon <hyun.kwon@xilinx.com> + * - Laurent Pinchart <laurent.pinchart@ideasonboard.com> + */ + +#ifndef _ZYNQMP_DISP_H_ +#define _ZYNQMP_DISP_H_ + +#include <linux/types.h> + +/* + * 3840x2160 is advertised as the maximum resolution, but almost any + * resolutions under a 300Mhz pixel rate would work. Pick 4096x4096. + */ +#define ZYNQMP_DISP_MAX_WIDTH 4096 +#define ZYNQMP_DISP_MAX_HEIGHT 4096 + +/* The DPDMA is limited to 44 bit addressing. */ +#define ZYNQMP_DISP_MAX_DMA_BIT 44 + +struct device; +struct drm_device; +struct platform_device; +struct zynqmp_disp; +struct zynqmp_dpsub; + +void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp); +bool zynqmp_disp_audio_enabled(struct zynqmp_disp *disp); +unsigned int zynqmp_disp_get_audio_clk_rate(struct zynqmp_disp *disp); +uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp); + +int zynqmp_disp_drm_init(struct zynqmp_dpsub *dpsub); +int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm); +void zynqmp_disp_remove(struct zynqmp_dpsub *dpsub); + +#endif /* _ZYNQMP_DISP_H_ */ diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp_regs.h b/drivers/gpu/drm/xlnx/zynqmp_disp_regs.h new file mode 100644 index 000000000000..f92a006d5070 --- /dev/null +++ b/drivers/gpu/drm/xlnx/zynqmp_disp_regs.h @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ZynqMP Display Controller Driver - Register Definitions + * + * Copyright (C) 2017 - 2020 Xilinx, Inc. + * + * Authors: + * - Hyun Woo Kwon <hyun.kwon@xilinx.com> + * - Laurent Pinchart <laurent.pinchart@ideasonboard.com> + */ + +#ifndef _ZYNQMP_DISP_REGS_H_ +#define _ZYNQMP_DISP_REGS_H_ + +#include <linux/bits.h> + +/* Blender registers */ +#define ZYNQMP_DISP_V_BLEND_BG_CLR_0 0x0 +#define ZYNQMP_DISP_V_BLEND_BG_CLR_1 0x4 +#define ZYNQMP_DISP_V_BLEND_BG_CLR_2 0x8 +#define ZYNQMP_DISP_V_BLEND_BG_MAX 0xfff +#define ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA 0xc +#define ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_VALUE(n) ((n) << 1) +#define ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_EN BIT(0) +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT 0x14 +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB 0x0 +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR444 0x1 +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR422 0x2 +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YONLY 0x3 +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_XVYCC 0x4 +#define ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_EN_DOWNSAMPLE BIT(4) +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL(n) (0x18 + ((n) * 4)) +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_EN_US BIT(0) +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_RGB BIT(1) +#define ZYNQMP_DISP_V_BLEND_LAYER_CONTROL_BYPASS BIT(8) +#define ZYNQMP_DISP_V_BLEND_NUM_COEFF 9 +#define ZYNQMP_DISP_V_BLEND_NUM_OFFSET 3 +#define ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF(n) (0x20 + ((n) * 4)) +#define ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF(n) (0x44 + ((n) * 4)) +#define ZYNQMP_DISP_V_BLEND_IN1CSC_OFFSET(n) (0x68 + ((n) * 4)) +#define ZYNQMP_DISP_V_BLEND_OUTCSC_OFFSET(n) (0x74 + ((n) * 4)) +#define ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF(n) (0x80 + ((n) * 4)) +#define ZYNQMP_DISP_V_BLEND_IN2CSC_OFFSET(n) (0xa4 + ((n) * 4)) +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_ENABLE 0x1d0 +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_COMP1 0x1d4 +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_COMP2 0x1d8 +#define ZYNQMP_DISP_V_BLEND_CHROMA_KEY_COMP3 0x1dc + +/* AV buffer manager registers */ +#define ZYNQMP_DISP_AV_BUF_FMT 0x0 +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_SHIFT 0 +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK (0x1f << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_UYVY (0 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_VYUY (1 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YVYU (2 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUYV (3 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16 (4 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24 (5 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI (6 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MONO (7 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2 (8 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUV444 (9 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888 (10 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGBA8880 (11 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_RGB888_10 (12 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YUV444_10 (13 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2_10 (14 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_10 (15 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_10 (16 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV24_10 (17 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YONLY_10 (18 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420 (19 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420 (20 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2_420 (21 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16_420_10 (22 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI_420_10 (23 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_VID_YV16CI2_420_10 (24 << 0) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_SHIFT 8 +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK (0xf << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA8888 (0 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_ABGR8888 (1 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB888 (2 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_BGR888 (3 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA5551 (4 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGBA4444 (5 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_RGB565 (6 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_8BPP (7 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_4BPP (8 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_2BPP (9 << 8) +#define ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_1BPP (10 << 8) +#define ZYNQMP_DISP_AV_BUF_NON_LIVE_LATENCY 0x8 +#define ZYNQMP_DISP_AV_BUF_CHBUF(n) (0x10 + ((n) * 4)) +#define ZYNQMP_DISP_AV_BUF_CHBUF_EN BIT(0) +#define ZYNQMP_DISP_AV_BUF_CHBUF_FLUSH BIT(1) +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_SHIFT 2 +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_MASK (0xf << 2) +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_MAX 0xf +#define ZYNQMP_DISP_AV_BUF_CHBUF_BURST_LEN_AUD_MAX 0x3 +#define ZYNQMP_DISP_AV_BUF_STATUS 0x28 +#define ZYNQMP_DISP_AV_BUF_STC_CTRL 0x2c +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EN BIT(0) +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_SHIFT 1 +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_EX_VSYNC 0 +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_EX_VID 1 +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_EX_AUD 2 +#define ZYNQMP_DISP_AV_BUF_STC_CTRL_EVENT_INT_VSYNC 3 +#define ZYNQMP_DISP_AV_BUF_STC_INIT_VALUE0 0x30 +#define ZYNQMP_DISP_AV_BUF_STC_INIT_VALUE1 0x34 +#define ZYNQMP_DISP_AV_BUF_STC_ADJ 0x38 +#define ZYNQMP_DISP_AV_BUF_STC_VID_VSYNC_TS0 0x3c +#define ZYNQMP_DISP_AV_BUF_STC_VID_VSYNC_TS1 0x40 +#define ZYNQMP_DISP_AV_BUF_STC_EXT_VSYNC_TS0 0x44 +#define ZYNQMP_DISP_AV_BUF_STC_EXT_VSYNC_TS1 0x48 +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT_TS0 0x4c +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT_TS1 0x50 +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT2_TS0 0x54 +#define ZYNQMP_DISP_AV_BUF_STC_CUSTOM_EVENT2_TS1 0x58 +#define ZYNQMP_DISP_AV_BUF_STC_SNAPSHOT0 0x60 +#define ZYNQMP_DISP_AV_BUF_STC_SNAPSHOT1 0x64 +#define ZYNQMP_DISP_AV_BUF_OUTPUT 0x70 +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_SHIFT 0 +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK (0x3 << 0) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_LIVE (0 << 0) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MEM (1 << 0) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_PATTERN (2 << 0) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_NONE (3 << 0) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_SHIFT 2 +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK (0x3 << 2) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_DISABLE (0 << 2) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM (1 << 2) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_LIVE (2 << 2) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_NONE (3 << 2) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_SHIFT 4 +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK (0x3 << 4) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_PL (0 << 4) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MEM (1 << 4) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_PATTERN (2 << 4) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_DISABLE (3 << 4) +#define ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN BIT(6) +#define ZYNQMP_DISP_AV_BUF_HCOUNT_VCOUNT_INT0 0x74 +#define ZYNQMP_DISP_AV_BUF_HCOUNT_VCOUNT_INT1 0x78 +#define ZYNQMP_DISP_AV_BUF_PATTERN_GEN_SELECT 0x100 +#define ZYNQMP_DISP_AV_BUF_CLK_SRC 0x120 +#define ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_FROM_PS BIT(0) +#define ZYNQMP_DISP_AV_BUF_CLK_SRC_AUD_FROM_PS BIT(1) +#define ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_INTERNAL_TIMING BIT(2) +#define ZYNQMP_DISP_AV_BUF_SRST_REG 0x124 +#define ZYNQMP_DISP_AV_BUF_SRST_REG_VID_RST BIT(1) +#define ZYNQMP_DISP_AV_BUF_AUDIO_CH_CONFIG 0x12c +#define ZYNQMP_DISP_AV_BUF_GFX_COMP_SF(n) (0x200 + ((n) * 4)) +#define ZYNQMP_DISP_AV_BUF_VID_COMP_SF(n) (0x20c + ((n) * 4)) +#define ZYNQMP_DISP_AV_BUF_LIVD_VID_COMP_SF(n) (0x218 + ((n) * 4)) +#define ZYNQMP_DISP_AV_BUF_LIVE_VID_CONFIG 0x224 +#define ZYNQMP_DISP_AV_BUF_LIVD_GFX_COMP_SF(n) (0x228 + ((n) * 4)) +#define ZYNQMP_DISP_AV_BUF_LIVE_GFX_CONFIG 0x234 +#define ZYNQMP_DISP_AV_BUF_4BIT_SF 0x11111 +#define ZYNQMP_DISP_AV_BUF_5BIT_SF 0x10842 +#define ZYNQMP_DISP_AV_BUF_6BIT_SF 0x10410 +#define ZYNQMP_DISP_AV_BUF_8BIT_SF 0x10101 +#define ZYNQMP_DISP_AV_BUF_10BIT_SF 0x10040 +#define ZYNQMP_DISP_AV_BUF_NULL_SF 0 +#define ZYNQMP_DISP_AV_BUF_NUM_SF 3 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_6 0x0 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 0x1 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_10 0x2 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_12 0x3 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_MASK GENMASK(2, 0) +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB 0x0 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV444 0x1 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422 0x2 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YONLY 0x3 +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_MASK GENMASK(5, 4) +#define ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_CB_FIRST BIT(8) +#define ZYNQMP_DISP_AV_BUF_PALETTE_MEMORY 0x400 + +/* Audio registers */ +#define ZYNQMP_DISP_AUD_MIXER_VOLUME 0x0 +#define ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE 0x20002000 +#define ZYNQMP_DISP_AUD_MIXER_META_DATA 0x4 +#define ZYNQMP_DISP_AUD_CH_STATUS0 0x8 +#define ZYNQMP_DISP_AUD_CH_STATUS1 0xc +#define ZYNQMP_DISP_AUD_CH_STATUS2 0x10 +#define ZYNQMP_DISP_AUD_CH_STATUS3 0x14 +#define ZYNQMP_DISP_AUD_CH_STATUS4 0x18 +#define ZYNQMP_DISP_AUD_CH_STATUS5 0x1c +#define ZYNQMP_DISP_AUD_CH_A_DATA0 0x20 +#define ZYNQMP_DISP_AUD_CH_A_DATA1 0x24 +#define ZYNQMP_DISP_AUD_CH_A_DATA2 0x28 +#define ZYNQMP_DISP_AUD_CH_A_DATA3 0x2c +#define ZYNQMP_DISP_AUD_CH_A_DATA4 0x30 +#define ZYNQMP_DISP_AUD_CH_A_DATA5 0x34 +#define ZYNQMP_DISP_AUD_CH_B_DATA0 0x38 +#define ZYNQMP_DISP_AUD_CH_B_DATA1 0x3c +#define ZYNQMP_DISP_AUD_CH_B_DATA2 0x40 +#define ZYNQMP_DISP_AUD_CH_B_DATA3 0x44 +#define ZYNQMP_DISP_AUD_CH_B_DATA4 0x48 +#define ZYNQMP_DISP_AUD_CH_B_DATA5 0x4c +#define ZYNQMP_DISP_AUD_SOFT_RESET 0xc00 +#define ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST BIT(0) + +#endif /* _ZYNQMP_DISP_REGS_H_ */ diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c new file mode 100644 index 000000000000..821f7a71e182 --- /dev/null +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -0,0 +1,1734 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ZynqMP DisplayPort Driver + * + * Copyright (C) 2017 - 2020 Xilinx, Inc. + * + * Authors: + * - Hyun Woo Kwon <hyun.kwon@xilinx.com> + * - Laurent Pinchart <laurent.pinchart@ideasonboard.com> + */ + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_dp_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_encoder.h> +#include <drm/drm_managed.h> +#include <drm/drm_modes.h> +#include <drm/drm_of.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_simple_kms_helper.h> + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/phy/phy.h> +#include <linux/reset.h> + +#include "zynqmp_disp.h" +#include "zynqmp_dp.h" +#include "zynqmp_dpsub.h" + +static uint zynqmp_dp_aux_timeout_ms = 50; +module_param_named(aux_timeout_ms, zynqmp_dp_aux_timeout_ms, uint, 0444); +MODULE_PARM_DESC(aux_timeout_ms, "DP aux timeout value in msec (default: 50)"); + +/* + * Some sink requires a delay after power on request + */ +static uint zynqmp_dp_power_on_delay_ms = 4; +module_param_named(power_on_delay_ms, zynqmp_dp_power_on_delay_ms, uint, 0444); +MODULE_PARM_DESC(aux_timeout_ms, "DP power on delay in msec (default: 4)"); + +/* Link configuration registers */ +#define ZYNQMP_DP_LINK_BW_SET 0x0 +#define ZYNQMP_DP_LANE_COUNT_SET 0x4 +#define ZYNQMP_DP_ENHANCED_FRAME_EN 0x8 +#define ZYNQMP_DP_TRAINING_PATTERN_SET 0xc +#define ZYNQMP_DP_SCRAMBLING_DISABLE 0x14 +#define ZYNQMP_DP_DOWNSPREAD_CTL 0x18 +#define ZYNQMP_DP_SOFTWARE_RESET 0x1c +#define ZYNQMP_DP_SOFTWARE_RESET_STREAM1 BIT(0) +#define ZYNQMP_DP_SOFTWARE_RESET_STREAM2 BIT(1) +#define ZYNQMP_DP_SOFTWARE_RESET_STREAM3 BIT(2) +#define ZYNQMP_DP_SOFTWARE_RESET_STREAM4 BIT(3) +#define ZYNQMP_DP_SOFTWARE_RESET_AUX BIT(7) +#define ZYNQMP_DP_SOFTWARE_RESET_ALL (ZYNQMP_DP_SOFTWARE_RESET_STREAM1 | \ + ZYNQMP_DP_SOFTWARE_RESET_STREAM2 | \ + ZYNQMP_DP_SOFTWARE_RESET_STREAM3 | \ + ZYNQMP_DP_SOFTWARE_RESET_STREAM4 | \ + ZYNQMP_DP_SOFTWARE_RESET_AUX) + +/* Core enable registers */ +#define ZYNQMP_DP_TRANSMITTER_ENABLE 0x80 +#define ZYNQMP_DP_MAIN_STREAM_ENABLE 0x84 +#define ZYNQMP_DP_FORCE_SCRAMBLER_RESET 0xc0 +#define ZYNQMP_DP_VERSION 0xf8 +#define ZYNQMP_DP_VERSION_MAJOR_MASK GENMASK(31, 24) +#define ZYNQMP_DP_VERSION_MAJOR_SHIFT 24 +#define ZYNQMP_DP_VERSION_MINOR_MASK GENMASK(23, 16) +#define ZYNQMP_DP_VERSION_MINOR_SHIFT 16 +#define ZYNQMP_DP_VERSION_REVISION_MASK GENMASK(15, 12) +#define ZYNQMP_DP_VERSION_REVISION_SHIFT 12 +#define ZYNQMP_DP_VERSION_PATCH_MASK GENMASK(11, 8) +#define ZYNQMP_DP_VERSION_PATCH_SHIFT 8 +#define ZYNQMP_DP_VERSION_INTERNAL_MASK GENMASK(7, 0) +#define ZYNQMP_DP_VERSION_INTERNAL_SHIFT 0 + +/* Core ID registers */ +#define ZYNQMP_DP_CORE_ID 0xfc +#define ZYNQMP_DP_CORE_ID_MAJOR_MASK GENMASK(31, 24) +#define ZYNQMP_DP_CORE_ID_MAJOR_SHIFT 24 +#define ZYNQMP_DP_CORE_ID_MINOR_MASK GENMASK(23, 16) +#define ZYNQMP_DP_CORE_ID_MINOR_SHIFT 16 +#define ZYNQMP_DP_CORE_ID_REVISION_MASK GENMASK(15, 8) +#define ZYNQMP_DP_CORE_ID_REVISION_SHIFT 8 +#define ZYNQMP_DP_CORE_ID_DIRECTION GENMASK(1) + +/* AUX channel interface registers */ +#define ZYNQMP_DP_AUX_COMMAND 0x100 +#define ZYNQMP_DP_AUX_COMMAND_CMD_SHIFT 8 +#define ZYNQMP_DP_AUX_COMMAND_ADDRESS_ONLY BIT(12) +#define ZYNQMP_DP_AUX_COMMAND_BYTES_SHIFT 0 +#define ZYNQMP_DP_AUX_WRITE_FIFO 0x104 +#define ZYNQMP_DP_AUX_ADDRESS 0x108 +#define ZYNQMP_DP_AUX_CLK_DIVIDER 0x10c +#define ZYNQMP_DP_AUX_CLK_DIVIDER_AUX_FILTER_SHIFT 8 +#define ZYNQMP_DP_INTERRUPT_SIGNAL_STATE 0x130 +#define ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_HPD BIT(0) +#define ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REQUEST BIT(1) +#define ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY BIT(2) +#define ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY_TIMEOUT BIT(3) +#define ZYNQMP_DP_AUX_REPLY_DATA 0x134 +#define ZYNQMP_DP_AUX_REPLY_CODE 0x138 +#define ZYNQMP_DP_AUX_REPLY_CODE_AUX_ACK (0) +#define ZYNQMP_DP_AUX_REPLY_CODE_AUX_NACK BIT(0) +#define ZYNQMP_DP_AUX_REPLY_CODE_AUX_DEFER BIT(1) +#define ZYNQMP_DP_AUX_REPLY_CODE_I2C_ACK (0) +#define ZYNQMP_DP_AUX_REPLY_CODE_I2C_NACK BIT(2) +#define ZYNQMP_DP_AUX_REPLY_CODE_I2C_DEFER BIT(3) +#define ZYNQMP_DP_AUX_REPLY_COUNT 0x13c +#define ZYNQMP_DP_REPLY_DATA_COUNT 0x148 +#define ZYNQMP_DP_REPLY_DATA_COUNT_MASK 0xff +#define ZYNQMP_DP_INT_STATUS 0x3a0 +#define ZYNQMP_DP_INT_MASK 0x3a4 +#define ZYNQMP_DP_INT_EN 0x3a8 +#define ZYNQMP_DP_INT_DS 0x3ac +#define ZYNQMP_DP_INT_HPD_IRQ BIT(0) +#define ZYNQMP_DP_INT_HPD_EVENT BIT(1) +#define ZYNQMP_DP_INT_REPLY_RECEIVED BIT(2) +#define ZYNQMP_DP_INT_REPLY_TIMEOUT BIT(3) +#define ZYNQMP_DP_INT_HPD_PULSE_DET BIT(4) +#define ZYNQMP_DP_INT_EXT_PKT_TXD BIT(5) +#define ZYNQMP_DP_INT_LIV_ABUF_UNDRFLW BIT(12) +#define ZYNQMP_DP_INT_VBLANK_START BIT(13) +#define ZYNQMP_DP_INT_PIXEL1_MATCH BIT(14) +#define ZYNQMP_DP_INT_PIXEL0_MATCH BIT(15) +#define ZYNQMP_DP_INT_CHBUF_UNDERFLW_MASK 0x3f0000 +#define ZYNQMP_DP_INT_CHBUF_OVERFLW_MASK 0xfc00000 +#define ZYNQMP_DP_INT_CUST_TS_2 BIT(28) +#define ZYNQMP_DP_INT_CUST_TS BIT(29) +#define ZYNQMP_DP_INT_EXT_VSYNC_TS BIT(30) +#define ZYNQMP_DP_INT_VSYNC_TS BIT(31) +#define ZYNQMP_DP_INT_ALL (ZYNQMP_DP_INT_HPD_IRQ | \ + ZYNQMP_DP_INT_HPD_EVENT | \ + ZYNQMP_DP_INT_CHBUF_UNDERFLW_MASK | \ + ZYNQMP_DP_INT_CHBUF_OVERFLW_MASK) + +/* Main stream attribute registers */ +#define ZYNQMP_DP_MAIN_STREAM_HTOTAL 0x180 +#define ZYNQMP_DP_MAIN_STREAM_VTOTAL 0x184 +#define ZYNQMP_DP_MAIN_STREAM_POLARITY 0x188 +#define ZYNQMP_DP_MAIN_STREAM_POLARITY_HSYNC_SHIFT 0 +#define ZYNQMP_DP_MAIN_STREAM_POLARITY_VSYNC_SHIFT 1 +#define ZYNQMP_DP_MAIN_STREAM_HSWIDTH 0x18c +#define ZYNQMP_DP_MAIN_STREAM_VSWIDTH 0x190 +#define ZYNQMP_DP_MAIN_STREAM_HRES 0x194 +#define ZYNQMP_DP_MAIN_STREAM_VRES 0x198 +#define ZYNQMP_DP_MAIN_STREAM_HSTART 0x19c +#define ZYNQMP_DP_MAIN_STREAM_VSTART 0x1a0 +#define ZYNQMP_DP_MAIN_STREAM_MISC0 0x1a4 +#define ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK BIT(0) +#define ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_RGB (0 << 1) +#define ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_YCRCB_422 (5 << 1) +#define ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_YCRCB_444 (6 << 1) +#define ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_MASK (7 << 1) +#define ZYNQMP_DP_MAIN_STREAM_MISC0_DYNAMIC_RANGE BIT(3) +#define ZYNQMP_DP_MAIN_STREAM_MISC0_YCBCR_COLR BIT(4) +#define ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_6 (0 << 5) +#define ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_8 (1 << 5) +#define ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_10 (2 << 5) +#define ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_12 (3 << 5) +#define ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_16 (4 << 5) +#define ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_MASK (7 << 5) +#define ZYNQMP_DP_MAIN_STREAM_MISC1 0x1a8 +#define ZYNQMP_DP_MAIN_STREAM_MISC1_Y_ONLY_EN BIT(7) +#define ZYNQMP_DP_MAIN_STREAM_M_VID 0x1ac +#define ZYNQMP_DP_MSA_TRANSFER_UNIT_SIZE 0x1b0 +#define ZYNQMP_DP_MSA_TRANSFER_UNIT_SIZE_TU_SIZE_DEF 64 +#define ZYNQMP_DP_MAIN_STREAM_N_VID 0x1b4 +#define ZYNQMP_DP_USER_PIX_WIDTH 0x1b8 +#define ZYNQMP_DP_USER_DATA_COUNT_PER_LANE 0x1bc +#define ZYNQMP_DP_MIN_BYTES_PER_TU 0x1c4 +#define ZYNQMP_DP_FRAC_BYTES_PER_TU 0x1c8 +#define ZYNQMP_DP_INIT_WAIT 0x1cc + +/* PHY configuration and status registers */ +#define ZYNQMP_DP_PHY_RESET 0x200 +#define ZYNQMP_DP_PHY_RESET_PHY_RESET BIT(0) +#define ZYNQMP_DP_PHY_RESET_GTTX_RESET BIT(1) +#define ZYNQMP_DP_PHY_RESET_PHY_PMA_RESET BIT(8) +#define ZYNQMP_DP_PHY_RESET_PHY_PCS_RESET BIT(9) +#define ZYNQMP_DP_PHY_RESET_ALL_RESET (ZYNQMP_DP_PHY_RESET_PHY_RESET | \ + ZYNQMP_DP_PHY_RESET_GTTX_RESET | \ + ZYNQMP_DP_PHY_RESET_PHY_PMA_RESET | \ + ZYNQMP_DP_PHY_RESET_PHY_PCS_RESET) +#define ZYNQMP_DP_PHY_PREEMPHASIS_LANE_0 0x210 +#define ZYNQMP_DP_PHY_PREEMPHASIS_LANE_1 0x214 +#define ZYNQMP_DP_PHY_PREEMPHASIS_LANE_2 0x218 +#define ZYNQMP_DP_PHY_PREEMPHASIS_LANE_3 0x21c +#define ZYNQMP_DP_PHY_VOLTAGE_DIFF_LANE_0 0x220 +#define ZYNQMP_DP_PHY_VOLTAGE_DIFF_LANE_1 0x224 +#define ZYNQMP_DP_PHY_VOLTAGE_DIFF_LANE_2 0x228 +#define ZYNQMP_DP_PHY_VOLTAGE_DIFF_LANE_3 0x22c +#define ZYNQMP_DP_PHY_CLOCK_SELECT 0x234 +#define ZYNQMP_DP_PHY_CLOCK_SELECT_1_62G 0x1 +#define ZYNQMP_DP_PHY_CLOCK_SELECT_2_70G 0x3 +#define ZYNQMP_DP_PHY_CLOCK_SELECT_5_40G 0x5 +#define ZYNQMP_DP_TX_PHY_POWER_DOWN 0x238 +#define ZYNQMP_DP_TX_PHY_POWER_DOWN_LANE_0 BIT(0) +#define ZYNQMP_DP_TX_PHY_POWER_DOWN_LANE_1 BIT(1) +#define ZYNQMP_DP_TX_PHY_POWER_DOWN_LANE_2 BIT(2) +#define ZYNQMP_DP_TX_PHY_POWER_DOWN_LANE_3 BIT(3) +#define ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL 0xf +#define ZYNQMP_DP_PHY_PRECURSOR_LANE_0 0x23c +#define ZYNQMP_DP_PHY_PRECURSOR_LANE_1 0x240 +#define ZYNQMP_DP_PHY_PRECURSOR_LANE_2 0x244 +#define ZYNQMP_DP_PHY_PRECURSOR_LANE_3 0x248 +#define ZYNQMP_DP_PHY_POSTCURSOR_LANE_0 0x24c +#define ZYNQMP_DP_PHY_POSTCURSOR_LANE_1 0x250 +#define ZYNQMP_DP_PHY_POSTCURSOR_LANE_2 0x254 +#define ZYNQMP_DP_PHY_POSTCURSOR_LANE_3 0x258 +#define ZYNQMP_DP_SUB_TX_PHY_PRECURSOR_LANE_0 0x24c +#define ZYNQMP_DP_SUB_TX_PHY_PRECURSOR_LANE_1 0x250 +#define ZYNQMP_DP_PHY_STATUS 0x280 +#define ZYNQMP_DP_PHY_STATUS_PLL_LOCKED_SHIFT 4 +#define ZYNQMP_DP_PHY_STATUS_FPGA_PLL_LOCKED BIT(6) + +/* Audio registers */ +#define ZYNQMP_DP_TX_AUDIO_CONTROL 0x300 +#define ZYNQMP_DP_TX_AUDIO_CHANNELS 0x304 +#define ZYNQMP_DP_TX_AUDIO_INFO_DATA 0x308 +#define ZYNQMP_DP_TX_M_AUD 0x328 +#define ZYNQMP_DP_TX_N_AUD 0x32c +#define ZYNQMP_DP_TX_AUDIO_EXT_DATA 0x330 + +#define ZYNQMP_DP_MAX_LANES 2 +#define ZYNQMP_MAX_FREQ 3000000 + +#define DP_REDUCED_BIT_RATE 162000 +#define DP_HIGH_BIT_RATE 270000 +#define DP_HIGH_BIT_RATE2 540000 +#define DP_MAX_TRAINING_TRIES 5 +#define DP_V1_2 0x12 + +/** + * struct zynqmp_dp_link_config - Common link config between source and sink + * @max_rate: maximum link rate + * @max_lanes: maximum number of lanes + */ +struct zynqmp_dp_link_config { + int max_rate; + u8 max_lanes; +}; + +/** + * struct zynqmp_dp_mode - Configured mode of DisplayPort + * @bw_code: code for bandwidth(link rate) + * @lane_cnt: number of lanes + * @pclock: pixel clock frequency of current mode + * @fmt: format identifier string + */ +struct zynqmp_dp_mode { + u8 bw_code; + u8 lane_cnt; + int pclock; + const char *fmt; +}; + +/** + * struct zynqmp_dp_config - Configuration of DisplayPort from DTS + * @misc0: misc0 configuration (per DP v1.2 spec) + * @misc1: misc1 configuration (per DP v1.2 spec) + * @bpp: bits per pixel + */ +struct zynqmp_dp_config { + u8 misc0; + u8 misc1; + u8 bpp; +}; + +/** + * struct zynqmp_dp - Xilinx DisplayPort core + * @encoder: the drm encoder structure + * @connector: the drm connector structure + * @dev: device structure + * @dpsub: Display subsystem + * @drm: DRM core + * @iomem: device I/O memory for register access + * @reset: reset controller + * @irq: irq + * @config: IP core configuration from DTS + * @aux: aux channel + * @phy: PHY handles for DP lanes + * @num_lanes: number of enabled phy lanes + * @hpd_work: hot plug detection worker + * @status: connection status + * @enabled: flag to indicate if the device is enabled + * @dpcd: DP configuration data from currently connected sink device + * @link_config: common link configuration between IP core and sink device + * @mode: current mode between IP core and sink device + * @train_set: set of training data + */ +struct zynqmp_dp { + struct drm_encoder encoder; + struct drm_connector connector; + struct device *dev; + struct zynqmp_dpsub *dpsub; + struct drm_device *drm; + void __iomem *iomem; + struct reset_control *reset; + int irq; + + struct zynqmp_dp_config config; + struct drm_dp_aux aux; + struct phy *phy[ZYNQMP_DP_MAX_LANES]; + u8 num_lanes; + struct delayed_work hpd_work; + enum drm_connector_status status; + bool enabled; + + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + struct zynqmp_dp_link_config link_config; + struct zynqmp_dp_mode mode; + u8 train_set[ZYNQMP_DP_MAX_LANES]; +}; + +static inline struct zynqmp_dp *encoder_to_dp(struct drm_encoder *encoder) +{ + return container_of(encoder, struct zynqmp_dp, encoder); +} + +static inline struct zynqmp_dp *connector_to_dp(struct drm_connector *connector) +{ + return container_of(connector, struct zynqmp_dp, connector); +} + +static void zynqmp_dp_write(struct zynqmp_dp *dp, int offset, u32 val) +{ + writel(val, dp->iomem + offset); +} + +static u32 zynqmp_dp_read(struct zynqmp_dp *dp, int offset) +{ + return readl(dp->iomem + offset); +} + +static void zynqmp_dp_clr(struct zynqmp_dp *dp, int offset, u32 clr) +{ + zynqmp_dp_write(dp, offset, zynqmp_dp_read(dp, offset) & ~clr); +} + +static void zynqmp_dp_set(struct zynqmp_dp *dp, int offset, u32 set) +{ + zynqmp_dp_write(dp, offset, zynqmp_dp_read(dp, offset) | set); +} + +/* ----------------------------------------------------------------------------- + * PHY Handling + */ + +#define RST_TIMEOUT_MS 1000 + +static int zynqmp_dp_reset(struct zynqmp_dp *dp, bool assert) +{ + unsigned long timeout; + + if (assert) + reset_control_assert(dp->reset); + else + reset_control_deassert(dp->reset); + + /* Wait for the (de)assert to complete. */ + timeout = jiffies + msecs_to_jiffies(RST_TIMEOUT_MS); + while (!time_after_eq(jiffies, timeout)) { + bool status = !!reset_control_status(dp->reset); + + if (assert == status) + return 0; + + cpu_relax(); + } + + dev_err(dp->dev, "reset %s timeout\n", assert ? "assert" : "deassert"); + return -ETIMEDOUT; +} + +/** + * zynqmp_dp_phy_init - Initialize the phy + * @dp: DisplayPort IP core structure + * + * Initialize the phy. + * + * Return: 0 if the phy instances are initialized correctly, or the error code + * returned from the callee functions. + */ +static int zynqmp_dp_phy_init(struct zynqmp_dp *dp) +{ + int ret; + int i; + + for (i = 0; i < dp->num_lanes; i++) { + ret = phy_init(dp->phy[i]); + if (ret) { + dev_err(dp->dev, "failed to init phy lane %d\n", i); + return ret; + } + } + + ret = zynqmp_dp_reset(dp, false); + if (ret < 0) + return ret; + + zynqmp_dp_clr(dp, ZYNQMP_DP_PHY_RESET, ZYNQMP_DP_PHY_RESET_ALL_RESET); + + /* + * Power on lanes in reverse order as only lane 0 waits for the PLL to + * lock. + */ + for (i = dp->num_lanes - 1; i >= 0; i--) { + ret = phy_power_on(dp->phy[i]); + if (ret) { + dev_err(dp->dev, "failed to power on phy lane %d\n", i); + return ret; + } + } + + return 0; +} + +/** + * zynqmp_dp_phy_exit - Exit the phy + * @dp: DisplayPort IP core structure + * + * Exit the phy. + */ +static void zynqmp_dp_phy_exit(struct zynqmp_dp *dp) +{ + unsigned int i; + int ret; + + for (i = 0; i < dp->num_lanes; i++) { + ret = phy_power_off(dp->phy[i]); + if (ret) + dev_err(dp->dev, "failed to power off phy(%d) %d\n", i, + ret); + } + + zynqmp_dp_reset(dp, true); + + for (i = 0; i < dp->num_lanes; i++) { + ret = phy_exit(dp->phy[i]); + if (ret) + dev_err(dp->dev, "failed to exit phy(%d) %d\n", i, ret); + } +} + +/** + * zynqmp_dp_phy_probe - Probe the PHYs + * @dp: DisplayPort IP core structure + * + * Probe PHYs for all lanes. Less PHYs may be available than the number of + * lanes, which is not considered an error as long as at least one PHY is + * found. The caller can check dp->num_lanes to check how many PHYs were found. + * + * Return: + * * 0 - Success + * * -ENXIO - No PHY found + * * -EPROBE_DEFER - Probe deferral requested + * * Other negative value - PHY retrieval failure + */ +static int zynqmp_dp_phy_probe(struct zynqmp_dp *dp) +{ + unsigned int i; + + for (i = 0; i < ZYNQMP_DP_MAX_LANES; i++) { + char phy_name[16]; + struct phy *phy; + + snprintf(phy_name, sizeof(phy_name), "dp-phy%d", i); + phy = devm_phy_get(dp->dev, phy_name); + + if (IS_ERR(phy)) { + switch (PTR_ERR(phy)) { + case -ENODEV: + if (dp->num_lanes) + return 0; + + dev_err(dp->dev, "no PHY found\n"); + return -ENXIO; + + case -EPROBE_DEFER: + return -EPROBE_DEFER; + + default: + dev_err(dp->dev, "failed to get PHY lane %u\n", + i); + return PTR_ERR(phy); + } + } + + dp->phy[i] = phy; + dp->num_lanes++; + } + + return 0; +} + +/** + * zynqmp_dp_phy_ready - Check if PHY is ready + * @dp: DisplayPort IP core structure + * + * Check if PHY is ready. If PHY is not ready, wait 1ms to check for 100 times. + * This amount of delay was suggested by IP designer. + * + * Return: 0 if PHY is ready, or -ENODEV if PHY is not ready. + */ +static int zynqmp_dp_phy_ready(struct zynqmp_dp *dp) +{ + u32 i, reg, ready; + + ready = (1 << dp->num_lanes) - 1; + + /* Wait for 100 * 1ms. This should be enough time for PHY to be ready */ + for (i = 0; ; i++) { + reg = zynqmp_dp_read(dp, ZYNQMP_DP_PHY_STATUS); + if ((reg & ready) == ready) + return 0; + + if (i == 100) { + dev_err(dp->dev, "PHY isn't ready\n"); + return -ENODEV; + } + + usleep_range(1000, 1100); + } + + return 0; +} + +/* ----------------------------------------------------------------------------- + * DisplayPort Link Training + */ + +/** + * zynqmp_dp_max_rate - Calculate and return available max pixel clock + * @link_rate: link rate (Kilo-bytes / sec) + * @lane_num: number of lanes + * @bpp: bits per pixel + * + * Return: max pixel clock (KHz) supported by current link config. + */ +static inline int zynqmp_dp_max_rate(int link_rate, u8 lane_num, u8 bpp) +{ + return link_rate * lane_num * 8 / bpp; +} + +/** + * zynqmp_dp_mode_configure - Configure the link values + * @dp: DisplayPort IP core structure + * @pclock: pixel clock for requested display mode + * @current_bw: current link rate + * + * Find the link configuration values, rate and lane count for requested pixel + * clock @pclock. The @pclock is stored in the mode to be used in other + * functions later. The returned rate is downshifted from the current rate + * @current_bw. + * + * Return: Current link rate code, or -EINVAL. + */ +static int zynqmp_dp_mode_configure(struct zynqmp_dp *dp, int pclock, + u8 current_bw) +{ + int max_rate = dp->link_config.max_rate; + u8 bws[3] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7, DP_LINK_BW_5_4 }; + u8 max_lanes = dp->link_config.max_lanes; + u8 max_link_rate_code = drm_dp_link_rate_to_bw_code(max_rate); + u8 bpp = dp->config.bpp; + u8 lane_cnt; + s8 i; + + if (current_bw == DP_LINK_BW_1_62) { + dev_err(dp->dev, "can't downshift. already lowest link rate\n"); + return -EINVAL; + } + + for (i = ARRAY_SIZE(bws) - 1; i >= 0; i--) { + if (current_bw && bws[i] >= current_bw) + continue; + + if (bws[i] <= max_link_rate_code) + break; + } + + for (lane_cnt = 1; lane_cnt <= max_lanes; lane_cnt <<= 1) { + int bw; + u32 rate; + + bw = drm_dp_bw_code_to_link_rate(bws[i]); + rate = zynqmp_dp_max_rate(bw, lane_cnt, bpp); + if (pclock <= rate) { + dp->mode.bw_code = bws[i]; + dp->mode.lane_cnt = lane_cnt; + dp->mode.pclock = pclock; + return dp->mode.bw_code; + } + } + + dev_err(dp->dev, "failed to configure link values\n"); + + return -EINVAL; +} + +/** + * zynqmp_dp_adjust_train - Adjust train values + * @dp: DisplayPort IP core structure + * @link_status: link status from sink which contains requested training values + */ +static void zynqmp_dp_adjust_train(struct zynqmp_dp *dp, + u8 link_status[DP_LINK_STATUS_SIZE]) +{ + u8 *train_set = dp->train_set; + u8 voltage = 0, preemphasis = 0; + u8 i; + + for (i = 0; i < dp->mode.lane_cnt; i++) { + u8 v = drm_dp_get_adjust_request_voltage(link_status, i); + u8 p = drm_dp_get_adjust_request_pre_emphasis(link_status, i); + + if (v > voltage) + voltage = v; + + if (p > preemphasis) + preemphasis = p; + } + + if (voltage >= DP_TRAIN_VOLTAGE_SWING_LEVEL_3) + voltage |= DP_TRAIN_MAX_SWING_REACHED; + + if (preemphasis >= DP_TRAIN_PRE_EMPH_LEVEL_2) + preemphasis |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + + for (i = 0; i < dp->mode.lane_cnt; i++) + train_set[i] = voltage | preemphasis; +} + +/** + * zynqmp_dp_update_vs_emph - Update the training values + * @dp: DisplayPort IP core structure + * + * Update the training values based on the request from sink. The mapped values + * are predefined, and values(vs, pe, pc) are from the device manual. + * + * Return: 0 if vs and emph are updated successfully, or the error code returned + * by drm_dp_dpcd_write(). + */ +static int zynqmp_dp_update_vs_emph(struct zynqmp_dp *dp) +{ + unsigned int i; + int ret; + + ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dp->train_set, + dp->mode.lane_cnt); + if (ret < 0) + return ret; + + for (i = 0; i < dp->mode.lane_cnt; i++) { + u32 reg = ZYNQMP_DP_SUB_TX_PHY_PRECURSOR_LANE_0 + i * 4; + union phy_configure_opts opts = { 0 }; + u8 train = dp->train_set[i]; + + opts.dp.voltage[0] = (train & DP_TRAIN_VOLTAGE_SWING_MASK) + >> DP_TRAIN_VOLTAGE_SWING_SHIFT; + opts.dp.pre[0] = (train & DP_TRAIN_PRE_EMPHASIS_MASK) + >> DP_TRAIN_PRE_EMPHASIS_SHIFT; + + phy_configure(dp->phy[i], &opts); + + zynqmp_dp_write(dp, reg, 0x2); + } + + return 0; +} + +/** + * zynqmp_dp_link_train_cr - Train clock recovery + * @dp: DisplayPort IP core structure + * + * Return: 0 if clock recovery train is done successfully, or corresponding + * error code. + */ +static int zynqmp_dp_link_train_cr(struct zynqmp_dp *dp) +{ + u8 link_status[DP_LINK_STATUS_SIZE]; + u8 lane_cnt = dp->mode.lane_cnt; + u8 vs = 0, tries = 0; + u16 max_tries, i; + bool cr_done; + int ret; + + zynqmp_dp_write(dp, ZYNQMP_DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_1); + ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_1 | + DP_LINK_SCRAMBLING_DISABLE); + if (ret < 0) + return ret; + + /* + * 256 loops should be maximum iterations for 4 lanes and 4 values. + * So, This loop should exit before 512 iterations + */ + for (max_tries = 0; max_tries < 512; max_tries++) { + ret = zynqmp_dp_update_vs_emph(dp); + if (ret) + return ret; + + drm_dp_link_train_clock_recovery_delay(dp->dpcd); + ret = drm_dp_dpcd_read_link_status(&dp->aux, link_status); + if (ret < 0) + return ret; + + cr_done = drm_dp_clock_recovery_ok(link_status, lane_cnt); + if (cr_done) + break; + + for (i = 0; i < lane_cnt; i++) + if (!(dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED)) + break; + if (i == lane_cnt) + break; + + if ((dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == vs) + tries++; + else + tries = 0; + + if (tries == DP_MAX_TRAINING_TRIES) + break; + + vs = dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + zynqmp_dp_adjust_train(dp, link_status); + } + + if (!cr_done) + return -EIO; + + return 0; +} + +/** + * zynqmp_dp_link_train_ce - Train channel equalization + * @dp: DisplayPort IP core structure + * + * Return: 0 if channel equalization train is done successfully, or + * corresponding error code. + */ +static int zynqmp_dp_link_train_ce(struct zynqmp_dp *dp) +{ + u8 link_status[DP_LINK_STATUS_SIZE]; + u8 lane_cnt = dp->mode.lane_cnt; + u32 pat, tries; + int ret; + bool ce_done; + + if (dp->dpcd[DP_DPCD_REV] >= DP_V1_2 && + dp->dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED) + pat = DP_TRAINING_PATTERN_3; + else + pat = DP_TRAINING_PATTERN_2; + + zynqmp_dp_write(dp, ZYNQMP_DP_TRAINING_PATTERN_SET, pat); + ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, + pat | DP_LINK_SCRAMBLING_DISABLE); + if (ret < 0) + return ret; + + for (tries = 0; tries < DP_MAX_TRAINING_TRIES; tries++) { + ret = zynqmp_dp_update_vs_emph(dp); + if (ret) + return ret; + + drm_dp_link_train_channel_eq_delay(dp->dpcd); + ret = drm_dp_dpcd_read_link_status(&dp->aux, link_status); + if (ret < 0) + return ret; + + ce_done = drm_dp_channel_eq_ok(link_status, lane_cnt); + if (ce_done) + break; + + zynqmp_dp_adjust_train(dp, link_status); + } + + if (!ce_done) + return -EIO; + + return 0; +} + +/** + * zynqmp_dp_link_train - Train the link + * @dp: DisplayPort IP core structure + * + * Return: 0 if all trains are done successfully, or corresponding error code. + */ +static int zynqmp_dp_train(struct zynqmp_dp *dp) +{ + u32 reg; + u8 bw_code = dp->mode.bw_code; + u8 lane_cnt = dp->mode.lane_cnt; + u8 aux_lane_cnt = lane_cnt; + bool enhanced; + int ret; + + zynqmp_dp_write(dp, ZYNQMP_DP_LANE_COUNT_SET, lane_cnt); + enhanced = drm_dp_enhanced_frame_cap(dp->dpcd); + if (enhanced) { + zynqmp_dp_write(dp, ZYNQMP_DP_ENHANCED_FRAME_EN, 1); + aux_lane_cnt |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + } + + if (dp->dpcd[3] & 0x1) { + zynqmp_dp_write(dp, ZYNQMP_DP_DOWNSPREAD_CTL, 1); + drm_dp_dpcd_writeb(&dp->aux, DP_DOWNSPREAD_CTRL, + DP_SPREAD_AMP_0_5); + } else { + zynqmp_dp_write(dp, ZYNQMP_DP_DOWNSPREAD_CTL, 0); + drm_dp_dpcd_writeb(&dp->aux, DP_DOWNSPREAD_CTRL, 0); + } + + ret = drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET, aux_lane_cnt); + if (ret < 0) { + dev_err(dp->dev, "failed to set lane count\n"); + return ret; + } + + ret = drm_dp_dpcd_writeb(&dp->aux, DP_MAIN_LINK_CHANNEL_CODING_SET, + DP_SET_ANSI_8B10B); + if (ret < 0) { + dev_err(dp->dev, "failed to set ANSI 8B/10B encoding\n"); + return ret; + } + + ret = drm_dp_dpcd_writeb(&dp->aux, DP_LINK_BW_SET, bw_code); + if (ret < 0) { + dev_err(dp->dev, "failed to set DP bandwidth\n"); + return ret; + } + + zynqmp_dp_write(dp, ZYNQMP_DP_LINK_BW_SET, bw_code); + switch (bw_code) { + case DP_LINK_BW_1_62: + reg = ZYNQMP_DP_PHY_CLOCK_SELECT_1_62G; + break; + case DP_LINK_BW_2_7: + reg = ZYNQMP_DP_PHY_CLOCK_SELECT_2_70G; + break; + case DP_LINK_BW_5_4: + default: + reg = ZYNQMP_DP_PHY_CLOCK_SELECT_5_40G; + break; + } + + zynqmp_dp_write(dp, ZYNQMP_DP_PHY_CLOCK_SELECT, reg); + ret = zynqmp_dp_phy_ready(dp); + if (ret < 0) + return ret; + + zynqmp_dp_write(dp, ZYNQMP_DP_SCRAMBLING_DISABLE, 1); + memset(dp->train_set, 0, 4); + ret = zynqmp_dp_link_train_cr(dp); + if (ret) + return ret; + + ret = zynqmp_dp_link_train_ce(dp); + if (ret) + return ret; + + ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_DISABLE); + if (ret < 0) { + dev_err(dp->dev, "failed to disable training pattern\n"); + return ret; + } + zynqmp_dp_write(dp, ZYNQMP_DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_DISABLE); + + zynqmp_dp_write(dp, ZYNQMP_DP_SCRAMBLING_DISABLE, 0); + + return 0; +} + +/** + * zynqmp_dp_train_loop - Downshift the link rate during training + * @dp: DisplayPort IP core structure + * + * Train the link by downshifting the link rate if training is not successful. + */ +static void zynqmp_dp_train_loop(struct zynqmp_dp *dp) +{ + struct zynqmp_dp_mode *mode = &dp->mode; + u8 bw = mode->bw_code; + int ret; + + do { + if (dp->status == connector_status_disconnected || + !dp->enabled) + return; + + ret = zynqmp_dp_train(dp); + if (!ret) + return; + + ret = zynqmp_dp_mode_configure(dp, mode->pclock, bw); + if (ret < 0) + goto err_out; + + bw = ret; + } while (bw >= DP_LINK_BW_1_62); + +err_out: + dev_err(dp->dev, "failed to train the DP link\n"); +} + +/* ----------------------------------------------------------------------------- + * DisplayPort AUX + */ + +#define AUX_READ_BIT 0x1 + +/** + * zynqmp_dp_aux_cmd_submit - Submit aux command + * @dp: DisplayPort IP core structure + * @cmd: aux command + * @addr: aux address + * @buf: buffer for command data + * @bytes: number of bytes for @buf + * @reply: reply code to be returned + * + * Submit an aux command. All aux related commands, native or i2c aux + * read/write, are submitted through this function. The function is mapped to + * the transfer function of struct drm_dp_aux. This function involves in + * multiple register reads/writes, thus synchronization is needed, and it is + * done by drm_dp_helper using @hw_mutex. The calling thread goes into sleep + * if there's no immediate reply to the command submission. The reply code is + * returned at @reply if @reply != NULL. + * + * Return: 0 if the command is submitted properly, or corresponding error code: + * -EBUSY when there is any request already being processed + * -ETIMEDOUT when receiving reply is timed out + * -EIO when received bytes are less than requested + */ +static int zynqmp_dp_aux_cmd_submit(struct zynqmp_dp *dp, u32 cmd, u16 addr, + u8 *buf, u8 bytes, u8 *reply) +{ + bool is_read = (cmd & AUX_READ_BIT) ? true : false; + u32 reg, i; + + reg = zynqmp_dp_read(dp, ZYNQMP_DP_INTERRUPT_SIGNAL_STATE); + if (reg & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REQUEST) + return -EBUSY; + + zynqmp_dp_write(dp, ZYNQMP_DP_AUX_ADDRESS, addr); + if (!is_read) + for (i = 0; i < bytes; i++) + zynqmp_dp_write(dp, ZYNQMP_DP_AUX_WRITE_FIFO, + buf[i]); + + reg = cmd << ZYNQMP_DP_AUX_COMMAND_CMD_SHIFT; + if (!buf || !bytes) + reg |= ZYNQMP_DP_AUX_COMMAND_ADDRESS_ONLY; + else + reg |= (bytes - 1) << ZYNQMP_DP_AUX_COMMAND_BYTES_SHIFT; + zynqmp_dp_write(dp, ZYNQMP_DP_AUX_COMMAND, reg); + + /* Wait for reply to be delivered upto 2ms */ + for (i = 0; ; i++) { + reg = zynqmp_dp_read(dp, ZYNQMP_DP_INTERRUPT_SIGNAL_STATE); + if (reg & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY) + break; + + if (reg & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY_TIMEOUT || + i == 2) + return -ETIMEDOUT; + + usleep_range(1000, 1100); + } + + reg = zynqmp_dp_read(dp, ZYNQMP_DP_AUX_REPLY_CODE); + if (reply) + *reply = reg; + + if (is_read && + (reg == ZYNQMP_DP_AUX_REPLY_CODE_AUX_ACK || + reg == ZYNQMP_DP_AUX_REPLY_CODE_I2C_ACK)) { + reg = zynqmp_dp_read(dp, ZYNQMP_DP_REPLY_DATA_COUNT); + if ((reg & ZYNQMP_DP_REPLY_DATA_COUNT_MASK) != bytes) + return -EIO; + + for (i = 0; i < bytes; i++) + buf[i] = zynqmp_dp_read(dp, ZYNQMP_DP_AUX_REPLY_DATA); + } + + return 0; +} + +static ssize_t +zynqmp_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) +{ + struct zynqmp_dp *dp = container_of(aux, struct zynqmp_dp, aux); + int ret; + unsigned int i, iter; + + /* Number of loops = timeout in msec / aux delay (400 usec) */ + iter = zynqmp_dp_aux_timeout_ms * 1000 / 400; + iter = iter ? iter : 1; + + for (i = 0; i < iter; i++) { + ret = zynqmp_dp_aux_cmd_submit(dp, msg->request, msg->address, + msg->buffer, msg->size, + &msg->reply); + if (!ret) { + dev_dbg(dp->dev, "aux %d retries\n", i); + return msg->size; + } + + if (dp->status == connector_status_disconnected) { + dev_dbg(dp->dev, "no connected aux device\n"); + return -ENODEV; + } + + usleep_range(400, 500); + } + + dev_dbg(dp->dev, "failed to do aux transfer (%d)\n", ret); + + return ret; +} + +/** + * zynqmp_dp_aux_init - Initialize and register the DP AUX + * @dp: DisplayPort IP core structure + * + * Program the AUX clock divider and filter and register the DP AUX adapter. + * + * Return: 0 on success, error value otherwise + */ +static int zynqmp_dp_aux_init(struct zynqmp_dp *dp) +{ + unsigned long rate; + unsigned int w; + + /* + * The AUX_SIGNAL_WIDTH_FILTER is the number of APB clock cycles + * corresponding to the AUX pulse. Allowable values are 8, 16, 24, 32, + * 40 and 48. The AUX pulse width must be between 0.4µs and 0.6µs, + * compute the w / 8 value corresponding to 0.4µs rounded up, and make + * sure it stays below 0.6µs and within the allowable values. + */ + rate = clk_get_rate(dp->dpsub->apb_clk); + w = DIV_ROUND_UP(4 * rate, 1000 * 1000 * 10 * 8) * 8; + if (w > 6 * rate / (1000 * 1000 * 10) || w > 48) { + dev_err(dp->dev, "aclk frequency too high\n"); + return -EINVAL; + } + + zynqmp_dp_write(dp, ZYNQMP_DP_AUX_CLK_DIVIDER, + (w << ZYNQMP_DP_AUX_CLK_DIVIDER_AUX_FILTER_SHIFT) | + (rate / (1000 * 1000))); + + dp->aux.name = "ZynqMP DP AUX"; + dp->aux.dev = dp->dev; + dp->aux.transfer = zynqmp_dp_aux_transfer; + + return drm_dp_aux_register(&dp->aux); +} + +/** + * zynqmp_dp_aux_cleanup - Cleanup the DP AUX + * @dp: DisplayPort IP core structure + * + * Unregister the DP AUX adapter. + */ +static void zynqmp_dp_aux_cleanup(struct zynqmp_dp *dp) +{ + drm_dp_aux_unregister(&dp->aux); +} + +/* ----------------------------------------------------------------------------- + * DisplayPort Generic Support + */ + +/** + * zynqmp_dp_update_misc - Write the misc registers + * @dp: DisplayPort IP core structure + * + * The misc register values are stored in the structure, and this + * function applies the values into the registers. + */ +static void zynqmp_dp_update_misc(struct zynqmp_dp *dp) +{ + zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_MISC0, dp->config.misc0); + zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_MISC1, dp->config.misc1); +} + +/** + * zynqmp_dp_set_format - Set the input format + * @dp: DisplayPort IP core structure + * @format: input format + * @bpc: bits per component + * + * Update misc register values based on input @format and @bpc. + * + * Return: 0 on success, or -EINVAL. + */ +static int zynqmp_dp_set_format(struct zynqmp_dp *dp, + enum zynqmp_dpsub_format format, + unsigned int bpc) +{ + static const struct drm_display_info *display; + struct zynqmp_dp_config *config = &dp->config; + unsigned int num_colors; + + config->misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_MASK; + config->misc1 &= ~ZYNQMP_DP_MAIN_STREAM_MISC1_Y_ONLY_EN; + + switch (format) { + case ZYNQMP_DPSUB_FORMAT_RGB: + config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_RGB; + num_colors = 3; + break; + + case ZYNQMP_DPSUB_FORMAT_YCRCB444: + config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_YCRCB_444; + num_colors = 3; + break; + + case ZYNQMP_DPSUB_FORMAT_YCRCB422: + config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_YCRCB_422; + num_colors = 2; + break; + + case ZYNQMP_DPSUB_FORMAT_YONLY: + config->misc1 |= ZYNQMP_DP_MAIN_STREAM_MISC1_Y_ONLY_EN; + num_colors = 1; + break; + + default: + dev_err(dp->dev, "Invalid colormetry in DT\n"); + return -EINVAL; + } + + display = &dp->connector.display_info; + if (display->bpc && bpc > display->bpc) { + dev_warn(dp->dev, + "downgrading requested %ubpc to display limit %ubpc\n", + bpc, display->bpc); + bpc = display->bpc; + } + + config->misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_MASK; + + switch (bpc) { + case 6: + config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_6; + break; + case 8: + config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_8; + break; + case 10: + config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_10; + break; + case 12: + config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_12; + break; + case 16: + config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_16; + break; + default: + dev_warn(dp->dev, "Not supported bpc (%u). fall back to 8bpc\n", + bpc); + config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_8; + bpc = 8; + break; + } + + /* Update the current bpp based on the format. */ + config->bpp = bpc * num_colors; + + return 0; +} + +/** + * zynqmp_dp_encoder_mode_set_transfer_unit - Set the transfer unit values + * @dp: DisplayPort IP core structure + * @mode: requested display mode + * + * Set the transfer unit, and calculate all transfer unit size related values. + * Calculation is based on DP and IP core specification. + */ +static void +zynqmp_dp_encoder_mode_set_transfer_unit(struct zynqmp_dp *dp, + struct drm_display_mode *mode) +{ + u32 tu = ZYNQMP_DP_MSA_TRANSFER_UNIT_SIZE_TU_SIZE_DEF; + u32 bw, vid_kbytes, avg_bytes_per_tu, init_wait; + + /* Use the max transfer unit size (default) */ + zynqmp_dp_write(dp, ZYNQMP_DP_MSA_TRANSFER_UNIT_SIZE, tu); + + vid_kbytes = mode->clock * (dp->config.bpp / 8); + bw = drm_dp_bw_code_to_link_rate(dp->mode.bw_code); + avg_bytes_per_tu = vid_kbytes * tu / (dp->mode.lane_cnt * bw / 1000); + zynqmp_dp_write(dp, ZYNQMP_DP_MIN_BYTES_PER_TU, + avg_bytes_per_tu / 1000); + zynqmp_dp_write(dp, ZYNQMP_DP_FRAC_BYTES_PER_TU, + avg_bytes_per_tu % 1000); + + /* Configure the initial wait cycle based on transfer unit size */ + if (tu < (avg_bytes_per_tu / 1000)) + init_wait = 0; + else if ((avg_bytes_per_tu / 1000) <= 4) + init_wait = tu; + else + init_wait = tu - avg_bytes_per_tu / 1000; + + zynqmp_dp_write(dp, ZYNQMP_DP_INIT_WAIT, init_wait); +} + +/** + * zynqmp_dp_encoder_mode_set_stream - Configure the main stream + * @dp: DisplayPort IP core structure + * @mode: requested display mode + * + * Configure the main stream based on the requested mode @mode. Calculation is + * based on IP core specification. + */ +static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp, + const struct drm_display_mode *mode) +{ + u8 lane_cnt = dp->mode.lane_cnt; + u32 reg, wpl; + unsigned int rate; + + zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_HTOTAL, mode->htotal); + zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_VTOTAL, mode->vtotal); + zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_POLARITY, + (!!(mode->flags & DRM_MODE_FLAG_PVSYNC) << + ZYNQMP_DP_MAIN_STREAM_POLARITY_VSYNC_SHIFT) | + (!!(mode->flags & DRM_MODE_FLAG_PHSYNC) << + ZYNQMP_DP_MAIN_STREAM_POLARITY_HSYNC_SHIFT)); + zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_HSWIDTH, + mode->hsync_end - mode->hsync_start); + zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_VSWIDTH, + mode->vsync_end - mode->vsync_start); + zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_HRES, mode->hdisplay); + zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_VRES, mode->vdisplay); + zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_HSTART, + mode->htotal - mode->hsync_start); + zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_VSTART, + mode->vtotal - mode->vsync_start); + + /* In synchronous mode, set the diviers */ + if (dp->config.misc0 & ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK) { + reg = drm_dp_bw_code_to_link_rate(dp->mode.bw_code); + zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_N_VID, reg); + zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_M_VID, mode->clock); + rate = zynqmp_disp_get_audio_clk_rate(dp->dpsub->disp); + if (rate) { + dev_dbg(dp->dev, "Audio rate: %d\n", rate / 512); + zynqmp_dp_write(dp, ZYNQMP_DP_TX_N_AUD, reg); + zynqmp_dp_write(dp, ZYNQMP_DP_TX_M_AUD, rate / 1000); + } + } + + /* Only 2 channel audio is supported now */ + if (zynqmp_disp_audio_enabled(dp->dpsub->disp)) + zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CHANNELS, 1); + + zynqmp_dp_write(dp, ZYNQMP_DP_USER_PIX_WIDTH, 1); + + /* Translate to the native 16 bit datapath based on IP core spec */ + wpl = (mode->hdisplay * dp->config.bpp + 15) / 16; + reg = wpl + wpl % lane_cnt - lane_cnt; + zynqmp_dp_write(dp, ZYNQMP_DP_USER_DATA_COUNT_PER_LANE, reg); +} + +/* ----------------------------------------------------------------------------- + * DRM Connector + */ + +static enum drm_connector_status +zynqmp_dp_connector_detect(struct drm_connector *connector, bool force) +{ + struct zynqmp_dp *dp = connector_to_dp(connector); + struct zynqmp_dp_link_config *link_config = &dp->link_config; + u32 state, i; + int ret; + + /* + * This is from heuristic. It takes some delay (ex, 100 ~ 500 msec) to + * get the HPD signal with some monitors. + */ + for (i = 0; i < 10; i++) { + state = zynqmp_dp_read(dp, ZYNQMP_DP_INTERRUPT_SIGNAL_STATE); + if (state & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_HPD) + break; + msleep(100); + } + + if (state & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_HPD) { + ret = drm_dp_dpcd_read(&dp->aux, 0x0, dp->dpcd, + sizeof(dp->dpcd)); + if (ret < 0) { + dev_dbg(dp->dev, "DPCD read failes"); + goto disconnected; + } + + link_config->max_rate = min_t(int, + drm_dp_max_link_rate(dp->dpcd), + DP_HIGH_BIT_RATE2); + link_config->max_lanes = min_t(u8, + drm_dp_max_lane_count(dp->dpcd), + dp->num_lanes); + + dp->status = connector_status_connected; + return connector_status_connected; + } + +disconnected: + dp->status = connector_status_disconnected; + return connector_status_disconnected; +} + +static int zynqmp_dp_connector_get_modes(struct drm_connector *connector) +{ + struct zynqmp_dp *dp = connector_to_dp(connector); + struct edid *edid; + int ret; + + edid = drm_get_edid(connector, &dp->aux.ddc); + if (!edid) + return 0; + + drm_connector_update_edid_property(connector, edid); + ret = drm_add_edid_modes(connector, edid); + kfree(edid); + + return ret; +} + +static struct drm_encoder * +zynqmp_dp_connector_best_encoder(struct drm_connector *connector) +{ + struct zynqmp_dp *dp = connector_to_dp(connector); + + return &dp->encoder; +} + +static int zynqmp_dp_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct zynqmp_dp *dp = connector_to_dp(connector); + u8 max_lanes = dp->link_config.max_lanes; + u8 bpp = dp->config.bpp; + int max_rate = dp->link_config.max_rate; + int rate; + + if (mode->clock > ZYNQMP_MAX_FREQ) { + dev_dbg(dp->dev, "filtered the mode, %s,for high pixel rate\n", + mode->name); + drm_mode_debug_printmodeline(mode); + return MODE_CLOCK_HIGH; + } + + /* Check with link rate and lane count */ + rate = zynqmp_dp_max_rate(max_rate, max_lanes, bpp); + if (mode->clock > rate) { + dev_dbg(dp->dev, "filtered the mode, %s,for high pixel rate\n", + mode->name); + drm_mode_debug_printmodeline(mode); + return MODE_CLOCK_HIGH; + } + + return MODE_OK; +} + +static const struct drm_connector_funcs zynqmp_dp_connector_funcs = { + .detect = zynqmp_dp_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .reset = drm_atomic_helper_connector_reset, +}; + +static const struct drm_connector_helper_funcs +zynqmp_dp_connector_helper_funcs = { + .get_modes = zynqmp_dp_connector_get_modes, + .best_encoder = zynqmp_dp_connector_best_encoder, + .mode_valid = zynqmp_dp_connector_mode_valid, +}; + +/* ----------------------------------------------------------------------------- + * DRM Encoder + */ + +static void zynqmp_dp_encoder_enable(struct drm_encoder *encoder) +{ + struct zynqmp_dp *dp = encoder_to_dp(encoder); + unsigned int i; + int ret = 0; + + pm_runtime_get_sync(dp->dev); + dp->enabled = true; + zynqmp_dp_update_misc(dp); + if (zynqmp_disp_audio_enabled(dp->dpsub->disp)) + zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 1); + zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN, 0); + if (dp->status == connector_status_connected) { + for (i = 0; i < 3; i++) { + ret = drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, + DP_SET_POWER_D0); + if (ret == 1) + break; + usleep_range(300, 500); + } + /* Some monitors take time to wake up properly */ + msleep(zynqmp_dp_power_on_delay_ms); + } + if (ret != 1) + dev_dbg(dp->dev, "DP aux failed\n"); + else + zynqmp_dp_train_loop(dp); + zynqmp_dp_write(dp, ZYNQMP_DP_SOFTWARE_RESET, + ZYNQMP_DP_SOFTWARE_RESET_ALL); + zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 1); +} + +static void zynqmp_dp_encoder_disable(struct drm_encoder *encoder) +{ + struct zynqmp_dp *dp = encoder_to_dp(encoder); + + dp->enabled = false; + cancel_delayed_work(&dp->hpd_work); + zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 0); + drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D3); + zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN, + ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL); + if (zynqmp_disp_audio_enabled(dp->dpsub->disp)) + zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 0); + pm_runtime_put_sync(dp->dev); +} + +static void +zynqmp_dp_encoder_atomic_mode_set(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *connector_state) +{ + struct zynqmp_dp *dp = encoder_to_dp(encoder); + struct drm_display_mode *mode = &crtc_state->mode; + struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; + u8 max_lanes = dp->link_config.max_lanes; + u8 bpp = dp->config.bpp; + int rate, max_rate = dp->link_config.max_rate; + int ret; + + zynqmp_dp_set_format(dp, ZYNQMP_DPSUB_FORMAT_RGB, 8); + + /* Check again as bpp or format might have been chagned */ + rate = zynqmp_dp_max_rate(max_rate, max_lanes, bpp); + if (mode->clock > rate) { + dev_err(dp->dev, "the mode, %s,has too high pixel rate\n", + mode->name); + drm_mode_debug_printmodeline(mode); + } + + ret = zynqmp_dp_mode_configure(dp, adjusted_mode->clock, 0); + if (ret < 0) + return; + + zynqmp_dp_encoder_mode_set_transfer_unit(dp, adjusted_mode); + zynqmp_dp_encoder_mode_set_stream(dp, adjusted_mode); +} + +#define ZYNQMP_DP_MIN_H_BACKPORCH 20 + +static int +zynqmp_dp_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_display_mode *mode = &crtc_state->mode; + struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; + int diff = mode->htotal - mode->hsync_end; + + /* + * ZynqMP DP requires horizontal backporch to be greater than 12. + * This limitation may not be compatible with the sink device. + */ + if (diff < ZYNQMP_DP_MIN_H_BACKPORCH) { + int vrefresh = (adjusted_mode->clock * 1000) / + (adjusted_mode->vtotal * adjusted_mode->htotal); + + dev_dbg(encoder->dev->dev, "hbackporch adjusted: %d to %d", + diff, ZYNQMP_DP_MIN_H_BACKPORCH - diff); + diff = ZYNQMP_DP_MIN_H_BACKPORCH - diff; + adjusted_mode->htotal += diff; + adjusted_mode->clock = adjusted_mode->vtotal * + adjusted_mode->htotal * vrefresh / 1000; + } + + return 0; +} + +static const struct drm_encoder_helper_funcs zynqmp_dp_encoder_helper_funcs = { + .enable = zynqmp_dp_encoder_enable, + .disable = zynqmp_dp_encoder_disable, + .atomic_mode_set = zynqmp_dp_encoder_atomic_mode_set, + .atomic_check = zynqmp_dp_encoder_atomic_check, +}; + +/* ----------------------------------------------------------------------------- + * Interrupt Handling + */ + +/** + * zynqmp_dp_enable_vblank - Enable vblank + * @dp: DisplayPort IP core structure + * + * Enable vblank interrupt + */ +void zynqmp_dp_enable_vblank(struct zynqmp_dp *dp) +{ + zynqmp_dp_write(dp, ZYNQMP_DP_INT_EN, ZYNQMP_DP_INT_VBLANK_START); +} + +/** + * zynqmp_dp_disable_vblank - Disable vblank + * @dp: DisplayPort IP core structure + * + * Disable vblank interrupt + */ +void zynqmp_dp_disable_vblank(struct zynqmp_dp *dp) +{ + zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, ZYNQMP_DP_INT_VBLANK_START); +} + +static void zynqmp_dp_hpd_work_func(struct work_struct *work) +{ + struct zynqmp_dp *dp; + + dp = container_of(work, struct zynqmp_dp, hpd_work.work); + + if (dp->drm) + drm_helper_hpd_irq_event(dp->drm); +} + +static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data) +{ + struct zynqmp_dp *dp = (struct zynqmp_dp *)data; + u32 status, mask; + + status = zynqmp_dp_read(dp, ZYNQMP_DP_INT_STATUS); + mask = zynqmp_dp_read(dp, ZYNQMP_DP_INT_MASK); + if (!(status & ~mask)) + return IRQ_NONE; + + /* dbg for diagnostic, but not much that the driver can do */ + if (status & ZYNQMP_DP_INT_CHBUF_UNDERFLW_MASK) + dev_dbg_ratelimited(dp->dev, "underflow interrupt\n"); + if (status & ZYNQMP_DP_INT_CHBUF_OVERFLW_MASK) + dev_dbg_ratelimited(dp->dev, "overflow interrupt\n"); + + zynqmp_dp_write(dp, ZYNQMP_DP_INT_STATUS, status); + + if (status & ZYNQMP_DP_INT_VBLANK_START) + zynqmp_disp_handle_vblank(dp->dpsub->disp); + + if (status & ZYNQMP_DP_INT_HPD_EVENT) + schedule_delayed_work(&dp->hpd_work, 0); + + if (status & ZYNQMP_DP_INT_HPD_IRQ) { + int ret; + u8 status[DP_LINK_STATUS_SIZE + 2]; + + ret = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, status, + DP_LINK_STATUS_SIZE + 2); + if (ret < 0) + goto handled; + + if (status[4] & DP_LINK_STATUS_UPDATED || + !drm_dp_clock_recovery_ok(&status[2], dp->mode.lane_cnt) || + !drm_dp_channel_eq_ok(&status[2], dp->mode.lane_cnt)) { + zynqmp_dp_train_loop(dp); + } + } + +handled: + return IRQ_HANDLED; +} + +/* ----------------------------------------------------------------------------- + * Initialization & Cleanup + */ + +int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub) +{ + struct zynqmp_dp *dp = dpsub->dp; + struct drm_encoder *encoder = &dp->encoder; + struct drm_connector *connector = &dp->connector; + int ret; + + dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK; + zynqmp_dp_set_format(dp, ZYNQMP_DPSUB_FORMAT_RGB, 8); + + /* Create the DRM encoder and connector. */ + encoder->possible_crtcs |= zynqmp_disp_get_crtc_mask(dpsub->disp); + drm_simple_encoder_init(dp->drm, encoder, DRM_MODE_ENCODER_TMDS); + drm_encoder_helper_add(encoder, &zynqmp_dp_encoder_helper_funcs); + + connector->polled = DRM_CONNECTOR_POLL_HPD; + ret = drm_connector_init(encoder->dev, connector, + &zynqmp_dp_connector_funcs, + DRM_MODE_CONNECTOR_DisplayPort); + if (ret) { + dev_err(dp->dev, "failed to create the DRM connector\n"); + return ret; + } + + drm_connector_helper_add(connector, &zynqmp_dp_connector_helper_funcs); + drm_connector_register(connector); + drm_connector_attach_encoder(connector, encoder); + + /* Initialize and register the AUX adapter. */ + ret = zynqmp_dp_aux_init(dp); + if (ret) { + dev_err(dp->dev, "failed to initialize DP aux\n"); + return ret; + } + + /* Now that initialisation is complete, enable interrupts. */ + zynqmp_dp_write(dp, ZYNQMP_DP_INT_EN, ZYNQMP_DP_INT_ALL); + + return 0; +} + +int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm) +{ + struct platform_device *pdev = to_platform_device(dpsub->dev); + struct zynqmp_dp *dp; + struct resource *res; + int ret; + + dp = drmm_kzalloc(drm, sizeof(*dp), GFP_KERNEL); + if (!dp) + return -ENOMEM; + + dp->dev = &pdev->dev; + dp->dpsub = dpsub; + dp->status = connector_status_disconnected; + dp->drm = drm; + + INIT_DELAYED_WORK(&dp->hpd_work, zynqmp_dp_hpd_work_func); + + dpsub->dp = dp; + + /* Acquire all resources (IOMEM, IRQ and PHYs). */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dp"); + dp->iomem = devm_ioremap_resource(dp->dev, res); + if (IS_ERR(dp->iomem)) + return PTR_ERR(dp->iomem); + + dp->irq = platform_get_irq(pdev, 0); + if (dp->irq < 0) + return dp->irq; + + dp->reset = devm_reset_control_get(dp->dev, NULL); + if (IS_ERR(dp->reset)) { + if (PTR_ERR(dp->reset) != -EPROBE_DEFER) + dev_err(dp->dev, "failed to get reset: %ld\n", + PTR_ERR(dp->reset)); + return PTR_ERR(dp->reset); + } + + ret = zynqmp_dp_phy_probe(dp); + if (ret) + return ret; + + /* Initialize the hardware. */ + zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN, + ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL); + zynqmp_dp_set(dp, ZYNQMP_DP_PHY_RESET, ZYNQMP_DP_PHY_RESET_ALL_RESET); + zynqmp_dp_write(dp, ZYNQMP_DP_FORCE_SCRAMBLER_RESET, 1); + zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMITTER_ENABLE, 0); + zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, 0xffffffff); + + ret = zynqmp_dp_phy_init(dp); + if (ret) + return ret; + + zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMITTER_ENABLE, 1); + + /* + * Now that the hardware is initialized and won't generate spurious + * interrupts, request the IRQ. + */ + ret = devm_request_threaded_irq(dp->dev, dp->irq, NULL, + zynqmp_dp_irq_handler, IRQF_ONESHOT, + dev_name(dp->dev), dp); + if (ret < 0) + goto error; + + dev_dbg(dp->dev, "ZynqMP DisplayPort Tx probed with %u lanes\n", + dp->num_lanes); + + return 0; + +error: + zynqmp_dp_phy_exit(dp); + return ret; +} + +void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub) +{ + struct zynqmp_dp *dp = dpsub->dp; + + zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, ZYNQMP_DP_INT_ALL); + disable_irq(dp->irq); + + cancel_delayed_work_sync(&dp->hpd_work); + zynqmp_dp_aux_cleanup(dp); + + zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMITTER_ENABLE, 0); + zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, 0xffffffff); + + zynqmp_dp_phy_exit(dp); +} diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.h b/drivers/gpu/drm/xlnx/zynqmp_dp.h new file mode 100644 index 000000000000..4507740093f6 --- /dev/null +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ZynqMP DisplayPort Driver + * + * Copyright (C) 2017 - 2020 Xilinx, Inc. + * + * Authors: + * - Hyun Woo Kwon <hyun.kwon@xilinx.com> + * - Laurent Pinchart <laurent.pinchart@ideasonboard.com> + */ + +#ifndef _ZYNQMP_DP_H_ +#define _ZYNQMP_DP_H_ + +struct drm_device; +struct platform_device; +struct zynqmp_dp; +struct zynqmp_dpsub; + +void zynqmp_dp_enable_vblank(struct zynqmp_dp *dp); +void zynqmp_dp_disable_vblank(struct zynqmp_dp *dp); + +int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub); +int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm); +void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub); + +#endif /* _ZYNQMP_DP_H_ */ diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c new file mode 100644 index 000000000000..26328c76305b --- /dev/null +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ZynqMP DisplayPort Subsystem Driver + * + * Copyright (C) 2017 - 2020 Xilinx, Inc. + * + * Authors: + * - Hyun Woo Kwon <hyun.kwon@xilinx.com> + * - Laurent Pinchart <laurent.pinchart@ideasonboard.com> + */ + +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/module.h> +#include <linux/of_reserved_mem.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_managed.h> +#include <drm/drm_mode_config.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h> + +#include "zynqmp_disp.h" +#include "zynqmp_dp.h" +#include "zynqmp_dpsub.h" + +/* ----------------------------------------------------------------------------- + * Dumb Buffer & Framebuffer Allocation + */ + +static int zynqmp_dpsub_dumb_create(struct drm_file *file_priv, + struct drm_device *drm, + struct drm_mode_create_dumb *args) +{ + struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(drm); + unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8); + + /* Enforce the alignment constraints of the DMA engine. */ + args->pitch = ALIGN(pitch, dpsub->dma_align); + + return drm_gem_cma_dumb_create_internal(file_priv, drm, args); +} + +static struct drm_framebuffer * +zynqmp_dpsub_fb_create(struct drm_device *drm, struct drm_file *file_priv, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(drm); + struct drm_mode_fb_cmd2 cmd = *mode_cmd; + unsigned int i; + + /* Enforce the alignment constraints of the DMA engine. */ + for (i = 0; i < ARRAY_SIZE(cmd.pitches); ++i) + cmd.pitches[i] = ALIGN(cmd.pitches[i], dpsub->dma_align); + + return drm_gem_fb_create(drm, file_priv, &cmd); +} + +static const struct drm_mode_config_funcs zynqmp_dpsub_mode_config_funcs = { + .fb_create = zynqmp_dpsub_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +/* ----------------------------------------------------------------------------- + * DRM/KMS Driver + */ + +DEFINE_DRM_GEM_CMA_FOPS(zynqmp_dpsub_drm_fops); + +static struct drm_driver zynqmp_dpsub_drm_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | + DRIVER_ATOMIC, + + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, + .gem_free_object_unlocked = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + .dumb_create = zynqmp_dpsub_dumb_create, + .dumb_destroy = drm_gem_dumb_destroy, + + .fops = &zynqmp_dpsub_drm_fops, + + .name = "zynqmp-dpsub", + .desc = "Xilinx DisplayPort Subsystem Driver", + .date = "20130509", + .major = 1, + .minor = 0, +}; + +static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub) +{ + struct drm_device *drm = &dpsub->drm; + int ret; + + /* Initialize mode config, vblank and the KMS poll helper. */ + ret = drmm_mode_config_init(drm); + if (ret < 0) + goto err_dev_put; + + drm->mode_config.funcs = &zynqmp_dpsub_mode_config_funcs; + drm->mode_config.min_width = 0; + drm->mode_config.min_height = 0; + drm->mode_config.max_width = ZYNQMP_DISP_MAX_WIDTH; + drm->mode_config.max_height = ZYNQMP_DISP_MAX_HEIGHT; + + ret = drm_vblank_init(drm, 1); + if (ret) + goto err_dev_put; + + drm->irq_enabled = 1; + + drm_kms_helper_poll_init(drm); + + /* + * Initialize the DISP and DP components. This will creates planes, + * CRTC, encoder and connector. The DISP should be initialized first as + * the DP encoder needs the CRTC. + */ + ret = zynqmp_disp_drm_init(dpsub); + if (ret) + goto err_poll_fini; + + ret = zynqmp_dp_drm_init(dpsub); + if (ret) + goto err_poll_fini; + + /* Reset all components and register the DRM device. */ + drm_mode_config_reset(drm); + + ret = drm_dev_register(drm, 0); + if (ret < 0) + goto err_poll_fini; + + /* Initialize fbdev generic emulation. */ + drm_fbdev_generic_setup(drm, 24); + + return 0; + +err_poll_fini: + drm_kms_helper_poll_fini(drm); +err_dev_put: + drm_dev_put(drm); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Power Management + */ + +static int __maybe_unused zynqmp_dpsub_suspend(struct device *dev) +{ + struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev); + + return drm_mode_config_helper_suspend(&dpsub->drm); +} + +static int __maybe_unused zynqmp_dpsub_resume(struct device *dev) +{ + struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev); + + return drm_mode_config_helper_resume(&dpsub->drm); +} + +static const struct dev_pm_ops zynqmp_dpsub_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(zynqmp_dpsub_suspend, zynqmp_dpsub_resume) +}; + +/* ----------------------------------------------------------------------------- + * Probe & Remove + */ + +static int zynqmp_dpsub_init_clocks(struct zynqmp_dpsub *dpsub) +{ + int ret; + + dpsub->apb_clk = devm_clk_get(dpsub->dev, "dp_apb_clk"); + if (IS_ERR(dpsub->apb_clk)) + return PTR_ERR(dpsub->apb_clk); + + ret = clk_prepare_enable(dpsub->apb_clk); + if (ret) { + dev_err(dpsub->dev, "failed to enable the APB clock\n"); + return ret; + } + + return 0; +} + +static int zynqmp_dpsub_probe(struct platform_device *pdev) +{ + struct zynqmp_dpsub *dpsub; + int ret; + + /* Allocate private data. */ + dpsub = kzalloc(sizeof(*dpsub), GFP_KERNEL); + if (!dpsub) + return -ENOMEM; + + dpsub->dev = &pdev->dev; + platform_set_drvdata(pdev, dpsub); + + dma_set_mask(dpsub->dev, DMA_BIT_MASK(ZYNQMP_DISP_MAX_DMA_BIT)); + + /* + * Initialize the DRM device early, as the DRM core mandates usage of + * the managed memory helpers tied to the DRM device. + */ + ret = drm_dev_init(&dpsub->drm, &zynqmp_dpsub_drm_driver, &pdev->dev); + if (ret < 0) { + kfree(dpsub); + return ret; + } + + drmm_add_final_kfree(&dpsub->drm, dpsub); + + /* Try the reserved memory. Proceed if there's none. */ + of_reserved_mem_device_init(&pdev->dev); + + ret = zynqmp_dpsub_init_clocks(dpsub); + if (ret < 0) + goto err_mem; + + pm_runtime_enable(&pdev->dev); + + /* + * DP should be probed first so that the zynqmp_disp can set the output + * format accordingly. + */ + ret = zynqmp_dp_probe(dpsub, &dpsub->drm); + if (ret) + goto err_pm; + + ret = zynqmp_disp_probe(dpsub, &dpsub->drm); + if (ret) + goto err_dp; + + ret = zynqmp_dpsub_drm_init(dpsub); + if (ret) + goto err_disp; + + dev_info(&pdev->dev, "ZynqMP DisplayPort Subsystem driver probed"); + + return 0; + +err_disp: + zynqmp_disp_remove(dpsub); +err_dp: + zynqmp_dp_remove(dpsub); +err_pm: + pm_runtime_disable(&pdev->dev); + clk_disable_unprepare(dpsub->apb_clk); +err_mem: + of_reserved_mem_device_release(&pdev->dev); + return ret; +} + +static int zynqmp_dpsub_remove(struct platform_device *pdev) +{ + struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev); + struct drm_device *drm = &dpsub->drm; + + drm_dev_unregister(drm); + drm_atomic_helper_shutdown(drm); + drm_kms_helper_poll_fini(drm); + + zynqmp_disp_remove(dpsub); + zynqmp_dp_remove(dpsub); + + pm_runtime_disable(&pdev->dev); + clk_disable_unprepare(dpsub->apb_clk); + of_reserved_mem_device_release(&pdev->dev); + + drm_dev_put(drm); + + return 0; +} + +static void zynqmp_dpsub_shutdown(struct platform_device *pdev) +{ + struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev); + + drm_atomic_helper_shutdown(&dpsub->drm); +} + +static const struct of_device_id zynqmp_dpsub_of_match[] = { + { .compatible = "xlnx,zynqmp-dpsub-1.7", }, + { /* end of table */ }, +}; +MODULE_DEVICE_TABLE(of, zynqmp_dpsub_of_match); + +static struct platform_driver zynqmp_dpsub_driver = { + .probe = zynqmp_dpsub_probe, + .remove = zynqmp_dpsub_remove, + .shutdown = zynqmp_dpsub_shutdown, + .driver = { + .name = "zynqmp-dpsub", + .pm = &zynqmp_dpsub_pm_ops, + .of_match_table = zynqmp_dpsub_of_match, + }, +}; + +module_platform_driver(zynqmp_dpsub_driver); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("ZynqMP DP Subsystem Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h new file mode 100644 index 000000000000..c04026d82639 --- /dev/null +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ZynqMP DPSUB Subsystem Driver + * + * Copyright (C) 2017 - 2020 Xilinx, Inc. + * + * Authors: + * - Hyun Woo Kwon <hyun.kwon@xilinx.com> + * - Laurent Pinchart <laurent.pinchart@ideasonboard.com> + */ + +#ifndef _ZYNQMP_DPSUB_H_ +#define _ZYNQMP_DPSUB_H_ + +struct clk; +struct device; +struct drm_device; +struct zynqmp_disp; +struct zynqmp_dp; + +enum zynqmp_dpsub_format { + ZYNQMP_DPSUB_FORMAT_RGB, + ZYNQMP_DPSUB_FORMAT_YCRCB444, + ZYNQMP_DPSUB_FORMAT_YCRCB422, + ZYNQMP_DPSUB_FORMAT_YONLY, +}; + +/** + * struct zynqmp_dpsub - ZynqMP DisplayPort Subsystem + * @drm: The DRM/KMS device + * @dev: The physical device + * @apb_clk: The APB clock + * @disp: The display controller + * @dp: The DisplayPort controller + * @dma_align: DMA alignment constraint (must be a power of 2) + */ +struct zynqmp_dpsub { + struct drm_device drm; + struct device *dev; + + struct clk *apb_clk; + + struct zynqmp_disp *disp; + struct zynqmp_dp *dp; + + unsigned int dma_align; +}; + +static inline struct zynqmp_dpsub *to_zynqmp_dpsub(struct drm_device *drm) +{ + return container_of(drm, struct zynqmp_dpsub, drm); +} + +#endif /* _ZYNQMP_DPSUB_H_ */ diff --git a/drivers/gpu/drm/zte/zx_vga.c b/drivers/gpu/drm/zte/zx_vga.c index a7ed7f5ca837..0f9bbb7e3b8d 100644 --- a/drivers/gpu/drm/zte/zx_vga.c +++ b/drivers/gpu/drm/zte/zx_vga.c @@ -155,7 +155,7 @@ static int zx_vga_register(struct drm_device *drm, struct zx_vga *vga) if (ret) { DRM_DEV_ERROR(dev, "failed to init encoder: %d\n", ret); return ret; - }; + } drm_encoder_helper_add(encoder, &zx_vga_encoder_helper_funcs); @@ -168,7 +168,7 @@ static int zx_vga_register(struct drm_device *drm, struct zx_vga *vga) if (ret) { DRM_DEV_ERROR(dev, "failed to init connector: %d\n", ret); goto clean_encoder; - }; + } drm_connector_helper_add(connector, &zx_vga_connector_helper_funcs); @@ -176,7 +176,7 @@ static int zx_vga_register(struct drm_device *drm, struct zx_vga *vga) if (ret) { DRM_DEV_ERROR(dev, "failed to attach encoder: %d\n", ret); goto clean_connector; - }; + } return 0; diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index ee2a025e54cf..b3dae9ec1a38 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -124,6 +124,8 @@ enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) case V4L2_PIX_FMT_RGBX32: case V4L2_PIX_FMT_ARGB32: case V4L2_PIX_FMT_XRGB32: + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: return IPUV3_COLORSPACE_RGB; default: return IPUV3_COLORSPACE_UNKNOWN; diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c index eeca50d9a1ee..aa1d4b6d278f 100644 --- a/drivers/gpu/ipu-v3/ipu-image-convert.c +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -137,6 +137,17 @@ struct ipu_image_convert_ctx; struct ipu_image_convert_chan; struct ipu_image_convert_priv; +enum eof_irq_mask { + EOF_IRQ_IN = BIT(0), + EOF_IRQ_ROT_IN = BIT(1), + EOF_IRQ_OUT = BIT(2), + EOF_IRQ_ROT_OUT = BIT(3), +}; + +#define EOF_IRQ_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT) +#define EOF_IRQ_ROT_COMPLETE (EOF_IRQ_IN | EOF_IRQ_OUT | \ + EOF_IRQ_ROT_IN | EOF_IRQ_ROT_OUT) + struct ipu_image_convert_ctx { struct ipu_image_convert_chan *chan; @@ -173,6 +184,9 @@ struct ipu_image_convert_ctx { /* where to place converted tile in dest image */ unsigned int out_tile_map[MAX_TILES]; + /* mask of completed EOF irqs at every tile conversion */ + enum eof_irq_mask eof_mask; + struct list_head list; }; @@ -189,6 +203,8 @@ struct ipu_image_convert_chan { struct ipuv3_channel *rotation_out_chan; /* the IPU end-of-frame irqs */ + int in_eof_irq; + int rot_in_eof_irq; int out_eof_irq; int rot_out_eof_irq; @@ -1380,6 +1396,9 @@ static int convert_start(struct ipu_image_convert_run *run, unsigned int tile) dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p tile %u -> %u\n", __func__, chan->ic_task, ctx, run, tile, dst_tile); + /* clear EOF irq mask */ + ctx->eof_mask = 0; + if (ipu_rot_mode_is_irt(ctx->rot_mode)) { /* swap width/height for resizer */ dest_width = d_image->tile[dst_tile].height; @@ -1615,7 +1634,7 @@ static bool ic_settings_changed(struct ipu_image_convert_ctx *ctx) } /* hold irqlock when calling */ -static irqreturn_t do_irq(struct ipu_image_convert_run *run) +static irqreturn_t do_tile_complete(struct ipu_image_convert_run *run) { struct ipu_image_convert_ctx *ctx = run->ctx; struct ipu_image_convert_chan *chan = ctx->chan; @@ -1700,6 +1719,7 @@ static irqreturn_t do_irq(struct ipu_image_convert_run *run) ctx->cur_buf_num ^= 1; } + ctx->eof_mask = 0; /* clear EOF irq mask for next tile */ ctx->next_tile++; return IRQ_HANDLED; done: @@ -1709,13 +1729,15 @@ done: return IRQ_WAKE_THREAD; } -static irqreturn_t norotate_irq(int irq, void *data) +static irqreturn_t eof_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; + irqreturn_t ret = IRQ_HANDLED; + bool tile_complete = false; unsigned long flags; - irqreturn_t ret; spin_lock_irqsave(&chan->irqlock, flags); @@ -1728,46 +1750,33 @@ static irqreturn_t norotate_irq(int irq, void *data) 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) { + if (irq == chan->in_eof_irq) { + ctx->eof_mask |= EOF_IRQ_IN; + } else if (irq == chan->out_eof_irq) { + ctx->eof_mask |= EOF_IRQ_OUT; + } else if (irq == chan->rot_in_eof_irq || + irq == chan->rot_out_eof_irq) { + if (!ipu_rot_mode_is_irt(ctx->rot_mode)) { + /* this was NOT a rotation op, shouldn't happen */ + dev_err(priv->ipu->dev, + "Unexpected rotation interrupt\n"); + goto out; + } + ctx->eof_mask |= (irq == chan->rot_in_eof_irq) ? + EOF_IRQ_ROT_IN : EOF_IRQ_ROT_OUT; + } else { + dev_err(priv->ipu->dev, "Received unknown irq %d\n", irq); 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; - } + if (ipu_rot_mode_is_irt(ctx->rot_mode)) + tile_complete = (ctx->eof_mask == EOF_IRQ_ROT_COMPLETE); + else + tile_complete = (ctx->eof_mask == EOF_IRQ_COMPLETE); - ret = do_irq(run); + if (tile_complete) + ret = do_tile_complete(run); out: spin_unlock_irqrestore(&chan->irqlock, flags); return ret; @@ -1801,6 +1810,10 @@ static void force_abort(struct ipu_image_convert_ctx *ctx) static void release_ipu_resources(struct ipu_image_convert_chan *chan) { + if (chan->in_eof_irq >= 0) + free_irq(chan->in_eof_irq, chan); + if (chan->rot_in_eof_irq >= 0) + free_irq(chan->rot_in_eof_irq, chan); if (chan->out_eof_irq >= 0) free_irq(chan->out_eof_irq, chan); if (chan->rot_out_eof_irq >= 0) @@ -1819,7 +1832,27 @@ static void release_ipu_resources(struct ipu_image_convert_chan *chan) 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; + chan->in_eof_irq = -1; + chan->rot_in_eof_irq = -1; + chan->out_eof_irq = -1; + chan->rot_out_eof_irq = -1; +} + +static int get_eof_irq(struct ipu_image_convert_chan *chan, + struct ipuv3_channel *channel) +{ + struct ipu_image_convert_priv *priv = chan->priv; + int ret, irq; + + irq = ipu_idmac_channel_irq(priv->ipu, channel, IPU_IRQ_EOF); + + ret = request_threaded_irq(irq, eof_irq, do_bh, 0, "ipu-ic", chan); + if (ret < 0) { + dev_err(priv->ipu->dev, "could not acquire irq %d\n", irq); + return ret; + } + + return irq; } static int get_ipu_resources(struct ipu_image_convert_chan *chan) @@ -1855,31 +1888,33 @@ static int get_ipu_resources(struct ipu_image_convert_chan *chan) } /* acquire the EOF interrupts */ - chan->out_eof_irq = ipu_idmac_channel_irq(priv->ipu, - chan->out_chan, - IPU_IRQ_EOF); + ret = get_eof_irq(chan, chan->in_chan); + if (ret < 0) { + chan->in_eof_irq = -1; + goto err; + } + chan->in_eof_irq = ret; - ret = request_threaded_irq(chan->out_eof_irq, norotate_irq, do_bh, - 0, "ipu-ic", chan); + ret = get_eof_irq(chan, chan->rotation_in_chan); if (ret < 0) { - dev_err(priv->ipu->dev, "could not acquire irq %d\n", - chan->out_eof_irq); - chan->out_eof_irq = -1; + chan->rot_in_eof_irq = -1; goto err; } + chan->rot_in_eof_irq = ret; - chan->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu, - chan->rotation_out_chan, - IPU_IRQ_EOF); + ret = get_eof_irq(chan, chan->out_chan); + if (ret < 0) { + chan->out_eof_irq = -1; + goto err; + } + chan->out_eof_irq = ret; - ret = request_threaded_irq(chan->rot_out_eof_irq, rotate_irq, do_bh, - 0, "ipu-ic", chan); + ret = get_eof_irq(chan, chan->rotation_out_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; } + chan->rot_out_eof_irq = ret; return 0; err: @@ -2458,6 +2493,8 @@ int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev) chan->ic_task = i; chan->priv = priv; chan->dma_ch = &image_convert_dma_chan[i]; + chan->in_eof_irq = -1; + chan->rot_in_eof_irq = -1; chan->out_eof_irq = -1; chan->rot_out_eof_irq = -1; diff --git a/drivers/of/property.c b/drivers/of/property.c index 1f2086f4e7ce..d3f9fb98e882 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -30,6 +30,29 @@ #include "of_private.h" /** + * of_graph_is_present() - check graph's presence + * @node: pointer to device_node containing graph port + * + * Return: True if @node has a port or ports (with a port) sub-node, + * false otherwise. + */ +bool of_graph_is_present(const struct device_node *node) +{ + struct device_node *ports, *port; + + ports = of_get_child_by_name(node, "ports"); + if (ports) + node = ports; + + port = of_get_child_by_name(node, "port"); + of_node_put(ports); + of_node_put(port); + + return !!port; +} +EXPORT_SYMBOL(of_graph_is_present); + +/** * of_property_count_elems_of_size - Count the number of elements in a property * * @np: device node from which the property value is to be read. diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 0f559aeaf469..bd5a9773a588 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -272,26 +272,6 @@ config FB_PM2_FIFO_DISCONNECT help Support the Permedia2 FIFO disconnect feature. -config FB_ARMCLCD - tristate "ARM PrimeCell PL110 support" - depends on ARM || ARM64 || COMPILE_TEST - depends on FB && ARM_AMBA && HAS_IOMEM - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - select FB_MODE_HELPERS if OF - select VIDEOMODE_HELPERS if OF - select BACKLIGHT_CLASS_DEVICE if OF - help - This framebuffer device driver is for the ARM PrimeCell PL110 - Colour LCD controller. ARM PrimeCells provide the building - blocks for System on a Chip devices. - - If you want to compile this as a module (=code which can be - inserted into and removed from the running kernel), say M - here and read <file:Documentation/kbuild/modules.rst>. The module - will be called amba-clcd. - config FB_ACORN bool "Acorn VIDC support" depends on (FB = y) && ARM && ARCH_ACORN @@ -844,7 +824,7 @@ config FB_OPENCORES systems (e.g. Altera socfpga or Xilinx Zynq) on FPGAs. The source code and specification for the core is available at - <http://opencores.org/project,vga_lcd> + <https://opencores.org/project,vga_lcd> config FB_S1D13XXX tristate "Epson S1D13XXX framebuffer support" @@ -855,7 +835,7 @@ config FB_S1D13XXX help Support for S1D13XXX framebuffer device family (currently only working with S1D13806). Product specs at - <http://vdc.epson.com/> + <https://vdc.epson.com/> config FB_ATMEL tristate "AT91 LCD Controller support" @@ -1213,7 +1193,7 @@ config FB_RADEON don't need to choose this to run the Radeon in plain VGA mode. There is a product page at - http://products.amd.com/en-us/GraphicCardResult.aspx + https://products.amd.com/en-us/GraphicCardResult.aspx config FB_RADEON_I2C bool "DDC/I2C for ATI Radeon support" @@ -1381,7 +1361,7 @@ config FB_SIS help This is the frame buffer device driver for the SiS 300, 315, 330 and 340 series as well as XGI V3XT, V5, V8, Z7 graphics chipsets. - Specs available at <http://www.sis.com> and <http://www.xgitech.com>. + Specs available at <https://www.sis.com> and <http://www.xgitech.com>. To compile this driver as a module, choose M here; the module will be called sisfb. diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index aa6352798cf4..76a43ec8f24c 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -75,7 +75,6 @@ obj-$(CONFIG_FB_HIT) += hitfb.o obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o obj-$(CONFIG_FB_PVR2) += pvr2fb.o obj-$(CONFIG_FB_VOODOO1) += sstfb.o -obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o obj-$(CONFIG_FB_68328) += 68328fb.o obj-$(CONFIG_FB_GBE) += gbefb.o diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c deleted file mode 100644 index b7682de412d8..000000000000 --- a/drivers/video/fbdev/amba-clcd.c +++ /dev/null @@ -1,986 +0,0 @@ -/* - * linux/drivers/video/amba-clcd.c - * - * Copyright (C) 2001 ARM Limited, by David A Rusling - * Updated to 2.5, Deep Blue Solutions Ltd. - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - * - * ARM PrimeCell PL110 Color LCD Controller - */ -#include <linux/amba/bus.h> -#include <linux/amba/clcd.h> -#include <linux/backlight.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/dma-mapping.h> -#include <linux/fb.h> -#include <linux/init.h> -#include <linux/ioport.h> -#include <linux/list.h> -#include <linux/mm.h> -#include <linux/module.h> -#include <linux/of_address.h> -#include <linux/of_graph.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <video/display_timing.h> -#include <video/of_display_timing.h> -#include <video/videomode.h> - -#define to_clcd(info) container_of(info, struct clcd_fb, fb) - -/* This is limited to 16 characters when displayed by X startup */ -static const char *clcd_name = "CLCD FB"; - -/* - * Unfortunately, the enable/disable functions may be called either from - * process or IRQ context, and we _need_ to delay. This is _not_ good. - */ -static inline void clcdfb_sleep(unsigned int ms) -{ - if (in_atomic()) { - mdelay(ms); - } else { - msleep(ms); - } -} - -static inline void clcdfb_set_start(struct clcd_fb *fb) -{ - unsigned long ustart = fb->fb.fix.smem_start; - unsigned long lstart; - - ustart += fb->fb.var.yoffset * fb->fb.fix.line_length; - lstart = ustart + fb->fb.var.yres * fb->fb.fix.line_length / 2; - - writel(ustart, fb->regs + CLCD_UBAS); - writel(lstart, fb->regs + CLCD_LBAS); -} - -static void clcdfb_disable(struct clcd_fb *fb) -{ - u32 val; - - if (fb->board->disable) - fb->board->disable(fb); - - if (fb->panel->backlight) { - fb->panel->backlight->props.power = FB_BLANK_POWERDOWN; - backlight_update_status(fb->panel->backlight); - } - - val = readl(fb->regs + fb->off_cntl); - if (val & CNTL_LCDPWR) { - val &= ~CNTL_LCDPWR; - writel(val, fb->regs + fb->off_cntl); - - clcdfb_sleep(20); - } - if (val & CNTL_LCDEN) { - val &= ~CNTL_LCDEN; - writel(val, fb->regs + fb->off_cntl); - } - - /* - * Disable CLCD clock source. - */ - if (fb->clk_enabled) { - fb->clk_enabled = false; - clk_disable(fb->clk); - } -} - -static void clcdfb_enable(struct clcd_fb *fb, u32 cntl) -{ - /* - * Enable the CLCD clock source. - */ - if (!fb->clk_enabled) { - fb->clk_enabled = true; - clk_enable(fb->clk); - } - - /* - * Bring up by first enabling.. - */ - cntl |= CNTL_LCDEN; - writel(cntl, fb->regs + fb->off_cntl); - - clcdfb_sleep(20); - - /* - * and now apply power. - */ - cntl |= CNTL_LCDPWR; - writel(cntl, fb->regs + fb->off_cntl); - - /* - * Turn on backlight - */ - if (fb->panel->backlight) { - fb->panel->backlight->props.power = FB_BLANK_UNBLANK; - backlight_update_status(fb->panel->backlight); - } - - /* - * finally, enable the interface. - */ - if (fb->board->enable) - fb->board->enable(fb); -} - -static int -clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var) -{ - u32 caps; - int ret = 0; - - if (fb->panel->caps && fb->board->caps) - caps = fb->panel->caps & fb->board->caps; - else { - /* Old way of specifying what can be used */ - caps = fb->panel->cntl & CNTL_BGR ? - CLCD_CAP_BGR : CLCD_CAP_RGB; - /* But mask out 444 modes as they weren't supported */ - caps &= ~CLCD_CAP_444; - } - - /* Only TFT panels can do RGB888/BGR888 */ - if (!(fb->panel->cntl & CNTL_LCDTFT)) - caps &= ~CLCD_CAP_888; - - memset(&var->transp, 0, sizeof(var->transp)); - - var->red.msb_right = 0; - var->green.msb_right = 0; - var->blue.msb_right = 0; - - switch (var->bits_per_pixel) { - case 1: - case 2: - case 4: - case 8: - /* If we can't do 5551, reject */ - caps &= CLCD_CAP_5551; - if (!caps) { - ret = -EINVAL; - break; - } - - var->red.length = var->bits_per_pixel; - var->red.offset = 0; - var->green.length = var->bits_per_pixel; - var->green.offset = 0; - var->blue.length = var->bits_per_pixel; - var->blue.offset = 0; - break; - - case 16: - /* If we can't do 444, 5551 or 565, reject */ - if (!(caps & (CLCD_CAP_444 | CLCD_CAP_5551 | CLCD_CAP_565))) { - ret = -EINVAL; - break; - } - - /* - * Green length can be 4, 5 or 6 depending whether - * we're operating in 444, 5551 or 565 mode. - */ - if (var->green.length == 4 && caps & CLCD_CAP_444) - caps &= CLCD_CAP_444; - if (var->green.length == 5 && caps & CLCD_CAP_5551) - caps &= CLCD_CAP_5551; - else if (var->green.length == 6 && caps & CLCD_CAP_565) - caps &= CLCD_CAP_565; - else { - /* - * PL110 officially only supports RGB555, - * but may be wired up to allow RGB565. - */ - if (caps & CLCD_CAP_565) { - var->green.length = 6; - caps &= CLCD_CAP_565; - } else if (caps & CLCD_CAP_5551) { - var->green.length = 5; - caps &= CLCD_CAP_5551; - } else { - var->green.length = 4; - caps &= CLCD_CAP_444; - } - } - - if (var->green.length >= 5) { - var->red.length = 5; - var->blue.length = 5; - } else { - var->red.length = 4; - var->blue.length = 4; - } - break; - case 32: - /* If we can't do 888, reject */ - caps &= CLCD_CAP_888; - if (!caps) { - ret = -EINVAL; - break; - } - - var->red.length = 8; - var->green.length = 8; - var->blue.length = 8; - break; - default: - ret = -EINVAL; - break; - } - - /* - * >= 16bpp displays have separate colour component bitfields - * encoded in the pixel data. Calculate their position from - * the bitfield length defined above. - */ - if (ret == 0 && var->bits_per_pixel >= 16) { - bool bgr, rgb; - - bgr = caps & CLCD_CAP_BGR && var->blue.offset == 0; - rgb = caps & CLCD_CAP_RGB && var->red.offset == 0; - - if (!bgr && !rgb) - /* - * The requested format was not possible, try just - * our capabilities. One of BGR or RGB must be - * supported. - */ - bgr = caps & CLCD_CAP_BGR; - - if (bgr) { - var->blue.offset = 0; - var->green.offset = var->blue.offset + var->blue.length; - var->red.offset = var->green.offset + var->green.length; - } else { - var->red.offset = 0; - var->green.offset = var->red.offset + var->red.length; - var->blue.offset = var->green.offset + var->green.length; - } - } - - return ret; -} - -static int clcdfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) -{ - struct clcd_fb *fb = to_clcd(info); - int ret = -EINVAL; - - if (fb->board->check) - ret = fb->board->check(fb, var); - - if (ret == 0 && - var->xres_virtual * var->bits_per_pixel / 8 * - var->yres_virtual > fb->fb.fix.smem_len) - ret = -EINVAL; - - if (ret == 0) - ret = clcdfb_set_bitfields(fb, var); - - return ret; -} - -static int clcdfb_set_par(struct fb_info *info) -{ - struct clcd_fb *fb = to_clcd(info); - struct clcd_regs regs; - - fb->fb.fix.line_length = fb->fb.var.xres_virtual * - fb->fb.var.bits_per_pixel / 8; - - if (fb->fb.var.bits_per_pixel <= 8) - fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; - else - fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; - - fb->board->decode(fb, ®s); - - clcdfb_disable(fb); - - writel(regs.tim0, fb->regs + CLCD_TIM0); - writel(regs.tim1, fb->regs + CLCD_TIM1); - writel(regs.tim2, fb->regs + CLCD_TIM2); - writel(regs.tim3, fb->regs + CLCD_TIM3); - - clcdfb_set_start(fb); - - clk_set_rate(fb->clk, (1000000000 / regs.pixclock) * 1000); - - fb->clcd_cntl = regs.cntl; - - clcdfb_enable(fb, regs.cntl); - -#ifdef DEBUG - printk(KERN_INFO - "CLCD: Registers set to\n" - " %08x %08x %08x %08x\n" - " %08x %08x %08x %08x\n", - readl(fb->regs + CLCD_TIM0), readl(fb->regs + CLCD_TIM1), - readl(fb->regs + CLCD_TIM2), readl(fb->regs + CLCD_TIM3), - readl(fb->regs + CLCD_UBAS), readl(fb->regs + CLCD_LBAS), - readl(fb->regs + fb->off_ienb), readl(fb->regs + fb->off_cntl)); -#endif - - return 0; -} - -static inline u32 convert_bitfield(int val, struct fb_bitfield *bf) -{ - unsigned int mask = (1 << bf->length) - 1; - - return (val >> (16 - bf->length) & mask) << bf->offset; -} - -/* - * Set a single color register. The values supplied have a 16 bit - * magnitude. Return != 0 for invalid regno. - */ -static int -clcdfb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, - unsigned int blue, unsigned int transp, struct fb_info *info) -{ - struct clcd_fb *fb = to_clcd(info); - - if (regno < 16) - fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) | - convert_bitfield(blue, &fb->fb.var.blue) | - convert_bitfield(green, &fb->fb.var.green) | - convert_bitfield(red, &fb->fb.var.red); - - if (fb->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) { - int hw_reg = CLCD_PALETTE + ((regno * 2) & ~3); - u32 val, mask, newval; - - newval = (red >> 11) & 0x001f; - newval |= (green >> 6) & 0x03e0; - newval |= (blue >> 1) & 0x7c00; - - /* - * 3.2.11: if we're configured for big endian - * byte order, the palette entries are swapped. - */ - if (fb->clcd_cntl & CNTL_BEBO) - regno ^= 1; - - if (regno & 1) { - newval <<= 16; - mask = 0x0000ffff; - } else { - mask = 0xffff0000; - } - - val = readl(fb->regs + hw_reg) & mask; - writel(val | newval, fb->regs + hw_reg); - } - - return regno > 255; -} - -/* - * Blank the screen if blank_mode != 0, else unblank. If blank == NULL - * then the caller blanks by setting the CLUT (Color Look Up Table) to all - * black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due - * to e.g. a video mode which doesn't support it. Implements VESA suspend - * and powerdown modes on hardware that supports disabling hsync/vsync: - * blank_mode == 2: suspend vsync - * blank_mode == 3: suspend hsync - * blank_mode == 4: powerdown - */ -static int clcdfb_blank(int blank_mode, struct fb_info *info) -{ - struct clcd_fb *fb = to_clcd(info); - - if (blank_mode != 0) { - clcdfb_disable(fb); - } else { - clcdfb_enable(fb, fb->clcd_cntl); - } - return 0; -} - -static int clcdfb_mmap(struct fb_info *info, - struct vm_area_struct *vma) -{ - struct clcd_fb *fb = to_clcd(info); - unsigned long len, off = vma->vm_pgoff << PAGE_SHIFT; - int ret = -EINVAL; - - len = info->fix.smem_len; - - if (off <= len && vma->vm_end - vma->vm_start <= len - off && - fb->board->mmap) - ret = fb->board->mmap(fb, vma); - - return ret; -} - -static const struct fb_ops clcdfb_ops = { - .owner = THIS_MODULE, - .fb_check_var = clcdfb_check_var, - .fb_set_par = clcdfb_set_par, - .fb_setcolreg = clcdfb_setcolreg, - .fb_blank = clcdfb_blank, - .fb_fillrect = cfb_fillrect, - .fb_copyarea = cfb_copyarea, - .fb_imageblit = cfb_imageblit, - .fb_mmap = clcdfb_mmap, -}; - -static int clcdfb_register(struct clcd_fb *fb) -{ - int ret; - - /* - * ARM PL111 always has IENB at 0x1c; it's only PL110 - * which is reversed on some platforms. - */ - if (amba_manf(fb->dev) == 0x41 && amba_part(fb->dev) == 0x111) { - fb->off_ienb = CLCD_PL111_IENB; - fb->off_cntl = CLCD_PL111_CNTL; - } else { - fb->off_ienb = CLCD_PL110_IENB; - fb->off_cntl = CLCD_PL110_CNTL; - } - - fb->clk = clk_get(&fb->dev->dev, NULL); - if (IS_ERR(fb->clk)) { - ret = PTR_ERR(fb->clk); - goto out; - } - - ret = clk_prepare(fb->clk); - if (ret) - goto free_clk; - - fb->fb.device = &fb->dev->dev; - - fb->fb.fix.mmio_start = fb->dev->res.start; - fb->fb.fix.mmio_len = resource_size(&fb->dev->res); - - fb->regs = ioremap(fb->fb.fix.mmio_start, fb->fb.fix.mmio_len); - if (!fb->regs) { - printk(KERN_ERR "CLCD: unable to remap registers\n"); - ret = -ENOMEM; - goto clk_unprep; - } - - fb->fb.fbops = &clcdfb_ops; - fb->fb.flags = FBINFO_FLAG_DEFAULT; - fb->fb.pseudo_palette = fb->cmap; - - strncpy(fb->fb.fix.id, clcd_name, sizeof(fb->fb.fix.id)); - fb->fb.fix.type = FB_TYPE_PACKED_PIXELS; - fb->fb.fix.type_aux = 0; - fb->fb.fix.xpanstep = 0; - fb->fb.fix.ypanstep = 0; - fb->fb.fix.ywrapstep = 0; - fb->fb.fix.accel = FB_ACCEL_NONE; - - fb->fb.var.xres = fb->panel->mode.xres; - fb->fb.var.yres = fb->panel->mode.yres; - fb->fb.var.xres_virtual = fb->panel->mode.xres; - fb->fb.var.yres_virtual = fb->panel->mode.yres; - fb->fb.var.bits_per_pixel = fb->panel->bpp; - fb->fb.var.grayscale = fb->panel->grayscale; - fb->fb.var.pixclock = fb->panel->mode.pixclock; - fb->fb.var.left_margin = fb->panel->mode.left_margin; - fb->fb.var.right_margin = fb->panel->mode.right_margin; - fb->fb.var.upper_margin = fb->panel->mode.upper_margin; - fb->fb.var.lower_margin = fb->panel->mode.lower_margin; - fb->fb.var.hsync_len = fb->panel->mode.hsync_len; - fb->fb.var.vsync_len = fb->panel->mode.vsync_len; - fb->fb.var.sync = fb->panel->mode.sync; - fb->fb.var.vmode = fb->panel->mode.vmode; - fb->fb.var.activate = FB_ACTIVATE_NOW; - fb->fb.var.nonstd = 0; - fb->fb.var.height = fb->panel->height; - fb->fb.var.width = fb->panel->width; - fb->fb.var.accel_flags = 0; - - fb->fb.monspecs.hfmin = 0; - fb->fb.monspecs.hfmax = 100000; - fb->fb.monspecs.vfmin = 0; - fb->fb.monspecs.vfmax = 400; - fb->fb.monspecs.dclkmin = 1000000; - fb->fb.monspecs.dclkmax = 100000000; - - /* - * Make sure that the bitfields are set appropriately. - */ - clcdfb_set_bitfields(fb, &fb->fb.var); - - /* - * Allocate colourmap. - */ - ret = fb_alloc_cmap(&fb->fb.cmap, 256, 0); - if (ret) - goto unmap; - - /* - * Ensure interrupts are disabled. - */ - writel(0, fb->regs + fb->off_ienb); - - fb_set_var(&fb->fb, &fb->fb.var); - - dev_info(&fb->dev->dev, "%s hardware, %s display\n", - fb->board->name, fb->panel->mode.name); - - ret = register_framebuffer(&fb->fb); - if (ret == 0) - goto out; - - printk(KERN_ERR "CLCD: cannot register framebuffer (%d)\n", ret); - - fb_dealloc_cmap(&fb->fb.cmap); - unmap: - iounmap(fb->regs); - clk_unprep: - clk_unprepare(fb->clk); - free_clk: - clk_put(fb->clk); - out: - return ret; -} - -#ifdef CONFIG_OF -static int clcdfb_of_get_dpi_panel_mode(struct device_node *node, - struct clcd_panel *clcd_panel) -{ - int err; - struct display_timing timing; - struct videomode video; - - err = of_get_display_timing(node, "panel-timing", &timing); - if (err) { - pr_err("%pOF: problems parsing panel-timing (%d)\n", node, err); - return err; - } - - videomode_from_timing(&timing, &video); - - err = fb_videomode_from_videomode(&video, &clcd_panel->mode); - if (err) - return err; - - /* Set up some inversion flags */ - if (timing.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) - clcd_panel->tim2 |= TIM2_IPC; - else if (!(timing.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)) - /* - * To preserve backwards compatibility, the IPC (inverted - * pixel clock) flag needs to be set on any display that - * doesn't explicitly specify that the pixel clock is - * active on the negative or positive edge. - */ - clcd_panel->tim2 |= TIM2_IPC; - - if (timing.flags & DISPLAY_FLAGS_HSYNC_LOW) - clcd_panel->tim2 |= TIM2_IHS; - - if (timing.flags & DISPLAY_FLAGS_VSYNC_LOW) - clcd_panel->tim2 |= TIM2_IVS; - - if (timing.flags & DISPLAY_FLAGS_DE_LOW) - clcd_panel->tim2 |= TIM2_IOE; - - return 0; -} - -static int clcdfb_snprintf_mode(char *buf, int size, struct fb_videomode *mode) -{ - return snprintf(buf, size, "%ux%u@%u", mode->xres, mode->yres, - mode->refresh); -} - -static int clcdfb_of_get_backlight(struct device *dev, - struct clcd_panel *clcd_panel) -{ - struct backlight_device *backlight; - - /* Look up the optional backlight device */ - backlight = devm_of_find_backlight(dev); - if (IS_ERR(backlight)) - return PTR_ERR(backlight); - - clcd_panel->backlight = backlight; - return 0; -} - -static int clcdfb_of_get_mode(struct device *dev, struct device_node *panel, - struct clcd_panel *clcd_panel) -{ - int err; - struct fb_videomode *mode; - char *name; - int len; - - /* Only directly connected DPI panels supported for now */ - if (of_device_is_compatible(panel, "panel-dpi")) - err = clcdfb_of_get_dpi_panel_mode(panel, clcd_panel); - else - err = -ENOENT; - if (err) - return err; - mode = &clcd_panel->mode; - - len = clcdfb_snprintf_mode(NULL, 0, mode); - name = devm_kzalloc(dev, len + 1, GFP_KERNEL); - if (!name) - return -ENOMEM; - - clcdfb_snprintf_mode(name, len + 1, mode); - mode->name = name; - - return 0; -} - -static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0) -{ - static struct { - unsigned int part; - u32 r0, g0, b0; - u32 caps; - } panels[] = { - { 0x110, 1, 7, 13, CLCD_CAP_5551 }, - { 0x110, 0, 8, 16, CLCD_CAP_888 }, - { 0x110, 16, 8, 0, CLCD_CAP_888 }, - { 0x111, 4, 14, 20, CLCD_CAP_444 }, - { 0x111, 3, 11, 19, CLCD_CAP_444 | CLCD_CAP_5551 }, - { 0x111, 3, 10, 19, CLCD_CAP_444 | CLCD_CAP_5551 | - CLCD_CAP_565 }, - { 0x111, 0, 8, 16, CLCD_CAP_444 | CLCD_CAP_5551 | - CLCD_CAP_565 | CLCD_CAP_888 }, - }; - int i; - - /* Bypass pixel clock divider */ - fb->panel->tim2 |= TIM2_BCD; - - /* TFT display, vert. comp. interrupt at the start of the back porch */ - fb->panel->cntl |= CNTL_LCDTFT | CNTL_LCDVCOMP(1); - - fb->panel->caps = 0; - - /* Match the setup with known variants */ - for (i = 0; i < ARRAY_SIZE(panels) && !fb->panel->caps; i++) { - if (amba_part(fb->dev) != panels[i].part) - continue; - if (g0 != panels[i].g0) - continue; - if (r0 == panels[i].r0 && b0 == panels[i].b0) - fb->panel->caps = panels[i].caps; - } - - /* - * If we actually physically connected the R lines to B and - * vice versa - */ - if (r0 != 0 && b0 == 0) - fb->panel->bgr_connection = true; - - return fb->panel->caps ? 0 : -EINVAL; -} - -static int clcdfb_of_init_display(struct clcd_fb *fb) -{ - struct device_node *endpoint, *panel; - int err; - unsigned int bpp; - u32 max_bandwidth; - u32 tft_r0b0g0[3]; - - fb->panel = devm_kzalloc(&fb->dev->dev, sizeof(*fb->panel), GFP_KERNEL); - if (!fb->panel) - return -ENOMEM; - - /* - * Fetch the panel endpoint. - */ - endpoint = of_graph_get_next_endpoint(fb->dev->dev.of_node, NULL); - if (!endpoint) - return -ENODEV; - - panel = of_graph_get_remote_port_parent(endpoint); - if (!panel) - return -ENODEV; - - err = clcdfb_of_get_backlight(&fb->dev->dev, fb->panel); - if (err) - return err; - - err = clcdfb_of_get_mode(&fb->dev->dev, panel, fb->panel); - if (err) - return err; - - err = of_property_read_u32(fb->dev->dev.of_node, "max-memory-bandwidth", - &max_bandwidth); - if (!err) { - /* - * max_bandwidth is in bytes per second and pixclock in - * pico-seconds, so the maximum allowed bits per pixel is - * 8 * max_bandwidth / (PICOS2KHZ(pixclock) * 1000) - * Rearrange this calculation to avoid overflow and then ensure - * result is a valid format. - */ - bpp = max_bandwidth / (1000 / 8) - / PICOS2KHZ(fb->panel->mode.pixclock); - bpp = rounddown_pow_of_two(bpp); - if (bpp > 32) - bpp = 32; - } else - bpp = 32; - fb->panel->bpp = bpp; - -#ifdef CONFIG_CPU_BIG_ENDIAN - fb->panel->cntl |= CNTL_BEBO; -#endif - fb->panel->width = -1; - fb->panel->height = -1; - - if (of_property_read_u32_array(endpoint, - "arm,pl11x,tft-r0g0b0-pads", - tft_r0b0g0, ARRAY_SIZE(tft_r0b0g0)) != 0) - return -ENOENT; - - return clcdfb_of_init_tft_panel(fb, tft_r0b0g0[0], - tft_r0b0g0[1], tft_r0b0g0[2]); -} - -static int clcdfb_of_vram_setup(struct clcd_fb *fb) -{ - int err; - struct device_node *memory; - u64 size; - - err = clcdfb_of_init_display(fb); - if (err) - return err; - - memory = of_parse_phandle(fb->dev->dev.of_node, "memory-region", 0); - if (!memory) - return -ENODEV; - - fb->fb.screen_base = of_iomap(memory, 0); - if (!fb->fb.screen_base) - return -ENOMEM; - - fb->fb.fix.smem_start = of_translate_address(memory, - of_get_address(memory, 0, &size, NULL)); - fb->fb.fix.smem_len = size; - - return 0; -} - -static int clcdfb_of_vram_mmap(struct clcd_fb *fb, struct vm_area_struct *vma) -{ - unsigned long off, user_size, kernel_size; - - - off = vma->vm_pgoff << PAGE_SHIFT; - user_size = vma->vm_end - vma->vm_start; - kernel_size = fb->fb.fix.smem_len; - - if (off >= kernel_size || user_size > (kernel_size - off)) - return -ENXIO; - - return remap_pfn_range(vma, vma->vm_start, - __phys_to_pfn(fb->fb.fix.smem_start) + vma->vm_pgoff, - user_size, - pgprot_writecombine(vma->vm_page_prot)); -} - -static void clcdfb_of_vram_remove(struct clcd_fb *fb) -{ - iounmap(fb->fb.screen_base); -} - -static int clcdfb_of_dma_setup(struct clcd_fb *fb) -{ - unsigned long framesize; - dma_addr_t dma; - int err; - - err = clcdfb_of_init_display(fb); - if (err) - return err; - - framesize = PAGE_ALIGN(fb->panel->mode.xres * fb->panel->mode.yres * - fb->panel->bpp / 8); - fb->fb.screen_base = dma_alloc_coherent(&fb->dev->dev, framesize, - &dma, GFP_KERNEL); - if (!fb->fb.screen_base) - return -ENOMEM; - - fb->fb.fix.smem_start = dma; - fb->fb.fix.smem_len = framesize; - - return 0; -} - -static int clcdfb_of_dma_mmap(struct clcd_fb *fb, struct vm_area_struct *vma) -{ - return dma_mmap_wc(&fb->dev->dev, vma, fb->fb.screen_base, - fb->fb.fix.smem_start, fb->fb.fix.smem_len); -} - -static void clcdfb_of_dma_remove(struct clcd_fb *fb) -{ - dma_free_coherent(&fb->dev->dev, fb->fb.fix.smem_len, - fb->fb.screen_base, fb->fb.fix.smem_start); -} - -static struct clcd_board *clcdfb_of_get_board(struct amba_device *dev) -{ - struct clcd_board *board = devm_kzalloc(&dev->dev, sizeof(*board), - GFP_KERNEL); - struct device_node *node = dev->dev.of_node; - - if (!board) - return NULL; - - board->name = of_node_full_name(node); - board->caps = CLCD_CAP_ALL; - board->check = clcdfb_check; - board->decode = clcdfb_decode; - if (of_find_property(node, "memory-region", NULL)) { - board->setup = clcdfb_of_vram_setup; - board->mmap = clcdfb_of_vram_mmap; - board->remove = clcdfb_of_vram_remove; - } else { - board->setup = clcdfb_of_dma_setup; - board->mmap = clcdfb_of_dma_mmap; - board->remove = clcdfb_of_dma_remove; - } - - return board; -} -#else -static struct clcd_board *clcdfb_of_get_board(struct amba_device *dev) -{ - return NULL; -} -#endif - -static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id) -{ - struct clcd_board *board = dev_get_platdata(&dev->dev); - struct clcd_fb *fb; - int ret; - - if (!board) - board = clcdfb_of_get_board(dev); - - if (!board) - return -EINVAL; - - ret = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); - if (ret) - goto out; - - ret = amba_request_regions(dev, NULL); - if (ret) { - printk(KERN_ERR "CLCD: unable to reserve regs region\n"); - goto out; - } - - fb = kzalloc(sizeof(*fb), GFP_KERNEL); - if (!fb) { - ret = -ENOMEM; - goto free_region; - } - - fb->dev = dev; - fb->board = board; - - dev_info(&fb->dev->dev, "PL%03x designer %02x rev%u at 0x%08llx\n", - amba_part(dev), amba_manf(dev), amba_rev(dev), - (unsigned long long)dev->res.start); - - ret = fb->board->setup(fb); - if (ret) - goto free_fb; - - ret = clcdfb_register(fb); - if (ret == 0) { - amba_set_drvdata(dev, fb); - goto out; - } - - fb->board->remove(fb); - free_fb: - kfree(fb); - free_region: - amba_release_regions(dev); - out: - return ret; -} - -static int clcdfb_remove(struct amba_device *dev) -{ - struct clcd_fb *fb = amba_get_drvdata(dev); - - clcdfb_disable(fb); - unregister_framebuffer(&fb->fb); - if (fb->fb.cmap.len) - fb_dealloc_cmap(&fb->fb.cmap); - iounmap(fb->regs); - clk_unprepare(fb->clk); - clk_put(fb->clk); - - fb->board->remove(fb); - - kfree(fb); - - amba_release_regions(dev); - - return 0; -} - -static const struct amba_id clcdfb_id_table[] = { - { - .id = 0x00041110, - .mask = 0x000ffffe, - }, - { 0, 0 }, -}; - -MODULE_DEVICE_TABLE(amba, clcdfb_id_table); - -static struct amba_driver clcd_driver = { - .drv = { - .name = "clcd-pl11x", - }, - .probe = clcdfb_probe, - .remove = clcdfb_remove, - .id_table = clcdfb_id_table, -}; - -static int __init amba_clcdfb_init(void) -{ - if (fb_get_options("ambafb", NULL)) - return -ENODEV; - - return amba_driver_register(&clcd_driver); -} - -module_init(amba_clcdfb_init); - -static void __exit amba_clcdfb_exit(void) -{ - amba_driver_unregister(&clcd_driver); -} - -module_exit(amba_clcdfb_exit); - -MODULE_DESCRIPTION("ARM PrimeCell PL110 CLCD core driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbdev/amifb.c b/drivers/video/fbdev/amifb.c index 6062104f3afb..226682550b4b 100644 --- a/drivers/video/fbdev/amifb.c +++ b/drivers/video/fbdev/amifb.c @@ -575,6 +575,12 @@ static u_short maxfmode, chipset; #define downx(x, v) ((v) & -(x)) #define modx(x, v) ((v) & ((x) - 1)) +/* + * FIXME: Use C variants of the code marked with #ifdef __mc68000__ + * in the driver. It shouldn't negatively affect the performance and + * is required for APUS support (once it is re-added to the kernel). + * Needs to be tested on the hardware though.. + */ /* if x1 is not a constant, this macro won't make real sense :-) */ #ifdef __mc68000__ #define DIVUL(x1, x2) ({int res; asm("divul %1,%2,%3": "=d" (res): \ @@ -1884,6 +1890,7 @@ static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var, | ((datawords >> 15) & 1)); datawords <<= 1; #endif + /* FIXME: check the return value + test the change */ put_user(color, data++); } if (bits > 0) { @@ -1952,6 +1959,7 @@ static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var, bits = 16; words = delta; datawords = 0; for (width = (short)var->width - 1; width >= 0; width--) { unsigned long tdata = 0; + /* FIXME: check the return value + test the change */ get_user(tdata, data); data++; #ifdef __mc68000__ diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 9d28a8e3328f..6af2734f2a7b 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -639,7 +639,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, GFP_KERNEL); if (save) { int i = cols < new_cols ? cols : new_cols; - scr_memsetw(save, erase, logo_lines * new_cols * 2); + scr_memsetw(save, erase, array3_size(logo_lines, new_cols, 2)); r = q - step; for (cnt = 0; cnt < logo_lines; cnt++, r += i) scr_memcpyw(save + cnt * new_cols, r, 2 * i); @@ -676,7 +676,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, q = (unsigned short *) (vc->vc_origin + vc->vc_size_row * rows); - scr_memcpyw(q, save, logo_lines * new_cols * 2); + scr_memcpyw(q, save, array3_size(logo_lines, new_cols, 2)); vc->vc_y += logo_lines; vc->vc_pos += logo_lines * vc->vc_size_row; kfree(save); diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c index d62a1e43864e..1bf82dbc9e3c 100644 --- a/drivers/video/fbdev/core/fbmon.c +++ b/drivers/video/fbdev/core/fbmon.c @@ -19,7 +19,7 @@ * Generalized Timing Formula is derived from: * * GTF Spreadsheet by Andy Morrish (1/5/97) - * available at http://www.vesa.org + * available at https://www.vesa.org * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive @@ -1201,7 +1201,7 @@ static void fb_timings_dclk(struct __fb_timings *timings) * ignored and @var will be filled with the calculated timings. * * All calculations are based on the VESA GTF Spreadsheet - * available at VESA's public ftp (http://www.vesa.org). + * available at VESA's public ftp (https://www.vesa.org). * * NOTES: * The timings generated by the GTF will be different from VESA diff --git a/drivers/video/fbdev/da8xx-fb.c b/drivers/video/fbdev/da8xx-fb.c index 73c3c4c8cc12..e38c0e3f9c61 100644 --- a/drivers/video/fbdev/da8xx-fb.c +++ b/drivers/video/fbdev/da8xx-fb.c @@ -1402,14 +1402,14 @@ static int fb_probe(struct platform_device *device) if (IS_ERR(par->lcd_supply)) { if (PTR_ERR(par->lcd_supply) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; - goto err_pm_runtime_disable; + goto err_release_fb; } par->lcd_supply = NULL; } else { ret = regulator_enable(par->lcd_supply); if (ret) - goto err_pm_runtime_disable; + goto err_release_fb; } fb_videomode_to_var(&da8xx_fb_var, lcdc_info); diff --git a/drivers/video/fbdev/ep93xx-fb.c b/drivers/video/fbdev/ep93xx-fb.c index cda2ef337423..ba33b4dce0df 100644 --- a/drivers/video/fbdev/ep93xx-fb.c +++ b/drivers/video/fbdev/ep93xx-fb.c @@ -430,7 +430,7 @@ static int ep93xxfb_alloc_videomem(struct fb_info *info) /* * There is a bug in the ep93xx framebuffer which causes problems * if bit 27 of the physical address is set. - * See: http://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2 + * See: https://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2 * There does not seem to be any official errata for this, but I * have confirmed the problem exists on my hardware (ep9315) at * least. diff --git a/drivers/video/fbdev/grvga.c b/drivers/video/fbdev/grvga.c index 07dda03e0957..24818b276241 100644 --- a/drivers/video/fbdev/grvga.c +++ b/drivers/video/fbdev/grvga.c @@ -5,7 +5,7 @@ * 2011 (c) Aeroflex Gaisler AB * * Full documentation of the core can be found here: - * http://www.gaisler.com/products/grlib/grip.pdf + * https://www.gaisler.com/products/grlib/grip.pdf * * Contributors: Kristoffer Glembo <kristoffer@gaisler.com> */ diff --git a/drivers/video/fbdev/macfb.c b/drivers/video/fbdev/macfb.c index e05a97662ca8..312e35c9aa6c 100644 --- a/drivers/video/fbdev/macfb.c +++ b/drivers/video/fbdev/macfb.c @@ -478,7 +478,7 @@ static int macfb_setcolreg(unsigned regno, unsigned red, unsigned green, break; /* * 24-bit colour almost doesn't exist on 68k Macs -- - * http://support.apple.com/kb/TA28634 (Old Article: 10992) + * https://support.apple.com/kb/TA28634 (Old Article: 10992) */ case 24: case 32: diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c index a42e2eceee48..952826557a0c 100644 --- a/drivers/video/fbdev/metronomefb.c +++ b/drivers/video/fbdev/metronomefb.c @@ -10,7 +10,7 @@ * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. * * This work was made possible by help and equipment support from E-Ink - * Corporation. http://www.eink.com/ + * Corporation. https://www.eink.com/ * * This driver is written to be used with the Metronome display controller. * It is intended to be architecture independent. A board specific driver diff --git a/drivers/video/fbdev/neofb.c b/drivers/video/fbdev/neofb.c index f5a676bfd67a..09a20d4ab35f 100644 --- a/drivers/video/fbdev/neofb.c +++ b/drivers/video/fbdev/neofb.c @@ -1819,6 +1819,7 @@ static int neo_scan_monitor(struct fb_info *info) #else printk(KERN_ERR "neofb: Only 640x480, 800x600/480 and 1024x768 panels are currently supported\n"); + kfree(info->monspecs.modedb); return -1; #endif default: diff --git a/drivers/video/fbdev/omap2/omapfb/dss/Kconfig b/drivers/video/fbdev/omap2/omapfb/dss/Kconfig index 36b97fee2d57..cc81a19537d2 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/Kconfig +++ b/drivers/video/fbdev/omap2/omapfb/dss/Kconfig @@ -60,7 +60,7 @@ config FB_OMAP5_DSS_HDMI select FB_OMAP2_DSS_HDMI_COMMON help HDMI Interface for OMAP5 and similar cores. This adds the High - Definition Multimedia Interface. See http://www.hdmi.org/ for HDMI + Definition Multimedia Interface. See https://www.hdmi.org/ for HDMI specification. config FB_OMAP2_DSS_SDI @@ -79,7 +79,7 @@ config FB_OMAP2_DSS_DSI DSI is a high speed half-duplex serial interface between the host processor and a peripheral, such as a display or a framebuffer chip. - See http://www.mipi.org/ for DSI specifications. + See https://www.mipi.org/ for DSI specifications. config FB_OMAP2_DSS_MIN_FCK_PER_PCK int "Minimum FCK/PCK ratio (for scaling)" diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dispc.c b/drivers/video/fbdev/omap2/omapfb/dss/dispc.c index 3bb951eb29c7..3920a0db0390 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dispc.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dispc.c @@ -520,8 +520,11 @@ int dispc_runtime_get(void) DSSDBG("dispc_runtime_get\n"); r = pm_runtime_get_sync(&dispc.pdev->dev); - WARN_ON(r < 0); - return r < 0 ? r : 0; + if (WARN_ON(r < 0)) { + pm_runtime_put_sync(&dispc.pdev->dev); + return r; + } + return 0; } EXPORT_SYMBOL(dispc_runtime_get); diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dsi.c b/drivers/video/fbdev/omap2/omapfb/dss/dsi.c index d620376216e1..6f9c25fec994 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dsi.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dsi.c @@ -1137,8 +1137,11 @@ static int dsi_runtime_get(struct platform_device *dsidev) DSSDBG("dsi_runtime_get\n"); r = pm_runtime_get_sync(&dsi->pdev->dev); - WARN_ON(r < 0); - return r < 0 ? r : 0; + if (WARN_ON(r < 0)) { + pm_runtime_put_sync(&dsi->pdev->dev); + return r; + } + return 0; } static void dsi_runtime_put(struct platform_device *dsidev) diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dss.c b/drivers/video/fbdev/omap2/omapfb/dss/dss.c index 7252d22dd117..a6b1c1598040 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dss.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dss.c @@ -768,8 +768,11 @@ int dss_runtime_get(void) DSSDBG("dss_runtime_get\n"); r = pm_runtime_get_sync(&dss.pdev->dev); - WARN_ON(r < 0); - return r < 0 ? r : 0; + if (WARN_ON(r < 0)) { + pm_runtime_put_sync(&dss.pdev->dev); + return r; + } + return 0; } void dss_runtime_put(void) @@ -833,7 +836,7 @@ static const struct dss_features omap34xx_dss_feats = { }; static const struct dss_features omap3630_dss_feats = { - .fck_div_max = 32, + .fck_div_max = 31, .dss_fck_multiplier = 1, .parent_clk_name = "dpll4_ck", .dpi_select_source = &dss_dpi_select_source_omap2_omap3, diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi.h b/drivers/video/fbdev/omap2/omapfb/dss/hdmi.h index b9d4480ecfad..9a7253355f6d 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi.h +++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi.h @@ -2,7 +2,7 @@ /* * HDMI driver definition for TI OMAP4 Processor. * - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/ */ #ifndef _HDMI_H diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c index ef659c89ba58..22f1d37a968a 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * HDMI interface DSS driver for TI's OMAP4 family of SoCs. - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/ * Authors: Yong Zhi * Mythri pk <mythripk@ti.com> */ @@ -39,9 +39,10 @@ static int hdmi_runtime_get(void) DSSDBG("hdmi_runtime_get\n"); r = pm_runtime_get_sync(&hdmi.pdev->dev); - WARN_ON(r < 0); - if (r < 0) + if (WARN_ON(r < 0)) { + pm_runtime_put_sync(&hdmi.pdev->dev); return r; + } return 0; } diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c index 6b79b52d5fad..7ca1803bf161 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c @@ -3,7 +3,7 @@ * ti_hdmi_4xxx_ip.c * * HDMI TI81xx, TI38xx, TI OMAP4 etc IP driver Library - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/ * Authors: Yong Zhi * Mythri pk <mythripk@ti.com> */ diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.h b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.h index f066d1f69132..b5c35277f06e 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.h +++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.h @@ -2,7 +2,7 @@ /* * HDMI header definition for OMAP4 HDMI core IP * - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/ */ #ifndef _HDMI4_CORE_H_ diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi5.c b/drivers/video/fbdev/omap2/omapfb/dss/hdmi5.c index ac49531e4732..a06b6f1355bd 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi5.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi5.c @@ -43,9 +43,10 @@ static int hdmi_runtime_get(void) DSSDBG("hdmi_runtime_get\n"); r = pm_runtime_get_sync(&hdmi.pdev->dev); - WARN_ON(r < 0); - if (r < 0) + if (WARN_ON(r < 0)) { + pm_runtime_put_sync(&hdmi.pdev->dev); return r; + } return 0; } diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi5_core.h b/drivers/video/fbdev/omap2/omapfb/dss/hdmi5_core.h index f10b8a283011..192c9b6e2f7b 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi5_core.h +++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi5_core.h @@ -2,7 +2,7 @@ /* * HDMI driver definition for TI OMAP5 processors. * - * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ */ #ifndef _HDMI5_CORE_H_ diff --git a/drivers/video/fbdev/omap2/omapfb/dss/venc.c b/drivers/video/fbdev/omap2/omapfb/dss/venc.c index d5404d56c922..0b0ad20afd63 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/venc.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/venc.c @@ -348,8 +348,11 @@ static int venc_runtime_get(void) DSSDBG("venc_runtime_get\n"); r = pm_runtime_get_sync(&venc.pdev->dev); - WARN_ON(r < 0); - return r < 0 ? r : 0; + if (WARN_ON(r < 0)) { + pm_runtime_put_sync(&venc.pdev->dev); + return r; + } + return 0; } static void venc_runtime_put(void) diff --git a/drivers/video/fbdev/sa1100fb.c b/drivers/video/fbdev/sa1100fb.c index 3e6e13f7a831..bda6cc313c8b 100644 --- a/drivers/video/fbdev/sa1100fb.c +++ b/drivers/video/fbdev/sa1100fb.c @@ -18,7 +18,7 @@ * Clean patches should be sent to the ARM Linux Patch System. Please see the * following web page for more information: * - * http://www.arm.linux.org.uk/developer/patches/info.shtml + * https://www.arm.linux.org.uk/developer/patches/info.shtml * * Thank you. * diff --git a/drivers/video/fbdev/savage/savagefb_driver.c b/drivers/video/fbdev/savage/savagefb_driver.c index 3c8ae87f0ea7..3fd87aeb6c79 100644 --- a/drivers/video/fbdev/savage/savagefb_driver.c +++ b/drivers/video/fbdev/savage/savagefb_driver.c @@ -2157,6 +2157,8 @@ static int savage_init_fb_info(struct fb_info *info, struct pci_dev *dev, info->flags |= FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_IMAGEBLIT; + else + kfree(info->pixmap.addr); } #endif return err; diff --git a/drivers/video/fbdev/sm712fb.c b/drivers/video/fbdev/sm712fb.c index 8cd655d6d628..bdbe9c68e274 100644 --- a/drivers/video/fbdev/sm712fb.c +++ b/drivers/video/fbdev/sm712fb.c @@ -1616,7 +1616,7 @@ static int smtcfb_pci_probe(struct pci_dev *pdev, default: dev_err(&pdev->dev, "No valid Silicon Motion display chip was detected!\n"); - + err = -ENODEV; goto failed_fb; } diff --git a/drivers/video/fbdev/vt8623fb.c b/drivers/video/fbdev/vt8623fb.c index 7b3eef1b893f..98ff8235c9e9 100644 --- a/drivers/video/fbdev/vt8623fb.c +++ b/drivers/video/fbdev/vt8623fb.c @@ -62,24 +62,24 @@ static const struct svga_pll vt8623_pll = {2, 127, 2, 7, 0, 3, /* CRT timing register sets */ -static struct vga_regset vt8623_h_total_regs[] = {{0x00, 0, 7}, {0x36, 3, 3}, VGA_REGSET_END}; -static struct vga_regset vt8623_h_display_regs[] = {{0x01, 0, 7}, VGA_REGSET_END}; -static struct vga_regset vt8623_h_blank_start_regs[] = {{0x02, 0, 7}, VGA_REGSET_END}; -static struct vga_regset vt8623_h_blank_end_regs[] = {{0x03, 0, 4}, {0x05, 7, 7}, {0x33, 5, 5}, VGA_REGSET_END}; -static struct vga_regset vt8623_h_sync_start_regs[] = {{0x04, 0, 7}, {0x33, 4, 4}, VGA_REGSET_END}; -static struct vga_regset vt8623_h_sync_end_regs[] = {{0x05, 0, 4}, VGA_REGSET_END}; - -static struct vga_regset vt8623_v_total_regs[] = {{0x06, 0, 7}, {0x07, 0, 0}, {0x07, 5, 5}, {0x35, 0, 0}, VGA_REGSET_END}; -static struct vga_regset vt8623_v_display_regs[] = {{0x12, 0, 7}, {0x07, 1, 1}, {0x07, 6, 6}, {0x35, 2, 2}, VGA_REGSET_END}; -static struct vga_regset vt8623_v_blank_start_regs[] = {{0x15, 0, 7}, {0x07, 3, 3}, {0x09, 5, 5}, {0x35, 3, 3}, VGA_REGSET_END}; -static struct vga_regset vt8623_v_blank_end_regs[] = {{0x16, 0, 7}, VGA_REGSET_END}; -static struct vga_regset vt8623_v_sync_start_regs[] = {{0x10, 0, 7}, {0x07, 2, 2}, {0x07, 7, 7}, {0x35, 1, 1}, VGA_REGSET_END}; -static struct vga_regset vt8623_v_sync_end_regs[] = {{0x11, 0, 3}, VGA_REGSET_END}; - -static struct vga_regset vt8623_offset_regs[] = {{0x13, 0, 7}, {0x35, 5, 7}, VGA_REGSET_END}; -static struct vga_regset vt8623_line_compare_regs[] = {{0x18, 0, 7}, {0x07, 4, 4}, {0x09, 6, 6}, {0x33, 0, 2}, {0x35, 4, 4}, VGA_REGSET_END}; -static struct vga_regset vt8623_fetch_count_regs[] = {{0x1C, 0, 7}, {0x1D, 0, 1}, VGA_REGSET_END}; -static struct vga_regset vt8623_start_address_regs[] = {{0x0d, 0, 7}, {0x0c, 0, 7}, {0x34, 0, 7}, {0x48, 0, 1}, VGA_REGSET_END}; +static const struct vga_regset vt8623_h_total_regs[] = {{0x00, 0, 7}, {0x36, 3, 3}, VGA_REGSET_END}; +static const struct vga_regset vt8623_h_display_regs[] = {{0x01, 0, 7}, VGA_REGSET_END}; +static const struct vga_regset vt8623_h_blank_start_regs[] = {{0x02, 0, 7}, VGA_REGSET_END}; +static const struct vga_regset vt8623_h_blank_end_regs[] = {{0x03, 0, 4}, {0x05, 7, 7}, {0x33, 5, 5}, VGA_REGSET_END}; +static const struct vga_regset vt8623_h_sync_start_regs[] = {{0x04, 0, 7}, {0x33, 4, 4}, VGA_REGSET_END}; +static const struct vga_regset vt8623_h_sync_end_regs[] = {{0x05, 0, 4}, VGA_REGSET_END}; + +static const struct vga_regset vt8623_v_total_regs[] = {{0x06, 0, 7}, {0x07, 0, 0}, {0x07, 5, 5}, {0x35, 0, 0}, VGA_REGSET_END}; +static const struct vga_regset vt8623_v_display_regs[] = {{0x12, 0, 7}, {0x07, 1, 1}, {0x07, 6, 6}, {0x35, 2, 2}, VGA_REGSET_END}; +static const struct vga_regset vt8623_v_blank_start_regs[] = {{0x15, 0, 7}, {0x07, 3, 3}, {0x09, 5, 5}, {0x35, 3, 3}, VGA_REGSET_END}; +static const struct vga_regset vt8623_v_blank_end_regs[] = {{0x16, 0, 7}, VGA_REGSET_END}; +static const struct vga_regset vt8623_v_sync_start_regs[] = {{0x10, 0, 7}, {0x07, 2, 2}, {0x07, 7, 7}, {0x35, 1, 1}, VGA_REGSET_END}; +static const struct vga_regset vt8623_v_sync_end_regs[] = {{0x11, 0, 3}, VGA_REGSET_END}; + +static const struct vga_regset vt8623_offset_regs[] = {{0x13, 0, 7}, {0x35, 5, 7}, VGA_REGSET_END}; +static const struct vga_regset vt8623_line_compare_regs[] = {{0x18, 0, 7}, {0x07, 4, 4}, {0x09, 6, 6}, {0x33, 0, 2}, {0x35, 4, 4}, VGA_REGSET_END}; +static const struct vga_regset vt8623_fetch_count_regs[] = {{0x1C, 0, 7}, {0x1D, 0, 1}, VGA_REGSET_END}; +static const struct vga_regset vt8623_start_address_regs[] = {{0x0d, 0, 7}, {0x0c, 0, 7}, {0x34, 0, 7}, {0x48, 0, 1}, VGA_REGSET_END}; static const struct svga_timing_regs vt8623_timing_regs = { vt8623_h_total_regs, vt8623_h_display_regs, vt8623_h_blank_start_regs, |