From 09120d4e88b13967d44d46280fb74d3ac4ac2f73 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Wed, 29 Jul 2015 17:23:45 +0100 Subject: [PATCH 001/145] drm/i915: Remove unnecessary gen8_clamp_pd gen8_clamp_pd clamps to the next page directory boundary, but the macro gen8_for_each_pde already has a check to stop at the page directory boundary. Furthermore, i915_pte_count also restricts to the next page table boundary. v2: Rebase after Mika's ppgtt cleanup / scratch merge patch series. Suggested-by: Akash Goel Signed-off-by: Michel Thierry Reviewed-by: "Akash Goel" Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 2 +- drivers/gpu/drm/i915/i915_gem_gtt.h | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 96054a560f4f..566466fe1917 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -955,7 +955,7 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, gen8_for_each_pdpe(pd, &ppgtt->pdp, start, length, temp, pdpe) { gen8_pde_t *const page_directory = kmap_px(pd); struct i915_page_table *pt; - uint64_t pd_len = gen8_clamp_pd(start, length); + uint64_t pd_len = length; uint64_t pd_start = start; uint32_t pde; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index e1cfa292f9ad..d5bf953deff9 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -444,17 +444,6 @@ static inline uint32_t gen6_pde_index(uint32_t addr) temp = min(temp, length), \ start += temp, length -= temp) -/* Clamp length to the next page_directory boundary */ -static inline uint64_t gen8_clamp_pd(uint64_t start, uint64_t length) -{ - uint64_t next_pd = ALIGN(start + 1, 1 << GEN8_PDPE_SHIFT); - - if (next_pd > (start + length)) - return length; - - return next_pd - start; -} - static inline uint32_t gen8_pte_index(uint64_t address) { return i915_pte_index(address, GEN8_PDE_SHIFT); From 6ac1850220732f47bc6ae767fa41542009674ad7 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Wed, 29 Jul 2015 17:23:46 +0100 Subject: [PATCH 002/145] drm/i915/gen8: Make pdp allocation more dynamic This transitional patch doesn't do much for the existing code. However, it should make upcoming patches to use the full 48b address space a bit easier. 32-bit ppgtt uses just 4 PDPs, while 48-bit ppgtt will have up-to 512; this patch prepares the existing functions to query the right number of pdps at run-time. This also means that used_pdpes should also be allocated during ppgtt_init, as the bitmap size will depend on the ppgtt address range selected. v2: Renamed pdp_free to be similar to pd/pt (unmap_and_free_pdp). v3: To facilitate testing, 48b mode will be available on Broadwell and GEN9+, when i915.enable_ppgtt = 3. v4: Rebase after s/page_tables/page_table/, added extra information about 4-level page table formats and use IS_ENABLED macro. v5: Check CONFIG_X86_64 instead of CONFIG_64BIT. v6: Rebase after Mika's ppgtt cleanup / scratch merge patch series, and follow his nomenclature in pdp functions (there is no alloc_pdp yet). v7: Rebase after merged version of Mika's ppgtt cleanup patch series. v8: Rebase after final merged version of Mika's ppgtt/scratch patches. v9: Introduce PML4 (and 48-bit checks) until next patch (Akash). v10: Also use test_bit to detect when pd/pt are already allocated (Akash) Cc: Akash Goel Signed-off-by: Ben Widawsky Signed-off-by: Michel Thierry (v2+) Reviewed-by: Akash Goel [danvet: Amend commit message as suggested by Michel.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 86 +++++++++++++++++++++++------ drivers/gpu/drm/i915/i915_gem_gtt.h | 17 ++++-- 2 files changed, 80 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 566466fe1917..6724e71cf1d7 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -522,6 +522,43 @@ static void gen8_initialize_pd(struct i915_address_space *vm, fill_px(vm->dev, pd, scratch_pde); } +static int __pdp_init(struct drm_device *dev, + struct i915_page_directory_pointer *pdp) +{ + size_t pdpes = I915_PDPES_PER_PDP(dev); + + pdp->used_pdpes = kcalloc(BITS_TO_LONGS(pdpes), + sizeof(unsigned long), + GFP_KERNEL); + if (!pdp->used_pdpes) + return -ENOMEM; + + pdp->page_directory = kcalloc(pdpes, sizeof(*pdp->page_directory), + GFP_KERNEL); + if (!pdp->page_directory) { + kfree(pdp->used_pdpes); + /* the PDP might be the statically allocated top level. Keep it + * as clean as possible */ + pdp->used_pdpes = NULL; + return -ENOMEM; + } + + return 0; +} + +static void __pdp_fini(struct i915_page_directory_pointer *pdp) +{ + kfree(pdp->used_pdpes); + kfree(pdp->page_directory); + pdp->page_directory = NULL; +} + +static void free_pdp(struct drm_device *dev, + struct i915_page_directory_pointer *pdp) +{ + __pdp_fini(pdp); +} + /* Broadwell Page Directory Pointer Descriptors */ static int gen8_write_pdp(struct drm_i915_gem_request *req, unsigned entry, @@ -720,7 +757,8 @@ static void gen8_ppgtt_cleanup(struct i915_address_space *vm) container_of(vm, struct i915_hw_ppgtt, base); int i; - for_each_set_bit(i, ppgtt->pdp.used_pdpes, GEN8_LEGACY_PDPES) { + for_each_set_bit(i, ppgtt->pdp.used_pdpes, + I915_PDPES_PER_PDP(ppgtt->base.dev)) { if (WARN_ON(!ppgtt->pdp.page_directory[i])) continue; @@ -729,6 +767,7 @@ static void gen8_ppgtt_cleanup(struct i915_address_space *vm) free_pd(ppgtt->base.dev, ppgtt->pdp.page_directory[i]); } + free_pdp(ppgtt->base.dev, &ppgtt->pdp); gen8_free_scratch(vm); } @@ -763,7 +802,7 @@ static int gen8_ppgtt_alloc_pagetabs(struct i915_hw_ppgtt *ppgtt, gen8_for_each_pde(pt, pd, start, length, temp, pde) { /* Don't reallocate page tables */ - if (pt) { + if (test_bit(pde, pd->used_pdes)) { /* Scratch is never allocated this way */ WARN_ON(pt == ppgtt->base.scratch_pt); continue; @@ -820,11 +859,12 @@ static int gen8_ppgtt_alloc_page_directories(struct i915_hw_ppgtt *ppgtt, struct i915_page_directory *pd; uint64_t temp; uint32_t pdpe; + uint32_t pdpes = I915_PDPES_PER_PDP(dev); - WARN_ON(!bitmap_empty(new_pds, GEN8_LEGACY_PDPES)); + WARN_ON(!bitmap_empty(new_pds, pdpes)); gen8_for_each_pdpe(pd, pdp, start, length, temp, pdpe) { - if (pd) + if (test_bit(pdpe, pdp->used_pdpes)) continue; pd = alloc_pd(dev); @@ -839,18 +879,19 @@ static int gen8_ppgtt_alloc_page_directories(struct i915_hw_ppgtt *ppgtt, return 0; unwind_out: - for_each_set_bit(pdpe, new_pds, GEN8_LEGACY_PDPES) + for_each_set_bit(pdpe, new_pds, pdpes) free_pd(dev, pdp->page_directory[pdpe]); return -ENOMEM; } static void -free_gen8_temp_bitmaps(unsigned long *new_pds, unsigned long **new_pts) +free_gen8_temp_bitmaps(unsigned long *new_pds, unsigned long **new_pts, + uint32_t pdpes) { int i; - for (i = 0; i < GEN8_LEGACY_PDPES; i++) + for (i = 0; i < pdpes; i++) kfree(new_pts[i]); kfree(new_pts); kfree(new_pds); @@ -861,23 +902,24 @@ free_gen8_temp_bitmaps(unsigned long *new_pds, unsigned long **new_pts) */ static int __must_check alloc_gen8_temp_bitmaps(unsigned long **new_pds, - unsigned long ***new_pts) + unsigned long ***new_pts, + uint32_t pdpes) { int i; unsigned long *pds; unsigned long **pts; - pds = kcalloc(BITS_TO_LONGS(GEN8_LEGACY_PDPES), sizeof(unsigned long), GFP_KERNEL); + pds = kcalloc(BITS_TO_LONGS(pdpes), sizeof(unsigned long), GFP_KERNEL); if (!pds) return -ENOMEM; - pts = kcalloc(GEN8_LEGACY_PDPES, sizeof(unsigned long *), GFP_KERNEL); + pts = kcalloc(pdpes, sizeof(unsigned long *), GFP_KERNEL); if (!pts) { kfree(pds); return -ENOMEM; } - for (i = 0; i < GEN8_LEGACY_PDPES; i++) { + for (i = 0; i < pdpes; i++) { pts[i] = kcalloc(BITS_TO_LONGS(I915_PDES), sizeof(unsigned long), GFP_KERNEL); if (!pts[i]) @@ -890,7 +932,7 @@ int __must_check alloc_gen8_temp_bitmaps(unsigned long **new_pds, return 0; err_out: - free_gen8_temp_bitmaps(pds, pts); + free_gen8_temp_bitmaps(pds, pts, pdpes); return -ENOMEM; } @@ -916,6 +958,7 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, const uint64_t orig_length = length; uint64_t temp; uint32_t pdpe; + uint32_t pdpes = I915_PDPES_PER_PDP(ppgtt->base.dev); int ret; /* Wrap is never okay since we can only represent 48b, and we don't @@ -927,7 +970,7 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, if (WARN_ON(start + length > ppgtt->base.total)) return -ENODEV; - ret = alloc_gen8_temp_bitmaps(&new_page_dirs, &new_page_tables); + ret = alloc_gen8_temp_bitmaps(&new_page_dirs, &new_page_tables, pdpes); if (ret) return ret; @@ -935,7 +978,7 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, ret = gen8_ppgtt_alloc_page_directories(ppgtt, &ppgtt->pdp, start, length, new_page_dirs); if (ret) { - free_gen8_temp_bitmaps(new_page_dirs, new_page_tables); + free_gen8_temp_bitmaps(new_page_dirs, new_page_tables, pdpes); return ret; } @@ -989,7 +1032,7 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, __set_bit(pdpe, ppgtt->pdp.used_pdpes); } - free_gen8_temp_bitmaps(new_page_dirs, new_page_tables); + free_gen8_temp_bitmaps(new_page_dirs, new_page_tables, pdpes); mark_tlbs_dirty(ppgtt); return 0; @@ -999,10 +1042,10 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, free_pt(vm->dev, ppgtt->pdp.page_directory[pdpe]->page_table[temp]); } - for_each_set_bit(pdpe, new_page_dirs, GEN8_LEGACY_PDPES) + for_each_set_bit(pdpe, new_page_dirs, pdpes) free_pd(vm->dev, ppgtt->pdp.page_directory[pdpe]); - free_gen8_temp_bitmaps(new_page_dirs, new_page_tables); + free_gen8_temp_bitmaps(new_page_dirs, new_page_tables, pdpes); mark_tlbs_dirty(ppgtt); return ret; } @@ -1040,7 +1083,16 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt) ppgtt->switch_mm = gen8_mm_switch; + ret = __pdp_init(false, &ppgtt->pdp); + + if (ret) + goto free_scratch; + return 0; + +free_scratch: + gen8_free_scratch(&ppgtt->base); + return ret; } static void gen6_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index d5bf953deff9..87e389c6c9c0 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -98,6 +98,9 @@ typedef uint64_t gen8_pde_t; #define GEN8_LEGACY_PDPES 4 #define GEN8_PTES I915_PTES(sizeof(gen8_pte_t)) +/* FIXME: Next patch will use dev */ +#define I915_PDPES_PER_PDP(dev) GEN8_LEGACY_PDPES + #define PPAT_UNCACHED_INDEX (_PAGE_PWT | _PAGE_PCD) #define PPAT_CACHED_PDE_INDEX 0 /* WB LLC */ #define PPAT_CACHED_INDEX _PAGE_PAT /* WB LLCeLLC */ @@ -241,9 +244,10 @@ struct i915_page_directory { }; struct i915_page_directory_pointer { - /* struct page *page; */ - DECLARE_BITMAP(used_pdpes, GEN8_LEGACY_PDPES); - struct i915_page_directory *page_directory[GEN8_LEGACY_PDPES]; + struct i915_page_dma base; + + unsigned long *used_pdpes; + struct i915_page_directory **page_directory; }; struct i915_address_space { @@ -436,9 +440,10 @@ static inline uint32_t gen6_pde_index(uint32_t addr) temp = min(temp, length), \ start += temp, length -= temp) -#define gen8_for_each_pdpe(pd, pdp, start, length, temp, iter) \ - for (iter = gen8_pdpe_index(start); \ - pd = (pdp)->page_directory[iter], length > 0 && iter < GEN8_LEGACY_PDPES; \ +#define gen8_for_each_pdpe(pd, pdp, start, length, temp, iter) \ + for (iter = gen8_pdpe_index(start); \ + pd = (pdp)->page_directory[iter], \ + length > 0 && (iter < I915_PDPES_PER_PDP(dev)); \ iter++, \ temp = ALIGN(start+1, 1 << GEN8_PDPE_SHIFT) - start, \ temp = min(temp, length), \ From d4ec9da0e17cb6a86c0b76c5b254981601d25031 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Thu, 30 Jul 2015 11:02:03 +0100 Subject: [PATCH 003/145] drm/i915/gen8: Abstract PDP usage Up until now, ppgtt->pdp has always been the root of our page tables. Legacy 32b addresses acted like it had 1 PDP with 4 PDPEs. In preparation for 4 level page tables, we need to stop using ppgtt->pdp directly unless we know it's what we want. The future structure will use ppgtt->pml4 for the top level, and the pdp is just one of the entries being pointed to by a pml4e. The temporal pdp local variable will be removed once the rest of the 4-level code lands. Also, start passing the vm pointer to the alloc functions, instead of ppgtt. v2: Updated after dynamic page allocation changes. v3: Rebase after s/page_tables/page_table/. v4: Rebase after changes in "Dynamic page table allocations" patch. v5: Rebase after Mika's ppgtt cleanup / scratch merge patch series. v6: Rebase after final merged version of Mika's ppgtt/scratch patches. v7: Keep pagetable map in-line (and avoid unnecessary for_each_pde loops), remove redundant ppgtt pointer in _alloc_pagetabs (Akash) v8: Fix text indentation in _alloc_pagetabs/page_directories (Chris) v9: Defer gen8_alloc_va_range_4lvl definition until 4lvl is implemented, clean-up gen8_ppgtt_cleanup [pun intended] (Akash). v10: Clean-up commit message (Akash). Cc: Akash Goel Signed-off-by: Ben Widawsky Signed-off-by: Michel Thierry (v2+) Reviewed-by: "Akash Goel" Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 84 +++++++++++++++-------------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 6724e71cf1d7..95a468815ed8 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -607,6 +607,7 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm, { struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); + struct i915_page_directory_pointer *pdp = &ppgtt->pdp; /* FIXME: 48b */ gen8_pte_t *pt_vaddr, scratch_pte; unsigned pdpe = start >> GEN8_PDPE_SHIFT & GEN8_PDPE_MASK; unsigned pde = start >> GEN8_PDE_SHIFT & GEN8_PDE_MASK; @@ -621,10 +622,10 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm, struct i915_page_directory *pd; struct i915_page_table *pt; - if (WARN_ON(!ppgtt->pdp.page_directory[pdpe])) + if (WARN_ON(!pdp->page_directory[pdpe])) break; - pd = ppgtt->pdp.page_directory[pdpe]; + pd = pdp->page_directory[pdpe]; if (WARN_ON(!pd->page_table[pde])) break; @@ -662,6 +663,7 @@ static void gen8_ppgtt_insert_entries(struct i915_address_space *vm, { struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); + struct i915_page_directory_pointer *pdp = &ppgtt->pdp; /* FIXME: 48b */ gen8_pte_t *pt_vaddr; unsigned pdpe = start >> GEN8_PDPE_SHIFT & GEN8_PDPE_MASK; unsigned pde = start >> GEN8_PDE_SHIFT & GEN8_PDE_MASK; @@ -675,7 +677,7 @@ static void gen8_ppgtt_insert_entries(struct i915_address_space *vm, break; if (pt_vaddr == NULL) { - struct i915_page_directory *pd = ppgtt->pdp.page_directory[pdpe]; + struct i915_page_directory *pd = pdp->page_directory[pdpe]; struct i915_page_table *pt = pd->page_table[pde]; pt_vaddr = kmap_px(pt); } @@ -755,28 +757,29 @@ static void gen8_ppgtt_cleanup(struct i915_address_space *vm) { struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); + struct i915_page_directory_pointer *pdp = &ppgtt->pdp; /* FIXME: 48b */ + struct drm_device *dev = ppgtt->base.dev; int i; - for_each_set_bit(i, ppgtt->pdp.used_pdpes, - I915_PDPES_PER_PDP(ppgtt->base.dev)) { - if (WARN_ON(!ppgtt->pdp.page_directory[i])) + for_each_set_bit(i, pdp->used_pdpes, I915_PDPES_PER_PDP(dev)) { + if (WARN_ON(!pdp->page_directory[i])) continue; - gen8_free_page_tables(ppgtt->base.dev, - ppgtt->pdp.page_directory[i]); - free_pd(ppgtt->base.dev, ppgtt->pdp.page_directory[i]); + gen8_free_page_tables(dev, pdp->page_directory[i]); + free_pd(dev, pdp->page_directory[i]); } - free_pdp(ppgtt->base.dev, &ppgtt->pdp); + free_pdp(dev, pdp); + gen8_free_scratch(vm); } /** * gen8_ppgtt_alloc_pagetabs() - Allocate page tables for VA range. - * @ppgtt: Master ppgtt structure. - * @pd: Page directory for this address range. + * @vm: Master vm structure. + * @pd: Page directory for this address range. * @start: Starting virtual address to begin allocations. - * @length Size of the allocations. + * @length: Size of the allocations. * @new_pts: Bitmap set by function with new allocations. Likely used by the * caller to free on error. * @@ -789,13 +792,13 @@ static void gen8_ppgtt_cleanup(struct i915_address_space *vm) * * Return: 0 if success; negative error code otherwise. */ -static int gen8_ppgtt_alloc_pagetabs(struct i915_hw_ppgtt *ppgtt, +static int gen8_ppgtt_alloc_pagetabs(struct i915_address_space *vm, struct i915_page_directory *pd, uint64_t start, uint64_t length, unsigned long *new_pts) { - struct drm_device *dev = ppgtt->base.dev; + struct drm_device *dev = vm->dev; struct i915_page_table *pt; uint64_t temp; uint32_t pde; @@ -804,7 +807,7 @@ static int gen8_ppgtt_alloc_pagetabs(struct i915_hw_ppgtt *ppgtt, /* Don't reallocate page tables */ if (test_bit(pde, pd->used_pdes)) { /* Scratch is never allocated this way */ - WARN_ON(pt == ppgtt->base.scratch_pt); + WARN_ON(pt == vm->scratch_pt); continue; } @@ -812,7 +815,7 @@ static int gen8_ppgtt_alloc_pagetabs(struct i915_hw_ppgtt *ppgtt, if (IS_ERR(pt)) goto unwind_out; - gen8_initialize_pt(&ppgtt->base, pt); + gen8_initialize_pt(vm, pt); pd->page_table[pde] = pt; __set_bit(pde, new_pts); } @@ -828,11 +831,11 @@ static int gen8_ppgtt_alloc_pagetabs(struct i915_hw_ppgtt *ppgtt, /** * gen8_ppgtt_alloc_page_directories() - Allocate page directories for VA range. - * @ppgtt: Master ppgtt structure. + * @vm: Master vm structure. * @pdp: Page directory pointer for this address range. * @start: Starting virtual address to begin allocations. - * @length Size of the allocations. - * @new_pds Bitmap set by function with new allocations. Likely used by the + * @length: Size of the allocations. + * @new_pds: Bitmap set by function with new allocations. Likely used by the * caller to free on error. * * Allocate the required number of page directories starting at the pde index of @@ -849,13 +852,14 @@ static int gen8_ppgtt_alloc_pagetabs(struct i915_hw_ppgtt *ppgtt, * * Return: 0 if success; negative error code otherwise. */ -static int gen8_ppgtt_alloc_page_directories(struct i915_hw_ppgtt *ppgtt, - struct i915_page_directory_pointer *pdp, - uint64_t start, - uint64_t length, - unsigned long *new_pds) +static int +gen8_ppgtt_alloc_page_directories(struct i915_address_space *vm, + struct i915_page_directory_pointer *pdp, + uint64_t start, + uint64_t length, + unsigned long *new_pds) { - struct drm_device *dev = ppgtt->base.dev; + struct drm_device *dev = vm->dev; struct i915_page_directory *pd; uint64_t temp; uint32_t pdpe; @@ -871,7 +875,7 @@ static int gen8_ppgtt_alloc_page_directories(struct i915_hw_ppgtt *ppgtt, if (IS_ERR(pd)) goto unwind_out; - gen8_initialize_pd(&ppgtt->base, pd); + gen8_initialize_pd(vm, pd); pdp->page_directory[pdpe] = pd; __set_bit(pdpe, new_pds); } @@ -947,18 +951,19 @@ static void mark_tlbs_dirty(struct i915_hw_ppgtt *ppgtt) } static int gen8_alloc_va_range(struct i915_address_space *vm, - uint64_t start, - uint64_t length) + uint64_t start, uint64_t length) { struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); unsigned long *new_page_dirs, **new_page_tables; + struct drm_device *dev = vm->dev; + struct i915_page_directory_pointer *pdp = &ppgtt->pdp; /* FIXME: 48b */ struct i915_page_directory *pd; const uint64_t orig_start = start; const uint64_t orig_length = length; uint64_t temp; uint32_t pdpe; - uint32_t pdpes = I915_PDPES_PER_PDP(ppgtt->base.dev); + uint32_t pdpes = I915_PDPES_PER_PDP(dev); int ret; /* Wrap is never okay since we can only represent 48b, and we don't @@ -967,7 +972,7 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, if (WARN_ON(start + length < start)) return -ENODEV; - if (WARN_ON(start + length > ppgtt->base.total)) + if (WARN_ON(start + length > vm->total)) return -ENODEV; ret = alloc_gen8_temp_bitmaps(&new_page_dirs, &new_page_tables, pdpes); @@ -975,16 +980,16 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, return ret; /* Do the allocations first so we can easily bail out */ - ret = gen8_ppgtt_alloc_page_directories(ppgtt, &ppgtt->pdp, start, length, - new_page_dirs); + ret = gen8_ppgtt_alloc_page_directories(vm, pdp, start, length, + new_page_dirs); if (ret) { free_gen8_temp_bitmaps(new_page_dirs, new_page_tables, pdpes); return ret; } /* For every page directory referenced, allocate page tables */ - gen8_for_each_pdpe(pd, &ppgtt->pdp, start, length, temp, pdpe) { - ret = gen8_ppgtt_alloc_pagetabs(ppgtt, pd, start, length, + gen8_for_each_pdpe(pd, pdp, start, length, temp, pdpe) { + ret = gen8_ppgtt_alloc_pagetabs(vm, pd, start, length, new_page_tables[pdpe]); if (ret) goto err_out; @@ -995,7 +1000,7 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, /* Allocations have completed successfully, so set the bitmaps, and do * the mappings. */ - gen8_for_each_pdpe(pd, &ppgtt->pdp, start, length, temp, pdpe) { + gen8_for_each_pdpe(pd, pdp, start, length, temp, pdpe) { gen8_pde_t *const page_directory = kmap_px(pd); struct i915_page_table *pt; uint64_t pd_len = length; @@ -1028,8 +1033,7 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, } kunmap_px(ppgtt, page_directory); - - __set_bit(pdpe, ppgtt->pdp.used_pdpes); + __set_bit(pdpe, pdp->used_pdpes); } free_gen8_temp_bitmaps(new_page_dirs, new_page_tables, pdpes); @@ -1039,11 +1043,11 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, err_out: while (pdpe--) { for_each_set_bit(temp, new_page_tables[pdpe], I915_PDES) - free_pt(vm->dev, ppgtt->pdp.page_directory[pdpe]->page_table[temp]); + free_pt(dev, pdp->page_directory[pdpe]->page_table[temp]); } for_each_set_bit(pdpe, new_page_dirs, pdpes) - free_pd(vm->dev, ppgtt->pdp.page_directory[pdpe]); + free_pd(dev, pdp->page_directory[pdpe]); free_gen8_temp_bitmaps(new_page_dirs, new_page_tables, pdpes); mark_tlbs_dirty(ppgtt); From f9b5b782c4720f6c353088b559af5842ce66aa6f Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Thu, 30 Jul 2015 11:02:49 +0100 Subject: [PATCH 004/145] drm/i915/gen8: Generalize PTE writing for GEN8 PPGTT The insert_entries function was the function used to write PTEs. For the PPGTT it was "hardcoded" to only understand two level page tables, which was the case for GEN7. We can reuse this for 4 level page tables, and remove the concept of insert_entries, which was never viable past 2 level page tables anyway, but it requires a bit of rework to make the function a bit more generic. v2: Rebase after Mika's ppgtt cleanup / scratch merge patch series. v3: Rebase after final merged version of Mika's ppgtt/scratch patches. v4: Check and warn for NULL value of pdp pointer (Akash). Cc: Akash Goel Signed-off-by: Ben Widawsky Signed-off-by: Michel Thierry (v2) Reviewed-by: Akash Goel Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 53 ++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 95a468815ed8..fc58f541d546 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -600,23 +600,23 @@ static int gen8_mm_switch(struct i915_hw_ppgtt *ppgtt, return 0; } -static void gen8_ppgtt_clear_range(struct i915_address_space *vm, - uint64_t start, - uint64_t length, - bool use_scratch) +static void gen8_ppgtt_clear_pte_range(struct i915_address_space *vm, + struct i915_page_directory_pointer *pdp, + uint64_t start, + uint64_t length, + gen8_pte_t scratch_pte) { struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); - struct i915_page_directory_pointer *pdp = &ppgtt->pdp; /* FIXME: 48b */ - gen8_pte_t *pt_vaddr, scratch_pte; + gen8_pte_t *pt_vaddr; unsigned pdpe = start >> GEN8_PDPE_SHIFT & GEN8_PDPE_MASK; unsigned pde = start >> GEN8_PDE_SHIFT & GEN8_PDE_MASK; unsigned pte = start >> GEN8_PTE_SHIFT & GEN8_PTE_MASK; unsigned num_entries = length >> PAGE_SHIFT; unsigned last_pte, i; - scratch_pte = gen8_pte_encode(px_dma(ppgtt->base.scratch_page), - I915_CACHE_LLC, use_scratch); + if (WARN_ON(!pdp)) + return; while (num_entries) { struct i915_page_directory *pd; @@ -656,14 +656,30 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm, } } -static void gen8_ppgtt_insert_entries(struct i915_address_space *vm, - struct sg_table *pages, - uint64_t start, - enum i915_cache_level cache_level, u32 unused) +static void gen8_ppgtt_clear_range(struct i915_address_space *vm, + uint64_t start, + uint64_t length, + bool use_scratch) { struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); struct i915_page_directory_pointer *pdp = &ppgtt->pdp; /* FIXME: 48b */ + + gen8_pte_t scratch_pte = gen8_pte_encode(px_dma(vm->scratch_page), + I915_CACHE_LLC, use_scratch); + + gen8_ppgtt_clear_pte_range(vm, pdp, start, length, scratch_pte); +} + +static void +gen8_ppgtt_insert_pte_entries(struct i915_address_space *vm, + struct i915_page_directory_pointer *pdp, + struct sg_table *pages, + uint64_t start, + enum i915_cache_level cache_level) +{ + struct i915_hw_ppgtt *ppgtt = + container_of(vm, struct i915_hw_ppgtt, base); gen8_pte_t *pt_vaddr; unsigned pdpe = start >> GEN8_PDPE_SHIFT & GEN8_PDPE_MASK; unsigned pde = start >> GEN8_PDE_SHIFT & GEN8_PDE_MASK; @@ -700,6 +716,19 @@ static void gen8_ppgtt_insert_entries(struct i915_address_space *vm, kunmap_px(ppgtt, pt_vaddr); } +static void gen8_ppgtt_insert_entries(struct i915_address_space *vm, + struct sg_table *pages, + uint64_t start, + enum i915_cache_level cache_level, + u32 unused) +{ + struct i915_hw_ppgtt *ppgtt = + container_of(vm, struct i915_hw_ppgtt, base); + struct i915_page_directory_pointer *pdp = &ppgtt->pdp; /* FIXME: 48b */ + + gen8_ppgtt_insert_pte_entries(vm, pdp, pages, start, cache_level); +} + static void gen8_free_page_tables(struct drm_device *dev, struct i915_page_directory *pd) { From 4c06ec8d13d2b0e57479bb135e49afd56ebe9275 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Wed, 29 Jul 2015 17:23:49 +0100 Subject: [PATCH 005/145] drm/i915/gen8: Add dynamic page trace events The dynamic page allocation patch series added it for GEN6, this patch adds them for GEN8. v2: Consolidate pagetable/page_directory events v3: Multiple rebases. v4: Rebase after s/page_tables/page_table/. v5: Rebase after Mika's ppgtt cleanup / scratch merge patch series. v6: Rebase after gen8_map_pagetable_range removal. v7: Use generic page name (px) in DECLARE_EVENT_CLASS (Akash) v8: Defer define of i915_page_directory_pointer_entry_alloc (Akash) Cc: Akash Goel Signed-off-by: Ben Widawsky Signed-off-by: Michel Thierry (v3+) Reviewed-by: Akash Goel Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 6 ++++++ drivers/gpu/drm/i915/i915_trace.h | 24 ++++++++++++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index fc58f541d546..72f4777f06fe 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -847,6 +847,7 @@ static int gen8_ppgtt_alloc_pagetabs(struct i915_address_space *vm, gen8_initialize_pt(vm, pt); pd->page_table[pde] = pt; __set_bit(pde, new_pts); + trace_i915_page_table_entry_alloc(vm, pde, start, GEN8_PDE_SHIFT); } return 0; @@ -907,6 +908,7 @@ gen8_ppgtt_alloc_page_directories(struct i915_address_space *vm, gen8_initialize_pd(vm, pd); pdp->page_directory[pdpe] = pd; __set_bit(pdpe, new_pds); + trace_i915_page_directory_entry_alloc(vm, pdpe, start, GEN8_PDPE_SHIFT); } return 0; @@ -1056,6 +1058,10 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, /* Map the PDE to the page table */ page_directory[pde] = gen8_pde_encode(px_dma(pt), I915_CACHE_LLC); + trace_i915_page_table_entry_map(&ppgtt->base, pde, pt, + gen8_pte_index(start), + gen8_pte_count(start, length), + GEN8_PTES); /* NB: We haven't yet mapped ptes to pages. At this * point we're still relying on insert_entries() */ diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index 2f34c47bd4bf..f230d7639000 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -186,33 +186,41 @@ DEFINE_EVENT(i915_va, i915_va_alloc, TP_ARGS(vm, start, length, name) ); -DECLARE_EVENT_CLASS(i915_page_table_entry, - TP_PROTO(struct i915_address_space *vm, u32 pde, u64 start, u64 pde_shift), - TP_ARGS(vm, pde, start, pde_shift), +DECLARE_EVENT_CLASS(i915_px_entry, + TP_PROTO(struct i915_address_space *vm, u32 px, u64 start, u64 px_shift), + TP_ARGS(vm, px, start, px_shift), TP_STRUCT__entry( __field(struct i915_address_space *, vm) - __field(u32, pde) + __field(u32, px) __field(u64, start) __field(u64, end) ), TP_fast_assign( __entry->vm = vm; - __entry->pde = pde; + __entry->px = px; __entry->start = start; - __entry->end = ((start + (1ULL << pde_shift)) & ~((1ULL << pde_shift)-1)) - 1; + __entry->end = ((start + (1ULL << px_shift)) & ~((1ULL << px_shift)-1)) - 1; ), TP_printk("vm=%p, pde=%d (0x%llx-0x%llx)", - __entry->vm, __entry->pde, __entry->start, __entry->end) + __entry->vm, __entry->px, __entry->start, __entry->end) ); -DEFINE_EVENT(i915_page_table_entry, i915_page_table_entry_alloc, +DEFINE_EVENT(i915_px_entry, i915_page_table_entry_alloc, TP_PROTO(struct i915_address_space *vm, u32 pde, u64 start, u64 pde_shift), TP_ARGS(vm, pde, start, pde_shift) ); +DEFINE_EVENT_PRINT(i915_px_entry, i915_page_directory_entry_alloc, + TP_PROTO(struct i915_address_space *vm, u32 pdpe, u64 start, u64 pdpe_shift), + TP_ARGS(vm, pdpe, start, pdpe_shift), + + TP_printk("vm=%p, pdpe=%d (0x%llx-0x%llx)", + __entry->vm, __entry->px, __entry->start, __entry->end) +); + /* Avoid extra math because we only support two sizes. The format is defined by * bitmap_scnprintf. Each 32 bits is 8 HEX digits followed by comma */ #define TRACE_PT_SIZE(bits) \ From 81ba8aefd03803a8aec3395d18f7b1dda5942105 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Mon, 3 Aug 2015 09:52:01 +0100 Subject: [PATCH 006/145] drm/i915/gen8: Add PML4 structure Introduces the Page Map Level 4 (PML4), ie. the new top level structure of the page tables. To facilitate testing, 48b mode will be available on Broadwell and GEN9+, when i915.enable_ppgtt = 3. v2: Remove unnecessary CONFIG_X86_64 checks, ppgtt code is already 32/64-bit safe (Chris). v3: Add goto free_scratch in temp 48-bit mode init code (Akash). v4: kfree the pdp until the 4lvl alloc/free patch (Akash). v5: Postpone 48-bit code in sanitize_enable_ppgtt (Akash). v6: Keep _insert_pte_entries changes outside this patch (Akash). Cc: Akash Goel Signed-off-by: Michel Thierry Reviewed-by: Akash Goel Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 3 ++- drivers/gpu/drm/i915/i915_gem_gtt.c | 27 +++++++++++++++++---------- drivers/gpu/drm/i915/i915_gem_gtt.h | 26 +++++++++++++++++++++----- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4273c281ce77..79d2dcb13bb0 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2510,7 +2510,8 @@ struct drm_i915_cmd_table { #define HAS_HW_CONTEXTS(dev) (INTEL_INFO(dev)->gen >= 6) #define HAS_LOGICAL_RING_CONTEXTS(dev) (INTEL_INFO(dev)->gen >= 8) #define USES_PPGTT(dev) (i915.enable_ppgtt) -#define USES_FULL_PPGTT(dev) (i915.enable_ppgtt == 2) +#define USES_FULL_PPGTT(dev) (i915.enable_ppgtt >= 2) +#define USES_FULL_48BIT_PPGTT(dev) (i915.enable_ppgtt == 3) #define HAS_OVERLAY(dev) (INTEL_INFO(dev)->has_overlay) #define OVERLAY_NEEDS_PHYSICAL(dev) (INTEL_INFO(dev)->overlay_needs_physical) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 72f4777f06fe..da7863b5c22b 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -1105,14 +1105,6 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt) return ret; ppgtt->base.start = 0; - ppgtt->base.total = 1ULL << 32; - if (IS_ENABLED(CONFIG_X86_32)) - /* While we have a proliferation of size_t variables - * we cannot represent the full ppgtt size on 32bit, - * so limit it to the same size as the GGTT (currently - * 2GiB). - */ - ppgtt->base.total = to_i915(ppgtt->base.dev)->gtt.base.total; ppgtt->base.cleanup = gen8_ppgtt_cleanup; ppgtt->base.allocate_va_range = gen8_alloc_va_range; ppgtt->base.insert_entries = gen8_ppgtt_insert_entries; @@ -1122,10 +1114,25 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt) ppgtt->switch_mm = gen8_mm_switch; - ret = __pdp_init(false, &ppgtt->pdp); + if (!USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) { + ret = __pdp_init(false, &ppgtt->pdp); - if (ret) + if (ret) + goto free_scratch; + + ppgtt->base.total = 1ULL << 32; + if (IS_ENABLED(CONFIG_X86_32)) + /* While we have a proliferation of size_t variables + * we cannot represent the full ppgtt size on 32bit, + * so limit it to the same size as the GGTT (currently + * 2GiB). + */ + ppgtt->base.total = to_i915(ppgtt->base.dev)->gtt.base.total; + } else { + ppgtt->base.total = 1ULL << 48; + ret = -EPERM; /* Not yet implemented */ goto free_scratch; + } return 0; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index 87e389c6c9c0..04bc66f113a6 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -88,9 +88,17 @@ typedef uint64_t gen8_pde_t; * PDPE | PDE | PTE | offset * The difference as compared to normal x86 3 level page table is the PDPEs are * programmed via register. + * + * GEN8 48b legacy style address is defined as a 4 level page table: + * 47:39 | 38:30 | 29:21 | 20:12 | 11:0 + * PML4E | PDPE | PDE | PTE | offset */ +#define GEN8_PML4ES_PER_PML4 512 +#define GEN8_PML4E_SHIFT 39 #define GEN8_PDPE_SHIFT 30 -#define GEN8_PDPE_MASK 0x3 +/* NB: GEN8_PDPE_MASK is untrue for 32b platforms, but it has no impact on 32b page + * tables */ +#define GEN8_PDPE_MASK 0x1ff #define GEN8_PDE_SHIFT 21 #define GEN8_PDE_MASK 0x1ff #define GEN8_PTE_SHIFT 12 @@ -98,8 +106,8 @@ typedef uint64_t gen8_pde_t; #define GEN8_LEGACY_PDPES 4 #define GEN8_PTES I915_PTES(sizeof(gen8_pte_t)) -/* FIXME: Next patch will use dev */ -#define I915_PDPES_PER_PDP(dev) GEN8_LEGACY_PDPES +#define I915_PDPES_PER_PDP(dev) (USES_FULL_48BIT_PPGTT(dev) ?\ + GEN8_PML4ES_PER_PML4 : GEN8_LEGACY_PDPES) #define PPAT_UNCACHED_INDEX (_PAGE_PWT | _PAGE_PCD) #define PPAT_CACHED_PDE_INDEX 0 /* WB LLC */ @@ -250,6 +258,13 @@ struct i915_page_directory_pointer { struct i915_page_directory **page_directory; }; +struct i915_pml4 { + struct i915_page_dma base; + + DECLARE_BITMAP(used_pml4es, GEN8_PML4ES_PER_PML4); + struct i915_page_directory_pointer *pdps[GEN8_PML4ES_PER_PML4]; +}; + struct i915_address_space { struct drm_mm mm; struct drm_device *dev; @@ -345,8 +360,9 @@ struct i915_hw_ppgtt { struct drm_mm_node node; unsigned long pd_dirty_rings; union { - struct i915_page_directory_pointer pdp; - struct i915_page_directory pd; + struct i915_pml4 pml4; /* GEN8+ & 48b PPGTT */ + struct i915_page_directory_pointer pdp; /* GEN8+ */ + struct i915_page_directory pd; /* GEN6-7 */ }; struct drm_i915_file_private *file_priv; From 762d99363dc9bc436f39f8bdc3f8670ea272a5a9 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Thu, 30 Jul 2015 11:05:29 +0100 Subject: [PATCH 007/145] drm/i915/gen8: implement alloc/free for 4lvl PML4 has no special attributes, and there will always be a PML4. So simply initialize it at creation, and destroy it at the end. The code for 4lvl is able to call into the existing 3lvl page table code to handle all of the lower levels. v2: Return something at the end of gen8_alloc_va_range_4lvl to keep the compiler happy. And define ret only in one place. Updated gen8_ppgtt_unmap_pages and gen8_ppgtt_free to handle 4lvl. v3: Use i915_dma_unmap_single instead of pci API. Fix a couple of incorrect checks when unmapping pdp and pd pages (Akash). v4: Call __pdp_fini also for 32b PPGTT. Clean up alloc_pdp param list. v5: Prevent (harmless) out of range access in gen8_for_each_pml4e. v6: Simplify alloc_vma_range_4lvl and gen8_ppgtt_init_common error paths. (Akash) v7: Rebase, s/gen8_ppgtt_free_*/gen8_ppgtt_cleanup_*/. v8: Change location of pml4_init/fini. It will make next patches cleaner. v9: Rebase after Mika's ppgtt cleanup / scratch merge patch series, while trying to reuse as much as possible for pdp alloc. pml4_init/fini replaced by setup/cleanup_px macros. v10: Rebase after Mika's merged ppgtt cleanup patch series. v11: Rebase after final merged version of Mika's ppgtt/scratch patches. v12: Fix pdpe start value in trace (Akash) v13: Define all 4lvl functions in this patch directly, instead of previous patches, add i915_page_directory_pointer_entry_alloc here, use test_bit to detect when pdp is already allocated (Akash). v14: Move pdp allocation into a new gen8_ppgtt_alloc_page_dirpointers funtion, as we do for pds and pts; move pd and pdp setup functions to this patch (Akash). v15: Added kfree(pdp) from previous patch to this (Akash). Cc: Akash Goel Signed-off-by: Ben Widawsky Signed-off-by: Michel Thierry (v2+) Reviewed-by: Akash Goel Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 239 ++++++++++++++++++++++++++-- drivers/gpu/drm/i915/i915_gem_gtt.h | 15 +- drivers/gpu/drm/i915/i915_trace.h | 8 + 3 files changed, 246 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index da7863b5c22b..229a31760260 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -204,6 +204,9 @@ static gen8_pde_t gen8_pde_encode(const dma_addr_t addr, return pde; } +#define gen8_pdpe_encode gen8_pde_encode +#define gen8_pml4e_encode gen8_pde_encode + static gen6_pte_t snb_pte_encode(dma_addr_t addr, enum i915_cache_level level, bool valid, u32 unused) @@ -553,10 +556,73 @@ static void __pdp_fini(struct i915_page_directory_pointer *pdp) pdp->page_directory = NULL; } +static struct +i915_page_directory_pointer *alloc_pdp(struct drm_device *dev) +{ + struct i915_page_directory_pointer *pdp; + int ret = -ENOMEM; + + WARN_ON(!USES_FULL_48BIT_PPGTT(dev)); + + pdp = kzalloc(sizeof(*pdp), GFP_KERNEL); + if (!pdp) + return ERR_PTR(-ENOMEM); + + ret = __pdp_init(dev, pdp); + if (ret) + goto fail_bitmap; + + ret = setup_px(dev, pdp); + if (ret) + goto fail_page_m; + + return pdp; + +fail_page_m: + __pdp_fini(pdp); +fail_bitmap: + kfree(pdp); + + return ERR_PTR(ret); +} + static void free_pdp(struct drm_device *dev, struct i915_page_directory_pointer *pdp) { __pdp_fini(pdp); + if (USES_FULL_48BIT_PPGTT(dev)) { + cleanup_px(dev, pdp); + kfree(pdp); + } +} + +static void +gen8_setup_page_directory(struct i915_hw_ppgtt *ppgtt, + struct i915_page_directory_pointer *pdp, + struct i915_page_directory *pd, + int index) +{ + gen8_ppgtt_pdpe_t *page_directorypo; + + if (!USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) + return; + + page_directorypo = kmap_px(pdp); + page_directorypo[index] = gen8_pdpe_encode(px_dma(pd), I915_CACHE_LLC); + kunmap_px(ppgtt, page_directorypo); +} + +static void +gen8_setup_page_directory_pointer(struct i915_hw_ppgtt *ppgtt, + struct i915_pml4 *pml4, + struct i915_page_directory_pointer *pdp, + int index) +{ + gen8_ppgtt_pml4e_t *pagemap = kmap_px(pml4); + + WARN_ON(!USES_FULL_48BIT_PPGTT(ppgtt->base.dev)); + pagemap[index] = gen8_pml4e_encode(px_dma(pdp), I915_CACHE_LLC); + kunmap_px(ppgtt, pagemap); } /* Broadwell Page Directory Pointer Descriptors */ @@ -782,12 +848,9 @@ static void gen8_free_scratch(struct i915_address_space *vm) free_scratch_page(dev, vm->scratch_page); } -static void gen8_ppgtt_cleanup(struct i915_address_space *vm) +static void gen8_ppgtt_cleanup_3lvl(struct drm_device *dev, + struct i915_page_directory_pointer *pdp) { - struct i915_hw_ppgtt *ppgtt = - container_of(vm, struct i915_hw_ppgtt, base); - struct i915_page_directory_pointer *pdp = &ppgtt->pdp; /* FIXME: 48b */ - struct drm_device *dev = ppgtt->base.dev; int i; for_each_set_bit(i, pdp->used_pdpes, I915_PDPES_PER_PDP(dev)) { @@ -799,6 +862,31 @@ static void gen8_ppgtt_cleanup(struct i915_address_space *vm) } free_pdp(dev, pdp); +} + +static void gen8_ppgtt_cleanup_4lvl(struct i915_hw_ppgtt *ppgtt) +{ + int i; + + for_each_set_bit(i, ppgtt->pml4.used_pml4es, GEN8_PML4ES_PER_PML4) { + if (WARN_ON(!ppgtt->pml4.pdps[i])) + continue; + + gen8_ppgtt_cleanup_3lvl(ppgtt->base.dev, ppgtt->pml4.pdps[i]); + } + + cleanup_px(ppgtt->base.dev, &ppgtt->pml4); +} + +static void gen8_ppgtt_cleanup(struct i915_address_space *vm) +{ + struct i915_hw_ppgtt *ppgtt = + container_of(vm, struct i915_hw_ppgtt, base); + + if (!USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) + gen8_ppgtt_cleanup_3lvl(ppgtt->base.dev, &ppgtt->pdp); + else + gen8_ppgtt_cleanup_4lvl(ppgtt); gen8_free_scratch(vm); } @@ -920,6 +1008,60 @@ gen8_ppgtt_alloc_page_directories(struct i915_address_space *vm, return -ENOMEM; } +/** + * gen8_ppgtt_alloc_page_dirpointers() - Allocate pdps for VA range. + * @vm: Master vm structure. + * @pml4: Page map level 4 for this address range. + * @start: Starting virtual address to begin allocations. + * @length: Size of the allocations. + * @new_pdps: Bitmap set by function with new allocations. Likely used by the + * caller to free on error. + * + * Allocate the required number of page directory pointers. Extremely similar to + * gen8_ppgtt_alloc_page_directories() and gen8_ppgtt_alloc_pagetabs(). + * The main difference is here we are limited by the pml4 boundary (instead of + * the page directory pointer). + * + * Return: 0 if success; negative error code otherwise. + */ +static int +gen8_ppgtt_alloc_page_dirpointers(struct i915_address_space *vm, + struct i915_pml4 *pml4, + uint64_t start, + uint64_t length, + unsigned long *new_pdps) +{ + struct drm_device *dev = vm->dev; + struct i915_page_directory_pointer *pdp; + uint64_t temp; + uint32_t pml4e; + + WARN_ON(!bitmap_empty(new_pdps, GEN8_PML4ES_PER_PML4)); + + gen8_for_each_pml4e(pdp, pml4, start, length, temp, pml4e) { + if (!test_bit(pml4e, pml4->used_pml4es)) { + pdp = alloc_pdp(dev); + if (IS_ERR(pdp)) + goto unwind_out; + + pml4->pdps[pml4e] = pdp; + __set_bit(pml4e, new_pdps); + trace_i915_page_directory_pointer_entry_alloc(vm, + pml4e, + start, + GEN8_PML4E_SHIFT); + } + } + + return 0; + +unwind_out: + for_each_set_bit(pml4e, new_pdps, GEN8_PML4ES_PER_PML4) + free_pdp(dev, pml4->pdps[pml4e]); + + return -ENOMEM; +} + static void free_gen8_temp_bitmaps(unsigned long *new_pds, unsigned long **new_pts, uint32_t pdpes) @@ -981,14 +1123,15 @@ static void mark_tlbs_dirty(struct i915_hw_ppgtt *ppgtt) ppgtt->pd_dirty_rings = INTEL_INFO(ppgtt->base.dev)->ring_mask; } -static int gen8_alloc_va_range(struct i915_address_space *vm, - uint64_t start, uint64_t length) +static int gen8_alloc_va_range_3lvl(struct i915_address_space *vm, + struct i915_page_directory_pointer *pdp, + uint64_t start, + uint64_t length) { struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); unsigned long *new_page_dirs, **new_page_tables; struct drm_device *dev = vm->dev; - struct i915_page_directory_pointer *pdp = &ppgtt->pdp; /* FIXME: 48b */ struct i915_page_directory *pd; const uint64_t orig_start = start; const uint64_t orig_length = length; @@ -1069,6 +1212,7 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, kunmap_px(ppgtt, page_directory); __set_bit(pdpe, pdp->used_pdpes); + gen8_setup_page_directory(ppgtt, pdp, pd, pdpe); } free_gen8_temp_bitmaps(new_page_dirs, new_page_tables, pdpes); @@ -1089,6 +1233,68 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, return ret; } +static int gen8_alloc_va_range_4lvl(struct i915_address_space *vm, + struct i915_pml4 *pml4, + uint64_t start, + uint64_t length) +{ + DECLARE_BITMAP(new_pdps, GEN8_PML4ES_PER_PML4); + struct i915_hw_ppgtt *ppgtt = + container_of(vm, struct i915_hw_ppgtt, base); + struct i915_page_directory_pointer *pdp; + uint64_t temp, pml4e; + int ret = 0; + + /* Do the pml4 allocations first, so we don't need to track the newly + * allocated tables below the pdp */ + bitmap_zero(new_pdps, GEN8_PML4ES_PER_PML4); + + /* The pagedirectory and pagetable allocations are done in the shared 3 + * and 4 level code. Just allocate the pdps. + */ + ret = gen8_ppgtt_alloc_page_dirpointers(vm, pml4, start, length, + new_pdps); + if (ret) + return ret; + + WARN(bitmap_weight(new_pdps, GEN8_PML4ES_PER_PML4) > 2, + "The allocation has spanned more than 512GB. " + "It is highly likely this is incorrect."); + + gen8_for_each_pml4e(pdp, pml4, start, length, temp, pml4e) { + WARN_ON(!pdp); + + ret = gen8_alloc_va_range_3lvl(vm, pdp, start, length); + if (ret) + goto err_out; + + gen8_setup_page_directory_pointer(ppgtt, pml4, pdp, pml4e); + } + + bitmap_or(pml4->used_pml4es, new_pdps, pml4->used_pml4es, + GEN8_PML4ES_PER_PML4); + + return 0; + +err_out: + for_each_set_bit(pml4e, new_pdps, GEN8_PML4ES_PER_PML4) + gen8_ppgtt_cleanup_3lvl(vm->dev, pml4->pdps[pml4e]); + + return ret; +} + +static int gen8_alloc_va_range(struct i915_address_space *vm, + uint64_t start, uint64_t length) +{ + struct i915_hw_ppgtt *ppgtt = + container_of(vm, struct i915_hw_ppgtt, base); + + if (USES_FULL_48BIT_PPGTT(vm->dev)) + return gen8_alloc_va_range_4lvl(vm, &ppgtt->pml4, start, length); + else + return gen8_alloc_va_range_3lvl(vm, &ppgtt->pdp, start, length); +} + /* * GEN8 legacy ppgtt programming is accomplished through a max 4 PDP registers * with a net effect resembling a 2-level page table in normal x86 terms. Each @@ -1114,9 +1320,14 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt) ppgtt->switch_mm = gen8_mm_switch; - if (!USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) { - ret = __pdp_init(false, &ppgtt->pdp); + if (USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) { + ret = setup_px(ppgtt->base.dev, &ppgtt->pml4); + if (ret) + goto free_scratch; + ppgtt->base.total = 1ULL << 48; + } else { + ret = __pdp_init(false, &ppgtt->pdp); if (ret) goto free_scratch; @@ -1128,10 +1339,10 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt) * 2GiB). */ ppgtt->base.total = to_i915(ppgtt->base.dev)->gtt.base.total; - } else { - ppgtt->base.total = 1ULL << 48; - ret = -EPERM; /* Not yet implemented */ - goto free_scratch; + + trace_i915_page_directory_pointer_entry_alloc(&ppgtt->base, + 0, 0, + GEN8_PML4E_SHIFT); } return 0; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index 04bc66f113a6..11d44b3d84a3 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -39,6 +39,8 @@ struct drm_i915_file_private; typedef uint32_t gen6_pte_t; typedef uint64_t gen8_pte_t; typedef uint64_t gen8_pde_t; +typedef uint64_t gen8_ppgtt_pdpe_t; +typedef uint64_t gen8_ppgtt_pml4e_t; #define gtt_total_entries(gtt) ((gtt).base.total >> PAGE_SHIFT) @@ -95,6 +97,7 @@ typedef uint64_t gen8_pde_t; */ #define GEN8_PML4ES_PER_PML4 512 #define GEN8_PML4E_SHIFT 39 +#define GEN8_PML4E_MASK (GEN8_PML4ES_PER_PML4 - 1) #define GEN8_PDPE_SHIFT 30 /* NB: GEN8_PDPE_MASK is untrue for 32b platforms, but it has no impact on 32b page * tables */ @@ -465,6 +468,15 @@ static inline uint32_t gen6_pde_index(uint32_t addr) temp = min(temp, length), \ start += temp, length -= temp) +#define gen8_for_each_pml4e(pdp, pml4, start, length, temp, iter) \ + for (iter = gen8_pml4e_index(start); \ + pdp = (pml4)->pdps[iter], \ + length > 0 && iter < GEN8_PML4ES_PER_PML4; \ + iter++, \ + temp = ALIGN(start+1, 1ULL << GEN8_PML4E_SHIFT) - start, \ + temp = min(temp, length), \ + start += temp, length -= temp) + static inline uint32_t gen8_pte_index(uint64_t address) { return i915_pte_index(address, GEN8_PDE_SHIFT); @@ -482,8 +494,7 @@ static inline uint32_t gen8_pdpe_index(uint64_t address) static inline uint32_t gen8_pml4e_index(uint64_t address) { - WARN_ON(1); /* For 64B */ - return 0; + return (address >> GEN8_PML4E_SHIFT) & GEN8_PML4E_MASK; } static inline size_t gen8_pte_count(uint64_t address, uint64_t length) diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index f230d7639000..e6b5c7470ba0 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -221,6 +221,14 @@ DEFINE_EVENT_PRINT(i915_px_entry, i915_page_directory_entry_alloc, __entry->vm, __entry->px, __entry->start, __entry->end) ); +DEFINE_EVENT_PRINT(i915_px_entry, i915_page_directory_pointer_entry_alloc, + TP_PROTO(struct i915_address_space *vm, u32 pml4e, u64 start, u64 pml4e_shift), + TP_ARGS(vm, pml4e, start, pml4e_shift), + + TP_printk("vm=%p, pml4e=%d (0x%llx-0x%llx)", + __entry->vm, __entry->px, __entry->start, __entry->end) +); + /* Avoid extra math because we only support two sizes. The format is defined by * bitmap_scnprintf. Each 32 bits is 8 HEX digits followed by comma */ #define TRACE_PT_SIZE(bits) \ From 2dba3239f5c7511ffac957887facd0a0c9d003a5 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Thu, 30 Jul 2015 11:06:23 +0100 Subject: [PATCH 008/145] drm/i915/gen8: Add 4 level switching infrastructure and lrc support In 64b (48bit canonical) PPGTT addressing, the PDP0 register contains the base address to PML4, while the other PDP registers are ignored. In LRC, the addressing mode must be specified in every context descriptor, and the base address to PML4 is stored in the reg state. v2: PML4 update in legacy context switch is left for historic reasons, the preferred mode of operation is with lrc context based submission. v3: s/gen8_map_page_directory/gen8_setup_page_directory and s/gen8_map_page_directory_pointer/gen8_setup_page_directory_pointer. Also, clflush will be needed for bxt. (Akash) v4: Squashed lrc-specific code and use a macro to set PML4 register. v5: Rebase after Mika's ppgtt cleanup / scratch merge patch series. PDP update in bb_start is only for legacy 32b mode. v6: Rebase after final merged version of Mika's ppgtt/scratch patches. v7: There is no need to update the pml4 register value in execlists_update_context. (Akash) v8: Move pd and pdp setup functions to a previous patch, they do not belong here. (Akash) v9: Check USES_FULL_48BIT_PPGTT instead of GEN8_CTX_ADDRESSING_MODE in gen8_emit_bb_start to check if emit pdps is needed. (Akash) Cc: Akash Goel Signed-off-by: Ben Widawsky Signed-off-by: Michel Thierry (v2+) Reviewed-by: Akash Goel Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 17 +++++--- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_lrc.c | 60 ++++++++++++++++++++--------- 3 files changed, 55 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 229a31760260..2ab3cefd60b3 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -650,8 +650,8 @@ static int gen8_write_pdp(struct drm_i915_gem_request *req, return 0; } -static int gen8_mm_switch(struct i915_hw_ppgtt *ppgtt, - struct drm_i915_gem_request *req) +static int gen8_legacy_mm_switch(struct i915_hw_ppgtt *ppgtt, + struct drm_i915_gem_request *req) { int i, ret; @@ -666,6 +666,12 @@ static int gen8_mm_switch(struct i915_hw_ppgtt *ppgtt, return 0; } +static int gen8_48b_mm_switch(struct i915_hw_ppgtt *ppgtt, + struct drm_i915_gem_request *req) +{ + return gen8_write_pdp(req, 0, px_dma(&ppgtt->pml4)); +} + static void gen8_ppgtt_clear_pte_range(struct i915_address_space *vm, struct i915_page_directory_pointer *pdp, uint64_t start, @@ -1318,14 +1324,13 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt) ppgtt->base.unbind_vma = ppgtt_unbind_vma; ppgtt->base.bind_vma = ppgtt_bind_vma; - ppgtt->switch_mm = gen8_mm_switch; - if (USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) { ret = setup_px(ppgtt->base.dev, &ppgtt->pml4); if (ret) goto free_scratch; ppgtt->base.total = 1ULL << 48; + ppgtt->switch_mm = gen8_48b_mm_switch; } else { ret = __pdp_init(false, &ppgtt->pdp); if (ret) @@ -1340,6 +1345,7 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt) */ ppgtt->base.total = to_i915(ppgtt->base.dev)->gtt.base.total; + ppgtt->switch_mm = gen8_legacy_mm_switch; trace_i915_page_directory_pointer_entry_alloc(&ppgtt->base, 0, 0, GEN8_PML4E_SHIFT); @@ -1537,8 +1543,9 @@ static void gen8_ppgtt_enable(struct drm_device *dev) int j; for_each_ring(ring, dev_priv, j) { + u32 four_level = USES_FULL_48BIT_PPGTT(dev) ? GEN8_GFX_PPGTT_48B : 0; I915_WRITE(RING_MODE_GEN7(ring), - _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE)); + _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE | four_level)); } } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 8e46c348366b..88f20cc5ab8e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1679,6 +1679,7 @@ enum skl_disp_power_wells { #define GFX_REPLAY_MODE (1<<11) #define GFX_PSMI_GRANULARITY (1<<10) #define GFX_PPGTT_ENABLE (1<<9) +#define GEN8_GFX_PPGTT_48B (1<<7) #define VLV_DISPLAY_BASE 0x180000 #define VLV_MIPI_BASE VLV_DISPLAY_BASE diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 5bc0ce1347ce..138964afd187 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -196,13 +196,21 @@ reg_state[CTX_PDP ## n ## _LDW+1] = lower_32_bits(_addr); \ } +#define ASSIGN_CTX_PML4(ppgtt, reg_state) { \ + reg_state[CTX_PDP0_UDW + 1] = upper_32_bits(px_dma(&ppgtt->pml4)); \ + reg_state[CTX_PDP0_LDW + 1] = lower_32_bits(px_dma(&ppgtt->pml4)); \ +} + enum { ADVANCED_CONTEXT = 0, - LEGACY_CONTEXT, + LEGACY_32B_CONTEXT, ADVANCED_AD_CONTEXT, LEGACY_64B_CONTEXT }; -#define GEN8_CTX_MODE_SHIFT 3 +#define GEN8_CTX_ADDRESSING_MODE_SHIFT 3 +#define GEN8_CTX_ADDRESSING_MODE(dev) (USES_FULL_48BIT_PPGTT(dev) ?\ + LEGACY_64B_CONTEXT :\ + LEGACY_32B_CONTEXT) enum { FAULT_AND_HANG = 0, FAULT_AND_HALT, /* Debug only */ @@ -273,7 +281,7 @@ static uint64_t execlists_ctx_descriptor(struct drm_i915_gem_request *rq) WARN_ON(lrca & 0xFFFFFFFF00000FFFULL); desc = GEN8_CTX_VALID; - desc |= LEGACY_CONTEXT << GEN8_CTX_MODE_SHIFT; + desc |= GEN8_CTX_ADDRESSING_MODE(dev) << GEN8_CTX_ADDRESSING_MODE_SHIFT; if (IS_GEN8(ctx_obj->base.dev)) desc |= GEN8_CTX_L3LLC_COHERENT; desc |= GEN8_CTX_PRIVILEGE; @@ -348,10 +356,12 @@ static int execlists_update_context(struct drm_i915_gem_request *rq) reg_state[CTX_RING_TAIL+1] = rq->tail; reg_state[CTX_RING_BUFFER_START+1] = i915_gem_obj_ggtt_offset(rb_obj); - /* True PPGTT with dynamic page allocation: update PDP registers and - * point the unallocated PDPs to the scratch page - */ - if (ppgtt) { + if (ppgtt && !USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) { + /* True 32b PPGTT with dynamic page allocation: update PDP + * registers and point the unallocated PDPs to scratch page. + * PML4 is allocated during ppgtt init, so this is not needed + * in 48-bit mode. + */ ASSIGN_CTX_PDP(ppgtt, reg_state, 3); ASSIGN_CTX_PDP(ppgtt, reg_state, 2); ASSIGN_CTX_PDP(ppgtt, reg_state, 1); @@ -1515,12 +1525,15 @@ static int gen8_emit_bb_start(struct drm_i915_gem_request *req, * Ideally, we should set Force PD Restore in ctx descriptor, * but we can't. Force Restore would be a second option, but * it is unsafe in case of lite-restore (because the ctx is - * not idle). */ + * not idle). PML4 is allocated during ppgtt init so this is + * not needed in 48-bit.*/ if (req->ctx->ppgtt && (intel_ring_flag(req->ring) & req->ctx->ppgtt->pd_dirty_rings)) { - ret = intel_logical_ring_emit_pdps(req); - if (ret) - return ret; + if (!USES_FULL_48BIT_PPGTT(req->i915)) { + ret = intel_logical_ring_emit_pdps(req); + if (ret) + return ret; + } req->ctx->ppgtt->pd_dirty_rings &= ~intel_ring_flag(req->ring); } @@ -2201,13 +2214,24 @@ populate_lr_context(struct intel_context *ctx, struct drm_i915_gem_object *ctx_o reg_state[CTX_PDP0_UDW] = GEN8_RING_PDP_UDW(ring, 0); reg_state[CTX_PDP0_LDW] = GEN8_RING_PDP_LDW(ring, 0); - /* With dynamic page allocation, PDPs may not be allocated at this point, - * Point the unallocated PDPs to the scratch page - */ - ASSIGN_CTX_PDP(ppgtt, reg_state, 3); - ASSIGN_CTX_PDP(ppgtt, reg_state, 2); - ASSIGN_CTX_PDP(ppgtt, reg_state, 1); - ASSIGN_CTX_PDP(ppgtt, reg_state, 0); + if (USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) { + /* 64b PPGTT (48bit canonical) + * PDP0_DESCRIPTOR contains the base address to PML4 and + * other PDP Descriptors are ignored. + */ + ASSIGN_CTX_PML4(ppgtt, reg_state); + } else { + /* 32b PPGTT + * PDP*_DESCRIPTOR contains the base address of space supported. + * With dynamic page allocation, PDPs may not be allocated at + * this point. Point the unallocated PDPs to the scratch page + */ + ASSIGN_CTX_PDP(ppgtt, reg_state, 3); + ASSIGN_CTX_PDP(ppgtt, reg_state, 2); + ASSIGN_CTX_PDP(ppgtt, reg_state, 1); + ASSIGN_CTX_PDP(ppgtt, reg_state, 0); + } + if (ring->id == RCS) { reg_state[CTX_LRI_HEADER_2] = MI_LOAD_REGISTER_IMM(1); reg_state[CTX_R_PWR_CLK_STATE] = GEN8_R_PWR_CLK_STATE; From 3387d433b0bbdda53ad51016ae9d10c6c17046a0 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Mon, 3 Aug 2015 09:52:47 +0100 Subject: [PATCH 009/145] drm/i915/gen8: Pass sg_iter through pte inserts As a step towards implementing 4 levels, while not discarding the existing pte insert functions, we need to pass the sg_iter through. The current function understands to the page directory granularity. An object's pages may span the page directory, and so using the iter directly as we write the PTEs allows the iterator to stay coherent through a VMA insert operation spanning multiple page table levels. v2: Rebase after s/page_tables/page_table/. v3: Rebase after Mika's ppgtt cleanup / scratch merge patch series; updated commit message (s/map/insert). v4: Rebase. Reviewed-by: Akash Goel (v3) Signed-off-by: Ben Widawsky Signed-off-by: Michel Thierry (v2+) Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 2ab3cefd60b3..408003445dd6 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -746,7 +746,7 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm, static void gen8_ppgtt_insert_pte_entries(struct i915_address_space *vm, struct i915_page_directory_pointer *pdp, - struct sg_table *pages, + struct sg_page_iter *sg_iter, uint64_t start, enum i915_cache_level cache_level) { @@ -756,11 +756,10 @@ gen8_ppgtt_insert_pte_entries(struct i915_address_space *vm, unsigned pdpe = start >> GEN8_PDPE_SHIFT & GEN8_PDPE_MASK; unsigned pde = start >> GEN8_PDE_SHIFT & GEN8_PDE_MASK; unsigned pte = start >> GEN8_PTE_SHIFT & GEN8_PTE_MASK; - struct sg_page_iter sg_iter; pt_vaddr = NULL; - for_each_sg_page(pages->sgl, &sg_iter, pages->nents, 0) { + while (__sg_page_iter_next(sg_iter)) { if (WARN_ON(pdpe >= GEN8_LEGACY_PDPES)) break; @@ -771,7 +770,7 @@ gen8_ppgtt_insert_pte_entries(struct i915_address_space *vm, } pt_vaddr[pte] = - gen8_pte_encode(sg_page_iter_dma_address(&sg_iter), + gen8_pte_encode(sg_page_iter_dma_address(sg_iter), cache_level, true); if (++pte == GEN8_PTES) { kunmap_px(ppgtt, pt_vaddr); @@ -797,8 +796,10 @@ static void gen8_ppgtt_insert_entries(struct i915_address_space *vm, struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); struct i915_page_directory_pointer *pdp = &ppgtt->pdp; /* FIXME: 48b */ + struct sg_page_iter sg_iter; - gen8_ppgtt_insert_pte_entries(vm, pdp, pages, start, cache_level); + __sg_page_iter_start(&sg_iter, pages->sgl, sg_nents(pages->sgl), 0); + gen8_ppgtt_insert_pte_entries(vm, pdp, &sg_iter, start, cache_level); } static void gen8_free_page_tables(struct drm_device *dev, From de5ba8eb9cefff78ac4951bd68633d2b7a0fc5c1 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Mon, 3 Aug 2015 09:53:27 +0100 Subject: [PATCH 010/145] drm/i915/gen8: Add 4 level support in insert_entries and clear_range When 48b is enabled, gen8_ppgtt_insert_entries needs to read the Page Map Level 4 (PML4), before it selects which Page Directory Pointer (PDP) it will write to. Similarly, gen8_ppgtt_clear_range needs to get the correct PDP/PD range. This patch was inspired by Ben's "Depend exclusively on map and unmap_vma". v2: Rebase after s/page_tables/page_table/. v3: Remove unnecessary pdpe loop in gen8_ppgtt_clear_range_4lvl and use clamp_pdp in gen8_ppgtt_insert_entries (Akash). v4: Merge gen8_ppgtt_clear_range_4lvl into gen8_ppgtt_clear_range to maintain symmetry with gen8_ppgtt_insert_entries (Akash). v5: Do not mix pages and bytes in insert_entries (Akash). v6: Prevent overflow in sg_nents << PAGE_SHIFT, when inserting 4GB at once. v7: Rebase after Mika's ppgtt cleanup / scratch merge patch series. Use gen8_px_index functions, and remove unnecessary number of pages parameter in insert_pte_entries. v8: Change gen8_ppgtt_clear_pte_range to stop at PDP boundary, instead of adding and extra clamp function; remove unnecessary pdp_start/pdp_len variables (Akash). v9: pages->orig_nents instead of sg_nents(pages->sgl) to get the length (Akash). v10: Remove pdp warning check ingen8_ppgtt_insert_pte_entries until this commit (Akash). Reviewed-by: Akash Goel (v9) Cc: Akash Goel Signed-off-by: Michel Thierry Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 52 ++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 408003445dd6..032801ef5025 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -681,9 +681,9 @@ static void gen8_ppgtt_clear_pte_range(struct i915_address_space *vm, struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); gen8_pte_t *pt_vaddr; - unsigned pdpe = start >> GEN8_PDPE_SHIFT & GEN8_PDPE_MASK; - unsigned pde = start >> GEN8_PDE_SHIFT & GEN8_PDE_MASK; - unsigned pte = start >> GEN8_PTE_SHIFT & GEN8_PTE_MASK; + unsigned pdpe = gen8_pdpe_index(start); + unsigned pde = gen8_pde_index(start); + unsigned pte = gen8_pte_index(start); unsigned num_entries = length >> PAGE_SHIFT; unsigned last_pte, i; @@ -722,7 +722,8 @@ static void gen8_ppgtt_clear_pte_range(struct i915_address_space *vm, pte = 0; if (++pde == I915_PDES) { - pdpe++; + if (++pdpe == I915_PDPES_PER_PDP(vm->dev)) + break; pde = 0; } } @@ -735,12 +736,21 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm, { struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); - struct i915_page_directory_pointer *pdp = &ppgtt->pdp; /* FIXME: 48b */ - gen8_pte_t scratch_pte = gen8_pte_encode(px_dma(vm->scratch_page), I915_CACHE_LLC, use_scratch); - gen8_ppgtt_clear_pte_range(vm, pdp, start, length, scratch_pte); + if (!USES_FULL_48BIT_PPGTT(vm->dev)) { + gen8_ppgtt_clear_pte_range(vm, &ppgtt->pdp, start, length, + scratch_pte); + } else { + uint64_t templ4, pml4e; + struct i915_page_directory_pointer *pdp; + + gen8_for_each_pml4e(pdp, &ppgtt->pml4, start, length, templ4, pml4e) { + gen8_ppgtt_clear_pte_range(vm, pdp, start, length, + scratch_pte); + } + } } static void @@ -753,16 +763,13 @@ gen8_ppgtt_insert_pte_entries(struct i915_address_space *vm, struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); gen8_pte_t *pt_vaddr; - unsigned pdpe = start >> GEN8_PDPE_SHIFT & GEN8_PDPE_MASK; - unsigned pde = start >> GEN8_PDE_SHIFT & GEN8_PDE_MASK; - unsigned pte = start >> GEN8_PTE_SHIFT & GEN8_PTE_MASK; + unsigned pdpe = gen8_pdpe_index(start); + unsigned pde = gen8_pde_index(start); + unsigned pte = gen8_pte_index(start); pt_vaddr = NULL; while (__sg_page_iter_next(sg_iter)) { - if (WARN_ON(pdpe >= GEN8_LEGACY_PDPES)) - break; - if (pt_vaddr == NULL) { struct i915_page_directory *pd = pdp->page_directory[pdpe]; struct i915_page_table *pt = pd->page_table[pde]; @@ -776,7 +783,8 @@ gen8_ppgtt_insert_pte_entries(struct i915_address_space *vm, kunmap_px(ppgtt, pt_vaddr); pt_vaddr = NULL; if (++pde == I915_PDES) { - pdpe++; + if (++pdpe == I915_PDPES_PER_PDP(vm->dev)) + break; pde = 0; } pte = 0; @@ -795,11 +803,23 @@ static void gen8_ppgtt_insert_entries(struct i915_address_space *vm, { struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); - struct i915_page_directory_pointer *pdp = &ppgtt->pdp; /* FIXME: 48b */ struct sg_page_iter sg_iter; __sg_page_iter_start(&sg_iter, pages->sgl, sg_nents(pages->sgl), 0); - gen8_ppgtt_insert_pte_entries(vm, pdp, &sg_iter, start, cache_level); + + if (!USES_FULL_48BIT_PPGTT(vm->dev)) { + gen8_ppgtt_insert_pte_entries(vm, &ppgtt->pdp, &sg_iter, start, + cache_level); + } else { + struct i915_page_directory_pointer *pdp; + uint64_t templ4, pml4e; + uint64_t length = (uint64_t)pages->orig_nents << PAGE_SHIFT; + + gen8_for_each_pml4e(pdp, &ppgtt->pml4, start, length, templ4, pml4e) { + gen8_ppgtt_insert_pte_entries(vm, pdp, &sg_iter, + start, cache_level); + } + } } static void gen8_free_page_tables(struct drm_device *dev, From 69ab76fd3d497816992b22dd201d2327cb921c94 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Wed, 29 Jul 2015 17:23:55 +0100 Subject: [PATCH 011/145] drm/i915/gen8: Initialize PDPs and PML4 Similar to PDs, while setting up a page directory pointer, make all entries of the pdp point to the scratch pd before mapping (and make all its entries point to the scratch page); this is to be safe in case of out of bound access or proactive prefetch. Also add a scratch pdp, which the PML4 entries point to. v2: Handle scratch_pdp allocation failure correctly, and keep initialize_px functions together (Akash) v3: Rebase after Mika's ppgtt cleanup / scratch merge patch series. Rely on the added macros to initialize the pdps. v4: Rebase after final merged version of Mika's ppgtt/scratch patches (and removed commit message part related to v3). v5: Update commit message to also mention PML4 table initialization and the new scratch pdp (Akash). Suggested-by: Akash Goel Signed-off-by: Michel Thierry Reviewed-by: Akash Goel Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 38 +++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_gem_gtt.h | 1 + 2 files changed, 39 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 032801ef5025..a4bdef30713c 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -596,6 +596,27 @@ static void free_pdp(struct drm_device *dev, } } +static void gen8_initialize_pdp(struct i915_address_space *vm, + struct i915_page_directory_pointer *pdp) +{ + gen8_ppgtt_pdpe_t scratch_pdpe; + + scratch_pdpe = gen8_pdpe_encode(px_dma(vm->scratch_pd), I915_CACHE_LLC); + + fill_px(vm->dev, pdp, scratch_pdpe); +} + +static void gen8_initialize_pml4(struct i915_address_space *vm, + struct i915_pml4 *pml4) +{ + gen8_ppgtt_pml4e_t scratch_pml4e; + + scratch_pml4e = gen8_pml4e_encode(px_dma(vm->scratch_pdp), + I915_CACHE_LLC); + + fill_px(vm->dev, pml4, scratch_pml4e); +} + static void gen8_setup_page_directory(struct i915_hw_ppgtt *ppgtt, struct i915_page_directory_pointer *pdp, @@ -860,8 +881,20 @@ static int gen8_init_scratch(struct i915_address_space *vm) return PTR_ERR(vm->scratch_pd); } + if (USES_FULL_48BIT_PPGTT(dev)) { + vm->scratch_pdp = alloc_pdp(dev); + if (IS_ERR(vm->scratch_pdp)) { + free_pd(dev, vm->scratch_pd); + free_pt(dev, vm->scratch_pt); + free_scratch_page(dev, vm->scratch_page); + return PTR_ERR(vm->scratch_pdp); + } + } + gen8_initialize_pt(vm, vm->scratch_pt); gen8_initialize_pd(vm, vm->scratch_pd); + if (USES_FULL_48BIT_PPGTT(dev)) + gen8_initialize_pdp(vm, vm->scratch_pdp); return 0; } @@ -870,6 +903,8 @@ static void gen8_free_scratch(struct i915_address_space *vm) { struct drm_device *dev = vm->dev; + if (USES_FULL_48BIT_PPGTT(dev)) + free_pdp(dev, vm->scratch_pdp); free_pd(dev, vm->scratch_pd); free_pt(dev, vm->scratch_pt); free_scratch_page(dev, vm->scratch_page); @@ -1071,6 +1106,7 @@ gen8_ppgtt_alloc_page_dirpointers(struct i915_address_space *vm, if (IS_ERR(pdp)) goto unwind_out; + gen8_initialize_pdp(vm, pdp); pml4->pdps[pml4e] = pdp; __set_bit(pml4e, new_pdps); trace_i915_page_directory_pointer_entry_alloc(vm, @@ -1350,6 +1386,8 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt) if (ret) goto free_scratch; + gen8_initialize_pml4(&ppgtt->base, &ppgtt->pml4); + ppgtt->base.total = 1ULL << 48; ppgtt->switch_mm = gen8_48b_mm_switch; } else { diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index 11d44b3d84a3..70c50e7c13f9 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -278,6 +278,7 @@ struct i915_address_space { struct i915_page_scratch *scratch_page; struct i915_page_table *scratch_pt; struct i915_page_directory *scratch_pd; + struct i915_page_directory_pointer *scratch_pdp; /* GEN8+ & 48b PPGTT */ /** * List of objects currently involved in rendering. From e1f123257a1f7d3af36a31a0fb2d4c6f40039fed Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Wed, 29 Jul 2015 17:23:56 +0100 Subject: [PATCH 012/145] drm/i915: Expand error state's address width to 64b v2: For semaphore errors, object is mapped to GGTT and offset will not be > 4GB, print only lower 32-bits (Akash) v3: Print gtt_offset in groups of 32-bit (Chris) Cc: Akash Goel Cc: Chris Wilson Signed-off-by: Ben Widawsky Signed-off-by: Michel Thierry Reviewed-by: Akash Goel Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 4 ++-- drivers/gpu/drm/i915/i915_gpu_error.c | 24 ++++++++++++++---------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 79d2dcb13bb0..91bd77864181 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -547,7 +547,7 @@ struct drm_i915_error_state { struct drm_i915_error_object { int page_count; - u32 gtt_offset; + u64 gtt_offset; u32 *pages[0]; } *ringbuffer, *batchbuffer, *wa_batchbuffer, *ctx, *hws_page; @@ -573,7 +573,7 @@ struct drm_i915_error_state { u32 size; u32 name; u32 rseqno[I915_NUM_RINGS], wseqno; - u32 gtt_offset; + u64 gtt_offset; u32 read_domains; u32 write_domain; s32 fence_reg:I915_MAX_NUM_FENCE_BITS; diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 41d0739e6fdf..0d0a7b1a6b4b 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -197,8 +197,9 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m, err_printf(m, " %s [%d]:\n", name, count); while (count--) { - err_printf(m, " %08x %8u %02x %02x [ ", - err->gtt_offset, + err_printf(m, " %08x_%08x %8u %02x %02x [ ", + upper_32_bits(err->gtt_offset), + lower_32_bits(err->gtt_offset), err->size, err->read_domains, err->write_domain); @@ -427,15 +428,17 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, err_printf(m, " (submitted by %s [%d])", error->ring[i].comm, error->ring[i].pid); - err_printf(m, " --- gtt_offset = 0x%08x\n", - obj->gtt_offset); + err_printf(m, " --- gtt_offset = 0x%08x %08x\n", + upper_32_bits(obj->gtt_offset), + lower_32_bits(obj->gtt_offset)); print_error_obj(m, obj); } obj = error->ring[i].wa_batchbuffer; if (obj) { err_printf(m, "%s (w/a) --- gtt_offset = 0x%08x\n", - dev_priv->ring[i].name, obj->gtt_offset); + dev_priv->ring[i].name, + lower_32_bits(obj->gtt_offset)); print_error_obj(m, obj); } @@ -454,14 +457,14 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, if ((obj = error->ring[i].ringbuffer)) { err_printf(m, "%s --- ringbuffer = 0x%08x\n", dev_priv->ring[i].name, - obj->gtt_offset); + lower_32_bits(obj->gtt_offset)); print_error_obj(m, obj); } if ((obj = error->ring[i].hws_page)) { err_printf(m, "%s --- HW Status = 0x%08x\n", dev_priv->ring[i].name, - obj->gtt_offset); + lower_32_bits(obj->gtt_offset)); offset = 0; for (elt = 0; elt < PAGE_SIZE/16; elt += 4) { err_printf(m, "[%04x] %08x %08x %08x %08x\n", @@ -477,13 +480,14 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, if ((obj = error->ring[i].ctx)) { err_printf(m, "%s --- HW Context = 0x%08x\n", dev_priv->ring[i].name, - obj->gtt_offset); + lower_32_bits(obj->gtt_offset)); print_error_obj(m, obj); } } if ((obj = error->semaphore_obj)) { - err_printf(m, "Semaphore page = 0x%08x\n", obj->gtt_offset); + err_printf(m, "Semaphore page = 0x%08x\n", + lower_32_bits(obj->gtt_offset)); for (elt = 0; elt < PAGE_SIZE/16; elt += 4) { err_printf(m, "[%04x] %08x %08x %08x %08x\n", elt * 4, @@ -591,7 +595,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv, int num_pages; bool use_ggtt; int i = 0; - u32 reloc_offset; + u64 reloc_offset; if (src == NULL || src->pages == NULL) return NULL; From ea91e401501a72597554cb5c080fe5671a6ccb84 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Wed, 29 Jul 2015 17:23:57 +0100 Subject: [PATCH 013/145] drm/i915/gen8: Add ppgtt info and debug_dump v2: Clean up patch after rebases. v3: gen8_dump_ppgtt for 32b and 48b PPGTT. v4: Use used_pml4es/pdpes (Akash). v5: Rebase after Mika's ppgtt cleanup / scratch merge patch series. v6: Rely on used_px bits instead of null checking (Akash) Cc: Akash Goel Signed-off-by: Ben Widawsky Signed-off-by: Michel Thierry (v2+) Reviewed-by: Akash Goel Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 18 ++++--- drivers/gpu/drm/i915/i915_gem_gtt.c | 84 +++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index d1c643a82267..95e7b82f05d1 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2250,7 +2250,6 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_engine_cs *ring; - struct drm_file *file; int i; if (INTEL_INFO(dev)->gen == 6) @@ -2273,13 +2272,6 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev) ppgtt->debug_dump(ppgtt, m); } - list_for_each_entry_reverse(file, &dev->filelist, lhead) { - struct drm_i915_file_private *file_priv = file->driver_priv; - - seq_printf(m, "proc: %s\n", - get_pid_task(file->pid, PIDTYPE_PID)->comm); - idr_for_each(&file_priv->context_idr, per_file_ctx, m); - } seq_printf(m, "ECOCHK: 0x%08x\n", I915_READ(GAM_ECOCHK)); } @@ -2288,6 +2280,7 @@ static int i915_ppgtt_info(struct seq_file *m, void *data) struct drm_info_node *node = m->private; struct drm_device *dev = node->minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_file *file; int ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) @@ -2299,6 +2292,15 @@ static int i915_ppgtt_info(struct seq_file *m, void *data) else if (INTEL_INFO(dev)->gen >= 6) gen6_ppgtt_info(m, dev); + list_for_each_entry_reverse(file, &dev->filelist, lhead) { + struct drm_i915_file_private *file_priv = file->driver_priv; + + seq_printf(m, "\nproc: %s\n", + get_pid_task(file->pid, PIDTYPE_PID)->comm); + idr_for_each(&file_priv->context_idr, per_file_ctx, + (void *)(unsigned long)m); + } + intel_runtime_pm_put(dev_priv); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index a4bdef30713c..38439fa339a9 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -1358,6 +1358,89 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, return gen8_alloc_va_range_3lvl(vm, &ppgtt->pdp, start, length); } +static void gen8_dump_pdp(struct i915_page_directory_pointer *pdp, + uint64_t start, uint64_t length, + gen8_pte_t scratch_pte, + struct seq_file *m) +{ + struct i915_page_directory *pd; + uint64_t temp; + uint32_t pdpe; + + gen8_for_each_pdpe(pd, pdp, start, length, temp, pdpe) { + struct i915_page_table *pt; + uint64_t pd_len = length; + uint64_t pd_start = start; + uint32_t pde; + + if (!test_bit(pdpe, pdp->used_pdpes)) + continue; + + seq_printf(m, "\tPDPE #%d\n", pdpe); + gen8_for_each_pde(pt, pd, pd_start, pd_len, temp, pde) { + uint32_t pte; + gen8_pte_t *pt_vaddr; + + if (!test_bit(pde, pd->used_pdes)) + continue; + + pt_vaddr = kmap_px(pt); + for (pte = 0; pte < GEN8_PTES; pte += 4) { + uint64_t va = + (pdpe << GEN8_PDPE_SHIFT) | + (pde << GEN8_PDE_SHIFT) | + (pte << GEN8_PTE_SHIFT); + int i; + bool found = false; + + for (i = 0; i < 4; i++) + if (pt_vaddr[pte + i] != scratch_pte) + found = true; + if (!found) + continue; + + seq_printf(m, "\t\t0x%llx [%03d,%03d,%04d]: =", va, pdpe, pde, pte); + for (i = 0; i < 4; i++) { + if (pt_vaddr[pte + i] != scratch_pte) + seq_printf(m, " %llx", pt_vaddr[pte + i]); + else + seq_puts(m, " SCRATCH "); + } + seq_puts(m, "\n"); + } + /* don't use kunmap_px, it could trigger + * an unnecessary flush. + */ + kunmap_atomic(pt_vaddr); + } + } +} + +static void gen8_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m) +{ + struct i915_address_space *vm = &ppgtt->base; + uint64_t start = ppgtt->base.start; + uint64_t length = ppgtt->base.total; + gen8_pte_t scratch_pte = gen8_pte_encode(px_dma(vm->scratch_page), + I915_CACHE_LLC, true); + + if (!USES_FULL_48BIT_PPGTT(vm->dev)) { + gen8_dump_pdp(&ppgtt->pdp, start, length, scratch_pte, m); + } else { + uint64_t templ4, pml4e; + struct i915_pml4 *pml4 = &ppgtt->pml4; + struct i915_page_directory_pointer *pdp; + + gen8_for_each_pml4e(pdp, pml4, start, length, templ4, pml4e) { + if (!test_bit(pml4e, pml4->used_pml4es)) + continue; + + seq_printf(m, " PML4E #%llu\n", pml4e); + gen8_dump_pdp(pdp, start, length, scratch_pte, m); + } + } +} + /* * GEN8 legacy ppgtt programming is accomplished through a max 4 PDP registers * with a net effect resembling a 2-level page table in normal x86 terms. Each @@ -1380,6 +1463,7 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt) ppgtt->base.clear_range = gen8_ppgtt_clear_range; ppgtt->base.unbind_vma = ppgtt_unbind_vma; ppgtt->base.bind_vma = ppgtt_bind_vma; + ppgtt->debug_dump = gen8_dump_ppgtt; if (USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) { ret = setup_px(ppgtt->base.dev, &ppgtt->pml4); From 65bd342ff2f1f9f3ca4a17691a825935d2aa13c9 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Wed, 29 Jul 2015 17:23:58 +0100 Subject: [PATCH 014/145] drm/i915: object size needs to be u64 In a 48b world, users can try to allocate buffers bigger than 4GB; in these cases it is important that size is a 64b variable. v2: Drop the warning about bind with size 0, it shouldn't happen anyway. Signed-off-by: Michel Thierry Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 84f91bcc12f7..73293b487217 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3355,7 +3355,8 @@ i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj, { struct drm_device *dev = obj->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 size, fence_size, fence_alignment, unfenced_alignment; + u32 fence_alignment, unfenced_alignment; + u64 size, fence_size; u64 start = flags & PIN_OFFSET_BIAS ? flags & PIN_OFFSET_MASK : 0; u64 end = @@ -3414,7 +3415,7 @@ i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj, * attempt to find space. */ if (size > end) { - DRM_DEBUG("Attempting to bind an object (view type=%u) larger than the aperture: size=%u > %s aperture=%llu\n", + DRM_DEBUG("Attempting to bind an object (view type=%u) larger than the aperture: size=%llu > %s aperture=%llu\n", ggtt_view ? ggtt_view->type : 0, size, flags & PIN_MAPPABLE ? "mappable" : "total", From af98714e5d05fcbf7638ea4b53d158d96d693bf2 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Wed, 29 Jul 2015 17:23:59 +0100 Subject: [PATCH 015/145] drm/i915: batch_obj vm offset must be u64 Otherwise it can overflow in 48-bit mode, and cause an incorrect exec_start. Before commit 5f19e2bffa63a91cd4ac1adcec648e14a44277ce ("drm/i915: Merged the many do_execbuf() parameters into a structure"), it was already an u64. Signed-off-by: Michel Thierry Reviewed-by: Akash Goel Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 91bd77864181..cefb6e96a941 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1686,7 +1686,7 @@ struct i915_execbuffer_params { struct drm_file *file; uint32_t dispatch_flags; uint32_t args_batch_start_offset; - uint32_t batch_obj_vm_offset; + uint64_t batch_obj_vm_offset; struct intel_engine_cs *ring; struct drm_i915_gem_object *batch_obj; struct intel_context *ctx; From c6d576cc576196593671eabc8aad6c1744b32568 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Wed, 29 Jul 2015 17:24:00 +0100 Subject: [PATCH 016/145] drm/i915/userptr: Kill user_size limit check GTT was only 32b and its max value is 4GB. In order to allow objects bigger than 4GB in 48b PPGTT, i915_gem_userptr_ioctl we could check against max 48b range (1ULL << 48). But since the check no longer applies, just kill the limit. v2: Use the default ctx to infer the ppgtt max size (Akash). v3: Just kill the limit, it was only there for early detection of an error when used for execbuffer (Chris). Cc: Akash Goel Reviewed-by: Chris Wilson Signed-off-by: Michel Thierry Reviewed-by: Akash Goel Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_userptr.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c index 8fd431bcdfd3..d11901d590ac 100644 --- a/drivers/gpu/drm/i915/i915_gem_userptr.c +++ b/drivers/gpu/drm/i915/i915_gem_userptr.c @@ -813,7 +813,6 @@ static const struct drm_i915_gem_object_ops i915_gem_userptr_ops = { int i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_userptr *args = data; struct drm_i915_gem_object *obj; int ret; @@ -826,9 +825,6 @@ i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file if (offset_in_page(args->user_ptr | args->user_size)) return -EINVAL; - if (args->user_size > dev_priv->gtt.base.total) - return -E2BIG; - if (!access_ok(args->flags & I915_USERPTR_READ_ONLY ? VERIFY_READ : VERIFY_WRITE, (char __user *)(unsigned long)args->user_ptr, args->user_size)) return -EFAULT; From e5a1cab5e57d7bd6ef77502f950cfab6e14d92a8 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 23 Jul 2015 16:35:48 -0700 Subject: [PATCH 017/145] drm/i915: Force sink crc stop before start. By Vesa DP spec, test counter at DP_TEST_SINK_MISC just reset to 0 when unsetting DP_TEST_SINK_START, so let's force this stop here. But let's minimize the aux transactions and just do it when we know it hasn't been properly stoped. Signed-off-by: Rodrigo Vivi Reviewed-by: Rafael Antognolli Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 22 +++++++++++++++++++--- drivers/gpu/drm/i915/intel_drv.h | 1 + 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 016e7bc6af0a..1f7cc404f3b7 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -3995,22 +3995,30 @@ intel_dp_probe_mst(struct intel_dp *intel_dp) return intel_dp->is_mst; } -static void intel_dp_sink_crc_stop(struct intel_dp *intel_dp) +static int intel_dp_sink_crc_stop(struct intel_dp *intel_dp) { struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); struct intel_crtc *intel_crtc = to_intel_crtc(dig_port->base.base.crtc); u8 buf; + int ret = 0; if (drm_dp_dpcd_readb(&intel_dp->aux, DP_TEST_SINK, &buf) < 0) { DRM_DEBUG_KMS("Sink CRC couldn't be stopped properly\n"); - return; + ret = -EIO; + goto out; } if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_SINK, - buf & ~DP_TEST_SINK_START) < 0) + buf & ~DP_TEST_SINK_START) < 0) { DRM_DEBUG_KMS("Sink CRC couldn't be stopped properly\n"); + ret = -EIO; + goto out; + } + intel_dp->sink_crc_started = false; + out: hsw_enable_ips(intel_crtc); + return ret; } static int intel_dp_sink_crc_start(struct intel_dp *intel_dp) @@ -4018,6 +4026,13 @@ static int intel_dp_sink_crc_start(struct intel_dp *intel_dp) struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); struct intel_crtc *intel_crtc = to_intel_crtc(dig_port->base.base.crtc); u8 buf; + int ret; + + if (intel_dp->sink_crc_started) { + ret = intel_dp_sink_crc_stop(intel_dp); + if (ret) + return ret; + } if (drm_dp_dpcd_readb(&intel_dp->aux, DP_TEST_SINK_MISC, &buf) < 0) return -EIO; @@ -4036,6 +4051,7 @@ static int intel_dp_sink_crc_start(struct intel_dp *intel_dp) return -EIO; } + intel_dp->sink_crc_started = true; return 0; } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index e31b95bf6966..8858bb18d18e 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -713,6 +713,7 @@ struct intel_dp { /* sink rates as reported by DP_SUPPORTED_LINK_RATES */ uint8_t num_sink_rates; int sink_rates[DP_MAX_SUPPORTED_RATES]; + bool sink_crc_started; struct drm_dp_aux aux; uint8_t train_set[4]; int panel_power_up_delay; From 621d4c76fd4ce7f648fa59a26e3eb6b2346cf873 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 23 Jul 2015 16:35:49 -0700 Subject: [PATCH 018/145] drm/i915: Save latest known sink CRC to compensate delayed counter reset. By Vesa DP 1.2 Spec TEST_CRC_COUNT should be "reset to 0 when TEST_SINK bit 0 = 0." However for some strange reason when PSR is enabled in certain platforms this is not true. At least not immediatelly. So we face cases like this: first get_sink_crc operation: count: 0, crc: 000000000000 count: 1, crc: c101c101c101 returned expected crc: c101c101c101 secont get_sink_crc operation: count: 1, crc: c101c101c101 count: 0, crc: 000000000000 count: 1, crc: 0000c1010000 should return expected crc: 0000c1010000 But also the reset to 0 should be faster resulting into: get_sink_crc operation: count: 1, crc: c101c101c101 count: 1, crc: 0000c1010000 should return expected crc: 0000c1010000 So in order to know that the second one is valid one we need to compare the pair (count, crc) with latest (count, crc). If the pair changed you have your valid CRC. Signed-off-by: Rodrigo Vivi Reviewed-by: Rafael Antognolli Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 42 ++++++++++++++++++++------------ drivers/gpu/drm/i915/intel_drv.h | 8 +++++- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 1f7cc404f3b7..abcc2a36aa95 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4015,7 +4015,7 @@ static int intel_dp_sink_crc_stop(struct intel_dp *intel_dp) goto out; } - intel_dp->sink_crc_started = false; + intel_dp->sink_crc.started = false; out: hsw_enable_ips(intel_crtc); return ret; @@ -4028,7 +4028,7 @@ static int intel_dp_sink_crc_start(struct intel_dp *intel_dp) u8 buf; int ret; - if (intel_dp->sink_crc_started) { + if (intel_dp->sink_crc.started) { ret = intel_dp_sink_crc_stop(intel_dp); if (ret) return ret; @@ -4040,6 +4040,8 @@ static int intel_dp_sink_crc_start(struct intel_dp *intel_dp) if (!(buf & DP_TEST_CRC_SUPPORTED)) return -ENOTTY; + intel_dp->sink_crc.last_count = buf & DP_TEST_COUNT_MASK; + if (drm_dp_dpcd_readb(&intel_dp->aux, DP_TEST_SINK, &buf) < 0) return -EIO; @@ -4051,7 +4053,7 @@ static int intel_dp_sink_crc_start(struct intel_dp *intel_dp) return -EIO; } - intel_dp->sink_crc_started = true; + intel_dp->sink_crc.started = true; return 0; } @@ -4061,29 +4063,39 @@ int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc) struct drm_device *dev = dig_port->base.base.dev; struct intel_crtc *intel_crtc = to_intel_crtc(dig_port->base.base.crtc); u8 buf; - int test_crc_count; + int count, ret; int attempts = 6; - int ret; ret = intel_dp_sink_crc_start(intel_dp); if (ret) return ret; - if (drm_dp_dpcd_readb(&intel_dp->aux, DP_TEST_SINK_MISC, &buf) < 0) { - ret = -EIO; - goto stop; - } - - test_crc_count = buf & DP_TEST_COUNT_MASK; - do { + intel_wait_for_vblank(dev, intel_crtc->pipe); + if (drm_dp_dpcd_readb(&intel_dp->aux, DP_TEST_SINK_MISC, &buf) < 0) { ret = -EIO; goto stop; } - intel_wait_for_vblank(dev, intel_crtc->pipe); - } while (--attempts && (buf & DP_TEST_COUNT_MASK) == test_crc_count); + count = buf & DP_TEST_COUNT_MASK; + /* + * Count might be reset during the loop. In this case + * last known count needs to be reset as well. + */ + if (count == 0) + intel_dp->sink_crc.last_count = 0; + + if (drm_dp_dpcd_read(&intel_dp->aux, DP_TEST_CRC_R_CR, crc, 6) < 0) { + ret = -EIO; + goto stop; + } + } while (--attempts && (count == 0 || (count == intel_dp->sink_crc.last_count && + !memcmp(intel_dp->sink_crc.last_crc, crc, + 6 * sizeof(u8))))); + + intel_dp->sink_crc.last_count = buf & DP_TEST_COUNT_MASK; + memcpy(intel_dp->sink_crc.last_crc, crc, 6 * sizeof(u8)); if (attempts == 0) { DRM_DEBUG_KMS("Panel is unable to calculate CRC after 6 vblanks\n"); @@ -4091,8 +4103,6 @@ int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc) goto stop; } - if (drm_dp_dpcd_read(&intel_dp->aux, DP_TEST_CRC_R_CR, crc, 6) < 0) - ret = -EIO; stop: intel_dp_sink_crc_stop(intel_dp); return ret; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8858bb18d18e..6b3e2c6e6c54 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -696,6 +696,12 @@ enum link_m_n_set { M2_N2 }; +struct sink_crc { + bool started; + u8 last_crc[6]; + int last_count; +}; + struct intel_dp { uint32_t output_reg; uint32_t aux_ch_ctl_reg; @@ -713,7 +719,7 @@ struct intel_dp { /* sink rates as reported by DP_SUPPORTED_LINK_RATES */ uint8_t num_sink_rates; int sink_rates[DP_MAX_SUPPORTED_RATES]; - bool sink_crc_started; + struct sink_crc sink_crc; struct drm_dp_aux aux; uint8_t train_set[4]; int panel_power_up_delay; From aabc95dcf200908993573b2019ff5210121bcba9 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 23 Jul 2015 16:35:50 -0700 Subject: [PATCH 019/145] drm/i915: Dont -ETIMEDOUT on identical new and previous (count, crc). By Vesa DP 1.2 spec TEST_CRC_COUNT is a "4 bit wrap counter which increments each time the TEST_CRC_x_x are updated." However if we are trying to verify the screen hasn't changed we get same (count, crc) pair twice. Without this patch we would return -ETIMEOUT in this case. So, if in 6 vblanks the pair (count, crc) hasn't changed we return it anyway instead of returning error and let test case decide if it was right or not. Signed-off-by: Rodrigo Vivi Reviewed-by: Rafael Antognolli Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index abcc2a36aa95..54e9b5380aa7 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4065,6 +4065,7 @@ int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc) u8 buf; int count, ret; int attempts = 6; + bool old_equal_new; ret = intel_dp_sink_crc_start(intel_dp); if (ret) @@ -4079,6 +4080,7 @@ int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc) goto stop; } count = buf & DP_TEST_COUNT_MASK; + /* * Count might be reset during the loop. In this case * last known count needs to be reset as well. @@ -4090,17 +4092,24 @@ int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc) ret = -EIO; goto stop; } - } while (--attempts && (count == 0 || (count == intel_dp->sink_crc.last_count && - !memcmp(intel_dp->sink_crc.last_crc, crc, - 6 * sizeof(u8))))); + + old_equal_new = (count == intel_dp->sink_crc.last_count && + !memcmp(intel_dp->sink_crc.last_crc, crc, + 6 * sizeof(u8))); + + } while (--attempts && (count == 0 || old_equal_new)); intel_dp->sink_crc.last_count = buf & DP_TEST_COUNT_MASK; memcpy(intel_dp->sink_crc.last_crc, crc, 6 * sizeof(u8)); if (attempts == 0) { - DRM_DEBUG_KMS("Panel is unable to calculate CRC after 6 vblanks\n"); - ret = -ETIMEDOUT; - goto stop; + if (old_equal_new) { + DRM_DEBUG_KMS("Unreliable Sink CRC counter: Current returned CRC is identical to the previous one\n"); + } else { + DRM_ERROR("Panel is unable to calculate any CRC after 6 vblanks\n"); + ret = -ETIMEDOUT; + goto stop; + } } stop: From 088e0df4020e30c4952f29dc672ceb4742b98e73 Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Fri, 7 Aug 2015 17:40:17 +0100 Subject: [PATCH 020/145] drm/i915/gtt: Allow >= 4GB offsets in X86_32 Similar to commit c44ef60e437019b8ca1dab8b4d2e8761fd4ce1e9 ("drm/i915/gtt: Allow >= 4GB sizes for vm"), i915_gem_obj_offset and i915_gem_obj_ggtt_offset return an unsigned long, which in only 4-bytes long in 32-bit kernels. Change return type (and other related offset variables) to u64. Since Global GTT is always limited to 4GB, this change would not be required in i915_gem_obj_ggtt_offset, but this is done for consistency. v2: Remove unnecessary offset variable in do_pin, as we already have vma->node.start (Chris). Update GGTT offset too (Tvrtko). Cc: Chris Wilson Cc: Daniel Vetter Cc: Tvrtko Ursulin Signed-off-by: Michel Thierry Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 12 +++++------- drivers/gpu/drm/i915/i915_gem.c | 18 +++++++----------- drivers/gpu/drm/i915/i915_gem_fence.c | 4 ++-- drivers/gpu/drm/i915/i915_gem_gtt.c | 8 ++++---- drivers/gpu/drm/i915/i915_gem_gtt.h | 2 +- drivers/gpu/drm/i915/intel_fbdev.c | 2 +- 6 files changed, 20 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index cefb6e96a941..6b85338c4b89 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2977,13 +2977,11 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, struct dma_buf *i915_gem_prime_export(struct drm_device *dev, struct drm_gem_object *gem_obj, int flags); -unsigned long -i915_gem_obj_ggtt_offset_view(struct drm_i915_gem_object *o, - const struct i915_ggtt_view *view); -unsigned long -i915_gem_obj_offset(struct drm_i915_gem_object *o, - struct i915_address_space *vm); -static inline unsigned long +u64 i915_gem_obj_ggtt_offset_view(struct drm_i915_gem_object *o, + const struct i915_ggtt_view *view); +u64 i915_gem_obj_offset(struct drm_i915_gem_object *o, + struct i915_address_space *vm); +static inline u64 i915_gem_obj_ggtt_offset(struct drm_i915_gem_object *o) { return i915_gem_obj_ggtt_offset_view(o, &i915_ggtt_view_normal); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 73293b487217..6d0f8349a91c 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -4012,15 +4012,13 @@ i915_gem_object_do_pin(struct drm_i915_gem_object *obj, return -EBUSY; if (i915_vma_misplaced(vma, alignment, flags)) { - unsigned long offset; - offset = ggtt_view ? i915_gem_obj_ggtt_offset_view(obj, ggtt_view) : - i915_gem_obj_offset(obj, vm); WARN(vma->pin_count, "bo is already pinned in %s with incorrect alignment:" - " offset=%lx, req.alignment=%x, req.map_and_fenceable=%d," + " offset=%08x %08x, req.alignment=%x, req.map_and_fenceable=%d," " obj->map_and_fenceable=%d\n", ggtt_view ? "ggtt" : "ppgtt", - offset, + upper_32_bits(vma->node.start), + lower_32_bits(vma->node.start), alignment, !!(flags & PIN_MAPPABLE), obj->map_and_fenceable); @@ -4975,9 +4973,8 @@ void i915_gem_track_fb(struct drm_i915_gem_object *old, } /* All the new VM stuff */ -unsigned long -i915_gem_obj_offset(struct drm_i915_gem_object *o, - struct i915_address_space *vm) +u64 i915_gem_obj_offset(struct drm_i915_gem_object *o, + struct i915_address_space *vm) { struct drm_i915_private *dev_priv = o->base.dev->dev_private; struct i915_vma *vma; @@ -4997,9 +4994,8 @@ i915_gem_obj_offset(struct drm_i915_gem_object *o, return -1; } -unsigned long -i915_gem_obj_ggtt_offset_view(struct drm_i915_gem_object *o, - const struct i915_ggtt_view *view) +u64 i915_gem_obj_ggtt_offset_view(struct drm_i915_gem_object *o, + const struct i915_ggtt_view *view) { struct i915_address_space *ggtt = i915_obj_to_ggtt(o); struct i915_vma *vma; diff --git a/drivers/gpu/drm/i915/i915_gem_fence.c b/drivers/gpu/drm/i915/i915_gem_fence.c index af1f8c461060..6077dffb318a 100644 --- a/drivers/gpu/drm/i915/i915_gem_fence.c +++ b/drivers/gpu/drm/i915/i915_gem_fence.c @@ -128,7 +128,7 @@ static void i915_write_fence_reg(struct drm_device *dev, int reg, WARN((i915_gem_obj_ggtt_offset(obj) & ~I915_FENCE_START_MASK) || (size & -size) != size || (i915_gem_obj_ggtt_offset(obj) & (size - 1)), - "object 0x%08lx [fenceable? %d] not 1M or pot-size (0x%08x) aligned\n", + "object 0x%08llx [fenceable? %d] not 1M or pot-size (0x%08x) aligned\n", i915_gem_obj_ggtt_offset(obj), obj->map_and_fenceable, size); if (obj->tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev)) @@ -171,7 +171,7 @@ static void i830_write_fence_reg(struct drm_device *dev, int reg, WARN((i915_gem_obj_ggtt_offset(obj) & ~I830_FENCE_START_MASK) || (size & -size) != size || (i915_gem_obj_ggtt_offset(obj) & (size - 1)), - "object 0x%08lx not 512K or pot-size 0x%08x aligned\n", + "object 0x%08llx not 512K or pot-size 0x%08x aligned\n", i915_gem_obj_ggtt_offset(obj), size); pitch_val = obj->stride / 128; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 38439fa339a9..7a89e97cbd3f 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -2543,9 +2543,9 @@ static void i915_gtt_color_adjust(struct drm_mm_node *node, } static int i915_gem_setup_global_gtt(struct drm_device *dev, - unsigned long start, - unsigned long mappable_end, - unsigned long end) + u64 start, + u64 mappable_end, + u64 end) { /* Let GEM Manage all of the aperture. * @@ -2584,7 +2584,7 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev, list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) { struct i915_vma *vma = i915_gem_obj_to_vma(obj, ggtt_vm); - DRM_DEBUG_KMS("reserving preallocated space: %lx + %zx\n", + DRM_DEBUG_KMS("reserving preallocated space: %llx + %zx\n", i915_gem_obj_ggtt_offset(obj), obj->base.size); WARN_ON(i915_gem_obj_ggtt_bound(obj)); diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index 70c50e7c13f9..82750073d5b3 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -149,7 +149,7 @@ struct i915_ggtt_view { union { struct { - unsigned long offset; + u64 offset; unsigned int size; } partial; } params; diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 7eff33ff84f6..6c9351b2e3af 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -295,7 +295,7 @@ static int intelfb_create(struct drm_fb_helper *helper, /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ - DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08lx, bo %p\n", + DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08llx, bo %p\n", fb->width, fb->height, i915_gem_obj_ggtt_offset(obj), obj); From f365f91181ce862181d0d2f4fb3ca0fe2276874b Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Fri, 7 Aug 2015 17:40:18 +0100 Subject: [PATCH 021/145] drm/i915: Use complete virtual address range on 32-bit platforms With the offset length being taken care of in ("drm/i915/gtt: Allow >= 4GB offsets in X86_32"), the code should be finally safe in 32-bit kernels. This reverts commit 501fd70fcaebc911b6b96a7b331e6960e5af67e7 Author: Michel Thierry Date: Fri May 29 14:15:05 2015 +0100 drm/i915: limit PPGTT size to 2GB in 32-bit platforms Signed-off-by: Michel Thierry Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 7a89e97cbd3f..8fda1fb7e506 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -1480,14 +1480,6 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt) goto free_scratch; ppgtt->base.total = 1ULL << 32; - if (IS_ENABLED(CONFIG_X86_32)) - /* While we have a proliferation of size_t variables - * we cannot represent the full ppgtt size on 32bit, - * so limit it to the same size as the GGTT (currently - * 2GiB). - */ - ppgtt->base.total = to_i915(ppgtt->base.dev)->gtt.base.total; - ppgtt->switch_mm = gen8_legacy_mm_switch; trace_i915_page_directory_pointer_entry_alloc(&ppgtt->base, 0, 0, From 25f5033761f85a659db729a10b59d80f93a6be0d Mon Sep 17 00:00:00 2001 From: Michel Thierry Date: Fri, 7 Aug 2015 17:40:19 +0100 Subject: [PATCH 022/145] drm/i915: Always pass dev pointer in pdp_init And fix 0-DAY kernel test infrastructure warning. Reported-by: Fengguang Wu Signed-off-by: Michel Thierry Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 8fda1fb7e506..4a76807143b1 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -1475,7 +1475,7 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt) ppgtt->base.total = 1ULL << 48; ppgtt->switch_mm = gen8_48b_mm_switch; } else { - ret = __pdp_init(false, &ppgtt->pdp); + ret = __pdp_init(ppgtt->base.dev, &ppgtt->pdp); if (ret) goto free_scratch; From 7f3561bec7cb4e4804bc246069420d3f2f7f3ebe Mon Sep 17 00:00:00 2001 From: Sonika Jindal Date: Mon, 10 Aug 2015 10:35:35 +0530 Subject: [PATCH 023/145] drm/i915/bxt: Add HPD support for DDIA Also remove redundant comments. Signed-off-by: Sonika Jindal Reviewed-by: Sivakumar Thulasimani Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 1118c39281f9..a05104562932 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -90,6 +90,7 @@ static const u32 hpd_status_i915[HPD_NUM_PINS] = { /* BXT hpd list */ static const u32 hpd_bxt[HPD_NUM_PINS] = { + [HPD_PORT_A] = BXT_DE_PORT_HP_DDIA, [HPD_PORT_B] = BXT_DE_PORT_HP_DDIB, [HPD_PORT_C] = BXT_DE_PORT_HP_DDIC }; @@ -3014,30 +3015,25 @@ static void bxt_hpd_irq_setup(struct drm_device *dev) u32 hotplug_port = 0; u32 hotplug_ctrl; - /* Now, enable HPD */ for_each_intel_encoder(dev, intel_encoder) { if (dev_priv->hotplug.stats[intel_encoder->hpd_pin].state == HPD_ENABLED) hotplug_port |= hpd_bxt[intel_encoder->hpd_pin]; } - /* Mask all HPD control bits */ hotplug_ctrl = I915_READ(BXT_HOTPLUG_CTL) & ~BXT_HOTPLUG_CTL_MASK; - /* Enable requested port in hotplug control */ - /* TODO: implement (short) HPD support on port A */ - WARN_ON_ONCE(hotplug_port & BXT_DE_PORT_HP_DDIA); + if (hotplug_port & BXT_DE_PORT_HP_DDIA) + hotplug_ctrl |= BXT_DDIA_HPD_ENABLE; if (hotplug_port & BXT_DE_PORT_HP_DDIB) hotplug_ctrl |= BXT_DDIB_HPD_ENABLE; if (hotplug_port & BXT_DE_PORT_HP_DDIC) hotplug_ctrl |= BXT_DDIC_HPD_ENABLE; I915_WRITE(BXT_HOTPLUG_CTL, hotplug_ctrl); - /* Unmask DDI hotplug in IMR */ hotplug_ctrl = I915_READ(GEN8_DE_PORT_IMR) & ~hotplug_port; I915_WRITE(GEN8_DE_PORT_IMR, hotplug_ctrl); - /* Enable DDI hotplug in IER */ hotplug_ctrl = I915_READ(GEN8_DE_PORT_IER) | hotplug_port; I915_WRITE(GEN8_DE_PORT_IER, hotplug_ctrl); POSTING_READ(GEN8_DE_PORT_IER); From cf1d58833f07afbb4534b15caa3fd48baa313b2c Mon Sep 17 00:00:00 2001 From: Sonika Jindal Date: Mon, 10 Aug 2015 10:35:36 +0530 Subject: [PATCH 024/145] drm/i915/bxt: WA for swapped HPD pins in A stepping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WA for BXT A0/A1, where DDIB's HPD pin is swapped to DDIA, so enabling DDIA HPD pin in place of DDIB. v2: For DP, irq_port is used to determine the encoder instead of hpd_pin and removing the edp HPD logic because port A HPD is not present(Imre) v3: Rebased on top of Imre's patchset for enabling HPD on PORT A. Added hpd_pin swapping for intel_dp_init_connector, setting encoder for PORT_A as per the WA in irq_port (Imre) v4: Dont enable interrupt for edp, also reframe the description (Siva) v5: Don’t check for PORT_A in intel_ddi_init to update dig_port, instead avoid setting hpd_pin itself (Imre) Signed-off-by: Sonika Jindal Reviewed-by: Sivakumar Thulasimani Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 10 +++++++++- drivers/gpu/drm/i915/intel_dp.c | 2 ++ drivers/gpu/drm/i915/intel_hdmi.c | 9 ++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 6cfe65d6a8cf..d5c331df81fd 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -3243,7 +3243,15 @@ void intel_ddi_init(struct drm_device *dev, enum port port) goto err; intel_dig_port->hpd_pulse = intel_dp_hpd_pulse; - dev_priv->hotplug.irq_port[port] = intel_dig_port; + /* + * On BXT A0/A1, sw needs to activate DDIA HPD logic and + * interrupts to check the external panel connection. + */ + if (IS_BROXTON(dev_priv) && (INTEL_REVID(dev) < BXT_REVID_B0) + && port == PORT_B) + dev_priv->hotplug.irq_port[PORT_A] = intel_dig_port; + else + dev_priv->hotplug.irq_port[port] = intel_dig_port; } /* In theory we don't need the encoder->type check, but leave it just in diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 54e9b5380aa7..a02824649423 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -5876,6 +5876,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, break; case PORT_B: intel_encoder->hpd_pin = HPD_PORT_B; + if (IS_BROXTON(dev_priv) && (INTEL_REVID(dev) < BXT_REVID_B0)) + intel_encoder->hpd_pin = HPD_PORT_A; break; case PORT_C: intel_encoder->hpd_pin = HPD_PORT_C; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 51cbea8247fe..0cfbe85fe271 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1973,7 +1973,14 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, intel_hdmi->ddc_bus = GMBUS_PIN_1_BXT; else intel_hdmi->ddc_bus = GMBUS_PIN_DPB; - intel_encoder->hpd_pin = HPD_PORT_B; + /* + * On BXT A0/A1, sw needs to activate DDIA HPD logic and + * interrupts to check the external panel connection. + */ + if (IS_BROXTON(dev_priv) && (INTEL_REVID(dev) < BXT_REVID_B0)) + intel_encoder->hpd_pin = HPD_PORT_A; + else + intel_encoder->hpd_pin = HPD_PORT_B; break; case PORT_C: if (IS_BROXTON(dev_priv)) From ed75a55bb36dec17b9e0f878cea7c8ccb037d8f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 11 Aug 2015 19:47:10 +0300 Subject: [PATCH 025/145] drm/i915: clflush on pin_to_display after pwrite to UC bo in LLC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we don't clflush on pin_to_display if the bo is already UC/WT and is not in the CPU write domain. This causes problems with pwrite since pwrite doesn't change the write domain, and it avoids clflushing on UC/WT buffers on LLC platforms unless the buffer is currently being scanned out. Fix the problem by marking the cache dirty and adjusting i915_gem_object_set_cache_level() to clflush when the cache is dirty even if the cache_level doesn't change. My last attempt [1] at fixing this via write domain frobbing was shot down, but now with the cache_dirty flag we can do things in a nicer way. [1] http://lists.freedesktop.org/archives/intel-gfx/2014-November/055390.html v2: Drop the I915_CACHE_NONE/WT checks from pwrite Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=86422 Testcase: igt/kms_pwrite_crc Testcase: igt/gem_pwrite_snooped Signed-off-by: Ville Syrjälä Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 6d0f8349a91c..407b6b3576ae 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1005,12 +1005,14 @@ i915_gem_shmem_pwrite(struct drm_device *dev, if (!needs_clflush_after && obj->base.write_domain != I915_GEM_DOMAIN_CPU) { if (i915_gem_clflush_object(obj, obj->pin_display)) - i915_gem_chipset_flush(dev); + needs_clflush_after = true; } } if (needs_clflush_after) i915_gem_chipset_flush(dev); + else + obj->cache_dirty = true; intel_fb_obj_flush(obj, false, ORIGIN_CPU); return ret; @@ -3639,10 +3641,10 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, { struct drm_device *dev = obj->base.dev; struct i915_vma *vma, *next; - int ret; + int ret = 0; if (obj->cache_level == cache_level) - return 0; + goto out; if (i915_gem_obj_is_pinned(obj)) { DRM_DEBUG("can not change the cache level of pinned objects\n"); @@ -3687,6 +3689,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, vma->node.color = cache_level; obj->cache_level = cache_level; +out: if (obj->cache_dirty && obj->base.write_domain != I915_GEM_DOMAIN_CPU && cpu_write_needs_clflush(obj)) { From 908565c20888f790b96b03ae5be9cd175f3d8d72 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 12 Aug 2015 13:08:22 +0100 Subject: [PATCH 026/145] drm/i915: Do not check or a stalled pageflip prior to it being queued MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we queue the command or operation to change the scanout address, we mark the flip as in progress. We can use this flag to prevent us from checking for a stalled flip prior to its existence! Signed-off-by: Chris Wilson Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 7f6d5fd7c2cf..dc3e6b2e5663 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11236,6 +11236,9 @@ static bool __intel_pageflip_stall_check(struct drm_device *dev, if (atomic_read(&work->pending) >= INTEL_FLIP_COMPLETE) return true; + if (atomic_read(&work->pending) < INTEL_FLIP_PENDING) + return false; + if (!work->enable_stall_check) return false; From 0f2a2a756e862e1936ef599451015ca4c12efde8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 6 Jul 2015 15:10:00 +0300 Subject: [PATCH 027/145] drm/i915: Clean up DP/HDMI limited color range handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we treat intel_{dp,hdmi}->color_range as partly user controller value (via the property) but we also change it during .compute_config() when using the "Automatic" mode. That is a bit confusing, so let's just change things so that we store the user property values in intel_dp, and only change what's stored in pipe_config during .compute_config(). There should be no functional change. Signed-off-by: Ville Syrjälä Reviewed-by: Sivakumar Thulasimani Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 25 ++++++++++++------------- drivers/gpu/drm/i915/intel_drv.h | 4 ++-- drivers/gpu/drm/i915/intel_hdmi.c | 26 ++++++++++++-------------- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index a02824649423..df1b663300c4 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1492,15 +1492,13 @@ intel_dp_compute_config(struct intel_encoder *encoder, * CEA-861-E - 5.1 Default Encoding Parameters * VESA DisplayPort Ver.1.2a - 5.1.1.1 Video Colorimetry */ - if (bpp != 18 && drm_match_cea_mode(adjusted_mode) > 1) - intel_dp->color_range = DP_COLOR_RANGE_16_235; - else - intel_dp->color_range = 0; + pipe_config->limited_color_range = + bpp != 18 && drm_match_cea_mode(adjusted_mode) > 1; + } else { + pipe_config->limited_color_range = + intel_dp->limited_color_range; } - if (intel_dp->color_range) - pipe_config->limited_color_range = true; - intel_dp->lane_count = lane_count; if (intel_dp->num_sink_rates) { @@ -1642,8 +1640,9 @@ static void intel_dp_prepare(struct intel_encoder *encoder) trans_dp &= ~TRANS_DP_ENH_FRAMING; I915_WRITE(TRANS_DP_CTL(crtc->pipe), trans_dp); } else { - if (!HAS_PCH_SPLIT(dev) && !IS_VALLEYVIEW(dev)) - intel_dp->DP |= intel_dp->color_range; + if (!HAS_PCH_SPLIT(dev) && !IS_VALLEYVIEW(dev) && + crtc->config->limited_color_range) + intel_dp->DP |= DP_COLOR_RANGE_16_235; if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) intel_dp->DP |= DP_SYNC_HS_HIGH; @@ -4751,7 +4750,7 @@ intel_dp_set_property(struct drm_connector *connector, if (property == dev_priv->broadcast_rgb_property) { bool old_auto = intel_dp->color_range_auto; - uint32_t old_range = intel_dp->color_range; + bool old_range = intel_dp->limited_color_range; switch (val) { case INTEL_BROADCAST_RGB_AUTO: @@ -4759,18 +4758,18 @@ intel_dp_set_property(struct drm_connector *connector, break; case INTEL_BROADCAST_RGB_FULL: intel_dp->color_range_auto = false; - intel_dp->color_range = 0; + intel_dp->limited_color_range = false; break; case INTEL_BROADCAST_RGB_LIMITED: intel_dp->color_range_auto = false; - intel_dp->color_range = DP_COLOR_RANGE_16_235; + intel_dp->limited_color_range = true; break; default: return -EINVAL; } if (old_auto == intel_dp->color_range_auto && - old_range == intel_dp->color_range) + old_range == intel_dp->limited_color_range) return 0; goto done; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 6b3e2c6e6c54..e2456bc089b8 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -657,7 +657,7 @@ struct cxsr_latency { struct intel_hdmi { u32 hdmi_reg; int ddc_bus; - uint32_t color_range; + bool limited_color_range; bool color_range_auto; bool has_hdmi_sink; bool has_audio; @@ -708,7 +708,7 @@ struct intel_dp { uint32_t DP; bool has_audio; enum hdmi_force_audio force_audio; - uint32_t color_range; + bool limited_color_range; bool color_range_auto; uint8_t link_bw; uint8_t rate_select; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 0cfbe85fe271..7185062c8c9b 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -848,8 +848,8 @@ static void intel_hdmi_prepare(struct intel_encoder *encoder) u32 hdmi_val; hdmi_val = SDVO_ENCODING_HDMI; - if (!HAS_PCH_SPLIT(dev)) - hdmi_val |= intel_hdmi->color_range; + if (!HAS_PCH_SPLIT(dev) && crtc->config->limited_color_range) + hdmi_val |= HDMI_COLOR_RANGE_16_235; if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) hdmi_val |= SDVO_VSYNC_ACTIVE_HIGH; if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) @@ -1260,11 +1260,12 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, if (intel_hdmi->color_range_auto) { /* See CEA-861-E - 5.1 Default Encoding Parameters */ - if (pipe_config->has_hdmi_sink && - drm_match_cea_mode(adjusted_mode) > 1) - intel_hdmi->color_range = HDMI_COLOR_RANGE_16_235; - else - intel_hdmi->color_range = 0; + pipe_config->limited_color_range = + pipe_config->has_hdmi_sink && + drm_match_cea_mode(adjusted_mode) > 1; + } else { + pipe_config->limited_color_range = + intel_hdmi->limited_color_range; } if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK) { @@ -1273,9 +1274,6 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, clock_12bpc *= 2; } - if (intel_hdmi->color_range) - pipe_config->limited_color_range = true; - if (HAS_PCH_SPLIT(dev) && !HAS_DDI(dev)) pipe_config->has_pch_encoder = true; @@ -1470,7 +1468,7 @@ intel_hdmi_set_property(struct drm_connector *connector, if (property == dev_priv->broadcast_rgb_property) { bool old_auto = intel_hdmi->color_range_auto; - uint32_t old_range = intel_hdmi->color_range; + bool old_range = intel_hdmi->limited_color_range; switch (val) { case INTEL_BROADCAST_RGB_AUTO: @@ -1478,18 +1476,18 @@ intel_hdmi_set_property(struct drm_connector *connector, break; case INTEL_BROADCAST_RGB_FULL: intel_hdmi->color_range_auto = false; - intel_hdmi->color_range = 0; + intel_hdmi->limited_color_range = false; break; case INTEL_BROADCAST_RGB_LIMITED: intel_hdmi->color_range_auto = false; - intel_hdmi->color_range = HDMI_COLOR_RANGE_16_235; + intel_hdmi->limited_color_range = true; break; default: return -EINVAL; } if (old_auto == intel_hdmi->color_range_auto && - old_range == intel_hdmi->color_range) + old_range == intel_hdmi->limited_color_range) return 0; goto done; From 840b32b7eda7837db6e0236293f71fc6644dedf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 11 Aug 2015 20:21:46 +0300 Subject: [PATCH 028/145] drm/i915: Don't use link_bw for PLL setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use port_clock instead of link_bw when picking the PLL parameters for DP. link_bw may be zero with an eDP 1.4 sink that supports DP_LINK_RATE_SET so we shouldn't use it for anything other than feed it to the sink appropriately. v2: Fix typo in commit message (Sivakumar) Reviewed-by: Sivakumar Thulasimani Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 11 +++----- drivers/gpu/drm/i915/intel_dp.c | 44 ++++++++++++++++---------------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index d5c331df81fd..0e2f982a2505 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1579,17 +1579,14 @@ skl_ddi_pll_select(struct intel_crtc *intel_crtc, DPLL_CFGCR2_PDIV(wrpll_params.pdiv) | wrpll_params.central_freq; } else if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) { - struct drm_encoder *encoder = &intel_encoder->base; - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - - switch (intel_dp->link_bw) { - case DP_LINK_BW_1_62: + switch (crtc_state->port_clock / 2) { + case 81000: ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_810, 0); break; - case DP_LINK_BW_2_7: + case 135000: ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1350, 0); break; - case DP_LINK_BW_5_4: + case 270000: ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_2700, 0); break; } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index df1b663300c4..3f885f6980f7 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -48,28 +48,28 @@ #define INTEL_DP_RESOLUTION_FAILSAFE (3 << INTEL_DP_RESOLUTION_SHIFT_MASK) struct dp_link_dpll { - int link_bw; + int clock; struct dpll dpll; }; static const struct dp_link_dpll gen4_dpll[] = { - { DP_LINK_BW_1_62, + { 162000, { .p1 = 2, .p2 = 10, .n = 2, .m1 = 23, .m2 = 8 } }, - { DP_LINK_BW_2_7, + { 270000, { .p1 = 1, .p2 = 10, .n = 1, .m1 = 14, .m2 = 2 } } }; static const struct dp_link_dpll pch_dpll[] = { - { DP_LINK_BW_1_62, + { 162000, { .p1 = 2, .p2 = 10, .n = 1, .m1 = 12, .m2 = 9 } }, - { DP_LINK_BW_2_7, + { 270000, { .p1 = 1, .p2 = 10, .n = 2, .m1 = 14, .m2 = 8 } } }; static const struct dp_link_dpll vlv_dpll[] = { - { DP_LINK_BW_1_62, + { 162000, { .p1 = 3, .p2 = 2, .n = 5, .m1 = 3, .m2 = 81 } }, - { DP_LINK_BW_2_7, + { 270000, { .p1 = 2, .p2 = 2, .n = 1, .m1 = 2, .m2 = 27 } } }; @@ -83,11 +83,11 @@ static const struct dp_link_dpll chv_dpll[] = { * m2 is stored in fixed point format using formula below * (m2_int << 22) | m2_fraction */ - { DP_LINK_BW_1_62, /* m2_int = 32, m2_fraction = 1677722 */ + { 162000, /* m2_int = 32, m2_fraction = 1677722 */ { .p1 = 4, .p2 = 2, .n = 1, .m1 = 2, .m2 = 0x819999a } }, - { DP_LINK_BW_2_7, /* m2_int = 27, m2_fraction = 0 */ + { 270000, /* m2_int = 27, m2_fraction = 0 */ { .p1 = 4, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6c00000 } }, - { DP_LINK_BW_5_4, /* m2_int = 27, m2_fraction = 0 */ + { 540000, /* m2_int = 27, m2_fraction = 0 */ { .p1 = 2, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6c00000 } } }; @@ -1133,7 +1133,7 @@ intel_dp_connector_unregister(struct intel_connector *intel_connector) } static void -skl_edp_set_pll_config(struct intel_crtc_state *pipe_config, int link_clock) +skl_edp_set_pll_config(struct intel_crtc_state *pipe_config) { u32 ctrl1; @@ -1145,7 +1145,7 @@ skl_edp_set_pll_config(struct intel_crtc_state *pipe_config, int link_clock) pipe_config->dpll_hw_state.cfgcr2 = 0; ctrl1 = DPLL_CTRL1_OVERRIDE(SKL_DPLL0); - switch (link_clock / 2) { + switch (pipe_config->port_clock / 2) { case 81000: ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_810, SKL_DPLL0); @@ -1179,19 +1179,19 @@ skl_edp_set_pll_config(struct intel_crtc_state *pipe_config, int link_clock) } static void -hsw_dp_set_ddi_pll_sel(struct intel_crtc_state *pipe_config, int link_bw) +hsw_dp_set_ddi_pll_sel(struct intel_crtc_state *pipe_config) { memset(&pipe_config->dpll_hw_state, 0, sizeof(pipe_config->dpll_hw_state)); - switch (link_bw) { - case DP_LINK_BW_1_62: + switch (pipe_config->port_clock / 2) { + case 81000: pipe_config->ddi_pll_sel = PORT_CLK_SEL_LCPLL_810; break; - case DP_LINK_BW_2_7: + case 135000: pipe_config->ddi_pll_sel = PORT_CLK_SEL_LCPLL_1350; break; - case DP_LINK_BW_5_4: + case 270000: pipe_config->ddi_pll_sel = PORT_CLK_SEL_LCPLL_2700; break; } @@ -1238,7 +1238,7 @@ intel_dp_source_rates(struct drm_device *dev, const int **source_rates) static void intel_dp_set_clock(struct intel_encoder *encoder, - struct intel_crtc_state *pipe_config, int link_bw) + struct intel_crtc_state *pipe_config) { struct drm_device *dev = encoder->base.dev; const struct dp_link_dpll *divisor = NULL; @@ -1260,7 +1260,7 @@ intel_dp_set_clock(struct intel_encoder *encoder, if (divisor && count) { for (i = 0; i < count; i++) { - if (link_bw == divisor[i].link_bw) { + if (pipe_config->port_clock == divisor[i].clock) { pipe_config->dpll = divisor[i].dpll; pipe_config->clock_set = true; break; @@ -1535,13 +1535,13 @@ intel_dp_compute_config(struct intel_encoder *encoder, } if (IS_SKYLAKE(dev) && is_edp(intel_dp)) - skl_edp_set_pll_config(pipe_config, common_rates[clock]); + skl_edp_set_pll_config(pipe_config); else if (IS_BROXTON(dev)) /* handled in ddi */; else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) - hsw_dp_set_ddi_pll_sel(pipe_config, intel_dp->link_bw); + hsw_dp_set_ddi_pll_sel(pipe_config); else - intel_dp_set_clock(encoder, pipe_config, intel_dp->link_bw); + intel_dp_set_clock(encoder, pipe_config); return true; } From 96f3f1f905e9c4977220fa07477cd8a8ed4af4da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 6 Jul 2015 15:10:02 +0300 Subject: [PATCH 029/145] drm/i915: Don't pass clock to DDI PLL select functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All the *_ddi_pll_select() functions get passed the port_clock and pipe config as parameters. We only need to pass the pipe config, and the functions can dig up the port_clock themselves. Signed-off-by: Ville Syrjälä Reviewed-by: Sivakumar Thulasimani Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 0e2f982a2505..b6d1ab27e5ba 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1267,9 +1267,10 @@ hsw_ddi_calculate_wrpll(int clock /* in Hz */, static bool hsw_ddi_pll_select(struct intel_crtc *intel_crtc, struct intel_crtc_state *crtc_state, - struct intel_encoder *intel_encoder, - int clock) + struct intel_encoder *intel_encoder) { + int clock = crtc_state->port_clock; + if (intel_encoder->type == INTEL_OUTPUT_HDMI) { struct intel_shared_dpll *pll; uint32_t val; @@ -1548,11 +1549,11 @@ skl_ddi_calculate_wrpll(int clock /* in Hz */, static bool skl_ddi_pll_select(struct intel_crtc *intel_crtc, struct intel_crtc_state *crtc_state, - struct intel_encoder *intel_encoder, - int clock) + struct intel_encoder *intel_encoder) { struct intel_shared_dpll *pll; uint32_t ctrl1, cfgcr1, cfgcr2; + int clock = crtc_state->port_clock; /* * See comment in intel_dpll_hw_state to understand why we always use 0 @@ -1640,14 +1641,14 @@ static const struct bxt_clk_div bxt_dp_clk_val[] = { static bool bxt_ddi_pll_select(struct intel_crtc *intel_crtc, struct intel_crtc_state *crtc_state, - struct intel_encoder *intel_encoder, - int clock) + struct intel_encoder *intel_encoder) { struct intel_shared_dpll *pll; struct bxt_clk_div clk_div = {0}; int vco = 0; uint32_t prop_coef, int_coef, gain_ctl, targ_cnt; uint32_t lanestagger; + int clock = crtc_state->port_clock; if (intel_encoder->type == INTEL_OUTPUT_HDMI) { intel_clock_t best_clock; @@ -1775,17 +1776,16 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc, struct drm_device *dev = intel_crtc->base.dev; struct intel_encoder *intel_encoder = intel_ddi_get_crtc_new_encoder(crtc_state); - int clock = crtc_state->port_clock; if (IS_SKYLAKE(dev)) return skl_ddi_pll_select(intel_crtc, crtc_state, - intel_encoder, clock); + intel_encoder); else if (IS_BROXTON(dev)) return bxt_ddi_pll_select(intel_crtc, crtc_state, - intel_encoder, clock); + intel_encoder); else return hsw_ddi_pll_select(intel_crtc, crtc_state, - intel_encoder, clock); + intel_encoder); } void intel_ddi_set_pipe_settings(struct drm_crtc *crtc) From b81e34c29e67f5b3e5ce28b5055ae2ca59c36f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 6 Jul 2015 15:10:03 +0300 Subject: [PATCH 030/145] drm/i915: Avoid confusion between DP and TRANS_DP_CTL in DP .get_config() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a separate variable for the TRANS_DP_CTL value instead of reusing 'tmp' that otherwise contains the DP port register value. Signed-off-by: Ville Syrjälä Reviewed-by: Sivakumar Thulasimani Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3f885f6980f7..3a9b8e4b8683 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2282,13 +2282,14 @@ static void intel_dp_get_config(struct intel_encoder *encoder, pipe_config->has_audio = tmp & DP_AUDIO_OUTPUT_ENABLE && port != PORT_A; if (HAS_PCH_CPT(dev) && port != PORT_A) { - tmp = I915_READ(TRANS_DP_CTL(crtc->pipe)); - if (tmp & TRANS_DP_HSYNC_ACTIVE_HIGH) + u32 trans_dp = I915_READ(TRANS_DP_CTL(crtc->pipe)); + + if (trans_dp & TRANS_DP_HSYNC_ACTIVE_HIGH) flags |= DRM_MODE_FLAG_PHSYNC; else flags |= DRM_MODE_FLAG_NHSYNC; - if (tmp & TRANS_DP_VSYNC_ACTIVE_HIGH) + if (trans_dp & TRANS_DP_VSYNC_ACTIVE_HIGH) flags |= DRM_MODE_FLAG_PVSYNC; else flags |= DRM_MODE_FLAG_NVSYNC; From 90a6b7b052b1aa17fbb98b049e9c8b7f729c35a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 6 Jul 2015 16:39:15 +0300 Subject: [PATCH 031/145] drm/i915: Move intel_dp->lane_count into pipe_config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we clobber intel_dp->lane_count in compute config, which means after a rejected modeset we may no longer be able to retrain the current link. Move lane_count into pipe_config to avoid that. v2: Add missing ':' to the pipe config debug dump Signed-off-by: Ville Syrjälä Reviewed-by: Sivakumar Thulasimani Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 3 ++ drivers/gpu/drm/i915/intel_ddi.c | 10 +++-- drivers/gpu/drm/i915/intel_display.c | 7 +++- drivers/gpu/drm/i915/intel_dp.c | 60 ++++++++++++++++++---------- drivers/gpu/drm/i915/intel_dp_mst.c | 6 ++- drivers/gpu/drm/i915/intel_drv.h | 3 +- 6 files changed, 61 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 88f20cc5ab8e..f22aca3218c2 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4108,6 +4108,7 @@ enum skl_disp_power_wells { /* How many wires to use. I guess 3 was too hard */ #define DP_PORT_WIDTH(width) (((width) - 1) << 19) #define DP_PORT_WIDTH_MASK (7 << 19) +#define DP_PORT_WIDTH_SHIFT 19 /* Mystic DPCD version 1.1 special mode */ #define DP_ENHANCED_FRAMING (1 << 18) @@ -7148,6 +7149,8 @@ enum skl_disp_power_wells { #define DDI_BUF_IS_IDLE (1<<7) #define DDI_A_4_LANES (1<<4) #define DDI_PORT_WIDTH(width) (((width) - 1) << 1) +#define DDI_PORT_WIDTH_MASK (7 << 1) +#define DDI_PORT_WIDTH_SHIFT 1 #define DDI_INIT_DISPLAY_DETECTED (1<<0) /* DDI Buffer Translations */ diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index b6d1ab27e5ba..56d778f00460 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -728,11 +728,11 @@ void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder) struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); struct intel_digital_port *intel_dig_port = enc_to_dig_port(&encoder->base); + struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); intel_dp->DP = intel_dig_port->saved_port_bits | DDI_BUF_CTL_ENABLE | DDI_BUF_TRANS_SELECT(0); - intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count); - + intel_dp->DP |= DDI_PORT_WIDTH(crtc->config->lane_count); } static struct intel_encoder * @@ -1918,7 +1918,7 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) } else temp |= TRANS_DDI_MODE_SELECT_DP_SST; - temp |= DDI_PORT_WIDTH(intel_dp->lane_count); + temp |= DDI_PORT_WIDTH(intel_crtc->config->lane_count); } else if (type == INTEL_OUTPUT_DP_MST) { struct intel_dp *intel_dp = &enc_to_mst(encoder)->primary->dp; @@ -1927,7 +1927,7 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) } else temp |= TRANS_DDI_MODE_SELECT_DP_SST; - temp |= DDI_PORT_WIDTH(intel_dp->lane_count); + temp |= DDI_PORT_WIDTH(intel_crtc->config->lane_count); } else { WARN(1, "Invalid encoder type %d for pipe %c\n", intel_encoder->type, pipe_name(pipe)); @@ -3094,6 +3094,8 @@ void intel_ddi_get_config(struct intel_encoder *encoder, case TRANS_DDI_MODE_SELECT_DP_SST: case TRANS_DDI_MODE_SELECT_DP_MST: pipe_config->has_dp_encoder = true; + pipe_config->lane_count = + ((temp & DDI_PORT_WIDTH_MASK) >> DDI_PORT_WIDTH_SHIFT) + 1; intel_dp_get_m_n(intel_crtc, pipe_config); break; default: diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index dc3e6b2e5663..40e70f20787f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11902,14 +11902,16 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc, pipe_config->fdi_m_n.gmch_m, pipe_config->fdi_m_n.gmch_n, pipe_config->fdi_m_n.link_m, pipe_config->fdi_m_n.link_n, pipe_config->fdi_m_n.tu); - DRM_DEBUG_KMS("dp: %i, gmch_m: %u, gmch_n: %u, link_m: %u, link_n: %u, tu: %u\n", + DRM_DEBUG_KMS("dp: %i, lanes: %i, gmch_m: %u, gmch_n: %u, link_m: %u, link_n: %u, tu: %u\n", pipe_config->has_dp_encoder, + pipe_config->lane_count, pipe_config->dp_m_n.gmch_m, pipe_config->dp_m_n.gmch_n, pipe_config->dp_m_n.link_m, pipe_config->dp_m_n.link_n, pipe_config->dp_m_n.tu); - DRM_DEBUG_KMS("dp: %i, gmch_m2: %u, gmch_n2: %u, link_m2: %u, link_n2: %u, tu2: %u\n", + DRM_DEBUG_KMS("dp: %i, lanes: %i, gmch_m2: %u, gmch_n2: %u, link_m2: %u, link_n2: %u, tu2: %u\n", pipe_config->has_dp_encoder, + pipe_config->lane_count, pipe_config->dp_m2_n2.gmch_m, pipe_config->dp_m2_n2.gmch_n, pipe_config->dp_m2_n2.link_m, @@ -12416,6 +12418,7 @@ intel_pipe_config_compare(struct drm_device *dev, PIPE_CONF_CHECK_M_N(fdi_m_n); PIPE_CONF_CHECK_I(has_dp_encoder); + PIPE_CONF_CHECK_I(lane_count); if (INTEL_INFO(dev)->gen < 8) { PIPE_CONF_CHECK_M_N(dp_m_n); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3a9b8e4b8683..3d3a908ac2f2 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1499,7 +1499,7 @@ intel_dp_compute_config(struct intel_encoder *encoder, intel_dp->limited_color_range; } - intel_dp->lane_count = lane_count; + pipe_config->lane_count = lane_count; if (intel_dp->num_sink_rates) { intel_dp->link_bw = 0; @@ -1515,7 +1515,7 @@ intel_dp_compute_config(struct intel_encoder *encoder, pipe_config->port_clock = common_rates[clock]; DRM_DEBUG_KMS("DP link bw %02x lane count %d clock %d bpp %d\n", - intel_dp->link_bw, intel_dp->lane_count, + intel_dp->link_bw, pipe_config->lane_count, pipe_config->port_clock, bpp); DRM_DEBUG_KMS("DP link bw required %i available %i\n", mode_rate, link_avail); @@ -1610,7 +1610,7 @@ static void intel_dp_prepare(struct intel_encoder *encoder) /* Handle DP bits in common between all three register formats */ intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; - intel_dp->DP |= DP_PORT_WIDTH(intel_dp->lane_count); + intel_dp->DP |= DP_PORT_WIDTH(crtc->config->lane_count); if (crtc->config->has_audio) intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE; @@ -2313,6 +2313,9 @@ static void intel_dp_get_config(struct intel_encoder *encoder, pipe_config->has_dp_encoder = true; + pipe_config->lane_count = + ((tmp & DP_PORT_WIDTH_MASK) >> DP_PORT_WIDTH_SHIFT) + 1; + intel_dp_get_m_n(crtc, pipe_config); if (port == PORT_A) { @@ -3338,13 +3341,15 @@ static void intel_get_adjust_train(struct intel_dp *intel_dp, const uint8_t link_status[DP_LINK_STATUS_SIZE]) { + struct intel_crtc *crtc = + to_intel_crtc(dp_to_dig_port(intel_dp)->base.base.crtc); uint8_t v = 0; uint8_t p = 0; int lane; uint8_t voltage_max; uint8_t preemph_max; - for (lane = 0; lane < intel_dp->lane_count; lane++) { + for (lane = 0; lane < crtc->config->lane_count; lane++) { uint8_t this_v = drm_dp_get_adjust_request_voltage(link_status, lane); uint8_t this_p = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); @@ -3513,8 +3518,10 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, uint8_t dp_train_pat) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - struct drm_device *dev = intel_dig_port->base.base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = + to_i915(intel_dig_port->base.base.dev); + struct intel_crtc *crtc = + to_intel_crtc(intel_dig_port->base.base.crtc); uint8_t buf[sizeof(intel_dp->train_set) + 1]; int ret, len; @@ -3530,8 +3537,8 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, len = 1; } else { /* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */ - memcpy(buf + 1, intel_dp->train_set, intel_dp->lane_count); - len = intel_dp->lane_count + 1; + memcpy(buf + 1, intel_dp->train_set, crtc->config->lane_count); + len = crtc->config->lane_count + 1; } ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_PATTERN_SET, @@ -3555,8 +3562,10 @@ intel_dp_update_link_train(struct intel_dp *intel_dp, uint32_t *DP, const uint8_t link_status[DP_LINK_STATUS_SIZE]) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - struct drm_device *dev = intel_dig_port->base.base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = + to_i915(intel_dig_port->base.base.dev); + struct intel_crtc *crtc = + to_intel_crtc(intel_dig_port->base.base.crtc); int ret; intel_get_adjust_train(intel_dp, link_status); @@ -3566,9 +3575,9 @@ intel_dp_update_link_train(struct intel_dp *intel_dp, uint32_t *DP, POSTING_READ(intel_dp->output_reg); ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_LANE0_SET, - intel_dp->train_set, intel_dp->lane_count); + intel_dp->train_set, crtc->config->lane_count); - return ret == intel_dp->lane_count; + return ret == crtc->config->lane_count; } static void intel_dp_set_idle_link_train(struct intel_dp *intel_dp) @@ -3607,6 +3616,8 @@ void intel_dp_start_link_train(struct intel_dp *intel_dp) { struct drm_encoder *encoder = &dp_to_dig_port(intel_dp)->base.base; + struct intel_crtc *crtc = + to_intel_crtc(dp_to_dig_port(intel_dp)->base.base.crtc); struct drm_device *dev = encoder->dev; int i; uint8_t voltage; @@ -3619,7 +3630,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) /* Write the link configuration data */ link_config[0] = intel_dp->link_bw; - link_config[1] = intel_dp->lane_count; + link_config[1] = crtc->config->lane_count; if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_BW_SET, link_config, 2); @@ -3653,7 +3664,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) break; } - if (drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) { + if (drm_dp_clock_recovery_ok(link_status, crtc->config->lane_count)) { DRM_DEBUG_KMS("clock recovery OK\n"); break; } @@ -3676,10 +3687,10 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) } /* Check to see if we've tried the max voltage */ - for (i = 0; i < intel_dp->lane_count; i++) + for (i = 0; i < crtc->config->lane_count; i++) if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) break; - if (i == intel_dp->lane_count) { + if (i == crtc->config->lane_count) { ++loop_tries; if (loop_tries == 5) { DRM_ERROR("too many full retries, give up\n"); @@ -3716,6 +3727,8 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) void intel_dp_complete_link_train(struct intel_dp *intel_dp) { + struct intel_crtc *crtc = + to_intel_crtc(dp_to_dig_port(intel_dp)->base.base.crtc); bool channel_eq = false; int tries, cr_tries; uint32_t DP = intel_dp->DP; @@ -3751,7 +3764,8 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) } /* Make sure clock is still ok */ - if (!drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) { + if (!drm_dp_clock_recovery_ok(link_status, + crtc->config->lane_count)) { intel_dp->train_set_valid = false; intel_dp_start_link_train(intel_dp); intel_dp_set_link_train(intel_dp, &DP, @@ -3761,7 +3775,8 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) continue; } - if (drm_dp_channel_eq_ok(link_status, intel_dp->lane_count)) { + if (drm_dp_channel_eq_ok(link_status, + crtc->config->lane_count)) { channel_eq = true; break; } @@ -4259,6 +4274,8 @@ static void intel_dp_handle_test_request(struct intel_dp *intel_dp) static int intel_dp_check_mst_status(struct intel_dp *intel_dp) { + struct intel_crtc *crtc = + to_intel_crtc(dp_to_dig_port(intel_dp)->base.base.crtc); bool bret; if (intel_dp->is_mst) { @@ -4271,7 +4288,8 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) if (bret == true) { /* check link status - esi[10] = 0x200c */ - if (intel_dp->active_mst_links && !drm_dp_channel_eq_ok(&esi[10], intel_dp->lane_count)) { + if (intel_dp->active_mst_links && + !drm_dp_channel_eq_ok(&esi[10], crtc->config->lane_count)) { DRM_DEBUG_KMS("channel EQ not ok, retraining\n"); intel_dp_start_link_train(intel_dp); intel_dp_complete_link_train(intel_dp); @@ -4326,6 +4344,8 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base; + struct intel_crtc *crtc = + to_intel_crtc(dp_to_dig_port(intel_dp)->base.base.crtc); u8 sink_irq_vector; u8 link_status[DP_LINK_STATUS_SIZE]; @@ -4361,7 +4381,7 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) DRM_DEBUG_DRIVER("CP or sink specific irq unhandled\n"); } - if (!drm_dp_channel_eq_ok(link_status, intel_dp->lane_count)) { + if (!drm_dp_channel_eq_ok(link_status, crtc->config->lane_count)) { DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n", intel_encoder->base.name); intel_dp_start_link_train(intel_dp); diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c index f4fe1183bae6..fdb5e31fedaa 100644 --- a/drivers/gpu/drm/i915/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/intel_dp_mst.c @@ -65,7 +65,7 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, intel_dp->rate_select = 0; } - intel_dp->lane_count = lane_count; + pipe_config->lane_count = lane_count; pipe_config->pipe_bpp = 24; pipe_config->port_clock = rate; @@ -276,6 +276,10 @@ static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder, break; } pipe_config->base.adjusted_mode.flags |= flags; + + pipe_config->lane_count = + ((temp & DDI_PORT_WIDTH_MASK) >> DDI_PORT_WIDTH_SHIFT) + 1; + intel_dp_get_m_n(crtc, pipe_config); intel_ddi_clock_get(&intel_dig_port->base, pipe_config); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index e2456bc089b8..0a11041232e4 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -423,6 +423,8 @@ struct intel_crtc_state { /* Used by SDVO (and if we ever fix it, HDMI). */ unsigned pixel_multiplier; + uint8_t lane_count; + /* Panel fitter controls for gen2-gen4 + VLV */ struct { u32 control; @@ -712,7 +714,6 @@ struct intel_dp { bool color_range_auto; uint8_t link_bw; uint8_t rate_select; - uint8_t lane_count; uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; uint8_t psr_dpcd[EDP_PSR_RECEIVER_CAP_SIZE]; uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; From a79b8165beed02a96dc54737feb20700ddf71127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 6 Jul 2015 15:10:05 +0300 Subject: [PATCH 032/145] drm/i915: Don't use link_bw to select between TP1 and TP3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_dp->link_bw is going away, so consul the port_clock instead when choosing between TP1 and TP3. Signed-off-by: Ville Syrjälä Reviewed-by: Sivakumar Thulasimani Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3d3a908ac2f2..6231eb14fb76 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -3734,8 +3734,8 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) uint32_t DP = intel_dp->DP; uint32_t training_pattern = DP_TRAINING_PATTERN_2; - /* Training Pattern 3 for HBR2 ot 1.2 devices that support it*/ - if (intel_dp->link_bw == DP_LINK_BW_5_4 || intel_dp->use_tps3) + /* Training Pattern 3 for HBR2 or 1.2 devices that support it*/ + if (crtc->config->port_clock == 540000 || intel_dp->use_tps3) training_pattern = DP_TRAINING_PATTERN_3; /* channel equalization */ From 04a60f9ffa9a9ac05d5616408e3f11115ed1450a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 6 Jul 2015 15:10:06 +0300 Subject: [PATCH 033/145] drm/i915: Kill intel_dp->{link_bw, rate_select} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only need the link_bw/rate_select parameters when starting link training, and they should be computed based on the currently active config, so throw them out from intel_dp and just compute on demand. Toss in an extra debug print to see rate_select in addition to link_bw, as the latter may be 0 for eDP 1.4. Signed-off-by: Ville Syrjälä Reviewed-by: Sivakumar Thulasimani Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 39 ++++++++++++++++++----------- drivers/gpu/drm/i915/intel_dp_mst.c | 13 ++-------- drivers/gpu/drm/i915/intel_drv.h | 2 -- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 6231eb14fb76..b905c19e11a1 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1376,6 +1376,19 @@ int intel_dp_rate_select(struct intel_dp *intel_dp, int rate) return rate_to_index(rate, intel_dp->sink_rates); } +static void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock, + uint8_t *link_bw, uint8_t *rate_select) +{ + if (intel_dp->num_sink_rates) { + *link_bw = 0; + *rate_select = + intel_dp_rate_select(intel_dp, port_clock); + } else { + *link_bw = drm_dp_link_rate_to_bw_code(port_clock); + *rate_select = 0; + } +} + bool intel_dp_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config) @@ -1397,6 +1410,7 @@ intel_dp_compute_config(struct intel_encoder *encoder, int link_avail, link_clock; int common_rates[DP_MAX_SUPPORTED_RATES] = {}; int common_len; + uint8_t link_bw, rate_select; common_len = intel_dp_common_rates(intel_dp, common_rates); @@ -1501,21 +1515,14 @@ intel_dp_compute_config(struct intel_encoder *encoder, pipe_config->lane_count = lane_count; - if (intel_dp->num_sink_rates) { - intel_dp->link_bw = 0; - intel_dp->rate_select = - intel_dp_rate_select(intel_dp, common_rates[clock]); - } else { - intel_dp->link_bw = - drm_dp_link_rate_to_bw_code(common_rates[clock]); - intel_dp->rate_select = 0; - } - pipe_config->pipe_bpp = bpp; pipe_config->port_clock = common_rates[clock]; - DRM_DEBUG_KMS("DP link bw %02x lane count %d clock %d bpp %d\n", - intel_dp->link_bw, pipe_config->lane_count, + intel_dp_compute_rate(intel_dp, pipe_config->port_clock, + &link_bw, &rate_select); + + DRM_DEBUG_KMS("DP link bw %02x rate select %02x lane count %d clock %d bpp %d\n", + link_bw, rate_select, pipe_config->lane_count, pipe_config->port_clock, bpp); DRM_DEBUG_KMS("DP link bw required %i available %i\n", mode_rate, link_avail); @@ -3624,19 +3631,23 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) int voltage_tries, loop_tries; uint32_t DP = intel_dp->DP; uint8_t link_config[2]; + uint8_t link_bw, rate_select; if (HAS_DDI(dev)) intel_ddi_prepare_link_retrain(encoder); + intel_dp_compute_rate(intel_dp, crtc->config->port_clock, + &link_bw, &rate_select); + /* Write the link configuration data */ - link_config[0] = intel_dp->link_bw; + link_config[0] = link_bw; link_config[1] = crtc->config->lane_count; if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_BW_SET, link_config, 2); if (intel_dp->num_sink_rates) drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_RATE_SET, - &intel_dp->rate_select, 1); + &rate_select, 1); link_config[0] = 0; link_config[1] = DP_SET_ANSI_8B10B; diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c index fdb5e31fedaa..1dd2b373c054 100644 --- a/drivers/gpu/drm/i915/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/intel_dp_mst.c @@ -38,7 +38,7 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, struct intel_dp *intel_dp = &intel_dig_port->dp; struct drm_atomic_state *state; int bpp, i; - int lane_count, slots, rate; + int lane_count, slots; struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; struct drm_connector *drm_connector; struct intel_connector *connector, *found = NULL; @@ -55,20 +55,11 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, */ lane_count = drm_dp_max_lane_count(intel_dp->dpcd); - rate = intel_dp_max_link_rate(intel_dp); - - if (intel_dp->num_sink_rates) { - intel_dp->link_bw = 0; - intel_dp->rate_select = intel_dp_rate_select(intel_dp, rate); - } else { - intel_dp->link_bw = drm_dp_link_rate_to_bw_code(rate); - intel_dp->rate_select = 0; - } pipe_config->lane_count = lane_count; pipe_config->pipe_bpp = 24; - pipe_config->port_clock = rate; + pipe_config->port_clock = intel_dp_max_link_rate(intel_dp); state = pipe_config->base.state; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 0a11041232e4..22dc8b6b4198 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -712,8 +712,6 @@ struct intel_dp { enum hdmi_force_audio force_audio; bool limited_color_range; bool color_range_auto; - uint8_t link_bw; - uint8_t rate_select; uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; uint8_t psr_dpcd[EDP_PSR_RECEIVER_CAP_SIZE]; uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; From 33a732f407fed464df687370d7bb4d64533f9920 Mon Sep 17 00:00:00 2001 From: Alex Dai Date: Wed, 12 Aug 2015 15:43:36 +0100 Subject: [PATCH 034/145] drm/i915: GuC-specific firmware loader This fetches the required firmware image from the filesystem, then loads it into the GuC's memory via a dedicated DMA engine. This patch is derived from GuC loading work originally done by Vinit Azad and Ben Widawsky. v2: Various improvements per review comments by Chris Wilson v3: Removed 'wait' parameter to intel_guc_ucode_load() as firmware prefetch is no longer supported in the common firmware loader, per Daniel Vetter's request. Firmware checker callback fn now returns errno rather than bool. v4: Squash uC-independent code into GuC-specifc loader [Daniel Vetter] Don't keep the driver working (by falling back to execlist mode) if GuC firmware loading fails [Daniel Vetter] v5: Clarify WOPCM-related #defines [Tom O'Rourke] Delete obsolete code no longer required with current h/w & f/w [Tom O'Rourke] Move the call to intel_guc_ucode_init() later, so that it can allocate GEM objects, and have it fetch the firmware; then intel_guc_ucode_load() doesn't need to fetch it later. [Daniel Vetter]. v6: Update comment describing intel_guc_ucode_load() [Tom O'Rourke] Issue: VIZ-4884 Signed-off-by: Alex Dai Signed-off-by: Dave Gordon Reviewed-by: Tom O'Rourke Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/Makefile | 3 + drivers/gpu/drm/i915/i915_dma.c | 9 + drivers/gpu/drm/i915/i915_drv.h | 11 + drivers/gpu/drm/i915/i915_gem.c | 16 + drivers/gpu/drm/i915/i915_guc_reg.h | 17 +- drivers/gpu/drm/i915/i915_reg.h | 4 +- drivers/gpu/drm/i915/intel_guc.h | 67 +++ drivers/gpu/drm/i915/intel_guc_fwif.h | 3 +- drivers/gpu/drm/i915/intel_guc_loader.c | 529 ++++++++++++++++++++++++ 9 files changed, 650 insertions(+), 9 deletions(-) create mode 100644 drivers/gpu/drm/i915/intel_guc.h create mode 100644 drivers/gpu/drm/i915/intel_guc_loader.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 41fb8a9c5bef..cc359e0e6678 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -40,6 +40,9 @@ i915-y += i915_cmd_parser.o \ intel_ringbuffer.o \ intel_uncore.o +# general-purpose microcontroller (GuC) support +i915-y += intel_guc_loader.o + # autogenerated null render state i915-y += intel_renderstate_gen6.o \ intel_renderstate_gen7.o \ diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index ab37d1121be8..2193cc28f70c 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -435,6 +435,11 @@ static int i915_load_modeset_init(struct drm_device *dev) * working irqs for e.g. gmbus and dp aux transfers. */ intel_modeset_init(dev); + /* intel_guc_ucode_init() needs the mutex to allocate GEM objects */ + mutex_lock(&dev->struct_mutex); + intel_guc_ucode_init(dev); + mutex_unlock(&dev->struct_mutex); + ret = i915_gem_init(dev); if (ret) goto cleanup_irq; @@ -476,6 +481,9 @@ static int i915_load_modeset_init(struct drm_device *dev) i915_gem_context_fini(dev); mutex_unlock(&dev->struct_mutex); cleanup_irq: + mutex_lock(&dev->struct_mutex); + intel_guc_ucode_fini(dev); + mutex_unlock(&dev->struct_mutex); drm_irq_uninstall(dev); cleanup_gem_stolen: i915_gem_cleanup_stolen(dev); @@ -1128,6 +1136,7 @@ int i915_driver_unload(struct drm_device *dev) flush_workqueue(dev_priv->wq); mutex_lock(&dev->struct_mutex); + intel_guc_ucode_fini(dev); i915_gem_cleanup_ringbuffer(dev); i915_gem_context_fini(dev); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 6b85338c4b89..61244455347f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -50,6 +50,7 @@ #include #include #include +#include "intel_guc.h" /* General customization: */ @@ -1709,6 +1710,8 @@ struct drm_i915_private { struct i915_virtual_gpu vgpu; + struct intel_guc guc; + struct intel_csr csr; /* Display CSR-related protection */ @@ -1953,6 +1956,11 @@ static inline struct drm_i915_private *dev_to_i915(struct device *dev) return to_i915(dev_get_drvdata(dev)); } +static inline struct drm_i915_private *guc_to_i915(struct intel_guc *guc) +{ + return container_of(guc, struct drm_i915_private, guc); +} + /* Iterate over initialised rings */ #define for_each_ring(ring__, dev_priv__, i__) \ for ((i__) = 0; (i__) < I915_NUM_RINGS; (i__)++) \ @@ -2557,6 +2565,9 @@ struct drm_i915_cmd_table { #define HAS_CSR(dev) (IS_SKYLAKE(dev)) +#define HAS_GUC_UCODE(dev) (IS_GEN9(dev)) +#define HAS_GUC_SCHED(dev) (IS_GEN9(dev)) + #define HAS_RESOURCE_STREAMER(dev) (IS_HASWELL(dev) || \ INTEL_INFO(dev)->gen >= 8) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 407b6b3576ae..1ac57ec803bd 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -4681,6 +4681,22 @@ i915_gem_init_hw(struct drm_device *dev) goto out; } + /* We can't enable contexts until all firmware is loaded */ + ret = intel_guc_ucode_load(dev); + if (ret) { + /* + * If we got an error and GuC submission is enabled, map + * the error to -EIO so the GPU will be declared wedged. + * OTOH, if we didn't intend to use the GuC anyway, just + * discard the error and carry on. + */ + DRM_ERROR("Failed to initialize GuC, error %d%s\n", ret, + i915.enable_guc_submission ? "" : " (ignored)"); + ret = i915.enable_guc_submission ? -EIO : 0; + if (ret) + goto out; + } + /* Now it is safe to go back round and do everything else: */ for_each_ring(ring, dev_priv, i) { struct drm_i915_gem_request *req; diff --git a/drivers/gpu/drm/i915/i915_guc_reg.h b/drivers/gpu/drm/i915/i915_guc_reg.h index ccdc6c8ac20b..8c8e574e63ba 100644 --- a/drivers/gpu/drm/i915/i915_guc_reg.h +++ b/drivers/gpu/drm/i915/i915_guc_reg.h @@ -38,10 +38,6 @@ #define GS_MIA_SHIFT 16 #define GS_MIA_MASK (0x07 << GS_MIA_SHIFT) -#define GUC_WOPCM_SIZE 0xc050 -#define GUC_WOPCM_SIZE_VALUE (0x80 << 12) /* 512KB */ -#define GUC_WOPCM_OFFSET 0x80000 /* 512KB */ - #define SOFT_SCRATCH(n) (0xc180 + ((n) * 4)) #define UOS_RSA_SCRATCH_0 0xc200 @@ -56,10 +52,18 @@ #define UOS_MOVE (1<<4) #define START_DMA (1<<0) #define DMA_GUC_WOPCM_OFFSET 0xc340 +#define GUC_WOPCM_OFFSET_VALUE 0x80000 /* 512KB */ + +#define GUC_WOPCM_SIZE 0xc050 +#define GUC_WOPCM_SIZE_VALUE (0x80 << 12) /* 512KB */ + +/* GuC addresses below GUC_WOPCM_TOP don't map through the GTT */ +#define GUC_WOPCM_TOP (GUC_WOPCM_SIZE_VALUE) #define GEN8_GT_PM_CONFIG 0x138140 +#define GEN9LP_GT_PM_CONFIG 0x138140 #define GEN9_GT_PM_CONFIG 0x13816c -#define GEN8_GT_DOORBELL_ENABLE (1<<0) +#define GT_DOORBELL_ENABLE (1<<0) #define GEN8_GTCR 0x4274 #define GEN8_GTCR_INVALIDATE (1<<0) @@ -80,7 +84,8 @@ GUC_ENABLE_READ_CACHE_LOGIC | \ GUC_ENABLE_MIA_CACHING | \ GUC_ENABLE_READ_CACHE_FOR_SRAM_DATA | \ - GUC_ENABLE_READ_CACHE_FOR_WOPCM_DATA) + GUC_ENABLE_READ_CACHE_FOR_WOPCM_DATA | \ + GUC_ENABLE_MIA_CLOCK_GATING) #define HOST2GUC_INTERRUPT 0xc4c8 #define HOST2GUC_TRIGGER (1<<0) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index f22aca3218c2..651d0dde774e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -6860,7 +6860,9 @@ enum skl_disp_power_wells { #define GEN9_PGCTL_SSB_EU311_ACK (1 << 14) #define GEN7_MISCCPCTL (0x9424) -#define GEN7_DOP_CLOCK_GATE_ENABLE (1<<0) +#define GEN7_DOP_CLOCK_GATE_ENABLE (1<<0) +#define GEN8_DOP_CLOCK_GATE_CFCLK_ENABLE (1<<2) +#define GEN8_DOP_CLOCK_GATE_GUC_ENABLE (1<<4) #define GEN8_GARBCNTL 0xB004 #define GEN9_GAPS_TSV_CREDIT_DISABLE (1<<7) diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h new file mode 100644 index 000000000000..2846b6dfdb8d --- /dev/null +++ b/drivers/gpu/drm/i915/intel_guc.h @@ -0,0 +1,67 @@ +/* + * Copyright © 2014 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ +#ifndef _INTEL_GUC_H_ +#define _INTEL_GUC_H_ + +#include "intel_guc_fwif.h" +#include "i915_guc_reg.h" + +enum intel_guc_fw_status { + GUC_FIRMWARE_FAIL = -1, + GUC_FIRMWARE_NONE = 0, + GUC_FIRMWARE_PENDING, + GUC_FIRMWARE_SUCCESS +}; + +/* + * This structure encapsulates all the data needed during the process + * of fetching, caching, and loading the firmware image into the GuC. + */ +struct intel_guc_fw { + struct drm_device * guc_dev; + const char * guc_fw_path; + size_t guc_fw_size; + struct drm_i915_gem_object * guc_fw_obj; + enum intel_guc_fw_status guc_fw_fetch_status; + enum intel_guc_fw_status guc_fw_load_status; + + uint16_t guc_fw_major_wanted; + uint16_t guc_fw_minor_wanted; + uint16_t guc_fw_major_found; + uint16_t guc_fw_minor_found; +}; + +struct intel_guc { + struct intel_guc_fw guc_fw; + + uint32_t log_flags; +}; + +/* intel_guc_loader.c */ +extern void intel_guc_ucode_init(struct drm_device *dev); +extern int intel_guc_ucode_load(struct drm_device *dev); +extern void intel_guc_ucode_fini(struct drm_device *dev); +extern const char *intel_guc_fw_status_repr(enum intel_guc_fw_status status); + +#endif diff --git a/drivers/gpu/drm/i915/intel_guc_fwif.h b/drivers/gpu/drm/i915/intel_guc_fwif.h index 18d7f20936c8..06aad6fb02ed 100644 --- a/drivers/gpu/drm/i915/intel_guc_fwif.h +++ b/drivers/gpu/drm/i915/intel_guc_fwif.h @@ -32,9 +32,8 @@ * EDITING THIS FILE IS THEREFORE NOT RECOMMENDED - YOUR CHANGES MAY BE LOST. */ -#define GFXCORE_FAMILY_GEN8 11 #define GFXCORE_FAMILY_GEN9 12 -#define GFXCORE_FAMILY_FORCE_ULONG 0x7fffffff +#define GFXCORE_FAMILY_UNKNOWN 0x7fffffff #define GUC_CTX_PRIORITY_CRITICAL 0 #define GUC_CTX_PRIORITY_HIGH 1 diff --git a/drivers/gpu/drm/i915/intel_guc_loader.c b/drivers/gpu/drm/i915/intel_guc_loader.c new file mode 100644 index 000000000000..dd62c3174a92 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_guc_loader.c @@ -0,0 +1,529 @@ +/* + * Copyright © 2014 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Vinit Azad + * Ben Widawsky + * Dave Gordon + * Alex Dai + */ +#include +#include "i915_drv.h" +#include "intel_guc.h" + +/** + * DOC: GuC + * + * intel_guc: + * Top level structure of guc. It handles firmware loading and manages client + * pool and doorbells. intel_guc owns a i915_guc_client to replace the legacy + * ExecList submission. + * + * Firmware versioning: + * The firmware build process will generate a version header file with major and + * minor version defined. The versions are built into CSS header of firmware. + * i915 kernel driver set the minimal firmware version required per platform. + * The firmware installation package will install (symbolic link) proper version + * of firmware. + * + * GuC address space: + * GuC does not allow any gfx GGTT address that falls into range [0, WOPCM_TOP), + * which is reserved for Boot ROM, SRAM and WOPCM. Currently this top address is + * 512K. In order to exclude 0-512K address space from GGTT, all gfx objects + * used by GuC is pinned with PIN_OFFSET_BIAS along with size of WOPCM. + * + * Firmware log: + * Firmware log is enabled by setting i915.guc_log_level to non-negative level. + * Log data is printed out via reading debugfs i915_guc_log_dump. Reading from + * i915_guc_load_status will print out firmware loading status and scratch + * registers value. + * + */ + +#define I915_SKL_GUC_UCODE "i915/skl_guc_ver3.bin" +MODULE_FIRMWARE(I915_SKL_GUC_UCODE); + +/* User-friendly representation of an enum */ +const char *intel_guc_fw_status_repr(enum intel_guc_fw_status status) +{ + switch (status) { + case GUC_FIRMWARE_FAIL: + return "FAIL"; + case GUC_FIRMWARE_NONE: + return "NONE"; + case GUC_FIRMWARE_PENDING: + return "PENDING"; + case GUC_FIRMWARE_SUCCESS: + return "SUCCESS"; + default: + return "UNKNOWN!"; + } +}; + +static u32 get_gttype(struct drm_i915_private *dev_priv) +{ + /* XXX: GT type based on PCI device ID? field seems unused by fw */ + return 0; +} + +static u32 get_core_family(struct drm_i915_private *dev_priv) +{ + switch (INTEL_INFO(dev_priv)->gen) { + case 9: + return GFXCORE_FAMILY_GEN9; + + default: + DRM_ERROR("GUC: unsupported core family\n"); + return GFXCORE_FAMILY_UNKNOWN; + } +} + +static void set_guc_init_params(struct drm_i915_private *dev_priv) +{ + struct intel_guc *guc = &dev_priv->guc; + u32 params[GUC_CTL_MAX_DWORDS]; + int i; + + memset(¶ms, 0, sizeof(params)); + + params[GUC_CTL_DEVICE_INFO] |= + (get_gttype(dev_priv) << GUC_CTL_GTTYPE_SHIFT) | + (get_core_family(dev_priv) << GUC_CTL_COREFAMILY_SHIFT); + + /* + * GuC ARAT increment is 10 ns. GuC default scheduler quantum is one + * second. This ARAR is calculated by: + * Scheduler-Quantum-in-ns / ARAT-increment-in-ns = 1000000000 / 10 + */ + params[GUC_CTL_ARAT_HIGH] = 0; + params[GUC_CTL_ARAT_LOW] = 100000000; + + params[GUC_CTL_WA] |= GUC_CTL_WA_UK_BY_DRIVER; + + params[GUC_CTL_FEATURE] |= GUC_CTL_DISABLE_SCHEDULER | + GUC_CTL_VCS2_ENABLED; + + if (i915.guc_log_level >= 0) { + params[GUC_CTL_LOG_PARAMS] = guc->log_flags; + params[GUC_CTL_DEBUG] = + i915.guc_log_level << GUC_LOG_VERBOSITY_SHIFT; + } + + I915_WRITE(SOFT_SCRATCH(0), 0); + + for (i = 0; i < GUC_CTL_MAX_DWORDS; i++) + I915_WRITE(SOFT_SCRATCH(1 + i), params[i]); +} + +/* + * Read the GuC status register (GUC_STATUS) and store it in the + * specified location; then return a boolean indicating whether + * the value matches either of two values representing completion + * of the GuC boot process. + * + * This is used for polling the GuC status in a wait_for_atomic() + * loop below. + */ +static inline bool guc_ucode_response(struct drm_i915_private *dev_priv, + u32 *status) +{ + u32 val = I915_READ(GUC_STATUS); + *status = val; + return ((val & GS_UKERNEL_MASK) == GS_UKERNEL_READY || + (val & GS_UKERNEL_MASK) == GS_UKERNEL_LAPIC_DONE); +} + +/* + * Transfer the firmware image to RAM for execution by the microcontroller. + * + * GuC Firmware layout: + * +-------------------------------+ ---- + * | CSS header | 128B + * | contains major/minor version | + * +-------------------------------+ ---- + * | uCode | + * +-------------------------------+ ---- + * | RSA signature | 256B + * +-------------------------------+ ---- + * | RSA public Key | 256B + * +-------------------------------+ ---- + * | Public key modulus | 4B + * +-------------------------------+ ---- + * + * Architecturally, the DMA engine is bidirectional, and can potentially even + * transfer between GTT locations. This functionality is left out of the API + * for now as there is no need for it. + * + * Note that GuC needs the CSS header plus uKernel code to be copied by the + * DMA engine in one operation, whereas the RSA signature is loaded via MMIO. + */ + +#define UOS_CSS_HEADER_OFFSET 0 +#define UOS_VER_MINOR_OFFSET 0x44 +#define UOS_VER_MAJOR_OFFSET 0x46 +#define UOS_CSS_HEADER_SIZE 0x80 +#define UOS_RSA_SIG_SIZE 0x100 +#define UOS_CSS_SIGNING_SIZE 0x204 + +static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv) +{ + struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; + struct drm_i915_gem_object *fw_obj = guc_fw->guc_fw_obj; + unsigned long offset; + struct sg_table *sg = fw_obj->pages; + u32 status, ucode_size, rsa[UOS_RSA_SIG_SIZE / sizeof(u32)]; + int i, ret = 0; + + /* uCode size, also is where RSA signature starts */ + offset = ucode_size = guc_fw->guc_fw_size - UOS_CSS_SIGNING_SIZE; + I915_WRITE(DMA_COPY_SIZE, ucode_size); + + /* Copy RSA signature from the fw image to HW for verification */ + sg_pcopy_to_buffer(sg->sgl, sg->nents, rsa, UOS_RSA_SIG_SIZE, offset); + for (i = 0; i < UOS_RSA_SIG_SIZE / sizeof(u32); i++) + I915_WRITE(UOS_RSA_SCRATCH_0 + i * sizeof(u32), rsa[i]); + + /* Set the source address for the new blob */ + offset = i915_gem_obj_ggtt_offset(fw_obj); + I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset)); + I915_WRITE(DMA_ADDR_0_HIGH, upper_32_bits(offset) & 0xFFFF); + + /* + * Set the DMA destination. Current uCode expects the code to be + * loaded at 8k; locations below this are used for the stack. + */ + I915_WRITE(DMA_ADDR_1_LOW, 0x2000); + I915_WRITE(DMA_ADDR_1_HIGH, DMA_ADDRESS_SPACE_WOPCM); + + /* Finally start the DMA */ + I915_WRITE(DMA_CTRL, _MASKED_BIT_ENABLE(UOS_MOVE | START_DMA)); + + /* + * Spin-wait for the DMA to complete & the GuC to start up. + * NB: Docs recommend not using the interrupt for completion. + * Measurements indicate this should take no more than 20ms, so a + * timeout here indicates that the GuC has failed and is unusable. + * (Higher levels of the driver will attempt to fall back to + * execlist mode if this happens.) + */ + ret = wait_for_atomic(guc_ucode_response(dev_priv, &status), 100); + + DRM_DEBUG_DRIVER("DMA status 0x%x, GuC status 0x%x\n", + I915_READ(DMA_CTRL), status); + + if ((status & GS_BOOTROM_MASK) == GS_BOOTROM_RSA_FAILED) { + DRM_ERROR("GuC firmware signature verification failed\n"); + ret = -ENOEXEC; + } + + DRM_DEBUG_DRIVER("returning %d\n", ret); + + return ret; +} + +/* + * Load the GuC firmware blob into the MinuteIA. + */ +static int guc_ucode_xfer(struct drm_i915_private *dev_priv) +{ + struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; + struct drm_device *dev = dev_priv->dev; + int ret; + + ret = i915_gem_object_set_to_gtt_domain(guc_fw->guc_fw_obj, false); + if (ret) { + DRM_DEBUG_DRIVER("set-domain failed %d\n", ret); + return ret; + } + + ret = i915_gem_obj_ggtt_pin(guc_fw->guc_fw_obj, 0, 0); + if (ret) { + DRM_DEBUG_DRIVER("pin failed %d\n", ret); + return ret; + } + + /* Invalidate GuC TLB to let GuC take the latest updates to GTT. */ + I915_WRITE(GEN8_GTCR, GEN8_GTCR_INVALIDATE); + + intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); + + /* init WOPCM */ + I915_WRITE(GUC_WOPCM_SIZE, GUC_WOPCM_SIZE_VALUE); + I915_WRITE(DMA_GUC_WOPCM_OFFSET, GUC_WOPCM_OFFSET_VALUE); + + /* Enable MIA caching. GuC clock gating is disabled. */ + I915_WRITE(GUC_SHIM_CONTROL, GUC_SHIM_CONTROL_VALUE); + + /* WaC6DisallowByGfxPause*/ + I915_WRITE(GEN6_GFXPAUSE, 0x30FFF); + + if (IS_BROXTON(dev)) + I915_WRITE(GEN9LP_GT_PM_CONFIG, GT_DOORBELL_ENABLE); + else + I915_WRITE(GEN9_GT_PM_CONFIG, GT_DOORBELL_ENABLE); + + if (IS_GEN9(dev)) { + /* DOP Clock Gating Enable for GuC clocks */ + I915_WRITE(GEN7_MISCCPCTL, (GEN8_DOP_CLOCK_GATE_GUC_ENABLE | + I915_READ(GEN7_MISCCPCTL))); + + /* allows for 5us before GT can go to RC6 */ + I915_WRITE(GUC_ARAT_C6DIS, 0x1FF); + } + + set_guc_init_params(dev_priv); + + ret = guc_ucode_xfer_dma(dev_priv); + + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); + + /* + * We keep the object pages for reuse during resume. But we can unpin it + * now that DMA has completed, so it doesn't continue to take up space. + */ + i915_gem_object_ggtt_unpin(guc_fw->guc_fw_obj); + + return ret; +} + +/** + * intel_guc_ucode_load() - load GuC uCode into the device + * @dev: drm device + * + * Called from gem_init_hw() during driver loading and also after a GPU reset. + * + * The firmware image should have already been fetched into memory by the + * earlier call to intel_guc_ucode_init(), so here we need only check that + * is succeeded, and then transfer the image to the h/w. + * + * Return: non-zero code on error + */ +int intel_guc_ucode_load(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; + int err = 0; + + DRM_DEBUG_DRIVER("GuC fw status: fetch %s, load %s\n", + intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status), + intel_guc_fw_status_repr(guc_fw->guc_fw_load_status)); + + if (guc_fw->guc_fw_fetch_status == GUC_FIRMWARE_NONE) + return 0; + + if (guc_fw->guc_fw_fetch_status == GUC_FIRMWARE_SUCCESS && + guc_fw->guc_fw_load_status == GUC_FIRMWARE_FAIL) + return -ENOEXEC; + + guc_fw->guc_fw_load_status = GUC_FIRMWARE_PENDING; + + DRM_DEBUG_DRIVER("GuC fw fetch status %s\n", + intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status)); + + switch (guc_fw->guc_fw_fetch_status) { + case GUC_FIRMWARE_FAIL: + /* something went wrong :( */ + err = -EIO; + goto fail; + + case GUC_FIRMWARE_NONE: + case GUC_FIRMWARE_PENDING: + default: + /* "can't happen" */ + WARN_ONCE(1, "GuC fw %s invalid guc_fw_fetch_status %s [%d]\n", + guc_fw->guc_fw_path, + intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status), + guc_fw->guc_fw_fetch_status); + err = -ENXIO; + goto fail; + + case GUC_FIRMWARE_SUCCESS: + break; + } + + err = guc_ucode_xfer(dev_priv); + if (err) + goto fail; + + guc_fw->guc_fw_load_status = GUC_FIRMWARE_SUCCESS; + + DRM_DEBUG_DRIVER("GuC fw status: fetch %s, load %s\n", + intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status), + intel_guc_fw_status_repr(guc_fw->guc_fw_load_status)); + + return 0; + +fail: + if (guc_fw->guc_fw_load_status == GUC_FIRMWARE_PENDING) + guc_fw->guc_fw_load_status = GUC_FIRMWARE_FAIL; + + return err; +} + +static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw) +{ + struct drm_i915_gem_object *obj; + const struct firmware *fw; + const u8 *css_header; + const size_t minsize = UOS_CSS_HEADER_SIZE + UOS_CSS_SIGNING_SIZE; + const size_t maxsize = GUC_WOPCM_SIZE_VALUE + UOS_CSS_SIGNING_SIZE + - 0x8000; /* 32k reserved (8K stack + 24k context) */ + int err; + + DRM_DEBUG_DRIVER("before requesting firmware: GuC fw fetch status %s\n", + intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status)); + + err = request_firmware(&fw, guc_fw->guc_fw_path, &dev->pdev->dev); + if (err) + goto fail; + if (!fw) + goto fail; + + DRM_DEBUG_DRIVER("fetch GuC fw from %s succeeded, fw %p\n", + guc_fw->guc_fw_path, fw); + DRM_DEBUG_DRIVER("firmware file size %zu (minimum %zu, maximum %zu)\n", + fw->size, minsize, maxsize); + + /* Check the size of the blob befoe examining buffer contents */ + if (fw->size < minsize || fw->size > maxsize) + goto fail; + + /* + * The GuC firmware image has the version number embedded at a well-known + * offset within the firmware blob; note that major / minor version are + * TWO bytes each (i.e. u16), although all pointers and offsets are defined + * in terms of bytes (u8). + */ + css_header = fw->data + UOS_CSS_HEADER_OFFSET; + guc_fw->guc_fw_major_found = *(u16 *)(css_header + UOS_VER_MAJOR_OFFSET); + guc_fw->guc_fw_minor_found = *(u16 *)(css_header + UOS_VER_MINOR_OFFSET); + + if (guc_fw->guc_fw_major_found != guc_fw->guc_fw_major_wanted || + guc_fw->guc_fw_minor_found < guc_fw->guc_fw_minor_wanted) { + DRM_ERROR("GuC firmware version %d.%d, required %d.%d\n", + guc_fw->guc_fw_major_found, guc_fw->guc_fw_minor_found, + guc_fw->guc_fw_major_wanted, guc_fw->guc_fw_minor_wanted); + err = -ENOEXEC; + goto fail; + } + + DRM_DEBUG_DRIVER("firmware version %d.%d OK (minimum %d.%d)\n", + guc_fw->guc_fw_major_found, guc_fw->guc_fw_minor_found, + guc_fw->guc_fw_major_wanted, guc_fw->guc_fw_minor_wanted); + + obj = i915_gem_object_create_from_data(dev, fw->data, fw->size); + if (IS_ERR_OR_NULL(obj)) { + err = obj ? PTR_ERR(obj) : -ENOMEM; + goto fail; + } + + guc_fw->guc_fw_obj = obj; + guc_fw->guc_fw_size = fw->size; + + DRM_DEBUG_DRIVER("GuC fw fetch status SUCCESS, obj %p\n", + guc_fw->guc_fw_obj); + + release_firmware(fw); + guc_fw->guc_fw_fetch_status = GUC_FIRMWARE_SUCCESS; + return; + +fail: + DRM_DEBUG_DRIVER("GuC fw fetch status FAIL; err %d, fw %p, obj %p\n", + err, fw, guc_fw->guc_fw_obj); + DRM_ERROR("Failed to fetch GuC firmware from %s (error %d)\n", + guc_fw->guc_fw_path, err); + + obj = guc_fw->guc_fw_obj; + if (obj) + drm_gem_object_unreference(&obj->base); + guc_fw->guc_fw_obj = NULL; + + release_firmware(fw); /* OK even if fw is NULL */ + guc_fw->guc_fw_fetch_status = GUC_FIRMWARE_FAIL; +} + +/** + * intel_guc_ucode_init() - define parameters and fetch firmware + * @dev: drm device + * + * Called early during driver load, but after GEM is initialised. + * The device struct_mutex must be held by the caller, as we're + * going to allocate a GEM object to hold the firmware image. + * + * The firmware will be transferred to the GuC's memory later, + * when intel_guc_ucode_load() is called. + */ +void intel_guc_ucode_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; + const char *fw_path; + + if (!HAS_GUC_SCHED(dev)) + i915.enable_guc_submission = false; + + if (!HAS_GUC_UCODE(dev)) { + fw_path = NULL; + } else if (IS_SKYLAKE(dev)) { + fw_path = I915_SKL_GUC_UCODE; + guc_fw->guc_fw_major_wanted = 3; + guc_fw->guc_fw_minor_wanted = 0; + } else { + i915.enable_guc_submission = false; + fw_path = ""; /* unknown device */ + } + + guc_fw->guc_dev = dev; + guc_fw->guc_fw_path = fw_path; + guc_fw->guc_fw_fetch_status = GUC_FIRMWARE_NONE; + guc_fw->guc_fw_load_status = GUC_FIRMWARE_NONE; + + if (fw_path == NULL) + return; + + if (*fw_path == '\0') { + DRM_ERROR("No GuC firmware known for this platform\n"); + guc_fw->guc_fw_fetch_status = GUC_FIRMWARE_FAIL; + return; + } + + guc_fw->guc_fw_fetch_status = GUC_FIRMWARE_PENDING; + DRM_DEBUG_DRIVER("GuC firmware pending, path %s\n", fw_path); + guc_fw_fetch(dev, guc_fw); + /* status must now be FAIL or SUCCESS */ +} + +/** + * intel_guc_ucode_fini() - clean up all allocated resources + * @dev: drm device + */ +void intel_guc_ucode_fini(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; + + if (guc_fw->guc_fw_obj) + drm_gem_object_unreference(&guc_fw->guc_fw_obj->base); + guc_fw->guc_fw_obj = NULL; + + guc_fw->guc_fw_fetch_status = GUC_FIRMWARE_NONE; +} From fdf5d3572fd1f2103a6bda0dba643e76a3ea978e Mon Sep 17 00:00:00 2001 From: Alex Dai Date: Wed, 12 Aug 2015 15:43:37 +0100 Subject: [PATCH 035/145] drm/i915: Debugfs interface to read GuC load status The new node provides access to the status of the GuC-specific loader; also the scratch registers used for communication between the i915 driver and the GuC firmware. v2: Changes to output formats per Chris Wilson's suggestions v6: Rebased Issue: VIZ-4884 Signed-off-by: Alex Dai Signed-off-by: Dave Gordon Reviewed-by: Tom O'Rourke Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 95e7b82f05d1..1946411e90e3 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2374,6 +2374,44 @@ static int i915_llc(struct seq_file *m, void *data) return 0; } +static int i915_guc_load_status_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = m->private; + struct drm_i915_private *dev_priv = node->minor->dev->dev_private; + struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; + u32 tmp, i; + + if (!HAS_GUC_UCODE(dev_priv->dev)) + return 0; + + seq_printf(m, "GuC firmware status:\n"); + seq_printf(m, "\tpath: %s\n", + guc_fw->guc_fw_path); + seq_printf(m, "\tfetch: %s\n", + intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status)); + seq_printf(m, "\tload: %s\n", + intel_guc_fw_status_repr(guc_fw->guc_fw_load_status)); + seq_printf(m, "\tversion wanted: %d.%d\n", + guc_fw->guc_fw_major_wanted, guc_fw->guc_fw_minor_wanted); + seq_printf(m, "\tversion found: %d.%d\n", + guc_fw->guc_fw_major_found, guc_fw->guc_fw_minor_found); + + tmp = I915_READ(GUC_STATUS); + + seq_printf(m, "\nGuC status 0x%08x:\n", tmp); + seq_printf(m, "\tBootrom status = 0x%x\n", + (tmp & GS_BOOTROM_MASK) >> GS_BOOTROM_SHIFT); + seq_printf(m, "\tuKernel status = 0x%x\n", + (tmp & GS_UKERNEL_MASK) >> GS_UKERNEL_SHIFT); + seq_printf(m, "\tMIA Core status = 0x%x\n", + (tmp & GS_MIA_MASK) >> GS_MIA_SHIFT); + seq_puts(m, "\nScratch registers:\n"); + for (i = 0; i < 16; i++) + seq_printf(m, "\t%2d: \t0x%x\n", i, I915_READ(SOFT_SCRATCH(i))); + + return 0; +} + static int i915_edp_psr_status(struct seq_file *m, void *data) { struct drm_info_node *node = m->private; @@ -5033,6 +5071,7 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_gem_hws_bsd", i915_hws_info, 0, (void *)VCS}, {"i915_gem_hws_vebox", i915_hws_info, 0, (void *)VECS}, {"i915_gem_batch_pool", i915_gem_batch_pool_info, 0}, + {"i915_guc_load_status", i915_guc_load_status_info, 0}, {"i915_frequency_info", i915_frequency_info, 0}, {"i915_hangcheck_info", i915_hangcheck_info, 0}, {"i915_drpc_info", i915_drpc_info, 0}, From 919f1f55d90b5487a9f38e94842e486509474f09 Mon Sep 17 00:00:00 2001 From: Dave Gordon Date: Wed, 12 Aug 2015 15:43:38 +0100 Subject: [PATCH 036/145] drm/i915: Expose one LRC function for GuC submission mode GuC submission is basically execlist submission, but with the GuC handling the actual writes to the ELSP and the resulting context switch interrupts. So to describe a context for submission via the GuC, we need one of the same functions used in execlist mode. This commit exposes one such function, changing its name to better describe what it does (it's related to logical ring contexts rather than to execlists per se). v2: Replaces previous "drm/i915: Move execlists defines from .c to .h" v3: Incorporates a change to one of the functions exposed here that was previously part of an internal patch, but which was omitted from the version recently committed to drm-intel-nightly: 7a01a0a drm/i915/lrc: Update PDPx registers with lri commands So we reinstate this change here. v4: Drop v3 change, update function parameters due to collision with 8ee3615 drm/i915: Convert execlists_ctx_descriptor() for requests v5: Don't expose execlists_update_context() after all. The current version is no longer compatible with GuC submission; trying to share the execlist version of this function results in both GuC and CPU updating TAIL in the context image, with bad results when they get out of step. The GuC submission path now has its own private version that just updates the ringbuffer start address, and not TAIL or PDPx. v6: Rebased Issue: VIZ-4884 Signed-off-by: Dave Gordon Reviewed-by: Tom O'Rourke Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_lrc.c | 10 +++++----- drivers/gpu/drm/i915/intel_lrc.h | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 138964afd187..f3411f835a5a 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -270,11 +270,11 @@ u32 intel_execlists_ctx_id(struct drm_i915_gem_object *ctx_obj) return lrca >> 12; } -static uint64_t execlists_ctx_descriptor(struct drm_i915_gem_request *rq) +uint64_t intel_lr_context_descriptor(struct intel_context *ctx, + struct intel_engine_cs *ring) { - struct intel_engine_cs *ring = rq->ring; struct drm_device *dev = ring->dev; - struct drm_i915_gem_object *ctx_obj = rq->ctx->engine[ring->id].state; + struct drm_i915_gem_object *ctx_obj = ctx->engine[ring->id].state; uint64_t desc; uint64_t lrca = i915_gem_obj_ggtt_offset(ctx_obj); @@ -312,13 +312,13 @@ static void execlists_elsp_write(struct drm_i915_gem_request *rq0, uint64_t desc[2]; if (rq1) { - desc[1] = execlists_ctx_descriptor(rq1); + desc[1] = intel_lr_context_descriptor(rq1->ctx, rq1->ring); rq1->elsp_submitted++; } else { desc[1] = 0; } - desc[0] = execlists_ctx_descriptor(rq0); + desc[0] = intel_lr_context_descriptor(rq0->ctx, rq0->ring); rq0->elsp_submitted++; /* You must always write both descriptors in the order below. */ diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h index 64f89f9982a2..5e5788c83eeb 100644 --- a/drivers/gpu/drm/i915/intel_lrc.h +++ b/drivers/gpu/drm/i915/intel_lrc.h @@ -74,6 +74,8 @@ int intel_lr_context_deferred_create(struct intel_context *ctx, void intel_lr_context_unpin(struct drm_i915_gem_request *req); void intel_lr_context_reset(struct drm_device *dev, struct intel_context *ctx); +uint64_t intel_lr_context_descriptor(struct intel_context *ctx, + struct intel_engine_cs *ring); /* Execlists */ int intel_sanitize_enable_execlists(struct drm_device *dev, int enable_execlists); From bac427f8abfc8c11e10274d5edd9db4e4b8e74d3 Mon Sep 17 00:00:00 2001 From: Alex Dai Date: Wed, 12 Aug 2015 15:43:39 +0100 Subject: [PATCH 037/145] drm/i915: Prepare for GuC-based command submission This adds the first of the data structures used to communicate with the GuC (the pool of guc_context structures). We create a GuC-specific wrapper round the GEM object allocator as all GEM objects shared with the GuC must be pinned into GGTT space at an address that is NOT in the range [0..WOPCM_TOP), as that range of GGTT addresses is not accessible to the GuC (from the GuC's point of view, it's permanently reserved for other objects such as the BootROM & SRAM). Later, we will need to allocate additional GuC-sharable objects for the submission client(s) and the GuC's debug log. v2: Remove redundant initialisation [Chris Wilson] Defer adding struct members until needed [Chris Wilson] Local functions should pass dev_priv rather than dev [Chris Wilson] v5: Invalidate GuC TLB after allocating and pinning a new object v6: Rebased Issue: VIZ-4884 Signed-off-by: Alex Dai Signed-off-by: Dave Gordon Reviewed-by: Tom O'Rourke Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/Makefile | 3 +- drivers/gpu/drm/i915/i915_guc_submission.c | 118 +++++++++++++++++++++ drivers/gpu/drm/i915/intel_guc.h | 7 ++ drivers/gpu/drm/i915/intel_guc_loader.c | 21 ++++ 4 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/i915/i915_guc_submission.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index cc359e0e6678..ddb69f337dc6 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -41,7 +41,8 @@ i915-y += i915_cmd_parser.o \ intel_uncore.o # general-purpose microcontroller (GuC) support -i915-y += intel_guc_loader.o +i915-y += intel_guc_loader.o \ + i915_guc_submission.o # autogenerated null render state i915-y += intel_renderstate_gen6.o \ diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c new file mode 100644 index 000000000000..8ff59aa690f9 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_guc_submission.c @@ -0,0 +1,118 @@ +/* + * Copyright © 2014 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ +#include +#include +#include "i915_drv.h" +#include "intel_guc.h" + +/** + * gem_allocate_guc_obj() - Allocate gem object for GuC usage + * @dev: drm device + * @size: size of object + * + * This is a wrapper to create a gem obj. In order to use it inside GuC, the + * object needs to be pinned lifetime. Also we must pin it to gtt space other + * than [0, GUC_WOPCM_TOP) because this range is reserved inside GuC. + * + * Return: A drm_i915_gem_object if successful, otherwise NULL. + */ +static struct drm_i915_gem_object *gem_allocate_guc_obj(struct drm_device *dev, + u32 size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj; + + obj = i915_gem_alloc_object(dev, size); + if (!obj) + return NULL; + + if (i915_gem_object_get_pages(obj)) { + drm_gem_object_unreference(&obj->base); + return NULL; + } + + if (i915_gem_obj_ggtt_pin(obj, PAGE_SIZE, + PIN_OFFSET_BIAS | GUC_WOPCM_TOP)) { + drm_gem_object_unreference(&obj->base); + return NULL; + } + + /* Invalidate GuC TLB to let GuC take the latest updates to GTT. */ + I915_WRITE(GEN8_GTCR, GEN8_GTCR_INVALIDATE); + + return obj; +} + +/** + * gem_release_guc_obj() - Release gem object allocated for GuC usage + * @obj: gem obj to be released + */ +static void gem_release_guc_obj(struct drm_i915_gem_object *obj) +{ + if (!obj) + return; + + if (i915_gem_obj_is_pinned(obj)) + i915_gem_object_ggtt_unpin(obj); + + drm_gem_object_unreference(&obj->base); +} + +/* + * Set up the memory resources to be shared with the GuC. At this point, + * we require just one object that can be mapped through the GGTT. + */ +int i915_guc_submission_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + const size_t ctxsize = sizeof(struct guc_context_desc); + const size_t poolsize = GUC_MAX_GPU_CONTEXTS * ctxsize; + const size_t gemsize = round_up(poolsize, PAGE_SIZE); + struct intel_guc *guc = &dev_priv->guc; + + if (!i915.enable_guc_submission) + return 0; /* not enabled */ + + if (guc->ctx_pool_obj) + return 0; /* already allocated */ + + guc->ctx_pool_obj = gem_allocate_guc_obj(dev_priv->dev, gemsize); + if (!guc->ctx_pool_obj) + return -ENOMEM; + + ida_init(&guc->ctx_ids); + + return 0; +} + +void i915_guc_submission_fini(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_guc *guc = &dev_priv->guc; + + if (guc->ctx_pool_obj) + ida_destroy(&guc->ctx_ids); + gem_release_guc_obj(guc->ctx_pool_obj); + guc->ctx_pool_obj = NULL; +} diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h index 2846b6dfdb8d..be3cad8dad98 100644 --- a/drivers/gpu/drm/i915/intel_guc.h +++ b/drivers/gpu/drm/i915/intel_guc.h @@ -56,6 +56,9 @@ struct intel_guc { struct intel_guc_fw guc_fw; uint32_t log_flags; + + struct drm_i915_gem_object *ctx_pool_obj; + struct ida ctx_ids; }; /* intel_guc_loader.c */ @@ -64,4 +67,8 @@ extern int intel_guc_ucode_load(struct drm_device *dev); extern void intel_guc_ucode_fini(struct drm_device *dev); extern const char *intel_guc_fw_status_repr(enum intel_guc_fw_status status); +/* i915_guc_submission.c */ +int i915_guc_submission_init(struct drm_device *dev); +void i915_guc_submission_fini(struct drm_device *dev); + #endif diff --git a/drivers/gpu/drm/i915/intel_guc_loader.c b/drivers/gpu/drm/i915/intel_guc_loader.c index dd62c3174a92..6ff7feab3c26 100644 --- a/drivers/gpu/drm/i915/intel_guc_loader.c +++ b/drivers/gpu/drm/i915/intel_guc_loader.c @@ -128,6 +128,21 @@ static void set_guc_init_params(struct drm_i915_private *dev_priv) i915.guc_log_level << GUC_LOG_VERBOSITY_SHIFT; } + /* If GuC submission is enabled, set up additional parameters here */ + if (i915.enable_guc_submission) { + u32 pgs = i915_gem_obj_ggtt_offset(dev_priv->guc.ctx_pool_obj); + u32 ctx_in_16 = GUC_MAX_GPU_CONTEXTS / 16; + + pgs >>= PAGE_SHIFT; + params[GUC_CTL_CTXINFO] = (pgs << GUC_CTL_BASE_ADDR_SHIFT) | + (ctx_in_16 << GUC_CTL_CTXNUM_IN16_SHIFT); + + params[GUC_CTL_FEATURE] |= GUC_CTL_KERNEL_SUBMISSIONS; + + /* Unmask this bit to enable the GuC's internal scheduler */ + params[GUC_CTL_FEATURE] &= ~GUC_CTL_DISABLE_SCHEDULER; + } + I915_WRITE(SOFT_SCRATCH(0), 0); for (i = 0; i < GUC_CTL_MAX_DWORDS; i++) @@ -360,6 +375,10 @@ int intel_guc_ucode_load(struct drm_device *dev) break; } + err = i915_guc_submission_init(dev); + if (err) + goto fail; + err = guc_ucode_xfer(dev_priv); if (err) goto fail; @@ -521,6 +540,8 @@ void intel_guc_ucode_fini(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; + i915_guc_submission_fini(dev); + if (guc_fw->guc_fw_obj) drm_gem_object_unreference(&guc_fw->guc_fw_obj->base); guc_fw->guc_fw_obj = NULL; From 4c7e77fc1007b1e12d25c6e5083369863346f5dd Mon Sep 17 00:00:00 2001 From: Alex Dai Date: Wed, 12 Aug 2015 15:43:40 +0100 Subject: [PATCH 038/145] drm/i915: Enable GuC firmware log Allocate a GEM object to hold GuC log data. A debugfs interface (i915_guc_log_dump) is provided to print out the log content. v2: Add struct members at point of use [Chris Wilson] v6: Rebased Issue: VIZ-4884 Signed-off-by: Alex Dai Signed-off-by: Dave Gordon Reviewed-by: Tom O'Rourke Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 29 ++++++++++++++ drivers/gpu/drm/i915/i915_guc_submission.c | 46 ++++++++++++++++++++++ drivers/gpu/drm/i915/intel_guc.h | 1 + 3 files changed, 76 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 1946411e90e3..1123b8e3b10a 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2412,6 +2412,34 @@ static int i915_guc_load_status_info(struct seq_file *m, void *data) return 0; } +static int i915_guc_log_dump(struct seq_file *m, void *data) +{ + struct drm_info_node *node = m->private; + struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *log_obj = dev_priv->guc.log_obj; + u32 *log; + int i = 0, pg; + + if (!log_obj) + return 0; + + for (pg = 0; pg < log_obj->base.size / PAGE_SIZE; pg++) { + log = kmap_atomic(i915_gem_object_get_page(log_obj, pg)); + + for (i = 0; i < PAGE_SIZE / sizeof(u32); i += 4) + seq_printf(m, "0x%08x 0x%08x 0x%08x 0x%08x\n", + *(log + i), *(log + i + 1), + *(log + i + 2), *(log + i + 3)); + + kunmap_atomic(log); + } + + seq_putc(m, '\n'); + + return 0; +} + static int i915_edp_psr_status(struct seq_file *m, void *data) { struct drm_info_node *node = m->private; @@ -5072,6 +5100,7 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_gem_hws_vebox", i915_hws_info, 0, (void *)VECS}, {"i915_gem_batch_pool", i915_gem_batch_pool_info, 0}, {"i915_guc_load_status", i915_guc_load_status_info, 0}, + {"i915_guc_log_dump", i915_guc_log_dump, 0}, {"i915_frequency_info", i915_frequency_info, 0}, {"i915_hangcheck_info", i915_hangcheck_info, 0}, {"i915_drpc_info", i915_drpc_info, 0}, diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c index 8ff59aa690f9..669c889ce82a 100644 --- a/drivers/gpu/drm/i915/i915_guc_submission.c +++ b/drivers/gpu/drm/i915/i915_guc_submission.c @@ -79,6 +79,47 @@ static void gem_release_guc_obj(struct drm_i915_gem_object *obj) drm_gem_object_unreference(&obj->base); } +static void guc_create_log(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + struct drm_i915_gem_object *obj; + unsigned long offset; + uint32_t size, flags; + + if (i915.guc_log_level < GUC_LOG_VERBOSITY_MIN) + return; + + if (i915.guc_log_level > GUC_LOG_VERBOSITY_MAX) + i915.guc_log_level = GUC_LOG_VERBOSITY_MAX; + + /* The first page is to save log buffer state. Allocate one + * extra page for others in case for overlap */ + size = (1 + GUC_LOG_DPC_PAGES + 1 + + GUC_LOG_ISR_PAGES + 1 + + GUC_LOG_CRASH_PAGES + 1) << PAGE_SHIFT; + + obj = guc->log_obj; + if (!obj) { + obj = gem_allocate_guc_obj(dev_priv->dev, size); + if (!obj) { + /* logging will be off */ + i915.guc_log_level = -1; + return; + } + + guc->log_obj = obj; + } + + /* each allocated unit is a page */ + flags = GUC_LOG_VALID | GUC_LOG_NOTIFY_ON_HALF_FULL | + (GUC_LOG_DPC_PAGES << GUC_LOG_DPC_SHIFT) | + (GUC_LOG_ISR_PAGES << GUC_LOG_ISR_SHIFT) | + (GUC_LOG_CRASH_PAGES << GUC_LOG_CRASH_SHIFT); + + offset = i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT; /* in pages */ + guc->log_flags = (offset << GUC_LOG_BUF_ADDR_SHIFT) | flags; +} + /* * Set up the memory resources to be shared with the GuC. At this point, * we require just one object that can be mapped through the GGTT. @@ -103,6 +144,8 @@ int i915_guc_submission_init(struct drm_device *dev) ida_init(&guc->ctx_ids); + guc_create_log(guc); + return 0; } @@ -111,6 +154,9 @@ void i915_guc_submission_fini(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_guc *guc = &dev_priv->guc; + gem_release_guc_obj(dev_priv->guc.log_obj); + guc->log_obj = NULL; + if (guc->ctx_pool_obj) ida_destroy(&guc->ctx_ids); gem_release_guc_obj(guc->ctx_pool_obj); diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h index be3cad8dad98..5b51b05777cd 100644 --- a/drivers/gpu/drm/i915/intel_guc.h +++ b/drivers/gpu/drm/i915/intel_guc.h @@ -56,6 +56,7 @@ struct intel_guc { struct intel_guc_fw guc_fw; uint32_t log_flags; + struct drm_i915_gem_object *log_obj; struct drm_i915_gem_object *ctx_pool_obj; struct ida ctx_ids; From 44a28b1d36762499de6fd701fcce6814eefe31d7 Mon Sep 17 00:00:00 2001 From: Dave Gordon Date: Wed, 12 Aug 2015 15:43:41 +0100 Subject: [PATCH 039/145] drm/i915: Implementation of GuC submission client A GuC client has its own doorbell and workqueue. It maintains the doorbell cache line, process description object and work queue item. A default guc_client is created for the i915 driver to use for normal-priority in-order submission. Note that the created client is not yet ready for use; doorbell allocation will fail as we haven't yet linked the GuC's context descriptor to the default contexts for each ring (see later patch). v2: Defer adding structure members until needed [Chris Wilson] Rationalise type declarations [Chris Wilson] v5: Add GuC per-engine submission & seqno statistics. Move wq locking to encompass both get_space() and add_item(). Take forcewake lock in host2guc_action() [Tom O'Rourke] v6: Fix GuC doorbell cacheline selection code (the cacheline-within-page calculation was wrong). Rename GuC priorities to make them closer to the names used in the GuC firmware source, matching what the autogenerated versions will (probably) be. Add per-ring statistics to client. Issue: VIZ-4884 Signed-off-by: Alex Dai Signed-off-by: Dave Gordon Reviewed-by: Tom O'Rourke Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_guc_submission.c | 666 +++++++++++++++++++++ drivers/gpu/drm/i915/intel_guc.h | 46 ++ drivers/gpu/drm/i915/intel_guc_fwif.h | 6 +- drivers/gpu/drm/i915/intel_guc_loader.c | 10 + 4 files changed, 725 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c index 669c889ce82a..3352b8521ddd 100644 --- a/drivers/gpu/drm/i915/i915_guc_submission.c +++ b/drivers/gpu/drm/i915/i915_guc_submission.c @@ -26,6 +26,529 @@ #include "i915_drv.h" #include "intel_guc.h" +/** + * DOC: GuC Client + * + * i915_guc_client: + * We use the term client to avoid confusion with contexts. A i915_guc_client is + * equivalent to GuC object guc_context_desc. This context descriptor is + * allocated from a pool of 1024 entries. Kernel driver will allocate doorbell + * and workqueue for it. Also the process descriptor (guc_process_desc), which + * is mapped to client space. So the client can write Work Item then ring the + * doorbell. + * + * To simplify the implementation, we allocate one gem object that contains all + * pages for doorbell, process descriptor and workqueue. + * + * The Scratch registers: + * There are 16 MMIO-based registers start from 0xC180. The kernel driver writes + * a value to the action register (SOFT_SCRATCH_0) along with any data. It then + * triggers an interrupt on the GuC via another register write (0xC4C8). + * Firmware writes a success/fail code back to the action register after + * processes the request. The kernel driver polls waiting for this update and + * then proceeds. + * See host2guc_action() + * + * Doorbells: + * Doorbells are interrupts to uKernel. A doorbell is a single cache line (QW) + * mapped into process space. + * + * Work Items: + * There are several types of work items that the host may place into a + * workqueue, each with its own requirements and limitations. Currently only + * WQ_TYPE_INORDER is needed to support legacy submission via GuC, which + * represents in-order queue. The kernel driver packs ring tail pointer and an + * ELSP context descriptor dword into Work Item. + * See guc_add_workqueue_item() + * + */ + +/* + * Read GuC command/status register (SOFT_SCRATCH_0) + * Return true if it contains a response rather than a command + */ +static inline bool host2guc_action_response(struct drm_i915_private *dev_priv, + u32 *status) +{ + u32 val = I915_READ(SOFT_SCRATCH(0)); + *status = val; + return GUC2HOST_IS_RESPONSE(val); +} + +static int host2guc_action(struct intel_guc *guc, u32 *data, u32 len) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + u32 status; + int i; + int ret; + + if (WARN_ON(len < 1 || len > 15)) + return -EINVAL; + + intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); + spin_lock(&dev_priv->guc.host2guc_lock); + + dev_priv->guc.action_count += 1; + dev_priv->guc.action_cmd = data[0]; + + for (i = 0; i < len; i++) + I915_WRITE(SOFT_SCRATCH(i), data[i]); + + POSTING_READ(SOFT_SCRATCH(i - 1)); + + I915_WRITE(HOST2GUC_INTERRUPT, HOST2GUC_TRIGGER); + + /* No HOST2GUC command should take longer than 10ms */ + ret = wait_for_atomic(host2guc_action_response(dev_priv, &status), 10); + if (status != GUC2HOST_STATUS_SUCCESS) { + /* + * Either the GuC explicitly returned an error (which + * we convert to -EIO here) or no response at all was + * received within the timeout limit (-ETIMEDOUT) + */ + if (ret != -ETIMEDOUT) + ret = -EIO; + + DRM_ERROR("GUC: host2guc action 0x%X failed. ret=%d " + "status=0x%08X response=0x%08X\n", + data[0], ret, status, + I915_READ(SOFT_SCRATCH(15))); + + dev_priv->guc.action_fail += 1; + dev_priv->guc.action_err = ret; + } + dev_priv->guc.action_status = status; + + spin_unlock(&dev_priv->guc.host2guc_lock); + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); + + return ret; +} + +/* + * Tell the GuC to allocate or deallocate a specific doorbell + */ + +static int host2guc_allocate_doorbell(struct intel_guc *guc, + struct i915_guc_client *client) +{ + u32 data[2]; + + data[0] = HOST2GUC_ACTION_ALLOCATE_DOORBELL; + data[1] = client->ctx_index; + + return host2guc_action(guc, data, 2); +} + +static int host2guc_release_doorbell(struct intel_guc *guc, + struct i915_guc_client *client) +{ + u32 data[2]; + + data[0] = HOST2GUC_ACTION_DEALLOCATE_DOORBELL; + data[1] = client->ctx_index; + + return host2guc_action(guc, data, 2); +} + +/* + * Initialise, update, or clear doorbell data shared with the GuC + * + * These functions modify shared data and so need access to the mapped + * client object which contains the page being used for the doorbell + */ + +static void guc_init_doorbell(struct intel_guc *guc, + struct i915_guc_client *client) +{ + struct guc_doorbell_info *doorbell; + void *base; + + base = kmap_atomic(i915_gem_object_get_page(client->client_obj, 0)); + doorbell = base + client->doorbell_offset; + + doorbell->db_status = 1; + doorbell->cookie = 0; + + kunmap_atomic(base); +} + +static int guc_ring_doorbell(struct i915_guc_client *gc) +{ + struct guc_process_desc *desc; + union guc_doorbell_qw db_cmp, db_exc, db_ret; + union guc_doorbell_qw *db; + void *base; + int attempt = 2, ret = -EAGAIN; + + base = kmap_atomic(i915_gem_object_get_page(gc->client_obj, 0)); + desc = base + gc->proc_desc_offset; + + /* Update the tail so it is visible to GuC */ + desc->tail = gc->wq_tail; + + /* current cookie */ + db_cmp.db_status = GUC_DOORBELL_ENABLED; + db_cmp.cookie = gc->cookie; + + /* cookie to be updated */ + db_exc.db_status = GUC_DOORBELL_ENABLED; + db_exc.cookie = gc->cookie + 1; + if (db_exc.cookie == 0) + db_exc.cookie = 1; + + /* pointer of current doorbell cacheline */ + db = base + gc->doorbell_offset; + + while (attempt--) { + /* lets ring the doorbell */ + db_ret.value_qw = atomic64_cmpxchg((atomic64_t *)db, + db_cmp.value_qw, db_exc.value_qw); + + /* if the exchange was successfully executed */ + if (db_ret.value_qw == db_cmp.value_qw) { + /* db was successfully rung */ + gc->cookie = db_exc.cookie; + ret = 0; + break; + } + + /* XXX: doorbell was lost and need to acquire it again */ + if (db_ret.db_status == GUC_DOORBELL_DISABLED) + break; + + DRM_ERROR("Cookie mismatch. Expected %d, returned %d\n", + db_cmp.cookie, db_ret.cookie); + + /* update the cookie to newly read cookie from GuC */ + db_cmp.cookie = db_ret.cookie; + db_exc.cookie = db_ret.cookie + 1; + if (db_exc.cookie == 0) + db_exc.cookie = 1; + } + + kunmap_atomic(base); + return ret; +} + +static void guc_disable_doorbell(struct intel_guc *guc, + struct i915_guc_client *client) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + struct guc_doorbell_info *doorbell; + void *base; + int drbreg = GEN8_DRBREGL(client->doorbell_id); + int value; + + base = kmap_atomic(i915_gem_object_get_page(client->client_obj, 0)); + doorbell = base + client->doorbell_offset; + + doorbell->db_status = 0; + + kunmap_atomic(base); + + I915_WRITE(drbreg, I915_READ(drbreg) & ~GEN8_DRB_VALID); + + value = I915_READ(drbreg); + WARN_ON((value & GEN8_DRB_VALID) != 0); + + I915_WRITE(GEN8_DRBREGU(client->doorbell_id), 0); + I915_WRITE(drbreg, 0); + + /* XXX: wait for any interrupts */ + /* XXX: wait for workqueue to drain */ +} + +/* + * Select, assign and relase doorbell cachelines + * + * These functions track which doorbell cachelines are in use. + * The data they manipulate is protected by the host2guc lock. + */ + +static uint32_t select_doorbell_cacheline(struct intel_guc *guc) +{ + const uint32_t cacheline_size = cache_line_size(); + uint32_t offset; + + spin_lock(&guc->host2guc_lock); + + /* Doorbell uses a single cache line within a page */ + offset = offset_in_page(guc->db_cacheline); + + /* Moving to next cache line to reduce contention */ + guc->db_cacheline += cacheline_size; + + spin_unlock(&guc->host2guc_lock); + + DRM_DEBUG_DRIVER("selected doorbell cacheline 0x%x, next 0x%x, linesize %u\n", + offset, guc->db_cacheline, cacheline_size); + + return offset; +} + +static uint16_t assign_doorbell(struct intel_guc *guc, uint32_t priority) +{ + /* + * The bitmap is split into two halves; the first half is used for + * normal priority contexts, the second half for high-priority ones. + * Note that logically higher priorities are numerically less than + * normal ones, so the test below means "is it high-priority?" + */ + const bool hi_pri = (priority <= GUC_CTX_PRIORITY_HIGH); + const uint16_t half = GUC_MAX_DOORBELLS / 2; + const uint16_t start = hi_pri ? half : 0; + const uint16_t end = start + half; + uint16_t id; + + spin_lock(&guc->host2guc_lock); + id = find_next_zero_bit(guc->doorbell_bitmap, end, start); + if (id == end) + id = GUC_INVALID_DOORBELL_ID; + else + bitmap_set(guc->doorbell_bitmap, id, 1); + spin_unlock(&guc->host2guc_lock); + + DRM_DEBUG_DRIVER("assigned %s priority doorbell id 0x%x\n", + hi_pri ? "high" : "normal", id); + + return id; +} + +static void release_doorbell(struct intel_guc *guc, uint16_t id) +{ + spin_lock(&guc->host2guc_lock); + bitmap_clear(guc->doorbell_bitmap, id, 1); + spin_unlock(&guc->host2guc_lock); +} + +/* + * Initialise the process descriptor shared with the GuC firmware. + */ +static void guc_init_proc_desc(struct intel_guc *guc, + struct i915_guc_client *client) +{ + struct guc_process_desc *desc; + void *base; + + base = kmap_atomic(i915_gem_object_get_page(client->client_obj, 0)); + desc = base + client->proc_desc_offset; + + memset(desc, 0, sizeof(*desc)); + + /* + * XXX: pDoorbell and WQVBaseAddress are pointers in process address + * space for ring3 clients (set them as in mmap_ioctl) or kernel + * space for kernel clients (map on demand instead? May make debug + * easier to have it mapped). + */ + desc->wq_base_addr = 0; + desc->db_base_addr = 0; + + desc->context_id = client->ctx_index; + desc->wq_size_bytes = client->wq_size; + desc->wq_status = WQ_STATUS_ACTIVE; + desc->priority = client->priority; + + kunmap_atomic(base); +} + +/* + * Initialise/clear the context descriptor shared with the GuC firmware. + * + * This descriptor tells the GuC where (in GGTT space) to find the important + * data structures relating to this client (doorbell, process descriptor, + * write queue, etc). + */ + +static void guc_init_ctx_desc(struct intel_guc *guc, + struct i915_guc_client *client) +{ + struct guc_context_desc desc; + struct sg_table *sg; + + memset(&desc, 0, sizeof(desc)); + + desc.attribute = GUC_CTX_DESC_ATTR_ACTIVE | GUC_CTX_DESC_ATTR_KERNEL; + desc.context_id = client->ctx_index; + desc.priority = client->priority; + desc.engines_used = (1 << RCS) | (1 << VCS) | (1 << BCS) | + (1 << VECS) | (1 << VCS2); /* all engines */ + desc.db_id = client->doorbell_id; + + /* + * The CPU address is only needed at certain points, so kmap_atomic on + * demand instead of storing it in the ctx descriptor. + * XXX: May make debug easier to have it mapped + */ + desc.db_trigger_cpu = 0; + desc.db_trigger_uk = client->doorbell_offset + + i915_gem_obj_ggtt_offset(client->client_obj); + desc.db_trigger_phy = client->doorbell_offset + + sg_dma_address(client->client_obj->pages->sgl); + + desc.process_desc = client->proc_desc_offset + + i915_gem_obj_ggtt_offset(client->client_obj); + + desc.wq_addr = client->wq_offset + + i915_gem_obj_ggtt_offset(client->client_obj); + + desc.wq_size = client->wq_size; + + /* + * XXX: Take LRCs from an existing intel_context if this is not an + * IsKMDCreatedContext client + */ + desc.desc_private = (uintptr_t)client; + + /* Pool context is pinned already */ + sg = guc->ctx_pool_obj->pages; + sg_pcopy_from_buffer(sg->sgl, sg->nents, &desc, sizeof(desc), + sizeof(desc) * client->ctx_index); +} + +static void guc_fini_ctx_desc(struct intel_guc *guc, + struct i915_guc_client *client) +{ + struct guc_context_desc desc; + struct sg_table *sg; + + memset(&desc, 0, sizeof(desc)); + + sg = guc->ctx_pool_obj->pages; + sg_pcopy_from_buffer(sg->sgl, sg->nents, &desc, sizeof(desc), + sizeof(desc) * client->ctx_index); +} + +/* Get valid workqueue item and return it back to offset */ +static int guc_get_workqueue_space(struct i915_guc_client *gc, u32 *offset) +{ + struct guc_process_desc *desc; + void *base; + u32 size = sizeof(struct guc_wq_item); + int ret = 0, timeout_counter = 200; + + base = kmap_atomic(i915_gem_object_get_page(gc->client_obj, 0)); + desc = base + gc->proc_desc_offset; + + while (timeout_counter-- > 0) { + ret = wait_for_atomic(CIRC_SPACE(gc->wq_tail, desc->head, + gc->wq_size) >= size, 1); + + if (!ret) { + *offset = gc->wq_tail; + + /* advance the tail for next workqueue item */ + gc->wq_tail += size; + gc->wq_tail &= gc->wq_size - 1; + + /* this will break the loop */ + timeout_counter = 0; + } + }; + + kunmap_atomic(base); + + return ret; +} + +static int guc_add_workqueue_item(struct i915_guc_client *gc, + struct drm_i915_gem_request *rq) +{ + enum intel_ring_id ring_id = rq->ring->id; + struct guc_wq_item *wqi; + void *base; + u32 tail, wq_len, wq_off = 0; + int ret; + + ret = guc_get_workqueue_space(gc, &wq_off); + if (ret) + return ret; + + /* For now workqueue item is 4 DWs; workqueue buffer is 2 pages. So we + * should not have the case where structure wqi is across page, neither + * wrapped to the beginning. This simplifies the implementation below. + * + * XXX: if not the case, we need save data to a temp wqi and copy it to + * workqueue buffer dw by dw. + */ + WARN_ON(sizeof(struct guc_wq_item) != 16); + WARN_ON(wq_off & 3); + + /* wq starts from the page after doorbell / process_desc */ + base = kmap_atomic(i915_gem_object_get_page(gc->client_obj, + (wq_off + GUC_DB_SIZE) >> PAGE_SHIFT)); + wq_off &= PAGE_SIZE - 1; + wqi = (struct guc_wq_item *)((char *)base + wq_off); + + /* len does not include the header */ + wq_len = sizeof(struct guc_wq_item) / sizeof(u32) - 1; + wqi->header = WQ_TYPE_INORDER | + (wq_len << WQ_LEN_SHIFT) | + (ring_id << WQ_TARGET_SHIFT) | + WQ_NO_WCFLUSH_WAIT; + + /* The GuC wants only the low-order word of the context descriptor */ + wqi->context_desc = (u32)intel_lr_context_descriptor(rq->ctx, rq->ring); + + /* The GuC firmware wants the tail index in QWords, not bytes */ + tail = rq->ringbuf->tail >> 3; + wqi->ring_tail = tail << WQ_RING_TAIL_SHIFT; + wqi->fence_id = 0; /*XXX: what fence to be here */ + + kunmap_atomic(base); + + return 0; +} + +/** + * i915_guc_submit() - Submit commands through GuC + * @client: the guc client where commands will go through + * @ctx: LRC where commands come from + * @ring: HW engine that will excute the commands + * + * Return: 0 if succeed + */ +int i915_guc_submit(struct i915_guc_client *client, + struct drm_i915_gem_request *rq) +{ + struct intel_guc *guc = client->guc; + enum intel_ring_id ring_id = rq->ring->id; + unsigned long flags; + int q_ret, b_ret; + + spin_lock_irqsave(&client->wq_lock, flags); + + q_ret = guc_add_workqueue_item(client, rq); + if (q_ret == 0) + b_ret = guc_ring_doorbell(client); + + client->submissions[ring_id] += 1; + if (q_ret) { + client->q_fail += 1; + client->retcode = q_ret; + } else if (b_ret) { + client->b_fail += 1; + client->retcode = q_ret = b_ret; + } else { + client->retcode = 0; + } + spin_unlock_irqrestore(&client->wq_lock, flags); + + spin_lock(&guc->host2guc_lock); + guc->submissions[ring_id] += 1; + guc->last_seqno[ring_id] = rq->seqno; + spin_unlock(&guc->host2guc_lock); + + return q_ret; +} + +/* + * Everything below here is concerned with setup & teardown, and is + * therefore not part of the somewhat time-critical batch-submission + * path of i915_guc_submit() above. + */ + /** * gem_allocate_guc_obj() - Allocate gem object for GuC usage * @dev: drm device @@ -79,6 +602,121 @@ static void gem_release_guc_obj(struct drm_i915_gem_object *obj) drm_gem_object_unreference(&obj->base); } +static void guc_client_free(struct drm_device *dev, + struct i915_guc_client *client) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_guc *guc = &dev_priv->guc; + + if (!client) + return; + + if (client->doorbell_id != GUC_INVALID_DOORBELL_ID) { + /* + * First disable the doorbell, then tell the GuC we've + * finished with it, finally deallocate it in our bitmap + */ + guc_disable_doorbell(guc, client); + host2guc_release_doorbell(guc, client); + release_doorbell(guc, client->doorbell_id); + } + + /* + * XXX: wait for any outstanding submissions before freeing memory. + * Be sure to drop any locks + */ + + gem_release_guc_obj(client->client_obj); + + if (client->ctx_index != GUC_INVALID_CTX_ID) { + guc_fini_ctx_desc(guc, client); + ida_simple_remove(&guc->ctx_ids, client->ctx_index); + } + + kfree(client); +} + +/** + * guc_client_alloc() - Allocate an i915_guc_client + * @dev: drm device + * @priority: four levels priority _CRITICAL, _HIGH, _NORMAL and _LOW + * The kernel client to replace ExecList submission is created with + * NORMAL priority. Priority of a client for scheduler can be HIGH, + * while a preemption context can use CRITICAL. + * + * Return: An i915_guc_client object if success. + */ +static struct i915_guc_client *guc_client_alloc(struct drm_device *dev, + uint32_t priority) +{ + struct i915_guc_client *client; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_guc *guc = &dev_priv->guc; + struct drm_i915_gem_object *obj; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return NULL; + + client->doorbell_id = GUC_INVALID_DOORBELL_ID; + client->priority = priority; + client->guc = guc; + + client->ctx_index = (uint32_t)ida_simple_get(&guc->ctx_ids, 0, + GUC_MAX_GPU_CONTEXTS, GFP_KERNEL); + if (client->ctx_index >= GUC_MAX_GPU_CONTEXTS) { + client->ctx_index = GUC_INVALID_CTX_ID; + goto err; + } + + /* The first page is doorbell/proc_desc. Two followed pages are wq. */ + obj = gem_allocate_guc_obj(dev, GUC_DB_SIZE + GUC_WQ_SIZE); + if (!obj) + goto err; + + client->client_obj = obj; + client->wq_offset = GUC_DB_SIZE; + client->wq_size = GUC_WQ_SIZE; + spin_lock_init(&client->wq_lock); + + client->doorbell_offset = select_doorbell_cacheline(guc); + + /* + * Since the doorbell only requires a single cacheline, we can save + * space by putting the application process descriptor in the same + * page. Use the half of the page that doesn't include the doorbell. + */ + if (client->doorbell_offset >= (GUC_DB_SIZE / 2)) + client->proc_desc_offset = 0; + else + client->proc_desc_offset = (GUC_DB_SIZE / 2); + + client->doorbell_id = assign_doorbell(guc, client->priority); + if (client->doorbell_id == GUC_INVALID_DOORBELL_ID) + /* XXX: evict a doorbell instead */ + goto err; + + guc_init_proc_desc(guc, client); + guc_init_ctx_desc(guc, client); + guc_init_doorbell(guc, client); + + /* XXX: Any cache flushes needed? General domain mgmt calls? */ + + if (host2guc_allocate_doorbell(guc, client)) + goto err; + + DRM_DEBUG_DRIVER("new priority %u client %p: ctx_index %u db_id %u\n", + priority, client, client->ctx_index, client->doorbell_id); + + return client; + +err: + DRM_ERROR("FAILED to create priority %u GuC client!\n", priority); + + guc_client_free(dev, client); + return NULL; +} + static void guc_create_log(struct intel_guc *guc) { struct drm_i915_private *dev_priv = guc_to_i915(guc); @@ -142,6 +780,8 @@ int i915_guc_submission_init(struct drm_device *dev) if (!guc->ctx_pool_obj) return -ENOMEM; + spin_lock_init(&dev_priv->guc.host2guc_lock); + ida_init(&guc->ctx_ids); guc_create_log(guc); @@ -149,6 +789,32 @@ int i915_guc_submission_init(struct drm_device *dev) return 0; } +int i915_guc_submission_enable(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_guc *guc = &dev_priv->guc; + struct i915_guc_client *client; + + /* client for execbuf submission */ + client = guc_client_alloc(dev, GUC_CTX_PRIORITY_KMD_NORMAL); + if (!client) { + DRM_ERROR("Failed to create execbuf guc_client\n"); + return -ENOMEM; + } + + guc->execbuf_client = client; + return 0; +} + +void i915_guc_submission_disable(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_guc *guc = &dev_priv->guc; + + guc_client_free(dev, guc->execbuf_client); + guc->execbuf_client = NULL; +} + void i915_guc_submission_fini(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h index 5b51b05777cd..92658ce45a61 100644 --- a/drivers/gpu/drm/i915/intel_guc.h +++ b/drivers/gpu/drm/i915/intel_guc.h @@ -27,6 +27,31 @@ #include "intel_guc_fwif.h" #include "i915_guc_reg.h" +struct i915_guc_client { + struct drm_i915_gem_object *client_obj; + struct intel_guc *guc; + uint32_t priority; + uint32_t ctx_index; + + uint32_t proc_desc_offset; + uint32_t doorbell_offset; + uint32_t cookie; + uint16_t doorbell_id; + uint16_t padding; /* Maintain alignment */ + + uint32_t wq_offset; + uint32_t wq_size; + + spinlock_t wq_lock; /* Protects all data below */ + uint32_t wq_tail; + + /* GuC submission statistics & status */ + uint64_t submissions[I915_NUM_RINGS]; + uint32_t q_fail; + uint32_t b_fail; + int retcode; +}; + enum intel_guc_fw_status { GUC_FIRMWARE_FAIL = -1, GUC_FIRMWARE_NONE = 0, @@ -60,6 +85,23 @@ struct intel_guc { struct drm_i915_gem_object *ctx_pool_obj; struct ida ctx_ids; + + struct i915_guc_client *execbuf_client; + + spinlock_t host2guc_lock; /* Protects all data below */ + + DECLARE_BITMAP(doorbell_bitmap, GUC_MAX_DOORBELLS); + uint32_t db_cacheline; /* Cyclic counter mod pagesize */ + + /* Action status & statistics */ + uint64_t action_count; /* Total commands issued */ + uint32_t action_cmd; /* Last command word */ + uint32_t action_status; /* Last return status */ + uint32_t action_fail; /* Total number of failures */ + int32_t action_err; /* Last error code */ + + uint64_t submissions[I915_NUM_RINGS]; + uint32_t last_seqno[I915_NUM_RINGS]; }; /* intel_guc_loader.c */ @@ -70,6 +112,10 @@ extern const char *intel_guc_fw_status_repr(enum intel_guc_fw_status status); /* i915_guc_submission.c */ int i915_guc_submission_init(struct drm_device *dev); +int i915_guc_submission_enable(struct drm_device *dev); +int i915_guc_submit(struct i915_guc_client *client, + struct drm_i915_gem_request *rq); +void i915_guc_submission_disable(struct drm_device *dev); void i915_guc_submission_fini(struct drm_device *dev); #endif diff --git a/drivers/gpu/drm/i915/intel_guc_fwif.h b/drivers/gpu/drm/i915/intel_guc_fwif.h index 06aad6fb02ed..950c7e70058b 100644 --- a/drivers/gpu/drm/i915/intel_guc_fwif.h +++ b/drivers/gpu/drm/i915/intel_guc_fwif.h @@ -35,10 +35,10 @@ #define GFXCORE_FAMILY_GEN9 12 #define GFXCORE_FAMILY_UNKNOWN 0x7fffffff -#define GUC_CTX_PRIORITY_CRITICAL 0 +#define GUC_CTX_PRIORITY_KMD_HIGH 0 #define GUC_CTX_PRIORITY_HIGH 1 -#define GUC_CTX_PRIORITY_NORMAL 2 -#define GUC_CTX_PRIORITY_LOW 3 +#define GUC_CTX_PRIORITY_KMD_NORMAL 2 +#define GUC_CTX_PRIORITY_NORMAL 3 #define GUC_MAX_GPU_CONTEXTS 1024 #define GUC_INVALID_CTX_ID (GUC_MAX_GPU_CONTEXTS + 1) diff --git a/drivers/gpu/drm/i915/intel_guc_loader.c b/drivers/gpu/drm/i915/intel_guc_loader.c index 6ff7feab3c26..8b4b057f2880 100644 --- a/drivers/gpu/drm/i915/intel_guc_loader.c +++ b/drivers/gpu/drm/i915/intel_guc_loader.c @@ -342,6 +342,8 @@ int intel_guc_ucode_load(struct drm_device *dev) intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status), intel_guc_fw_status_repr(guc_fw->guc_fw_load_status)); + i915_guc_submission_disable(dev); + if (guc_fw->guc_fw_fetch_status == GUC_FIRMWARE_NONE) return 0; @@ -389,12 +391,20 @@ int intel_guc_ucode_load(struct drm_device *dev) intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status), intel_guc_fw_status_repr(guc_fw->guc_fw_load_status)); + if (i915.enable_guc_submission) { + err = i915_guc_submission_enable(dev); + if (err) + goto fail; + } + return 0; fail: if (guc_fw->guc_fw_load_status == GUC_FIRMWARE_PENDING) guc_fw->guc_fw_load_status = GUC_FIRMWARE_FAIL; + i915_guc_submission_disable(dev); + return err; } From 4df001d3989ed319bc5abf2058dc5173bb02c53b Mon Sep 17 00:00:00 2001 From: Dave Gordon Date: Wed, 12 Aug 2015 15:43:42 +0100 Subject: [PATCH 040/145] drm/i915: Interrupt routing for GuC submission Turn on interrupt steering to route necessary interrupts to GuC. v6: Rebased Issue: VIZ-4884 Signed-off-by: Alex Dai Signed-off-by: Dave Gordon Reviewed-by: Tom O'Rourke Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 11 +++++- drivers/gpu/drm/i915/intel_guc_loader.c | 51 +++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 651d0dde774e..5cdb328ac833 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1674,6 +1674,7 @@ enum skl_disp_power_wells { #define GFX_MODE_GEN7 0x0229c #define RING_MODE_GEN7(ring) ((ring)->mmio_base+0x29c) #define GFX_RUN_LIST_ENABLE (1<<15) +#define GFX_INTERRUPT_STEERING (1<<14) #define GFX_TLB_INVALIDATE_EXPLICIT (1<<13) #define GFX_SURFACE_FAULT_ENABLE (1<<12) #define GFX_REPLAY_MODE (1<<11) @@ -1681,6 +1682,11 @@ enum skl_disp_power_wells { #define GFX_PPGTT_ENABLE (1<<9) #define GEN8_GFX_PPGTT_48B (1<<7) +#define GFX_FORWARD_VBLANK_MASK (3<<5) +#define GFX_FORWARD_VBLANK_NEVER (0<<5) +#define GFX_FORWARD_VBLANK_ALWAYS (1<<5) +#define GFX_FORWARD_VBLANK_COND (2<<5) + #define VLV_DISPLAY_BASE 0x180000 #define VLV_MIPI_BASE VLV_DISPLAY_BASE @@ -5695,11 +5701,12 @@ enum skl_disp_power_wells { #define GEN8_GT_IIR(which) (0x44308 + (0x10 * (which))) #define GEN8_GT_IER(which) (0x4430c + (0x10 * (which))) -#define GEN8_BCS_IRQ_SHIFT 16 #define GEN8_RCS_IRQ_SHIFT 0 -#define GEN8_VCS2_IRQ_SHIFT 16 +#define GEN8_BCS_IRQ_SHIFT 16 #define GEN8_VCS1_IRQ_SHIFT 0 +#define GEN8_VCS2_IRQ_SHIFT 16 #define GEN8_VECS_IRQ_SHIFT 0 +#define GEN8_WD_IRQ_SHIFT 16 #define GEN8_DE_PIPE_ISR(pipe) (0x44400 + (0x10 * (pipe))) #define GEN8_DE_PIPE_IMR(pipe) (0x44404 + (0x10 * (pipe))) diff --git a/drivers/gpu/drm/i915/intel_guc_loader.c b/drivers/gpu/drm/i915/intel_guc_loader.c index 8b4b057f2880..13e75f6b1100 100644 --- a/drivers/gpu/drm/i915/intel_guc_loader.c +++ b/drivers/gpu/drm/i915/intel_guc_loader.c @@ -79,6 +79,53 @@ const char *intel_guc_fw_status_repr(enum intel_guc_fw_status status) } }; +static void direct_interrupts_to_host(struct drm_i915_private *dev_priv) +{ + struct intel_engine_cs *ring; + int i, irqs; + + /* tell all command streamers NOT to forward interrupts and vblank to GuC */ + irqs = _MASKED_FIELD(GFX_FORWARD_VBLANK_MASK, GFX_FORWARD_VBLANK_NEVER); + irqs |= _MASKED_BIT_DISABLE(GFX_INTERRUPT_STEERING); + for_each_ring(ring, dev_priv, i) + I915_WRITE(RING_MODE_GEN7(ring), irqs); + + /* tell DE to send nothing to GuC */ + I915_WRITE(DE_GUCRMR, ~0); + + /* route all GT interrupts to the host */ + I915_WRITE(GUC_BCS_RCS_IER, 0); + I915_WRITE(GUC_VCS2_VCS1_IER, 0); + I915_WRITE(GUC_WD_VECS_IER, 0); +} + +static void direct_interrupts_to_guc(struct drm_i915_private *dev_priv) +{ + struct intel_engine_cs *ring; + int i, irqs; + + /* tell all command streamers to forward interrupts and vblank to GuC */ + irqs = _MASKED_FIELD(GFX_FORWARD_VBLANK_MASK, GFX_FORWARD_VBLANK_ALWAYS); + irqs |= _MASKED_BIT_ENABLE(GFX_INTERRUPT_STEERING); + for_each_ring(ring, dev_priv, i) + I915_WRITE(RING_MODE_GEN7(ring), irqs); + + /* tell DE to send (all) flip_done to GuC */ + irqs = DERRMR_PIPEA_PRI_FLIP_DONE | DERRMR_PIPEA_SPR_FLIP_DONE | + DERRMR_PIPEB_PRI_FLIP_DONE | DERRMR_PIPEB_SPR_FLIP_DONE | + DERRMR_PIPEC_PRI_FLIP_DONE | DERRMR_PIPEC_SPR_FLIP_DONE; + /* Unmasked bits will cause GuC response message to be sent */ + I915_WRITE(DE_GUCRMR, ~irqs); + + /* route USER_INTERRUPT to Host, all others are sent to GuC. */ + irqs = GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT | + GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT; + /* These three registers have the same bit definitions */ + I915_WRITE(GUC_BCS_RCS_IER, ~irqs); + I915_WRITE(GUC_VCS2_VCS1_IER, ~irqs); + I915_WRITE(GUC_WD_VECS_IER, ~irqs); +} + static u32 get_gttype(struct drm_i915_private *dev_priv) { /* XXX: GT type based on PCI device ID? field seems unused by fw */ @@ -342,6 +389,7 @@ int intel_guc_ucode_load(struct drm_device *dev) intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status), intel_guc_fw_status_repr(guc_fw->guc_fw_load_status)); + direct_interrupts_to_host(dev_priv); i915_guc_submission_disable(dev); if (guc_fw->guc_fw_fetch_status == GUC_FIRMWARE_NONE) @@ -395,6 +443,7 @@ int intel_guc_ucode_load(struct drm_device *dev) err = i915_guc_submission_enable(dev); if (err) goto fail; + direct_interrupts_to_guc(dev_priv); } return 0; @@ -403,6 +452,7 @@ int intel_guc_ucode_load(struct drm_device *dev) if (guc_fw->guc_fw_load_status == GUC_FIRMWARE_PENDING) guc_fw->guc_fw_load_status = GUC_FIRMWARE_FAIL; + direct_interrupts_to_host(dev_priv); i915_guc_submission_disable(dev); return err; @@ -550,6 +600,7 @@ void intel_guc_ucode_fini(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; + direct_interrupts_to_host(dev_priv); i915_guc_submission_fini(dev); if (guc_fw->guc_fw_obj) From d1675198ed1f21aec6e036336e4340c40b726497 Mon Sep 17 00:00:00 2001 From: Alex Dai Date: Wed, 12 Aug 2015 15:43:43 +0100 Subject: [PATCH 041/145] drm/i915: Integrate GuC-based command submission GuC-based submission is mostly the same as execlist mode, up to intel_logical_ring_advance_and_submit(), where the context being dispatched would be added to the execlist queue; at this point we submit the context to the GuC backend instead. There are, however, a few other changes also required, notably: 1. Contexts must be pinned at GGTT addresses accessible by the GuC i.e. NOT in the range [0..WOPCM_SIZE), so we have to add the PIN_OFFSET_BIAS flag to the relevant GGTT-pinning calls. 2. The GuC's TLB must be invalidated after a context is pinned at a new GGTT address. 3. GuC firmware uses the one page before Ring Context as shared data. Therefore, whenever driver wants to get base address of LRC, we will offset one page for it. LRC_PPHWSP_PN is defined as the page number of LRCA. 4. In the work queue used to pass requests to the GuC, the GuC firmware requires the ring-tail-offset to be represented as an 11-bit value, expressed in QWords. Therefore, the ringbuffer size must be reduced to the representable range (4 pages). v2: Defer adding #defines until needed [Chris Wilson] Rationalise type declarations [Chris Wilson] v4: Squashed kerneldoc patch into here [Daniel Vetter] v5: Update request->tail in code common to both GuC and execlist modes. Add a private version of lr_context_update(), as sharing the execlist version leads to race conditions when the CPU and the GuC both update TAIL in the context image. Conversion of error-captured HWS page to string must account for offset from start of object to actual HWS (LRC_PPHWSP_PN). Issue: VIZ-4884 Signed-off-by: Alex Dai Signed-off-by: Dave Gordon Reviewed-by: Tom O'Rourke Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 14 ++++ drivers/gpu/drm/i915/i915_debugfs.c | 2 +- drivers/gpu/drm/i915/i915_gpu_error.c | 14 ++-- drivers/gpu/drm/i915/i915_guc_submission.c | 79 ++++++++++++++++++++-- drivers/gpu/drm/i915/intel_guc.h | 1 + drivers/gpu/drm/i915/intel_lrc.c | 55 ++++++++++----- drivers/gpu/drm/i915/intel_lrc.h | 6 ++ 7 files changed, 142 insertions(+), 29 deletions(-) diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 9ddf8c6cb887..069958c00f1c 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -4237,6 +4237,20 @@ int num_ioctls; !Idrivers/gpu/drm/i915/i915_gem_shrinker.c + + GuC-based Command Submission + + GuC +!Pdrivers/gpu/drm/i915/intel_guc_loader.c GuC-specific firmware loader +!Idrivers/gpu/drm/i915/intel_guc_loader.c + + + GuC Client +!Pdrivers/gpu/drm/i915/intel_guc_submission.c GuC-based command submissison +!Idrivers/gpu/drm/i915/intel_guc_submission.c + + + Tracing diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 1123b8e3b10a..a815fcc407c6 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1995,7 +1995,7 @@ static void i915_dump_lrc_obj(struct seq_file *m, return; } - page = i915_gem_object_get_page(ctx_obj, 1); + page = i915_gem_object_get_page(ctx_obj, LRC_STATE_PN); if (!WARN_ON(page == NULL)) { reg_state = kmap_atomic(page); diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 0d0a7b1a6b4b..493e9b294a69 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -462,17 +462,17 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, } if ((obj = error->ring[i].hws_page)) { - err_printf(m, "%s --- HW Status = 0x%08x\n", - dev_priv->ring[i].name, - lower_32_bits(obj->gtt_offset)); + err_printf(m, "%s --- HW Status = 0x%08llx\n", + dev_priv->ring[i].name, + obj->gtt_offset + LRC_PPHWSP_PN * PAGE_SIZE); offset = 0; for (elt = 0; elt < PAGE_SIZE/16; elt += 4) { err_printf(m, "[%04x] %08x %08x %08x %08x\n", offset, - obj->pages[0][elt], - obj->pages[0][elt+1], - obj->pages[0][elt+2], - obj->pages[0][elt+3]); + obj->pages[LRC_PPHWSP_PN][elt], + obj->pages[LRC_PPHWSP_PN][elt+1], + obj->pages[LRC_PPHWSP_PN][elt+2], + obj->pages[LRC_PPHWSP_PN][elt+3]); offset += 16; } } diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c index 3352b8521ddd..ec703937f363 100644 --- a/drivers/gpu/drm/i915/i915_guc_submission.c +++ b/drivers/gpu/drm/i915/i915_guc_submission.c @@ -364,18 +364,58 @@ static void guc_init_proc_desc(struct intel_guc *guc, static void guc_init_ctx_desc(struct intel_guc *guc, struct i915_guc_client *client) { + struct intel_context *ctx = client->owner; struct guc_context_desc desc; struct sg_table *sg; + int i; memset(&desc, 0, sizeof(desc)); desc.attribute = GUC_CTX_DESC_ATTR_ACTIVE | GUC_CTX_DESC_ATTR_KERNEL; desc.context_id = client->ctx_index; desc.priority = client->priority; - desc.engines_used = (1 << RCS) | (1 << VCS) | (1 << BCS) | - (1 << VECS) | (1 << VCS2); /* all engines */ desc.db_id = client->doorbell_id; + for (i = 0; i < I915_NUM_RINGS; i++) { + struct guc_execlist_context *lrc = &desc.lrc[i]; + struct intel_ringbuffer *ringbuf = ctx->engine[i].ringbuf; + struct intel_engine_cs *ring; + struct drm_i915_gem_object *obj; + uint64_t ctx_desc; + + /* TODO: We have a design issue to be solved here. Only when we + * receive the first batch, we know which engine is used by the + * user. But here GuC expects the lrc and ring to be pinned. It + * is not an issue for default context, which is the only one + * for now who owns a GuC client. But for future owner of GuC + * client, need to make sure lrc is pinned prior to enter here. + */ + obj = ctx->engine[i].state; + if (!obj) + break; /* XXX: continue? */ + + ring = ringbuf->ring; + ctx_desc = intel_lr_context_descriptor(ctx, ring); + lrc->context_desc = (u32)ctx_desc; + + /* The state page is after PPHWSP */ + lrc->ring_lcra = i915_gem_obj_ggtt_offset(obj) + + LRC_STATE_PN * PAGE_SIZE; + lrc->context_id = (client->ctx_index << GUC_ELC_CTXID_OFFSET) | + (ring->id << GUC_ELC_ENGINE_OFFSET); + + obj = ringbuf->obj; + + lrc->ring_begin = i915_gem_obj_ggtt_offset(obj); + lrc->ring_end = lrc->ring_begin + obj->base.size - 1; + lrc->ring_next_free_location = lrc->ring_begin; + lrc->ring_current_tail_pointer_value = 0; + + desc.engines_used |= (1 << ring->id); + } + + WARN_ON(desc.engines_used == 0); + /* * The CPU address is only needed at certain points, so kmap_atomic on * demand instead of storing it in the ctx descriptor. @@ -501,6 +541,29 @@ static int guc_add_workqueue_item(struct i915_guc_client *gc, return 0; } +#define CTX_RING_BUFFER_START 0x08 + +/* Update the ringbuffer pointer in a saved context image */ +static void lr_context_update(struct drm_i915_gem_request *rq) +{ + enum intel_ring_id ring_id = rq->ring->id; + struct drm_i915_gem_object *ctx_obj = rq->ctx->engine[ring_id].state; + struct drm_i915_gem_object *rb_obj = rq->ringbuf->obj; + struct page *page; + uint32_t *reg_state; + + BUG_ON(!ctx_obj); + WARN_ON(!i915_gem_obj_is_pinned(ctx_obj)); + WARN_ON(!i915_gem_obj_is_pinned(rb_obj)); + + page = i915_gem_object_get_page(ctx_obj, LRC_STATE_PN); + reg_state = kmap_atomic(page); + + reg_state[CTX_RING_BUFFER_START+1] = i915_gem_obj_ggtt_offset(rb_obj); + + kunmap_atomic(reg_state); +} + /** * i915_guc_submit() - Submit commands through GuC * @client: the guc client where commands will go through @@ -517,6 +580,10 @@ int i915_guc_submit(struct i915_guc_client *client, unsigned long flags; int q_ret, b_ret; + /* Need this because of the deferred pin ctx and ring */ + /* Shall we move this right after ring is pinned? */ + lr_context_update(rq); + spin_lock_irqsave(&client->wq_lock, flags); q_ret = guc_add_workqueue_item(client, rq); @@ -643,11 +710,13 @@ static void guc_client_free(struct drm_device *dev, * The kernel client to replace ExecList submission is created with * NORMAL priority. Priority of a client for scheduler can be HIGH, * while a preemption context can use CRITICAL. + * @ctx the context to own the client (we use the default render context) * * Return: An i915_guc_client object if success. */ static struct i915_guc_client *guc_client_alloc(struct drm_device *dev, - uint32_t priority) + uint32_t priority, + struct intel_context *ctx) { struct i915_guc_client *client; struct drm_i915_private *dev_priv = dev->dev_private; @@ -660,6 +729,7 @@ static struct i915_guc_client *guc_client_alloc(struct drm_device *dev, client->doorbell_id = GUC_INVALID_DOORBELL_ID; client->priority = priority; + client->owner = ctx; client->guc = guc; client->ctx_index = (uint32_t)ida_simple_get(&guc->ctx_ids, 0, @@ -793,10 +863,11 @@ int i915_guc_submission_enable(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_guc *guc = &dev_priv->guc; + struct intel_context *ctx = dev_priv->ring[RCS].default_context; struct i915_guc_client *client; /* client for execbuf submission */ - client = guc_client_alloc(dev, GUC_CTX_PRIORITY_KMD_NORMAL); + client = guc_client_alloc(dev, GUC_CTX_PRIORITY_KMD_NORMAL, ctx); if (!client) { DRM_ERROR("Failed to create execbuf guc_client\n"); return -ENOMEM; diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h index 92658ce45a61..4ec2d27a557e 100644 --- a/drivers/gpu/drm/i915/intel_guc.h +++ b/drivers/gpu/drm/i915/intel_guc.h @@ -29,6 +29,7 @@ struct i915_guc_client { struct drm_i915_gem_object *client_obj; + struct intel_context *owner; struct intel_guc *guc; uint32_t priority; uint32_t ctx_index; diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index f3411f835a5a..e77b6b0d44d9 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -263,7 +263,8 @@ int intel_sanitize_enable_execlists(struct drm_device *dev, int enable_execlists */ u32 intel_execlists_ctx_id(struct drm_i915_gem_object *ctx_obj) { - u32 lrca = i915_gem_obj_ggtt_offset(ctx_obj); + u32 lrca = i915_gem_obj_ggtt_offset(ctx_obj) + + LRC_PPHWSP_PN * PAGE_SIZE; /* LRCA is required to be 4K aligned so the more significant 20 bits * are globally unique */ @@ -276,7 +277,8 @@ uint64_t intel_lr_context_descriptor(struct intel_context *ctx, struct drm_device *dev = ring->dev; struct drm_i915_gem_object *ctx_obj = ctx->engine[ring->id].state; uint64_t desc; - uint64_t lrca = i915_gem_obj_ggtt_offset(ctx_obj); + uint64_t lrca = i915_gem_obj_ggtt_offset(ctx_obj) + + LRC_PPHWSP_PN * PAGE_SIZE; WARN_ON(lrca & 0xFFFFFFFF00000FFFULL); @@ -350,7 +352,7 @@ static int execlists_update_context(struct drm_i915_gem_request *rq) WARN_ON(!i915_gem_obj_is_pinned(ctx_obj)); WARN_ON(!i915_gem_obj_is_pinned(rb_obj)); - page = i915_gem_object_get_page(ctx_obj, 1); + page = i915_gem_object_get_page(ctx_obj, LRC_STATE_PN); reg_state = kmap_atomic(page); reg_state[CTX_RING_TAIL+1] = rq->tail; @@ -548,8 +550,6 @@ static int execlists_context_queue(struct drm_i915_gem_request *request) i915_gem_request_reference(request); - request->tail = request->ringbuf->tail; - spin_lock_irq(&ring->execlist_lock); list_for_each_entry(cursor, &ring->execlist_queue, execlist_link) @@ -702,13 +702,19 @@ static void intel_logical_ring_advance_and_submit(struct drm_i915_gem_request *request) { struct intel_engine_cs *ring = request->ring; + struct drm_i915_private *dev_priv = request->i915; intel_logical_ring_advance(request->ringbuf); + request->tail = request->ringbuf->tail; + if (intel_ring_stopped(ring)) return; - execlists_context_queue(request); + if (dev_priv->guc.execbuf_client) + i915_guc_submit(dev_priv->guc.execbuf_client, request); + else + execlists_context_queue(request); } static void __wrap_ring_buffer(struct intel_ringbuffer *ringbuf) @@ -998,6 +1004,7 @@ int logical_ring_flush_all_caches(struct drm_i915_gem_request *req) static int intel_lr_context_pin(struct drm_i915_gem_request *rq) { + struct drm_i915_private *dev_priv = rq->i915; struct intel_engine_cs *ring = rq->ring; struct drm_i915_gem_object *ctx_obj = rq->ctx->engine[ring->id].state; struct intel_ringbuffer *ringbuf = rq->ringbuf; @@ -1005,14 +1012,18 @@ static int intel_lr_context_pin(struct drm_i915_gem_request *rq) WARN_ON(!mutex_is_locked(&ring->dev->struct_mutex)); if (rq->ctx->engine[ring->id].pin_count++ == 0) { - ret = i915_gem_obj_ggtt_pin(ctx_obj, - GEN8_LR_CONTEXT_ALIGN, 0); + ret = i915_gem_obj_ggtt_pin(ctx_obj, GEN8_LR_CONTEXT_ALIGN, + PIN_OFFSET_BIAS | GUC_WOPCM_TOP); if (ret) goto reset_pin_count; ret = intel_pin_and_map_ringbuffer_obj(ring->dev, ringbuf); if (ret) goto unpin_ctx_obj; + + /* Invalidate GuC TLB. */ + if (i915.enable_guc_submission) + I915_WRITE(GEN8_GTCR, GEN8_GTCR_INVALIDATE); } return ret; @@ -2137,7 +2148,7 @@ populate_lr_context(struct intel_context *ctx, struct drm_i915_gem_object *ctx_o /* The second page of the context object contains some fields which must * be set up prior to the first execution. */ - page = i915_gem_object_get_page(ctx_obj, 1); + page = i915_gem_object_get_page(ctx_obj, LRC_STATE_PN); reg_state = kmap_atomic(page); /* A context is actually a big batch buffer with several MI_LOAD_REGISTER_IMM @@ -2307,12 +2318,13 @@ static void lrc_setup_hardware_status_page(struct intel_engine_cs *ring, struct drm_i915_gem_object *default_ctx_obj) { struct drm_i915_private *dev_priv = ring->dev->dev_private; + struct page *page; - /* The status page is offset 0 from the default context object - * in LRC mode. */ - ring->status_page.gfx_addr = i915_gem_obj_ggtt_offset(default_ctx_obj); - ring->status_page.page_addr = - kmap(sg_page(default_ctx_obj->pages->sgl)); + /* The HWSP is part of the default context object in LRC mode. */ + ring->status_page.gfx_addr = i915_gem_obj_ggtt_offset(default_ctx_obj) + + LRC_PPHWSP_PN * PAGE_SIZE; + page = i915_gem_object_get_page(default_ctx_obj, LRC_PPHWSP_PN); + ring->status_page.page_addr = kmap(page); ring->status_page.obj = default_ctx_obj; I915_WRITE(RING_HWS_PGA(ring->mmio_base), @@ -2338,6 +2350,7 @@ int intel_lr_context_deferred_create(struct intel_context *ctx, { const bool is_global_default_ctx = (ctx == ring->default_context); struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *ctx_obj; uint32_t context_size; struct intel_ringbuffer *ringbuf; @@ -2348,6 +2361,9 @@ int intel_lr_context_deferred_create(struct intel_context *ctx, context_size = round_up(get_lr_context_size(ring), 4096); + /* One extra page as the sharing data between driver and GuC */ + context_size += PAGE_SIZE * LRC_PPHWSP_PN; + ctx_obj = i915_gem_alloc_object(dev, context_size); if (!ctx_obj) { DRM_DEBUG_DRIVER("Alloc LRC backing obj failed.\n"); @@ -2355,13 +2371,18 @@ int intel_lr_context_deferred_create(struct intel_context *ctx, } if (is_global_default_ctx) { - ret = i915_gem_obj_ggtt_pin(ctx_obj, GEN8_LR_CONTEXT_ALIGN, 0); + ret = i915_gem_obj_ggtt_pin(ctx_obj, GEN8_LR_CONTEXT_ALIGN, + PIN_OFFSET_BIAS | GUC_WOPCM_TOP); if (ret) { DRM_DEBUG_DRIVER("Pin LRC backing obj failed: %d\n", ret); drm_gem_object_unreference(&ctx_obj->base); return ret; } + + /* Invalidate GuC TLB. */ + if (i915.enable_guc_submission) + I915_WRITE(GEN8_GTCR, GEN8_GTCR_INVALIDATE); } ringbuf = kzalloc(sizeof(*ringbuf), GFP_KERNEL); @@ -2374,7 +2395,7 @@ int intel_lr_context_deferred_create(struct intel_context *ctx, ringbuf->ring = ring; - ringbuf->size = 32 * PAGE_SIZE; + ringbuf->size = 4 * PAGE_SIZE; ringbuf->effective_size = ringbuf->size; ringbuf->head = 0; ringbuf->tail = 0; @@ -2474,7 +2495,7 @@ void intel_lr_context_reset(struct drm_device *dev, WARN(1, "Failed get_pages for context obj\n"); continue; } - page = i915_gem_object_get_page(ctx_obj, 1); + page = i915_gem_object_get_page(ctx_obj, LRC_STATE_PN); reg_state = kmap_atomic(page); reg_state[CTX_RING_HEAD+1] = 0; diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h index 5e5788c83eeb..4cc54b371afd 100644 --- a/drivers/gpu/drm/i915/intel_lrc.h +++ b/drivers/gpu/drm/i915/intel_lrc.h @@ -68,6 +68,12 @@ static inline void intel_logical_ring_emit(struct intel_ringbuffer *ringbuf, } /* Logical Ring Contexts */ + +/* One extra page is added before LRC for GuC as shared data */ +#define LRC_GUCSHR_PN (0) +#define LRC_PPHWSP_PN (LRC_GUCSHR_PN + 1) +#define LRC_STATE_PN (LRC_PPHWSP_PN + 1) + void intel_lr_context_free(struct intel_context *ctx); int intel_lr_context_deferred_create(struct intel_context *ctx, struct intel_engine_cs *ring); From 8b417c266b715b3797cd3e65342149372b9ac0c8 Mon Sep 17 00:00:00 2001 From: Dave Gordon Date: Wed, 12 Aug 2015 15:43:44 +0100 Subject: [PATCH 042/145] drm/i915: Debugfs interface for GuC submission statistics This provides a means of reading status and counts relating to GuC actions and submissions. v2: Remove surplus blank line in output [Chris Wilson] v5: Added GuC per-engine submission & seqno statistics v6: Add per-ring statistics to client, refactor client-dumper. Signed-off-by: Dave Gordon Signed-off-by: Alex Dai Reviewed-by: Tom O'Rourke Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 76 +++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a815fcc407c6..e192f021fda3 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2412,6 +2412,81 @@ static int i915_guc_load_status_info(struct seq_file *m, void *data) return 0; } +static void i915_guc_client_info(struct seq_file *m, + struct drm_i915_private *dev_priv, + struct i915_guc_client *client) +{ + struct intel_engine_cs *ring; + uint64_t tot = 0; + uint32_t i; + + seq_printf(m, "\tPriority %d, GuC ctx index: %u, PD offset 0x%x\n", + client->priority, client->ctx_index, client->proc_desc_offset); + seq_printf(m, "\tDoorbell id %d, offset: 0x%x, cookie 0x%x\n", + client->doorbell_id, client->doorbell_offset, client->cookie); + seq_printf(m, "\tWQ size %d, offset: 0x%x, tail %d\n", + client->wq_size, client->wq_offset, client->wq_tail); + + seq_printf(m, "\tFailed to queue: %u\n", client->q_fail); + seq_printf(m, "\tFailed doorbell: %u\n", client->b_fail); + seq_printf(m, "\tLast submission result: %d\n", client->retcode); + + for_each_ring(ring, dev_priv, i) { + seq_printf(m, "\tSubmissions: %llu %s\n", + client->submissions[i], + ring->name); + tot += client->submissions[i]; + } + seq_printf(m, "\tTotal: %llu\n", tot); +} + +static int i915_guc_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = m->private; + struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_guc guc; + struct i915_guc_client client = { .client_obj = 0 }; + struct intel_engine_cs *ring; + enum intel_ring_id i; + u64 total = 0; + + if (!HAS_GUC_SCHED(dev_priv->dev)) + return 0; + + /* Take a local copy of the GuC data, so we can dump it at leisure */ + spin_lock(&dev_priv->guc.host2guc_lock); + guc = dev_priv->guc; + if (guc.execbuf_client) { + spin_lock(&guc.execbuf_client->wq_lock); + client = *guc.execbuf_client; + spin_unlock(&guc.execbuf_client->wq_lock); + } + spin_unlock(&dev_priv->guc.host2guc_lock); + + seq_printf(m, "GuC total action count: %llu\n", guc.action_count); + seq_printf(m, "GuC action failure count: %u\n", guc.action_fail); + seq_printf(m, "GuC last action command: 0x%x\n", guc.action_cmd); + seq_printf(m, "GuC last action status: 0x%x\n", guc.action_status); + seq_printf(m, "GuC last action error code: %d\n", guc.action_err); + + seq_printf(m, "\nGuC submissions:\n"); + for_each_ring(ring, dev_priv, i) { + seq_printf(m, "\t%-24s: %10llu, last seqno 0x%08x %9d\n", + ring->name, guc.submissions[i], + guc.last_seqno[i], guc.last_seqno[i]); + total += guc.submissions[i]; + } + seq_printf(m, "\t%s: %llu\n", "Total", total); + + seq_printf(m, "\nGuC execbuf client @ %p:\n", guc.execbuf_client); + i915_guc_client_info(m, dev_priv, &client); + + /* Add more as required ... */ + + return 0; +} + static int i915_guc_log_dump(struct seq_file *m, void *data) { struct drm_info_node *node = m->private; @@ -5099,6 +5174,7 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_gem_hws_bsd", i915_hws_info, 0, (void *)VCS}, {"i915_gem_hws_vebox", i915_hws_info, 0, (void *)VECS}, {"i915_gem_batch_pool", i915_gem_batch_pool_info, 0}, + {"i915_guc_info", i915_guc_info, 0}, {"i915_guc_load_status", i915_guc_load_status_info, 0}, {"i915_guc_log_dump", i915_guc_log_dump, 0}, {"i915_frequency_info", i915_frequency_info, 0}, From 66e2806656b12bbb98ff36b7beb1538b87cb2f2f Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 14 Aug 2015 15:59:53 +0300 Subject: [PATCH 043/145] drm/i915: remove excessive scaler debugging messages There's so much scaler debugging messages that it makes other debugging hard. Remove them. Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_atomic.c | 3 --- drivers/gpu/drm/i915/intel_display.c | 2 -- 2 files changed, 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index e2531cf59266..9336e8030980 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -149,9 +149,6 @@ int intel_atomic_setup_scalers(struct drm_device *dev, int i, j; num_scalers_need = hweight32(scaler_state->scaler_users); - DRM_DEBUG_KMS("crtc_state = %p need = %d avail = %d scaler_users = 0x%x\n", - crtc_state, num_scalers_need, intel_crtc->num_scalers, - scaler_state->scaler_users); /* * High level flow: diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 40e70f20787f..364104dee32f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2933,8 +2933,6 @@ static void skl_detach_scaler(struct intel_crtc *intel_crtc, int id) I915_WRITE(SKL_PS_CTRL(intel_crtc->pipe, id), 0); I915_WRITE(SKL_PS_WIN_POS(intel_crtc->pipe, id), 0); I915_WRITE(SKL_PS_WIN_SZ(intel_crtc->pipe, id), 0); - DRM_DEBUG_KMS("CRTC:%d Disabled scaler id %u.%u\n", - intel_crtc->base.base.id, intel_crtc->pipe, id); } /* From cff4f55bf4fb1e32483885bf0e571847668d9d67 Mon Sep 17 00:00:00 2001 From: Graham Whaley Date: Mon, 24 Aug 2015 14:41:21 +0100 Subject: [PATCH 044/145] doc: drm: Fix mis-spelling of i915_guc_submission includes In commit d1675198e: drm/i915: Integrate GuC-based command submission the drm.tmpl include lines reference the intel_guc_submission.c but the patch adds the file i915_guc_submission.c. drm.tmpl fails to build with: docproc: .//drivers/gpu/drm/i915/intel_guc_submission.c: No such file or directory Change the file reference to the actual file. Signed-off-by: Graham Whaley Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 069958c00f1c..1f9dc3e97ab7 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -4246,8 +4246,8 @@ int num_ioctls; GuC Client -!Pdrivers/gpu/drm/i915/intel_guc_submission.c GuC-based command submissison -!Idrivers/gpu/drm/i915/intel_guc_submission.c +!Pdrivers/gpu/drm/i915/i915_guc_submission.c GuC-based command submissison +!Idrivers/gpu/drm/i915/i915_guc_submission.c From f1afe24f0e736b9d7f2275e2b1504af3fe612f2a Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Tue, 4 Aug 2015 16:22:20 +0100 Subject: [PATCH 045/145] drm/i915: Change SRM, LRM instructions to use correct length MI_STORE_REGISTER_MEM, MI_LOAD_REGISTER_MEM instructions are not really variable length instructions unlike MI_LOAD_REGISTER_IMM where it expects (reg, addr) pairs so use fixed length for these instructions. v2: rebase Cc: Dave Gordon Signed-off-by: Arun Siluvery Reviewed-by: Mika Kuoppala [danvet: Appease checkpatch as Mika spotted in i915_reg.h - it seems terminally unhappy about i915_cmd_parser.c so that would be a separate patch.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_cmd_parser.c | 10 +++++----- drivers/gpu/drm/i915/i915_reg.h | 8 ++++---- drivers/gpu/drm/i915/intel_display.c | 4 ++-- drivers/gpu/drm/i915/intel_lrc.c | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c index 237ff6884a22..3977bf31676f 100644 --- a/drivers/gpu/drm/i915/i915_cmd_parser.c +++ b/drivers/gpu/drm/i915/i915_cmd_parser.c @@ -94,7 +94,7 @@ #define CMD(op, opm, f, lm, fl, ...) \ { \ .flags = (fl) | ((f) ? CMD_DESC_FIXED : 0), \ - .cmd = { (op), (opm) }, \ + .cmd = { (op), (opm) }, \ .length = { (lm) }, \ __VA_ARGS__ \ } @@ -124,14 +124,14 @@ static const struct drm_i915_cmd_descriptor common_cmds[] = { CMD( MI_STORE_DWORD_INDEX, SMI, !F, 0xFF, R ), CMD( MI_LOAD_REGISTER_IMM(1), SMI, !F, 0xFF, W, .reg = { .offset = 1, .mask = 0x007FFFFC, .step = 2 } ), - CMD( MI_STORE_REGISTER_MEM(1), SMI, !F, 0xFF, W | B, + CMD( MI_STORE_REGISTER_MEM, SMI, F, 1, W | B, .reg = { .offset = 1, .mask = 0x007FFFFC }, .bits = {{ .offset = 0, .mask = MI_GLOBAL_GTT, .expected = 0, }}, ), - CMD( MI_LOAD_REGISTER_MEM(1), SMI, !F, 0xFF, W | B, + CMD( MI_LOAD_REGISTER_MEM, SMI, F, 1, W | B, .reg = { .offset = 1, .mask = 0x007FFFFC }, .bits = {{ .offset = 0, @@ -1021,7 +1021,7 @@ static bool check_cmd(const struct intel_engine_cs *ring, * only MI_LOAD_REGISTER_IMM commands. */ if (reg_addr == OACONTROL) { - if (desc->cmd.value == MI_LOAD_REGISTER_MEM(1)) { + if (desc->cmd.value == MI_LOAD_REGISTER_MEM) { DRM_DEBUG_DRIVER("CMD: Rejected LRM to OACONTROL\n"); return false; } @@ -1035,7 +1035,7 @@ static bool check_cmd(const struct intel_engine_cs *ring, * allowed mask/value pair given in the whitelist entry. */ if (reg->mask) { - if (desc->cmd.value == MI_LOAD_REGISTER_MEM(1)) { + if (desc->cmd.value == MI_LOAD_REGISTER_MEM) { DRM_DEBUG_DRIVER("CMD: Rejected LRM to masked register 0x%08X\n", reg_addr); return false; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 5cdb328ac833..211b73ab7780 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -352,8 +352,8 @@ */ #define MI_LOAD_REGISTER_IMM(x) MI_INSTR(0x22, 2*(x)-1) #define MI_LRI_FORCE_POSTED (1<<12) -#define MI_STORE_REGISTER_MEM(x) MI_INSTR(0x24, 2*(x)-1) -#define MI_STORE_REGISTER_MEM_GEN8(x) MI_INSTR(0x24, 3*(x)-1) +#define MI_STORE_REGISTER_MEM MI_INSTR(0x24, 1) +#define MI_STORE_REGISTER_MEM_GEN8 MI_INSTR(0x24, 2) #define MI_SRM_LRM_GLOBAL_GTT (1<<22) #define MI_FLUSH_DW MI_INSTR(0x26, 1) /* for GEN6 */ #define MI_FLUSH_DW_STORE_INDEX (1<<21) @@ -364,8 +364,8 @@ #define MI_INVALIDATE_BSD (1<<7) #define MI_FLUSH_DW_USE_GTT (1<<2) #define MI_FLUSH_DW_USE_PPGTT (0<<2) -#define MI_LOAD_REGISTER_MEM(x) MI_INSTR(0x29, 2*(x)-1) -#define MI_LOAD_REGISTER_MEM_GEN8(x) MI_INSTR(0x29, 3*(x)-1) +#define MI_LOAD_REGISTER_MEM MI_INSTR(0x29, 1) +#define MI_LOAD_REGISTER_MEM_GEN8 MI_INSTR(0x29, 2) #define MI_BATCH_BUFFER MI_INSTR(0x30, 1) #define MI_BATCH_NON_SECURE (1) /* for snb/ivb/vlv this also means "batch in ppgtt" when ppgtt is enabled. */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 364104dee32f..542fb8c34412 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11031,10 +11031,10 @@ static int intel_gen7_queue_flip(struct drm_device *dev, DERRMR_PIPEB_PRI_FLIP_DONE | DERRMR_PIPEC_PRI_FLIP_DONE)); if (IS_GEN8(dev)) - intel_ring_emit(ring, MI_STORE_REGISTER_MEM_GEN8(1) | + intel_ring_emit(ring, MI_STORE_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT); else - intel_ring_emit(ring, MI_STORE_REGISTER_MEM(1) | + intel_ring_emit(ring, MI_STORE_REGISTER_MEM | MI_SRM_LRM_GLOBAL_GTT); intel_ring_emit(ring, DERRMR); intel_ring_emit(ring, ring->scratch.gtt_offset + 256); diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index e77b6b0d44d9..8e9a85b55ec2 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -1130,7 +1130,7 @@ static inline int gen8_emit_flush_coherentl3_wa(struct intel_engine_cs *ring, if (IS_SKYLAKE(ring->dev) && INTEL_REVID(ring->dev) <= SKL_REVID_E0) l3sqc4_flush |= GEN8_LQSC_RO_PERF_DIS; - wa_ctx_emit(batch, index, (MI_STORE_REGISTER_MEM_GEN8(1) | + wa_ctx_emit(batch, index, (MI_STORE_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT)); wa_ctx_emit(batch, index, GEN8_L3SQCREG4); wa_ctx_emit(batch, index, ring->scratch.gtt_offset + 256); @@ -1148,7 +1148,7 @@ static inline int gen8_emit_flush_coherentl3_wa(struct intel_engine_cs *ring, wa_ctx_emit(batch, index, 0); wa_ctx_emit(batch, index, 0); - wa_ctx_emit(batch, index, (MI_LOAD_REGISTER_MEM_GEN8(1) | + wa_ctx_emit(batch, index, (MI_LOAD_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT)); wa_ctx_emit(batch, index, GEN8_L3SQCREG4); wa_ctx_emit(batch, index, ring->scratch.gtt_offset + 256); From 8be6ca8537e1230da8e92c753df4125151a3f6b1 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Mon, 24 Aug 2015 16:38:23 -0700 Subject: [PATCH 046/145] drm/i915: Also call frontbuffer flip when disabling planes. We also need to call the frontbuffer flip to trigger proper invalidations when disabling planes. Otherwise we will miss screen updates when disabling sprites or cursor. On core platforms where HW tracking also works, this issue is totally masked because HW tracking triggers PSR exit however on VLV/CHV that has only SW tracking we miss screen updates when disabling planes. It was caught with kms_psr_sink_crc sprite_plane_onoff and cursor_plane_onoff subtests running on VLV/CHV. This is probably a regression since I can also get this with the manual test case, but with so many changes on atomic modeset I couldn't track exactly when this was introduced. Reviewed-by: Paulo Zanoni Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 542fb8c34412..cf8dfa53e338 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11627,7 +11627,7 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state, intel_crtc->atomic.update_wm_pre = true; } - if (visible) + if (visible || was_visible) intel_crtc->atomic.fb_bits |= to_intel_plane(plane)->frontbuffer_bit; From 319404df2f682ddabf98cbf6421bb43b951ca978 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 14 Aug 2015 18:35:27 +0300 Subject: [PATCH 047/145] drm/i915/bxt: work around HW coherency issue when accessing GPU seqno By running igt/store_dword_loop_render on BXT we can hit a coherency problem where the seqno written at GPU command completion time is not seen by the CPU. This results in __i915_wait_request seeing the stale seqno and not completing the request (not considering the lost interrupt/GPU reset mechanism). I also verified that this isn't a case of a lost interrupt, or that the command didn't complete somehow: when the coherency issue occured I read the seqno via an uncached GTT mapping too. While the cached version of the seqno still showed the stale value the one read via the uncached mapping was the correct one. Work around this issue by clflushing the corresponding CPU cacheline following any store of the seqno and preceding any reading of it. When reading it do this only when the caller expects a coherent view. v2: - fix using the proper logical && instead of a bitwise & (Jani, Mika) - limit the workaround to A stepping, on later steppings this HW issue is fixed v3: - use a separate get_seqno/set_seqno vfunc (Chris) Testcase: igt/store_dword_loop_render Signed-off-by: Imre Deak Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_lrc.c | 64 +++++++++++++++++++++---- drivers/gpu/drm/i915/intel_ringbuffer.h | 7 +++ 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 8e9a85b55ec2..1af84c52115c 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -1710,6 +1710,34 @@ static void gen8_set_seqno(struct intel_engine_cs *ring, u32 seqno) intel_write_status_page(ring, I915_GEM_HWS_INDEX, seqno); } +static u32 bxt_a_get_seqno(struct intel_engine_cs *ring, bool lazy_coherency) +{ + + /* + * On BXT A steppings there is a HW coherency issue whereby the + * MI_STORE_DATA_IMM storing the completed request's seqno + * occasionally doesn't invalidate the CPU cache. Work around this by + * clflushing the corresponding cacheline whenever the caller wants + * the coherency to be guaranteed. Note that this cacheline is known + * to be clean at this point, since we only write it in + * bxt_a_set_seqno(), where we also do a clflush after the write. So + * this clflush in practice becomes an invalidate operation. + */ + + if (!lazy_coherency) + intel_flush_status_page(ring, I915_GEM_HWS_INDEX); + + return intel_read_status_page(ring, I915_GEM_HWS_INDEX); +} + +static void bxt_a_set_seqno(struct intel_engine_cs *ring, u32 seqno) +{ + intel_write_status_page(ring, I915_GEM_HWS_INDEX, seqno); + + /* See bxt_a_get_seqno() explaining the reason for the clflush. */ + intel_flush_status_page(ring, I915_GEM_HWS_INDEX); +} + static int gen8_emit_request(struct drm_i915_gem_request *request) { struct intel_ringbuffer *ringbuf = request->ringbuf; @@ -1879,8 +1907,13 @@ static int logical_render_ring_init(struct drm_device *dev) ring->init_hw = gen8_init_render_ring; ring->init_context = gen8_init_rcs_context; ring->cleanup = intel_fini_pipe_control; - ring->get_seqno = gen8_get_seqno; - ring->set_seqno = gen8_set_seqno; + if (IS_BROXTON(dev) && INTEL_REVID(dev) < BXT_REVID_B0) { + ring->get_seqno = bxt_a_get_seqno; + ring->set_seqno = bxt_a_set_seqno; + } else { + ring->get_seqno = gen8_get_seqno; + ring->set_seqno = gen8_set_seqno; + } ring->emit_request = gen8_emit_request; ring->emit_flush = gen8_emit_flush_render; ring->irq_get = gen8_logical_ring_get_irq; @@ -1926,8 +1959,13 @@ static int logical_bsd_ring_init(struct drm_device *dev) GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VCS1_IRQ_SHIFT; ring->init_hw = gen8_init_common_ring; - ring->get_seqno = gen8_get_seqno; - ring->set_seqno = gen8_set_seqno; + if (IS_BROXTON(dev) && INTEL_REVID(dev) < BXT_REVID_B0) { + ring->get_seqno = bxt_a_get_seqno; + ring->set_seqno = bxt_a_set_seqno; + } else { + ring->get_seqno = gen8_get_seqno; + ring->set_seqno = gen8_set_seqno; + } ring->emit_request = gen8_emit_request; ring->emit_flush = gen8_emit_flush; ring->irq_get = gen8_logical_ring_get_irq; @@ -1976,8 +2014,13 @@ static int logical_blt_ring_init(struct drm_device *dev) GT_CONTEXT_SWITCH_INTERRUPT << GEN8_BCS_IRQ_SHIFT; ring->init_hw = gen8_init_common_ring; - ring->get_seqno = gen8_get_seqno; - ring->set_seqno = gen8_set_seqno; + if (IS_BROXTON(dev) && INTEL_REVID(dev) < BXT_REVID_B0) { + ring->get_seqno = bxt_a_get_seqno; + ring->set_seqno = bxt_a_set_seqno; + } else { + ring->get_seqno = gen8_get_seqno; + ring->set_seqno = gen8_set_seqno; + } ring->emit_request = gen8_emit_request; ring->emit_flush = gen8_emit_flush; ring->irq_get = gen8_logical_ring_get_irq; @@ -2001,8 +2044,13 @@ static int logical_vebox_ring_init(struct drm_device *dev) GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VECS_IRQ_SHIFT; ring->init_hw = gen8_init_common_ring; - ring->get_seqno = gen8_get_seqno; - ring->set_seqno = gen8_set_seqno; + if (IS_BROXTON(dev) && INTEL_REVID(dev) < BXT_REVID_B0) { + ring->get_seqno = bxt_a_get_seqno; + ring->set_seqno = bxt_a_set_seqno; + } else { + ring->get_seqno = gen8_get_seqno; + ring->set_seqno = gen8_set_seqno; + } ring->emit_request = gen8_emit_request; ring->emit_flush = gen8_emit_flush; ring->irq_get = gen8_logical_ring_get_irq; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 2e85fda94963..95b0b4b55fa6 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -377,6 +377,13 @@ intel_ring_sync_index(struct intel_engine_cs *ring, return idx; } +static inline void +intel_flush_status_page(struct intel_engine_cs *ring, int reg) +{ + drm_clflush_virt_range(&ring->status_page.page_addr[reg], + sizeof(uint32_t)); +} + static inline u32 intel_read_status_page(struct intel_engine_cs *ring, int reg) From e5756c10d841ddb448293c849392f3d6b809561f Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 14 Aug 2015 18:43:30 +0300 Subject: [PATCH 048/145] drm/i915/bxt: don't allow cached GEM mappings on A stepping Due to a coherency issue on BXT A steppings we can't guarantee a coherent view of cached (CPU snooped) GPU mappings, so fail such requests. User space is supposed to fall back to uncached mappings in this case. v2: - limit the WA to A steppings, on later stepping this HW issue is fixed v3: - return error instead of trying to work around the issue in kernel, since that could confuse user space (Chris) Testcast: igt/gem_store_dword_batches_loop/cached-mapping Signed-off-by: Imre Deak Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 1ac57ec803bd..41263cd4170c 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3742,6 +3742,15 @@ int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data, level = I915_CACHE_NONE; break; case I915_CACHING_CACHED: + /* + * Due to a HW issue on BXT A stepping, GPU stores via a + * snooped mapping may leave stale data in a corresponding CPU + * cacheline, whereas normally such cachelines would get + * invalidated. + */ + if (IS_BROXTON(dev) && INTEL_REVID(dev) < BXT_REVID_B0) + return -ENODEV; + level = I915_CACHE_LLC; break; case I915_CACHING_DISPLAY: From 901c2daf05c8ae6c3f85370fc96b9b6892f5da2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 17 Aug 2015 18:05:12 +0300 Subject: [PATCH 049/145] drm/i915: Put back lane_count into intel_dp and add link_rate too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With MST there won't be a crtc assigned to the main link encoder, so trying to dig up the pipe_config from there is a recipe for an oops. Instead store the parameters (lane_count and link_rate) in the encoder, and use those values during link training etc. Since those parameters are now assigned only when the link is actually enabled, .compute_config() won't clobber them as it did before. Hardware state readout is still bonkers though as we don't transfer the link parameters from pipe_config intel_dp. We should do that during encoder sanitation. But since we don't even do a proper job of reading out the main link encoder state for MST there's littel point in worrying about this now. Fixes a regression with MST caused by: commit 90a6b7b052b1aa17fbb98b049e9c8b7f729c35a7 Author: Ville Syrjälä Date: Mon Jul 6 16:39:15 2015 +0300 drm/i915: Move intel_dp->lane_count into pipe_config v2: Different apporoach that should keep intel_dp_check_mst_status() somewhat less oopsy Cc: Maarten Lankhorst Reported-by: Maarten Lankhorst Signed-off-by: Ville Syrjälä Tested-by: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 5 +-- drivers/gpu/drm/i915/intel_dp.c | 53 +++++++++++++---------------- drivers/gpu/drm/i915/intel_dp_mst.c | 2 ++ drivers/gpu/drm/i915/intel_drv.h | 4 +++ 4 files changed, 33 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 56d778f00460..5dff8b7e0f03 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -728,11 +728,10 @@ void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder) struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); struct intel_digital_port *intel_dig_port = enc_to_dig_port(&encoder->base); - struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); intel_dp->DP = intel_dig_port->saved_port_bits | DDI_BUF_CTL_ENABLE | DDI_BUF_TRANS_SELECT(0); - intel_dp->DP |= DDI_PORT_WIDTH(crtc->config->lane_count); + intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count); } static struct intel_encoder * @@ -2314,6 +2313,8 @@ static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder) if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + intel_dp_set_link_params(intel_dp, crtc->config); + intel_ddi_init_dp_buf_reg(intel_encoder); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index b905c19e11a1..dcda86a7d68d 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1584,6 +1584,13 @@ static void ironlake_set_pll_cpu_edp(struct intel_dp *intel_dp) udelay(500); } +void intel_dp_set_link_params(struct intel_dp *intel_dp, + const struct intel_crtc_state *pipe_config) +{ + intel_dp->link_rate = pipe_config->port_clock; + intel_dp->lane_count = pipe_config->lane_count; +} + static void intel_dp_prepare(struct intel_encoder *encoder) { struct drm_device *dev = encoder->base.dev; @@ -1593,6 +1600,8 @@ static void intel_dp_prepare(struct intel_encoder *encoder) struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode; + intel_dp_set_link_params(intel_dp, crtc->config); + /* * There are four kinds of DP registers: * @@ -3348,15 +3357,13 @@ static void intel_get_adjust_train(struct intel_dp *intel_dp, const uint8_t link_status[DP_LINK_STATUS_SIZE]) { - struct intel_crtc *crtc = - to_intel_crtc(dp_to_dig_port(intel_dp)->base.base.crtc); uint8_t v = 0; uint8_t p = 0; int lane; uint8_t voltage_max; uint8_t preemph_max; - for (lane = 0; lane < crtc->config->lane_count; lane++) { + for (lane = 0; lane < intel_dp->lane_count; lane++) { uint8_t this_v = drm_dp_get_adjust_request_voltage(link_status, lane); uint8_t this_p = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); @@ -3527,8 +3534,6 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct drm_i915_private *dev_priv = to_i915(intel_dig_port->base.base.dev); - struct intel_crtc *crtc = - to_intel_crtc(intel_dig_port->base.base.crtc); uint8_t buf[sizeof(intel_dp->train_set) + 1]; int ret, len; @@ -3544,8 +3549,8 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, len = 1; } else { /* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */ - memcpy(buf + 1, intel_dp->train_set, crtc->config->lane_count); - len = crtc->config->lane_count + 1; + memcpy(buf + 1, intel_dp->train_set, intel_dp->lane_count); + len = intel_dp->lane_count + 1; } ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_PATTERN_SET, @@ -3571,8 +3576,6 @@ intel_dp_update_link_train(struct intel_dp *intel_dp, uint32_t *DP, struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct drm_i915_private *dev_priv = to_i915(intel_dig_port->base.base.dev); - struct intel_crtc *crtc = - to_intel_crtc(intel_dig_port->base.base.crtc); int ret; intel_get_adjust_train(intel_dp, link_status); @@ -3582,9 +3585,9 @@ intel_dp_update_link_train(struct intel_dp *intel_dp, uint32_t *DP, POSTING_READ(intel_dp->output_reg); ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_LANE0_SET, - intel_dp->train_set, crtc->config->lane_count); + intel_dp->train_set, intel_dp->lane_count); - return ret == crtc->config->lane_count; + return ret == intel_dp->lane_count; } static void intel_dp_set_idle_link_train(struct intel_dp *intel_dp) @@ -3623,8 +3626,6 @@ void intel_dp_start_link_train(struct intel_dp *intel_dp) { struct drm_encoder *encoder = &dp_to_dig_port(intel_dp)->base.base; - struct intel_crtc *crtc = - to_intel_crtc(dp_to_dig_port(intel_dp)->base.base.crtc); struct drm_device *dev = encoder->dev; int i; uint8_t voltage; @@ -3636,12 +3637,12 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) if (HAS_DDI(dev)) intel_ddi_prepare_link_retrain(encoder); - intel_dp_compute_rate(intel_dp, crtc->config->port_clock, + intel_dp_compute_rate(intel_dp, intel_dp->link_rate, &link_bw, &rate_select); /* Write the link configuration data */ link_config[0] = link_bw; - link_config[1] = crtc->config->lane_count; + link_config[1] = intel_dp->lane_count; if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_BW_SET, link_config, 2); @@ -3675,7 +3676,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) break; } - if (drm_dp_clock_recovery_ok(link_status, crtc->config->lane_count)) { + if (drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) { DRM_DEBUG_KMS("clock recovery OK\n"); break; } @@ -3698,10 +3699,10 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) } /* Check to see if we've tried the max voltage */ - for (i = 0; i < crtc->config->lane_count; i++) + for (i = 0; i < intel_dp->lane_count; i++) if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) break; - if (i == crtc->config->lane_count) { + if (i == intel_dp->lane_count) { ++loop_tries; if (loop_tries == 5) { DRM_ERROR("too many full retries, give up\n"); @@ -3738,15 +3739,13 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) void intel_dp_complete_link_train(struct intel_dp *intel_dp) { - struct intel_crtc *crtc = - to_intel_crtc(dp_to_dig_port(intel_dp)->base.base.crtc); bool channel_eq = false; int tries, cr_tries; uint32_t DP = intel_dp->DP; uint32_t training_pattern = DP_TRAINING_PATTERN_2; /* Training Pattern 3 for HBR2 or 1.2 devices that support it*/ - if (crtc->config->port_clock == 540000 || intel_dp->use_tps3) + if (intel_dp->link_rate == 540000 || intel_dp->use_tps3) training_pattern = DP_TRAINING_PATTERN_3; /* channel equalization */ @@ -3776,7 +3775,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) /* Make sure clock is still ok */ if (!drm_dp_clock_recovery_ok(link_status, - crtc->config->lane_count)) { + intel_dp->lane_count)) { intel_dp->train_set_valid = false; intel_dp_start_link_train(intel_dp); intel_dp_set_link_train(intel_dp, &DP, @@ -3787,7 +3786,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) } if (drm_dp_channel_eq_ok(link_status, - crtc->config->lane_count)) { + intel_dp->lane_count)) { channel_eq = true; break; } @@ -4285,8 +4284,6 @@ static void intel_dp_handle_test_request(struct intel_dp *intel_dp) static int intel_dp_check_mst_status(struct intel_dp *intel_dp) { - struct intel_crtc *crtc = - to_intel_crtc(dp_to_dig_port(intel_dp)->base.base.crtc); bool bret; if (intel_dp->is_mst) { @@ -4300,7 +4297,7 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) /* check link status - esi[10] = 0x200c */ if (intel_dp->active_mst_links && - !drm_dp_channel_eq_ok(&esi[10], crtc->config->lane_count)) { + !drm_dp_channel_eq_ok(&esi[10], intel_dp->lane_count)) { DRM_DEBUG_KMS("channel EQ not ok, retraining\n"); intel_dp_start_link_train(intel_dp); intel_dp_complete_link_train(intel_dp); @@ -4355,8 +4352,6 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base; - struct intel_crtc *crtc = - to_intel_crtc(dp_to_dig_port(intel_dp)->base.base.crtc); u8 sink_irq_vector; u8 link_status[DP_LINK_STATUS_SIZE]; @@ -4392,7 +4387,7 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) DRM_DEBUG_DRIVER("CP or sink specific irq unhandled\n"); } - if (!drm_dp_channel_eq_ok(link_status, crtc->config->lane_count)) { + if (!drm_dp_channel_eq_ok(link_status, intel_dp->lane_count)) { DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n", intel_encoder->base.name); intel_dp_start_link_train(intel_dp); diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c index 1dd2b373c054..dd291d123219 100644 --- a/drivers/gpu/drm/i915/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/intel_dp_mst.c @@ -165,6 +165,8 @@ static void intel_mst_pre_enable_dp(struct intel_encoder *encoder) if (intel_dp->active_mst_links == 0) { enum port port = intel_ddi_get_encoder_port(encoder); + intel_dp_set_link_params(intel_dp, intel_crtc->config); + /* FIXME: add support for SKL */ if (INTEL_INFO(dev)->gen < 9) I915_WRITE(PORT_CLK_SEL(port), diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 22dc8b6b4198..8383442b6fa0 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -708,6 +708,8 @@ struct intel_dp { uint32_t output_reg; uint32_t aux_ch_ctl_reg; uint32_t DP; + int link_rate; + uint8_t lane_count; bool has_audio; enum hdmi_force_audio force_audio; bool limited_color_range; @@ -1161,6 +1163,8 @@ void assert_csr_loaded(struct drm_i915_private *dev_priv); void intel_dp_init(struct drm_device *dev, int output_reg, enum port port); bool intel_dp_init_connector(struct intel_digital_port *intel_dig_port, struct intel_connector *intel_connector); +void intel_dp_set_link_params(struct intel_dp *intel_dp, + const struct intel_crtc_state *pipe_config); void intel_dp_start_link_train(struct intel_dp *intel_dp); void intel_dp_complete_link_train(struct intel_dp *intel_dp); void intel_dp_stop_link_train(struct intel_dp *intel_dp); From 4eee4920f05e39e22571deb57f1c314ce7a46e7f Mon Sep 17 00:00:00 2001 From: Dave Gordon Date: Mon, 17 Aug 2015 17:30:52 +0100 Subject: [PATCH 050/145] drm/i915: fix driver's versions of WARN_ON & WARN_ON_ONCE The current versions of these two macros don't work correctly if the argument expression happens to contain a modulo operator (%) -- when stringified, it gets interpreted as a printf formatting character! With a specifically crafted parameter, this could probably cause a kernel OOPS; consider WARN_ON(p%s) or WARN_ON(f %*pEp). Instead, we should use an explicit "%s" format, with the stringified expression as the coresponding literal-string argument. Signed-off-by: Dave Gordon Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 61244455347f..ce41d24c6888 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -68,11 +68,11 @@ BUILD_BUG_ON(__i915_warn_cond); \ WARN(__i915_warn_cond, "WARN_ON(" #x ")"); }) #else -#define WARN_ON(x) WARN((x), "WARN_ON(" #x ")") +#define WARN_ON(x) WARN((x), "WARN_ON(%s)", #x ) #endif #undef WARN_ON_ONCE -#define WARN_ON_ONCE(x) WARN_ONCE((x), "WARN_ON_ONCE(" #x ")") +#define WARN_ON_ONCE(x) WARN_ONCE((x), "WARN_ON_ONCE(%s)", #x ) #define MISSING_CASE(x) WARN(1, "Missing switch case (%lu) in %s\n", \ (long) (x), __func__); From 25a25dfce4e678bb18c4d63937d240839ecd19cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 8 Jul 2015 23:45:47 +0300 Subject: [PATCH 051/145] drm/i915: Always program m2 fractional value on CHV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When fractional m2 divider isn't used on CHV the fractional part is ignore by the hardware. Despite that, program the fractional value (0 in this case) to the hardware register just to keep things a bit more consistent. Might at least make register dumps a bit less confusing when there isn't some stale fractional part hanging around. Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index cf8dfa53e338..a64da67b4f40 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7374,8 +7374,7 @@ static void chv_prepare_pll(struct intel_crtc *crtc, 1 << DPIO_CHV_N_DIV_SHIFT); /* M2 fraction division */ - if (bestm2_frac) - vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW2(port), bestm2_frac); + vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW2(port), bestm2_frac); /* M2 fraction division enable */ dpio_val = vlv_dpio_read(dev_priv, pipe, CHV_PLL_DW3(port)); From 67fa24b4041436f1a21f7640d1d7ab56d023d7e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 8 Jul 2015 23:45:48 +0300 Subject: [PATCH 052/145] drm/i915: Always program unique transition scale for CHV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The docs give you the impression that the unique transition scale value shouldn't matter when unique transition scale is enabled. But as Imre found on BXT (and I verfied also on BSW) the value does matter. So from now on just program the same value 0x9a always. Cc: Imre Deak Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 53 +++++++++++++++---------------- drivers/gpu/drm/i915/intel_hdmi.c | 30 +++++++++-------- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index dcda86a7d68d..da7d1ae18b9b 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -3179,6 +3179,12 @@ static uint32_t vlv_signal_levels(struct intel_dp *intel_dp) return 0; } +static bool chv_need_uniq_trans_scale(uint8_t train_set) +{ + return (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) == DP_TRAIN_PRE_EMPH_LEVEL_0 && + (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) == DP_TRAIN_VOLTAGE_SWING_LEVEL_3; +} + static uint32_t chv_signal_levels(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); @@ -3297,41 +3303,34 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp) /* Program swing margin */ for (i = 0; i < 4; i++) { val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i)); + val &= ~DPIO_SWING_MARGIN000_MASK; val |= margin_reg_value << DPIO_SWING_MARGIN000_SHIFT; + + /* + * Supposedly this value shouldn't matter when unique transition + * scale is disabled, but in fact it does matter. Let's just + * always program the same value and hope it's OK. + */ + val &= ~(0xff << DPIO_UNIQ_TRANS_SCALE_SHIFT); + val |= 0x9a << DPIO_UNIQ_TRANS_SCALE_SHIFT; + vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val); } - /* Disable unique transition scale */ + /* + * The document said it needs to set bit 27 for ch0 and bit 26 + * for ch1. Might be a typo in the doc. + * For now, for this unique transition scale selection, set bit + * 27 for ch0 and ch1. + */ for (i = 0; i < 4; i++) { val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i)); - val &= ~DPIO_TX_UNIQ_TRANS_SCALE_EN; - vlv_dpio_write(dev_priv, pipe, CHV_TX_DW3(ch, i), val); - } - - if (((train_set & DP_TRAIN_PRE_EMPHASIS_MASK) - == DP_TRAIN_PRE_EMPH_LEVEL_0) && - ((train_set & DP_TRAIN_VOLTAGE_SWING_MASK) - == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)) { - - /* - * The document said it needs to set bit 27 for ch0 and bit 26 - * for ch1. Might be a typo in the doc. - * For now, for this unique transition scale selection, set bit - * 27 for ch0 and ch1. - */ - for (i = 0; i < 4; i++) { - val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i)); + if (chv_need_uniq_trans_scale(train_set)) val |= DPIO_TX_UNIQ_TRANS_SCALE_EN; - vlv_dpio_write(dev_priv, pipe, CHV_TX_DW3(ch, i), val); - } - - for (i = 0; i < 4; i++) { - val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i)); - val &= ~(0xff << DPIO_UNIQ_TRANS_SCALE_SHIFT); - val |= (0x9a << DPIO_UNIQ_TRANS_SCALE_SHIFT); - vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val); - } + else + val &= ~DPIO_TX_UNIQ_TRANS_SCALE_EN; + vlv_dpio_write(dev_priv, pipe, CHV_TX_DW3(ch, i), val); } /* Start swing calculation */ diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 7185062c8c9b..c25835f46c62 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1849,31 +1849,33 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder) for (i = 0; i < 4; i++) { val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i)); + val &= ~DPIO_SWING_MARGIN000_MASK; val |= 102 << DPIO_SWING_MARGIN000_SHIFT; + + /* + * Supposedly this value shouldn't matter when unique transition + * scale is disabled, but in fact it does matter. Let's just + * always program the same value and hope it's OK. + */ + val &= ~(0xff << DPIO_UNIQ_TRANS_SCALE_SHIFT); + val |= 0x9a << DPIO_UNIQ_TRANS_SCALE_SHIFT; + vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val); } - /* Disable unique transition scale */ + /* + * The document said it needs to set bit 27 for ch0 and bit 26 + * for ch1. Might be a typo in the doc. + * For now, for this unique transition scale selection, set bit + * 27 for ch0 and ch1. + */ for (i = 0; i < 4; i++) { val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i)); val &= ~DPIO_TX_UNIQ_TRANS_SCALE_EN; vlv_dpio_write(dev_priv, pipe, CHV_TX_DW3(ch, i), val); } - /* Additional steps for 1200mV-0dB */ -#if 0 - val = vlv_dpio_read(dev_priv, pipe, VLV_TX_DW3(ch)); - if (ch) - val |= DPIO_TX_UNIQ_TRANS_SCALE_CH1; - else - val |= DPIO_TX_UNIQ_TRANS_SCALE_CH0; - vlv_dpio_write(dev_priv, pipe, VLV_TX_DW3(ch), val); - - vlv_dpio_write(dev_priv, pipe, VLV_TX_DW2(ch), - vlv_dpio_read(dev_priv, pipe, VLV_TX_DW2(ch)) | - (0x9a << DPIO_UNIQ_TRANS_SCALE_SHIFT)); -#endif /* Start swing calculation */ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch)); val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3; From d6db995fe36c7460a30c8c6250bb47937d38c482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 8 Jul 2015 23:45:49 +0300 Subject: [PATCH 053/145] drm/i915: Add encoder->post_pll_disable() hooks and move CHV clock buffer disables there MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the CHV clock buffer disable from chv_disable_pll() to the new encoder .post_pll_disable() hook. This is more symmetric since the clock buffer enable happens from the .pre_pll_enable() hook. We'll have more use for the new hook soon. Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 15 ++++----------- drivers/gpu/drm/i915/intel_dp.c | 23 +++++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_hdmi.c | 23 +++++++++++++++++++++++ 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a64da67b4f40..328d1eec6af4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1828,17 +1828,6 @@ static void chv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) val &= ~DPIO_DCLKP_EN; vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW14(port), val); - /* disable left/right clock distribution */ - if (pipe != PIPE_B) { - val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW5_CH0); - val &= ~(CHV_BUFLEFTENA1_MASK | CHV_BUFRIGHTENA1_MASK); - vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW5_CH0, val); - } else { - val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW1_CH1); - val &= ~(CHV_BUFLEFTENA2_MASK | CHV_BUFRIGHTENA2_MASK); - vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW1_CH1, val); - } - mutex_unlock(&dev_priv->sb_lock); } @@ -6193,6 +6182,10 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) i9xx_disable_pll(intel_crtc); } + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->post_pll_disable) + encoder->post_pll_disable(encoder); + if (!IS_GEN2(dev)) intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index da7d1ae18b9b..4e5bd87b75db 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2943,6 +2943,28 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder) mutex_unlock(&dev_priv->sb_lock); } +static void chv_dp_post_pll_disable(struct intel_encoder *encoder) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + enum pipe pipe = to_intel_crtc(encoder->base.crtc)->pipe; + u32 val; + + mutex_lock(&dev_priv->sb_lock); + + /* disable left/right clock distribution */ + if (pipe != PIPE_B) { + val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW5_CH0); + val &= ~(CHV_BUFLEFTENA1_MASK | CHV_BUFRIGHTENA1_MASK); + vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW5_CH0, val); + } else { + val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW1_CH1); + val &= ~(CHV_BUFLEFTENA2_MASK | CHV_BUFRIGHTENA2_MASK); + vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW1_CH1, val); + } + + mutex_unlock(&dev_priv->sb_lock); +} + /* * Native read with retry for link status and receiver capability reads for * cases where the sink may still be asleep. @@ -6000,6 +6022,7 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) intel_encoder->pre_enable = chv_pre_enable_dp; intel_encoder->enable = vlv_enable_dp; intel_encoder->post_disable = chv_post_disable_dp; + intel_encoder->post_pll_disable = chv_dp_post_pll_disable; } else if (IS_VALLEYVIEW(dev)) { intel_encoder->pre_pll_enable = vlv_dp_pre_pll_enable; intel_encoder->pre_enable = vlv_pre_enable_dp; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8383442b6fa0..525e45736e1c 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -142,6 +142,7 @@ struct intel_encoder { void (*mode_set)(struct intel_encoder *intel_encoder); void (*disable)(struct intel_encoder *); void (*post_disable)(struct intel_encoder *); + void (*post_pll_disable)(struct intel_encoder *); /* Read out the current hw state of this connector, returning true if * the encoder is active. If the encoder is enabled it also set the pipe * it is connected to in the pipe parameter. */ diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index c25835f46c62..269937a7919a 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1681,6 +1681,28 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) mutex_unlock(&dev_priv->sb_lock); } +static void chv_hdmi_post_pll_disable(struct intel_encoder *encoder) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + enum pipe pipe = to_intel_crtc(encoder->base.crtc)->pipe; + u32 val; + + mutex_lock(&dev_priv->sb_lock); + + /* disable left/right clock distribution */ + if (pipe != PIPE_B) { + val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW5_CH0); + val &= ~(CHV_BUFLEFTENA1_MASK | CHV_BUFRIGHTENA1_MASK); + vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW5_CH0, val); + } else { + val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW1_CH1); + val &= ~(CHV_BUFLEFTENA2_MASK | CHV_BUFRIGHTENA2_MASK); + vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW1_CH1, val); + } + + mutex_unlock(&dev_priv->sb_lock); +} + static void vlv_hdmi_post_disable(struct intel_encoder *encoder) { struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); @@ -2083,6 +2105,7 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port) intel_encoder->pre_enable = chv_hdmi_pre_enable; intel_encoder->enable = vlv_enable_hdmi; intel_encoder->post_disable = chv_hdmi_post_disable; + intel_encoder->post_pll_disable = chv_hdmi_post_pll_disable; } else if (IS_VALLEYVIEW(dev)) { intel_encoder->pre_pll_enable = vlv_hdmi_pre_pll_enable; intel_encoder->pre_enable = vlv_hdmi_pre_enable; From e27f299ec3ddd03373bb07db23dd19747eecef72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 8 Jul 2015 23:45:50 +0300 Subject: [PATCH 054/145] drm/i915: Move DPIO port init earlier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To implement DPIO lane power gating on CHV we're going to need to access DPIO registers from the cmn power well enable hook. That gets called rather early, so we need to move the DPIO port IOSF sideband port assignment earlier as well. Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 20 ++++++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 22 ---------------------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 2193cc28f70c..097d4ba0421c 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -799,6 +799,24 @@ static void intel_device_info_runtime_init(struct drm_device *dev) info->has_eu_pg ? "y" : "n"); } +static void intel_init_dpio(struct drm_i915_private *dev_priv) +{ + if (!IS_VALLEYVIEW(dev_priv)) + return; + + /* + * IOSF_PORT_DPIO is used for VLV x2 PHY (DP/HDMI B and C), + * CHV x1 PHY (DP/HDMI D) + * IOSF_PORT_DPIO_2 is used for CHV x2 PHY (DP/HDMI B and C) + */ + if (IS_CHERRYVIEW(dev_priv)) { + DPIO_PHY_IOSF_PORT(DPIO_PHY0) = IOSF_PORT_DPIO_2; + DPIO_PHY_IOSF_PORT(DPIO_PHY1) = IOSF_PORT_DPIO; + } else { + DPIO_PHY_IOSF_PORT(DPIO_PHY0) = IOSF_PORT_DPIO; + } +} + /** * i915_driver_load - setup chip and create an initial config * @dev: DRM device @@ -999,6 +1017,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) intel_device_info_runtime_init(dev); + intel_init_dpio(dev_priv); + if (INTEL_INFO(dev)->num_pipes) { ret = drm_vblank_init(dev, INTEL_INFO(dev)->num_pipes); if (ret) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 328d1eec6af4..3c2c76b388b4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1582,26 +1582,6 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv, assert_pch_hdmi_disabled(dev_priv, pipe, PCH_HDMID); } -static void intel_init_dpio(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - if (!IS_VALLEYVIEW(dev)) - return; - - /* - * IOSF_PORT_DPIO is used for VLV x2 PHY (DP/HDMI B and C), - * CHV x1 PHY (DP/HDMI D) - * IOSF_PORT_DPIO_2 is used for CHV x2 PHY (DP/HDMI B and C) - */ - if (IS_CHERRYVIEW(dev)) { - DPIO_PHY_IOSF_PORT(DPIO_PHY0) = IOSF_PORT_DPIO_2; - DPIO_PHY_IOSF_PORT(DPIO_PHY1) = IOSF_PORT_DPIO; - } else { - DPIO_PHY_IOSF_PORT(DPIO_PHY0) = IOSF_PORT_DPIO; - } -} - static void vlv_enable_pll(struct intel_crtc *crtc, const struct intel_crtc_state *pipe_config) { @@ -14767,8 +14747,6 @@ void intel_modeset_init(struct drm_device *dev) } } - intel_init_dpio(dev); - intel_shared_dpll_init(dev); /* Just disable it once at startup */ From 770effb19fbdcb44c6bdacf4a78571d28393f48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 8 Jul 2015 23:45:51 +0300 Subject: [PATCH 055/145] drm/i915: Add locking around chv_phy_control_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dev_priv->chv_phy_control is protected by the power_domains->lock elsewhere, so also grab it when initializing chv_phy_control. Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_runtime_pm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index 821644d1b544..974c4cbf3fdb 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -1685,7 +1685,9 @@ void intel_power_domains_init_hw(struct drm_i915_private *dev_priv) power_domains->initializing = true; if (IS_CHERRYVIEW(dev)) { + mutex_lock(&power_domains->lock); chv_phy_control_init(dev_priv); + mutex_unlock(&power_domains->lock); } else if (IS_VALLEYVIEW(dev)) { mutex_lock(&power_domains->lock); vlv_cmnlane_wa(dev_priv); From c0b4c660311c73c8ee4ed0e58b65ae9b4b75276d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 8 Jul 2015 23:45:52 +0300 Subject: [PATCH 056/145] drm/i915: Move VLV/CHV prepare_pll later MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With DPIO powergating active on CHV, we can't even access the DPIO PLL registers until the lane power state overrides have been enabled. That will happen from the encoder .pre_pll_enable() hook, so move chv_prepare_pll() to happen after that point, which puts it just before chv_enable_pll() actually. Do the same for VLV to avoid accumulating weird differences between the platforms. Both platforms seem happy with the new arrangement. Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3c2c76b388b4..50cbc7e374cc 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6001,13 +6001,6 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) is_dsi = intel_pipe_has_type(intel_crtc, INTEL_OUTPUT_DSI); - if (!is_dsi) { - if (IS_CHERRYVIEW(dev)) - chv_prepare_pll(intel_crtc, intel_crtc->config); - else - vlv_prepare_pll(intel_crtc, intel_crtc->config); - } - if (intel_crtc->config->has_dp_encoder) intel_dp_set_m_n(intel_crtc, M1_N1); @@ -6031,10 +6024,13 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) encoder->pre_pll_enable(encoder); if (!is_dsi) { - if (IS_CHERRYVIEW(dev)) + if (IS_CHERRYVIEW(dev)) { + chv_prepare_pll(intel_crtc, intel_crtc->config); chv_enable_pll(intel_crtc, intel_crtc->config); - else + } else { + vlv_prepare_pll(intel_crtc, intel_crtc->config); vlv_enable_pll(intel_crtc, intel_crtc->config); + } } for_each_encoder_on_crtc(dev, crtc, encoder) From 65d64cc5bb7aee1f5a8d6717f4d421623c58ea30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 8 Jul 2015 23:45:53 +0300 Subject: [PATCH 057/145] drm/i915: Add vlv_dport_to_phy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add vlv_dport_to_phy() and fix up the return values of vlv_dport_to_channel() and vlv_pipe_to_channel() to use the appropriate enums. Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_drv.h | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 525e45736e1c..49d1b61336a1 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -788,7 +788,7 @@ struct intel_dp_mst_encoder { void *port; /* store this opaque as its illegal to dereference it */ }; -static inline int +static inline enum dpio_channel vlv_dport_to_channel(struct intel_digital_port *dport) { switch (dport->port) { @@ -802,7 +802,21 @@ vlv_dport_to_channel(struct intel_digital_port *dport) } } -static inline int +static inline enum dpio_phy +vlv_dport_to_phy(struct intel_digital_port *dport) +{ + switch (dport->port) { + case PORT_B: + case PORT_C: + return DPIO_PHY0; + case PORT_D: + return DPIO_PHY1; + default: + BUG(); + } +} + +static inline enum dpio_channel vlv_pipe_to_channel(enum pipe pipe) { switch (pipe) { From adafdc6fcb66ea6541a88884ec522273f857c145 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Tue, 18 Aug 2015 14:36:59 +0300 Subject: [PATCH 058/145] drm/i915: Store max dotclock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Store max dotclock into dev_priv structure so we are able to filter out the modes that are not supported by our platforms. V2: - limit the max dot clock frequency to max CD clock frequency for the gen9 and above - limit the max dot clock frequency to 90% of the max CD clock frequency for the older gens - for Cherryview the max dot clock frequency is limited to 95% of the max CD clock frequency - for gen2 and gen3 the max dot clock limit is set to 90% of the 2X max CD clock frequency V3: - max_dotclk variable renamed as max_dotclk_freq in i915_drv.h - in intel_compute_max_dotclk() the rounding method changed from round up to round down when computing max dotclock V4: - Haswell and Broadwell supports now dot clocks up to max CD clock frequency Signed-off-by: Mika Kahola Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/intel_display.c | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ce41d24c6888..705f208b49a0 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1792,6 +1792,7 @@ struct drm_i915_private { unsigned int fsb_freq, mem_freq, is_ddr3; unsigned int skl_boot_cdclk; unsigned int cdclk_freq, max_cdclk_freq; + unsigned int max_dotclk_freq; unsigned int hpll_freq; /** diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 50cbc7e374cc..03e84ace8a09 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5240,6 +5240,21 @@ static void modeset_update_crtc_power_domains(struct drm_atomic_state *state) modeset_put_power_domains(dev_priv, put_domains[i]); } +static int intel_compute_max_dotclk(struct drm_i915_private *dev_priv) +{ + int max_cdclk_freq = dev_priv->max_cdclk_freq; + + if (INTEL_INFO(dev_priv)->gen >= 9 || + IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) + return max_cdclk_freq; + else if (IS_CHERRYVIEW(dev_priv)) + return max_cdclk_freq*95/100; + else if (INTEL_INFO(dev_priv)->gen < 4) + return 2*max_cdclk_freq*90/100; + else + return max_cdclk_freq*90/100; +} + static void intel_update_max_cdclk(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -5279,8 +5294,13 @@ static void intel_update_max_cdclk(struct drm_device *dev) dev_priv->max_cdclk_freq = dev_priv->cdclk_freq; } + dev_priv->max_dotclk_freq = intel_compute_max_dotclk(dev_priv); + DRM_DEBUG_DRIVER("Max CD clock rate: %d kHz\n", dev_priv->max_cdclk_freq); + + DRM_DEBUG_DRIVER("Max dotclock rate: %d kHz\n", + dev_priv->max_dotclk_freq); } static void intel_update_cdclk(struct drm_device *dev) From 7f7b58cc61883bd16dfd4cae67d7404f7905d0a7 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Tue, 18 Aug 2015 14:37:00 +0300 Subject: [PATCH 059/145] drm/i915: LVDS pixel clock check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is possible the we request to have a mode that has higher pixel clock than our HW can support. This patch checks if requested pixel clock is lower than the one supported by the HW. The requested mode is discarded if we cannot support the requested pixel clock. This patch applies to LVDS. V2: - removed computation for max pixel clock V3: - cleanup by removing unnecessary lines V4: - moved supported dotclock check from mode_valid() to intel_lvds_init() V5: - dotclock check moved back to mode_valid() function - dotclock check for fixed mode Signed-off-by: Mika Kahola Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_lvds.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 881b5d13592e..0794dc84ff01 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -289,11 +289,14 @@ intel_lvds_mode_valid(struct drm_connector *connector, { struct intel_connector *intel_connector = to_intel_connector(connector); struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; + int max_pixclk = to_i915(connector->dev)->max_dotclk_freq; if (mode->hdisplay > fixed_mode->hdisplay) return MODE_PANEL; if (mode->vdisplay > fixed_mode->vdisplay) return MODE_PANEL; + if (fixed_mode->clock > max_pixclk) + return MODE_CLOCK_HIGH; return MODE_OK; } From 759a1e98213f17d51926495e6754fdaced5f7d81 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Tue, 18 Aug 2015 14:37:01 +0300 Subject: [PATCH 060/145] drm/i915: DSI pixel clock check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is possible the we request to have a mode that has higher pixel clock than our HW can support. This patch checks if requested pixel clock is lower than the one supported by the HW. The requested mode is discarded if we cannot support the requested pixel clock. This patch applies to DSI. V2: - removed computation for max pixel clock V3: - cleanup by removing unnecessary lines V4: - max_pixclk variable renamed as max_dotclk - moved dot clock checking inside 'if (fixed_mode)' V5: - dot clock checked against fixed_mode clock Signed-off-by: Mika Kahola Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dsi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index 4a601cf90f16..781c267ea97c 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -654,6 +654,7 @@ intel_dsi_mode_valid(struct drm_connector *connector, { struct intel_connector *intel_connector = to_intel_connector(connector); struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; + int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; DRM_DEBUG_KMS("\n"); @@ -667,6 +668,8 @@ intel_dsi_mode_valid(struct drm_connector *connector, return MODE_PANEL; if (mode->vdisplay > fixed_mode->vdisplay) return MODE_PANEL; + if (fixed_mode->clock > max_dotclk) + return MODE_CLOCK_HIGH; } return MODE_OK; From 26a91555bd27b0672ab4efaf4de04fe7321540dc Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Tue, 18 Aug 2015 14:37:02 +0300 Subject: [PATCH 061/145] drm/i915: DVO pixel clock check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is possible the we request to have a mode that has higher pixel clock than our HW can support. This patch checks if requested pixel clock is lower than the one supported by the HW. The requested mode is discarded if we cannot support the requested pixel clock. This patch applies to DVO. V2: - removed computation for max pixel clock V3: - cleanup by removing unnecessary lines V4: - clock check against max dotclock moved inside 'if (fixed_mode)' V5: - dot clock check against fixed_mode clock when available Signed-off-by: Mika Kahola Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dvo.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index dc532bb61d22..c80fe1f49ede 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -201,6 +201,8 @@ intel_dvo_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct intel_dvo *intel_dvo = intel_attached_dvo(connector); + int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; + int target_clock = mode->clock; if (mode->flags & DRM_MODE_FLAG_DBLSCAN) return MODE_NO_DBLESCAN; @@ -212,8 +214,13 @@ intel_dvo_mode_valid(struct drm_connector *connector, return MODE_PANEL; if (mode->vdisplay > intel_dvo->panel_fixed_mode->vdisplay) return MODE_PANEL; + + target_clock = intel_dvo->panel_fixed_mode->clock; } + if (target_clock > max_dotclk) + return MODE_CLOCK_HIGH; + return intel_dvo->dev.dev_ops->mode_valid(&intel_dvo->dev, mode); } From b93433ccf64846820b9448f5ff5dd4348b58a8ed Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 20 Aug 2015 10:47:36 +0300 Subject: [PATCH 062/145] drm/i915: move ibx_digital_port_connected to intel_dp.c The function can be made static there. No functional changes. Reviewed-by: Durgadoss R Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 45 -------------------- drivers/gpu/drm/i915/intel_dp.c | 61 ++++++++++++++++++++++++---- drivers/gpu/drm/i915/intel_drv.h | 2 - 3 files changed, 53 insertions(+), 55 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 03e84ace8a09..f32206d95986 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1061,51 +1061,6 @@ static void intel_wait_for_pipe_off(struct intel_crtc *crtc) } } -/* - * ibx_digital_port_connected - is the specified port connected? - * @dev_priv: i915 private structure - * @port: the port to test - * - * Returns true if @port is connected, false otherwise. - */ -bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, - struct intel_digital_port *port) -{ - u32 bit; - - if (HAS_PCH_IBX(dev_priv->dev)) { - switch (port->port) { - case PORT_B: - bit = SDE_PORTB_HOTPLUG; - break; - case PORT_C: - bit = SDE_PORTC_HOTPLUG; - break; - case PORT_D: - bit = SDE_PORTD_HOTPLUG; - break; - default: - return true; - } - } else { - switch (port->port) { - case PORT_B: - bit = SDE_PORTB_HOTPLUG_CPT; - break; - case PORT_C: - bit = SDE_PORTC_HOTPLUG_CPT; - break; - case PORT_D: - bit = SDE_PORTD_HOTPLUG_CPT; - break; - default: - return true; - } - } - - return I915_READ(SDEISR) & bit; -} - static const char *state_string(bool enabled) { return enabled ? "on" : "off"; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 4e5bd87b75db..307a45b2a404 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4480,17 +4480,49 @@ edp_detect(struct intel_dp *intel_dp) return status; } -static enum drm_connector_status -ironlake_dp_detect(struct intel_dp *intel_dp) +/* + * ibx_digital_port_connected - is the specified port connected? + * @dev_priv: i915 private structure + * @port: the port to test + * + * Returns true if @port is connected, false otherwise. + */ +static bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, + struct intel_digital_port *port) { - struct drm_device *dev = intel_dp_to_dev(intel_dp); - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + u32 bit; - if (!ibx_digital_port_connected(dev_priv, intel_dig_port)) - return connector_status_disconnected; + if (HAS_PCH_IBX(dev_priv->dev)) { + switch (port->port) { + case PORT_B: + bit = SDE_PORTB_HOTPLUG; + break; + case PORT_C: + bit = SDE_PORTC_HOTPLUG; + break; + case PORT_D: + bit = SDE_PORTD_HOTPLUG; + break; + default: + return true; + } + } else { + switch (port->port) { + case PORT_B: + bit = SDE_PORTB_HOTPLUG_CPT; + break; + case PORT_C: + bit = SDE_PORTC_HOTPLUG_CPT; + break; + case PORT_D: + bit = SDE_PORTD_HOTPLUG_CPT; + break; + default: + return true; + } + } - return intel_dp_detect_dpcd(intel_dp); + return I915_READ(SDEISR) & bit; } static int g4x_digital_port_connected(struct drm_device *dev, @@ -4534,6 +4566,19 @@ static int g4x_digital_port_connected(struct drm_device *dev, return 1; } +static enum drm_connector_status +ironlake_dp_detect(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + + if (!ibx_digital_port_connected(dev_priv, intel_dig_port)) + return connector_status_disconnected; + + return intel_dp_detect_dpcd(intel_dp); +} + static enum drm_connector_status g4x_dp_detect(struct intel_dp *intel_dp) { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 49d1b61336a1..aa7971798639 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1018,8 +1018,6 @@ void intel_encoder_destroy(struct drm_encoder *encoder); int intel_connector_init(struct intel_connector *); struct intel_connector *intel_connector_alloc(void); bool intel_connector_get_hw_state(struct intel_connector *connector); -bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, - struct intel_digital_port *port); void intel_connector_attach_encoder(struct intel_connector *connector, struct intel_encoder *encoder); struct drm_encoder *intel_best_encoder(struct drm_connector *connector); From 1d245987754f8cd0bf21c77ecda2023ca13ff8c1 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 20 Aug 2015 10:47:37 +0300 Subject: [PATCH 063/145] drm/i915: make g4x_digital_port_connected return boolean status We should not be hitting any of the default cases in g4x_digital_port_connected, so add MISSING_CASE annotation and return boolean status. The current behaviour is just cargo culting from the days of yonder when the display port support was added to i915. Signed-off-by: Jani Nikula Reviewed-by: Durgadoss R Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 307a45b2a404..be53e36499b8 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4525,14 +4525,14 @@ static bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, return I915_READ(SDEISR) & bit; } -static int g4x_digital_port_connected(struct drm_device *dev, - struct intel_digital_port *intel_dig_port) +static bool g4x_digital_port_connected(struct drm_device *dev, + struct intel_digital_port *port) { struct drm_i915_private *dev_priv = dev->dev_private; uint32_t bit; if (IS_VALLEYVIEW(dev)) { - switch (intel_dig_port->port) { + switch (port->port) { case PORT_B: bit = PORTB_HOTPLUG_LIVE_STATUS_VLV; break; @@ -4543,10 +4543,11 @@ static int g4x_digital_port_connected(struct drm_device *dev, bit = PORTD_HOTPLUG_LIVE_STATUS_VLV; break; default: - return -EINVAL; + MISSING_CASE(port->port); + return false; } } else { - switch (intel_dig_port->port) { + switch (port->port) { case PORT_B: bit = PORTB_HOTPLUG_LIVE_STATUS_G4X; break; @@ -4557,13 +4558,12 @@ static int g4x_digital_port_connected(struct drm_device *dev, bit = PORTD_HOTPLUG_LIVE_STATUS_G4X; break; default: - return -EINVAL; + MISSING_CASE(port->port); + return false; } } - if ((I915_READ(PORT_HOTPLUG_STAT) & bit) == 0) - return 0; - return 1; + return I915_READ(PORT_HOTPLUG_STAT) & bit; } static enum drm_connector_status @@ -4584,7 +4584,6 @@ g4x_dp_detect(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - int ret; /* Can't disconnect eDP, but you can close the lid... */ if (is_edp(intel_dp)) { @@ -4596,10 +4595,7 @@ g4x_dp_detect(struct intel_dp *intel_dp) return status; } - ret = g4x_digital_port_connected(dev, intel_dig_port); - if (ret == -EINVAL) - return connector_status_unknown; - else if (ret == 0) + if (!g4x_digital_port_connected(dev, intel_dig_port)) return connector_status_disconnected; return intel_dp_detect_dpcd(intel_dp); @@ -5066,7 +5062,7 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) if (!ibx_digital_port_connected(dev_priv, intel_dig_port)) goto mst_fail; } else { - if (g4x_digital_port_connected(dev, intel_dig_port) != 1) + if (!g4x_digital_port_connected(dev, intel_dig_port)) goto mst_fail; } From 196cabd4a39496038ee81c2755496955e7355bd5 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 20 Aug 2015 10:47:38 +0300 Subject: [PATCH 064/145] drm/i915: add MISSING_CASE annotation to ibx_digital_port_connected With the case added for eDP on port A (always connected from this function's point of view), we should not be hitting any of the default cases in ibx_digital_port_connected, so add MISSING_CASE annotation. Signed-off-by: Jani Nikula Reviewed-by: Durgadoss R Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index be53e36499b8..8c8070ee26d7 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4494,6 +4494,8 @@ static bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, if (HAS_PCH_IBX(dev_priv->dev)) { switch (port->port) { + case PORT_A: + return true; case PORT_B: bit = SDE_PORTB_HOTPLUG; break; @@ -4504,10 +4506,13 @@ static bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, bit = SDE_PORTD_HOTPLUG; break; default: - return true; + MISSING_CASE(port->port); + return false; } } else { switch (port->port) { + case PORT_A: + return true; case PORT_B: bit = SDE_PORTB_HOTPLUG_CPT; break; @@ -4518,7 +4523,8 @@ static bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, bit = SDE_PORTD_HOTPLUG_CPT; break; default: - return true; + MISSING_CASE(port->port); + return false; } } From 7e66bcf2656ad2e53de939c5a5f211288a332ede Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 20 Aug 2015 10:47:39 +0300 Subject: [PATCH 065/145] drm/i915: add common intel_digital_port_connected function Add a common intel_digital_port_connected() that splits out to functions for different platforms. No functional changes. v2: make the function return a boolean Signed-off-by: Jani Nikula Reviewed-by: Durgadoss R Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 41 ++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 8c8070ee26d7..1db7e1edd00d 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4480,13 +4480,6 @@ edp_detect(struct intel_dp *intel_dp) return status; } -/* - * ibx_digital_port_connected - is the specified port connected? - * @dev_priv: i915 private structure - * @port: the port to test - * - * Returns true if @port is connected, false otherwise. - */ static bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, struct intel_digital_port *port) { @@ -4531,13 +4524,12 @@ static bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, return I915_READ(SDEISR) & bit; } -static bool g4x_digital_port_connected(struct drm_device *dev, +static bool g4x_digital_port_connected(struct drm_i915_private *dev_priv, struct intel_digital_port *port) { - struct drm_i915_private *dev_priv = dev->dev_private; uint32_t bit; - if (IS_VALLEYVIEW(dev)) { + if (IS_VALLEYVIEW(dev_priv)) { switch (port->port) { case PORT_B: bit = PORTB_HOTPLUG_LIVE_STATUS_VLV; @@ -4572,6 +4564,22 @@ static bool g4x_digital_port_connected(struct drm_device *dev, return I915_READ(PORT_HOTPLUG_STAT) & bit; } +/* + * intel_digital_port_connected - is the specified port connected? + * @dev_priv: i915 private structure + * @port: the port to test + * + * Return %true if @port is connected, %false otherwise. + */ +static bool intel_digital_port_connected(struct drm_i915_private *dev_priv, + struct intel_digital_port *port) +{ + if (HAS_PCH_SPLIT(dev_priv)) + return ibx_digital_port_connected(dev_priv, port); + else + return g4x_digital_port_connected(dev_priv, port); +} + static enum drm_connector_status ironlake_dp_detect(struct intel_dp *intel_dp) { @@ -4579,7 +4587,7 @@ ironlake_dp_detect(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - if (!ibx_digital_port_connected(dev_priv, intel_dig_port)) + if (!intel_digital_port_connected(dev_priv, intel_dig_port)) return connector_status_disconnected; return intel_dp_detect_dpcd(intel_dp); @@ -4601,7 +4609,7 @@ g4x_dp_detect(struct intel_dp *intel_dp) return status; } - if (!g4x_digital_port_connected(dev, intel_dig_port)) + if (!intel_digital_port_connected(dev->dev_private, intel_dig_port)) return connector_status_disconnected; return intel_dp_detect_dpcd(intel_dp); @@ -5064,13 +5072,8 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) /* indicate that we need to restart link training */ intel_dp->train_set_valid = false; - if (HAS_PCH_SPLIT(dev)) { - if (!ibx_digital_port_connected(dev_priv, intel_dig_port)) - goto mst_fail; - } else { - if (!g4x_digital_port_connected(dev, intel_dig_port)) - goto mst_fail; - } + if (!intel_digital_port_connected(dev_priv, intel_dig_port)) + goto mst_fail; if (!intel_dp_get_dpcd(intel_dp)) { goto mst_fail; From 0df53b77287198502ff2eb51120c041ba317f059 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 20 Aug 2015 10:47:40 +0300 Subject: [PATCH 066/145] drm/i915: split ibx_digital_port_connected to ibx and cpt variants Choose the right function at the intel_digital_port_connected level. Signed-off-by: Jani Nikula Reviewed-by: Durgadoss R Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 78 ++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 1db7e1edd00d..689dfe65d27b 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4485,40 +4485,46 @@ static bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, { u32 bit; - if (HAS_PCH_IBX(dev_priv->dev)) { - switch (port->port) { - case PORT_A: - return true; - case PORT_B: - bit = SDE_PORTB_HOTPLUG; - break; - case PORT_C: - bit = SDE_PORTC_HOTPLUG; - break; - case PORT_D: - bit = SDE_PORTD_HOTPLUG; - break; - default: - MISSING_CASE(port->port); - return false; - } - } else { - switch (port->port) { - case PORT_A: - return true; - case PORT_B: - bit = SDE_PORTB_HOTPLUG_CPT; - break; - case PORT_C: - bit = SDE_PORTC_HOTPLUG_CPT; - break; - case PORT_D: - bit = SDE_PORTD_HOTPLUG_CPT; - break; - default: - MISSING_CASE(port->port); - return false; - } + switch (port->port) { + case PORT_A: + return true; + case PORT_B: + bit = SDE_PORTB_HOTPLUG; + break; + case PORT_C: + bit = SDE_PORTC_HOTPLUG; + break; + case PORT_D: + bit = SDE_PORTD_HOTPLUG; + break; + default: + MISSING_CASE(port->port); + return false; + } + + return I915_READ(SDEISR) & bit; +} + +static bool cpt_digital_port_connected(struct drm_i915_private *dev_priv, + struct intel_digital_port *port) +{ + u32 bit; + + switch (port->port) { + case PORT_A: + return true; + case PORT_B: + bit = SDE_PORTB_HOTPLUG_CPT; + break; + case PORT_C: + bit = SDE_PORTC_HOTPLUG_CPT; + break; + case PORT_D: + bit = SDE_PORTD_HOTPLUG_CPT; + break; + default: + MISSING_CASE(port->port); + return false; } return I915_READ(SDEISR) & bit; @@ -4574,8 +4580,10 @@ static bool g4x_digital_port_connected(struct drm_i915_private *dev_priv, static bool intel_digital_port_connected(struct drm_i915_private *dev_priv, struct intel_digital_port *port) { - if (HAS_PCH_SPLIT(dev_priv)) + if (HAS_PCH_IBX(dev_priv)) return ibx_digital_port_connected(dev_priv, port); + if (HAS_PCH_SPLIT(dev_priv)) + return cpt_digital_port_connected(dev_priv, port); else return g4x_digital_port_connected(dev_priv, port); } From 9642c81c225a43982536fcb7a3fa212dcfaa67ea Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 20 Aug 2015 10:47:41 +0300 Subject: [PATCH 067/145] drm/i915: split g4x_digital_port_connected to g4x and vlv variants Choose the right function at the intel_digital_port_connected level. Signed-off-by: Jani Nikula Reviewed-by: Durgadoss R Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 70 ++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 689dfe65d27b..e7ebacd51432 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4533,38 +4533,44 @@ static bool cpt_digital_port_connected(struct drm_i915_private *dev_priv, static bool g4x_digital_port_connected(struct drm_i915_private *dev_priv, struct intel_digital_port *port) { - uint32_t bit; + u32 bit; - if (IS_VALLEYVIEW(dev_priv)) { - switch (port->port) { - case PORT_B: - bit = PORTB_HOTPLUG_LIVE_STATUS_VLV; - break; - case PORT_C: - bit = PORTC_HOTPLUG_LIVE_STATUS_VLV; - break; - case PORT_D: - bit = PORTD_HOTPLUG_LIVE_STATUS_VLV; - break; - default: - MISSING_CASE(port->port); - return false; - } - } else { - switch (port->port) { - case PORT_B: - bit = PORTB_HOTPLUG_LIVE_STATUS_G4X; - break; - case PORT_C: - bit = PORTC_HOTPLUG_LIVE_STATUS_G4X; - break; - case PORT_D: - bit = PORTD_HOTPLUG_LIVE_STATUS_G4X; - break; - default: - MISSING_CASE(port->port); - return false; - } + switch (port->port) { + case PORT_B: + bit = PORTB_HOTPLUG_LIVE_STATUS_G4X; + break; + case PORT_C: + bit = PORTC_HOTPLUG_LIVE_STATUS_G4X; + break; + case PORT_D: + bit = PORTD_HOTPLUG_LIVE_STATUS_G4X; + break; + default: + MISSING_CASE(port->port); + return false; + } + + return I915_READ(PORT_HOTPLUG_STAT) & bit; +} + +static bool vlv_digital_port_connected(struct drm_i915_private *dev_priv, + struct intel_digital_port *port) +{ + u32 bit; + + switch (port->port) { + case PORT_B: + bit = PORTB_HOTPLUG_LIVE_STATUS_VLV; + break; + case PORT_C: + bit = PORTC_HOTPLUG_LIVE_STATUS_VLV; + break; + case PORT_D: + bit = PORTD_HOTPLUG_LIVE_STATUS_VLV; + break; + default: + MISSING_CASE(port->port); + return false; } return I915_READ(PORT_HOTPLUG_STAT) & bit; @@ -4584,6 +4590,8 @@ static bool intel_digital_port_connected(struct drm_i915_private *dev_priv, return ibx_digital_port_connected(dev_priv, port); if (HAS_PCH_SPLIT(dev_priv)) return cpt_digital_port_connected(dev_priv, port); + else if (IS_VALLEYVIEW(dev_priv)) + return vlv_digital_port_connected(dev_priv, port); else return g4x_digital_port_connected(dev_priv, port); } From e464bfdeda3da3b0ef29ca84960715993ccbb1fc Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 20 Aug 2015 10:47:42 +0300 Subject: [PATCH 068/145] drm/i915/bxt: Use correct live status register for BXT platform BXT platform uses live status bits from 0x44440 register to obtain DP status on hotplug. The existing g4x_digital_port_connected() uses a different register and hence misses DP hotplug events on BXT platform. This patch fixes it by using the appropriate register(0x44440) and live status bits(3:5). Based on a patch by Durgadoss R , from whom the commit message is shamelessly copy pasted. Reported-by: Durgadoss R Signed-off-by: Jani Nikula Reviewed-by: Durgadoss R Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index e7ebacd51432..1ff063e31584 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4576,6 +4576,29 @@ static bool vlv_digital_port_connected(struct drm_i915_private *dev_priv, return I915_READ(PORT_HOTPLUG_STAT) & bit; } +static bool bxt_digital_port_connected(struct drm_i915_private *dev_priv, + struct intel_digital_port *port) +{ + u32 bit; + + switch (port->port) { + case PORT_A: + bit = BXT_DE_PORT_HP_DDIA; + break; + case PORT_B: + bit = BXT_DE_PORT_HP_DDIB; + break; + case PORT_C: + bit = BXT_DE_PORT_HP_DDIC; + break; + default: + MISSING_CASE(port->port); + return false; + } + + return I915_READ(GEN8_DE_PORT_ISR) & bit; +} + /* * intel_digital_port_connected - is the specified port connected? * @dev_priv: i915 private structure @@ -4590,6 +4613,8 @@ static bool intel_digital_port_connected(struct drm_i915_private *dev_priv, return ibx_digital_port_connected(dev_priv, port); if (HAS_PCH_SPLIT(dev_priv)) return cpt_digital_port_connected(dev_priv, port); + else if (IS_BROXTON(dev_priv)) + return bxt_digital_port_connected(dev_priv, port); else if (IS_VALLEYVIEW(dev_priv)) return vlv_digital_port_connected(dev_priv, port); else From 0a0b457fbf765d0b98fa48495b9bb7901eb8b091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 21 Aug 2015 20:45:27 +0300 Subject: [PATCH 069/145] drm/i915: Fix some gcc warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simple one: drivers/gpu/drm/i915/i915_debugfs.c:2449:57: warning: Using plain integer as NULL pointer And something a bit more peculiar: drivers/gpu/drm/i915/i915_debugfs.c:4953:18: warning: Variable length array is used. drivers/gpu/drm/i915/i915_debugfs.c:4953:32: warning: Variable length array is used. We pass a 'const int' as the array size which results in the warning, dropping the const gets rid of the warning. Weird, but I think getting rid of the warnings is better than holding on to the const. Signed-off-by: Ville Syrjälä Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index e192f021fda3..624024c288aa 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2446,7 +2446,7 @@ static int i915_guc_info(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_guc guc; - struct i915_guc_client client = { .client_obj = 0 }; + struct i915_guc_client client = {}; struct intel_engine_cs *ring; enum intel_ring_id i; u64 total = 0; @@ -4948,7 +4948,7 @@ static void cherryview_sseu_device_status(struct drm_device *dev, struct sseu_dev_status *stat) { struct drm_i915_private *dev_priv = dev->dev_private; - const int ss_max = 2; + int ss_max = 2; int ss; u32 sig1[ss_max], sig2[ss_max]; From 53abb6794ac022e6c0b5f239fc26d99ea054ee86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 21 Aug 2015 20:45:28 +0300 Subject: [PATCH 070/145] drm/i915: Use ARRAY_SIZE() instead of hand rolling it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A couple of hand rolled ARRAY_SIZE()s caught my eye. Get rid of them. Signed-off-by: Ville Syrjälä Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_sdvo.c | 2 +- drivers/gpu/drm/i915/intel_tv.c | 2 +- drivers/gpu/drm/i915/intel_uncore.c | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index c98098e884cc..25d74d23e85e 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -63,7 +63,7 @@ static const char *tv_format_names[] = { "SECAM_60" }; -#define TV_FORMAT_NUM (sizeof(tv_format_names) / sizeof(*tv_format_names)) +#define TV_FORMAT_NUM ARRAY_SIZE(tv_format_names) struct intel_sdvo { struct intel_encoder base; diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 0568ae6ec9dd..cbe39dcc7bc0 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1291,7 +1291,7 @@ static void intel_tv_find_better_format(struct drm_connector *connector) return; - for (i = 0; i < sizeof(tv_modes) / sizeof(*tv_modes); i++) { + for (i = 0; i < ARRAY_SIZE(tv_modes); i++) { tv_mode = tv_modes + i; if ((intel_tv->type == DRM_MODE_CONNECTOR_Component) == diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index 9d3c2e420d2b..ad4e421226b2 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -52,8 +52,7 @@ static const char * const forcewake_domain_names[] = { const char * intel_uncore_forcewake_domain_to_str(const enum forcewake_domain_id id) { - BUILD_BUG_ON((sizeof(forcewake_domain_names)/sizeof(const char *)) != - FW_DOMAIN_ID_COUNT); + BUILD_BUG_ON(ARRAY_SIZE(forcewake_domain_names) != FW_DOMAIN_ID_COUNT); if (id >= 0 && id < FW_DOMAIN_ID_COUNT) return forcewake_domain_names[id]; From 4d9194dec37a9bf22354f6a5872e285e1bb8c1da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 21 Aug 2015 20:45:29 +0300 Subject: [PATCH 071/145] drm/i915: Make some string arrays const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of our char* arrays are markes as const already, but a few slipped through the cracks. Fix it. Signed-off-by: Ville Syrjälä Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_sdvo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 25d74d23e85e..ca3dd7c682bd 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -53,7 +53,7 @@ #define IS_DIGITAL(c) (c->output_flag & (SDVO_TMDS_MASK | SDVO_LVDS_MASK)) -static const char *tv_format_names[] = { +static const char * const tv_format_names[] = { "NTSC_M" , "NTSC_J" , "NTSC_443", "PAL_B" , "PAL_D" , "PAL_G" , "PAL_H" , "PAL_I" , "PAL_M" , @@ -452,7 +452,7 @@ static void intel_sdvo_debug_write(struct intel_sdvo *intel_sdvo, u8 cmd, DRM_DEBUG_KMS("%s: W: %02X %s\n", SDVO_NAME(intel_sdvo), cmd, buffer); } -static const char *cmd_status_names[] = { +static const char * const cmd_status_names[] = { "Power on", "Success", "Not supported", From 5a8fbb7d192b96de3d258164e5fc95b769d698c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Mon, 29 Jun 2015 15:25:53 +0300 Subject: [PATCH 072/145] drm/i915: Move DPLL ref/cri/VGA mode frobbing to the disp2d well enable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bunch of stuff needs the DPLL ref/cri clocks on both VLV and CHV, and having VGA mode enabled causes some problems for CHV. So let's just pull the code to configure those bits into the disp2d well enable hook. With the DPLL disable code also fixed to leave those bits alone we should now have a consistent DPLL state all the time even if the DPLL is disabled. This also neatly removes some duplicated code between the VLV and CHV codepaths. Signed-off-by: Ville Syrjälä Reviewed-by: Sivakumar Thulasimani Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_runtime_pm.c | 45 +++++++++++++------------ 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index 974c4cbf3fdb..0588baaed14a 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -853,6 +853,25 @@ static bool vlv_power_well_enabled(struct drm_i915_private *dev_priv, static void vlv_display_power_well_init(struct drm_i915_private *dev_priv) { + enum pipe pipe; + + /* + * Enable the CRI clock source so we can get at the + * display and the reference clock for VGA + * hotplug / manual detection. Supposedly DSI also + * needs the ref clock up and running. + * + * CHV DPLL B/C have some issues if VGA mode is enabled. + */ + for_each_pipe(dev_priv->dev, pipe) { + u32 val = I915_READ(DPLL(pipe)); + + val |= DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS; + if (pipe != PIPE_A) + val |= DPLL_INTEGRATED_CRI_CLK_VLV; + + I915_WRITE(DPLL(pipe), val); + } spin_lock_irq(&dev_priv->irq_lock); valleyview_enable_display_irqs(dev_priv); @@ -904,13 +923,7 @@ static void vlv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, { WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC); - /* - * Enable the CRI clock source so we can get at the - * display and the reference clock for VGA - * hotplug / manual detection. - */ - I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) | DPLL_VGA_MODE_DIS | - DPLL_REF_CLK_ENABLE_VLV | DPLL_INTEGRATED_CRI_CLK_VLV); + /* since ref/cri clock was enabled */ udelay(1); /* >10ns for cmnreset, >0ns for sidereset */ vlv_set_power_well(dev_priv, power_well, true); @@ -953,22 +966,12 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC && power_well->data != PUNIT_POWER_WELL_DPIO_CMN_D); - /* - * Enable the CRI clock source so we can get at the - * display and the reference clock for VGA - * hotplug / manual detection. - */ - if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) { + if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) phy = DPIO_PHY0; - I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) | DPLL_VGA_MODE_DIS | - DPLL_REF_CLK_ENABLE_VLV); - I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) | DPLL_VGA_MODE_DIS | - DPLL_REF_CLK_ENABLE_VLV | DPLL_INTEGRATED_CRI_CLK_VLV); - } else { + else phy = DPIO_PHY1; - I915_WRITE(DPLL(PIPE_C), I915_READ(DPLL(PIPE_C)) | DPLL_VGA_MODE_DIS | - DPLL_REF_CLK_ENABLE_VLV | DPLL_INTEGRATED_CRI_CLK_VLV); - } + + /* since ref/cri clock was enabled */ udelay(1); /* >10ns for cmnreset, >0ns for sidereset */ vlv_set_power_well(dev_priv, power_well, true); From e0fce78f041014846d77940d3a350a4cffe4ab2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 8 Jul 2015 23:45:54 +0300 Subject: [PATCH 073/145] drm/i915: Implement PHY lane power gating for CHV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Powergate the PHY lanes when they're not needed. For HDMI all four lanes are needed always, but for DP we can enable only the needed lanes. To power down the unused lanes we use some power down override bits in the DISPLAY_PHY_CONTROL register. Without the overrides it appears that the hardware always powers on all the lanes. When the port is disabled the power down override is not needed and the lanes will shut off on their own. That also means the override is critical to actually be able to access the DPIO registers before the port is actually enabled. Additionally the common lanes will power down when not needed. CL1 remains on as long as anything else is on, CL2 will shut down when all the lanes in the same channel will shut down. There is one exception for CL2 that will be dealt in a separate patch for clarity. With potentially some lanes powered down, the DP code now has to check the number of active lanes before accessing PCS/TX registers. All registers in powered down blocks will reads as 0xffffffff, and soe we would drown in warnings from vlv_dpio_read() if we allowed the code to access all those registers. Another important detail in the DP code is the "TX latency optimal" setting. Normally the second TX lane acts as some kind of reset master, with the other lanes as slaves. But when only a single lane is enabled, that single lane obviously has to be the master. A bit of extra care is needed to reconstruct the initial state of the DISPLAY_PHY_CONTROL register since it can't be read safely. So instead read the actual lane status from the DPLL/PHY_STATUS registers and use that to determine which lanes ought to be powergated initially. We also need to switch the PHY power modes to "deep PSR" to avoid a hard system hang when powering down the single channel PHY. Also sprinkle a few debug prints around so that we can monitor the DISPLAY_PHY_STATUS changes without having to read it and risk corrupting it. v2: Add locking to chv_powergate_phy_lanes() v3: Actually enable dynamic powerdown in the PHY and deal with the fallout Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 8 ++ drivers/gpu/drm/i915/intel_dp.c | 141 +++++++++++++++--------- drivers/gpu/drm/i915/intel_drv.h | 4 + drivers/gpu/drm/i915/intel_hdmi.c | 4 + drivers/gpu/drm/i915/intel_runtime_pm.c | 123 +++++++++++++++++++-- 5 files changed, 221 insertions(+), 59 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 211b73ab7780..6918ffab168e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1135,9 +1135,15 @@ enum skl_disp_power_wells { #define _CHV_CMN_DW19_CH0 0x814c #define _CHV_CMN_DW6_CH1 0x8098 +#define DPIO_DYNPWRDOWNEN_CH1 (1 << 28) /* CL2 DW6 only */ #define CHV_CMN_USEDCLKCHANNEL (1 << 13) + #define CHV_CMN_DW19(ch) _PIPE(ch, _CHV_CMN_DW19_CH0, _CHV_CMN_DW6_CH1) +#define CHV_CMN_DW28 0x8170 +#define DPIO_CL1POWERDOWNEN (1 << 23) +#define DPIO_DYNPWRDOWNEN_CH0 (1 << 22) + #define CHV_CMN_DW30 0x8178 #define DPIO_LRC_BYPASS (1 << 3) @@ -2192,10 +2198,12 @@ enum skl_disp_power_wells { #define DPIO_PHY_STATUS (VLV_DISPLAY_BASE + 0x6240) #define DPLL_PORTD_READY_MASK (0xf) #define DISPLAY_PHY_CONTROL (VLV_DISPLAY_BASE + 0x60100) +#define PHY_CH_POWER_DOWN_OVRD_EN(phy, ch) (1 << (2*(phy)+(ch)+27)) #define PHY_LDO_DELAY_0NS 0x0 #define PHY_LDO_DELAY_200NS 0x1 #define PHY_LDO_DELAY_600NS 0x2 #define PHY_LDO_SEQ_DELAY(delay, phy) ((delay) << (2*(phy)+23)) +#define PHY_CH_POWER_DOWN_OVRD(mask, phy, ch) ((mask) << (8*(phy)+4*(ch)+11)) #define PHY_CH_SU_PSR 0x1 #define PHY_CH_DEEP_PSR 0x7 #define PHY_CH_POWER_MODE(mode, phy, ch) ((mode) << (6*(phy)+3*(ch)+2)) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 1ff063e31584..c0eaf130ed9b 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -133,6 +133,11 @@ static void vlv_init_panel_power_sequencer(struct intel_dp *intel_dp); static void vlv_steal_power_sequencer(struct drm_device *dev, enum pipe pipe); +static unsigned int intel_dp_unused_lane_mask(int lane_count) +{ + return ~((1 << lane_count) - 1) & 0xf; +} + static int intel_dp_max_link_bw(struct intel_dp *intel_dp) { @@ -2432,17 +2437,21 @@ static void chv_post_disable_dp(struct intel_encoder *encoder) val |= CHV_PCS_REQ_SOFTRESET_EN; vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); - val |= CHV_PCS_REQ_SOFTRESET_EN; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); + val |= CHV_PCS_REQ_SOFTRESET_EN; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); + } val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch)); val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); - val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); + val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); + } mutex_unlock(&dev_priv->sb_lock); } @@ -2562,7 +2571,6 @@ static void intel_enable_dp(struct intel_encoder *encoder) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); uint32_t dp_reg = I915_READ(intel_dp->output_reg); - unsigned int lane_mask = 0x0; if (WARN_ON(dp_reg & DP_PORT_EN)) return; @@ -2580,9 +2588,15 @@ static void intel_enable_dp(struct intel_encoder *encoder) pps_unlock(intel_dp); - if (IS_VALLEYVIEW(dev)) + if (IS_VALLEYVIEW(dev)) { + unsigned int lane_mask = 0x0; + + if (IS_CHERRYVIEW(dev)) + lane_mask = intel_dp_unused_lane_mask(crtc->config->lane_count); + vlv_wait_port_ready(dev_priv, dp_to_dig_port(intel_dp), lane_mask); + } intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); intel_dp_start_link_train(intel_dp); @@ -2809,31 +2823,40 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) val &= ~DPIO_LANEDESKEW_STRAP_OVRD; vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW11(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch)); - val &= ~DPIO_LANEDESKEW_STRAP_OVRD; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch)); + val &= ~DPIO_LANEDESKEW_STRAP_OVRD; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); + } /* Deassert soft data lane reset*/ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch)); val |= CHV_PCS_REQ_SOFTRESET_EN; vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); - val |= CHV_PCS_REQ_SOFTRESET_EN; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); + val |= CHV_PCS_REQ_SOFTRESET_EN; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); + } val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch)); val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); - val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); + val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); + } /* Program Tx lane latency optimal setting*/ - for (i = 0; i < 4; i++) { + for (i = 0; i < intel_crtc->config->lane_count; i++) { /* Set the upar bit */ - data = (i == 1) ? 0x0 : 0x1; + if (intel_crtc->config->lane_count == 1) + data = 0x0; + else + data = (i == 1) ? 0x0 : 0x1; vlv_dpio_write(dev_priv, pipe, CHV_TX_DW14(ch, i), data << DPIO_UPAR_SHIFT); } @@ -2854,9 +2877,11 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) val |= DPIO_TX2_STAGGER_MASK(0x1f); vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW11(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch)); - val |= DPIO_TX2_STAGGER_MASK(0x1f); - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch)); + val |= DPIO_TX2_STAGGER_MASK(0x1f); + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); + } vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW12(ch), DPIO_LANESTAGGER_STRAP(stagger) | @@ -2865,12 +2890,14 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) DPIO_TX1_STAGGER_MULT(6) | DPIO_TX2_STAGGER_MULT(0)); - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW12(ch), - DPIO_LANESTAGGER_STRAP(stagger) | - DPIO_LANESTAGGER_STRAP_OVRD | - DPIO_TX1_STAGGER_MASK(0x1f) | - DPIO_TX1_STAGGER_MULT(7) | - DPIO_TX2_STAGGER_MULT(5)); + if (intel_crtc->config->lane_count > 2) { + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW12(ch), + DPIO_LANESTAGGER_STRAP(stagger) | + DPIO_LANESTAGGER_STRAP_OVRD | + DPIO_TX1_STAGGER_MASK(0x1f) | + DPIO_TX1_STAGGER_MULT(7) | + DPIO_TX2_STAGGER_MULT(5)); + } mutex_unlock(&dev_priv->sb_lock); @@ -2886,10 +2913,14 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder) to_intel_crtc(encoder->base.crtc); enum dpio_channel ch = vlv_dport_to_channel(dport); enum pipe pipe = intel_crtc->pipe; + unsigned int lane_mask = + intel_dp_unused_lane_mask(intel_crtc->config->lane_count); u32 val; intel_dp_prepare(encoder); + chv_phy_powergate_lanes(encoder, true, lane_mask); + mutex_lock(&dev_priv->sb_lock); /* program left/right clock distribution */ @@ -2920,13 +2951,15 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder) val |= CHV_PCS_USEDCLKCHANNEL; vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW8(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW8(ch)); - val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; - if (pipe != PIPE_B) - val &= ~CHV_PCS_USEDCLKCHANNEL; - else - val |= CHV_PCS_USEDCLKCHANNEL; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW8(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW8(ch)); + val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; + if (pipe != PIPE_B) + val &= ~CHV_PCS_USEDCLKCHANNEL; + else + val |= CHV_PCS_USEDCLKCHANNEL; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW8(ch), val); + } /* * This a a bit weird since generally CL @@ -2963,6 +2996,8 @@ static void chv_dp_post_pll_disable(struct intel_encoder *encoder) } mutex_unlock(&dev_priv->sb_lock); + + chv_phy_powergate_lanes(encoder, false, 0x0); } /* @@ -3298,24 +3333,28 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp) val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5; vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); - val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3); - val &= ~(DPIO_PCS_TX1DEEMP_MASK | DPIO_PCS_TX2DEEMP_MASK); - val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); + val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3); + val &= ~(DPIO_PCS_TX1DEEMP_MASK | DPIO_PCS_TX2DEEMP_MASK); + val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); + } val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW9(ch)); val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK); val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000; vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW9(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW9(ch)); - val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK); - val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW9(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW9(ch)); + val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK); + val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW9(ch), val); + } /* Program swing deemph */ - for (i = 0; i < 4; i++) { + for (i = 0; i < intel_crtc->config->lane_count; i++) { val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW4(ch, i)); val &= ~DPIO_SWING_DEEMPH9P5_MASK; val |= deemph_reg_value << DPIO_SWING_DEEMPH9P5_SHIFT; @@ -3323,7 +3362,7 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp) } /* Program swing margin */ - for (i = 0; i < 4; i++) { + for (i = 0; i < intel_crtc->config->lane_count; i++) { val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i)); val &= ~DPIO_SWING_MARGIN000_MASK; @@ -3346,7 +3385,7 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp) * For now, for this unique transition scale selection, set bit * 27 for ch0 and ch1. */ - for (i = 0; i < 4; i++) { + for (i = 0; i < intel_crtc->config->lane_count; i++) { val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i)); if (chv_need_uniq_trans_scale(train_set)) val |= DPIO_TX_UNIQ_TRANS_SCALE_EN; @@ -3360,9 +3399,11 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp) val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3; vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val); - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); - val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); + if (intel_crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); + val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); + } /* LRC Bypass */ val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index aa7971798639..df420d892ffe 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1361,6 +1361,10 @@ void intel_runtime_pm_put(struct drm_i915_private *dev_priv); void intel_display_set_init_power(struct drm_i915_private *dev, bool enable); +void chv_phy_powergate_lanes(struct intel_encoder *encoder, + bool override, unsigned int mask); + + /* intel_pm.c */ void intel_init_clock_gating(struct drm_device *dev); void intel_suspend_hw(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 269937a7919a..f1ecc2cbbd48 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1628,6 +1628,8 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) intel_hdmi_prepare(encoder); + chv_phy_powergate_lanes(encoder, true, 0x0); + mutex_lock(&dev_priv->sb_lock); /* program left/right clock distribution */ @@ -1701,6 +1703,8 @@ static void chv_hdmi_post_pll_disable(struct intel_encoder *encoder) } mutex_unlock(&dev_priv->sb_lock); + + chv_phy_powergate_lanes(encoder, false, 0x0); } static void vlv_hdmi_post_disable(struct intel_encoder *encoder) diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index 0588baaed14a..a1597712d27d 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -962,14 +962,19 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, struct i915_power_well *power_well) { enum dpio_phy phy; + enum pipe pipe; + uint32_t tmp; WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC && power_well->data != PUNIT_POWER_WELL_DPIO_CMN_D); - if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) + if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) { + pipe = PIPE_A; phy = DPIO_PHY0; - else + } else { + pipe = PIPE_C; phy = DPIO_PHY1; + } /* since ref/cri clock was enabled */ udelay(1); /* >10ns for cmnreset, >0ns for sidereset */ @@ -979,8 +984,26 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, if (wait_for(I915_READ(DISPLAY_PHY_STATUS) & PHY_POWERGOOD(phy), 1)) DRM_ERROR("Display PHY %d is not power up\n", phy); + mutex_lock(&dev_priv->sb_lock); + + /* Enable dynamic power down */ + tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW28); + tmp |= DPIO_DYNPWRDOWNEN_CH0 | DPIO_CL1POWERDOWNEN; + vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW28, tmp); + + if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) { + tmp = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW6_CH1); + tmp |= DPIO_DYNPWRDOWNEN_CH1; + vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW6_CH1, tmp); + } + + mutex_unlock(&dev_priv->sb_lock); + dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(phy); I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); + + DRM_DEBUG_KMS("Enabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", + phy, dev_priv->chv_phy_control); } static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, @@ -1004,6 +1027,35 @@ static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); vlv_set_power_well(dev_priv, power_well, false); + + DRM_DEBUG_KMS("Disabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", + phy, dev_priv->chv_phy_control); +} + +void chv_phy_powergate_lanes(struct intel_encoder *encoder, + bool override, unsigned int mask) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct i915_power_domains *power_domains = &dev_priv->power_domains; + enum dpio_phy phy = vlv_dport_to_phy(enc_to_dig_port(&encoder->base)); + enum dpio_channel ch = vlv_dport_to_channel(enc_to_dig_port(&encoder->base)); + + mutex_lock(&power_domains->lock); + + dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD(0xf, phy, ch); + dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD(mask, phy, ch); + + if (override) + dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); + else + dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); + + I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); + + DRM_DEBUG_KMS("Power gating DPIO PHY%d CH%d lanes 0x%x (PHY_CONTROL=0x%08x)\n", + phy, ch, mask, dev_priv->chv_phy_control); + + mutex_unlock(&power_domains->lock); } static bool chv_pipe_power_well_enabled(struct drm_i915_private *dev_priv, @@ -1630,19 +1682,72 @@ static void chv_phy_control_init(struct drm_i915_private *dev_priv) * DISPLAY_PHY_CONTROL can get corrupted if read. As a * workaround never ever read DISPLAY_PHY_CONTROL, and * instead maintain a shadow copy ourselves. Use the actual - * power well state to reconstruct the expected initial - * value. + * power well state and lane status to reconstruct the + * expected initial value. */ dev_priv->chv_phy_control = PHY_LDO_SEQ_DELAY(PHY_LDO_DELAY_600NS, DPIO_PHY0) | PHY_LDO_SEQ_DELAY(PHY_LDO_DELAY_600NS, DPIO_PHY1) | - PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY0, DPIO_CH0) | - PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY0, DPIO_CH1) | - PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY1, DPIO_CH0); - if (cmn_bc->ops->is_enabled(dev_priv, cmn_bc)) + PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY0, DPIO_CH0) | + PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY0, DPIO_CH1) | + PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY1, DPIO_CH0); + + /* + * If all lanes are disabled we leave the override disabled + * with all power down bits cleared to match the state we + * would use after disabling the port. Otherwise enable the + * override and set the lane powerdown bits accding to the + * current lane status. + */ + if (cmn_bc->ops->is_enabled(dev_priv, cmn_bc)) { + uint32_t status = I915_READ(DPLL(PIPE_A)); + unsigned int mask; + + mask = status & DPLL_PORTB_READY_MASK; + if (mask == 0xf) + mask = 0x0; + else + dev_priv->chv_phy_control |= + PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH0); + + dev_priv->chv_phy_control |= + PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY0, DPIO_CH0); + + mask = (status & DPLL_PORTC_READY_MASK) >> 4; + if (mask == 0xf) + mask = 0x0; + else + dev_priv->chv_phy_control |= + PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH1); + + dev_priv->chv_phy_control |= + PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY0, DPIO_CH1); + dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(DPIO_PHY0); - if (cmn_d->ops->is_enabled(dev_priv, cmn_d)) + } + + if (cmn_d->ops->is_enabled(dev_priv, cmn_d)) { + uint32_t status = I915_READ(DPIO_PHY_STATUS); + unsigned int mask; + + mask = status & DPLL_PORTD_READY_MASK; + + if (mask == 0xf) + mask = 0x0; + else + dev_priv->chv_phy_control |= + PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY1, DPIO_CH0); + + dev_priv->chv_phy_control |= + PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY1, DPIO_CH0); + dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(DPIO_PHY1); + } + + I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); + + DRM_DEBUG_KMS("Initial PHY_CONTROL=0x%08x\n", + dev_priv->chv_phy_control); } static void vlv_cmnlane_wa(struct drm_i915_private *dev_priv) From b0b3384612bd4ce608c5d95626149311bb43f121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 8 Jul 2015 23:45:55 +0300 Subject: [PATCH 074/145] drm/i915: Trick CL2 into life on CHV when using pipe B with port B MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Normmally the common lane in a PHY channel gets powered up when some of the data lanes get powered up. But when we're driving port B with pipe B we don't want to enabled any of the data lanes, and just want the DPLL in the common lane to be active. To make that happens we have to temporarily enable some data lanes after which we can access the DPLL registers in the common lane. Once the pipe is up and running we can drop the power override on the data lanes allowing them to shut down. From this point forward the common lane will in fact stay powered on until the data lanes in the other channel get powered down. Ville's extended explanation from the review thread: On Wed, Aug 19, 2015 at 07:47:41AM +0530, Deepak wrote: > One Q, why only for port B? Port C is also in same common lane right? Port B is in the first PHY channel which also houses CL1. CL1 always powers up whenever any lanes in either PHY channel are powered up. CL2 only powers up if lanes in the second channel (ie. the one with port C) powers up. So in this scenario (pipe B->port B) we want the DPLL from CL2, but ideally we only want to power up the lanes for port B. Powering up port B lanes will only power up CL1, but as we need CL2 instead we need to, temporarily, power up some lanes in port C as well. Crossing the streams the other way (pipe A->port C) is not a problem since CL1 powers up whenever anything else powers up. So powering up some port C lanes is enough on its own to make the CL1 DPLL operational, even though CL1 and the lanes live in separate channels. Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S [danvet: Amend commit message with extended explanation.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 23 ++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 3 +++ drivers/gpu/drm/i915/intel_hdmi.c | 23 ++++++++++++++++++++ drivers/gpu/drm/i915/intel_runtime_pm.c | 29 +++++++++++++++++++++++++ 4 files changed, 78 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index c0eaf130ed9b..b032eef8aeb8 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2902,6 +2902,12 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) mutex_unlock(&dev_priv->sb_lock); intel_enable_dp(encoder); + + /* Second common lane will stay alive on its own now */ + if (dport->release_cl2_override) { + chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, false); + dport->release_cl2_override = false; + } } static void chv_dp_pre_pll_enable(struct intel_encoder *encoder) @@ -2919,6 +2925,14 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder) intel_dp_prepare(encoder); + /* + * Must trick the second common lane into life. + * Otherwise we can't even access the PLL. + */ + if (ch == DPIO_CH0 && pipe == PIPE_B) + dport->release_cl2_override = + !chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, true); + chv_phy_powergate_lanes(encoder, true, lane_mask); mutex_lock(&dev_priv->sb_lock); @@ -2997,6 +3011,15 @@ static void chv_dp_post_pll_disable(struct intel_encoder *encoder) mutex_unlock(&dev_priv->sb_lock); + /* + * Leave the power down bit cleared for at least one + * lane so that chv_powergate_phy_ch() will power + * on something when the channel is otherwise unused. + * When the port is off and the override is removed + * the lanes power down anyway, so otherwise it doesn't + * really matter what the state of power down bits is + * after this. + */ chv_phy_powergate_lanes(encoder, false, 0x0); } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index df420d892ffe..72c1181bd7f5 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -779,6 +779,7 @@ struct intel_digital_port { struct intel_dp dp; struct intel_hdmi hdmi; enum irqreturn (*hpd_pulse)(struct intel_digital_port *, bool); + bool release_cl2_override; }; struct intel_dp_mst_encoder { @@ -1363,6 +1364,8 @@ void intel_display_set_init_power(struct drm_i915_private *dev, bool enable); 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, + enum dpio_channel ch, bool override); /* intel_pm.c */ diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index f1ecc2cbbd48..7c56053d4240 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1628,6 +1628,14 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) intel_hdmi_prepare(encoder); + /* + * Must trick the second common lane into life. + * Otherwise we can't even access the PLL. + */ + if (ch == DPIO_CH0 && pipe == PIPE_B) + dport->release_cl2_override = + !chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, true); + chv_phy_powergate_lanes(encoder, true, 0x0); mutex_lock(&dev_priv->sb_lock); @@ -1704,6 +1712,15 @@ static void chv_hdmi_post_pll_disable(struct intel_encoder *encoder) mutex_unlock(&dev_priv->sb_lock); + /* + * Leave the power down bit cleared for at least one + * lane so that chv_powergate_phy_ch() will power + * on something when the channel is otherwise unused. + * When the port is off and the override is removed + * the lanes power down anyway, so otherwise it doesn't + * really matter what the state of power down bits is + * after this. + */ chv_phy_powergate_lanes(encoder, false, 0x0); } @@ -1925,6 +1942,12 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder) g4x_enable_hdmi(encoder); vlv_wait_port_ready(dev_priv, dport, 0x0); + + /* Second common lane will stay alive on its own now */ + if (dport->release_cl2_override) { + chv_phy_powergate_ch(dev_priv, DPIO_PHY0, DPIO_CH1, false); + dport->release_cl2_override = false; + } } static void intel_hdmi_destroy(struct drm_connector *connector) diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index a1597712d27d..4a43885e571d 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -1032,6 +1032,35 @@ static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, phy, dev_priv->chv_phy_control); } +bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy, + enum dpio_channel ch, bool override) +{ + struct i915_power_domains *power_domains = &dev_priv->power_domains; + bool was_override; + + mutex_lock(&power_domains->lock); + + was_override = dev_priv->chv_phy_control & PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); + + if (override == was_override) + goto out; + + if (override) + dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); + else + dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); + + I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); + + DRM_DEBUG_KMS("Power gating DPIO PHY%d CH%d (DPIO_PHY_CONTROL=0x%08x)\n", + phy, ch, dev_priv->chv_phy_control); + +out: + mutex_unlock(&power_domains->lock); + + return was_override; +} + void chv_phy_powergate_lanes(struct intel_encoder *encoder, bool override, unsigned int mask) { From 0047eedc48869f8c7797dd10f0cf976ac34c1d33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 10 Jul 2015 10:56:24 +0300 Subject: [PATCH 075/145] drm/i915: Force common lane on for the PPS kick on CHV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With DPIO powergating active the DPLL can't be accessed unless something else is keeping the common lane in the channel on. That means the PPS kick procedure could fail to enable the PLL. Power up some data lanes to force the common lane to power up so that the PLL can be enabled temporarily. v2: Avoid gcc uninitilized variable warning Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index b032eef8aeb8..ea114690ffd4 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -341,7 +341,9 @@ vlv_power_sequencer_kick(struct intel_dp *intel_dp) struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; enum pipe pipe = intel_dp->pps_pipe; - bool pll_enabled; + bool pll_enabled, release_cl_override = false; + enum dpio_phy phy = DPIO_PHY(pipe); + enum dpio_channel ch = vlv_pipe_to_channel(pipe); uint32_t DP; if (WARN(I915_READ(intel_dp->output_reg) & DP_PORT_EN, @@ -371,9 +373,13 @@ vlv_power_sequencer_kick(struct intel_dp *intel_dp) * The DPLL for the pipe must be enabled for this to work. * So enable temporarily it if it's not already enabled. */ - if (!pll_enabled) + if (!pll_enabled) { + release_cl_override = IS_CHERRYVIEW(dev) && + !chv_phy_powergate_ch(dev_priv, phy, ch, true); + vlv_force_pll_on(dev, pipe, IS_CHERRYVIEW(dev) ? &chv_dpll[0].dpll : &vlv_dpll[0].dpll); + } /* * Similar magic as in intel_dp_enable_port(). @@ -390,8 +396,12 @@ vlv_power_sequencer_kick(struct intel_dp *intel_dp) I915_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN); POSTING_READ(intel_dp->output_reg); - if (!pll_enabled) + if (!pll_enabled) { vlv_force_pll_off(dev, pipe); + + if (release_cl_override) + chv_phy_powergate_ch(dev_priv, phy, ch, false); + } } static enum pipe From ee27921824e6ad0ca2d8e5abfa12cf4d853ded6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 8 Jul 2015 23:45:57 +0300 Subject: [PATCH 076/145] drm/i915: Enable DPIO SUS clock gating on CHV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CHV has supports some form of automagic clock gating for the DPIO SUS clock. We can simply enable the magic bits and the hardware should take care of the rest. Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 4 ++++ drivers/gpu/drm/i915/intel_runtime_pm.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 6918ffab168e..6ed7141c9505 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1143,6 +1143,10 @@ enum skl_disp_power_wells { #define CHV_CMN_DW28 0x8170 #define DPIO_CL1POWERDOWNEN (1 << 23) #define DPIO_DYNPWRDOWNEN_CH0 (1 << 22) +#define DPIO_SUS_CLK_CONFIG_ON (0 << 0) +#define DPIO_SUS_CLK_CONFIG_CLKREQ (1 << 0) +#define DPIO_SUS_CLK_CONFIG_GATE (2 << 0) +#define DPIO_SUS_CLK_CONFIG_GATE_CLKREQ (3 << 0) #define CHV_CMN_DW30 0x8178 #define DPIO_LRC_BYPASS (1 << 3) diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index 4a43885e571d..ef043b2c06b2 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -988,7 +988,8 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, /* Enable dynamic power down */ tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW28); - tmp |= DPIO_DYNPWRDOWNEN_CH0 | DPIO_CL1POWERDOWNEN; + tmp |= DPIO_DYNPWRDOWNEN_CH0 | DPIO_CL1POWERDOWNEN | + DPIO_SUS_CLK_CONFIG_GATE_CLKREQ; vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW28, tmp); if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) { From 3e28878635cc3bb3159445dc9cfbdc3d34eb8daf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 8 Jul 2015 23:45:58 +0300 Subject: [PATCH 077/145] drm/i915: Force CL2 off in CHV x1 PHY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can choose to leave the display PHY CL2 powerdown up to some hardware signals, or we can force it. The BXT code forces the nonexistent CL2 in the x1 PHY to power down. Follow suit on CHV. Maybe it can still save some extra power by disabling some extra logic in CL1, or something. Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_runtime_pm.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 6ed7141c9505..439bb27a63be 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1149,6 +1149,7 @@ enum skl_disp_power_wells { #define DPIO_SUS_CLK_CONFIG_GATE_CLKREQ (3 << 0) #define CHV_CMN_DW30 0x8178 +#define DPIO_CL2_LDOFUSE_PWRENB (1 << 6) #define DPIO_LRC_BYPASS (1 << 3) #define _TXLANE(ch, lane, offset) ((ch ? 0x2400 : 0) + \ diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index ef043b2c06b2..a0df156ee92e 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -996,6 +996,15 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, tmp = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW6_CH1); tmp |= DPIO_DYNPWRDOWNEN_CH1; vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW6_CH1, tmp); + } else { + /* + * Force the non-existing CL2 off. BXT does this + * too, so maybe it saves some power even though + * CL2 doesn't exist? + */ + tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30); + tmp |= DPIO_CL2_LDOFUSE_PWRENB; + vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW30, tmp); } mutex_unlock(&dev_priv->sb_lock); From 6c908bf43df417b10917c3a463d3bd413eb476fd Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Tue, 25 Aug 2015 19:03:41 -0300 Subject: [PATCH 078/145] drm/i915: gen 9 can check for unclaimed registers too Dear git bisect user, Even though this is the patch that introduced the WARN() you're bisecting, please notice that it's very likely that the problem you're facing was already present before this commit. In other words: this commit adds code to detect errors and give WARN()s about them, but the errors were already there. In order to continue your debug, please use the i915.mmio_debug option, check the backtraces and try to discover which read or write operation is causing the error message. Then check if this is happening because the register does not exist or because its power well is down when the operation is being done. On my SKL machine, if I use i915.mmio_debug=999, this patch triggers 42 WARNs just by booting. I didn't investigate them yet. Normal users are only going to get a single WARN due to the default i915.mmio_debug setting. Thank you for your comprehension, Paulo Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 3 +++ drivers/gpu/drm/i915/intel_uncore.c | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 1d887459e37f..6e03e115ea1f 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -362,6 +362,7 @@ static const struct intel_device_info intel_skylake_info = { .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING, .has_llc = 1, .has_ddi = 1, + .has_fpga_dbg = 1, .has_fbc = 1, GEN_DEFAULT_PIPEOFFSETS, IVB_CURSOR_OFFSETS, @@ -374,6 +375,7 @@ static const struct intel_device_info intel_skylake_gt3_info = { .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING, .has_llc = 1, .has_ddi = 1, + .has_fpga_dbg = 1, .has_fbc = 1, GEN_DEFAULT_PIPEOFFSETS, IVB_CURSOR_OFFSETS, @@ -386,6 +388,7 @@ static const struct intel_device_info intel_broxton_info = { .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING, .num_pipes = 3, .has_ddi = 1, + .has_fpga_dbg = 1, .has_fbc = 1, GEN_DEFAULT_PIPEOFFSETS, IVB_CURSOR_OFFSETS, diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index ad4e421226b2..dec20d60daf0 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -769,6 +769,7 @@ static u##x \ gen9_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \ enum forcewake_domains fw_engine; \ GEN6_READ_HEADER(x); \ + hsw_unclaimed_reg_debug(dev_priv, reg, true, true); \ if (!SKL_NEEDS_FORCE_WAKE((dev_priv), (reg))) \ fw_engine = 0; \ else if (FORCEWAKE_GEN9_RENDER_RANGE_OFFSET(reg)) \ @@ -782,6 +783,7 @@ gen9_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \ if (fw_engine) \ __force_wake_get(dev_priv, fw_engine); \ val = __raw_i915_read##x(dev_priv, reg); \ + hsw_unclaimed_reg_debug(dev_priv, reg, true, false); \ GEN6_READ_FOOTER; \ } @@ -982,6 +984,7 @@ gen9_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, \ bool trace) { \ enum forcewake_domains fw_engine; \ GEN6_WRITE_HEADER; \ + hsw_unclaimed_reg_debug(dev_priv, reg, false, true); \ if (!SKL_NEEDS_FORCE_WAKE((dev_priv), (reg)) || \ is_gen9_shadowed(dev_priv, reg)) \ fw_engine = 0; \ @@ -996,6 +999,8 @@ gen9_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, \ if (fw_engine) \ __force_wake_get(dev_priv, fw_engine); \ __raw_i915_write##x(dev_priv, reg, val); \ + hsw_unclaimed_reg_debug(dev_priv, reg, false, false); \ + hsw_unclaimed_reg_detect(dev_priv); \ GEN6_WRITE_FOOTER; \ } From c5b852f33e7844f4664794a3b3c738d07b09ce0a Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Wed, 26 Aug 2015 09:29:56 +0200 Subject: [PATCH 079/145] Partially revert "drm/i915: Use full atomic modeset." This partially reverts commit 74c090b1bdc57b1c9f1361908cca5a3d8a80fb08. The DRIVER_ATOMIC cap cannot yet be exported because i915 lacks async support. Signed-off-by: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 2 +- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_params.c | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 6e03e115ea1f..ce3bd0c713b9 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1726,7 +1726,7 @@ static int __init i915_init(void) * to the atomic ioctl and the atomic properties. Only plane operations on * a single CRTC will actually work. */ - if (driver.driver_features & DRIVER_MODESET) + if (i915.nuclear_pageflip) driver.driver_features |= DRIVER_ATOMIC; return drm_pci_init(&driver, &i915_pci_driver); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 705f208b49a0..7cd15556fc09 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2640,6 +2640,7 @@ struct i915_params { int use_mmio_flip; int mmio_debug; bool verbose_state_checks; + bool nuclear_pageflip; int edp_vswing; }; extern struct i915_params i915 __read_mostly; diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 5ae4b0aba564..05053e2e9ff0 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -51,6 +51,7 @@ struct i915_params i915 __read_mostly = { .use_mmio_flip = 0, .mmio_debug = 0, .verbose_state_checks = 1, + .nuclear_pageflip = 0, .edp_vswing = 0, .enable_guc_submission = false, .guc_log_level = -1, @@ -177,6 +178,10 @@ module_param_named(verbose_state_checks, i915.verbose_state_checks, bool, 0600); MODULE_PARM_DESC(verbose_state_checks, "Enable verbose logs (ie. WARN_ON()) in case of unexpected hw state conditions."); +module_param_named_unsafe(nuclear_pageflip, i915.nuclear_pageflip, bool, 0600); +MODULE_PARM_DESC(nuclear_pageflip, + "Force atomic modeset functionality; asynchronous mode is not yet supported. (default: false)."); + /* WA to get away with the default setting in VBT for early platforms.Will be removed */ module_param_named_unsafe(edp_vswing, i915.edp_vswing, int, 0400); MODULE_PARM_DESC(edp_vswing, From 01302d4d17ed524e7d855f5fbbac5822f14f9c5e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 28 Aug 2015 18:05:26 +0200 Subject: [PATCH 080/145] drm/i915: Update DRIVER_DATE to 20150828 Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7cd15556fc09..e1bbe460f8cc 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -57,7 +57,7 @@ #define DRIVER_NAME "i915" #define DRIVER_DESC "Intel Graphics" -#define DRIVER_DATE "20150731" +#define DRIVER_DATE "20150828" #undef WARN_ON /* Many gcc seem to no see through this and fall over :( */ From 7cc53cf01e61bd1a774d2ba492bbe3e93e58ca79 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 26 Aug 2015 14:33:31 +0300 Subject: [PATCH 081/145] drm/dp: add drm_dp_tps3_supported helper Cc: Thierry Reding Signed-off-by: Jani Nikula Reviewed-by: Alex Deucher Signed-off-by: Daniel Vetter --- include/drm/drm_dp_helper.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 94898f6ea02a..6aa59b9c7335 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -633,6 +633,13 @@ drm_dp_enhanced_frame_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) (dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP); } +static inline bool +drm_dp_tps3_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_DPCD_REV] >= 0x12 && + dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED; +} + /* * DisplayPort AUX channel */ From caa860d919865ade32e029631bd0d531eead9212 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 27 Aug 2015 13:25:37 +0300 Subject: [PATCH 082/145] drm/i915/dp: use the drm dp helper for determining sink tps3 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No functional changes. Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä [danvet: s/intel_dp_tps/drm_dp_tps/.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index ea114690ffd4..b79443958258 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4023,8 +4023,7 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) } /* Training Pattern 3 support, both source and sink */ - if (intel_dp->dpcd[DP_DPCD_REV] >= 0x12 && - intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED && + if (drm_dp_tps3_supported(intel_dp->dpcd) && (IS_HASWELL(dev_priv) || INTEL_INFO(dev_priv)->gen >= 8)) { intel_dp->use_tps3 = true; DRM_DEBUG_KMS("Displayport TPS3 supported\n"); From 2bbe6bbb0dc94fd4ce287bdac9e1bd184e23057b Mon Sep 17 00:00:00 2001 From: Francisco Jerez Date: Mon, 15 Jun 2015 14:03:29 +0300 Subject: [PATCH 083/145] drm/i915: Bump command parser version number. This was forgotten in commit d351f6d94893f3ba98b1b20c5ef44c35fc1da124 Author: Francisco Jerez Date: Fri May 29 16:44:15 2015 +0300 drm/i915: Add SCRATCH1 and ROW_CHICKEN3 to the register whitelist. Signed-off-by: Francisco Jerez Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_cmd_parser.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c index 3977bf31676f..ad7d7ab76d3f 100644 --- a/drivers/gpu/drm/i915/i915_cmd_parser.c +++ b/drivers/gpu/drm/i915/i915_cmd_parser.c @@ -1213,6 +1213,7 @@ int i915_cmd_parser_get_version(void) * 2. Allow access to the MI_PREDICATE_SRC0 and * MI_PREDICATE_SRC1 registers. * 3. Allow access to the GPGPU_THREADS_DISPATCHED register. + * 4. L3 atomic chicken bits of HSW_SCRATCH1 and HSW_ROW_CHICKEN3. */ - return 3; + return 4; } From a8f327fb8464875e5b764008a1c50dbc0b384c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 9 Jul 2015 20:14:11 +0300 Subject: [PATCH 084/145] drm/i915: Clean up CHV lane soft reset programming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we release the lane soft reset before lane stagger settings have been programmed. I believe that means we don't actually do lane staggering. So move the soft reset deassert to happen after lane staggering has been programmed. The one confusing thing in this is that when we remove the power down override from the lanes, they power up with defaul register values, which do not have the soft reset overrides enabled. And according to some docs by default the data lane resets are tied to cmnreset. So that would mean that lanes would come out of reset without staggering as soon as the power down overrides are removed. But since we can't access either the lane stagger register nor the soft reset override registers until the lanes are powered on, we can't really do anything about it. So let's just set the soft reset overrides as soon as the lane is powered on and hope for the best. v2: Fix typos in commit message (Daniel) Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 99 ++++++++++++++++--------------- drivers/gpu/drm/i915/intel_hdmi.c | 91 ++++++++++++++++------------ 2 files changed, 104 insertions(+), 86 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index b79443958258..3a9c43d2c2c4 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2426,42 +2426,62 @@ static void vlv_post_disable_dp(struct intel_encoder *encoder) intel_dp_link_down(intel_dp); } +static void chv_data_lane_soft_reset(struct intel_encoder *encoder, + bool reset) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + enum dpio_channel ch = vlv_dport_to_channel(enc_to_dig_port(&encoder->base)); + struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + enum pipe pipe = crtc->pipe; + uint32_t val; + + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch)); + if (reset) + val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); + else + val |= DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET; + vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val); + + if (crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); + if (reset) + val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); + else + val |= DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); + } + + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch)); + val |= CHV_PCS_REQ_SOFTRESET_EN; + if (reset) + val &= ~DPIO_PCS_CLK_SOFT_RESET; + else + val |= DPIO_PCS_CLK_SOFT_RESET; + vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val); + + if (crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); + val |= CHV_PCS_REQ_SOFTRESET_EN; + if (reset) + val &= ~DPIO_PCS_CLK_SOFT_RESET; + else + val |= DPIO_PCS_CLK_SOFT_RESET; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); + } +} + static void chv_post_disable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); - struct intel_digital_port *dport = dp_to_dig_port(intel_dp); struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = - to_intel_crtc(encoder->base.crtc); - enum dpio_channel ch = vlv_dport_to_channel(dport); - enum pipe pipe = intel_crtc->pipe; - u32 val; intel_dp_link_down(intel_dp); mutex_lock(&dev_priv->sb_lock); - /* Propagate soft reset to data lane reset */ - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch)); - val |= CHV_PCS_REQ_SOFTRESET_EN; - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val); - - if (intel_crtc->config->lane_count > 2) { - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); - val |= CHV_PCS_REQ_SOFTRESET_EN; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); - } - - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch)); - val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val); - - if (intel_crtc->config->lane_count > 2) { - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); - val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); - } + /* Assert data lane reset */ + chv_data_lane_soft_reset(encoder, true); mutex_unlock(&dev_priv->sb_lock); } @@ -2839,27 +2859,6 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); } - /* Deassert soft data lane reset*/ - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch)); - val |= CHV_PCS_REQ_SOFTRESET_EN; - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val); - - if (intel_crtc->config->lane_count > 2) { - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); - val |= CHV_PCS_REQ_SOFTRESET_EN; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); - } - - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch)); - val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val); - - if (intel_crtc->config->lane_count > 2) { - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); - val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); - } - /* Program Tx lane latency optimal setting*/ for (i = 0; i < intel_crtc->config->lane_count; i++) { /* Set the upar bit */ @@ -2909,6 +2908,9 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) DPIO_TX2_STAGGER_MULT(5)); } + /* Deassert data lane reset */ + chv_data_lane_soft_reset(encoder, false); + mutex_unlock(&dev_priv->sb_lock); intel_enable_dp(encoder); @@ -2947,6 +2949,9 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder) mutex_lock(&dev_priv->sb_lock); + /* Assert data lane reset */ + chv_data_lane_soft_reset(encoder, true); + /* program left/right clock distribution */ if (pipe != PIPE_B) { val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW5_CH0); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 7c56053d4240..4da737ccf69a 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1615,6 +1615,50 @@ static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder) mutex_unlock(&dev_priv->sb_lock); } +static void chv_data_lane_soft_reset(struct intel_encoder *encoder, + bool reset) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + enum dpio_channel ch = vlv_dport_to_channel(enc_to_dig_port(&encoder->base)); + struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + enum pipe pipe = crtc->pipe; + uint32_t val; + + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch)); + if (reset) + val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); + else + val |= DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET; + vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val); + + if (crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); + if (reset) + val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); + else + val |= DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); + } + + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch)); + val |= CHV_PCS_REQ_SOFTRESET_EN; + if (reset) + val &= ~DPIO_PCS_CLK_SOFT_RESET; + else + val |= DPIO_PCS_CLK_SOFT_RESET; + vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val); + + if (crtc->config->lane_count > 2) { + val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); + val |= CHV_PCS_REQ_SOFTRESET_EN; + if (reset) + val &= ~DPIO_PCS_CLK_SOFT_RESET; + else + val |= DPIO_PCS_CLK_SOFT_RESET; + vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); + } +} + static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) { struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); @@ -1640,6 +1684,9 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) mutex_lock(&dev_priv->sb_lock); + /* Assert data lane reset */ + chv_data_lane_soft_reset(encoder, true); + /* program left/right clock distribution */ if (pipe != PIPE_B) { val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW5_CH0); @@ -1742,33 +1789,13 @@ static void vlv_hdmi_post_disable(struct intel_encoder *encoder) static void chv_hdmi_post_disable(struct intel_encoder *encoder) { - struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = - to_intel_crtc(encoder->base.crtc); - enum dpio_channel ch = vlv_dport_to_channel(dport); - enum pipe pipe = intel_crtc->pipe; - u32 val; mutex_lock(&dev_priv->sb_lock); - /* Propagate soft reset to data lane reset */ - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch)); - val |= CHV_PCS_REQ_SOFTRESET_EN; - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val); - - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); - val |= CHV_PCS_REQ_SOFTRESET_EN; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); - - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch)); - val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val); - - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); - val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); + /* Assert data lane reset */ + chv_data_lane_soft_reset(encoder, true); mutex_unlock(&dev_priv->sb_lock); } @@ -1799,23 +1826,6 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder) val &= ~DPIO_LANEDESKEW_STRAP_OVRD; vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); - /* Deassert soft data lane reset*/ - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch)); - val |= CHV_PCS_REQ_SOFTRESET_EN; - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val); - - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); - val |= CHV_PCS_REQ_SOFTRESET_EN; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); - - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch)); - val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val); - - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); - val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); - /* Program Tx latency optimal setting */ for (i = 0; i < 4; i++) { /* Set the upar bit */ @@ -1858,6 +1868,9 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder) DPIO_TX1_STAGGER_MULT(7) | DPIO_TX2_STAGGER_MULT(5)); + /* Deassert data lane reset */ + chv_data_lane_soft_reset(encoder, false); + /* Clear calc init */ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch)); val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3); From 6669e39f95b5530ca8cb9137703ceb5e83e5d648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 8 Jul 2015 23:46:00 +0300 Subject: [PATCH 085/145] drm/i915: Add some CHV DPIO lane power state asserts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add some checks that the state of the DPIO lanes is more or less what we expect based on the overrides. The hardware only provides two bits per channel indicating whether all or some of the lanes are powered down, so we can't do an exact check. Additionally, CL2 powering down before we can check it adds another twist. To work around this we simply check for the 0 value of the CL2 register (which is what we get when it's powered down) and adjust our expectations. Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 8 ++++ drivers/gpu/drm/i915/intel_runtime_pm.c | 54 +++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 439bb27a63be..8a180230e206 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1099,6 +1099,12 @@ enum skl_disp_power_wells { #define DPIO_CHV_INT_LOCK_THRESHOLD_SEL_COARSE 1 /* 1: coarse & 0 : fine */ #define CHV_PLL_DW9(ch) _PIPE(ch, _CHV_PLL_DW9_CH0, _CHV_PLL_DW9_CH1) +#define _CHV_CMN_DW0_CH0 0x8100 +#define DPIO_ALLDL_POWERDOWN_SHIFT_CH0 19 +#define DPIO_ANYDL_POWERDOWN_SHIFT_CH0 18 +#define DPIO_ALLDL_POWERDOWN (1 << 1) +#define DPIO_ANYDL_POWERDOWN (1 << 0) + #define _CHV_CMN_DW5_CH0 0x8114 #define CHV_BUFRIGHTENA1_DISABLE (0 << 20) #define CHV_BUFRIGHTENA1_NORMAL (1 << 20) @@ -1135,6 +1141,8 @@ enum skl_disp_power_wells { #define _CHV_CMN_DW19_CH0 0x814c #define _CHV_CMN_DW6_CH1 0x8098 +#define DPIO_ALLDL_POWERDOWN_SHIFT_CH1 30 /* CL2 DW6 only */ +#define DPIO_ANYDL_POWERDOWN_SHIFT_CH1 29 /* CL2 DW6 only */ #define DPIO_DYNPWRDOWNEN_CH1 (1 << 28) /* CL2 DW6 only */ #define CHV_CMN_USEDCLKCHANNEL (1 << 13) diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index a0df156ee92e..7991eff01662 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -1042,6 +1042,58 @@ static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, phy, dev_priv->chv_phy_control); } +static void assert_chv_phy_powergate(struct drm_i915_private *dev_priv, enum dpio_phy phy, + enum dpio_channel ch, bool override, unsigned int mask) +{ + enum pipe pipe = phy == DPIO_PHY0 ? PIPE_A : PIPE_C; + u32 reg, val, expected, actual; + + if (ch == DPIO_CH0) + reg = _CHV_CMN_DW0_CH0; + else + reg = _CHV_CMN_DW6_CH1; + + mutex_lock(&dev_priv->sb_lock); + val = vlv_dpio_read(dev_priv, pipe, reg); + mutex_unlock(&dev_priv->sb_lock); + + /* + * This assumes !override is only used when the port is disabled. + * All lanes should power down even without the override when + * the port is disabled. + */ + if (!override || mask == 0xf) { + expected = DPIO_ALLDL_POWERDOWN | DPIO_ANYDL_POWERDOWN; + /* + * If CH1 common lane is not active anymore + * (eg. for pipe B DPLL) the entire channel will + * shut down, which causes the common lane registers + * to read as 0. That means we can't actually check + * the lane power down status bits, but as the entire + * register reads as 0 it's a good indication that the + * channel is indeed entirely powered down. + */ + if (ch == DPIO_CH1 && val == 0) + expected = 0; + } else if (mask != 0x0) { + expected = DPIO_ANYDL_POWERDOWN; + } else { + expected = 0; + } + + if (ch == DPIO_CH0) + actual = val >> DPIO_ANYDL_POWERDOWN_SHIFT_CH0; + else + actual = val >> DPIO_ANYDL_POWERDOWN_SHIFT_CH1; + actual &= DPIO_ALLDL_POWERDOWN | DPIO_ANYDL_POWERDOWN; + + WARN(actual != expected, + "Unexpected DPIO lane power down: all %d, any %d. Expected: all %d, any %d. (0x%x = 0x%08x)\n", + !!(actual & DPIO_ALLDL_POWERDOWN), !!(actual & DPIO_ANYDL_POWERDOWN), + !!(expected & DPIO_ALLDL_POWERDOWN), !!(expected & DPIO_ANYDL_POWERDOWN), + reg, val); +} + bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy, enum dpio_channel ch, bool override) { @@ -1094,6 +1146,8 @@ void chv_phy_powergate_lanes(struct intel_encoder *encoder, DRM_DEBUG_KMS("Power gating DPIO PHY%d CH%d lanes 0x%x (PHY_CONTROL=0x%08x)\n", phy, ch, mask, dev_priv->chv_phy_control); + assert_chv_phy_powergate(dev_priv, phy, ch, override, mask); + mutex_unlock(&power_domains->lock); } From 30142273a3e83936fd7b45aa5339311a9295ca51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 8 Jul 2015 23:46:01 +0300 Subject: [PATCH 086/145] drm/i915: Add CHV PHY LDO power sanity checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At various points when changing the DPIO lane/phy power states, construct an expected value of the DISPLAY_PHY_STATUS register and compare it with the real thing. To construct the expected value we look at our shadow PHY_CONTROL register value (which should match what we've just written to the hardware), and we also need to look at the actual state of the cmn power wells as a disabled power well causes the relevant LDO status to be reported as 'on' in DISPLAY_PHY_STATUS. When initially powering up the PHY it performs various internal calibrations for which it fully powers up. That means that if we check for the expetected power state immediately upon releasing cmnreset we would get the occasional false positive. But we can of course poll until the expected value appears. It shouldn't be too long so this shouldn't make modesets substantially longer. One extra complication is introduced when we cross the streams, ie. drive port B with pipe B. In this case we trick CL2 (where the DPLL lives) into life by temporaily powering up the lanes in the second channel, and once the pipe is up and runnign we release the lane power override. At that point the power state of CL2 has somehow gotten entangled with the power state of the first channel. That means that constructing the expected DISPLAY_PHY_STATUS value is a bit tricky since based on the lane power states in the second channel, CL2 should also be powered down. But we can use the DPLL enable bit to determine when CL2 should be alive even if the lanes are powered down. However the power state of CL2 isn't actually tied in with the DPLL state, but to the state of the lanes in first channel, so we have to avoid checking the expected state between shutting down the DPLL and powering down the lanes in the first channel. So no calling assert_chv_phy_status() before the DISPLAY_PHY_CONTROL write in chv_phy_powergate_lanes(), but after the write is a safe time to check. Signed-off-by: Ville Syrjälä Reviewed-by: Deepak S Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 2 + drivers/gpu/drm/i915/intel_runtime_pm.c | 126 ++++++++++++++++++++---- 2 files changed, 111 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 8a180230e206..c82db2aed55c 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2223,6 +2223,8 @@ enum skl_disp_power_wells { #define PHY_COM_LANE_RESET_DEASSERT(phy) (1 << (phy)) #define DISPLAY_PHY_STATUS (VLV_DISPLAY_BASE + 0x60104) #define PHY_POWERGOOD(phy) (((phy) == DPIO_PHY0) ? (1<<31) : (1<<30)) +#define PHY_STATUS_CMN_LDO(phy, ch) (1 << (6-(6*(phy)+3*(ch)))) +#define PHY_STATUS_SPLINE_LDO(phy, ch, spline) (1 << (8-(6*(phy)+3*(ch)+(spline)))) /* * The i830 generation, in LVDS mode, defines P1 as the bit number set within diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index 7991eff01662..b1bd25e1e853 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -958,6 +958,107 @@ static void vlv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, vlv_set_power_well(dev_priv, power_well, false); } +#define POWER_DOMAIN_MASK (BIT(POWER_DOMAIN_NUM) - 1) + +static struct i915_power_well *lookup_power_well(struct drm_i915_private *dev_priv, + int power_well_id) +{ + struct i915_power_domains *power_domains = &dev_priv->power_domains; + struct i915_power_well *power_well; + int i; + + for_each_power_well(i, power_well, POWER_DOMAIN_MASK, power_domains) { + if (power_well->data == power_well_id) + return power_well; + } + + return NULL; +} + +#define BITS_SET(val, bits) (((val) & (bits)) == (bits)) + +static void assert_chv_phy_status(struct drm_i915_private *dev_priv) +{ + struct i915_power_well *cmn_bc = + lookup_power_well(dev_priv, PUNIT_POWER_WELL_DPIO_CMN_BC); + struct i915_power_well *cmn_d = + lookup_power_well(dev_priv, PUNIT_POWER_WELL_DPIO_CMN_D); + u32 phy_control = dev_priv->chv_phy_control; + u32 phy_status = 0; + u32 tmp; + + if (cmn_bc->ops->is_enabled(dev_priv, cmn_bc)) { + phy_status |= PHY_POWERGOOD(DPIO_PHY0); + + /* this assumes override is only used to enable lanes */ + if ((phy_control & PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH0)) == 0) + phy_control |= PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH0); + + if ((phy_control & PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH1)) == 0) + phy_control |= PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH1); + + /* CL1 is on whenever anything is on in either channel */ + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH0) | + PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH1))) + phy_status |= PHY_STATUS_CMN_LDO(DPIO_PHY0, DPIO_CH0); + + /* + * The DPLLB check accounts for the pipe B + port A usage + * with CL2 powered up but all the lanes in the second channel + * powered down. + */ + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH1)) && + (I915_READ(DPLL(PIPE_B)) & DPLL_VCO_ENABLE) == 0) + phy_status |= PHY_STATUS_CMN_LDO(DPIO_PHY0, DPIO_CH1); + + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0x3, DPIO_PHY0, DPIO_CH0))) + phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH0, 0); + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0xc, DPIO_PHY0, DPIO_CH0))) + phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH0, 1); + + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0x3, DPIO_PHY0, DPIO_CH1))) + phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH1, 0); + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0xc, DPIO_PHY0, DPIO_CH1))) + phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH1, 1); + } + + if (cmn_d->ops->is_enabled(dev_priv, cmn_d)) { + phy_status |= PHY_POWERGOOD(DPIO_PHY1); + + /* this assumes override is only used to enable lanes */ + if ((phy_control & PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY1, DPIO_CH0)) == 0) + phy_control |= PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY1, DPIO_CH0); + + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY1, DPIO_CH0))) + phy_status |= PHY_STATUS_CMN_LDO(DPIO_PHY1, DPIO_CH0); + + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0x3, DPIO_PHY1, DPIO_CH0))) + phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY1, DPIO_CH0, 0); + if (BITS_SET(phy_control, + PHY_CH_POWER_DOWN_OVRD(0xc, DPIO_PHY1, DPIO_CH0))) + phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY1, DPIO_CH0, 1); + } + + /* + * The PHY may be busy with some initial calibration and whatnot, + * so the power state can take a while to actually change. + */ + if (wait_for((tmp = I915_READ(DISPLAY_PHY_STATUS)) == phy_status, 10)) + WARN(phy_status != tmp, + "Unexpected PHY_STATUS 0x%08x, expected 0x%08x (PHY_CONTROL=0x%08x)\n", + tmp, phy_status, dev_priv->chv_phy_control); +} + +#undef BITS_SET + static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, struct i915_power_well *power_well) { @@ -1014,6 +1115,8 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, DRM_DEBUG_KMS("Enabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", phy, dev_priv->chv_phy_control); + + assert_chv_phy_status(dev_priv); } static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, @@ -1040,6 +1143,8 @@ static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, DRM_DEBUG_KMS("Disabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", phy, dev_priv->chv_phy_control); + + assert_chv_phy_status(dev_priv); } static void assert_chv_phy_powergate(struct drm_i915_private *dev_priv, enum dpio_phy phy, @@ -1117,6 +1222,8 @@ bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy, DRM_DEBUG_KMS("Power gating DPIO PHY%d CH%d (DPIO_PHY_CONTROL=0x%08x)\n", phy, ch, dev_priv->chv_phy_control); + assert_chv_phy_status(dev_priv); + out: mutex_unlock(&power_domains->lock); @@ -1146,6 +1253,8 @@ void chv_phy_powergate_lanes(struct intel_encoder *encoder, DRM_DEBUG_KMS("Power gating DPIO PHY%d CH%d lanes 0x%x (PHY_CONTROL=0x%08x)\n", phy, ch, mask, dev_priv->chv_phy_control); + assert_chv_phy_status(dev_priv); + assert_chv_phy_powergate(dev_priv, phy, ch, override, mask); mutex_unlock(&power_domains->lock); @@ -1312,8 +1421,6 @@ void intel_display_power_put(struct drm_i915_private *dev_priv, intel_runtime_pm_put(dev_priv); } -#define POWER_DOMAIN_MASK (BIT(POWER_DOMAIN_NUM) - 1) - #define HSW_ALWAYS_ON_POWER_DOMAINS ( \ BIT(POWER_DOMAIN_PIPE_A) | \ BIT(POWER_DOMAIN_TRANSCODER_EDP) | \ @@ -1575,21 +1682,6 @@ static struct i915_power_well chv_power_wells[] = { }, }; -static struct i915_power_well *lookup_power_well(struct drm_i915_private *dev_priv, - int power_well_id) -{ - struct i915_power_domains *power_domains = &dev_priv->power_domains; - struct i915_power_well *power_well; - int i; - - for_each_power_well(i, power_well, POWER_DOMAIN_MASK, power_domains) { - if (power_well->data == power_well_id) - return power_well; - } - - return NULL; -} - bool intel_display_power_well_is_enabled(struct drm_i915_private *dev_priv, int power_well_id) { From 0f64614dde172e4f4b2734a55091a4d307863cbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2015 19:39:18 +0300 Subject: [PATCH 087/145] drm/i915: Fix clock readout when pipes are enabled w/o ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The BIOS sometimes likes to enable pipes w/o any ports, at least on older machines. Currently we fail to assign anything sensible to crtc->hwmode.crtc_clock which leads to complaints from the vblank code. Deal with active pipes w/o ports and assign something sensible to crtc_clock in i9xx_get_pipe_config(). The encoder .get_config() will override this if the port is enabled. Gets rid of rest of these on my gen4: [drm:drm_calc_timestamping_constants [drm]] *ERROR* crtc 24: Can't calculate constants, dotclock = 0! [drm:i915_get_vblank_timestamp] crtc 1 is disabled v2: Fill out crtc_clock already in i9xx_get_pipe_config() (Maarten) Cc: Maarten Lankhorst Signed-off-by: Ville Syrjälä Reviewed-by: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f32206d95986..ba0c85e69f6f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8059,6 +8059,14 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc, else i9xx_crtc_clock_get(crtc, pipe_config); + /* + * Normally the dotclock is filled in by the encoder .get_config() + * but in case the pipe is enabled w/o any ports we need a sane + * default. + */ + pipe_config->base.adjusted_mode.crtc_clock = + pipe_config->port_clock / pipe_config->pixel_multiplier; + return true; } From 02e93c3537d5b496bbf4d6a97c0d02becc501b5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 26 Aug 2015 19:39:19 +0300 Subject: [PATCH 088/145] drm/i915: Factor out intel_crtc_has_encoders() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the code mode readable by pulling the "does this crtc have any encoders?" deduction into a separate function. Cc: Maarten Lankhorst Signed-off-by: Ville Syrjälä Reviewed-by: Maarten Lankhorst Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ba0c85e69f6f..439e66e7c7cb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -14807,13 +14807,22 @@ intel_check_plane_mapping(struct intel_crtc *crtc) return true; } +static bool intel_crtc_has_encoders(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct intel_encoder *encoder; + + for_each_encoder_on_crtc(dev, &crtc->base, encoder) + return true; + + return false; +} + static void intel_sanitize_crtc(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_encoder *encoder; u32 reg; - bool enable; /* Clear any frame start delays used for debugging left by the BIOS */ reg = PIPECONF(crtc->config->cpu_transcoder); @@ -14857,16 +14866,11 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) /* Adjust the state of the output pipe according to whether we * have active connectors/encoders. */ - enable = false; - for_each_encoder_on_crtc(dev, &crtc->base, encoder) { - enable = true; - break; - } - - if (!enable) + if (!intel_crtc_has_encoders(crtc)) intel_crtc_disable_noatomic(&crtc->base); if (crtc->active != crtc->base.state->active) { + struct intel_encoder *encoder; /* This can happen either due to bugs in the get_hw_state * functions or because of calls to intel_crtc_disable_noatomic, From 1751fcf9f92ebfd230260230956535b19b4ec675 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 27 Aug 2015 15:15:15 +0200 Subject: [PATCH 089/145] drm/i915: Fix module initialisation, v2. The driver doesn't support UMS any more, so set DRIVER_MODESET by default, remove the legacy s/r callbacks, and rename the s/r functions to make it more clear they're only in use by switcheroo now. Also remove an obsolete comment about atomic. Normal updates are supported only async updates aren't yet. v2: Don't unconditionally set DRIVER_ATOMIC, we're not yet there. Signed-off-by: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 4 ++-- drivers/gpu/drm/i915/i915_drv.c | 17 +++-------------- drivers/gpu/drm/i915/i915_drv.h | 4 ++-- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 097d4ba0421c..f0eaa6f8826b 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -364,12 +364,12 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_ dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; /* i915 resume handler doesn't set to D0 */ pci_set_power_state(dev->pdev, PCI_D0); - i915_resume_legacy(dev); + i915_resume_switcheroo(dev); dev->switch_power_state = DRM_SWITCH_POWER_ON; } else { pr_err("switched off\n"); dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; - i915_suspend_legacy(dev, pmm); + i915_suspend_switcheroo(dev, pmm); dev->switch_power_state = DRM_SWITCH_POWER_OFF; } } diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index ce3bd0c713b9..4646fe1a0499 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -679,7 +679,7 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation) return 0; } -int i915_suspend_legacy(struct drm_device *dev, pm_message_t state) +int i915_suspend_switcheroo(struct drm_device *dev, pm_message_t state) { int error; @@ -812,7 +812,7 @@ static int i915_drm_resume_early(struct drm_device *dev) return ret; } -int i915_resume_legacy(struct drm_device *dev) +int i915_resume_switcheroo(struct drm_device *dev) { int ret; @@ -1649,7 +1649,7 @@ static struct drm_driver driver = { */ .driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM | DRIVER_PRIME | - DRIVER_RENDER, + DRIVER_RENDER | DRIVER_MODESET, .load = i915_driver_load, .unload = i915_driver_unload, .open = i915_driver_open, @@ -1658,10 +1658,6 @@ static struct drm_driver driver = { .postclose = i915_driver_postclose, .set_busid = drm_pci_set_busid, - /* Used in place of i915_pm_ops for non-DRIVER_MODESET */ - .suspend = i915_suspend_legacy, - .resume = i915_resume_legacy, - #if defined(CONFIG_DEBUG_FS) .debugfs_init = i915_debugfs_init, .debugfs_cleanup = i915_debugfs_cleanup, @@ -1704,7 +1700,6 @@ static int __init i915_init(void) * either the i915.modeset prarameter or by the * vga_text_mode_force boot option. */ - driver.driver_features |= DRIVER_MODESET; if (i915.modeset == 0) driver.driver_features &= ~DRIVER_MODESET; @@ -1715,17 +1710,11 @@ static int __init i915_init(void) #endif if (!(driver.driver_features & DRIVER_MODESET)) { - driver.get_vblank_timestamp = NULL; /* Silently fail loading to not upset userspace. */ DRM_DEBUG_DRIVER("KMS and UMS disabled.\n"); return 0; } - /* - * FIXME: Note that we're lying to the DRM core here so that we can get access - * to the atomic ioctl and the atomic properties. Only plane operations on - * a single CRTC will actually work. - */ if (i915.nuclear_pageflip) driver.driver_features |= DRIVER_ATOMIC; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e1bbe460f8cc..d2c444971679 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2606,8 +2606,8 @@ struct drm_i915_cmd_table { extern const struct drm_ioctl_desc i915_ioctls[]; extern int i915_max_ioctl; -extern int i915_suspend_legacy(struct drm_device *dev, pm_message_t state); -extern int i915_resume_legacy(struct drm_device *dev); +extern int i915_suspend_switcheroo(struct drm_device *dev, pm_message_t state); +extern int i915_resume_switcheroo(struct drm_device *dev); /* i915_params.c */ struct i915_params { From aa557ab01576531c39fa3e7ceaa33314f65164fd Mon Sep 17 00:00:00 2001 From: Alex Dai Date: Tue, 18 Aug 2015 14:32:35 -0700 Subject: [PATCH 090/145] drm/i915/guc: Support GuC version 4.3 The firmware layout changes that now it only has css header + uCode + RSA signature. Plus, other trivial changes to support GuC V4.3. Signed-off-by: Alex Dai Reviewed-by: Dave Gordon Reviewed-by: Dave Gordon Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_guc_fwif.h | 11 ++++++++--- drivers/gpu/drm/i915/intel_guc_loader.c | 17 ++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_guc_fwif.h b/drivers/gpu/drm/i915/intel_guc_fwif.h index 950c7e70058b..e1f47ba2b4b0 100644 --- a/drivers/gpu/drm/i915/intel_guc_fwif.h +++ b/drivers/gpu/drm/i915/intel_guc_fwif.h @@ -41,7 +41,7 @@ #define GUC_CTX_PRIORITY_NORMAL 3 #define GUC_MAX_GPU_CONTEXTS 1024 -#define GUC_INVALID_CTX_ID (GUC_MAX_GPU_CONTEXTS + 1) +#define GUC_INVALID_CTX_ID GUC_MAX_GPU_CONTEXTS /* Work queue item header definitions */ #define WQ_STATUS_ACTIVE 1 @@ -75,6 +75,7 @@ #define GUC_CTX_DESC_ATTR_RESET (1 << 4) #define GUC_CTX_DESC_ATTR_WQLOCKED (1 << 5) #define GUC_CTX_DESC_ATTR_PCH (1 << 6) +#define GUC_CTX_DESC_ATTR_TERMINATED (1 << 7) /* The guc control data is 10 DWORDs */ #define GUC_CTL_CTXINFO 0 @@ -107,6 +108,7 @@ #define GUC_CTL_DISABLE_SCHEDULER (1 << 4) #define GUC_CTL_PREEMPTION_LOG (1 << 5) #define GUC_CTL_ENABLE_SLPC (1 << 7) +#define GUC_CTL_RESET_ON_PREMPT_FAILURE (1 << 8) #define GUC_CTL_DEBUG 8 #define GUC_LOG_VERBOSITY_SHIFT 0 #define GUC_LOG_VERBOSITY_LOW (0 << GUC_LOG_VERBOSITY_SHIFT) @@ -116,8 +118,9 @@ /* Verbosity range-check limits, without the shift */ #define GUC_LOG_VERBOSITY_MIN 0 #define GUC_LOG_VERBOSITY_MAX 3 +#define GUC_CTL_RSRVD 9 -#define GUC_CTL_MAX_DWORDS (GUC_CTL_DEBUG + 1) +#define GUC_CTL_MAX_DWORDS (GUC_CTL_RSRVD + 1) struct guc_doorbell_info { u32 db_status; @@ -207,7 +210,9 @@ struct guc_context_desc { u32 engine_presence; - u32 reserved0[1]; + u8 engine_suspended; + + u8 reserved0[3]; u64 reserved1[1]; u64 desc_private; diff --git a/drivers/gpu/drm/i915/intel_guc_loader.c b/drivers/gpu/drm/i915/intel_guc_loader.c index 13e75f6b1100..5eafd31fb4a6 100644 --- a/drivers/gpu/drm/i915/intel_guc_loader.c +++ b/drivers/gpu/drm/i915/intel_guc_loader.c @@ -59,7 +59,7 @@ * */ -#define I915_SKL_GUC_UCODE "i915/skl_guc_ver3.bin" +#define I915_SKL_GUC_UCODE "i915/skl_guc_ver4.bin" MODULE_FIRMWARE(I915_SKL_GUC_UCODE); /* User-friendly representation of an enum */ @@ -226,10 +226,6 @@ static inline bool guc_ucode_response(struct drm_i915_private *dev_priv, * +-------------------------------+ ---- * | RSA signature | 256B * +-------------------------------+ ---- - * | RSA public Key | 256B - * +-------------------------------+ ---- - * | Public key modulus | 4B - * +-------------------------------+ ---- * * Architecturally, the DMA engine is bidirectional, and can potentially even * transfer between GTT locations. This functionality is left out of the API @@ -244,7 +240,6 @@ static inline bool guc_ucode_response(struct drm_i915_private *dev_priv, #define UOS_VER_MAJOR_OFFSET 0x46 #define UOS_CSS_HEADER_SIZE 0x80 #define UOS_RSA_SIG_SIZE 0x100 -#define UOS_CSS_SIGNING_SIZE 0x204 static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv) { @@ -256,7 +251,7 @@ static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv) int i, ret = 0; /* uCode size, also is where RSA signature starts */ - offset = ucode_size = guc_fw->guc_fw_size - UOS_CSS_SIGNING_SIZE; + offset = ucode_size = guc_fw->guc_fw_size - UOS_RSA_SIG_SIZE; I915_WRITE(DMA_COPY_SIZE, ucode_size); /* Copy RSA signature from the fw image to HW for verification */ @@ -463,8 +458,8 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw) struct drm_i915_gem_object *obj; const struct firmware *fw; const u8 *css_header; - const size_t minsize = UOS_CSS_HEADER_SIZE + UOS_CSS_SIGNING_SIZE; - const size_t maxsize = GUC_WOPCM_SIZE_VALUE + UOS_CSS_SIGNING_SIZE + const size_t minsize = UOS_CSS_HEADER_SIZE + UOS_RSA_SIG_SIZE; + const size_t maxsize = GUC_WOPCM_SIZE_VALUE + UOS_RSA_SIG_SIZE - 0x8000; /* 32k reserved (8K stack + 24k context) */ int err; @@ -564,8 +559,8 @@ void intel_guc_ucode_init(struct drm_device *dev) fw_path = NULL; } else if (IS_SKYLAKE(dev)) { fw_path = I915_SKL_GUC_UCODE; - guc_fw->guc_fw_major_wanted = 3; - guc_fw->guc_fw_minor_wanted = 0; + guc_fw->guc_fw_major_wanted = 4; + guc_fw->guc_fw_minor_wanted = 3; } else { i915.enable_guc_submission = false; fw_path = ""; /* unknown device */ From f5d3c3eaab1d9e38854deb0251e659d04e3bc771 Mon Sep 17 00:00:00 2001 From: Alex Dai Date: Tue, 18 Aug 2015 14:34:47 -0700 Subject: [PATCH 091/145] drm/i915: Notify GuC rc6 state If rc6 is enabled, notify GuC so it can do proper forcewake before command submission. Signed-off-by: Alex Dai Reviewed-by: Tom O'Rourke Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_guc_submission.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c index ec703937f363..792d0b958a2c 100644 --- a/drivers/gpu/drm/i915/i915_guc_submission.c +++ b/drivers/gpu/drm/i915/i915_guc_submission.c @@ -151,6 +151,18 @@ static int host2guc_release_doorbell(struct intel_guc *guc, return host2guc_action(guc, data, 2); } +static int host2guc_sample_forcewake(struct intel_guc *guc, + struct i915_guc_client *client) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + u32 data[2]; + + data[0] = HOST2GUC_ACTION_SAMPLE_FORCEWAKE; + data[1] = (intel_enable_rc6(dev_priv->dev)) ? 1 : 0; + + return host2guc_action(guc, data, 2); +} + /* * Initialise, update, or clear doorbell data shared with the GuC * @@ -874,6 +886,9 @@ int i915_guc_submission_enable(struct drm_device *dev) } guc->execbuf_client = client; + + host2guc_sample_forcewake(guc, client); + return 0; } From 79e50a4f7228e5f27d2e9785fad5509d1392a0d3 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 26 Aug 2015 10:58:20 +0300 Subject: [PATCH 092/145] drm/i915: move intel_hrawclk() to intel_display.c Make it available outside of intel_dp.c. Signed-off-by: Jani Nikula Reviewed-by: Clint Taylor Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 33 +++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_dp.c | 34 ---------------------------- drivers/gpu/drm/i915/intel_drv.h | 1 + 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 439e66e7c7cb..3f60cf4fe6fd 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -135,6 +135,39 @@ intel_pch_rawclk(struct drm_device *dev) return I915_READ(PCH_RAWCLK_FREQ) & RAWCLK_FREQ_MASK; } +/* hrawclock is 1/4 the FSB frequency */ +int intel_hrawclk(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t clkcfg; + + /* There is no CLKCFG reg in Valleyview. VLV hrawclk is 200 MHz */ + if (IS_VALLEYVIEW(dev)) + return 200; + + clkcfg = I915_READ(CLKCFG); + switch (clkcfg & CLKCFG_FSB_MASK) { + case CLKCFG_FSB_400: + return 100; + case CLKCFG_FSB_533: + return 133; + case CLKCFG_FSB_667: + return 166; + case CLKCFG_FSB_800: + return 200; + case CLKCFG_FSB_1067: + return 266; + case CLKCFG_FSB_1333: + return 333; + /* these two are just a guess; one of them might be right */ + case CLKCFG_FSB_1600: + case CLKCFG_FSB_1600_ALT: + return 400; + default: + return 133; + } +} + static inline u32 /* units of 100MHz */ intel_fdi_link_freq(struct drm_device *dev) { diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3a9c43d2c2c4..f45872cc6d24 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -261,40 +261,6 @@ static void intel_dp_unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes) dst[i] = src >> ((3-i) * 8); } -/* hrawclock is 1/4 the FSB frequency */ -static int -intel_hrawclk(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - uint32_t clkcfg; - - /* There is no CLKCFG reg in Valleyview. VLV hrawclk is 200 MHz */ - if (IS_VALLEYVIEW(dev)) - return 200; - - clkcfg = I915_READ(CLKCFG); - switch (clkcfg & CLKCFG_FSB_MASK) { - case CLKCFG_FSB_400: - return 100; - case CLKCFG_FSB_533: - return 133; - case CLKCFG_FSB_667: - return 166; - case CLKCFG_FSB_800: - return 200; - case CLKCFG_FSB_1067: - return 266; - case CLKCFG_FSB_1333: - return 333; - /* these two are just a guess; one of them might be right */ - case CLKCFG_FSB_1600: - case CLKCFG_FSB_1600_ALT: - return 400; - default: - return 133; - } -} - static void intel_dp_init_panel_power_sequencer(struct drm_device *dev, struct intel_dp *intel_dp); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 72c1181bd7f5..30012c8ea42f 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1011,6 +1011,7 @@ void i915_audio_component_cleanup(struct drm_i915_private *dev_priv); extern const struct drm_plane_funcs intel_plane_funcs; bool intel_has_pending_fb_unpin(struct drm_device *dev); int intel_pch_rawclk(struct drm_device *dev); +int intel_hrawclk(struct drm_device *dev); void intel_mark_busy(struct drm_device *dev); void intel_mark_idle(struct drm_device *dev); void intel_crtc_restore_mode(struct drm_crtc *crtc); From 42a8ca4cb4a48ddbf40e8edb291425e76bcdc230 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 27 Aug 2015 16:23:30 +0300 Subject: [PATCH 093/145] drm/i915: add yesno utility function Add a common function to return "yes" or "no" string based on the argument, and drop the local versions of it. Signed-off-by: Jani Nikula Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 5 ----- drivers/gpu/drm/i915/i915_drv.h | 5 +++++ drivers/gpu/drm/i915/i915_gpu_error.c | 5 ----- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 624024c288aa..2f1b693d7162 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -46,11 +46,6 @@ enum { PINNED_LIST, }; -static const char *yesno(int v) -{ - return v ? "yes" : "no"; -} - /* As the drm_debugfs_init() routines are called before dev->dev_private is * allocated we need to hook into the minor for release. */ static int diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index d2c444971679..1d99402e4a1e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -106,6 +106,11 @@ unlikely(__ret_warn_on); \ }) +static inline const char *yesno(bool v) +{ + return v ? "yes" : "no"; +} + enum pipe { INVALID_PIPE = -1, PIPE_A = 0, diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 493e9b294a69..3379f9c1ef88 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -30,11 +30,6 @@ #include #include "i915_drv.h" -static const char *yesno(int v) -{ - return v ? "yes" : "no"; -} - static const char *ring_str(int ring) { switch (ring) { From 331f38e77d4519d2ca60cea232c7f2bfad5e4d28 Mon Sep 17 00:00:00 2001 From: Zhiyuan Lv Date: Fri, 28 Aug 2015 15:41:14 +0800 Subject: [PATCH 094/145] drm/i915: preallocate pdps for 32 bit vgpu This is based on Mika Kuoppala's patch below: http://article.gmane.org/gmane.comp.freedesktop.xorg.drivers.intel/61104/match=workaround+hw+preload The patch will preallocate the page directories for 32-bit PPGTT when i915 runs inside a virtual machine with Intel GVT-g. With this change, the root pointers in EXECLIST context will always keep the same. The change is needed for vGPU because Intel GVT-g will do page table shadowing, and needs to track all the page table changes from guest i915 driver. However, if guest PPGTT is modified through GPU commands like LRI, it is not possible to trap the operations in the right time, so it will be hard to make shadow PPGTT to work correctly. Shadow PPGTT could be much simpler with this change. Meanwhile hypervisor could simply prohibit any attempt of PPGTT modification through GPU command for security. The function gen8_preallocate_top_level_pdps() in the patch is from Mika, with only one change to set "used_pdpes" to avoid duplicated allocation later. Cc: Mika Kuoppala Cc: Dave Gordon Cc: Michel Thierry Signed-off-by: Zhiyuan Lv Signed-off-by: Zhi Wang Reviewed-by: Joonas Lahtinen Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 33 +++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_lrc.c | 3 ++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 4a76807143b1..ed10e776670b 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -1441,6 +1441,33 @@ static void gen8_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m) } } +static int gen8_preallocate_top_level_pdps(struct i915_hw_ppgtt *ppgtt) +{ + unsigned long *new_page_dirs, **new_page_tables; + uint32_t pdpes = I915_PDPES_PER_PDP(dev); + int ret; + + /* We allocate temp bitmap for page tables for no gain + * but as this is for init only, lets keep the things simple + */ + ret = alloc_gen8_temp_bitmaps(&new_page_dirs, &new_page_tables, pdpes); + if (ret) + return ret; + + /* Allocate for all pdps regardless of how the ppgtt + * was defined. + */ + ret = gen8_ppgtt_alloc_page_directories(&ppgtt->base, &ppgtt->pdp, + 0, 1ULL << 32, + new_page_dirs); + if (!ret) + *ppgtt->pdp.used_pdpes = *new_page_dirs; + + free_gen8_temp_bitmaps(new_page_dirs, new_page_tables, pdpes); + + return ret; +} + /* * GEN8 legacy ppgtt programming is accomplished through a max 4 PDP registers * with a net effect resembling a 2-level page table in normal x86 terms. Each @@ -1484,6 +1511,12 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt) trace_i915_page_directory_pointer_entry_alloc(&ppgtt->base, 0, 0, GEN8_PML4E_SHIFT); + + if (intel_vgpu_active(ppgtt->base.dev)) { + ret = gen8_preallocate_top_level_pdps(ppgtt); + if (ret) + goto free_scratch; + } } return 0; diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 1af84c52115c..258af9b3f765 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -1540,7 +1540,8 @@ static int gen8_emit_bb_start(struct drm_i915_gem_request *req, * not needed in 48-bit.*/ if (req->ctx->ppgtt && (intel_ring_flag(req->ring) & req->ctx->ppgtt->pd_dirty_rings)) { - if (!USES_FULL_48BIT_PPGTT(req->i915)) { + if (!USES_FULL_48BIT_PPGTT(req->i915) && + !intel_vgpu_active(req->i915->dev)) { ret = intel_logical_ring_emit_pdps(req); if (ret) return ret; From a0bd6c3183a164970e934ee4f514956dd577a628 Mon Sep 17 00:00:00 2001 From: Zhiyuan Lv Date: Fri, 28 Aug 2015 15:41:16 +0800 Subject: [PATCH 095/145] drm/i915: Always enable execlists on BDW for vgpu Broadwell hardware supports both ring buffer mode and execlist mode. When i915 runs inside a VM with Intel GVT-g, we allow execlist mode only. The main reason of EXECLIST only is that GVT-g does not support the dynamic mode switch between ring buffer mode and execlist mode when running multiple virtual machines. v2: - Adjust the position of vgpu check in sanitize function (Joonas) - Add vgpu error check in context initialization. (Joonas, Daniel) Signed-off-by: Zhiyuan Lv Signed-off-by: Zhi Wang Reviewed-by: Joonas Lahtinen Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_context.c | 7 +++++++ drivers/gpu/drm/i915/intel_lrc.c | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 8e893b354bcc..74aa0c9929ba 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -332,6 +332,13 @@ int i915_gem_context_init(struct drm_device *dev) if (WARN_ON(dev_priv->ring[RCS].default_context)) return 0; + if (intel_vgpu_active(dev) && HAS_LOGICAL_RING_CONTEXTS(dev)) { + if (!i915.enable_execlists) { + DRM_INFO("Only EXECLIST mode is supported in vgpu.\n"); + return -EINVAL; + } + } + if (i915.enable_execlists) { /* NB: intentionally left blank. We will allocate our own * backing objects as we need them, thank you very much */ diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 258af9b3f765..e9520afc2033 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -236,6 +236,12 @@ int intel_sanitize_enable_execlists(struct drm_device *dev, int enable_execlists { WARN_ON(i915.enable_ppgtt == -1); + /* On platforms with execlist available, vGPU will only + * support execlist mode, no ring buffer mode. + */ + if (HAS_LOGICAL_RING_CONTEXTS(dev) && intel_vgpu_active(dev)) + return 1; + if (INTEL_INFO(dev)->gen >= 9) return 1; From 532beabf343d16d269491c63ed4590e5a675ea2b Mon Sep 17 00:00:00 2001 From: Zhiyuan Lv Date: Fri, 28 Aug 2015 15:41:17 +0800 Subject: [PATCH 096/145] drm/i915: Update PV INFO page definition for Intel GVT-g Some more definitions in the PV info page are added. They are mainly for the guest notification to Intel GVT-g device model. They are used for Broadwell enabling. The notification of PPGTT page table creation/destroy is to notify GVT-g device model the life cycle of guest page tables. Then device model will implement shadow page table for guests. The notification of context create/destroy is optional. If it is used, the device model will create/destroy shadow context corresponding to the context's life cycle. Guest driver needs to make sure that the context's LRCA and backing storage address unchanged. If it is not used, the device model will perform the context shadow work in the context scheduling time. Signed-off-by: Zhiyuan Lv Signed-off-by: Zhi Wang Reviewed-by: Joonas Lahtinen Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_vgpu.h | 34 ++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_vgpu.h b/drivers/gpu/drm/i915/i915_vgpu.h index 97a88b5f6a26..21c97f44d637 100644 --- a/drivers/gpu/drm/i915/i915_vgpu.h +++ b/drivers/gpu/drm/i915/i915_vgpu.h @@ -40,6 +40,19 @@ #define INTEL_VGT_IF_VERSION \ INTEL_VGT_IF_VERSION_ENCODE(VGT_VERSION_MAJOR, VGT_VERSION_MINOR) +/* + * notifications from guest to vgpu device model + */ +enum vgt_g2v_type { + VGT_G2V_PPGTT_L3_PAGE_TABLE_CREATE = 2, + VGT_G2V_PPGTT_L3_PAGE_TABLE_DESTROY, + VGT_G2V_PPGTT_L4_PAGE_TABLE_CREATE, + VGT_G2V_PPGTT_L4_PAGE_TABLE_DESTROY, + VGT_G2V_EXECLIST_CONTEXT_CREATE, + VGT_G2V_EXECLIST_CONTEXT_DESTROY, + VGT_G2V_MAX, +}; + struct vgt_if { uint64_t magic; /* VGT_MAGIC */ uint16_t version_major; @@ -70,11 +83,28 @@ struct vgt_if { uint32_t rsv3[0x200 - 24]; /* pad to half page */ /* * The bottom half page is for response from Gfx driver to hypervisor. - * Set to reserved fields temporarily by now. */ uint32_t rsv4; uint32_t display_ready; /* ready for display owner switch */ - uint32_t rsv5[0x200 - 2]; /* pad to one page */ + + uint32_t rsv5[4]; + + uint32_t g2v_notify; + uint32_t rsv6[7]; + + uint32_t pdp0_lo; + uint32_t pdp0_hi; + uint32_t pdp1_lo; + uint32_t pdp1_hi; + uint32_t pdp2_lo; + uint32_t pdp2_hi; + uint32_t pdp3_lo; + uint32_t pdp3_hi; + + uint32_t execlist_context_descriptor_lo; + uint32_t execlist_context_descriptor_hi; + + uint32_t rsv7[0x200 - 24]; /* pad to one page */ } __packed; #define vgtif_reg(x) \ From 650da34c4af12921e0148bbc0dba7419d7776189 Mon Sep 17 00:00:00 2001 From: Zhiyuan Lv Date: Fri, 28 Aug 2015 15:41:18 +0800 Subject: [PATCH 097/145] drm/i915: guest i915 notification for Intel GVT-g When i915 drivers run inside a VM with Intel GVT-g, some explicit notifications are needed from guest to host device model through PV INFO page write. The notifications include: PPGTT create PPGTT destroy They are used for the shadow implementation of PPGTT. Intel GVT-g needs to write-protect the guest pages of PPGTT, and clear the write protection when they end their life cycle. v2: - Use lower_32_bits()/upper_32_bits() for qword operations; - Remove the notification of guest context creation/destroy; Signed-off-by: Zhiyuan Lv Signed-off-by: Zhi Wang Reviewed-by: Joonas Lahtinen Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 41 +++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index ed10e776670b..bdb7adc7359e 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -899,6 +899,41 @@ static int gen8_init_scratch(struct i915_address_space *vm) return 0; } +static int gen8_ppgtt_notify_vgt(struct i915_hw_ppgtt *ppgtt, bool create) +{ + enum vgt_g2v_type msg; + struct drm_device *dev = ppgtt->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned int offset = vgtif_reg(pdp0_lo); + int i; + + if (USES_FULL_48BIT_PPGTT(dev)) { + u64 daddr = px_dma(&ppgtt->pml4); + + I915_WRITE(offset, lower_32_bits(daddr)); + I915_WRITE(offset + 4, upper_32_bits(daddr)); + + msg = (create ? VGT_G2V_PPGTT_L4_PAGE_TABLE_CREATE : + VGT_G2V_PPGTT_L4_PAGE_TABLE_DESTROY); + } else { + for (i = 0; i < GEN8_LEGACY_PDPES; i++) { + u64 daddr = i915_page_dir_dma_addr(ppgtt, i); + + I915_WRITE(offset, lower_32_bits(daddr)); + I915_WRITE(offset + 4, upper_32_bits(daddr)); + + offset += 8; + } + + msg = (create ? VGT_G2V_PPGTT_L3_PAGE_TABLE_CREATE : + VGT_G2V_PPGTT_L3_PAGE_TABLE_DESTROY); + } + + I915_WRITE(vgtif_reg(g2v_notify), msg); + + return 0; +} + static void gen8_free_scratch(struct i915_address_space *vm) { struct drm_device *dev = vm->dev; @@ -945,6 +980,9 @@ static void gen8_ppgtt_cleanup(struct i915_address_space *vm) struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); + if (intel_vgpu_active(vm->dev)) + gen8_ppgtt_notify_vgt(ppgtt, false); + if (!USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) gen8_ppgtt_cleanup_3lvl(ppgtt->base.dev, &ppgtt->pdp); else @@ -1519,6 +1557,9 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt) } } + if (intel_vgpu_active(ppgtt->base.dev)) + gen8_ppgtt_notify_vgt(ppgtt, true); + return 0; free_scratch: From 34e0adbb4508a318e8039ee3959383e4bf080f04 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Mon, 31 Aug 2015 13:04:25 +0200 Subject: [PATCH 098/145] drm/i915: Remove start frame argument to pipe_update_begin/end. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's already a per crtc member that can be used for it. Signed-off-by: Maarten Lankhorst Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 9 ++++----- drivers/gpu/drm/i915/intel_drv.h | 5 ++--- drivers/gpu/drm/i915/intel_sprite.c | 15 ++++++++------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3f60cf4fe6fd..d38ceb039de6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11142,11 +11142,10 @@ static void ilk_do_mmio_flip(struct intel_crtc *intel_crtc) static void intel_do_mmio_flip(struct intel_crtc *intel_crtc) { struct drm_device *dev = intel_crtc->base.dev; - u32 start_vbl_count; intel_mark_page_flip_active(intel_crtc); - intel_pipe_update_start(intel_crtc, &start_vbl_count); + intel_pipe_update_start(intel_crtc); if (INTEL_INFO(dev)->gen >= 9) skl_do_mmio_flip(intel_crtc); @@ -11154,7 +11153,7 @@ static void intel_do_mmio_flip(struct intel_crtc *intel_crtc) /* use_mmio_flip() retricts MMIO flips to ilk+ */ ilk_do_mmio_flip(intel_crtc); - intel_pipe_update_end(intel_crtc, start_vbl_count); + intel_pipe_update_end(intel_crtc); } static void intel_mmio_flip_work_func(struct work_struct *work) @@ -13463,7 +13462,7 @@ static void intel_begin_crtc_commit(struct drm_crtc *crtc, /* Perform vblank evasion around commit operation */ if (crtc->state->active) - intel_pipe_update_start(intel_crtc, &intel_crtc->start_vbl_count); + intel_pipe_update_start(intel_crtc); if (!needs_modeset(crtc->state) && INTEL_INFO(dev)->gen >= 9) skl_detach_scalers(intel_crtc); @@ -13475,7 +13474,7 @@ static void intel_finish_crtc_commit(struct drm_crtc *crtc, struct intel_crtc *intel_crtc = to_intel_crtc(crtc); if (crtc->state->active) - intel_pipe_update_end(intel_crtc, intel_crtc->start_vbl_count); + intel_pipe_update_end(intel_crtc); } /** diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 30012c8ea42f..8f8d320486ff 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1414,9 +1414,8 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob); int intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane); int intel_sprite_set_colorkey(struct drm_device *dev, void *data, struct drm_file *file_priv); -void intel_pipe_update_start(struct intel_crtc *crtc, - uint32_t *start_vbl_count); -void intel_pipe_update_end(struct intel_crtc *crtc, u32 start_vbl_count); +void intel_pipe_update_start(struct intel_crtc *crtc); +void intel_pipe_update_end(struct intel_crtc *crtc); /* intel_tv.c */ void intel_tv_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 9d8af2f8a875..c35c2339f472 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -76,7 +76,7 @@ static int usecs_to_scanlines(const struct drm_display_mode *mode, int usecs) * avoid random delays. The value written to @start_vbl_count should be * supplied to intel_pipe_update_end() for error checking. */ -void intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl_count) +void intel_pipe_update_start(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; const struct drm_display_mode *mode = &crtc->config->base.adjusted_mode; @@ -95,7 +95,7 @@ void intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl_count) max = vblank_start - 1; local_irq_disable(); - *start_vbl_count = 0; + crtc->start_vbl_count = 0; if (min <= 0 || max <= 0) return; @@ -134,9 +134,10 @@ void intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl_count) drm_crtc_vblank_put(&crtc->base); - *start_vbl_count = dev->driver->get_vblank_counter(dev, pipe); + crtc->start_vbl_count = dev->driver->get_vblank_counter(dev, pipe); - trace_i915_pipe_update_vblank_evaded(crtc, min, max, *start_vbl_count); + trace_i915_pipe_update_vblank_evaded(crtc, min, max, + crtc->start_vbl_count); } /** @@ -148,7 +149,7 @@ void intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl_count) * re-enables interrupts and verifies the update was actually completed * before a vblank using the value of @start_vbl_count. */ -void intel_pipe_update_end(struct intel_crtc *crtc, u32 start_vbl_count) +void intel_pipe_update_end(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; enum pipe pipe = crtc->pipe; @@ -158,9 +159,9 @@ void intel_pipe_update_end(struct intel_crtc *crtc, u32 start_vbl_count) local_irq_enable(); - if (start_vbl_count && start_vbl_count != end_vbl_count) + if (crtc->start_vbl_count && crtc->start_vbl_count != end_vbl_count) DRM_ERROR("Atomic update failure on pipe %c (start=%u end=%u)\n", - pipe_name(pipe), start_vbl_count, end_vbl_count); + pipe_name(pipe), crtc->start_vbl_count, end_vbl_count); } static void From 85a62bf9d8ef8d533635270ae985281c58e8c974 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 1 Sep 2015 12:15:33 +0200 Subject: [PATCH 099/145] drm/i915: Also record time difference if vblank evasion fails, v2. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes the error message slightly more useful. Changes since v1: - Use ktime_get() while irqs are still disabled. (vsyrjala) Signed-off-by: Maarten Lankhorst Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_drv.h | 2 ++ drivers/gpu/drm/i915/intel_sprite.c | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8f8d320486ff..090d67b04307 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -564,6 +564,8 @@ struct intel_crtc { int scanline_offset; unsigned start_vbl_count; + ktime_t start_vbl_time; + struct intel_crtc_atomic_commit atomic; /* scalers available on this crtc */ diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index c35c2339f472..ca7e26430e66 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -134,6 +134,7 @@ void intel_pipe_update_start(struct intel_crtc *crtc) drm_crtc_vblank_put(&crtc->base); + crtc->start_vbl_time = ktime_get(); crtc->start_vbl_count = dev->driver->get_vblank_counter(dev, pipe); trace_i915_pipe_update_vblank_evaded(crtc, min, max, @@ -154,14 +155,16 @@ void intel_pipe_update_end(struct intel_crtc *crtc) struct drm_device *dev = crtc->base.dev; enum pipe pipe = crtc->pipe; u32 end_vbl_count = dev->driver->get_vblank_counter(dev, pipe); + ktime_t end_vbl_time = ktime_get(); trace_i915_pipe_update_end(crtc, end_vbl_count); local_irq_enable(); if (crtc->start_vbl_count && crtc->start_vbl_count != end_vbl_count) - DRM_ERROR("Atomic update failure on pipe %c (start=%u end=%u)\n", - pipe_name(pipe), crtc->start_vbl_count, end_vbl_count); + DRM_ERROR("Atomic update failure on pipe %c (start=%u end=%u) time %lld us\n", + pipe_name(pipe), crtc->start_vbl_count, end_vbl_count, + ktime_us_delta(end_vbl_time, crtc->start_vbl_time)); } static void From 62f0ace531b2ca69a1defe5a01a85539fd28353f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 26 Aug 2015 18:57:26 +0200 Subject: [PATCH 100/145] drm/i915: Update comments around base bpp Forgot to do that in commit d328c9d78d64ca11e744fe227096990430a88477 Author: Daniel Vetter Date: Fri Apr 10 16:22:37 2015 +0200 drm/i915: Select starting pipe bpp irrespective or the primary plane and it's confusing. Fix it. Cc: Jesse Barnes Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index deba3330de71..ae485e717c57 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -12102,10 +12102,6 @@ intel_modeset_pipe_config(struct drm_crtc *crtc, (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC))) pipe_config->base.adjusted_mode.flags |= DRM_MODE_FLAG_NVSYNC; - /* Compute a starting value for pipe_config->pipe_bpp taking the source - * plane pixel format and any sink constraints into account. Returns the - * source plane bpp so that dithering can be selected on mismatches - * after encoders and crtc also have had their say. */ base_bpp = compute_baseline_pipe_bpp(to_intel_crtc(crtc), pipe_config); if (base_bpp < 0) @@ -12174,7 +12170,7 @@ intel_modeset_pipe_config(struct drm_crtc *crtc, /* Dithering seems to not pass-through bits correctly when it should, so * only enable it on 6bpc panels. */ pipe_config->dither = pipe_config->pipe_bpp == 6*3; - DRM_DEBUG_KMS("plane bpp: %i, pipe bpp: %i, dithering: %i\n", + DRM_DEBUG_KMS("hw max bpp: %i, pipe bpp: %i, dithering: %i\n", base_bpp, pipe_config->pipe_bpp, pipe_config->dither); fail: From 40bfd7a3303b7c383493c80a250c59b61d812ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 27 Aug 2015 23:55:56 +0300 Subject: [PATCH 101/145] drm/i915: Clean up various HPD defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Indent the PORTx_HOTPLUG_... defines appropriately, and fix some space vs. tab issues. v2: Document pre-HSW/LPT bits, and order another tab (Paulo) Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 74 +++++++++++++++++---------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index e7c9dc8e24fe..e36baca48ab8 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -5394,15 +5394,17 @@ enum skl_disp_power_wells { #define CPU_VGACNTRL 0x41000 -#define DIGITAL_PORT_HOTPLUG_CNTRL 0x44030 -#define DIGITAL_PORTA_HOTPLUG_ENABLE (1 << 4) -#define DIGITAL_PORTA_SHORT_PULSE_2MS (0 << 2) -#define DIGITAL_PORTA_SHORT_PULSE_4_5MS (1 << 2) -#define DIGITAL_PORTA_SHORT_PULSE_6MS (2 << 2) -#define DIGITAL_PORTA_SHORT_PULSE_100MS (3 << 2) -#define DIGITAL_PORTA_NO_DETECT (0 << 0) -#define DIGITAL_PORTA_LONG_PULSE_DETECT_MASK (1 << 1) -#define DIGITAL_PORTA_SHORT_PULSE_DETECT_MASK (1 << 0) +#define DIGITAL_PORT_HOTPLUG_CNTRL 0x44030 +#define DIGITAL_PORTA_HOTPLUG_ENABLE (1 << 4) +#define DIGITAL_PORTA_PULSE_DURATION_2ms (0 << 2) /* pre-HSW */ +#define DIGITAL_PORTA_PULSE_DURATION_4_5ms (1 << 2) /* pre-HSW */ +#define DIGITAL_PORTA_PULSE_DURATION_6ms (2 << 2) /* pre-HSW */ +#define DIGITAL_PORTA_PULSE_DURATION_100ms (3 << 2) /* pre-HSW */ +#define DIGITAL_PORTA_PULSE_DURATION_MASK (3 << 2) /* pre-HSW */ +#define DIGITAL_PORTA_HOTPLUG_STATUS_MASK (3 << 0) +#define DIGITAL_PORTA_HOTPLUG_NO_DETECT (0 << 0) +#define DIGITAL_PORTA_HOTPLUG_SHORT_DETECT (1 << 0) +#define DIGITAL_PORTA_HOTPLUG_LONG_DETECT (2 << 0) /* refresh rate hardware control */ #define RR_HW_CTL 0x45300 @@ -6030,46 +6032,46 @@ enum skl_disp_power_wells { #define SERR_INT_TRANS_FIFO_UNDERRUN(pipe) (1<<(pipe*3)) /* digital port hotplug */ -#define PCH_PORT_HOTPLUG 0xc4030 /* SHOTPLUG_CTL */ -#define BXT_PORTA_HOTPLUG_ENABLE (1 << 28) -#define BXT_PORTA_HOTPLUG_STATUS_MASK (0x3 << 24) +#define PCH_PORT_HOTPLUG 0xc4030 /* SHOTPLUG_CTL */ +#define BXT_PORTA_HOTPLUG_ENABLE (1 << 28) +#define BXT_PORTA_HOTPLUG_STATUS_MASK (3 << 24) #define BXT_PORTA_HOTPLUG_NO_DETECT (0 << 24) #define BXT_PORTA_HOTPLUG_SHORT_DETECT (1 << 24) #define BXT_PORTA_HOTPLUG_LONG_DETECT (2 << 24) -#define PORTD_HOTPLUG_ENABLE (1 << 20) -#define PORTD_PULSE_DURATION_2ms (0) -#define PORTD_PULSE_DURATION_4_5ms (1 << 18) -#define PORTD_PULSE_DURATION_6ms (2 << 18) -#define PORTD_PULSE_DURATION_100ms (3 << 18) -#define PORTD_PULSE_DURATION_MASK (3 << 18) -#define PORTD_HOTPLUG_STATUS_MASK (0x3 << 16) +#define PORTD_HOTPLUG_ENABLE (1 << 20) +#define PORTD_PULSE_DURATION_2ms (0 << 18) /* pre-LPT */ +#define PORTD_PULSE_DURATION_4_5ms (1 << 18) /* pre-LPT */ +#define PORTD_PULSE_DURATION_6ms (2 << 18) /* pre-LPT */ +#define PORTD_PULSE_DURATION_100ms (3 << 18) /* pre-LPT */ +#define PORTD_PULSE_DURATION_MASK (3 << 18) /* pre-LPT */ +#define PORTD_HOTPLUG_STATUS_MASK (3 << 16) #define PORTD_HOTPLUG_NO_DETECT (0 << 16) #define PORTD_HOTPLUG_SHORT_DETECT (1 << 16) #define PORTD_HOTPLUG_LONG_DETECT (2 << 16) -#define PORTC_HOTPLUG_ENABLE (1 << 12) -#define PORTC_PULSE_DURATION_2ms (0) -#define PORTC_PULSE_DURATION_4_5ms (1 << 10) -#define PORTC_PULSE_DURATION_6ms (2 << 10) -#define PORTC_PULSE_DURATION_100ms (3 << 10) -#define PORTC_PULSE_DURATION_MASK (3 << 10) -#define PORTC_HOTPLUG_STATUS_MASK (0x3 << 8) +#define PORTC_HOTPLUG_ENABLE (1 << 12) +#define PORTC_PULSE_DURATION_2ms (0 << 10) /* pre-LPT */ +#define PORTC_PULSE_DURATION_4_5ms (1 << 10) /* pre-LPT */ +#define PORTC_PULSE_DURATION_6ms (2 << 10) /* pre-LPT */ +#define PORTC_PULSE_DURATION_100ms (3 << 10) /* pre-LPT */ +#define PORTC_PULSE_DURATION_MASK (3 << 10) /* pre-LPT */ +#define PORTC_HOTPLUG_STATUS_MASK (3 << 8) #define PORTC_HOTPLUG_NO_DETECT (0 << 8) #define PORTC_HOTPLUG_SHORT_DETECT (1 << 8) #define PORTC_HOTPLUG_LONG_DETECT (2 << 8) -#define PORTB_HOTPLUG_ENABLE (1 << 4) -#define PORTB_PULSE_DURATION_2ms (0) -#define PORTB_PULSE_DURATION_4_5ms (1 << 2) -#define PORTB_PULSE_DURATION_6ms (2 << 2) -#define PORTB_PULSE_DURATION_100ms (3 << 2) -#define PORTB_PULSE_DURATION_MASK (3 << 2) -#define PORTB_HOTPLUG_STATUS_MASK (0x3 << 0) +#define PORTB_HOTPLUG_ENABLE (1 << 4) +#define PORTB_PULSE_DURATION_2ms (0 << 2) /* pre-LPT */ +#define PORTB_PULSE_DURATION_4_5ms (1 << 2) /* pre-LPT */ +#define PORTB_PULSE_DURATION_6ms (2 << 2) /* pre-LPT */ +#define PORTB_PULSE_DURATION_100ms (3 << 2) /* pre-LPT */ +#define PORTB_PULSE_DURATION_MASK (3 << 2) /* pre-LPT */ +#define PORTB_HOTPLUG_STATUS_MASK (3 << 0) #define PORTB_HOTPLUG_NO_DETECT (0 << 0) #define PORTB_HOTPLUG_SHORT_DETECT (1 << 0) #define PORTB_HOTPLUG_LONG_DETECT (2 << 0) -#define PCH_PORT_HOTPLUG2 0xc403C /* SHOTPLUG_CTL2 */ -#define PORTE_HOTPLUG_ENABLE (1 << 4) -#define PORTE_HOTPLUG_STATUS_MASK (0x3 << 0) +#define PCH_PORT_HOTPLUG2 0xc403C /* SHOTPLUG_CTL2 SPT+ */ +#define PORTE_HOTPLUG_ENABLE (1 << 4) +#define PORTE_HOTPLUG_STATUS_MASK (3 << 0) #define PORTE_HOTPLUG_NO_DETECT (0 << 0) #define PORTE_HOTPLUG_SHORT_DETECT (1 << 0) #define PORTE_HOTPLUG_LONG_DETECT (2 << 0) From 87a0210665eeca7c5fa237592da025650b216831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 27 Aug 2015 23:55:57 +0300 Subject: [PATCH 102/145] drm/i915: Extract intel_hpd_enabled_irqs() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eliminate a bunch of duplicated code that calculates the currently enabled HPD interrupt bits. v2: s/;/:/ in patch subject (Paulo) Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 43 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 8485bea966cc..de0edbdb05ed 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -3002,27 +3002,34 @@ static void cherryview_irq_preinstall(struct drm_device *dev) vlv_display_irq_reset(dev_priv); } +static u32 intel_hpd_enabled_irqs(struct drm_device *dev, + const u32 hpd[HPD_NUM_PINS]) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_encoder *encoder; + u32 enabled_irqs = 0; + + for_each_intel_encoder(dev, encoder) + if (dev_priv->hotplug.stats[encoder->hpd_pin].state == HPD_ENABLED) + enabled_irqs |= hpd[encoder->hpd_pin]; + + return enabled_irqs; +} + static void ibx_hpd_irq_setup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_encoder *intel_encoder; - u32 hotplug_irqs, hotplug, enabled_irqs = 0; + u32 hotplug_irqs, hotplug, enabled_irqs; if (HAS_PCH_IBX(dev)) { hotplug_irqs = SDE_HOTPLUG_MASK; - for_each_intel_encoder(dev, intel_encoder) - if (dev_priv->hotplug.stats[intel_encoder->hpd_pin].state == HPD_ENABLED) - enabled_irqs |= hpd_ibx[intel_encoder->hpd_pin]; + enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_ibx); } else if (HAS_PCH_SPT(dev)) { hotplug_irqs = SDE_HOTPLUG_MASK_SPT; - for_each_intel_encoder(dev, intel_encoder) - if (dev_priv->hotplug.stats[intel_encoder->hpd_pin].state == HPD_ENABLED) - enabled_irqs |= hpd_spt[intel_encoder->hpd_pin]; + enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_spt); } else { hotplug_irqs = SDE_HOTPLUG_MASK_CPT; - for_each_intel_encoder(dev, intel_encoder) - if (dev_priv->hotplug.stats[intel_encoder->hpd_pin].state == HPD_ENABLED) - enabled_irqs |= hpd_cpt[intel_encoder->hpd_pin]; + enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_cpt); } ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs); @@ -3051,15 +3058,10 @@ static void ibx_hpd_irq_setup(struct drm_device *dev) static void bxt_hpd_irq_setup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_encoder *intel_encoder; - u32 hotplug_port = 0; + u32 hotplug_port; u32 hotplug_ctrl; - for_each_intel_encoder(dev, intel_encoder) { - if (dev_priv->hotplug.stats[intel_encoder->hpd_pin].state - == HPD_ENABLED) - hotplug_port |= hpd_bxt[intel_encoder->hpd_pin]; - } + hotplug_port = intel_hpd_enabled_irqs(dev, hpd_bxt); hotplug_ctrl = I915_READ(BXT_HOTPLUG_CTL) & ~BXT_HOTPLUG_CTL_MASK; @@ -3935,7 +3937,6 @@ static int i965_irq_postinstall(struct drm_device *dev) static void i915_hpd_irq_setup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_encoder *intel_encoder; u32 hotplug_en; assert_spin_locked(&dev_priv->irq_lock); @@ -3944,9 +3945,7 @@ static void i915_hpd_irq_setup(struct drm_device *dev) hotplug_en &= ~HOTPLUG_INT_EN_MASK; /* Note HDMI and DP share hotplug bits */ /* enable bits are the same for all generations */ - for_each_intel_encoder(dev, intel_encoder) - if (dev_priv->hotplug.stats[intel_encoder->hpd_pin].state == HPD_ENABLED) - hotplug_en |= hpd_mask_i915[intel_encoder->hpd_pin]; + hotplug_en |= intel_hpd_enabled_irqs(dev, hpd_mask_i915); /* Programming the CRT detection parameters tends to generate a spurious hotplug event about three seconds later. So just do it once. From d9dc34f1a1e9649f6e1e3709f6177f7883ce75ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 27 Aug 2015 23:55:58 +0300 Subject: [PATCH 103/145] drm/i915: Factor out ilk_update_display_irq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the core of ironlake_{enable,disable}_display_irq() into a new function. We'll have further use for it later. v2: Warn about invalid mask vs. enable bits (Paulo) Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 41 +++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index de0edbdb05ed..baff96333498 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -154,35 +154,46 @@ static const u32 hpd_bxt[HPD_NUM_PINS] = { static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir); -/* For display hotplug interrupt */ -void -ironlake_enable_display_irq(struct drm_i915_private *dev_priv, u32 mask) +/** + * ilk_update_display_irq - update DEIMR + * @dev_priv: driver private + * @interrupt_mask: mask of interrupt bits to update + * @enabled_irq_mask: mask of interrupt bits to enable + */ +static void ilk_update_display_irq(struct drm_i915_private *dev_priv, + uint32_t interrupt_mask, + uint32_t enabled_irq_mask) { + uint32_t new_val; + assert_spin_locked(&dev_priv->irq_lock); + WARN_ON(enabled_irq_mask & ~interrupt_mask); + if (WARN_ON(!intel_irqs_enabled(dev_priv))) return; - if ((dev_priv->irq_mask & mask) != 0) { - dev_priv->irq_mask &= ~mask; + new_val = dev_priv->irq_mask; + new_val &= ~interrupt_mask; + new_val |= (~enabled_irq_mask & interrupt_mask); + + if (new_val != dev_priv->irq_mask) { + dev_priv->irq_mask = new_val; I915_WRITE(DEIMR, dev_priv->irq_mask); POSTING_READ(DEIMR); } } +void +ironlake_enable_display_irq(struct drm_i915_private *dev_priv, u32 mask) +{ + ilk_update_display_irq(dev_priv, mask, mask); +} + void ironlake_disable_display_irq(struct drm_i915_private *dev_priv, u32 mask) { - assert_spin_locked(&dev_priv->irq_lock); - - if (WARN_ON(!intel_irqs_enabled(dev_priv))) - return; - - if ((dev_priv->irq_mask & mask) != mask) { - dev_priv->irq_mask |= mask; - I915_WRITE(DEIMR, dev_priv->irq_mask); - POSTING_READ(DEIMR); - } + ilk_update_display_irq(dev_priv, mask, 0); } /** From c2699524d666bd606a2de0aa68691c984efb1fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 27 Aug 2015 23:55:59 +0300 Subject: [PATCH 104/145] drm/i915: Add HAS_PCH_LPT_LP() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make LPT:LP checks look neater by wrapping the details in a new HAS_PCH_LPT_LP() macro. Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/intel_display.c | 13 +++++-------- drivers/gpu/drm/i915/intel_pm.c | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4eabe19a684f..482ddb745ef8 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2599,6 +2599,7 @@ struct drm_i915_cmd_table { #define INTEL_PCH_TYPE(dev) (__I915__(dev)->pch_type) #define HAS_PCH_SPT(dev) (INTEL_PCH_TYPE(dev) == PCH_SPT) #define HAS_PCH_LPT(dev) (INTEL_PCH_TYPE(dev) == PCH_LPT) +#define HAS_PCH_LPT_LP(dev) (__I915__(dev)->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) #define HAS_PCH_CPT(dev) (INTEL_PCH_TYPE(dev) == PCH_CPT) #define HAS_PCH_IBX(dev) (INTEL_PCH_TYPE(dev) == PCH_IBX) #define HAS_PCH_NOP(dev) (INTEL_PCH_TYPE(dev) == PCH_NOP) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ae485e717c57..888175c353f1 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8359,8 +8359,7 @@ static void lpt_enable_clkout_dp(struct drm_device *dev, bool with_spread, if (WARN(with_fdi && !with_spread, "FDI requires downspread\n")) with_spread = true; - if (WARN(dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE && - with_fdi, "LP PCH doesn't have FDI\n")) + if (WARN(HAS_PCH_LPT_LP(dev) && with_fdi, "LP PCH doesn't have FDI\n")) with_fdi = false; mutex_lock(&dev_priv->sb_lock); @@ -8383,8 +8382,7 @@ static void lpt_enable_clkout_dp(struct drm_device *dev, bool with_spread, } } - reg = (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) ? - SBI_GEN0 : SBI_DBUFF0; + reg = HAS_PCH_LPT_LP(dev) ? SBI_GEN0 : SBI_DBUFF0; tmp = intel_sbi_read(dev_priv, reg, SBI_ICLK); tmp |= SBI_GEN0_CFG_BUFFENABLE_DISABLE; intel_sbi_write(dev_priv, reg, tmp, SBI_ICLK); @@ -8400,8 +8398,7 @@ static void lpt_disable_clkout_dp(struct drm_device *dev) mutex_lock(&dev_priv->sb_lock); - reg = (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) ? - SBI_GEN0 : SBI_DBUFF0; + reg = HAS_PCH_LPT_LP(dev) ? SBI_GEN0 : SBI_DBUFF0; tmp = intel_sbi_read(dev_priv, reg, SBI_ICLK); tmp &= ~SBI_GEN0_CFG_BUFFENABLE_DISABLE; intel_sbi_write(dev_priv, reg, tmp, SBI_ICLK); @@ -9413,7 +9410,7 @@ void hsw_enable_pc8(struct drm_i915_private *dev_priv) DRM_DEBUG_KMS("Enabling package C8+\n"); - if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) { + if (HAS_PCH_LPT_LP(dev)) { val = I915_READ(SOUTH_DSPCLK_GATE_D); val &= ~PCH_LP_PARTITION_LEVEL_DISABLE; I915_WRITE(SOUTH_DSPCLK_GATE_D, val); @@ -9433,7 +9430,7 @@ void hsw_disable_pc8(struct drm_i915_private *dev_priv) hsw_restore_lcpll(dev_priv); lpt_init_pch_refclk(dev); - if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) { + if (HAS_PCH_LPT_LP(dev)) { val = I915_READ(SOUTH_DSPCLK_GATE_D); val |= PCH_LP_PARTITION_LEVEL_DISABLE; I915_WRITE(SOUTH_DSPCLK_GATE_D, val); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index fff0c22682ee..ea4966144650 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6588,7 +6588,7 @@ static void lpt_init_clock_gating(struct drm_device *dev) * TODO: this bit should only be enabled when really needed, then * disabled when not needed anymore in order to save power. */ - if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) + if (HAS_PCH_LPT_LP(dev)) I915_WRITE(SOUTH_DSPCLK_GATE_D, I915_READ(SOUTH_DSPCLK_GATE_D) | PCH_LP_PARTITION_LEVEL_DISABLE); @@ -6603,7 +6603,7 @@ static void lpt_suspend_hw(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) { + if (HAS_PCH_LPT_LP(dev)) { uint32_t val = I915_READ(SOUTH_DSPCLK_GATE_D); val &= ~PCH_LP_PARTITION_LEVEL_DISABLE; From 195baa0673345c70f04d19e9e18470c9cbf88bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 27 Aug 2015 23:56:00 +0300 Subject: [PATCH 105/145] drm/i915: Rename BXT PORTA HPD defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PORTA HPD defines are not BXT specific. They also exist on SPT, and partially already on LPT:LP. Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 2 +- drivers/gpu/drm/i915/i915_reg.h | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index baff96333498..3388b6475058 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1250,7 +1250,7 @@ static bool bxt_port_hotplug_long_detect(enum port port, u32 val) { switch (port) { case PORT_A: - return val & BXT_PORTA_HOTPLUG_LONG_DETECT; + return val & PORTA_HOTPLUG_LONG_DETECT; case PORT_B: return val & PORTB_HOTPLUG_LONG_DETECT; case PORT_C: diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index e36baca48ab8..7eb76b781996 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -6033,11 +6033,11 @@ enum skl_disp_power_wells { /* digital port hotplug */ #define PCH_PORT_HOTPLUG 0xc4030 /* SHOTPLUG_CTL */ -#define BXT_PORTA_HOTPLUG_ENABLE (1 << 28) -#define BXT_PORTA_HOTPLUG_STATUS_MASK (3 << 24) -#define BXT_PORTA_HOTPLUG_NO_DETECT (0 << 24) -#define BXT_PORTA_HOTPLUG_SHORT_DETECT (1 << 24) -#define BXT_PORTA_HOTPLUG_LONG_DETECT (2 << 24) +#define PORTA_HOTPLUG_ENABLE (1 << 28) /* LPT:LP+ & BXT */ +#define PORTA_HOTPLUG_STATUS_MASK (3 << 24) /* SPT+ & BXT */ +#define PORTA_HOTPLUG_NO_DETECT (0 << 24) /* SPT+ & BXT */ +#define PORTA_HOTPLUG_SHORT_DETECT (1 << 24) /* SPT+ & BXT */ +#define PORTA_HOTPLUG_LONG_DETECT (2 << 24) /* SPT+ & BXT */ #define PORTD_HOTPLUG_ENABLE (1 << 20) #define PORTD_PULSE_DURATION_2ms (0 << 18) /* pre-LPT */ #define PORTD_PULSE_DURATION_4_5ms (1 << 18) /* pre-LPT */ From 42db67d664af417e946211c7f1f4178c782a582a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 28 Aug 2015 21:26:27 +0300 Subject: [PATCH 106/145] drm/i915: Move {pin, long}_mask initialization to caller from intel_get_hpd_pins() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the 0 initialization of pin_mask and long_mask from intel_get_hpd_pins() into each caller. This we we can call intel_get_hpd_pins() multiple times to accumulate more pins from several sources. v2: Add a comment explaining the dangers of intel_get_hpd_pins() (Paulo) Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 3388b6475058..71293893482a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1292,7 +1292,13 @@ static bool i9xx_port_hotplug_long_detect(enum port port, u32 val) } } -/* Get a bit mask of pins that have triggered, and which ones may be long. */ +/* + * Get a bit mask of pins that have triggered, and which ones may be long. + * This can be called multiple times with the same masks to accumulate + * hotplug detection results from several registers. + * + * Note that the caller is expected to zero out the masks initially. + */ static void intel_get_hpd_pins(u32 *pin_mask, u32 *long_mask, u32 hotplug_trigger, u32 dig_hotplug_reg, const u32 hpd[HPD_NUM_PINS], @@ -1301,9 +1307,6 @@ static void intel_get_hpd_pins(u32 *pin_mask, u32 *long_mask, enum port port; int i; - *pin_mask = 0; - *long_mask = 0; - for_each_hpd_pin(i) { if ((hpd[i] & hotplug_trigger) == 0) continue; @@ -1544,7 +1547,7 @@ static void i9xx_hpd_irq_handler(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT); - u32 pin_mask, long_mask; + u32 pin_mask = 0, long_mask = 0; if (!hotplug_status) return; @@ -1673,7 +1676,7 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK; if (hotplug_trigger) { - u32 dig_hotplug_reg, pin_mask, long_mask; + u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG); I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg); @@ -1781,7 +1784,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT; if (hotplug_trigger) { - u32 dig_hotplug_reg, pin_mask, long_mask; + u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG); I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg); @@ -2004,7 +2007,7 @@ static void bxt_hpd_handler(struct drm_device *dev, uint32_t iir_status) { struct drm_i915_private *dev_priv = dev->dev_private; u32 hp_control, hp_trigger; - u32 pin_mask, long_mask; + u32 pin_mask = 0, long_mask = 0; /* Get the status */ hp_trigger = iir_status & BXT_DE_PORT_HOTPLUG_MASK; From 6dbf30ce234925e4770f0b52950b601e816b6586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 27 Aug 2015 23:56:02 +0300 Subject: [PATCH 107/145] drm/i915: Introduce spt_irq_handler() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Starting from SPT the only interrupts living in the south are GMBUS and HPD. What's worse some of the SPT specific new bits conflict with some other bits on earlier PCH generations. So better not use the cpt_irq_handler() for SPT+ anymore. Also kill the hand rolled port E handling with something more standardish. This also avoids accidentally confusing port B and port E long pulses since the bits occupy the same positions, just in different registers. Also add a comment noting that the short pulse duration bits are reserved on LPT+. The 2ms value we program is 0, so no issue wrt. the MBZ in the spec. v2: Call intel_hpd_irq_handler() only once (Paulo) Signed-off-by: Ville Syrjälä Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 124 +++++++++++++++++++++----------- 1 file changed, 84 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 71293893482a..755d9ceb26a2 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1262,6 +1262,16 @@ static bool bxt_port_hotplug_long_detect(enum port port, u32 val) } } +static bool spt_port_hotplug2_long_detect(enum port port, u32 val) +{ + switch (port) { + case PORT_E: + return val & PORTE_HOTPLUG_LONG_DETECT; + default: + return false; + } +} + static bool pch_port_hotplug_long_detect(enum port port, u32 val) { switch (port) { @@ -1271,8 +1281,6 @@ static bool pch_port_hotplug_long_detect(enum port port, u32 val) return val & PORTC_HOTPLUG_LONG_DETECT; case PORT_D: return val & PORTD_HOTPLUG_LONG_DETECT; - case PORT_E: - return val & PORTE_HOTPLUG_LONG_DETECT; default: return false; } @@ -1776,12 +1784,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) { struct drm_i915_private *dev_priv = dev->dev_private; int pipe; - u32 hotplug_trigger; - - if (HAS_PCH_SPT(dev)) - hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_SPT; - else - hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT; + u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT; if (hotplug_trigger) { u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; @@ -1789,22 +1792,10 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG); I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg); - if (HAS_PCH_SPT(dev)) { - intel_get_hpd_pins(&pin_mask, &long_mask, - hotplug_trigger, - dig_hotplug_reg, hpd_spt, - pch_port_hotplug_long_detect); - - /* detect PORTE HP event */ - dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG2); - if (pch_port_hotplug_long_detect(PORT_E, - dig_hotplug_reg)) - long_mask |= 1 << HPD_PORT_E; - } else - intel_get_hpd_pins(&pin_mask, &long_mask, - hotplug_trigger, - dig_hotplug_reg, hpd_cpt, - pch_port_hotplug_long_detect); + intel_get_hpd_pins(&pin_mask, &long_mask, + hotplug_trigger, + dig_hotplug_reg, hpd_cpt, + pch_port_hotplug_long_detect); intel_hpd_irq_handler(dev, pin_mask, long_mask); } @@ -1838,6 +1829,43 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) cpt_serr_int_handler(dev); } +static void spt_irq_handler(struct drm_device *dev, u32 pch_iir) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_SPT & + ~SDE_PORTE_HOTPLUG_SPT; + u32 hotplug2_trigger = pch_iir & SDE_PORTE_HOTPLUG_SPT; + u32 pin_mask = 0, long_mask = 0; + + if (hotplug_trigger) { + u32 dig_hotplug_reg; + + dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG); + I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg); + + intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, + dig_hotplug_reg, hpd_spt, + pch_port_hotplug_long_detect); + } + + if (hotplug2_trigger) { + u32 dig_hotplug_reg; + + dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG2); + I915_WRITE(PCH_PORT_HOTPLUG2, dig_hotplug_reg); + + intel_get_hpd_pins(&pin_mask, &long_mask, hotplug2_trigger, + dig_hotplug_reg, hpd_spt, + spt_port_hotplug2_long_detect); + } + + if (pin_mask) + intel_hpd_irq_handler(dev, pin_mask, long_mask); + + if (pch_iir & SDE_GMBUS_CPT) + gmbus_irq_handler(dev); +} + static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2156,7 +2184,11 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) if (pch_iir) { I915_WRITE(SDEIIR, pch_iir); ret = IRQ_HANDLED; - cpt_irq_handler(dev, pch_iir); + + if (HAS_PCH_SPT(dev_priv)) + spt_irq_handler(dev, pch_iir); + else + cpt_irq_handler(dev, pch_iir); } else DRM_ERROR("The master control interrupt lied (SDE)!\n"); @@ -3038,9 +3070,6 @@ static void ibx_hpd_irq_setup(struct drm_device *dev) if (HAS_PCH_IBX(dev)) { hotplug_irqs = SDE_HOTPLUG_MASK; enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_ibx); - } else if (HAS_PCH_SPT(dev)) { - hotplug_irqs = SDE_HOTPLUG_MASK_SPT; - enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_spt); } else { hotplug_irqs = SDE_HOTPLUG_MASK_CPT; enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_cpt); @@ -3050,9 +3079,8 @@ static void ibx_hpd_irq_setup(struct drm_device *dev) /* * Enable digital hotplug on the PCH, and configure the DP short pulse - * duration to 2ms (which is the minimum in the Display Port spec) - * - * This register is the same on all known PCH chips. + * duration to 2ms (which is the minimum in the Display Port spec). + * The pulse duration bits are reserved on LPT+. */ hotplug = I915_READ(PCH_PORT_HOTPLUG); hotplug &= ~(PORTD_PULSE_DURATION_MASK|PORTC_PULSE_DURATION_MASK|PORTB_PULSE_DURATION_MASK); @@ -3060,13 +3088,27 @@ static void ibx_hpd_irq_setup(struct drm_device *dev) hotplug |= PORTC_HOTPLUG_ENABLE | PORTC_PULSE_DURATION_2ms; hotplug |= PORTB_HOTPLUG_ENABLE | PORTB_PULSE_DURATION_2ms; I915_WRITE(PCH_PORT_HOTPLUG, hotplug); +} - /* enable SPT PORTE hot plug */ - if (HAS_PCH_SPT(dev)) { - hotplug = I915_READ(PCH_PORT_HOTPLUG2); - hotplug |= PORTE_HOTPLUG_ENABLE; - I915_WRITE(PCH_PORT_HOTPLUG2, hotplug); - } +static void spt_hpd_irq_setup(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 hotplug_irqs, hotplug, enabled_irqs; + + hotplug_irqs = SDE_HOTPLUG_MASK_SPT; + enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_spt); + + ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs); + + /* Enable digital hotplug on the PCH */ + hotplug = I915_READ(PCH_PORT_HOTPLUG); + hotplug |= PORTD_HOTPLUG_ENABLE | PORTC_HOTPLUG_ENABLE | + PORTB_HOTPLUG_ENABLE; + I915_WRITE(PCH_PORT_HOTPLUG, hotplug); + + hotplug = I915_READ(PCH_PORT_HOTPLUG2); + hotplug |= PORTE_HOTPLUG_ENABLE; + I915_WRITE(PCH_PORT_HOTPLUG2, hotplug); } static void bxt_hpd_irq_setup(struct drm_device *dev) @@ -4171,10 +4213,12 @@ void intel_irq_init(struct drm_i915_private *dev_priv) dev->driver->irq_uninstall = gen8_irq_uninstall; dev->driver->enable_vblank = gen8_enable_vblank; dev->driver->disable_vblank = gen8_disable_vblank; - if (HAS_PCH_SPLIT(dev)) - dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup; - else + if (IS_BROXTON(dev)) dev_priv->display.hpd_irq_setup = bxt_hpd_irq_setup; + else if (HAS_PCH_SPT(dev)) + dev_priv->display.hpd_irq_setup = spt_hpd_irq_setup; + else + dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup; } else if (HAS_PCH_SPLIT(dev)) { dev->driver->irq_handler = ironlake_irq_handler; dev->driver->irq_preinstall = ironlake_irq_reset; From e4ce95aa661e411d7da554eb8e6c7f81504acae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 27 Aug 2015 23:56:03 +0300 Subject: [PATCH 108/145] drm/i915: Add port A HPD support for ILK/SNB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ILK/SNB support port A HPD. While HPD is optional on eDP let's at least try to wite it up so that we might notice if the link has issues. The eDP spec suggests that if HPD is not wired up, one should poll the link status instead. We don't even do that currently. v2: 0 initialize pin_mask/long_mask due to intel_get_hpd_pins() changes Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 59 +++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 755d9ceb26a2..57d23f0163eb 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -45,6 +45,10 @@ * and related files, but that will be described in separate chapters. */ +static const u32 hpd_ilk[HPD_NUM_PINS] = { + [HPD_PORT_A] = DE_DP_A_HOTPLUG, +}; + static const u32 hpd_ibx[HPD_NUM_PINS] = { [HPD_CRT] = SDE_CRT_HOTPLUG, [HPD_SDVO_B] = SDE_SDVOB_HOTPLUG, @@ -1272,6 +1276,16 @@ static bool spt_port_hotplug2_long_detect(enum port port, u32 val) } } +static bool ilk_port_hotplug_long_detect(enum port port, u32 val) +{ + switch (port) { + case PORT_A: + return val & DIGITAL_PORTA_HOTPLUG_LONG_DETECT; + default: + return false; + } +} + static bool pch_port_hotplug_long_detect(enum port port, u32 val) { switch (port) { @@ -1870,6 +1884,19 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir) { struct drm_i915_private *dev_priv = dev->dev_private; enum pipe pipe; + u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG; + + if (hotplug_trigger) { + u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; + + dig_hotplug_reg = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL); + I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, dig_hotplug_reg); + + intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, + dig_hotplug_reg, hpd_ilk, + ilk_port_hotplug_long_detect); + intel_hpd_irq_handler(dev, pin_mask, long_mask); + } if (de_iir & DE_AUX_CHANNEL_A) dp_aux_irq_handler(dev); @@ -3111,6 +3138,28 @@ static void spt_hpd_irq_setup(struct drm_device *dev) I915_WRITE(PCH_PORT_HOTPLUG2, hotplug); } +static void ilk_hpd_irq_setup(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 hotplug_irqs, hotplug, enabled_irqs; + + hotplug_irqs = DE_DP_A_HOTPLUG; + enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_ilk); + + ilk_update_display_irq(dev_priv, hotplug_irqs, enabled_irqs); + + /* + * Enable digital hotplug on the CPU, and configure the DP short pulse + * duration to 2ms (which is the minimum in the Display Port spec) + */ + hotplug = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL); + hotplug &= ~DIGITAL_PORTA_PULSE_DURATION_MASK; + hotplug |= DIGITAL_PORTA_HOTPLUG_ENABLE | DIGITAL_PORTA_PULSE_DURATION_2ms; + I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, hotplug); + + ibx_hpd_irq_setup(dev); +} + static void bxt_hpd_irq_setup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -3209,8 +3258,9 @@ static int ironlake_irq_postinstall(struct drm_device *dev) DE_AUX_CHANNEL_A | DE_PIPEB_CRC_DONE | DE_PIPEA_CRC_DONE | DE_POISON); - extra_mask = DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT | - DE_PIPEB_FIFO_UNDERRUN | DE_PIPEA_FIFO_UNDERRUN; + extra_mask = (DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT | + DE_PIPEB_FIFO_UNDERRUN | DE_PIPEA_FIFO_UNDERRUN | + DE_DP_A_HOTPLUG); } dev_priv->irq_mask = ~display_mask; @@ -4226,7 +4276,10 @@ void intel_irq_init(struct drm_i915_private *dev_priv) dev->driver->irq_uninstall = ironlake_irq_uninstall; dev->driver->enable_vblank = ironlake_enable_vblank; dev->driver->disable_vblank = ironlake_disable_vblank; - dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup; + if (INTEL_INFO(dev)->gen >= 7) + dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup; + else + dev_priv->display.hpd_irq_setup = ilk_hpd_irq_setup; } else { if (INTEL_INFO(dev_priv)->gen == 2) { dev->driver->irq_preinstall = i8xx_irq_preinstall; From 23bb4cb5122c2c6379be38aaa5c0fd6786ae6c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 27 Aug 2015 23:56:04 +0300 Subject: [PATCH 109/145] drm/i915: Add port A HPD support for IVB/HSW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As with ILK/SNB wire up the port A HPD on IVB/HSW. This might be more important on HSW with PSR. BSpec tells us that if the automagic link training performed by the hardware fails for some reason, we're going to get a short HPD and are supposed to re-train the link manyally. v2: 0 initialize pin_mask/long_mask due to intel_get_hpd_pins() changes Add a comment about the pulse duration bits being reserved on HSW+ like we have for LPT+ in ibx_hpd_irq_setup() Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 35 ++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 57d23f0163eb..8864aaf9d30a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -49,6 +49,10 @@ static const u32 hpd_ilk[HPD_NUM_PINS] = { [HPD_PORT_A] = DE_DP_A_HOTPLUG, }; +static const u32 hpd_ivb[HPD_NUM_PINS] = { + [HPD_PORT_A] = DE_DP_A_HOTPLUG_IVB, +}; + static const u32 hpd_ibx[HPD_NUM_PINS] = { [HPD_CRT] = SDE_CRT_HOTPLUG, [HPD_SDVO_B] = SDE_SDVOB_HOTPLUG, @@ -1946,6 +1950,19 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir) { struct drm_i915_private *dev_priv = dev->dev_private; enum pipe pipe; + u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG_IVB; + + if (hotplug_trigger) { + u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; + + dig_hotplug_reg = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL); + I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, dig_hotplug_reg); + + intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, + dig_hotplug_reg, hpd_ivb, + ilk_port_hotplug_long_detect); + intel_hpd_irq_handler(dev, pin_mask, long_mask); + } if (de_iir & DE_ERR_INT_IVB) ivb_err_int_handler(dev); @@ -3143,14 +3160,20 @@ static void ilk_hpd_irq_setup(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; u32 hotplug_irqs, hotplug, enabled_irqs; - hotplug_irqs = DE_DP_A_HOTPLUG; - enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_ilk); + if (INTEL_INFO(dev)->gen >= 7) { + hotplug_irqs = DE_DP_A_HOTPLUG_IVB; + enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_ivb); + } else { + hotplug_irqs = DE_DP_A_HOTPLUG; + enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_ilk); + } ilk_update_display_irq(dev_priv, hotplug_irqs, enabled_irqs); /* * Enable digital hotplug on the CPU, and configure the DP short pulse * duration to 2ms (which is the minimum in the Display Port spec) + * The pulse duration bits are reserved on HSW+. */ hotplug = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL); hotplug &= ~DIGITAL_PORTA_PULSE_DURATION_MASK; @@ -3251,7 +3274,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev) DE_PLANEB_FLIP_DONE_IVB | DE_PLANEA_FLIP_DONE_IVB | DE_AUX_CHANNEL_A_IVB); extra_mask = (DE_PIPEC_VBLANK_IVB | DE_PIPEB_VBLANK_IVB | - DE_PIPEA_VBLANK_IVB | DE_ERR_INT_IVB); + DE_PIPEA_VBLANK_IVB | DE_ERR_INT_IVB | + DE_DP_A_HOTPLUG_IVB); } else { display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT | DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE | @@ -4276,10 +4300,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv) dev->driver->irq_uninstall = ironlake_irq_uninstall; dev->driver->enable_vblank = ironlake_enable_vblank; dev->driver->disable_vblank = ironlake_disable_vblank; - if (INTEL_INFO(dev)->gen >= 7) - dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup; - else - dev_priv->display.hpd_irq_setup = ilk_hpd_irq_setup; + dev_priv->display.hpd_irq_setup = ilk_hpd_irq_setup; } else { if (INTEL_INFO(dev_priv)->gen == 2) { dev->driver->irq_preinstall = i8xx_irq_preinstall; From 0b2eb33e5e2b4aaeaa786d551a1b5f08904a9bfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 27 Aug 2015 23:56:05 +0300 Subject: [PATCH 110/145] drm/i915: LPT:LP needs port A HPD enabled in both north and south MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the CPU and PCH are on the same package we must enabled the port A HPD also in the south hotplug register. To identify the package type we simply look at the PCH type: LPT-H means separate package, and LPT-LP means multi chip package (MCP). v2: Add comment and pimp commit message Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 8864aaf9d30a..0aa87d03a619 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -3131,6 +3131,12 @@ static void ibx_hpd_irq_setup(struct drm_device *dev) hotplug |= PORTD_HOTPLUG_ENABLE | PORTD_PULSE_DURATION_2ms; hotplug |= PORTC_HOTPLUG_ENABLE | PORTC_PULSE_DURATION_2ms; hotplug |= PORTB_HOTPLUG_ENABLE | PORTB_PULSE_DURATION_2ms; + /* + * When CPU and PCH are on the same package, port A + * HPD must be enabled in both north and south. + */ + if (HAS_PCH_LPT_LP(dev)) + hotplug |= PORTA_HOTPLUG_ENABLE; I915_WRITE(PCH_PORT_HOTPLUG, hotplug); } From 3a3b3c7da37b35955c7c6a36a1f059d02f17eafb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 27 Aug 2015 23:56:06 +0300 Subject: [PATCH 111/145] drm/i915: Add port A HPD support for BDW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire up the port A HPD for BDW. Compared to earlier platforms the interrupt setup is a bit different, but basically everything else looks the same. v2: 0 initialize pin_mask/long_mask due to intel_get_hpd_pins() changes Check for BDW before processing the HPD to not break BXT Set found=true when processing port A HPD Sort out the mess I made of the irq setup in v1 Warn about bad irq mask vs. enable bits in bdw_update_port_irq() (Paulo) Signed-off-by: Ville Syrjälä Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 86 ++++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 0aa87d03a619..03c79f6d46fc 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -53,6 +53,10 @@ static const u32 hpd_ivb[HPD_NUM_PINS] = { [HPD_PORT_A] = DE_DP_A_HOTPLUG_IVB, }; +static const u32 hpd_bdw[HPD_NUM_PINS] = { + [HPD_PORT_A] = GEN8_PORT_DP_A_HOTPLUG, +}; + static const u32 hpd_ibx[HPD_NUM_PINS] = { [HPD_CRT] = SDE_CRT_HOTPLUG, [HPD_SDVO_B] = SDE_SDVOB_HOTPLUG, @@ -370,6 +374,38 @@ void gen6_disable_rps_interrupts(struct drm_device *dev) synchronize_irq(dev->irq); } +/** + * bdw_update_port_irq - update DE port interrupt + * @dev_priv: driver private + * @interrupt_mask: mask of interrupt bits to update + * @enabled_irq_mask: mask of interrupt bits to enable + */ +static void bdw_update_port_irq(struct drm_i915_private *dev_priv, + uint32_t interrupt_mask, + uint32_t enabled_irq_mask) +{ + uint32_t new_val; + uint32_t old_val; + + assert_spin_locked(&dev_priv->irq_lock); + + WARN_ON(enabled_irq_mask & ~interrupt_mask); + + if (WARN_ON(!intel_irqs_enabled(dev_priv))) + return; + + old_val = I915_READ(GEN8_DE_PORT_IMR); + + new_val = old_val; + new_val &= ~interrupt_mask; + new_val |= (~enabled_irq_mask & interrupt_mask); + + if (new_val != old_val) { + I915_WRITE(GEN8_DE_PORT_IMR, new_val); + POSTING_READ(GEN8_DE_PORT_IMR); + } +} + /** * ibx_display_interrupt_update - update SDEIMR * @dev_priv: driver private @@ -2145,10 +2181,24 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) tmp = I915_READ(GEN8_DE_PORT_IIR); if (tmp) { bool found = false; + u32 hotplug_trigger = tmp & GEN8_PORT_DP_A_HOTPLUG; I915_WRITE(GEN8_DE_PORT_IIR, tmp); ret = IRQ_HANDLED; + if (IS_BROADWELL(dev) && hotplug_trigger) { + u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; + + dig_hotplug_reg = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL); + I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, dig_hotplug_reg); + + intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, + dig_hotplug_reg, hpd_bdw, + ilk_port_hotplug_long_detect); + intel_hpd_irq_handler(dev, pin_mask, long_mask); + found = true; + } + if (tmp & aux_mask) { dp_aux_irq_handler(dev); found = true; @@ -3166,15 +3216,22 @@ static void ilk_hpd_irq_setup(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; u32 hotplug_irqs, hotplug, enabled_irqs; - if (INTEL_INFO(dev)->gen >= 7) { + if (INTEL_INFO(dev)->gen >= 8) { + hotplug_irqs = GEN8_PORT_DP_A_HOTPLUG; + enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_bdw); + + bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs); + } else if (INTEL_INFO(dev)->gen >= 7) { hotplug_irqs = DE_DP_A_HOTPLUG_IVB; enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_ivb); + + ilk_update_display_irq(dev_priv, hotplug_irqs, enabled_irqs); } else { hotplug_irqs = DE_DP_A_HOTPLUG; enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_ilk); - } - ilk_update_display_irq(dev_priv, hotplug_irqs, enabled_irqs); + ilk_update_display_irq(dev_priv, hotplug_irqs, enabled_irqs); + } /* * Enable digital hotplug on the CPU, and configure the DP short pulse @@ -3486,24 +3543,29 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) { uint32_t de_pipe_masked = GEN8_PIPE_CDCLK_CRC_DONE; uint32_t de_pipe_enables; - int pipe; - u32 de_port_en = GEN8_AUX_CHANNEL_A; + u32 de_port_masked = GEN8_AUX_CHANNEL_A; + u32 de_port_enables; + enum pipe pipe; if (IS_GEN9(dev_priv)) { de_pipe_masked |= GEN9_PIPE_PLANE1_FLIP_DONE | GEN9_DE_PIPE_IRQ_FAULT_ERRORS; - de_port_en |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C | - GEN9_AUX_CHANNEL_D; - + de_port_masked |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C | + GEN9_AUX_CHANNEL_D; if (IS_BROXTON(dev_priv)) - de_port_en |= BXT_DE_PORT_GMBUS; - } else + de_port_masked |= BXT_DE_PORT_GMBUS; + } else { de_pipe_masked |= GEN8_PIPE_PRIMARY_FLIP_DONE | GEN8_DE_PIPE_IRQ_FAULT_ERRORS; + } de_pipe_enables = de_pipe_masked | GEN8_PIPE_VBLANK | GEN8_PIPE_FIFO_UNDERRUN; + de_port_enables = de_port_masked; + if (IS_BROADWELL(dev_priv)) + de_port_enables |= GEN8_PORT_DP_A_HOTPLUG; + dev_priv->de_irq_mask[PIPE_A] = ~de_pipe_masked; dev_priv->de_irq_mask[PIPE_B] = ~de_pipe_masked; dev_priv->de_irq_mask[PIPE_C] = ~de_pipe_masked; @@ -3515,7 +3577,7 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) dev_priv->de_irq_mask[pipe], de_pipe_enables); - GEN5_IRQ_INIT(GEN8_DE_PORT_, ~de_port_en, de_port_en); + GEN5_IRQ_INIT(GEN8_DE_PORT_, ~de_port_masked, de_port_enables); } static int gen8_irq_postinstall(struct drm_device *dev) @@ -4298,7 +4360,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv) else if (HAS_PCH_SPT(dev)) dev_priv->display.hpd_irq_setup = spt_hpd_irq_setup; else - dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup; + dev_priv->display.hpd_irq_setup = ilk_hpd_irq_setup; } else if (HAS_PCH_SPLIT(dev)) { dev->driver->irq_handler = ironlake_irq_handler; dev->driver->irq_preinstall = ironlake_irq_reset; From 74c0b395fce4c807a49c404fb59462b22069b001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 27 Aug 2015 23:56:07 +0300 Subject: [PATCH 112/145] drm/i915: Add port A HPD support for SPT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On SKL the port A HPD has moved to the PCH. Hook it up. Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 21 +++++++++++++++++++-- drivers/gpu/drm/i915/i915_reg.h | 4 +++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 03c79f6d46fc..c3edcce648e2 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -74,6 +74,7 @@ static const u32 hpd_cpt[HPD_NUM_PINS] = { }; static const u32 hpd_spt[HPD_NUM_PINS] = { + [HPD_PORT_A] = SDE_PORTA_HOTPLUG_SPT, [HPD_PORT_B] = SDE_PORTB_HOTPLUG_CPT, [HPD_PORT_C] = SDE_PORTC_HOTPLUG_CPT, [HPD_PORT_D] = SDE_PORTD_HOTPLUG_CPT, @@ -1316,6 +1317,22 @@ static bool spt_port_hotplug2_long_detect(enum port port, u32 val) } } +static bool spt_port_hotplug_long_detect(enum port port, u32 val) +{ + switch (port) { + case PORT_A: + return val & PORTA_HOTPLUG_LONG_DETECT; + case PORT_B: + return val & PORTB_HOTPLUG_LONG_DETECT; + case PORT_C: + return val & PORTC_HOTPLUG_LONG_DETECT; + case PORT_D: + return val & PORTD_HOTPLUG_LONG_DETECT; + default: + return false; + } +} + static bool ilk_port_hotplug_long_detect(enum port port, u32 val) { switch (port) { @@ -1899,7 +1916,7 @@ static void spt_irq_handler(struct drm_device *dev, u32 pch_iir) intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, dig_hotplug_reg, hpd_spt, - pch_port_hotplug_long_detect); + spt_port_hotplug_long_detect); } if (hotplug2_trigger) { @@ -3203,7 +3220,7 @@ static void spt_hpd_irq_setup(struct drm_device *dev) /* Enable digital hotplug on the PCH */ hotplug = I915_READ(PCH_PORT_HOTPLUG); hotplug |= PORTD_HOTPLUG_ENABLE | PORTC_HOTPLUG_ENABLE | - PORTB_HOTPLUG_ENABLE; + PORTB_HOTPLUG_ENABLE | PORTA_HOTPLUG_ENABLE; I915_WRITE(PCH_PORT_HOTPLUG, hotplug); hotplug = I915_READ(PCH_PORT_HOTPLUG2); diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 7eb76b781996..689b42813141 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -5984,6 +5984,7 @@ enum skl_disp_power_wells { #define SDE_AUXB_CPT (1 << 25) #define SDE_AUX_MASK_CPT (7 << 25) #define SDE_PORTE_HOTPLUG_SPT (1 << 25) +#define SDE_PORTA_HOTPLUG_SPT (1 << 24) #define SDE_PORTD_HOTPLUG_CPT (1 << 23) #define SDE_PORTC_HOTPLUG_CPT (1 << 22) #define SDE_PORTB_HOTPLUG_CPT (1 << 21) @@ -5997,7 +5998,8 @@ enum skl_disp_power_wells { #define SDE_HOTPLUG_MASK_SPT (SDE_PORTE_HOTPLUG_SPT | \ SDE_PORTD_HOTPLUG_CPT | \ SDE_PORTC_HOTPLUG_CPT | \ - SDE_PORTB_HOTPLUG_CPT) + SDE_PORTB_HOTPLUG_CPT | \ + SDE_PORTA_HOTPLUG_SPT) #define SDE_GMBUS_CPT (1 << 17) #define SDE_ERROR_CPT (1 << 16) #define SDE_AUDIO_CP_REQ_C_CPT (1 << 10) From 08d8a232aa5e145f995a98ac7f3de55e2fe6832b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 27 Aug 2015 23:56:08 +0300 Subject: [PATCH 113/145] drm/i915: Reinitialize HPD after runtime D3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Runtime suspends disabled all interrupts, so in order to get them back fully we need to also do the HPD irq setup on runtime resume. Except on VLV/CHV where the display interrupt initialization is part of the display power well powerup. Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 4737d15de5f0..bdec64ca6f13 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1555,6 +1555,15 @@ static int intel_runtime_resume(struct device *device) gen6_update_ring_freq(dev); intel_runtime_pm_enable_interrupts(dev_priv); + + /* + * On VLV/CHV display interrupts are part of the display + * power well, so hpd is reinitialized from there. For + * everyone else do it here. + */ + if (!IS_VALLEYVIEW(dev_priv)) + intel_hpd_init(dev_priv); + intel_enable_gt_powersave(dev); if (ret) From cebd87a07748b649f22356efd56f478248aec300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 27 Aug 2015 23:56:09 +0300 Subject: [PATCH 114/145] drm/i915: Rewrite bxt_hpd_handler() to look like everyone else MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bxt_hpd_handler() looks different to everyone else for no good reason. Rewrite it to use the standard variable namees etc. Signed-off-by: Ville Syrjälä Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 37 ++++++++++++++------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c3edcce648e2..f0dedfff1126 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2128,27 +2128,17 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) return ret; } -static void bxt_hpd_handler(struct drm_device *dev, uint32_t iir_status) +static void bxt_hpd_irq_handler(struct drm_device *dev, u32 hotplug_trigger) { - struct drm_i915_private *dev_priv = dev->dev_private; - u32 hp_control, hp_trigger; - u32 pin_mask = 0, long_mask = 0; + struct drm_i915_private *dev_priv = to_i915(dev); + u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; - /* Get the status */ - hp_trigger = iir_status & BXT_DE_PORT_HOTPLUG_MASK; - hp_control = I915_READ(BXT_HOTPLUG_CTL); + dig_hotplug_reg = I915_READ(BXT_HOTPLUG_CTL); + I915_WRITE(BXT_HOTPLUG_CTL, dig_hotplug_reg); - /* Hotplug not enabled ? */ - if (!(hp_control & BXT_HOTPLUG_CTL_MASK)) { - DRM_ERROR("Interrupt when HPD disabled\n"); - return; - } - - /* Clear sticky bits in hpd status */ - I915_WRITE(BXT_HOTPLUG_CTL, hp_control); - - intel_get_hpd_pins(&pin_mask, &long_mask, hp_trigger, hp_control, - hpd_bxt, bxt_port_hotplug_long_detect); + intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, + dig_hotplug_reg, hpd_bxt, + bxt_port_hotplug_long_detect); intel_hpd_irq_handler(dev, pin_mask, long_mask); } @@ -2198,7 +2188,12 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) tmp = I915_READ(GEN8_DE_PORT_IIR); if (tmp) { bool found = false; - u32 hotplug_trigger = tmp & GEN8_PORT_DP_A_HOTPLUG; + u32 hotplug_trigger = 0; + + if (IS_BROXTON(dev_priv)) + hotplug_trigger = tmp & BXT_DE_PORT_HOTPLUG_MASK; + else if (IS_BROADWELL(dev_priv)) + hotplug_trigger = tmp & GEN8_PORT_DP_A_HOTPLUG; I915_WRITE(GEN8_DE_PORT_IIR, tmp); ret = IRQ_HANDLED; @@ -2221,8 +2216,8 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) found = true; } - if (IS_BROXTON(dev) && tmp & BXT_DE_PORT_HOTPLUG_MASK) { - bxt_hpd_handler(dev, tmp); + if (IS_BROXTON(dev) && hotplug_trigger) { + bxt_hpd_irq_handler(dev, hotplug_trigger); found = true; } From 40e5641086f6f64acb13ff6893cf82c4fb30a6d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 27 Aug 2015 23:56:10 +0300 Subject: [PATCH 115/145] drm/i915: Refactor the hpd irq handling functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A lot of the hpd irq handling is duplicated code, so refactor it a bit by observing that in several places the only difference is the hpd[] array. So pull the code to a few functions and pass in the hpd[] array from the caller. Another option would be to determine the correct array to use within the functions themselves, but somehow passing it in felt nicer. Further code reduction could be achieved by passing in the hotplug register offset, and the long pulse detection function pointer. But that didn't feel as good for some reason, so I left it at the middle ground. Signed-off-by: Ville Syrjälä Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 112 ++++++++++++++------------------ 1 file changed, 49 insertions(+), 63 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index f0dedfff1126..d7a37a5cf93f 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1748,23 +1748,30 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg) return ret; } +static void ibx_hpd_irq_handler(struct drm_device *dev, u32 hotplug_trigger, + const u32 hpd[HPD_NUM_PINS]) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; + + dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG); + I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg); + + intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, + dig_hotplug_reg, hpd, + pch_port_hotplug_long_detect); + + intel_hpd_irq_handler(dev, pin_mask, long_mask); +} + static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) { struct drm_i915_private *dev_priv = dev->dev_private; int pipe; u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK; - if (hotplug_trigger) { - u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; - - dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG); - I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg); - - intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, - dig_hotplug_reg, hpd_ibx, - pch_port_hotplug_long_detect); - intel_hpd_irq_handler(dev, pin_mask, long_mask); - } + if (hotplug_trigger) + ibx_hpd_irq_handler(dev, hotplug_trigger, hpd_ibx); if (pch_iir & SDE_AUDIO_POWER_MASK) { int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK) >> @@ -1857,19 +1864,8 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) int pipe; u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT; - if (hotplug_trigger) { - u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; - - dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG); - I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg); - - intel_get_hpd_pins(&pin_mask, &long_mask, - hotplug_trigger, - dig_hotplug_reg, hpd_cpt, - pch_port_hotplug_long_detect); - - intel_hpd_irq_handler(dev, pin_mask, long_mask); - } + if (hotplug_trigger) + ibx_hpd_irq_handler(dev, hotplug_trigger, hpd_cpt); if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) { int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK_CPT) >> @@ -1937,23 +1933,30 @@ static void spt_irq_handler(struct drm_device *dev, u32 pch_iir) gmbus_irq_handler(dev); } +static void ilk_hpd_irq_handler(struct drm_device *dev, u32 hotplug_trigger, + const u32 hpd[HPD_NUM_PINS]) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; + + dig_hotplug_reg = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL); + I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, dig_hotplug_reg); + + intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, + dig_hotplug_reg, hpd, + ilk_port_hotplug_long_detect); + + intel_hpd_irq_handler(dev, pin_mask, long_mask); +} + static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir) { struct drm_i915_private *dev_priv = dev->dev_private; enum pipe pipe; u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG; - if (hotplug_trigger) { - u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; - - dig_hotplug_reg = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL); - I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, dig_hotplug_reg); - - intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, - dig_hotplug_reg, hpd_ilk, - ilk_port_hotplug_long_detect); - intel_hpd_irq_handler(dev, pin_mask, long_mask); - } + if (hotplug_trigger) + ilk_hpd_irq_handler(dev, hotplug_trigger, hpd_ilk); if (de_iir & DE_AUX_CHANNEL_A) dp_aux_irq_handler(dev); @@ -2005,17 +2008,8 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir) enum pipe pipe; u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG_IVB; - if (hotplug_trigger) { - u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; - - dig_hotplug_reg = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL); - I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, dig_hotplug_reg); - - intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, - dig_hotplug_reg, hpd_ivb, - ilk_port_hotplug_long_detect); - intel_hpd_irq_handler(dev, pin_mask, long_mask); - } + if (hotplug_trigger) + ilk_hpd_irq_handler(dev, hotplug_trigger, hpd_ivb); if (de_iir & DE_ERR_INT_IVB) ivb_err_int_handler(dev); @@ -2128,7 +2122,8 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) return ret; } -static void bxt_hpd_irq_handler(struct drm_device *dev, u32 hotplug_trigger) +static void bxt_hpd_irq_handler(struct drm_device *dev, u32 hotplug_trigger, + const u32 hpd[HPD_NUM_PINS]) { struct drm_i915_private *dev_priv = to_i915(dev); u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; @@ -2137,8 +2132,9 @@ static void bxt_hpd_irq_handler(struct drm_device *dev, u32 hotplug_trigger) I915_WRITE(BXT_HOTPLUG_CTL, dig_hotplug_reg); intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, - dig_hotplug_reg, hpd_bxt, + dig_hotplug_reg, hpd, bxt_port_hotplug_long_detect); + intel_hpd_irq_handler(dev, pin_mask, long_mask); } @@ -2198,26 +2194,16 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) I915_WRITE(GEN8_DE_PORT_IIR, tmp); ret = IRQ_HANDLED; - if (IS_BROADWELL(dev) && hotplug_trigger) { - u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; - - dig_hotplug_reg = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL); - I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, dig_hotplug_reg); - - intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, - dig_hotplug_reg, hpd_bdw, - ilk_port_hotplug_long_detect); - intel_hpd_irq_handler(dev, pin_mask, long_mask); - found = true; - } - if (tmp & aux_mask) { dp_aux_irq_handler(dev); found = true; } - if (IS_BROXTON(dev) && hotplug_trigger) { - bxt_hpd_irq_handler(dev, hotplug_trigger); + if (hotplug_trigger) { + if (IS_BROXTON(dev)) + bxt_hpd_irq_handler(dev, hotplug_trigger, hpd_bxt); + else + ilk_hpd_irq_handler(dev, hotplug_trigger, hpd_bdw); found = true; } From a52bb15bfad09115cff2433e76b164be94c42af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 27 Aug 2015 23:56:11 +0300 Subject: [PATCH 116/145] drm/i915: Rewrite BXT HPD code to conform to pre-existing style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrite the BXT hpd setup to match the way we do it on other platforms: - Throw out BXT_HOTPLUG_CTL since it's the same as PCH_PORT_HOTPLUG - Enable the HPD bits in the DE port IER in gen8_de_irq_postinstall() - Update DE port IMR using bdw_update_port_irq() Also throw out port D from bxt_port_hotplug_long_detect() since BXT only goes up to C. Signed-off-by: Ville Syrjälä Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 36 ++++++++++++--------------------- drivers/gpu/drm/i915/i915_reg.h | 15 -------------- 2 files changed, 13 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index d7a37a5cf93f..218fe1247e54 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1300,8 +1300,6 @@ static bool bxt_port_hotplug_long_detect(enum port port, u32 val) return val & PORTB_HOTPLUG_LONG_DETECT; case PORT_C: return val & PORTC_HOTPLUG_LONG_DETECT; - case PORT_D: - return val & PORTD_HOTPLUG_LONG_DETECT; default: return false; } @@ -2128,8 +2126,8 @@ static void bxt_hpd_irq_handler(struct drm_device *dev, u32 hotplug_trigger, struct drm_i915_private *dev_priv = to_i915(dev); u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; - dig_hotplug_reg = I915_READ(BXT_HOTPLUG_CTL); - I915_WRITE(BXT_HOTPLUG_CTL, dig_hotplug_reg); + dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG); + I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg); intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, dig_hotplug_reg, hpd, @@ -3247,27 +3245,17 @@ static void ilk_hpd_irq_setup(struct drm_device *dev) static void bxt_hpd_irq_setup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 hotplug_port; - u32 hotplug_ctrl; + u32 hotplug_irqs, hotplug, enabled_irqs; - hotplug_port = intel_hpd_enabled_irqs(dev, hpd_bxt); + enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_bxt); + hotplug_irqs = BXT_DE_PORT_HOTPLUG_MASK; - hotplug_ctrl = I915_READ(BXT_HOTPLUG_CTL) & ~BXT_HOTPLUG_CTL_MASK; + bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs); - if (hotplug_port & BXT_DE_PORT_HP_DDIA) - hotplug_ctrl |= BXT_DDIA_HPD_ENABLE; - if (hotplug_port & BXT_DE_PORT_HP_DDIB) - hotplug_ctrl |= BXT_DDIB_HPD_ENABLE; - if (hotplug_port & BXT_DE_PORT_HP_DDIC) - hotplug_ctrl |= BXT_DDIC_HPD_ENABLE; - I915_WRITE(BXT_HOTPLUG_CTL, hotplug_ctrl); - - hotplug_ctrl = I915_READ(GEN8_DE_PORT_IMR) & ~hotplug_port; - I915_WRITE(GEN8_DE_PORT_IMR, hotplug_ctrl); - - hotplug_ctrl = I915_READ(GEN8_DE_PORT_IER) | hotplug_port; - I915_WRITE(GEN8_DE_PORT_IER, hotplug_ctrl); - POSTING_READ(GEN8_DE_PORT_IER); + hotplug = I915_READ(PCH_PORT_HOTPLUG); + hotplug |= PORTC_HOTPLUG_ENABLE | PORTB_HOTPLUG_ENABLE | + PORTA_HOTPLUG_ENABLE; + I915_WRITE(PCH_PORT_HOTPLUG, hotplug); } static void ibx_irq_postinstall(struct drm_device *dev) @@ -3561,7 +3549,9 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) GEN8_PIPE_FIFO_UNDERRUN; de_port_enables = de_port_masked; - if (IS_BROADWELL(dev_priv)) + if (IS_BROXTON(dev_priv)) + de_port_enables |= BXT_DE_PORT_HOTPLUG_MASK; + else if (IS_BROADWELL(dev_priv)) de_port_enables |= GEN8_PORT_DP_A_HOTPLUG; dev_priv->de_irq_mask[PIPE_A] = ~de_pipe_masked; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 689b42813141..5811eebafd16 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -5797,21 +5797,6 @@ enum skl_disp_power_wells { #define GEN8_PCU_IIR 0x444e8 #define GEN8_PCU_IER 0x444ec -/* BXT hotplug control */ -#define BXT_HOTPLUG_CTL 0xC4030 -#define BXT_DDIA_HPD_ENABLE (1 << 28) -#define BXT_DDIA_HPD_STATUS (3 << 24) -#define BXT_DDIC_HPD_ENABLE (1 << 12) -#define BXT_DDIC_HPD_STATUS (3 << 8) -#define BXT_DDIB_HPD_ENABLE (1 << 4) -#define BXT_DDIB_HPD_STATUS (3 << 0) -#define BXT_HOTPLUG_CTL_MASK (BXT_DDIA_HPD_ENABLE | \ - BXT_DDIB_HPD_ENABLE | \ - BXT_DDIC_HPD_ENABLE) -#define BXT_HPD_STATUS_MASK (BXT_DDIA_HPD_STATUS | \ - BXT_DDIB_HPD_STATUS | \ - BXT_DDIC_HPD_STATUS) - #define ILK_DISPLAY_CHICKEN2 0x42004 /* Required on all Ironlake and Sandybridge according to the B-Spec. */ #define ILK_ELPIN_409_SELECT (1 << 25) From 58f2cf241fb98414e2e35fc3d4d0494eb30b3245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 28 Aug 2015 22:59:08 +0300 Subject: [PATCH 117/145] drm/i915: Don't call intel_get_hpd_pins() when there's no hotplug interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On GMCH plaforms we are now getting the following spew on aux interrupts: [drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000 [drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000 [drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000 [drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000 [drm:intel_get_hpd_pins] hotplug event received, stat 0x00000000, dig 0x00000000, pins 0x00000000 [drm:intel_dp_aux_ch] dp_aux_ch timeout status 0x71450064 Prevent it by not calling intel_get_hpd_pins() unless one of the HPD interrupt bits are actually set. I already fixed similar annoyance once with 4bca26d0a6518d51a9abe64fbde4b12f04c74053 drm/i915: Use HOTPLUG_INT_STATUS_G4X on VLV/CHV but another source for it got added in fd63e2a972c670887e5e8a08440111d3812c0996 drm/i915: combine i9xx_get_hpd_pins and pch_get_hpd_pins due to pch_get_hpd_pins() being chosen over i9xx_get_hpd_pins() to serve as the new unified piece of code. pch_get_hpd_pins() had the debug print, and i9xx_get_hpd_pins() didn't. Cc: Imre Deak Signed-off-by: Ville Syrjälä Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 218fe1247e54..ff543a4880b9 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1639,20 +1639,26 @@ static void i9xx_hpd_irq_handler(struct drm_device *dev) if (IS_G4X(dev) || IS_VALLEYVIEW(dev)) { u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_G4X; - intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, - hotplug_trigger, hpd_status_g4x, - i9xx_port_hotplug_long_detect); - intel_hpd_irq_handler(dev, pin_mask, long_mask); + if (hotplug_trigger) { + intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, + hotplug_trigger, hpd_status_g4x, + i9xx_port_hotplug_long_detect); + + intel_hpd_irq_handler(dev, pin_mask, long_mask); + } if (hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X) dp_aux_irq_handler(dev); } else { u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915; - intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, - hotplug_trigger, hpd_status_g4x, - i9xx_port_hotplug_long_detect); - intel_hpd_irq_handler(dev, pin_mask, long_mask); + if (hotplug_trigger) { + intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger, + hotplug_trigger, hpd_status_g4x, + i9xx_port_hotplug_long_detect); + + intel_hpd_irq_handler(dev, pin_mask, long_mask); + } } } From 395ab7541a0e141ffbde127b534e44f28d39a050 Mon Sep 17 00:00:00 2001 From: "Kumar, Mahesh" Date: Thu, 3 Sep 2015 16:17:08 +0530 Subject: [PATCH 118/145] drm/i915/skl: Avoid using un-initialized bits_per_pixel Don't rely on fb->bits_per_pixel as intel_framebuffer_init is not filling bits_per_pixel field of fb-struct for YUV pixel format. This leads to divide by zero error during watermark calculation. Signed-off-by: Kumar, Mahesh Cc: Konduru, Chandra Reviewed-by: Sonika Jindal Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index ea4966144650..1b90f03f7025 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3170,7 +3170,8 @@ static void skl_compute_wm_pipe_parameters(struct drm_crtc *crtc, if (fb) { p->plane[0].enabled = true; p->plane[0].bytes_per_pixel = fb->pixel_format == DRM_FORMAT_NV12 ? - drm_format_plane_cpp(fb->pixel_format, 1) : fb->bits_per_pixel / 8; + drm_format_plane_cpp(fb->pixel_format, 1) : + drm_format_plane_cpp(fb->pixel_format, 0); p->plane[0].y_bytes_per_pixel = fb->pixel_format == DRM_FORMAT_NV12 ? drm_format_plane_cpp(fb->pixel_format, 0) : 0; p->plane[0].tiling = fb->modifier[0]; From ea916ea057299f66e774c9131e269df070a84779 Mon Sep 17 00:00:00 2001 From: "Kumar, Mahesh" Date: Thu, 3 Sep 2015 16:17:09 +0530 Subject: [PATCH 119/145] drm/i915/skl+: Add YUV pixel format in Capability list GEN >= 9 supports YUV format for all planes, but it's not exported in Capability list of primary plane. Add YUV formats in skl_primary_formats list. Testcase: igt/kms_universal_plane.c Signed-off-by: Kumar, Mahesh Cc: Konduru, Chandra Reviewed-by: Sonika Jindal Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 888175c353f1..6985f6f3d055 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -72,6 +72,10 @@ static const uint32_t skl_primary_formats[] = { DRM_FORMAT_ABGR8888, DRM_FORMAT_XRGB2101010, DRM_FORMAT_XBGR2101010, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, }; /* Cursor formats */ From 3a41a05d70b4fb455256fd3a8348e186f156c310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Winiarski?= Date: Thu, 3 Sep 2015 19:22:18 +0200 Subject: [PATCH 120/145] drm/i915/gtt: Avoid calling kcalloc in a loop when allocating temp bitmaps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On each call to gen8_alloc_va_range_3lvl we're allocating temporary bitmaps needed for error handling. Unfortunately, when we increase address space size (48b ppgtt) we do additional (512 - 4) calls to kcalloc, increasing latency between exec and actual start of execution on the GPU. Let's just do a single kcalloc, we can also drop the size from free_gen8_temp_bitmaps since it's no longer used. v2: Use GFP_TEMPORARY to make the allocations reclaimable. v3: Drop the 2D array, just allocate a single block. v4: Rebase to handle gen8_preallocate_top_level_pdps. v5: Align misaligned bracket. Cc: Chris Wilson Cc: Mika Kuoppala Cc: Michel Thierry Signed-off-by: Michał Winiarski Reviewed-by: Chris Wilson [danvet: Correct kcalloc arguments as suggested by Chris.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 49 +++++++++++------------------ 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index bdb7adc7359e..87862813cfde 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -1164,13 +1164,8 @@ gen8_ppgtt_alloc_page_dirpointers(struct i915_address_space *vm, } static void -free_gen8_temp_bitmaps(unsigned long *new_pds, unsigned long **new_pts, - uint32_t pdpes) +free_gen8_temp_bitmaps(unsigned long *new_pds, unsigned long *new_pts) { - int i; - - for (i = 0; i < pdpes; i++) - kfree(new_pts[i]); kfree(new_pts); kfree(new_pds); } @@ -1180,29 +1175,20 @@ free_gen8_temp_bitmaps(unsigned long *new_pds, unsigned long **new_pts, */ static int __must_check alloc_gen8_temp_bitmaps(unsigned long **new_pds, - unsigned long ***new_pts, + unsigned long **new_pts, uint32_t pdpes) { - int i; unsigned long *pds; - unsigned long **pts; + unsigned long *pts; - pds = kcalloc(BITS_TO_LONGS(pdpes), sizeof(unsigned long), GFP_KERNEL); + pds = kcalloc(BITS_TO_LONGS(pdpes), sizeof(unsigned long), GFP_TEMPORARY); if (!pds) return -ENOMEM; - pts = kcalloc(pdpes, sizeof(unsigned long *), GFP_KERNEL); - if (!pts) { - kfree(pds); - return -ENOMEM; - } - - for (i = 0; i < pdpes; i++) { - pts[i] = kcalloc(BITS_TO_LONGS(I915_PDES), - sizeof(unsigned long), GFP_KERNEL); - if (!pts[i]) - goto err_out; - } + pts = kcalloc(pdpes, BITS_TO_LONGS(I915_PDES) * sizeof(unsigned long), + GFP_TEMPORARY); + if (!pts) + goto err_out; *new_pds = pds; *new_pts = pts; @@ -1210,7 +1196,7 @@ int __must_check alloc_gen8_temp_bitmaps(unsigned long **new_pds, return 0; err_out: - free_gen8_temp_bitmaps(pds, pts, pdpes); + free_gen8_temp_bitmaps(pds, pts); return -ENOMEM; } @@ -1231,7 +1217,7 @@ static int gen8_alloc_va_range_3lvl(struct i915_address_space *vm, { struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); - unsigned long *new_page_dirs, **new_page_tables; + unsigned long *new_page_dirs, *new_page_tables; struct drm_device *dev = vm->dev; struct i915_page_directory *pd; const uint64_t orig_start = start; @@ -1258,14 +1244,14 @@ static int gen8_alloc_va_range_3lvl(struct i915_address_space *vm, ret = gen8_ppgtt_alloc_page_directories(vm, pdp, start, length, new_page_dirs); if (ret) { - free_gen8_temp_bitmaps(new_page_dirs, new_page_tables, pdpes); + free_gen8_temp_bitmaps(new_page_dirs, new_page_tables); return ret; } /* For every page directory referenced, allocate page tables */ gen8_for_each_pdpe(pd, pdp, start, length, temp, pdpe) { ret = gen8_ppgtt_alloc_pagetabs(vm, pd, start, length, - new_page_tables[pdpe]); + new_page_tables + pdpe * BITS_TO_LONGS(I915_PDES)); if (ret) goto err_out; } @@ -1316,20 +1302,21 @@ static int gen8_alloc_va_range_3lvl(struct i915_address_space *vm, gen8_setup_page_directory(ppgtt, pdp, pd, pdpe); } - free_gen8_temp_bitmaps(new_page_dirs, new_page_tables, pdpes); + free_gen8_temp_bitmaps(new_page_dirs, new_page_tables); mark_tlbs_dirty(ppgtt); return 0; err_out: while (pdpe--) { - for_each_set_bit(temp, new_page_tables[pdpe], I915_PDES) + for_each_set_bit(temp, new_page_tables + pdpe * + BITS_TO_LONGS(I915_PDES), I915_PDES) free_pt(dev, pdp->page_directory[pdpe]->page_table[temp]); } for_each_set_bit(pdpe, new_page_dirs, pdpes) free_pd(dev, pdp->page_directory[pdpe]); - free_gen8_temp_bitmaps(new_page_dirs, new_page_tables, pdpes); + free_gen8_temp_bitmaps(new_page_dirs, new_page_tables); mark_tlbs_dirty(ppgtt); return ret; } @@ -1481,7 +1468,7 @@ static void gen8_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m) static int gen8_preallocate_top_level_pdps(struct i915_hw_ppgtt *ppgtt) { - unsigned long *new_page_dirs, **new_page_tables; + unsigned long *new_page_dirs, *new_page_tables; uint32_t pdpes = I915_PDPES_PER_PDP(dev); int ret; @@ -1501,7 +1488,7 @@ static int gen8_preallocate_top_level_pdps(struct i915_hw_ppgtt *ppgtt) if (!ret) *ppgtt->pdp.used_pdpes = *new_page_dirs; - free_gen8_temp_bitmaps(new_page_dirs, new_page_tables, pdpes); + free_gen8_temp_bitmaps(new_page_dirs, new_page_tables); return ret; } From b4834a508a1c25773c929c3d8eb7a29ad1041e19 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Wed, 2 Sep 2015 15:19:24 -0700 Subject: [PATCH 121/145] drm/i915: Future proof interrupt handler. These functions are already being called for gen >= 9, so let's be sure when this happens we use whatever is there already for the latest platform. No functional change. Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index ff543a4880b9..ca6d806a195a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2155,7 +2155,7 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) if (!intel_irqs_enabled(dev_priv)) return IRQ_NONE; - if (IS_GEN9(dev)) + if (INTEL_INFO(dev_priv)->gen >= 9) aux_mask |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C | GEN9_AUX_CHANNEL_D; @@ -2238,7 +2238,7 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) intel_pipe_handle_vblank(dev, pipe)) intel_check_page_flip(dev, pipe); - if (IS_GEN9(dev)) + if (INTEL_INFO(dev_priv)->gen >= 9) flip_done = pipe_iir & GEN9_PIPE_PLANE1_FLIP_DONE; else flip_done = pipe_iir & GEN8_PIPE_PRIMARY_FLIP_DONE; @@ -2256,7 +2256,7 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) pipe); - if (IS_GEN9(dev)) + if (INTEL_INFO(dev_priv)->gen >= 9) fault_errors = pipe_iir & GEN9_DE_PIPE_IRQ_FAULT_ERRORS; else fault_errors = pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS; @@ -3539,7 +3539,7 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) u32 de_port_enables; enum pipe pipe; - if (IS_GEN9(dev_priv)) { + if (INTEL_INFO(dev_priv)->gen >= 9) { de_pipe_masked |= GEN9_PIPE_PLANE1_FLIP_DONE | GEN9_DE_PIPE_IRQ_FAULT_ERRORS; de_port_masked |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C | From 22cc27bb7a47cf6ad33b983539b14bc46e65f9b2 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 3 Sep 2015 14:38:00 -0700 Subject: [PATCH 122/145] drm/i915: Future proof uncore_init. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unless future specs tells otherwise we can assume future gens inherit some stuff from the previous so let's handle missed cases when we know tehy should't be there and assume default equals newest one. No functional changes. v2: Remove useless case as pointed out by Ville. Cc: Ville Syrjälä Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_uncore.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index dec20d60daf0..440e2a5658af 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -1202,8 +1202,6 @@ void intel_uncore_init(struct drm_device *dev) switch (INTEL_INFO(dev)->gen) { default: - MISSING_CASE(INTEL_INFO(dev)->gen); - return; case 9: ASSIGN_WRITE_MMIO_VFUNCS(gen9); ASSIGN_READ_MMIO_VFUNCS(gen9); From 1c132b44f0eabaaf801e79a3e4b98770431a4274 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Wed, 2 Sep 2015 15:19:26 -0700 Subject: [PATCH 123/145] drm/i915: Future proof panel fitter. This is another case where we can consider the default is the newest available and not actually a missed case. No functional change. Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6985f6f3d055..b59717a6e6a6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4919,12 +4919,10 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_ddi_enable_pipe_clock(intel_crtc); - if (INTEL_INFO(dev)->gen == 9) + if (INTEL_INFO(dev)->gen >= 9) skylake_pfit_enable(intel_crtc); - else if (INTEL_INFO(dev)->gen < 9) - ironlake_pfit_enable(intel_crtc); else - MISSING_CASE(INTEL_INFO(dev)->gen); + ironlake_pfit_enable(intel_crtc); /* * On ILK+ LUT must be loaded before the pipe is running but with @@ -5056,12 +5054,10 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) intel_ddi_disable_transcoder_func(dev_priv, cpu_transcoder); - if (INTEL_INFO(dev)->gen == 9) + if (INTEL_INFO(dev)->gen >= 9) skylake_scaler_disable(intel_crtc); - else if (INTEL_INFO(dev)->gen < 9) - ironlake_pfit_disable(intel_crtc); else - MISSING_CASE(INTEL_INFO(dev)->gen); + ironlake_pfit_disable(intel_crtc); intel_ddi_disable_pipe_clock(intel_crtc); @@ -9784,12 +9780,10 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, } if (intel_display_power_is_enabled(dev_priv, pfit_domain)) { - if (INTEL_INFO(dev)->gen == 9) + if (INTEL_INFO(dev)->gen >= 9) skylake_get_pfit_config(crtc, pipe_config); - else if (INTEL_INFO(dev)->gen < 9) - ironlake_get_pfit_config(crtc, pipe_config); else - MISSING_CASE(INTEL_INFO(dev)->gen); + ironlake_get_pfit_config(crtc, pipe_config); } if (IS_HASWELL(dev)) From bc5133d595a0ddfaaa5edb4b077f44d92cc8fb0d Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 3 Sep 2015 11:16:07 +0300 Subject: [PATCH 124/145] drm/i915/dp: move TPS3 logic to where it's used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no need to have a separate flag for tps3 as the information is only used at one location. Move the logic there to make it easier to follow. Reviewed-by: Ville Syrjälä Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 31 +++++++++++++++++-------------- drivers/gpu/drm/i915/intel_drv.h | 1 - 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f8f4d99440c1..9a6fb5dbf021 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -3812,13 +3812,25 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) void intel_dp_complete_link_train(struct intel_dp *intel_dp) { + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = dig_port->base.base.dev; bool channel_eq = false; int tries, cr_tries; uint32_t DP = intel_dp->DP; uint32_t training_pattern = DP_TRAINING_PATTERN_2; - /* Training Pattern 3 for HBR2 or 1.2 devices that support it*/ - if (intel_dp->link_rate == 540000 || intel_dp->use_tps3) + /* + * Training Pattern 3 for HBR2 or 1.2 devices that support it. + * + * Intel platforms that support HBR2 also support TPS3. TPS3 support is + * also mandatory for downstream devices that support HBR2. + * + * Due to WaDisableHBR2 SKL < B0 is the only exception where TPS3 is + * supported but still not enabled. + */ + if (intel_dp->link_rate == 540000 || + (intel_dp_source_supports_hbr2(dev) && + drm_dp_tps3_supported(intel_dp->dpcd))) training_pattern = DP_TRAINING_PATTERN_3; /* channel equalization */ @@ -4000,18 +4012,9 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) } } - /* Training Pattern 3 support, Intel platforms that support HBR2 alone - * have support for TP3 hence that check is used along with dpcd check - * to ensure TP3 can be enabled. - * SKL < B0: due it's WaDisableHBR2 is the only exception where TP3 is - * supported but still not enabled. - */ - if (drm_dp_tps3_supported(intel_dp->dpcd) && - intel_dp_source_supports_hbr2(dev)) { - intel_dp->use_tps3 = true; - DRM_DEBUG_KMS("Displayport TPS3 supported\n"); - } else - intel_dp->use_tps3 = false; + DRM_DEBUG_KMS("Display Port TPS3 support: source %s, sink %s\n", + intel_dp_source_supports_hbr2(dev) ? "yes" : "no", + drm_dp_tps3_supported(intel_dp->dpcd) ? "yes" : "no"); /* Intermediate frequency support */ if (is_edp(intel_dp) && diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 40e825d6a26f..2bdd5449b889 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -746,7 +746,6 @@ struct intel_dp { enum pipe pps_pipe; struct edp_power_seq pps_delays; - bool use_tps3; bool can_mst; /* this port supports mst */ bool is_mst; int active_mst_links; From 1da7d7131c35cde83f1bab8ec732b57b69bef814 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 3 Sep 2015 11:16:08 +0300 Subject: [PATCH 125/145] drm/i915: ignore link rate in TPS3 selection TPS3 is mandatory for downstream devices that support HBR2, and Intel platforms that support HBR2 also support TPS3. Whenever TPS3 is supported by both the source and sink, it should be used. In other words, whenever the source and sink are capable of 5.4 Gbps link, we should anyway go for TPS3, regardless of the link rate being selected. Log an error if the sink has advertized HBR2 capability without TPS3 capability. Reviewed-by: Chris Wilson Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 9a6fb5dbf021..44e753d5f7f5 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -3828,10 +3828,11 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) * Due to WaDisableHBR2 SKL < B0 is the only exception where TPS3 is * supported but still not enabled. */ - if (intel_dp->link_rate == 540000 || - (intel_dp_source_supports_hbr2(dev) && - drm_dp_tps3_supported(intel_dp->dpcd))) + if (intel_dp_source_supports_hbr2(dev) && + drm_dp_tps3_supported(intel_dp->dpcd)) training_pattern = DP_TRAINING_PATTERN_3; + else if (intel_dp->link_rate == 540000) + DRM_ERROR("5.4 Gbps link rate without HBR2/TPS3 support\n"); /* channel equalization */ if (!intel_dp_set_link_train(intel_dp, &DP, From 742f491d2c204204086d2bc85cc5100daa6ff336 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 3 Sep 2015 11:16:09 +0300 Subject: [PATCH 126/145] drm/i915: use the yesno helper for logging Reviewed-by: Chris Wilson Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 17 +++++++---------- drivers/gpu/drm/i915/intel_dp.c | 4 ++-- drivers/gpu/drm/i915/intel_pm.c | 4 ++-- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 4563f8b955ea..41629faaf939 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1382,17 +1382,16 @@ static int ironlake_drpc_info(struct seq_file *m) intel_runtime_pm_put(dev_priv); mutex_unlock(&dev->struct_mutex); - seq_printf(m, "HD boost: %s\n", (rgvmodectl & MEMMODE_BOOST_EN) ? - "yes" : "no"); + seq_printf(m, "HD boost: %s\n", yesno(rgvmodectl & MEMMODE_BOOST_EN)); seq_printf(m, "Boost freq: %d\n", (rgvmodectl & MEMMODE_BOOST_FREQ_MASK) >> MEMMODE_BOOST_FREQ_SHIFT); seq_printf(m, "HW control enabled: %s\n", - rgvmodectl & MEMMODE_HWIDLE_EN ? "yes" : "no"); + yesno(rgvmodectl & MEMMODE_HWIDLE_EN)); seq_printf(m, "SW control enabled: %s\n", - rgvmodectl & MEMMODE_SWMODE_EN ? "yes" : "no"); + yesno(rgvmodectl & MEMMODE_SWMODE_EN)); seq_printf(m, "Gated voltage change: %s\n", - rgvmodectl & MEMMODE_RCLK_GATE ? "yes" : "no"); + yesno(rgvmodectl & MEMMODE_RCLK_GATE)); seq_printf(m, "Starting frequency: P%d\n", (rgvmodectl & MEMMODE_FSTART_MASK) >> MEMMODE_FSTART_SHIFT); seq_printf(m, "Max P-state: P%d\n", @@ -1401,7 +1400,7 @@ static int ironlake_drpc_info(struct seq_file *m) seq_printf(m, "RS1 VID: %d\n", (crstandvid & 0x3f)); seq_printf(m, "RS2 VID: %d\n", ((crstandvid >> 8) & 0x3f)); seq_printf(m, "Render standby enabled: %s\n", - (rstdbyctl & RCX_SW_EXIT) ? "no" : "yes"); + yesno(!(rstdbyctl & RCX_SW_EXIT))); seq_puts(m, "Current RS state: "); switch (rstdbyctl & RSX_STATUS_MASK) { case RSX_STATUS_ON: @@ -2844,8 +2843,7 @@ static void intel_dp_info(struct seq_file *m, struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base); seq_printf(m, "\tDPCD rev: %x\n", intel_dp->dpcd[DP_DPCD_REV]); - seq_printf(m, "\taudio support: %s\n", intel_dp->has_audio ? "yes" : - "no"); + seq_printf(m, "\taudio support: %s\n", yesno(intel_dp->has_audio)); if (intel_encoder->type == INTEL_OUTPUT_EDP) intel_panel_info(m, &intel_connector->panel); } @@ -2856,8 +2854,7 @@ static void intel_hdmi_info(struct seq_file *m, struct intel_encoder *intel_encoder = intel_connector->encoder; struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base); - seq_printf(m, "\taudio support: %s\n", intel_hdmi->has_audio ? "yes" : - "no"); + seq_printf(m, "\taudio support: %s\n", yesno(intel_hdmi->has_audio)); } static void intel_lvds_info(struct seq_file *m, diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 44e753d5f7f5..45ab25e479ae 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4014,8 +4014,8 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) } DRM_DEBUG_KMS("Display Port TPS3 support: source %s, sink %s\n", - intel_dp_source_supports_hbr2(dev) ? "yes" : "no", - drm_dp_tps3_supported(intel_dp->dpcd) ? "yes" : "no"); + yesno(intel_dp_source_supports_hbr2(dev)), + yesno(drm_dp_tps3_supported(intel_dp->dpcd))); /* Intermediate frequency support */ if (is_edp(intel_dp) && diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 1b90f03f7025..79cd9872bf1f 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5550,7 +5550,7 @@ static void cherryview_enable_rps(struct drm_device *dev) /* RPS code assumes GPLL is used */ WARN_ONCE((val & GPLLENABLE) == 0, "GPLL not enabled\n"); - DRM_DEBUG_DRIVER("GPLL enabled? %s\n", val & GPLLENABLE ? "yes" : "no"); + DRM_DEBUG_DRIVER("GPLL enabled? %s\n", yesno(val & GPLLENABLE)); DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val); dev_priv->rps.cur_freq = (val >> 8) & 0xff; @@ -5640,7 +5640,7 @@ static void valleyview_enable_rps(struct drm_device *dev) /* RPS code assumes GPLL is used */ WARN_ONCE((val & GPLLENABLE) == 0, "GPLL not enabled\n"); - DRM_DEBUG_DRIVER("GPLL enabled? %s\n", val & GPLLENABLE ? "yes" : "no"); + DRM_DEBUG_DRIVER("GPLL enabled? %s\n", yesno(val & GPLLENABLE)); DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val); dev_priv->rps.cur_freq = (val >> 8) & 0xff; From 01101fa7cc85fffc95798d1f67970dad96063fc6 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 3 Sep 2015 13:01:39 +0100 Subject: [PATCH 127/145] drm/i915: Refactor common ringbuffer allocation code A small, very small, step to sharing the duplicate code between execlists and legacy submission engines, starting with the ringbuffer allocation code. Signed-off-by: Chris Wilson Cc: Arun Siluvery Cc: Mika Kuoppala Cc: Dave Gordon Reviewed-by: Paulo Zanoni Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_lrc.c | 49 ++++---------- drivers/gpu/drm/i915/intel_ringbuffer.c | 89 +++++++++++++++---------- drivers/gpu/drm/i915/intel_ringbuffer.h | 8 +-- 3 files changed, 70 insertions(+), 76 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 40cbba4ea4ba..28a712e7d2d0 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -2340,8 +2340,7 @@ void intel_lr_context_free(struct intel_context *ctx) i915_gem_object_ggtt_unpin(ctx_obj); } WARN_ON(ctx->engine[ring->id].pin_count); - intel_destroy_ringbuffer_obj(ringbuf); - kfree(ringbuf); + intel_ringbuffer_free(ringbuf); drm_gem_object_unreference(&ctx_obj->base); } } @@ -2442,42 +2441,20 @@ int intel_lr_context_deferred_create(struct intel_context *ctx, I915_WRITE(GEN8_GTCR, GEN8_GTCR_INVALIDATE); } - ringbuf = kzalloc(sizeof(*ringbuf), GFP_KERNEL); - if (!ringbuf) { - DRM_DEBUG_DRIVER("Failed to allocate ringbuffer %s\n", - ring->name); - ret = -ENOMEM; + ringbuf = intel_engine_create_ringbuffer(ring, 4 * PAGE_SIZE); + if (IS_ERR(ringbuf)) { + ret = PTR_ERR(ringbuf); goto error_unpin_ctx; } - ringbuf->ring = ring; - - ringbuf->size = 4 * PAGE_SIZE; - ringbuf->effective_size = ringbuf->size; - ringbuf->head = 0; - ringbuf->tail = 0; - ringbuf->last_retired_head = -1; - intel_ring_update_space(ringbuf); - - if (ringbuf->obj == NULL) { - ret = intel_alloc_ringbuffer_obj(dev, ringbuf); + if (is_global_default_ctx) { + ret = intel_pin_and_map_ringbuffer_obj(dev, ringbuf); if (ret) { - DRM_DEBUG_DRIVER( - "Failed to allocate ringbuffer obj %s: %d\n", - ring->name, ret); - goto error_free_rbuf; + DRM_ERROR( + "Failed to pin and map ringbuffer %s: %d\n", + ring->name, ret); + goto error_ringbuf; } - - if (is_global_default_ctx) { - ret = intel_pin_and_map_ringbuffer_obj(dev, ringbuf); - if (ret) { - DRM_ERROR( - "Failed to pin and map ringbuffer %s: %d\n", - ring->name, ret); - goto error_destroy_rbuf; - } - } - } ret = populate_lr_context(ctx, ctx_obj, ring, ringbuf); @@ -2519,10 +2496,8 @@ int intel_lr_context_deferred_create(struct intel_context *ctx, error: if (is_global_default_ctx) intel_unpin_ringbuffer_obj(ringbuf); -error_destroy_rbuf: - intel_destroy_ringbuffer_obj(ringbuf); -error_free_rbuf: - kfree(ringbuf); +error_ringbuf: + intel_ringbuffer_free(ringbuf); error_unpin_ctx: if (is_global_default_ctx) i915_gem_object_ggtt_unpin(ctx_obj); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 6e6b8db996ef..20a75bb516ac 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1996,14 +1996,14 @@ int intel_pin_and_map_ringbuffer_obj(struct drm_device *dev, return 0; } -void intel_destroy_ringbuffer_obj(struct intel_ringbuffer *ringbuf) +static void intel_destroy_ringbuffer_obj(struct intel_ringbuffer *ringbuf) { drm_gem_object_unreference(&ringbuf->obj->base); ringbuf->obj = NULL; } -int intel_alloc_ringbuffer_obj(struct drm_device *dev, - struct intel_ringbuffer *ringbuf) +static int intel_alloc_ringbuffer_obj(struct drm_device *dev, + struct intel_ringbuffer *ringbuf) { struct drm_i915_gem_object *obj; @@ -2023,6 +2023,48 @@ int intel_alloc_ringbuffer_obj(struct drm_device *dev, return 0; } +struct intel_ringbuffer * +intel_engine_create_ringbuffer(struct intel_engine_cs *engine, int size) +{ + struct intel_ringbuffer *ring; + int ret; + + ring = kzalloc(sizeof(*ring), GFP_KERNEL); + if (ring == NULL) + return ERR_PTR(-ENOMEM); + + ring->ring = engine; + + ring->size = size; + /* Workaround an erratum on the i830 which causes a hang if + * the TAIL pointer points to within the last 2 cachelines + * of the buffer. + */ + ring->effective_size = size; + if (IS_I830(engine->dev) || IS_845G(engine->dev)) + ring->effective_size -= 2 * CACHELINE_BYTES; + + ring->last_retired_head = -1; + intel_ring_update_space(ring); + + ret = intel_alloc_ringbuffer_obj(engine->dev, ring); + if (ret) { + DRM_ERROR("Failed to allocate ringbuffer %s: %d\n", + engine->name, ret); + kfree(ring); + return ERR_PTR(ret); + } + + return ring; +} + +void +intel_ringbuffer_free(struct intel_ringbuffer *ring) +{ + intel_destroy_ringbuffer_obj(ring); + kfree(ring); +} + static int intel_init_ring_buffer(struct drm_device *dev, struct intel_engine_cs *ring) { @@ -2031,22 +2073,20 @@ static int intel_init_ring_buffer(struct drm_device *dev, WARN_ON(ring->buffer); - ringbuf = kzalloc(sizeof(*ringbuf), GFP_KERNEL); - if (!ringbuf) - return -ENOMEM; - ring->buffer = ringbuf; - ring->dev = dev; INIT_LIST_HEAD(&ring->active_list); INIT_LIST_HEAD(&ring->request_list); INIT_LIST_HEAD(&ring->execlist_queue); i915_gem_batch_pool_init(dev, &ring->batch_pool); - ringbuf->size = 32 * PAGE_SIZE; - ringbuf->ring = ring; memset(ring->semaphore.sync_seqno, 0, sizeof(ring->semaphore.sync_seqno)); init_waitqueue_head(&ring->irq_queue); + ringbuf = intel_engine_create_ringbuffer(ring, 32 * PAGE_SIZE); + if (IS_ERR(ringbuf)) + return PTR_ERR(ringbuf); + ring->buffer = ringbuf; + if (I915_NEED_GFX_HWS(dev)) { ret = init_status_page(ring); if (ret) @@ -2058,15 +2098,6 @@ static int intel_init_ring_buffer(struct drm_device *dev, goto error; } - WARN_ON(ringbuf->obj); - - ret = intel_alloc_ringbuffer_obj(dev, ringbuf); - if (ret) { - DRM_ERROR("Failed to allocate ringbuffer %s: %d\n", - ring->name, ret); - goto error; - } - ret = intel_pin_and_map_ringbuffer_obj(dev, ringbuf); if (ret) { DRM_ERROR("Failed to pin and map ringbuffer %s: %d\n", @@ -2075,14 +2106,6 @@ static int intel_init_ring_buffer(struct drm_device *dev, goto error; } - /* Workaround an erratum on the i830 which causes a hang if - * the TAIL pointer points to within the last 2 cachelines - * of the buffer. - */ - ringbuf->effective_size = ringbuf->size; - if (IS_I830(dev) || IS_845G(dev)) - ringbuf->effective_size -= 2 * CACHELINE_BYTES; - ret = i915_cmd_parser_init_ring(ring); if (ret) goto error; @@ -2090,7 +2113,7 @@ static int intel_init_ring_buffer(struct drm_device *dev, return 0; error: - kfree(ringbuf); + intel_ringbuffer_free(ringbuf); ring->buffer = NULL; return ret; } @@ -2098,19 +2121,18 @@ static int intel_init_ring_buffer(struct drm_device *dev, void intel_cleanup_ring_buffer(struct intel_engine_cs *ring) { struct drm_i915_private *dev_priv; - struct intel_ringbuffer *ringbuf; if (!intel_ring_initialized(ring)) return; dev_priv = to_i915(ring->dev); - ringbuf = ring->buffer; intel_stop_ring_buffer(ring); WARN_ON(!IS_GEN2(ring->dev) && (I915_READ_MODE(ring) & MODE_IDLE) == 0); - intel_unpin_ringbuffer_obj(ringbuf); - intel_destroy_ringbuffer_obj(ringbuf); + intel_unpin_ringbuffer_obj(ring->buffer); + intel_ringbuffer_free(ring->buffer); + ring->buffer = NULL; if (ring->cleanup) ring->cleanup(ring); @@ -2119,9 +2141,6 @@ void intel_cleanup_ring_buffer(struct intel_engine_cs *ring) i915_cmd_parser_fini_ring(ring); i915_gem_batch_pool_fini(&ring->batch_pool); - - kfree(ringbuf); - ring->buffer = NULL; } static int ring_wait_for_space(struct intel_engine_cs *ring, int n) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 95b0b4b55fa6..49fa41dc0eb6 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -420,12 +420,12 @@ intel_write_status_page(struct intel_engine_cs *ring, #define I915_GEM_HWS_SCRATCH_INDEX 0x40 #define I915_GEM_HWS_SCRATCH_ADDR (I915_GEM_HWS_SCRATCH_INDEX << MI_STORE_DWORD_INDEX_SHIFT) -void intel_unpin_ringbuffer_obj(struct intel_ringbuffer *ringbuf); +struct intel_ringbuffer * +intel_engine_create_ringbuffer(struct intel_engine_cs *engine, int size); int intel_pin_and_map_ringbuffer_obj(struct drm_device *dev, struct intel_ringbuffer *ringbuf); -void intel_destroy_ringbuffer_obj(struct intel_ringbuffer *ringbuf); -int intel_alloc_ringbuffer_obj(struct drm_device *dev, - struct intel_ringbuffer *ringbuf); +void intel_unpin_ringbuffer_obj(struct intel_ringbuffer *ringbuf); +void intel_ringbuffer_free(struct intel_ringbuffer *ring); void intel_stop_ring_buffer(struct intel_engine_cs *ring); void intel_cleanup_ring_buffer(struct intel_engine_cs *ring); From c5796b7126b2497513bfa9a9341d02cb833d83d6 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 3 Sep 2015 16:24:35 +0300 Subject: [PATCH 128/145] drm/i915: access the PP_CONTROL reg only pre GEN5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This register exists only pre GEN5, but atm we also access it on VLV/BXT/CHV. Prevent accessing it on these latter platforms. Signed-off-by: Imre Deak Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_lvds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 0794dc84ff01..a16308ad9124 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -955,7 +955,7 @@ void intel_lvds_init(struct drm_device *dev) if (HAS_PCH_SPLIT(dev)) { I915_WRITE(PCH_PP_CONTROL, I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); - } else { + } else if (INTEL_INFO(dev_priv)->gen < 5) { I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); } From 96d12cbdbd03a9ce87a75520512ef9ad8c585e92 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 3 Sep 2015 16:24:36 +0300 Subject: [PATCH 129/145] drm/i915: access the PP_ON_DELAYS/PP_OFF_DELAYS regs only pre GEN5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These registers exist only before GEN5, so currently we may access undefined registers on VLV/CHV and BXT. Apply the workaround only pre GEN5. Since the workaround is relevant only when LVDS is present, for clarity apply it only if this is the case. This triggered an unclaimed register access warning on BXT. v2: (Ville) - move the workaround to the LVDS init code - print a debug note about the workaround Signed-off-by: Imre Deak Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 2 -- drivers/gpu/drm/i915/intel_bios.c | 18 ------------------ drivers/gpu/drm/i915/intel_bios.h | 1 - drivers/gpu/drm/i915/intel_lvds.c | 12 ++++++++++++ 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index f0eaa6f8826b..066a0efa75d7 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -997,8 +997,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) intel_setup_gmbus(dev); intel_opregion_setup(dev); - intel_setup_bios(dev); - i915_gem_load(dev); /* On the 945G/GM, the chipset reports the MSI capability on the diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index b3e437b3bb54..c8acc2952f82 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -1340,21 +1340,3 @@ intel_parse_bios(struct drm_device *dev) return 0; } - -/* Ensure that vital registers have been initialised, even if the BIOS - * is absent or just failing to do its job. - */ -void intel_setup_bios(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - /* Set the Panel Power On/Off timings if uninitialized. */ - if (!HAS_PCH_SPLIT(dev) && - I915_READ(PP_ON_DELAYS) == 0 && I915_READ(PP_OFF_DELAYS) == 0) { - /* Set T2 to 40ms and T5 to 200ms */ - I915_WRITE(PP_ON_DELAYS, 0x019007d0); - - /* Set T3 to 35ms and Tx to 200ms */ - I915_WRITE(PP_OFF_DELAYS, 0x015e07d0); - } -} diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h index 46cd5c7ebacd..1b7417e3131b 100644 --- a/drivers/gpu/drm/i915/intel_bios.h +++ b/drivers/gpu/drm/i915/intel_bios.h @@ -588,7 +588,6 @@ struct bdb_psr { struct psr_table psr_table[16]; } __packed; -void intel_setup_bios(struct drm_device *dev); int intel_parse_bios(struct drm_device *dev); /* diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index a16308ad9124..2c2d1f0737c8 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -985,6 +985,18 @@ void intel_lvds_init(struct drm_device *dev) DRM_DEBUG_KMS("LVDS is not present in VBT, but enabled anyway\n"); } + /* Set the Panel Power On/Off timings if uninitialized. */ + if (INTEL_INFO(dev_priv)->gen < 5 && + I915_READ(PP_ON_DELAYS) == 0 && I915_READ(PP_OFF_DELAYS) == 0) { + /* Set T2 to 40ms and T5 to 200ms */ + I915_WRITE(PP_ON_DELAYS, 0x019007d0); + + /* Set T3 to 35ms and Tx to 200ms */ + I915_WRITE(PP_OFF_DELAYS, 0x015e07d0); + + DRM_DEBUG_KMS("Panel power timings uninitialized, setting defaults\n"); + } + lvds_encoder = kzalloc(sizeof(*lvds_encoder), GFP_KERNEL); if (!lvds_encoder) return; From 333b8ca8cfcafc8c556d4333fe660283fea691d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 3 Sep 2015 21:50:16 +0300 Subject: [PATCH 130/145] drm/i915: Dump pfit state as hex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pfit state is stored as register values, so dump them as hex instead of decimal to make some sense of the error messages. Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b59717a6e6a6..110b8dc7bbd0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -12439,16 +12439,16 @@ intel_pipe_config_compare(struct drm_device *dev, PIPE_CONF_CHECK_I(pipe_src_w); PIPE_CONF_CHECK_I(pipe_src_h); - PIPE_CONF_CHECK_I(gmch_pfit.control); + PIPE_CONF_CHECK_X(gmch_pfit.control); /* pfit ratios are autocomputed by the hw on gen4+ */ if (INTEL_INFO(dev)->gen < 4) PIPE_CONF_CHECK_I(gmch_pfit.pgm_ratios); - PIPE_CONF_CHECK_I(gmch_pfit.lvds_border_bits); + PIPE_CONF_CHECK_X(gmch_pfit.lvds_border_bits); PIPE_CONF_CHECK_I(pch_pfit.enabled); if (current_config->pch_pfit.enabled) { - PIPE_CONF_CHECK_I(pch_pfit.pos); - PIPE_CONF_CHECK_I(pch_pfit.size); + PIPE_CONF_CHECK_X(pch_pfit.pos); + PIPE_CONF_CHECK_X(pch_pfit.size); } PIPE_CONF_CHECK_I(scaler_state.scaler_id); From 614f4ad7984f930623bf0d25adba707d0d36c6dc Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 2 Sep 2015 12:29:40 +0100 Subject: [PATCH 131/145] drm/i915: Fix cmdparser STORE/LOAD command descriptors Fixes regression from commit f1afe24f0e736b9d7f2275e2b1504af3fe612f2a Author: Arun Siluvery Date: Tue Aug 4 16:22:20 2015 +0100 drm/i915: Change SRM, LRM instructions to use correct length which forgot to account for the length bias when declaring the fixed length. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=91844 Reported-by: Andreas Reis Signed-off-by: Chris Wilson Cc: Dave Gordon Cc: Arun Siluvery Cc: Mika Kuoppala Cc: Daniel Vetter Reviewed-by: Arun Siluvery Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_cmd_parser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c index ad7d7ab76d3f..09932cab1a3f 100644 --- a/drivers/gpu/drm/i915/i915_cmd_parser.c +++ b/drivers/gpu/drm/i915/i915_cmd_parser.c @@ -124,14 +124,14 @@ static const struct drm_i915_cmd_descriptor common_cmds[] = { CMD( MI_STORE_DWORD_INDEX, SMI, !F, 0xFF, R ), CMD( MI_LOAD_REGISTER_IMM(1), SMI, !F, 0xFF, W, .reg = { .offset = 1, .mask = 0x007FFFFC, .step = 2 } ), - CMD( MI_STORE_REGISTER_MEM, SMI, F, 1, W | B, + CMD( MI_STORE_REGISTER_MEM, SMI, F, 3, W | B, .reg = { .offset = 1, .mask = 0x007FFFFC }, .bits = {{ .offset = 0, .mask = MI_GLOBAL_GTT, .expected = 0, }}, ), - CMD( MI_LOAD_REGISTER_MEM, SMI, F, 1, W | B, + CMD( MI_LOAD_REGISTER_MEM, SMI, F, 3, W | B, .reg = { .offset = 1, .mask = 0x007FFFFC }, .bits = {{ .offset = 0, From a7546159718171ba9f2a4df37b574c64b1589b5d Mon Sep 17 00:00:00 2001 From: Nick Hoath Date: Mon, 29 Jun 2015 14:07:32 +0100 Subject: [PATCH 132/145] drm/i915/bxt: Clean up bxt_init_clock_gating Add stepping check for A0 workarounds, and remove the associated FIXME tags. Split out unrelated WAs for later condition checking. v2: Fixed format (PeterL) v3: Corrected stepping check for WaDisableSDEUnitClockGating - Ignoring comment, following hardware spec instead. (ChrisH) Added description for TILECTL setting (JonB) Cc: Peter Lawthers Cc: Chris Harris Cc: Jon Bloomfield Signed-off-by: Nick Hoath Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 79cd9872bf1f..64bc77ef6b2e 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -116,18 +116,24 @@ static void bxt_init_clock_gating(struct drm_device *dev) gen9_init_clock_gating(dev); + /* WaDisableSDEUnitClockGating:bxt */ + I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | + GEN8_SDEUNIT_CLOCK_GATE_DISABLE); + /* * FIXME: - * GEN8_SDEUNIT_CLOCK_GATE_DISABLE applies on A0 only. * GEN8_HDCUNIT_CLOCK_GATE_DISABLE_HDCREQ applies on 3x6 GT SKUs only. */ - /* WaDisableSDEUnitClockGating:bxt */ I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | - GEN8_SDEUNIT_CLOCK_GATE_DISABLE | GEN8_HDCUNIT_CLOCK_GATE_DISABLE_HDCREQ); - /* FIXME: apply on A0 only */ - I915_WRITE(TILECTL, I915_READ(TILECTL) | TILECTL_TLBPF); + if (INTEL_REVID(dev) == BXT_REVID_A0) { + /* + * Hardware specification requires this bit to be + * set to 1 for A0 + */ + I915_WRITE(TILECTL, I915_READ(TILECTL) | TILECTL_TLBPF); + } } static void i915_pineview_get_mem_freq(struct drm_device *dev) From 437b15b8017e0d946453c10794b0c5d4591cf180 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 4 Sep 2015 16:55:13 +0300 Subject: [PATCH 133/145] drm/i915: use pch backlight override on hsw too Currently the difference between backlight control on HSW vs. BDW/SKL is that on HSW we modify the duty cycle on the CPU register, and have the hardware pass the changes on to the PCH registers. We still drive the PCH PWM on both. While HSW and BDW use the same LPT PCH, BDW does not pass these messages on to the PCH. Therefore on BDW we need to enable the PCH override bit, and program the PCH directly. (On SPT PCH, this mode is the default.) We could as well do this on HSW too, and in fact I've been told this is what a certain other operating system does. So use PCH backlight override on HSW too. This simplifies some follow-up code, but it does have the danger of breaking backlight on HSW machines. It should work, but mysterious are the ways of backlight. While at it, name the related backlight hooks according to the PCH rather than the CPU for clarity. Cc: Clint Taylor Signed-off-by: Jani Nikula Reviewed-by: Clint Taylor Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_panel.c | 34 ++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index e2ab3f6ed022..681686e5d550 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -484,7 +484,7 @@ static u32 intel_panel_compute_brightness(struct intel_connector *connector, return val; } -static u32 bdw_get_backlight(struct intel_connector *connector) +static u32 lpt_get_backlight(struct intel_connector *connector) { struct drm_device *dev = connector->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -576,7 +576,7 @@ static u32 intel_panel_get_backlight(struct intel_connector *connector) return val; } -static void bdw_set_backlight(struct intel_connector *connector, u32 level) +static void lpt_set_backlight(struct intel_connector *connector, u32 level) { struct drm_device *dev = connector->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -729,6 +729,18 @@ void intel_panel_set_backlight_acpi(struct intel_connector *connector, mutex_unlock(&dev_priv->backlight_lock); } +static void lpt_disable_backlight(struct intel_connector *connector) +{ + struct drm_device *dev = connector->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 tmp; + + intel_panel_actually_set_backlight(connector, 0); + + tmp = I915_READ(BLC_PWM_PCH_CTL1); + I915_WRITE(BLC_PWM_PCH_CTL1, tmp & ~BLM_PCH_PWM_ENABLE); +} + static void pch_disable_backlight(struct intel_connector *connector) { struct drm_device *dev = connector->base.dev; @@ -829,7 +841,7 @@ void intel_panel_disable_backlight(struct intel_connector *connector) mutex_unlock(&dev_priv->backlight_lock); } -static void bdw_enable_backlight(struct intel_connector *connector) +static void lpt_enable_backlight(struct intel_connector *connector) { struct drm_device *dev = connector->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -1243,7 +1255,7 @@ static u32 get_backlight_min_vbt(struct intel_connector *connector) return scale(min, 0, 255, 0, panel->backlight.max); } -static int bdw_setup_backlight(struct intel_connector *connector, enum pipe unused) +static int lpt_setup_backlight(struct intel_connector *connector, enum pipe unused) { struct drm_device *dev = connector->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -1260,7 +1272,7 @@ static int bdw_setup_backlight(struct intel_connector *connector, enum pipe unus panel->backlight.min = get_backlight_min_vbt(connector); - val = bdw_get_backlight(connector); + val = lpt_get_backlight(connector); panel->backlight.level = intel_panel_compute_brightness(connector, val); panel->backlight.enabled = (pch_ctl1 & BLM_PCH_PWM_ENABLE) && @@ -1519,12 +1531,12 @@ void intel_panel_init_backlight_funcs(struct drm_device *dev) dev_priv->display.disable_backlight = bxt_disable_backlight; dev_priv->display.set_backlight = bxt_set_backlight; dev_priv->display.get_backlight = bxt_get_backlight; - } else if (IS_BROADWELL(dev) || IS_SKYLAKE(dev)) { - dev_priv->display.setup_backlight = bdw_setup_backlight; - dev_priv->display.enable_backlight = bdw_enable_backlight; - dev_priv->display.disable_backlight = pch_disable_backlight; - dev_priv->display.set_backlight = bdw_set_backlight; - dev_priv->display.get_backlight = bdw_get_backlight; + } else if (HAS_PCH_LPT(dev) || HAS_PCH_SPT(dev)) { + dev_priv->display.setup_backlight = lpt_setup_backlight; + dev_priv->display.enable_backlight = lpt_enable_backlight; + dev_priv->display.disable_backlight = lpt_disable_backlight; + dev_priv->display.set_backlight = lpt_set_backlight; + dev_priv->display.get_backlight = lpt_get_backlight; } else if (HAS_PCH_SPLIT(dev)) { dev_priv->display.setup_backlight = pch_setup_backlight; dev_priv->display.enable_backlight = pch_enable_backlight; From aa17cdb4f8367875d3a53c41e004fc02e983e317 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 4 Sep 2015 16:55:14 +0300 Subject: [PATCH 134/145] drm/i915: initialize backlight max from VBT Normally we determine the backlight PWM modulation frequency (which we also use as backlight max value) from the backlight registers at module load time, expecting the registers have been initialized by the BIOS. If this is not the case, we fail. The VBT contains the backlight modulation frequency in Hz. Add platform specific functions to convert the frequency in Hz to backlight PWM modulation frequency, and use them to initialize the backlight when the registers are not initialized by the BIOS. v2: Fix SPT and VLV. Thanks to Clint for the VLV code. Cc: Clint Taylor Signed-off-by: Jani Nikula Reviewed-by: Clint Taylor Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 + drivers/gpu/drm/i915/i915_reg.h | 3 + drivers/gpu/drm/i915/intel_panel.c | 187 +++++++++++++++++++++++++++-- 3 files changed, 185 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 482ddb745ef8..3e46187f5b50 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -671,6 +671,8 @@ struct drm_i915_display_funcs { uint32_t level); void (*disable_backlight)(struct intel_connector *connector); void (*enable_backlight)(struct intel_connector *connector); + uint32_t (*backlight_hz_to_pwm)(struct intel_connector *connector, + uint32_t hz); }; enum forcewake_domain_id { diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 5811eebafd16..65b5682b19ac 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4648,6 +4648,7 @@ enum skl_disp_power_wells { #define CBR1_VLV (VLV_DISPLAY_BASE + 0x70400) #define CBR_PND_DEADLINE_DISABLE (1<<31) +#define CBR_PWM_CLOCK_MUX_SELECT (1<<30) /* FIFO watermark sizes etc */ #define G4X_FIFO_LINE_SIZE 64 @@ -6325,9 +6326,11 @@ enum skl_disp_power_wells { #define FDI_PHASE_SYNC_OVR(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_OVR - ((pipe) * 2))) #define FDI_PHASE_SYNC_EN(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_EN - ((pipe) * 2))) #define FDI_BC_BIFURCATION_SELECT (1 << 12) +#define SPT_PWM_GRANULARITY (1<<0) #define SOUTH_CHICKEN2 0xc2004 #define FDI_MPHY_IOSFSB_RESET_STATUS (1<<13) #define FDI_MPHY_IOSFSB_RESET_CTL (1<<12) +#define LPT_PWM_GRANULARITY (1<<5) #define DPLS_EDP_PPS_FIX_DIS (1<<0) #define _FDI_RXA_CHICKEN 0xc200c diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 681686e5d550..d3a312c409eb 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -1224,10 +1224,149 @@ static void intel_backlight_device_unregister(struct intel_connector *connector) #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */ /* - * Note: The setup hooks can't assume pipe is set! + * SPT: This value represents the period of the PWM stream in clock periods + * multiplied by 16 (default increment) or 128 (alternate increment selected in + * SCHICKEN_1 bit 0). PWM clock is 24 MHz. + */ +static u32 spt_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz) +{ + struct drm_device *dev = connector->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 mul, clock; + + if (I915_READ(SOUTH_CHICKEN1) & SPT_PWM_GRANULARITY) + mul = 128; + else + mul = 16; + + clock = MHz(24); + + return clock / (pwm_freq_hz * mul); +} + +/* + * LPT: This value represents the period of the PWM stream in clock periods + * multiplied by 128 (default increment) or 16 (alternate increment, selected in + * LPT SOUTH_CHICKEN2 register bit 5). + */ +static u32 lpt_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz) +{ + struct drm_device *dev = connector->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 mul, clock; + + if (I915_READ(SOUTH_CHICKEN2) & LPT_PWM_GRANULARITY) + mul = 16; + else + mul = 128; + + if (dev_priv->pch_id == INTEL_PCH_LPT_DEVICE_ID_TYPE) + clock = MHz(135); /* LPT:H */ + else + clock = MHz(24); /* LPT:LP */ + + return clock / (pwm_freq_hz * mul); +} + +/* + * ILK/SNB/IVB: This value represents the period of the PWM stream in PCH + * display raw clocks multiplied by 128. + */ +static u32 pch_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz) +{ + struct drm_device *dev = connector->base.dev; + int clock = MHz(intel_pch_rawclk(dev)); + + return clock / (pwm_freq_hz * 128); +} + +/* + * Gen2: This field determines the number of time base events (display core + * clock frequency/32) in total for a complete cycle of modulated backlight + * control. * - * XXX: Query mode clock or hardware clock and program PWM modulation frequency - * appropriately when it's 0. Use VBT and/or sane defaults. + * Gen3: A time base event equals the display core clock ([DevPNV] HRAW clock) + * divided by 32. + */ +static u32 i9xx_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz) +{ + struct drm_device *dev = connector->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int clock; + + if (IS_PINEVIEW(dev)) + clock = intel_hrawclk(dev); + else + clock = 1000 * dev_priv->display.get_display_clock_speed(dev); + + return clock / (pwm_freq_hz * 32); +} + +/* + * Gen4: This value represents the period of the PWM stream in display core + * clocks multiplied by 128. + */ +static u32 i965_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz) +{ + struct drm_device *dev = connector->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int clock = 1000 * dev_priv->display.get_display_clock_speed(dev); + + return clock / (pwm_freq_hz * 128); +} + +/* + * VLV: This value represents the period of the PWM stream in display core + * clocks ([DevCTG] 200MHz HRAW clocks) multiplied by 128 or 25MHz S0IX clocks + * multiplied by 16. CHV uses a 19.2MHz S0IX clock. + */ +static u32 vlv_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz) +{ + struct drm_device *dev = connector->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int clock; + + if ((I915_READ(CBR1_VLV) & CBR_PWM_CLOCK_MUX_SELECT) == 0) { + if (IS_CHERRYVIEW(dev)) + return KHz(19200) / (pwm_freq_hz * 16); + else + return MHz(25) / (pwm_freq_hz * 16); + } else { + clock = intel_hrawclk(dev); + return MHz(clock) / (pwm_freq_hz * 128); + } +} + +static u32 get_backlight_max_vbt(struct intel_connector *connector) +{ + struct drm_device *dev = connector->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u16 pwm_freq_hz = dev_priv->vbt.backlight.pwm_freq_hz; + u32 pwm; + + if (!pwm_freq_hz) { + DRM_DEBUG_KMS("backlight frequency not specified in VBT\n"); + return 0; + } + + if (!dev_priv->display.backlight_hz_to_pwm) { + DRM_DEBUG_KMS("backlight frequency setting from VBT currently not supported on this platform\n"); + return 0; + } + + pwm = dev_priv->display.backlight_hz_to_pwm(connector, pwm_freq_hz); + if (!pwm) { + DRM_DEBUG_KMS("backlight frequency conversion failed\n"); + return 0; + } + + DRM_DEBUG_KMS("backlight frequency %u Hz from VBT\n", pwm_freq_hz); + + return pwm; +} + +/* + * Note: The setup hooks can't assume pipe is set! */ static u32 get_backlight_min_vbt(struct intel_connector *connector) { @@ -1267,6 +1406,10 @@ static int lpt_setup_backlight(struct intel_connector *connector, enum pipe unus pch_ctl2 = I915_READ(BLC_PWM_PCH_CTL2); panel->backlight.max = pch_ctl2 >> 16; + + if (!panel->backlight.max) + panel->backlight.max = get_backlight_max_vbt(connector); + if (!panel->backlight.max) return -ENODEV; @@ -1293,6 +1436,10 @@ static int pch_setup_backlight(struct intel_connector *connector, enum pipe unus pch_ctl2 = I915_READ(BLC_PWM_PCH_CTL2); panel->backlight.max = pch_ctl2 >> 16; + + if (!panel->backlight.max) + panel->backlight.max = get_backlight_max_vbt(connector); + if (!panel->backlight.max) return -ENODEV; @@ -1324,12 +1471,18 @@ static int i9xx_setup_backlight(struct intel_connector *connector, enum pipe unu panel->backlight.active_low_pwm = ctl & BLM_POLARITY_PNV; panel->backlight.max = ctl >> 17; - if (panel->backlight.combination_mode) - panel->backlight.max *= 0xff; + + if (!panel->backlight.max) { + panel->backlight.max = get_backlight_max_vbt(connector); + panel->backlight.max >>= 1; + } if (!panel->backlight.max) return -ENODEV; + if (panel->backlight.combination_mode) + panel->backlight.max *= 0xff; + panel->backlight.min = get_backlight_min_vbt(connector); val = i9xx_get_backlight(connector); @@ -1353,12 +1506,16 @@ static int i965_setup_backlight(struct intel_connector *connector, enum pipe unu ctl = I915_READ(BLC_PWM_CTL); panel->backlight.max = ctl >> 16; - if (panel->backlight.combination_mode) - panel->backlight.max *= 0xff; + + if (!panel->backlight.max) + panel->backlight.max = get_backlight_max_vbt(connector); if (!panel->backlight.max) return -ENODEV; + if (panel->backlight.combination_mode) + panel->backlight.max *= 0xff; + panel->backlight.min = get_backlight_min_vbt(connector); val = i9xx_get_backlight(connector); @@ -1398,6 +1555,10 @@ static int vlv_setup_backlight(struct intel_connector *connector, enum pipe pipe ctl = I915_READ(VLV_BLC_PWM_CTL(pipe)); panel->backlight.max = ctl >> 16; + + if (!panel->backlight.max) + panel->backlight.max = get_backlight_max_vbt(connector); + if (!panel->backlight.max) return -ENODEV; @@ -1424,6 +1585,10 @@ bxt_setup_backlight(struct intel_connector *connector, enum pipe unused) panel->backlight.active_low_pwm = pwm_ctl & BXT_BLC_PWM_POLARITY; panel->backlight.max = I915_READ(BXT_BLC_PWM_FREQ1); + + if (!panel->backlight.max) + panel->backlight.max = get_backlight_max_vbt(connector); + if (!panel->backlight.max) return -ENODEV; @@ -1537,12 +1702,17 @@ void intel_panel_init_backlight_funcs(struct drm_device *dev) dev_priv->display.disable_backlight = lpt_disable_backlight; dev_priv->display.set_backlight = lpt_set_backlight; dev_priv->display.get_backlight = lpt_get_backlight; + if (HAS_PCH_LPT(dev)) + dev_priv->display.backlight_hz_to_pwm = lpt_hz_to_pwm; + else + dev_priv->display.backlight_hz_to_pwm = spt_hz_to_pwm; } else if (HAS_PCH_SPLIT(dev)) { dev_priv->display.setup_backlight = pch_setup_backlight; dev_priv->display.enable_backlight = pch_enable_backlight; dev_priv->display.disable_backlight = pch_disable_backlight; dev_priv->display.set_backlight = pch_set_backlight; dev_priv->display.get_backlight = pch_get_backlight; + dev_priv->display.backlight_hz_to_pwm = pch_hz_to_pwm; } else if (IS_VALLEYVIEW(dev)) { if (dev_priv->vbt.has_mipi) { dev_priv->display.setup_backlight = pwm_setup_backlight; @@ -1556,6 +1726,7 @@ void intel_panel_init_backlight_funcs(struct drm_device *dev) dev_priv->display.disable_backlight = vlv_disable_backlight; dev_priv->display.set_backlight = vlv_set_backlight; dev_priv->display.get_backlight = vlv_get_backlight; + dev_priv->display.backlight_hz_to_pwm = vlv_hz_to_pwm; } } else if (IS_GEN4(dev)) { dev_priv->display.setup_backlight = i965_setup_backlight; @@ -1563,12 +1734,14 @@ void intel_panel_init_backlight_funcs(struct drm_device *dev) dev_priv->display.disable_backlight = i965_disable_backlight; dev_priv->display.set_backlight = i9xx_set_backlight; dev_priv->display.get_backlight = i9xx_get_backlight; + dev_priv->display.backlight_hz_to_pwm = i965_hz_to_pwm; } else { dev_priv->display.setup_backlight = i9xx_setup_backlight; dev_priv->display.enable_backlight = i9xx_enable_backlight; dev_priv->display.disable_backlight = i9xx_disable_backlight; dev_priv->display.set_backlight = i9xx_set_backlight; dev_priv->display.get_backlight = i9xx_get_backlight; + dev_priv->display.backlight_hz_to_pwm = i9xx_hz_to_pwm; } } From 4a999d253195db87f18215049cf7a44f795f4ca0 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 4 Sep 2015 16:55:15 +0300 Subject: [PATCH 135/145] drm/i915: don't hard code vlv backlight frequency if unset Fall back to VBT based backlight modulation frequency if it's not set. Do not hard code. This could be a problem if there is no VBT. Cc: Clint Taylor Signed-off-by: Jani Nikula Tested-by: Clint Taylor Reviewed-by: Clint Taylor Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_panel.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index d3a312c409eb..2034438a0664 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -1532,21 +1532,8 @@ static int vlv_setup_backlight(struct intel_connector *connector, enum pipe pipe struct drm_device *dev = connector->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_panel *panel = &connector->panel; - enum pipe p; u32 ctl, ctl2, val; - for_each_pipe(dev_priv, p) { - u32 cur_val = I915_READ(VLV_BLC_PWM_CTL(p)); - - /* Skip if the modulation freq is already set */ - if (cur_val & ~BACKLIGHT_DUTY_CYCLE_MASK) - continue; - - cur_val &= BACKLIGHT_DUTY_CYCLE_MASK; - I915_WRITE(VLV_BLC_PWM_CTL(p), (0xf42 << 16) | - cur_val); - } - if (WARN_ON(pipe != PIPE_A && pipe != PIPE_B)) return -ENODEV; From d8b4c43a4baccecee7106c1432baa986fab9593a Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 4 Sep 2015 18:56:11 +0530 Subject: [PATCH 136/145] drm/i915: add attached connector to hdmi container This patch adds the intel_connector initialized to intel_hdmi display, during the init phase, just like the other encoders do. This attachment is very useful when we need to extract the connector pointer during the hotplug handler function Signed-off-by: Shashank Sharma Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_hdmi.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 2bdd5449b889..129d99e33bc2 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -669,6 +669,7 @@ struct intel_hdmi { enum hdmi_force_audio force_audio; bool rgb_quant_range_selectable; enum hdmi_picture_aspect aspect_ratio; + struct intel_connector *attached_connector; void (*write_infoframe)(struct drm_encoder *encoder, enum hdmi_infoframe_type type, const void *frame, ssize_t len); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index feb31d891482..e978c59dc243 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -2120,6 +2120,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, intel_connector_attach_encoder(intel_connector, intel_encoder); drm_connector_register(connector); + intel_hdmi->attached_connector = intel_connector; /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written * 0xd. Failure to do so will result in spurious interrupts being From d4b08630fb6e3fe9948456b09feb223fba2b48a6 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 10 Sep 2015 16:07:56 +0200 Subject: [PATCH 137/145] drm/i915: Use atomic plane state in the primary plane update. Signed-off-by: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 110b8dc7bbd0..0f5df11dabd6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13427,7 +13427,9 @@ intel_commit_primary_plane(struct drm_plane *plane, /* FIXME: kill this fastboot hack */ intel_update_pipe_size(intel_crtc); - dev_priv->display.update_primary_plane(crtc, fb, crtc->x, crtc->y); + dev_priv->display.update_primary_plane(crtc, fb, + state->src.x1 >> 16, + state->src.y1 >> 16); } static void From 23a48d5323bc488f0e350467996e7226f902e0a7 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 10 Sep 2015 16:07:57 +0200 Subject: [PATCH 138/145] drm/i915: Use the plane state in intel_crtc_info. Legacy state might not be updated any more. Signed-off-by: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 41629faaf939..72ae3472ddbe 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -2817,11 +2817,13 @@ static void intel_crtc_info(struct seq_file *m, struct intel_crtc *intel_crtc) struct drm_device *dev = node->minor->dev; struct drm_crtc *crtc = &intel_crtc->base; struct intel_encoder *intel_encoder; + struct drm_plane_state *plane_state = crtc->primary->state; + struct drm_framebuffer *fb = plane_state->fb; - if (crtc->primary->fb) + if (fb) seq_printf(m, "\tfb: %d, pos: %dx%d, size: %dx%d\n", - crtc->primary->fb->base.id, crtc->x, crtc->y, - crtc->primary->fb->width, crtc->primary->fb->height); + fb->base.id, plane_state->src_x >> 16, + plane_state->src_y >> 16, fb->width, fb->height); else seq_puts(m, "\tprimary plane disabled\n"); for_each_encoder_on_crtc(dev, crtc, intel_encoder) From 11c22da619101a5a4332df718268e6214e3919f3 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 10 Sep 2015 16:07:58 +0200 Subject: [PATCH 139/145] drm/i915: Use the atomic state in intel_update_primary_planes. This function was still using the legacy state, convert it to atomic. While we're at it, fix the FIXME too and disable the primary plane. v2 (Daniel): - Add FIXME explaining that update_primary_planes should soon get removed anyway. - Don't call ->disable_plane since we can't disable the primary plane with a CS flip (noticed by Ville). Signed-off-by: Maarten Lankhorst (v1) Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0f5df11dabd6..b7e61588a35d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3135,24 +3135,20 @@ static void intel_complete_page_flips(struct drm_device *dev) static void intel_update_primary_planes(struct drm_device *dev) { - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc; for_each_crtc(dev, crtc) { - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_plane *plane = to_intel_plane(crtc->primary); + struct intel_plane_state *plane_state; - drm_modeset_lock(&crtc->mutex, NULL); - /* - * FIXME: Once we have proper support for primary planes (and - * disabling them without disabling the entire crtc) allow again - * a NULL crtc->primary->fb. - */ - if (intel_crtc->active && crtc->primary->fb) - dev_priv->display.update_primary_plane(crtc, - crtc->primary->fb, - crtc->x, - crtc->y); - drm_modeset_unlock(&crtc->mutex); + drm_modeset_lock_crtc(crtc, &plane->base); + + plane_state = to_intel_plane_state(plane->base.state); + + if (plane_state->base.fb) + plane->commit_plane(&plane->base, plane_state); + + drm_modeset_unlock_crtc(crtc); } } @@ -3196,6 +3192,9 @@ void intel_finish_reset(struct drm_device *dev) * so update the base address of all primary * planes to the the last fb to make sure we're * showing the correct fb after a reset. + * + * FIXME: Atomic will make this obsolete since we won't schedule + * CS-based flips (which might get lost in gpu resets) any more. */ intel_update_primary_planes(dev); return; From 9b4101be5bb165484d143629d17d084d76fdd77c Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 10 Sep 2015 16:07:59 +0200 Subject: [PATCH 140/145] drm/i915: Use atomic state when changing cursor visibility. Signed-off-by: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b7e61588a35d..ea3a4a5eb4fb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9916,8 +9916,9 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; - int x = crtc->cursor_x; - int y = crtc->cursor_y; + struct drm_plane_state *cursor_state = crtc->cursor->state; + int x = cursor_state->crtc_x; + int y = cursor_state->crtc_y; u32 base = 0, pos = 0; if (on) @@ -9930,7 +9931,7 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, base = 0; if (x < 0) { - if (x + intel_crtc->base.cursor->state->crtc_w <= 0) + if (x + cursor_state->crtc_w <= 0) base = 0; pos |= CURSOR_POS_SIGN << CURSOR_X_SHIFT; @@ -9939,7 +9940,7 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, pos |= x << CURSOR_X_SHIFT; if (y < 0) { - if (y + intel_crtc->base.cursor->state->crtc_h <= 0) + if (y + cursor_state->crtc_h <= 0) base = 0; pos |= CURSOR_POS_SIGN << CURSOR_Y_SHIFT; @@ -9955,8 +9956,8 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, /* ILK+ do this automagically */ if (HAS_GMCH_DISPLAY(dev) && crtc->cursor->state->rotation == BIT(DRM_ROTATE_180)) { - base += (intel_crtc->base.cursor->state->crtc_h * - intel_crtc->base.cursor->state->crtc_w - 1) * 4; + base += (cursor_state->crtc_h * + cursor_state->crtc_w - 1) * 4; } if (IS_845G(dev) || IS_I865G(dev)) From 8e0e9ceaa05317c6644635f8816705b609f2c274 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 10 Sep 2015 16:08:00 +0200 Subject: [PATCH 141/145] drm/i915: Remove legacy plane updates for cursor and sprite planes. Unfortunately fbc still depends on legacy primary state, so it can't be killed off completely yet. Signed-off-by: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 4 ---- drivers/gpu/drm/i915/intel_sprite.c | 2 -- 2 files changed, 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ea3a4a5eb4fb..62573d5595fa 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13634,10 +13634,6 @@ intel_commit_cursor_plane(struct drm_plane *plane, crtc = crtc ? crtc : plane->crtc; intel_crtc = to_intel_crtc(crtc); - plane->fb = state->base.fb; - crtc->cursor_x = state->base.crtc_x; - crtc->cursor_y = state->base.crtc_y; - if (intel_crtc->cursor_bo == obj) goto update; diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index ca7e26430e66..9553859ca151 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -927,8 +927,6 @@ intel_commit_sprite_plane(struct drm_plane *plane, crtc = crtc ? crtc : plane->crtc; - plane->fb = fb; - if (!crtc->state->active) return; From bca8013ec24210087bfed49678ec821c811982b1 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 10 Sep 2015 16:08:02 +0200 Subject: [PATCH 142/145] drm/i915: Do not handle a null plane state. With the conversion to atomic this cannot happen any more. Signed-off-by: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_atomic_plane.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_atomic_plane.c b/drivers/gpu/drm/i915/intel_atomic_plane.c index f1ab8e4b9c11..a11980696595 100644 --- a/drivers/gpu/drm/i915/intel_atomic_plane.c +++ b/drivers/gpu/drm/i915/intel_atomic_plane.c @@ -76,11 +76,7 @@ intel_plane_duplicate_state(struct drm_plane *plane) struct drm_plane_state *state; struct intel_plane_state *intel_state; - if (WARN_ON(!plane->state)) - intel_state = intel_create_plane_state(plane); - else - intel_state = kmemdup(plane->state, sizeof(*intel_state), - GFP_KERNEL); + intel_state = kmemdup(plane->state, sizeof(*intel_state), GFP_KERNEL); if (!intel_state) return NULL; From f2a066f3dee8a0e37015d15298e14e9ad6c0a53d Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 10 Sep 2015 16:08:03 +0200 Subject: [PATCH 143/145] drm/i915: Use crtc->state for duplication. In async mode crtc->config can be updated after the locks are released, resulting in the wrong state being duplicated. Note that this also removes a spurious assignment of crtc_state->crtc introduced in commit f0c60574eb1216b0904c0d696c64d5096d6e4913 Author: Ander Conselvan de Oliveira Date: Tue Apr 21 17:12:58 2015 +0300 drm/i915: Call drm helpers when duplicating crtc and plane states Signed-off-by: Maarten Lankhorst Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_atomic.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 9336e8030980..2c4b1c44296c 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -85,22 +85,14 @@ intel_connector_atomic_get_property(struct drm_connector *connector, struct drm_crtc_state * intel_crtc_duplicate_state(struct drm_crtc *crtc) { - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_crtc_state *crtc_state; - if (WARN_ON(!intel_crtc->config)) - crtc_state = kzalloc(sizeof(*crtc_state), GFP_KERNEL); - else - crtc_state = kmemdup(intel_crtc->config, - sizeof(*intel_crtc->config), GFP_KERNEL); - + crtc_state = kmemdup(crtc->state, sizeof(*crtc_state), GFP_KERNEL); if (!crtc_state) return NULL; __drm_atomic_helper_crtc_duplicate_state(crtc, &crtc_state->base); - crtc_state->base.crtc = crtc; - return &crtc_state->base; } From f0fea8dd362c77a06e7b8744d63f429fe065c6b4 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Tue, 8 Sep 2015 12:52:48 +0100 Subject: [PATCH 144/145] drm/i915: Remove one very outdated comment Comment disagrees with the code which has changed a lot since it was documented. Note that the logic to remove -EIO handling was dropped in commit 1488fc08c1706288616c602416654fd38c773deb Author: Chris Wilson Date: Tue Apr 24 15:47:31 2012 +0100 drm/i915: Remove the deferred-free list Signed-off-by: Tvrtko Ursulin Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 41263cd4170c..760cfba22e71 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3230,10 +3230,6 @@ int i915_vma_unbind(struct i915_vma *vma) ret = i915_gem_object_wait_rendering(obj, false); if (ret) return ret; - /* Continue on if we fail due to EIO, the GPU is hung so we - * should be safe and we need to cleanup or else we might - * cause memory corruption through use-after-free. - */ if (i915_is_ggtt(vma->vm) && vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL) { From fd1ee4cc9326c97b52154ee2ef8cdd23ac6aae1c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 11 Sep 2015 21:57:24 +0200 Subject: [PATCH 145/145] drm/i915: Update DRIVER_DATE to 20150911 Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3e46187f5b50..da0f2640d1d7 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -57,7 +57,7 @@ #define DRIVER_NAME "i915" #define DRIVER_DESC "Intel Graphics" -#define DRIVER_DATE "20150828" +#define DRIVER_DATE "20150911" #undef WARN_ON /* Many gcc seem to no see through this and fall over :( */