18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright © 2012 Intel Corporation 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next 128c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 138c2ecf20Sopenharmony_ci * Software. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 188c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 198c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 208c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 218c2ecf20Sopenharmony_ci * IN THE SOFTWARE. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Authors: 248c2ecf20Sopenharmony_ci * Eugeni Dodonov <eugeni.dodonov@intel.com> 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <linux/module.h> 298c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 328c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 338c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include "display/intel_atomic.h" 368c2ecf20Sopenharmony_ci#include "display/intel_bw.h" 378c2ecf20Sopenharmony_ci#include "display/intel_display_types.h" 388c2ecf20Sopenharmony_ci#include "display/intel_fbc.h" 398c2ecf20Sopenharmony_ci#include "display/intel_sprite.h" 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#include "gt/intel_llc.h" 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#include "i915_drv.h" 448c2ecf20Sopenharmony_ci#include "i915_fixed.h" 458c2ecf20Sopenharmony_ci#include "i915_irq.h" 468c2ecf20Sopenharmony_ci#include "i915_trace.h" 478c2ecf20Sopenharmony_ci#include "intel_pm.h" 488c2ecf20Sopenharmony_ci#include "intel_sideband.h" 498c2ecf20Sopenharmony_ci#include "../../../platform/x86/intel_ips.h" 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Stores plane specific WM parameters */ 528c2ecf20Sopenharmony_cistruct skl_wm_params { 538c2ecf20Sopenharmony_ci bool x_tiled, y_tiled; 548c2ecf20Sopenharmony_ci bool rc_surface; 558c2ecf20Sopenharmony_ci bool is_planar; 568c2ecf20Sopenharmony_ci u32 width; 578c2ecf20Sopenharmony_ci u8 cpp; 588c2ecf20Sopenharmony_ci u32 plane_pixel_rate; 598c2ecf20Sopenharmony_ci u32 y_min_scanlines; 608c2ecf20Sopenharmony_ci u32 plane_bytes_per_line; 618c2ecf20Sopenharmony_ci uint_fixed_16_16_t plane_blocks_per_line; 628c2ecf20Sopenharmony_ci uint_fixed_16_16_t y_tile_minimum; 638c2ecf20Sopenharmony_ci u32 linetime_us; 648c2ecf20Sopenharmony_ci u32 dbuf_block_size; 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* used in computing the new watermarks state */ 688c2ecf20Sopenharmony_cistruct intel_wm_config { 698c2ecf20Sopenharmony_ci unsigned int num_pipes_active; 708c2ecf20Sopenharmony_ci bool sprites_enabled; 718c2ecf20Sopenharmony_ci bool sprites_scaled; 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void gen9_init_clock_gating(struct drm_i915_private *dev_priv) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci if (HAS_LLC(dev_priv)) { 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * WaCompressedResourceDisplayNewHashMode:skl,kbl 798c2ecf20Sopenharmony_ci * Display WA #0390: skl,kbl 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * Must match Sampler, Pixel Back End, and Media. See 828c2ecf20Sopenharmony_ci * WaCompressedResourceSamplerPbeMediaNewHashMode. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci I915_WRITE(CHICKEN_PAR1_1, 858c2ecf20Sopenharmony_ci I915_READ(CHICKEN_PAR1_1) | 868c2ecf20Sopenharmony_ci SKL_DE_COMPRESSED_HASH_MODE); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* See Bspec note for PSR2_CTL bit 31, Wa#828:skl,bxt,kbl,cfl */ 908c2ecf20Sopenharmony_ci I915_WRITE(CHICKEN_PAR1_1, 918c2ecf20Sopenharmony_ci I915_READ(CHICKEN_PAR1_1) | SKL_EDP_PSR_FIX_RDWRAP); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* WaEnableChickenDCPR:skl,bxt,kbl,glk,cfl */ 948c2ecf20Sopenharmony_ci I915_WRITE(GEN8_CHICKEN_DCPR_1, 958c2ecf20Sopenharmony_ci I915_READ(GEN8_CHICKEN_DCPR_1) | MASK_WAKEMEM); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* 988c2ecf20Sopenharmony_ci * WaFbcWakeMemOn:skl,bxt,kbl,glk,cfl 998c2ecf20Sopenharmony_ci * Display WA #0859: skl,bxt,kbl,glk,cfl 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci I915_WRITE(DISP_ARB_CTL, I915_READ(DISP_ARB_CTL) | 1028c2ecf20Sopenharmony_ci DISP_FBC_MEMORY_WAKE); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void bxt_init_clock_gating(struct drm_i915_private *dev_priv) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci gen9_init_clock_gating(dev_priv); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* WaDisableSDEUnitClockGating:bxt */ 1108c2ecf20Sopenharmony_ci I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | 1118c2ecf20Sopenharmony_ci GEN8_SDEUNIT_CLOCK_GATE_DISABLE); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* 1148c2ecf20Sopenharmony_ci * FIXME: 1158c2ecf20Sopenharmony_ci * GEN8_HDCUNIT_CLOCK_GATE_DISABLE_HDCREQ applies on 3x6 GT SKUs only. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | 1188c2ecf20Sopenharmony_ci GEN8_HDCUNIT_CLOCK_GATE_DISABLE_HDCREQ); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* 1218c2ecf20Sopenharmony_ci * Wa: Backlight PWM may stop in the asserted state, causing backlight 1228c2ecf20Sopenharmony_ci * to stay fully on. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci I915_WRITE(GEN9_CLKGATE_DIS_0, I915_READ(GEN9_CLKGATE_DIS_0) | 1258c2ecf20Sopenharmony_ci PWM1_GATING_DIS | PWM2_GATING_DIS); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* 1288c2ecf20Sopenharmony_ci * Lower the display internal timeout. 1298c2ecf20Sopenharmony_ci * This is needed to avoid any hard hangs when DSI port PLL 1308c2ecf20Sopenharmony_ci * is off and a MMIO access is attempted by any privilege 1318c2ecf20Sopenharmony_ci * application, using batch buffers or any other means. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci I915_WRITE(RM_TIMEOUT, MMIO_TIMEOUT_US(950)); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* 1368c2ecf20Sopenharmony_ci * WaFbcTurnOffFbcWatermark:bxt 1378c2ecf20Sopenharmony_ci * Display WA #0562: bxt 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_ci I915_WRITE(DISP_ARB_CTL, I915_READ(DISP_ARB_CTL) | 1408c2ecf20Sopenharmony_ci DISP_FBC_WM_DIS); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci * WaFbcHighMemBwCorruptionAvoidance:bxt 1448c2ecf20Sopenharmony_ci * Display WA #0883: bxt 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_ci I915_WRITE(ILK_DPFC_CHICKEN, I915_READ(ILK_DPFC_CHICKEN) | 1478c2ecf20Sopenharmony_ci ILK_DPFC_DISABLE_DUMMY0); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void glk_init_clock_gating(struct drm_i915_private *dev_priv) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci gen9_init_clock_gating(dev_priv); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* 1558c2ecf20Sopenharmony_ci * WaDisablePWMClockGating:glk 1568c2ecf20Sopenharmony_ci * Backlight PWM may stop in the asserted state, causing backlight 1578c2ecf20Sopenharmony_ci * to stay fully on. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci I915_WRITE(GEN9_CLKGATE_DIS_0, I915_READ(GEN9_CLKGATE_DIS_0) | 1608c2ecf20Sopenharmony_ci PWM1_GATING_DIS | PWM2_GATING_DIS); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void pnv_get_mem_freq(struct drm_i915_private *dev_priv) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci u32 tmp; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci tmp = I915_READ(CLKCFG); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci switch (tmp & CLKCFG_FSB_MASK) { 1708c2ecf20Sopenharmony_ci case CLKCFG_FSB_533: 1718c2ecf20Sopenharmony_ci dev_priv->fsb_freq = 533; /* 133*4 */ 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci case CLKCFG_FSB_800: 1748c2ecf20Sopenharmony_ci dev_priv->fsb_freq = 800; /* 200*4 */ 1758c2ecf20Sopenharmony_ci break; 1768c2ecf20Sopenharmony_ci case CLKCFG_FSB_667: 1778c2ecf20Sopenharmony_ci dev_priv->fsb_freq = 667; /* 167*4 */ 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci case CLKCFG_FSB_400: 1808c2ecf20Sopenharmony_ci dev_priv->fsb_freq = 400; /* 100*4 */ 1818c2ecf20Sopenharmony_ci break; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci switch (tmp & CLKCFG_MEM_MASK) { 1858c2ecf20Sopenharmony_ci case CLKCFG_MEM_533: 1868c2ecf20Sopenharmony_ci dev_priv->mem_freq = 533; 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci case CLKCFG_MEM_667: 1898c2ecf20Sopenharmony_ci dev_priv->mem_freq = 667; 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci case CLKCFG_MEM_800: 1928c2ecf20Sopenharmony_ci dev_priv->mem_freq = 800; 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* detect pineview DDR3 setting */ 1978c2ecf20Sopenharmony_ci tmp = I915_READ(CSHRDDR3CTL); 1988c2ecf20Sopenharmony_ci dev_priv->is_ddr3 = (tmp & CSHRDDR3CTL_DDR3) ? 1 : 0; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic void ilk_get_mem_freq(struct drm_i915_private *dev_priv) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci u16 ddrpll, csipll; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ddrpll = intel_uncore_read16(&dev_priv->uncore, DDRMPLL1); 2068c2ecf20Sopenharmony_ci csipll = intel_uncore_read16(&dev_priv->uncore, CSIPLL0); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci switch (ddrpll & 0xff) { 2098c2ecf20Sopenharmony_ci case 0xc: 2108c2ecf20Sopenharmony_ci dev_priv->mem_freq = 800; 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci case 0x10: 2138c2ecf20Sopenharmony_ci dev_priv->mem_freq = 1066; 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci case 0x14: 2168c2ecf20Sopenharmony_ci dev_priv->mem_freq = 1333; 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci case 0x18: 2198c2ecf20Sopenharmony_ci dev_priv->mem_freq = 1600; 2208c2ecf20Sopenharmony_ci break; 2218c2ecf20Sopenharmony_ci default: 2228c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, "unknown memory frequency 0x%02x\n", 2238c2ecf20Sopenharmony_ci ddrpll & 0xff); 2248c2ecf20Sopenharmony_ci dev_priv->mem_freq = 0; 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci switch (csipll & 0x3ff) { 2298c2ecf20Sopenharmony_ci case 0x00c: 2308c2ecf20Sopenharmony_ci dev_priv->fsb_freq = 3200; 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci case 0x00e: 2338c2ecf20Sopenharmony_ci dev_priv->fsb_freq = 3733; 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci case 0x010: 2368c2ecf20Sopenharmony_ci dev_priv->fsb_freq = 4266; 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci case 0x012: 2398c2ecf20Sopenharmony_ci dev_priv->fsb_freq = 4800; 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci case 0x014: 2428c2ecf20Sopenharmony_ci dev_priv->fsb_freq = 5333; 2438c2ecf20Sopenharmony_ci break; 2448c2ecf20Sopenharmony_ci case 0x016: 2458c2ecf20Sopenharmony_ci dev_priv->fsb_freq = 5866; 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci case 0x018: 2488c2ecf20Sopenharmony_ci dev_priv->fsb_freq = 6400; 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci default: 2518c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, "unknown fsb frequency 0x%04x\n", 2528c2ecf20Sopenharmony_ci csipll & 0x3ff); 2538c2ecf20Sopenharmony_ci dev_priv->fsb_freq = 0; 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic const struct cxsr_latency cxsr_latency_table[] = { 2598c2ecf20Sopenharmony_ci {1, 0, 800, 400, 3382, 33382, 3983, 33983}, /* DDR2-400 SC */ 2608c2ecf20Sopenharmony_ci {1, 0, 800, 667, 3354, 33354, 3807, 33807}, /* DDR2-667 SC */ 2618c2ecf20Sopenharmony_ci {1, 0, 800, 800, 3347, 33347, 3763, 33763}, /* DDR2-800 SC */ 2628c2ecf20Sopenharmony_ci {1, 1, 800, 667, 6420, 36420, 6873, 36873}, /* DDR3-667 SC */ 2638c2ecf20Sopenharmony_ci {1, 1, 800, 800, 5902, 35902, 6318, 36318}, /* DDR3-800 SC */ 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci {1, 0, 667, 400, 3400, 33400, 4021, 34021}, /* DDR2-400 SC */ 2668c2ecf20Sopenharmony_ci {1, 0, 667, 667, 3372, 33372, 3845, 33845}, /* DDR2-667 SC */ 2678c2ecf20Sopenharmony_ci {1, 0, 667, 800, 3386, 33386, 3822, 33822}, /* DDR2-800 SC */ 2688c2ecf20Sopenharmony_ci {1, 1, 667, 667, 6438, 36438, 6911, 36911}, /* DDR3-667 SC */ 2698c2ecf20Sopenharmony_ci {1, 1, 667, 800, 5941, 35941, 6377, 36377}, /* DDR3-800 SC */ 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci {1, 0, 400, 400, 3472, 33472, 4173, 34173}, /* DDR2-400 SC */ 2728c2ecf20Sopenharmony_ci {1, 0, 400, 667, 3443, 33443, 3996, 33996}, /* DDR2-667 SC */ 2738c2ecf20Sopenharmony_ci {1, 0, 400, 800, 3430, 33430, 3946, 33946}, /* DDR2-800 SC */ 2748c2ecf20Sopenharmony_ci {1, 1, 400, 667, 6509, 36509, 7062, 37062}, /* DDR3-667 SC */ 2758c2ecf20Sopenharmony_ci {1, 1, 400, 800, 5985, 35985, 6501, 36501}, /* DDR3-800 SC */ 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci {0, 0, 800, 400, 3438, 33438, 4065, 34065}, /* DDR2-400 SC */ 2788c2ecf20Sopenharmony_ci {0, 0, 800, 667, 3410, 33410, 3889, 33889}, /* DDR2-667 SC */ 2798c2ecf20Sopenharmony_ci {0, 0, 800, 800, 3403, 33403, 3845, 33845}, /* DDR2-800 SC */ 2808c2ecf20Sopenharmony_ci {0, 1, 800, 667, 6476, 36476, 6955, 36955}, /* DDR3-667 SC */ 2818c2ecf20Sopenharmony_ci {0, 1, 800, 800, 5958, 35958, 6400, 36400}, /* DDR3-800 SC */ 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci {0, 0, 667, 400, 3456, 33456, 4103, 34106}, /* DDR2-400 SC */ 2848c2ecf20Sopenharmony_ci {0, 0, 667, 667, 3428, 33428, 3927, 33927}, /* DDR2-667 SC */ 2858c2ecf20Sopenharmony_ci {0, 0, 667, 800, 3443, 33443, 3905, 33905}, /* DDR2-800 SC */ 2868c2ecf20Sopenharmony_ci {0, 1, 667, 667, 6494, 36494, 6993, 36993}, /* DDR3-667 SC */ 2878c2ecf20Sopenharmony_ci {0, 1, 667, 800, 5998, 35998, 6460, 36460}, /* DDR3-800 SC */ 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci {0, 0, 400, 400, 3528, 33528, 4255, 34255}, /* DDR2-400 SC */ 2908c2ecf20Sopenharmony_ci {0, 0, 400, 667, 3500, 33500, 4079, 34079}, /* DDR2-667 SC */ 2918c2ecf20Sopenharmony_ci {0, 0, 400, 800, 3487, 33487, 4029, 34029}, /* DDR2-800 SC */ 2928c2ecf20Sopenharmony_ci {0, 1, 400, 667, 6566, 36566, 7145, 37145}, /* DDR3-667 SC */ 2938c2ecf20Sopenharmony_ci {0, 1, 400, 800, 6042, 36042, 6584, 36584}, /* DDR3-800 SC */ 2948c2ecf20Sopenharmony_ci}; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic const struct cxsr_latency *intel_get_cxsr_latency(bool is_desktop, 2978c2ecf20Sopenharmony_ci bool is_ddr3, 2988c2ecf20Sopenharmony_ci int fsb, 2998c2ecf20Sopenharmony_ci int mem) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci const struct cxsr_latency *latency; 3028c2ecf20Sopenharmony_ci int i; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (fsb == 0 || mem == 0) 3058c2ecf20Sopenharmony_ci return NULL; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cxsr_latency_table); i++) { 3088c2ecf20Sopenharmony_ci latency = &cxsr_latency_table[i]; 3098c2ecf20Sopenharmony_ci if (is_desktop == latency->is_desktop && 3108c2ecf20Sopenharmony_ci is_ddr3 == latency->is_ddr3 && 3118c2ecf20Sopenharmony_ci fsb == latency->fsb_freq && mem == latency->mem_freq) 3128c2ecf20Sopenharmony_ci return latency; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n"); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return NULL; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic void chv_set_memory_dvfs(struct drm_i915_private *dev_priv, bool enable) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci u32 val; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci vlv_punit_get(dev_priv); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci val = vlv_punit_read(dev_priv, PUNIT_REG_DDR_SETUP2); 3278c2ecf20Sopenharmony_ci if (enable) 3288c2ecf20Sopenharmony_ci val &= ~FORCE_DDR_HIGH_FREQ; 3298c2ecf20Sopenharmony_ci else 3308c2ecf20Sopenharmony_ci val |= FORCE_DDR_HIGH_FREQ; 3318c2ecf20Sopenharmony_ci val &= ~FORCE_DDR_LOW_FREQ; 3328c2ecf20Sopenharmony_ci val |= FORCE_DDR_FREQ_REQ_ACK; 3338c2ecf20Sopenharmony_ci vlv_punit_write(dev_priv, PUNIT_REG_DDR_SETUP2, val); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (wait_for((vlv_punit_read(dev_priv, PUNIT_REG_DDR_SETUP2) & 3368c2ecf20Sopenharmony_ci FORCE_DDR_FREQ_REQ_ACK) == 0, 3)) 3378c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, 3388c2ecf20Sopenharmony_ci "timed out waiting for Punit DDR DVFS request\n"); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci vlv_punit_put(dev_priv); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic void chv_set_memory_pm5(struct drm_i915_private *dev_priv, bool enable) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci u32 val; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci vlv_punit_get(dev_priv); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci val = vlv_punit_read(dev_priv, PUNIT_REG_DSPSSPM); 3508c2ecf20Sopenharmony_ci if (enable) 3518c2ecf20Sopenharmony_ci val |= DSP_MAXFIFO_PM5_ENABLE; 3528c2ecf20Sopenharmony_ci else 3538c2ecf20Sopenharmony_ci val &= ~DSP_MAXFIFO_PM5_ENABLE; 3548c2ecf20Sopenharmony_ci vlv_punit_write(dev_priv, PUNIT_REG_DSPSSPM, val); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci vlv_punit_put(dev_priv); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci#define FW_WM(value, plane) \ 3608c2ecf20Sopenharmony_ci (((value) << DSPFW_ ## plane ## _SHIFT) & DSPFW_ ## plane ## _MASK) 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic bool _intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci bool was_enabled; 3658c2ecf20Sopenharmony_ci u32 val; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { 3688c2ecf20Sopenharmony_ci was_enabled = I915_READ(FW_BLC_SELF_VLV) & FW_CSPWRDWNEN; 3698c2ecf20Sopenharmony_ci I915_WRITE(FW_BLC_SELF_VLV, enable ? FW_CSPWRDWNEN : 0); 3708c2ecf20Sopenharmony_ci POSTING_READ(FW_BLC_SELF_VLV); 3718c2ecf20Sopenharmony_ci } else if (IS_G4X(dev_priv) || IS_I965GM(dev_priv)) { 3728c2ecf20Sopenharmony_ci was_enabled = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN; 3738c2ecf20Sopenharmony_ci I915_WRITE(FW_BLC_SELF, enable ? FW_BLC_SELF_EN : 0); 3748c2ecf20Sopenharmony_ci POSTING_READ(FW_BLC_SELF); 3758c2ecf20Sopenharmony_ci } else if (IS_PINEVIEW(dev_priv)) { 3768c2ecf20Sopenharmony_ci val = I915_READ(DSPFW3); 3778c2ecf20Sopenharmony_ci was_enabled = val & PINEVIEW_SELF_REFRESH_EN; 3788c2ecf20Sopenharmony_ci if (enable) 3798c2ecf20Sopenharmony_ci val |= PINEVIEW_SELF_REFRESH_EN; 3808c2ecf20Sopenharmony_ci else 3818c2ecf20Sopenharmony_ci val &= ~PINEVIEW_SELF_REFRESH_EN; 3828c2ecf20Sopenharmony_ci I915_WRITE(DSPFW3, val); 3838c2ecf20Sopenharmony_ci POSTING_READ(DSPFW3); 3848c2ecf20Sopenharmony_ci } else if (IS_I945G(dev_priv) || IS_I945GM(dev_priv)) { 3858c2ecf20Sopenharmony_ci was_enabled = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN; 3868c2ecf20Sopenharmony_ci val = enable ? _MASKED_BIT_ENABLE(FW_BLC_SELF_EN) : 3878c2ecf20Sopenharmony_ci _MASKED_BIT_DISABLE(FW_BLC_SELF_EN); 3888c2ecf20Sopenharmony_ci I915_WRITE(FW_BLC_SELF, val); 3898c2ecf20Sopenharmony_ci POSTING_READ(FW_BLC_SELF); 3908c2ecf20Sopenharmony_ci } else if (IS_I915GM(dev_priv)) { 3918c2ecf20Sopenharmony_ci /* 3928c2ecf20Sopenharmony_ci * FIXME can't find a bit like this for 915G, and 3938c2ecf20Sopenharmony_ci * and yet it does have the related watermark in 3948c2ecf20Sopenharmony_ci * FW_BLC_SELF. What's going on? 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ci was_enabled = I915_READ(INSTPM) & INSTPM_SELF_EN; 3978c2ecf20Sopenharmony_ci val = enable ? _MASKED_BIT_ENABLE(INSTPM_SELF_EN) : 3988c2ecf20Sopenharmony_ci _MASKED_BIT_DISABLE(INSTPM_SELF_EN); 3998c2ecf20Sopenharmony_ci I915_WRITE(INSTPM, val); 4008c2ecf20Sopenharmony_ci POSTING_READ(INSTPM); 4018c2ecf20Sopenharmony_ci } else { 4028c2ecf20Sopenharmony_ci return false; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci trace_intel_memory_cxsr(dev_priv, was_enabled, enable); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, "memory self-refresh is %s (was %s)\n", 4088c2ecf20Sopenharmony_ci enableddisabled(enable), 4098c2ecf20Sopenharmony_ci enableddisabled(was_enabled)); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return was_enabled; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci/** 4158c2ecf20Sopenharmony_ci * intel_set_memory_cxsr - Configure CxSR state 4168c2ecf20Sopenharmony_ci * @dev_priv: i915 device 4178c2ecf20Sopenharmony_ci * @enable: Allow vs. disallow CxSR 4188c2ecf20Sopenharmony_ci * 4198c2ecf20Sopenharmony_ci * Allow or disallow the system to enter a special CxSR 4208c2ecf20Sopenharmony_ci * (C-state self refresh) state. What typically happens in CxSR mode 4218c2ecf20Sopenharmony_ci * is that several display FIFOs may get combined into a single larger 4228c2ecf20Sopenharmony_ci * FIFO for a particular plane (so called max FIFO mode) to allow the 4238c2ecf20Sopenharmony_ci * system to defer memory fetches longer, and the memory will enter 4248c2ecf20Sopenharmony_ci * self refresh. 4258c2ecf20Sopenharmony_ci * 4268c2ecf20Sopenharmony_ci * Note that enabling CxSR does not guarantee that the system enter 4278c2ecf20Sopenharmony_ci * this special mode, nor does it guarantee that the system stays 4288c2ecf20Sopenharmony_ci * in that mode once entered. So this just allows/disallows the system 4298c2ecf20Sopenharmony_ci * to autonomously utilize the CxSR mode. Other factors such as core 4308c2ecf20Sopenharmony_ci * C-states will affect when/if the system actually enters/exits the 4318c2ecf20Sopenharmony_ci * CxSR mode. 4328c2ecf20Sopenharmony_ci * 4338c2ecf20Sopenharmony_ci * Note that on VLV/CHV this actually only controls the max FIFO mode, 4348c2ecf20Sopenharmony_ci * and the system is free to enter/exit memory self refresh at any time 4358c2ecf20Sopenharmony_ci * even when the use of CxSR has been disallowed. 4368c2ecf20Sopenharmony_ci * 4378c2ecf20Sopenharmony_ci * While the system is actually in the CxSR/max FIFO mode, some plane 4388c2ecf20Sopenharmony_ci * control registers will not get latched on vblank. Thus in order to 4398c2ecf20Sopenharmony_ci * guarantee the system will respond to changes in the plane registers 4408c2ecf20Sopenharmony_ci * we must always disallow CxSR prior to making changes to those registers. 4418c2ecf20Sopenharmony_ci * Unfortunately the system will re-evaluate the CxSR conditions at 4428c2ecf20Sopenharmony_ci * frame start which happens after vblank start (which is when the plane 4438c2ecf20Sopenharmony_ci * registers would get latched), so we can't proceed with the plane update 4448c2ecf20Sopenharmony_ci * during the same frame where we disallowed CxSR. 4458c2ecf20Sopenharmony_ci * 4468c2ecf20Sopenharmony_ci * Certain platforms also have a deeper HPLL SR mode. Fortunately the 4478c2ecf20Sopenharmony_ci * HPLL SR mode depends on CxSR itself, so we don't have to hand hold 4488c2ecf20Sopenharmony_ci * the hardware w.r.t. HPLL SR when writing to plane registers. 4498c2ecf20Sopenharmony_ci * Disallowing just CxSR is sufficient. 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_cibool intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci bool ret; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci mutex_lock(&dev_priv->wm.wm_mutex); 4568c2ecf20Sopenharmony_ci ret = _intel_set_memory_cxsr(dev_priv, enable); 4578c2ecf20Sopenharmony_ci if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) 4588c2ecf20Sopenharmony_ci dev_priv->wm.vlv.cxsr = enable; 4598c2ecf20Sopenharmony_ci else if (IS_G4X(dev_priv)) 4608c2ecf20Sopenharmony_ci dev_priv->wm.g4x.cxsr = enable; 4618c2ecf20Sopenharmony_ci mutex_unlock(&dev_priv->wm.wm_mutex); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return ret; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci/* 4678c2ecf20Sopenharmony_ci * Latency for FIFO fetches is dependent on several factors: 4688c2ecf20Sopenharmony_ci * - memory configuration (speed, channels) 4698c2ecf20Sopenharmony_ci * - chipset 4708c2ecf20Sopenharmony_ci * - current MCH state 4718c2ecf20Sopenharmony_ci * It can be fairly high in some situations, so here we assume a fairly 4728c2ecf20Sopenharmony_ci * pessimal value. It's a tradeoff between extra memory fetches (if we 4738c2ecf20Sopenharmony_ci * set this value too high, the FIFO will fetch frequently to stay full) 4748c2ecf20Sopenharmony_ci * and power consumption (set it too low to save power and we might see 4758c2ecf20Sopenharmony_ci * FIFO underruns and display "flicker"). 4768c2ecf20Sopenharmony_ci * 4778c2ecf20Sopenharmony_ci * A value of 5us seems to be a good balance; safe for very low end 4788c2ecf20Sopenharmony_ci * platforms but not overly aggressive on lower latency configs. 4798c2ecf20Sopenharmony_ci */ 4808c2ecf20Sopenharmony_cistatic const int pessimal_latency_ns = 5000; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci#define VLV_FIFO_START(dsparb, dsparb2, lo_shift, hi_shift) \ 4838c2ecf20Sopenharmony_ci ((((dsparb) >> (lo_shift)) & 0xff) | ((((dsparb2) >> (hi_shift)) & 0x1) << 8)) 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic void vlv_get_fifo_size(struct intel_crtc_state *crtc_state) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); 4888c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 4898c2ecf20Sopenharmony_ci struct vlv_fifo_state *fifo_state = &crtc_state->wm.vlv.fifo_state; 4908c2ecf20Sopenharmony_ci enum pipe pipe = crtc->pipe; 4918c2ecf20Sopenharmony_ci int sprite0_start, sprite1_start; 4928c2ecf20Sopenharmony_ci u32 dsparb, dsparb2, dsparb3; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci switch (pipe) { 4958c2ecf20Sopenharmony_ci case PIPE_A: 4968c2ecf20Sopenharmony_ci dsparb = I915_READ(DSPARB); 4978c2ecf20Sopenharmony_ci dsparb2 = I915_READ(DSPARB2); 4988c2ecf20Sopenharmony_ci sprite0_start = VLV_FIFO_START(dsparb, dsparb2, 0, 0); 4998c2ecf20Sopenharmony_ci sprite1_start = VLV_FIFO_START(dsparb, dsparb2, 8, 4); 5008c2ecf20Sopenharmony_ci break; 5018c2ecf20Sopenharmony_ci case PIPE_B: 5028c2ecf20Sopenharmony_ci dsparb = I915_READ(DSPARB); 5038c2ecf20Sopenharmony_ci dsparb2 = I915_READ(DSPARB2); 5048c2ecf20Sopenharmony_ci sprite0_start = VLV_FIFO_START(dsparb, dsparb2, 16, 8); 5058c2ecf20Sopenharmony_ci sprite1_start = VLV_FIFO_START(dsparb, dsparb2, 24, 12); 5068c2ecf20Sopenharmony_ci break; 5078c2ecf20Sopenharmony_ci case PIPE_C: 5088c2ecf20Sopenharmony_ci dsparb2 = I915_READ(DSPARB2); 5098c2ecf20Sopenharmony_ci dsparb3 = I915_READ(DSPARB3); 5108c2ecf20Sopenharmony_ci sprite0_start = VLV_FIFO_START(dsparb3, dsparb2, 0, 16); 5118c2ecf20Sopenharmony_ci sprite1_start = VLV_FIFO_START(dsparb3, dsparb2, 8, 20); 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci default: 5148c2ecf20Sopenharmony_ci MISSING_CASE(pipe); 5158c2ecf20Sopenharmony_ci return; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci fifo_state->plane[PLANE_PRIMARY] = sprite0_start; 5198c2ecf20Sopenharmony_ci fifo_state->plane[PLANE_SPRITE0] = sprite1_start - sprite0_start; 5208c2ecf20Sopenharmony_ci fifo_state->plane[PLANE_SPRITE1] = 511 - sprite1_start; 5218c2ecf20Sopenharmony_ci fifo_state->plane[PLANE_CURSOR] = 63; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic int i9xx_get_fifo_size(struct drm_i915_private *dev_priv, 5258c2ecf20Sopenharmony_ci enum i9xx_plane_id i9xx_plane) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci u32 dsparb = I915_READ(DSPARB); 5288c2ecf20Sopenharmony_ci int size; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci size = dsparb & 0x7f; 5318c2ecf20Sopenharmony_ci if (i9xx_plane == PLANE_B) 5328c2ecf20Sopenharmony_ci size = ((dsparb >> DSPARB_CSTART_SHIFT) & 0x7f) - size; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, "FIFO size - (0x%08x) %c: %d\n", 5358c2ecf20Sopenharmony_ci dsparb, plane_name(i9xx_plane), size); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return size; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic int i830_get_fifo_size(struct drm_i915_private *dev_priv, 5418c2ecf20Sopenharmony_ci enum i9xx_plane_id i9xx_plane) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci u32 dsparb = I915_READ(DSPARB); 5448c2ecf20Sopenharmony_ci int size; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci size = dsparb & 0x1ff; 5478c2ecf20Sopenharmony_ci if (i9xx_plane == PLANE_B) 5488c2ecf20Sopenharmony_ci size = ((dsparb >> DSPARB_BEND_SHIFT) & 0x1ff) - size; 5498c2ecf20Sopenharmony_ci size >>= 1; /* Convert to cachelines */ 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, "FIFO size - (0x%08x) %c: %d\n", 5528c2ecf20Sopenharmony_ci dsparb, plane_name(i9xx_plane), size); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return size; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic int i845_get_fifo_size(struct drm_i915_private *dev_priv, 5588c2ecf20Sopenharmony_ci enum i9xx_plane_id i9xx_plane) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci u32 dsparb = I915_READ(DSPARB); 5618c2ecf20Sopenharmony_ci int size; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci size = dsparb & 0x7f; 5648c2ecf20Sopenharmony_ci size >>= 2; /* Convert to cachelines */ 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, "FIFO size - (0x%08x) %c: %d\n", 5678c2ecf20Sopenharmony_ci dsparb, plane_name(i9xx_plane), size); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci return size; 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci/* Pineview has different values for various configs */ 5738c2ecf20Sopenharmony_cistatic const struct intel_watermark_params pnv_display_wm = { 5748c2ecf20Sopenharmony_ci .fifo_size = PINEVIEW_DISPLAY_FIFO, 5758c2ecf20Sopenharmony_ci .max_wm = PINEVIEW_MAX_WM, 5768c2ecf20Sopenharmony_ci .default_wm = PINEVIEW_DFT_WM, 5778c2ecf20Sopenharmony_ci .guard_size = PINEVIEW_GUARD_WM, 5788c2ecf20Sopenharmony_ci .cacheline_size = PINEVIEW_FIFO_LINE_SIZE, 5798c2ecf20Sopenharmony_ci}; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic const struct intel_watermark_params pnv_display_hplloff_wm = { 5828c2ecf20Sopenharmony_ci .fifo_size = PINEVIEW_DISPLAY_FIFO, 5838c2ecf20Sopenharmony_ci .max_wm = PINEVIEW_MAX_WM, 5848c2ecf20Sopenharmony_ci .default_wm = PINEVIEW_DFT_HPLLOFF_WM, 5858c2ecf20Sopenharmony_ci .guard_size = PINEVIEW_GUARD_WM, 5868c2ecf20Sopenharmony_ci .cacheline_size = PINEVIEW_FIFO_LINE_SIZE, 5878c2ecf20Sopenharmony_ci}; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic const struct intel_watermark_params pnv_cursor_wm = { 5908c2ecf20Sopenharmony_ci .fifo_size = PINEVIEW_CURSOR_FIFO, 5918c2ecf20Sopenharmony_ci .max_wm = PINEVIEW_CURSOR_MAX_WM, 5928c2ecf20Sopenharmony_ci .default_wm = PINEVIEW_CURSOR_DFT_WM, 5938c2ecf20Sopenharmony_ci .guard_size = PINEVIEW_CURSOR_GUARD_WM, 5948c2ecf20Sopenharmony_ci .cacheline_size = PINEVIEW_FIFO_LINE_SIZE, 5958c2ecf20Sopenharmony_ci}; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic const struct intel_watermark_params pnv_cursor_hplloff_wm = { 5988c2ecf20Sopenharmony_ci .fifo_size = PINEVIEW_CURSOR_FIFO, 5998c2ecf20Sopenharmony_ci .max_wm = PINEVIEW_CURSOR_MAX_WM, 6008c2ecf20Sopenharmony_ci .default_wm = PINEVIEW_CURSOR_DFT_WM, 6018c2ecf20Sopenharmony_ci .guard_size = PINEVIEW_CURSOR_GUARD_WM, 6028c2ecf20Sopenharmony_ci .cacheline_size = PINEVIEW_FIFO_LINE_SIZE, 6038c2ecf20Sopenharmony_ci}; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistatic const struct intel_watermark_params i965_cursor_wm_info = { 6068c2ecf20Sopenharmony_ci .fifo_size = I965_CURSOR_FIFO, 6078c2ecf20Sopenharmony_ci .max_wm = I965_CURSOR_MAX_WM, 6088c2ecf20Sopenharmony_ci .default_wm = I965_CURSOR_DFT_WM, 6098c2ecf20Sopenharmony_ci .guard_size = 2, 6108c2ecf20Sopenharmony_ci .cacheline_size = I915_FIFO_LINE_SIZE, 6118c2ecf20Sopenharmony_ci}; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic const struct intel_watermark_params i945_wm_info = { 6148c2ecf20Sopenharmony_ci .fifo_size = I945_FIFO_SIZE, 6158c2ecf20Sopenharmony_ci .max_wm = I915_MAX_WM, 6168c2ecf20Sopenharmony_ci .default_wm = 1, 6178c2ecf20Sopenharmony_ci .guard_size = 2, 6188c2ecf20Sopenharmony_ci .cacheline_size = I915_FIFO_LINE_SIZE, 6198c2ecf20Sopenharmony_ci}; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic const struct intel_watermark_params i915_wm_info = { 6228c2ecf20Sopenharmony_ci .fifo_size = I915_FIFO_SIZE, 6238c2ecf20Sopenharmony_ci .max_wm = I915_MAX_WM, 6248c2ecf20Sopenharmony_ci .default_wm = 1, 6258c2ecf20Sopenharmony_ci .guard_size = 2, 6268c2ecf20Sopenharmony_ci .cacheline_size = I915_FIFO_LINE_SIZE, 6278c2ecf20Sopenharmony_ci}; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic const struct intel_watermark_params i830_a_wm_info = { 6308c2ecf20Sopenharmony_ci .fifo_size = I855GM_FIFO_SIZE, 6318c2ecf20Sopenharmony_ci .max_wm = I915_MAX_WM, 6328c2ecf20Sopenharmony_ci .default_wm = 1, 6338c2ecf20Sopenharmony_ci .guard_size = 2, 6348c2ecf20Sopenharmony_ci .cacheline_size = I830_FIFO_LINE_SIZE, 6358c2ecf20Sopenharmony_ci}; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic const struct intel_watermark_params i830_bc_wm_info = { 6388c2ecf20Sopenharmony_ci .fifo_size = I855GM_FIFO_SIZE, 6398c2ecf20Sopenharmony_ci .max_wm = I915_MAX_WM/2, 6408c2ecf20Sopenharmony_ci .default_wm = 1, 6418c2ecf20Sopenharmony_ci .guard_size = 2, 6428c2ecf20Sopenharmony_ci .cacheline_size = I830_FIFO_LINE_SIZE, 6438c2ecf20Sopenharmony_ci}; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic const struct intel_watermark_params i845_wm_info = { 6468c2ecf20Sopenharmony_ci .fifo_size = I830_FIFO_SIZE, 6478c2ecf20Sopenharmony_ci .max_wm = I915_MAX_WM, 6488c2ecf20Sopenharmony_ci .default_wm = 1, 6498c2ecf20Sopenharmony_ci .guard_size = 2, 6508c2ecf20Sopenharmony_ci .cacheline_size = I830_FIFO_LINE_SIZE, 6518c2ecf20Sopenharmony_ci}; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci/** 6548c2ecf20Sopenharmony_ci * intel_wm_method1 - Method 1 / "small buffer" watermark formula 6558c2ecf20Sopenharmony_ci * @pixel_rate: Pipe pixel rate in kHz 6568c2ecf20Sopenharmony_ci * @cpp: Plane bytes per pixel 6578c2ecf20Sopenharmony_ci * @latency: Memory wakeup latency in 0.1us units 6588c2ecf20Sopenharmony_ci * 6598c2ecf20Sopenharmony_ci * Compute the watermark using the method 1 or "small buffer" 6608c2ecf20Sopenharmony_ci * formula. The caller may additonally add extra cachelines 6618c2ecf20Sopenharmony_ci * to account for TLB misses and clock crossings. 6628c2ecf20Sopenharmony_ci * 6638c2ecf20Sopenharmony_ci * This method is concerned with the short term drain rate 6648c2ecf20Sopenharmony_ci * of the FIFO, ie. it does not account for blanking periods 6658c2ecf20Sopenharmony_ci * which would effectively reduce the average drain rate across 6668c2ecf20Sopenharmony_ci * a longer period. The name "small" refers to the fact the 6678c2ecf20Sopenharmony_ci * FIFO is relatively small compared to the amount of data 6688c2ecf20Sopenharmony_ci * fetched. 6698c2ecf20Sopenharmony_ci * 6708c2ecf20Sopenharmony_ci * The FIFO level vs. time graph might look something like: 6718c2ecf20Sopenharmony_ci * 6728c2ecf20Sopenharmony_ci * |\ |\ 6738c2ecf20Sopenharmony_ci * | \ | \ 6748c2ecf20Sopenharmony_ci * __---__---__ (- plane active, _ blanking) 6758c2ecf20Sopenharmony_ci * -> time 6768c2ecf20Sopenharmony_ci * 6778c2ecf20Sopenharmony_ci * or perhaps like this: 6788c2ecf20Sopenharmony_ci * 6798c2ecf20Sopenharmony_ci * |\|\ |\|\ 6808c2ecf20Sopenharmony_ci * __----__----__ (- plane active, _ blanking) 6818c2ecf20Sopenharmony_ci * -> time 6828c2ecf20Sopenharmony_ci * 6838c2ecf20Sopenharmony_ci * Returns: 6848c2ecf20Sopenharmony_ci * The watermark in bytes 6858c2ecf20Sopenharmony_ci */ 6868c2ecf20Sopenharmony_cistatic unsigned int intel_wm_method1(unsigned int pixel_rate, 6878c2ecf20Sopenharmony_ci unsigned int cpp, 6888c2ecf20Sopenharmony_ci unsigned int latency) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci u64 ret; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci ret = mul_u32_u32(pixel_rate, cpp * latency); 6938c2ecf20Sopenharmony_ci ret = DIV_ROUND_UP_ULL(ret, 10000); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci return ret; 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci/** 6998c2ecf20Sopenharmony_ci * intel_wm_method2 - Method 2 / "large buffer" watermark formula 7008c2ecf20Sopenharmony_ci * @pixel_rate: Pipe pixel rate in kHz 7018c2ecf20Sopenharmony_ci * @htotal: Pipe horizontal total 7028c2ecf20Sopenharmony_ci * @width: Plane width in pixels 7038c2ecf20Sopenharmony_ci * @cpp: Plane bytes per pixel 7048c2ecf20Sopenharmony_ci * @latency: Memory wakeup latency in 0.1us units 7058c2ecf20Sopenharmony_ci * 7068c2ecf20Sopenharmony_ci * Compute the watermark using the method 2 or "large buffer" 7078c2ecf20Sopenharmony_ci * formula. The caller may additonally add extra cachelines 7088c2ecf20Sopenharmony_ci * to account for TLB misses and clock crossings. 7098c2ecf20Sopenharmony_ci * 7108c2ecf20Sopenharmony_ci * This method is concerned with the long term drain rate 7118c2ecf20Sopenharmony_ci * of the FIFO, ie. it does account for blanking periods 7128c2ecf20Sopenharmony_ci * which effectively reduce the average drain rate across 7138c2ecf20Sopenharmony_ci * a longer period. The name "large" refers to the fact the 7148c2ecf20Sopenharmony_ci * FIFO is relatively large compared to the amount of data 7158c2ecf20Sopenharmony_ci * fetched. 7168c2ecf20Sopenharmony_ci * 7178c2ecf20Sopenharmony_ci * The FIFO level vs. time graph might look something like: 7188c2ecf20Sopenharmony_ci * 7198c2ecf20Sopenharmony_ci * |\___ |\___ 7208c2ecf20Sopenharmony_ci * | \___ | \___ 7218c2ecf20Sopenharmony_ci * | \ | \ 7228c2ecf20Sopenharmony_ci * __ --__--__--__--__--__--__ (- plane active, _ blanking) 7238c2ecf20Sopenharmony_ci * -> time 7248c2ecf20Sopenharmony_ci * 7258c2ecf20Sopenharmony_ci * Returns: 7268c2ecf20Sopenharmony_ci * The watermark in bytes 7278c2ecf20Sopenharmony_ci */ 7288c2ecf20Sopenharmony_cistatic unsigned int intel_wm_method2(unsigned int pixel_rate, 7298c2ecf20Sopenharmony_ci unsigned int htotal, 7308c2ecf20Sopenharmony_ci unsigned int width, 7318c2ecf20Sopenharmony_ci unsigned int cpp, 7328c2ecf20Sopenharmony_ci unsigned int latency) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci unsigned int ret; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci /* 7378c2ecf20Sopenharmony_ci * FIXME remove once all users are computing 7388c2ecf20Sopenharmony_ci * watermarks in the correct place. 7398c2ecf20Sopenharmony_ci */ 7408c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(htotal == 0)) 7418c2ecf20Sopenharmony_ci htotal = 1; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci ret = (latency * pixel_rate) / (htotal * 10000); 7448c2ecf20Sopenharmony_ci ret = (ret + 1) * width * cpp; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci return ret; 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci/** 7508c2ecf20Sopenharmony_ci * intel_calculate_wm - calculate watermark level 7518c2ecf20Sopenharmony_ci * @pixel_rate: pixel clock 7528c2ecf20Sopenharmony_ci * @wm: chip FIFO params 7538c2ecf20Sopenharmony_ci * @fifo_size: size of the FIFO buffer 7548c2ecf20Sopenharmony_ci * @cpp: bytes per pixel 7558c2ecf20Sopenharmony_ci * @latency_ns: memory latency for the platform 7568c2ecf20Sopenharmony_ci * 7578c2ecf20Sopenharmony_ci * Calculate the watermark level (the level at which the display plane will 7588c2ecf20Sopenharmony_ci * start fetching from memory again). Each chip has a different display 7598c2ecf20Sopenharmony_ci * FIFO size and allocation, so the caller needs to figure that out and pass 7608c2ecf20Sopenharmony_ci * in the correct intel_watermark_params structure. 7618c2ecf20Sopenharmony_ci * 7628c2ecf20Sopenharmony_ci * As the pixel clock runs, the FIFO will be drained at a rate that depends 7638c2ecf20Sopenharmony_ci * on the pixel size. When it reaches the watermark level, it'll start 7648c2ecf20Sopenharmony_ci * fetching FIFO line sized based chunks from memory until the FIFO fills 7658c2ecf20Sopenharmony_ci * past the watermark point. If the FIFO drains completely, a FIFO underrun 7668c2ecf20Sopenharmony_ci * will occur, and a display engine hang could result. 7678c2ecf20Sopenharmony_ci */ 7688c2ecf20Sopenharmony_cistatic unsigned int intel_calculate_wm(int pixel_rate, 7698c2ecf20Sopenharmony_ci const struct intel_watermark_params *wm, 7708c2ecf20Sopenharmony_ci int fifo_size, int cpp, 7718c2ecf20Sopenharmony_ci unsigned int latency_ns) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci int entries, wm_size; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* 7768c2ecf20Sopenharmony_ci * Note: we need to make sure we don't overflow for various clock & 7778c2ecf20Sopenharmony_ci * latency values. 7788c2ecf20Sopenharmony_ci * clocks go from a few thousand to several hundred thousand. 7798c2ecf20Sopenharmony_ci * latency is usually a few thousand 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_ci entries = intel_wm_method1(pixel_rate, cpp, 7828c2ecf20Sopenharmony_ci latency_ns / 100); 7838c2ecf20Sopenharmony_ci entries = DIV_ROUND_UP(entries, wm->cacheline_size) + 7848c2ecf20Sopenharmony_ci wm->guard_size; 7858c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("FIFO entries required for mode: %d\n", entries); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci wm_size = fifo_size - entries; 7888c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("FIFO watermark level: %d\n", wm_size); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci /* Don't promote wm_size to unsigned... */ 7918c2ecf20Sopenharmony_ci if (wm_size > wm->max_wm) 7928c2ecf20Sopenharmony_ci wm_size = wm->max_wm; 7938c2ecf20Sopenharmony_ci if (wm_size <= 0) 7948c2ecf20Sopenharmony_ci wm_size = wm->default_wm; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* 7978c2ecf20Sopenharmony_ci * Bspec seems to indicate that the value shouldn't be lower than 7988c2ecf20Sopenharmony_ci * 'burst size + 1'. Certainly 830 is quite unhappy with low values. 7998c2ecf20Sopenharmony_ci * Lets go for 8 which is the burst size since certain platforms 8008c2ecf20Sopenharmony_ci * already use a hardcoded 8 (which is what the spec says should be 8018c2ecf20Sopenharmony_ci * done). 8028c2ecf20Sopenharmony_ci */ 8038c2ecf20Sopenharmony_ci if (wm_size <= 8) 8048c2ecf20Sopenharmony_ci wm_size = 8; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci return wm_size; 8078c2ecf20Sopenharmony_ci} 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_cistatic bool is_disabling(int old, int new, int threshold) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci return old >= threshold && new < threshold; 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic bool is_enabling(int old, int new, int threshold) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci return old < threshold && new >= threshold; 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cistatic int intel_wm_num_levels(struct drm_i915_private *dev_priv) 8208c2ecf20Sopenharmony_ci{ 8218c2ecf20Sopenharmony_ci return dev_priv->wm.max_level + 1; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic bool intel_wm_plane_visible(const struct intel_crtc_state *crtc_state, 8258c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci /* FIXME check the 'enable' instead */ 8308c2ecf20Sopenharmony_ci if (!crtc_state->hw.active) 8318c2ecf20Sopenharmony_ci return false; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* 8348c2ecf20Sopenharmony_ci * Treat cursor with fb as always visible since cursor updates 8358c2ecf20Sopenharmony_ci * can happen faster than the vrefresh rate, and the current 8368c2ecf20Sopenharmony_ci * watermark code doesn't handle that correctly. Cursor updates 8378c2ecf20Sopenharmony_ci * which set/clear the fb or change the cursor size are going 8388c2ecf20Sopenharmony_ci * to get throttled by intel_legacy_cursor_update() to work 8398c2ecf20Sopenharmony_ci * around this problem with the watermark code. 8408c2ecf20Sopenharmony_ci */ 8418c2ecf20Sopenharmony_ci if (plane->id == PLANE_CURSOR) 8428c2ecf20Sopenharmony_ci return plane_state->hw.fb != NULL; 8438c2ecf20Sopenharmony_ci else 8448c2ecf20Sopenharmony_ci return plane_state->uapi.visible; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic bool intel_crtc_active(struct intel_crtc *crtc) 8488c2ecf20Sopenharmony_ci{ 8498c2ecf20Sopenharmony_ci /* Be paranoid as we can arrive here with only partial 8508c2ecf20Sopenharmony_ci * state retrieved from the hardware during setup. 8518c2ecf20Sopenharmony_ci * 8528c2ecf20Sopenharmony_ci * We can ditch the adjusted_mode.crtc_clock check as soon 8538c2ecf20Sopenharmony_ci * as Haswell has gained clock readout/fastboot support. 8548c2ecf20Sopenharmony_ci * 8558c2ecf20Sopenharmony_ci * We can ditch the crtc->primary->state->fb check as soon as we can 8568c2ecf20Sopenharmony_ci * properly reconstruct framebuffers. 8578c2ecf20Sopenharmony_ci * 8588c2ecf20Sopenharmony_ci * FIXME: The intel_crtc->active here should be switched to 8598c2ecf20Sopenharmony_ci * crtc->state->active once we have proper CRTC states wired up 8608c2ecf20Sopenharmony_ci * for atomic. 8618c2ecf20Sopenharmony_ci */ 8628c2ecf20Sopenharmony_ci return crtc->active && crtc->base.primary->state->fb && 8638c2ecf20Sopenharmony_ci crtc->config->hw.adjusted_mode.crtc_clock; 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic struct intel_crtc *single_enabled_crtc(struct drm_i915_private *dev_priv) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci struct intel_crtc *crtc, *enabled = NULL; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci for_each_intel_crtc(&dev_priv->drm, crtc) { 8718c2ecf20Sopenharmony_ci if (intel_crtc_active(crtc)) { 8728c2ecf20Sopenharmony_ci if (enabled) 8738c2ecf20Sopenharmony_ci return NULL; 8748c2ecf20Sopenharmony_ci enabled = crtc; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci return enabled; 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cistatic void pnv_update_wm(struct intel_crtc *unused_crtc) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(unused_crtc->base.dev); 8848c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 8858c2ecf20Sopenharmony_ci const struct cxsr_latency *latency; 8868c2ecf20Sopenharmony_ci u32 reg; 8878c2ecf20Sopenharmony_ci unsigned int wm; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci latency = intel_get_cxsr_latency(!IS_MOBILE(dev_priv), 8908c2ecf20Sopenharmony_ci dev_priv->is_ddr3, 8918c2ecf20Sopenharmony_ci dev_priv->fsb_freq, 8928c2ecf20Sopenharmony_ci dev_priv->mem_freq); 8938c2ecf20Sopenharmony_ci if (!latency) { 8948c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 8958c2ecf20Sopenharmony_ci "Unknown FSB/MEM found, disable CxSR\n"); 8968c2ecf20Sopenharmony_ci intel_set_memory_cxsr(dev_priv, false); 8978c2ecf20Sopenharmony_ci return; 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci crtc = single_enabled_crtc(dev_priv); 9018c2ecf20Sopenharmony_ci if (crtc) { 9028c2ecf20Sopenharmony_ci const struct drm_display_mode *adjusted_mode = 9038c2ecf20Sopenharmony_ci &crtc->config->hw.adjusted_mode; 9048c2ecf20Sopenharmony_ci const struct drm_framebuffer *fb = 9058c2ecf20Sopenharmony_ci crtc->base.primary->state->fb; 9068c2ecf20Sopenharmony_ci int cpp = fb->format->cpp[0]; 9078c2ecf20Sopenharmony_ci int clock = adjusted_mode->crtc_clock; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci /* Display SR */ 9108c2ecf20Sopenharmony_ci wm = intel_calculate_wm(clock, &pnv_display_wm, 9118c2ecf20Sopenharmony_ci pnv_display_wm.fifo_size, 9128c2ecf20Sopenharmony_ci cpp, latency->display_sr); 9138c2ecf20Sopenharmony_ci reg = I915_READ(DSPFW1); 9148c2ecf20Sopenharmony_ci reg &= ~DSPFW_SR_MASK; 9158c2ecf20Sopenharmony_ci reg |= FW_WM(wm, SR); 9168c2ecf20Sopenharmony_ci I915_WRITE(DSPFW1, reg); 9178c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, "DSPFW1 register is %x\n", reg); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci /* cursor SR */ 9208c2ecf20Sopenharmony_ci wm = intel_calculate_wm(clock, &pnv_cursor_wm, 9218c2ecf20Sopenharmony_ci pnv_display_wm.fifo_size, 9228c2ecf20Sopenharmony_ci 4, latency->cursor_sr); 9238c2ecf20Sopenharmony_ci reg = I915_READ(DSPFW3); 9248c2ecf20Sopenharmony_ci reg &= ~DSPFW_CURSOR_SR_MASK; 9258c2ecf20Sopenharmony_ci reg |= FW_WM(wm, CURSOR_SR); 9268c2ecf20Sopenharmony_ci I915_WRITE(DSPFW3, reg); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci /* Display HPLL off SR */ 9298c2ecf20Sopenharmony_ci wm = intel_calculate_wm(clock, &pnv_display_hplloff_wm, 9308c2ecf20Sopenharmony_ci pnv_display_hplloff_wm.fifo_size, 9318c2ecf20Sopenharmony_ci cpp, latency->display_hpll_disable); 9328c2ecf20Sopenharmony_ci reg = I915_READ(DSPFW3); 9338c2ecf20Sopenharmony_ci reg &= ~DSPFW_HPLL_SR_MASK; 9348c2ecf20Sopenharmony_ci reg |= FW_WM(wm, HPLL_SR); 9358c2ecf20Sopenharmony_ci I915_WRITE(DSPFW3, reg); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci /* cursor HPLL off SR */ 9388c2ecf20Sopenharmony_ci wm = intel_calculate_wm(clock, &pnv_cursor_hplloff_wm, 9398c2ecf20Sopenharmony_ci pnv_display_hplloff_wm.fifo_size, 9408c2ecf20Sopenharmony_ci 4, latency->cursor_hpll_disable); 9418c2ecf20Sopenharmony_ci reg = I915_READ(DSPFW3); 9428c2ecf20Sopenharmony_ci reg &= ~DSPFW_HPLL_CURSOR_MASK; 9438c2ecf20Sopenharmony_ci reg |= FW_WM(wm, HPLL_CURSOR); 9448c2ecf20Sopenharmony_ci I915_WRITE(DSPFW3, reg); 9458c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, "DSPFW3 register is %x\n", reg); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci intel_set_memory_cxsr(dev_priv, true); 9488c2ecf20Sopenharmony_ci } else { 9498c2ecf20Sopenharmony_ci intel_set_memory_cxsr(dev_priv, false); 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci} 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci/* 9548c2ecf20Sopenharmony_ci * Documentation says: 9558c2ecf20Sopenharmony_ci * "If the line size is small, the TLB fetches can get in the way of the 9568c2ecf20Sopenharmony_ci * data fetches, causing some lag in the pixel data return which is not 9578c2ecf20Sopenharmony_ci * accounted for in the above formulas. The following adjustment only 9588c2ecf20Sopenharmony_ci * needs to be applied if eight whole lines fit in the buffer at once. 9598c2ecf20Sopenharmony_ci * The WM is adjusted upwards by the difference between the FIFO size 9608c2ecf20Sopenharmony_ci * and the size of 8 whole lines. This adjustment is always performed 9618c2ecf20Sopenharmony_ci * in the actual pixel depth regardless of whether FBC is enabled or not." 9628c2ecf20Sopenharmony_ci */ 9638c2ecf20Sopenharmony_cistatic unsigned int g4x_tlb_miss_wa(int fifo_size, int width, int cpp) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci int tlb_miss = fifo_size * 64 - width * cpp * 8; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci return max(0, tlb_miss); 9688c2ecf20Sopenharmony_ci} 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_cistatic void g4x_write_wm_values(struct drm_i915_private *dev_priv, 9718c2ecf20Sopenharmony_ci const struct g4x_wm_values *wm) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci enum pipe pipe; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) 9768c2ecf20Sopenharmony_ci trace_g4x_wm(intel_get_crtc_for_pipe(dev_priv, pipe), wm); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci I915_WRITE(DSPFW1, 9798c2ecf20Sopenharmony_ci FW_WM(wm->sr.plane, SR) | 9808c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_B].plane[PLANE_CURSOR], CURSORB) | 9818c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_B].plane[PLANE_PRIMARY], PLANEB) | 9828c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_A].plane[PLANE_PRIMARY], PLANEA)); 9838c2ecf20Sopenharmony_ci I915_WRITE(DSPFW2, 9848c2ecf20Sopenharmony_ci (wm->fbc_en ? DSPFW_FBC_SR_EN : 0) | 9858c2ecf20Sopenharmony_ci FW_WM(wm->sr.fbc, FBC_SR) | 9868c2ecf20Sopenharmony_ci FW_WM(wm->hpll.fbc, FBC_HPLL_SR) | 9878c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_B].plane[PLANE_SPRITE0], SPRITEB) | 9888c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_A].plane[PLANE_CURSOR], CURSORA) | 9898c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_A].plane[PLANE_SPRITE0], SPRITEA)); 9908c2ecf20Sopenharmony_ci I915_WRITE(DSPFW3, 9918c2ecf20Sopenharmony_ci (wm->hpll_en ? DSPFW_HPLL_SR_EN : 0) | 9928c2ecf20Sopenharmony_ci FW_WM(wm->sr.cursor, CURSOR_SR) | 9938c2ecf20Sopenharmony_ci FW_WM(wm->hpll.cursor, HPLL_CURSOR) | 9948c2ecf20Sopenharmony_ci FW_WM(wm->hpll.plane, HPLL_SR)); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci POSTING_READ(DSPFW1); 9978c2ecf20Sopenharmony_ci} 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci#define FW_WM_VLV(value, plane) \ 10008c2ecf20Sopenharmony_ci (((value) << DSPFW_ ## plane ## _SHIFT) & DSPFW_ ## plane ## _MASK_VLV) 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic void vlv_write_wm_values(struct drm_i915_private *dev_priv, 10038c2ecf20Sopenharmony_ci const struct vlv_wm_values *wm) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci enum pipe pipe; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 10088c2ecf20Sopenharmony_ci trace_vlv_wm(intel_get_crtc_for_pipe(dev_priv, pipe), wm); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci I915_WRITE(VLV_DDL(pipe), 10118c2ecf20Sopenharmony_ci (wm->ddl[pipe].plane[PLANE_CURSOR] << DDL_CURSOR_SHIFT) | 10128c2ecf20Sopenharmony_ci (wm->ddl[pipe].plane[PLANE_SPRITE1] << DDL_SPRITE_SHIFT(1)) | 10138c2ecf20Sopenharmony_ci (wm->ddl[pipe].plane[PLANE_SPRITE0] << DDL_SPRITE_SHIFT(0)) | 10148c2ecf20Sopenharmony_ci (wm->ddl[pipe].plane[PLANE_PRIMARY] << DDL_PLANE_SHIFT)); 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* 10188c2ecf20Sopenharmony_ci * Zero the (unused) WM1 watermarks, and also clear all the 10198c2ecf20Sopenharmony_ci * high order bits so that there are no out of bounds values 10208c2ecf20Sopenharmony_ci * present in the registers during the reprogramming. 10218c2ecf20Sopenharmony_ci */ 10228c2ecf20Sopenharmony_ci I915_WRITE(DSPHOWM, 0); 10238c2ecf20Sopenharmony_ci I915_WRITE(DSPHOWM1, 0); 10248c2ecf20Sopenharmony_ci I915_WRITE(DSPFW4, 0); 10258c2ecf20Sopenharmony_ci I915_WRITE(DSPFW5, 0); 10268c2ecf20Sopenharmony_ci I915_WRITE(DSPFW6, 0); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci I915_WRITE(DSPFW1, 10298c2ecf20Sopenharmony_ci FW_WM(wm->sr.plane, SR) | 10308c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_B].plane[PLANE_CURSOR], CURSORB) | 10318c2ecf20Sopenharmony_ci FW_WM_VLV(wm->pipe[PIPE_B].plane[PLANE_PRIMARY], PLANEB) | 10328c2ecf20Sopenharmony_ci FW_WM_VLV(wm->pipe[PIPE_A].plane[PLANE_PRIMARY], PLANEA)); 10338c2ecf20Sopenharmony_ci I915_WRITE(DSPFW2, 10348c2ecf20Sopenharmony_ci FW_WM_VLV(wm->pipe[PIPE_A].plane[PLANE_SPRITE1], SPRITEB) | 10358c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_A].plane[PLANE_CURSOR], CURSORA) | 10368c2ecf20Sopenharmony_ci FW_WM_VLV(wm->pipe[PIPE_A].plane[PLANE_SPRITE0], SPRITEA)); 10378c2ecf20Sopenharmony_ci I915_WRITE(DSPFW3, 10388c2ecf20Sopenharmony_ci FW_WM(wm->sr.cursor, CURSOR_SR)); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci if (IS_CHERRYVIEW(dev_priv)) { 10418c2ecf20Sopenharmony_ci I915_WRITE(DSPFW7_CHV, 10428c2ecf20Sopenharmony_ci FW_WM_VLV(wm->pipe[PIPE_B].plane[PLANE_SPRITE1], SPRITED) | 10438c2ecf20Sopenharmony_ci FW_WM_VLV(wm->pipe[PIPE_B].plane[PLANE_SPRITE0], SPRITEC)); 10448c2ecf20Sopenharmony_ci I915_WRITE(DSPFW8_CHV, 10458c2ecf20Sopenharmony_ci FW_WM_VLV(wm->pipe[PIPE_C].plane[PLANE_SPRITE1], SPRITEF) | 10468c2ecf20Sopenharmony_ci FW_WM_VLV(wm->pipe[PIPE_C].plane[PLANE_SPRITE0], SPRITEE)); 10478c2ecf20Sopenharmony_ci I915_WRITE(DSPFW9_CHV, 10488c2ecf20Sopenharmony_ci FW_WM_VLV(wm->pipe[PIPE_C].plane[PLANE_PRIMARY], PLANEC) | 10498c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_C].plane[PLANE_CURSOR], CURSORC)); 10508c2ecf20Sopenharmony_ci I915_WRITE(DSPHOWM, 10518c2ecf20Sopenharmony_ci FW_WM(wm->sr.plane >> 9, SR_HI) | 10528c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_C].plane[PLANE_SPRITE1] >> 8, SPRITEF_HI) | 10538c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_C].plane[PLANE_SPRITE0] >> 8, SPRITEE_HI) | 10548c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_C].plane[PLANE_PRIMARY] >> 8, PLANEC_HI) | 10558c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_B].plane[PLANE_SPRITE1] >> 8, SPRITED_HI) | 10568c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_B].plane[PLANE_SPRITE0] >> 8, SPRITEC_HI) | 10578c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_B].plane[PLANE_PRIMARY] >> 8, PLANEB_HI) | 10588c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_A].plane[PLANE_SPRITE1] >> 8, SPRITEB_HI) | 10598c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_A].plane[PLANE_SPRITE0] >> 8, SPRITEA_HI) | 10608c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_A].plane[PLANE_PRIMARY] >> 8, PLANEA_HI)); 10618c2ecf20Sopenharmony_ci } else { 10628c2ecf20Sopenharmony_ci I915_WRITE(DSPFW7, 10638c2ecf20Sopenharmony_ci FW_WM_VLV(wm->pipe[PIPE_B].plane[PLANE_SPRITE1], SPRITED) | 10648c2ecf20Sopenharmony_ci FW_WM_VLV(wm->pipe[PIPE_B].plane[PLANE_SPRITE0], SPRITEC)); 10658c2ecf20Sopenharmony_ci I915_WRITE(DSPHOWM, 10668c2ecf20Sopenharmony_ci FW_WM(wm->sr.plane >> 9, SR_HI) | 10678c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_B].plane[PLANE_SPRITE1] >> 8, SPRITED_HI) | 10688c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_B].plane[PLANE_SPRITE0] >> 8, SPRITEC_HI) | 10698c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_B].plane[PLANE_PRIMARY] >> 8, PLANEB_HI) | 10708c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_A].plane[PLANE_SPRITE1] >> 8, SPRITEB_HI) | 10718c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_A].plane[PLANE_SPRITE0] >> 8, SPRITEA_HI) | 10728c2ecf20Sopenharmony_ci FW_WM(wm->pipe[PIPE_A].plane[PLANE_PRIMARY] >> 8, PLANEA_HI)); 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci POSTING_READ(DSPFW1); 10768c2ecf20Sopenharmony_ci} 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci#undef FW_WM_VLV 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cistatic void g4x_setup_wm_latency(struct drm_i915_private *dev_priv) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci /* all latencies in usec */ 10838c2ecf20Sopenharmony_ci dev_priv->wm.pri_latency[G4X_WM_LEVEL_NORMAL] = 5; 10848c2ecf20Sopenharmony_ci dev_priv->wm.pri_latency[G4X_WM_LEVEL_SR] = 12; 10858c2ecf20Sopenharmony_ci dev_priv->wm.pri_latency[G4X_WM_LEVEL_HPLL] = 35; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci dev_priv->wm.max_level = G4X_WM_LEVEL_HPLL; 10888c2ecf20Sopenharmony_ci} 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_cistatic int g4x_plane_fifo_size(enum plane_id plane_id, int level) 10918c2ecf20Sopenharmony_ci{ 10928c2ecf20Sopenharmony_ci /* 10938c2ecf20Sopenharmony_ci * DSPCNTR[13] supposedly controls whether the 10948c2ecf20Sopenharmony_ci * primary plane can use the FIFO space otherwise 10958c2ecf20Sopenharmony_ci * reserved for the sprite plane. It's not 100% clear 10968c2ecf20Sopenharmony_ci * what the actual FIFO size is, but it looks like we 10978c2ecf20Sopenharmony_ci * can happily set both primary and sprite watermarks 10988c2ecf20Sopenharmony_ci * up to 127 cachelines. So that would seem to mean 10998c2ecf20Sopenharmony_ci * that either DSPCNTR[13] doesn't do anything, or that 11008c2ecf20Sopenharmony_ci * the total FIFO is >= 256 cachelines in size. Either 11018c2ecf20Sopenharmony_ci * way, we don't seem to have to worry about this 11028c2ecf20Sopenharmony_ci * repartitioning as the maximum watermark value the 11038c2ecf20Sopenharmony_ci * register can hold for each plane is lower than the 11048c2ecf20Sopenharmony_ci * minimum FIFO size. 11058c2ecf20Sopenharmony_ci */ 11068c2ecf20Sopenharmony_ci switch (plane_id) { 11078c2ecf20Sopenharmony_ci case PLANE_CURSOR: 11088c2ecf20Sopenharmony_ci return 63; 11098c2ecf20Sopenharmony_ci case PLANE_PRIMARY: 11108c2ecf20Sopenharmony_ci return level == G4X_WM_LEVEL_NORMAL ? 127 : 511; 11118c2ecf20Sopenharmony_ci case PLANE_SPRITE0: 11128c2ecf20Sopenharmony_ci return level == G4X_WM_LEVEL_NORMAL ? 127 : 0; 11138c2ecf20Sopenharmony_ci default: 11148c2ecf20Sopenharmony_ci MISSING_CASE(plane_id); 11158c2ecf20Sopenharmony_ci return 0; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci} 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_cistatic int g4x_fbc_fifo_size(int level) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci switch (level) { 11228c2ecf20Sopenharmony_ci case G4X_WM_LEVEL_SR: 11238c2ecf20Sopenharmony_ci return 7; 11248c2ecf20Sopenharmony_ci case G4X_WM_LEVEL_HPLL: 11258c2ecf20Sopenharmony_ci return 15; 11268c2ecf20Sopenharmony_ci default: 11278c2ecf20Sopenharmony_ci MISSING_CASE(level); 11288c2ecf20Sopenharmony_ci return 0; 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_cistatic u16 g4x_compute_wm(const struct intel_crtc_state *crtc_state, 11338c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state, 11348c2ecf20Sopenharmony_ci int level) 11358c2ecf20Sopenharmony_ci{ 11368c2ecf20Sopenharmony_ci struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); 11378c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(plane->base.dev); 11388c2ecf20Sopenharmony_ci const struct drm_display_mode *adjusted_mode = 11398c2ecf20Sopenharmony_ci &crtc_state->hw.adjusted_mode; 11408c2ecf20Sopenharmony_ci unsigned int latency = dev_priv->wm.pri_latency[level] * 10; 11418c2ecf20Sopenharmony_ci unsigned int clock, htotal, cpp, width, wm; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci if (latency == 0) 11448c2ecf20Sopenharmony_ci return USHRT_MAX; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci if (!intel_wm_plane_visible(crtc_state, plane_state)) 11478c2ecf20Sopenharmony_ci return 0; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci cpp = plane_state->hw.fb->format->cpp[0]; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci /* 11528c2ecf20Sopenharmony_ci * Not 100% sure which way ELK should go here as the 11538c2ecf20Sopenharmony_ci * spec only says CL/CTG should assume 32bpp and BW 11548c2ecf20Sopenharmony_ci * doesn't need to. But as these things followed the 11558c2ecf20Sopenharmony_ci * mobile vs. desktop lines on gen3 as well, let's 11568c2ecf20Sopenharmony_ci * assume ELK doesn't need this. 11578c2ecf20Sopenharmony_ci * 11588c2ecf20Sopenharmony_ci * The spec also fails to list such a restriction for 11598c2ecf20Sopenharmony_ci * the HPLL watermark, which seems a little strange. 11608c2ecf20Sopenharmony_ci * Let's use 32bpp for the HPLL watermark as well. 11618c2ecf20Sopenharmony_ci */ 11628c2ecf20Sopenharmony_ci if (IS_GM45(dev_priv) && plane->id == PLANE_PRIMARY && 11638c2ecf20Sopenharmony_ci level != G4X_WM_LEVEL_NORMAL) 11648c2ecf20Sopenharmony_ci cpp = max(cpp, 4u); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci clock = adjusted_mode->crtc_clock; 11678c2ecf20Sopenharmony_ci htotal = adjusted_mode->crtc_htotal; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci width = drm_rect_width(&plane_state->uapi.dst); 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (plane->id == PLANE_CURSOR) { 11728c2ecf20Sopenharmony_ci wm = intel_wm_method2(clock, htotal, width, cpp, latency); 11738c2ecf20Sopenharmony_ci } else if (plane->id == PLANE_PRIMARY && 11748c2ecf20Sopenharmony_ci level == G4X_WM_LEVEL_NORMAL) { 11758c2ecf20Sopenharmony_ci wm = intel_wm_method1(clock, cpp, latency); 11768c2ecf20Sopenharmony_ci } else { 11778c2ecf20Sopenharmony_ci unsigned int small, large; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci small = intel_wm_method1(clock, cpp, latency); 11808c2ecf20Sopenharmony_ci large = intel_wm_method2(clock, htotal, width, cpp, latency); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci wm = min(small, large); 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci wm += g4x_tlb_miss_wa(g4x_plane_fifo_size(plane->id, level), 11868c2ecf20Sopenharmony_ci width, cpp); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci wm = DIV_ROUND_UP(wm, 64) + 2; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci return min_t(unsigned int, wm, USHRT_MAX); 11918c2ecf20Sopenharmony_ci} 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic bool g4x_raw_plane_wm_set(struct intel_crtc_state *crtc_state, 11948c2ecf20Sopenharmony_ci int level, enum plane_id plane_id, u16 value) 11958c2ecf20Sopenharmony_ci{ 11968c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 11978c2ecf20Sopenharmony_ci bool dirty = false; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci for (; level < intel_wm_num_levels(dev_priv); level++) { 12008c2ecf20Sopenharmony_ci struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level]; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci dirty |= raw->plane[plane_id] != value; 12038c2ecf20Sopenharmony_ci raw->plane[plane_id] = value; 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci return dirty; 12078c2ecf20Sopenharmony_ci} 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_cistatic bool g4x_raw_fbc_wm_set(struct intel_crtc_state *crtc_state, 12108c2ecf20Sopenharmony_ci int level, u16 value) 12118c2ecf20Sopenharmony_ci{ 12128c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 12138c2ecf20Sopenharmony_ci bool dirty = false; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci /* NORMAL level doesn't have an FBC watermark */ 12168c2ecf20Sopenharmony_ci level = max(level, G4X_WM_LEVEL_SR); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci for (; level < intel_wm_num_levels(dev_priv); level++) { 12198c2ecf20Sopenharmony_ci struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level]; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci dirty |= raw->fbc != value; 12228c2ecf20Sopenharmony_ci raw->fbc = value; 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci return dirty; 12268c2ecf20Sopenharmony_ci} 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_cistatic u32 ilk_compute_fbc_wm(const struct intel_crtc_state *crtc_state, 12298c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state, 12308c2ecf20Sopenharmony_ci u32 pri_val); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_cistatic bool g4x_raw_plane_wm_compute(struct intel_crtc_state *crtc_state, 12338c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state) 12348c2ecf20Sopenharmony_ci{ 12358c2ecf20Sopenharmony_ci struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); 12368c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 12378c2ecf20Sopenharmony_ci int num_levels = intel_wm_num_levels(to_i915(plane->base.dev)); 12388c2ecf20Sopenharmony_ci enum plane_id plane_id = plane->id; 12398c2ecf20Sopenharmony_ci bool dirty = false; 12408c2ecf20Sopenharmony_ci int level; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci if (!intel_wm_plane_visible(crtc_state, plane_state)) { 12438c2ecf20Sopenharmony_ci dirty |= g4x_raw_plane_wm_set(crtc_state, 0, plane_id, 0); 12448c2ecf20Sopenharmony_ci if (plane_id == PLANE_PRIMARY) 12458c2ecf20Sopenharmony_ci dirty |= g4x_raw_fbc_wm_set(crtc_state, 0, 0); 12468c2ecf20Sopenharmony_ci goto out; 12478c2ecf20Sopenharmony_ci } 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci for (level = 0; level < num_levels; level++) { 12508c2ecf20Sopenharmony_ci struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level]; 12518c2ecf20Sopenharmony_ci int wm, max_wm; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci wm = g4x_compute_wm(crtc_state, plane_state, level); 12548c2ecf20Sopenharmony_ci max_wm = g4x_plane_fifo_size(plane_id, level); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci if (wm > max_wm) 12578c2ecf20Sopenharmony_ci break; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci dirty |= raw->plane[plane_id] != wm; 12608c2ecf20Sopenharmony_ci raw->plane[plane_id] = wm; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci if (plane_id != PLANE_PRIMARY || 12638c2ecf20Sopenharmony_ci level == G4X_WM_LEVEL_NORMAL) 12648c2ecf20Sopenharmony_ci continue; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci wm = ilk_compute_fbc_wm(crtc_state, plane_state, 12678c2ecf20Sopenharmony_ci raw->plane[plane_id]); 12688c2ecf20Sopenharmony_ci max_wm = g4x_fbc_fifo_size(level); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci /* 12718c2ecf20Sopenharmony_ci * FBC wm is not mandatory as we 12728c2ecf20Sopenharmony_ci * can always just disable its use. 12738c2ecf20Sopenharmony_ci */ 12748c2ecf20Sopenharmony_ci if (wm > max_wm) 12758c2ecf20Sopenharmony_ci wm = USHRT_MAX; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci dirty |= raw->fbc != wm; 12788c2ecf20Sopenharmony_ci raw->fbc = wm; 12798c2ecf20Sopenharmony_ci } 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci /* mark watermarks as invalid */ 12828c2ecf20Sopenharmony_ci dirty |= g4x_raw_plane_wm_set(crtc_state, level, plane_id, USHRT_MAX); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci if (plane_id == PLANE_PRIMARY) 12858c2ecf20Sopenharmony_ci dirty |= g4x_raw_fbc_wm_set(crtc_state, level, USHRT_MAX); 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci out: 12888c2ecf20Sopenharmony_ci if (dirty) { 12898c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 12908c2ecf20Sopenharmony_ci "%s watermarks: normal=%d, SR=%d, HPLL=%d\n", 12918c2ecf20Sopenharmony_ci plane->base.name, 12928c2ecf20Sopenharmony_ci crtc_state->wm.g4x.raw[G4X_WM_LEVEL_NORMAL].plane[plane_id], 12938c2ecf20Sopenharmony_ci crtc_state->wm.g4x.raw[G4X_WM_LEVEL_SR].plane[plane_id], 12948c2ecf20Sopenharmony_ci crtc_state->wm.g4x.raw[G4X_WM_LEVEL_HPLL].plane[plane_id]); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci if (plane_id == PLANE_PRIMARY) 12978c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 12988c2ecf20Sopenharmony_ci "FBC watermarks: SR=%d, HPLL=%d\n", 12998c2ecf20Sopenharmony_ci crtc_state->wm.g4x.raw[G4X_WM_LEVEL_SR].fbc, 13008c2ecf20Sopenharmony_ci crtc_state->wm.g4x.raw[G4X_WM_LEVEL_HPLL].fbc); 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci return dirty; 13048c2ecf20Sopenharmony_ci} 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_cistatic bool g4x_raw_plane_wm_is_valid(const struct intel_crtc_state *crtc_state, 13078c2ecf20Sopenharmony_ci enum plane_id plane_id, int level) 13088c2ecf20Sopenharmony_ci{ 13098c2ecf20Sopenharmony_ci const struct g4x_pipe_wm *raw = &crtc_state->wm.g4x.raw[level]; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci return raw->plane[plane_id] <= g4x_plane_fifo_size(plane_id, level); 13128c2ecf20Sopenharmony_ci} 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_cistatic bool g4x_raw_crtc_wm_is_valid(const struct intel_crtc_state *crtc_state, 13158c2ecf20Sopenharmony_ci int level) 13168c2ecf20Sopenharmony_ci{ 13178c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci if (level > dev_priv->wm.max_level) 13208c2ecf20Sopenharmony_ci return false; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci return g4x_raw_plane_wm_is_valid(crtc_state, PLANE_PRIMARY, level) && 13238c2ecf20Sopenharmony_ci g4x_raw_plane_wm_is_valid(crtc_state, PLANE_SPRITE0, level) && 13248c2ecf20Sopenharmony_ci g4x_raw_plane_wm_is_valid(crtc_state, PLANE_CURSOR, level); 13258c2ecf20Sopenharmony_ci} 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci/* mark all levels starting from 'level' as invalid */ 13288c2ecf20Sopenharmony_cistatic void g4x_invalidate_wms(struct intel_crtc *crtc, 13298c2ecf20Sopenharmony_ci struct g4x_wm_state *wm_state, int level) 13308c2ecf20Sopenharmony_ci{ 13318c2ecf20Sopenharmony_ci if (level <= G4X_WM_LEVEL_NORMAL) { 13328c2ecf20Sopenharmony_ci enum plane_id plane_id; 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) 13358c2ecf20Sopenharmony_ci wm_state->wm.plane[plane_id] = USHRT_MAX; 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci if (level <= G4X_WM_LEVEL_SR) { 13398c2ecf20Sopenharmony_ci wm_state->cxsr = false; 13408c2ecf20Sopenharmony_ci wm_state->sr.cursor = USHRT_MAX; 13418c2ecf20Sopenharmony_ci wm_state->sr.plane = USHRT_MAX; 13428c2ecf20Sopenharmony_ci wm_state->sr.fbc = USHRT_MAX; 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (level <= G4X_WM_LEVEL_HPLL) { 13468c2ecf20Sopenharmony_ci wm_state->hpll_en = false; 13478c2ecf20Sopenharmony_ci wm_state->hpll.cursor = USHRT_MAX; 13488c2ecf20Sopenharmony_ci wm_state->hpll.plane = USHRT_MAX; 13498c2ecf20Sopenharmony_ci wm_state->hpll.fbc = USHRT_MAX; 13508c2ecf20Sopenharmony_ci } 13518c2ecf20Sopenharmony_ci} 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_cistatic bool g4x_compute_fbc_en(const struct g4x_wm_state *wm_state, 13548c2ecf20Sopenharmony_ci int level) 13558c2ecf20Sopenharmony_ci{ 13568c2ecf20Sopenharmony_ci if (level < G4X_WM_LEVEL_SR) 13578c2ecf20Sopenharmony_ci return false; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci if (level >= G4X_WM_LEVEL_SR && 13608c2ecf20Sopenharmony_ci wm_state->sr.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_SR)) 13618c2ecf20Sopenharmony_ci return false; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci if (level >= G4X_WM_LEVEL_HPLL && 13648c2ecf20Sopenharmony_ci wm_state->hpll.fbc > g4x_fbc_fifo_size(G4X_WM_LEVEL_HPLL)) 13658c2ecf20Sopenharmony_ci return false; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci return true; 13688c2ecf20Sopenharmony_ci} 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_cistatic int g4x_compute_pipe_wm(struct intel_crtc_state *crtc_state) 13718c2ecf20Sopenharmony_ci{ 13728c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); 13738c2ecf20Sopenharmony_ci struct intel_atomic_state *state = 13748c2ecf20Sopenharmony_ci to_intel_atomic_state(crtc_state->uapi.state); 13758c2ecf20Sopenharmony_ci struct g4x_wm_state *wm_state = &crtc_state->wm.g4x.optimal; 13768c2ecf20Sopenharmony_ci int num_active_planes = hweight8(crtc_state->active_planes & 13778c2ecf20Sopenharmony_ci ~BIT(PLANE_CURSOR)); 13788c2ecf20Sopenharmony_ci const struct g4x_pipe_wm *raw; 13798c2ecf20Sopenharmony_ci const struct intel_plane_state *old_plane_state; 13808c2ecf20Sopenharmony_ci const struct intel_plane_state *new_plane_state; 13818c2ecf20Sopenharmony_ci struct intel_plane *plane; 13828c2ecf20Sopenharmony_ci enum plane_id plane_id; 13838c2ecf20Sopenharmony_ci int i, level; 13848c2ecf20Sopenharmony_ci unsigned int dirty = 0; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci for_each_oldnew_intel_plane_in_state(state, plane, 13878c2ecf20Sopenharmony_ci old_plane_state, 13888c2ecf20Sopenharmony_ci new_plane_state, i) { 13898c2ecf20Sopenharmony_ci if (new_plane_state->hw.crtc != &crtc->base && 13908c2ecf20Sopenharmony_ci old_plane_state->hw.crtc != &crtc->base) 13918c2ecf20Sopenharmony_ci continue; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci if (g4x_raw_plane_wm_compute(crtc_state, new_plane_state)) 13948c2ecf20Sopenharmony_ci dirty |= BIT(plane->id); 13958c2ecf20Sopenharmony_ci } 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci if (!dirty) 13988c2ecf20Sopenharmony_ci return 0; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci level = G4X_WM_LEVEL_NORMAL; 14018c2ecf20Sopenharmony_ci if (!g4x_raw_crtc_wm_is_valid(crtc_state, level)) 14028c2ecf20Sopenharmony_ci goto out; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci raw = &crtc_state->wm.g4x.raw[level]; 14058c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) 14068c2ecf20Sopenharmony_ci wm_state->wm.plane[plane_id] = raw->plane[plane_id]; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci level = G4X_WM_LEVEL_SR; 14098c2ecf20Sopenharmony_ci if (!g4x_raw_crtc_wm_is_valid(crtc_state, level)) 14108c2ecf20Sopenharmony_ci goto out; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci raw = &crtc_state->wm.g4x.raw[level]; 14138c2ecf20Sopenharmony_ci wm_state->sr.plane = raw->plane[PLANE_PRIMARY]; 14148c2ecf20Sopenharmony_ci wm_state->sr.cursor = raw->plane[PLANE_CURSOR]; 14158c2ecf20Sopenharmony_ci wm_state->sr.fbc = raw->fbc; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci wm_state->cxsr = num_active_planes == BIT(PLANE_PRIMARY); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci level = G4X_WM_LEVEL_HPLL; 14208c2ecf20Sopenharmony_ci if (!g4x_raw_crtc_wm_is_valid(crtc_state, level)) 14218c2ecf20Sopenharmony_ci goto out; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci raw = &crtc_state->wm.g4x.raw[level]; 14248c2ecf20Sopenharmony_ci wm_state->hpll.plane = raw->plane[PLANE_PRIMARY]; 14258c2ecf20Sopenharmony_ci wm_state->hpll.cursor = raw->plane[PLANE_CURSOR]; 14268c2ecf20Sopenharmony_ci wm_state->hpll.fbc = raw->fbc; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci wm_state->hpll_en = wm_state->cxsr; 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci level++; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci out: 14338c2ecf20Sopenharmony_ci if (level == G4X_WM_LEVEL_NORMAL) 14348c2ecf20Sopenharmony_ci return -EINVAL; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci /* invalidate the higher levels */ 14378c2ecf20Sopenharmony_ci g4x_invalidate_wms(crtc, wm_state, level); 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci /* 14408c2ecf20Sopenharmony_ci * Determine if the FBC watermark(s) can be used. IF 14418c2ecf20Sopenharmony_ci * this isn't the case we prefer to disable the FBC 14428c2ecf20Sopenharmony_ci * watermark(s) rather than disable the SR/HPLL 14438c2ecf20Sopenharmony_ci * level(s) entirely. 'level-1' is the highest valid 14448c2ecf20Sopenharmony_ci * level here. 14458c2ecf20Sopenharmony_ci */ 14468c2ecf20Sopenharmony_ci wm_state->fbc_en = g4x_compute_fbc_en(wm_state, level - 1); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci return 0; 14498c2ecf20Sopenharmony_ci} 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_cistatic int g4x_compute_intermediate_wm(struct intel_crtc_state *new_crtc_state) 14528c2ecf20Sopenharmony_ci{ 14538c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->uapi.crtc); 14548c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 14558c2ecf20Sopenharmony_ci struct g4x_wm_state *intermediate = &new_crtc_state->wm.g4x.intermediate; 14568c2ecf20Sopenharmony_ci const struct g4x_wm_state *optimal = &new_crtc_state->wm.g4x.optimal; 14578c2ecf20Sopenharmony_ci struct intel_atomic_state *intel_state = 14588c2ecf20Sopenharmony_ci to_intel_atomic_state(new_crtc_state->uapi.state); 14598c2ecf20Sopenharmony_ci const struct intel_crtc_state *old_crtc_state = 14608c2ecf20Sopenharmony_ci intel_atomic_get_old_crtc_state(intel_state, crtc); 14618c2ecf20Sopenharmony_ci const struct g4x_wm_state *active = &old_crtc_state->wm.g4x.optimal; 14628c2ecf20Sopenharmony_ci enum plane_id plane_id; 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci if (!new_crtc_state->hw.active || drm_atomic_crtc_needs_modeset(&new_crtc_state->uapi)) { 14658c2ecf20Sopenharmony_ci *intermediate = *optimal; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci intermediate->cxsr = false; 14688c2ecf20Sopenharmony_ci intermediate->hpll_en = false; 14698c2ecf20Sopenharmony_ci goto out; 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci intermediate->cxsr = optimal->cxsr && active->cxsr && 14738c2ecf20Sopenharmony_ci !new_crtc_state->disable_cxsr; 14748c2ecf20Sopenharmony_ci intermediate->hpll_en = optimal->hpll_en && active->hpll_en && 14758c2ecf20Sopenharmony_ci !new_crtc_state->disable_cxsr; 14768c2ecf20Sopenharmony_ci intermediate->fbc_en = optimal->fbc_en && active->fbc_en; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) { 14798c2ecf20Sopenharmony_ci intermediate->wm.plane[plane_id] = 14808c2ecf20Sopenharmony_ci max(optimal->wm.plane[plane_id], 14818c2ecf20Sopenharmony_ci active->wm.plane[plane_id]); 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, intermediate->wm.plane[plane_id] > 14848c2ecf20Sopenharmony_ci g4x_plane_fifo_size(plane_id, G4X_WM_LEVEL_NORMAL)); 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci intermediate->sr.plane = max(optimal->sr.plane, 14888c2ecf20Sopenharmony_ci active->sr.plane); 14898c2ecf20Sopenharmony_ci intermediate->sr.cursor = max(optimal->sr.cursor, 14908c2ecf20Sopenharmony_ci active->sr.cursor); 14918c2ecf20Sopenharmony_ci intermediate->sr.fbc = max(optimal->sr.fbc, 14928c2ecf20Sopenharmony_ci active->sr.fbc); 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci intermediate->hpll.plane = max(optimal->hpll.plane, 14958c2ecf20Sopenharmony_ci active->hpll.plane); 14968c2ecf20Sopenharmony_ci intermediate->hpll.cursor = max(optimal->hpll.cursor, 14978c2ecf20Sopenharmony_ci active->hpll.cursor); 14988c2ecf20Sopenharmony_ci intermediate->hpll.fbc = max(optimal->hpll.fbc, 14998c2ecf20Sopenharmony_ci active->hpll.fbc); 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, 15028c2ecf20Sopenharmony_ci (intermediate->sr.plane > 15038c2ecf20Sopenharmony_ci g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_SR) || 15048c2ecf20Sopenharmony_ci intermediate->sr.cursor > 15058c2ecf20Sopenharmony_ci g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_SR)) && 15068c2ecf20Sopenharmony_ci intermediate->cxsr); 15078c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, 15088c2ecf20Sopenharmony_ci (intermediate->sr.plane > 15098c2ecf20Sopenharmony_ci g4x_plane_fifo_size(PLANE_PRIMARY, G4X_WM_LEVEL_HPLL) || 15108c2ecf20Sopenharmony_ci intermediate->sr.cursor > 15118c2ecf20Sopenharmony_ci g4x_plane_fifo_size(PLANE_CURSOR, G4X_WM_LEVEL_HPLL)) && 15128c2ecf20Sopenharmony_ci intermediate->hpll_en); 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, 15158c2ecf20Sopenharmony_ci intermediate->sr.fbc > g4x_fbc_fifo_size(1) && 15168c2ecf20Sopenharmony_ci intermediate->fbc_en && intermediate->cxsr); 15178c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, 15188c2ecf20Sopenharmony_ci intermediate->hpll.fbc > g4x_fbc_fifo_size(2) && 15198c2ecf20Sopenharmony_ci intermediate->fbc_en && intermediate->hpll_en); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ciout: 15228c2ecf20Sopenharmony_ci /* 15238c2ecf20Sopenharmony_ci * If our intermediate WM are identical to the final WM, then we can 15248c2ecf20Sopenharmony_ci * omit the post-vblank programming; only update if it's different. 15258c2ecf20Sopenharmony_ci */ 15268c2ecf20Sopenharmony_ci if (memcmp(intermediate, optimal, sizeof(*intermediate)) != 0) 15278c2ecf20Sopenharmony_ci new_crtc_state->wm.need_postvbl_update = true; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci return 0; 15308c2ecf20Sopenharmony_ci} 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cistatic void g4x_merge_wm(struct drm_i915_private *dev_priv, 15338c2ecf20Sopenharmony_ci struct g4x_wm_values *wm) 15348c2ecf20Sopenharmony_ci{ 15358c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 15368c2ecf20Sopenharmony_ci int num_active_pipes = 0; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci wm->cxsr = true; 15398c2ecf20Sopenharmony_ci wm->hpll_en = true; 15408c2ecf20Sopenharmony_ci wm->fbc_en = true; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci for_each_intel_crtc(&dev_priv->drm, crtc) { 15438c2ecf20Sopenharmony_ci const struct g4x_wm_state *wm_state = &crtc->wm.active.g4x; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci if (!crtc->active) 15468c2ecf20Sopenharmony_ci continue; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci if (!wm_state->cxsr) 15498c2ecf20Sopenharmony_ci wm->cxsr = false; 15508c2ecf20Sopenharmony_ci if (!wm_state->hpll_en) 15518c2ecf20Sopenharmony_ci wm->hpll_en = false; 15528c2ecf20Sopenharmony_ci if (!wm_state->fbc_en) 15538c2ecf20Sopenharmony_ci wm->fbc_en = false; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci num_active_pipes++; 15568c2ecf20Sopenharmony_ci } 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci if (num_active_pipes != 1) { 15598c2ecf20Sopenharmony_ci wm->cxsr = false; 15608c2ecf20Sopenharmony_ci wm->hpll_en = false; 15618c2ecf20Sopenharmony_ci wm->fbc_en = false; 15628c2ecf20Sopenharmony_ci } 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci for_each_intel_crtc(&dev_priv->drm, crtc) { 15658c2ecf20Sopenharmony_ci const struct g4x_wm_state *wm_state = &crtc->wm.active.g4x; 15668c2ecf20Sopenharmony_ci enum pipe pipe = crtc->pipe; 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci wm->pipe[pipe] = wm_state->wm; 15698c2ecf20Sopenharmony_ci if (crtc->active && wm->cxsr) 15708c2ecf20Sopenharmony_ci wm->sr = wm_state->sr; 15718c2ecf20Sopenharmony_ci if (crtc->active && wm->hpll_en) 15728c2ecf20Sopenharmony_ci wm->hpll = wm_state->hpll; 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci} 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_cistatic void g4x_program_watermarks(struct drm_i915_private *dev_priv) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci struct g4x_wm_values *old_wm = &dev_priv->wm.g4x; 15798c2ecf20Sopenharmony_ci struct g4x_wm_values new_wm = {}; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci g4x_merge_wm(dev_priv, &new_wm); 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci if (memcmp(old_wm, &new_wm, sizeof(new_wm)) == 0) 15848c2ecf20Sopenharmony_ci return; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci if (is_disabling(old_wm->cxsr, new_wm.cxsr, true)) 15878c2ecf20Sopenharmony_ci _intel_set_memory_cxsr(dev_priv, false); 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci g4x_write_wm_values(dev_priv, &new_wm); 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci if (is_enabling(old_wm->cxsr, new_wm.cxsr, true)) 15928c2ecf20Sopenharmony_ci _intel_set_memory_cxsr(dev_priv, true); 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci *old_wm = new_wm; 15958c2ecf20Sopenharmony_ci} 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_cistatic void g4x_initial_watermarks(struct intel_atomic_state *state, 15988c2ecf20Sopenharmony_ci struct intel_crtc *crtc) 15998c2ecf20Sopenharmony_ci{ 16008c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 16018c2ecf20Sopenharmony_ci const struct intel_crtc_state *crtc_state = 16028c2ecf20Sopenharmony_ci intel_atomic_get_new_crtc_state(state, crtc); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci mutex_lock(&dev_priv->wm.wm_mutex); 16058c2ecf20Sopenharmony_ci crtc->wm.active.g4x = crtc_state->wm.g4x.intermediate; 16068c2ecf20Sopenharmony_ci g4x_program_watermarks(dev_priv); 16078c2ecf20Sopenharmony_ci mutex_unlock(&dev_priv->wm.wm_mutex); 16088c2ecf20Sopenharmony_ci} 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_cistatic void g4x_optimize_watermarks(struct intel_atomic_state *state, 16118c2ecf20Sopenharmony_ci struct intel_crtc *crtc) 16128c2ecf20Sopenharmony_ci{ 16138c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 16148c2ecf20Sopenharmony_ci const struct intel_crtc_state *crtc_state = 16158c2ecf20Sopenharmony_ci intel_atomic_get_new_crtc_state(state, crtc); 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci if (!crtc_state->wm.need_postvbl_update) 16188c2ecf20Sopenharmony_ci return; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci mutex_lock(&dev_priv->wm.wm_mutex); 16218c2ecf20Sopenharmony_ci crtc->wm.active.g4x = crtc_state->wm.g4x.optimal; 16228c2ecf20Sopenharmony_ci g4x_program_watermarks(dev_priv); 16238c2ecf20Sopenharmony_ci mutex_unlock(&dev_priv->wm.wm_mutex); 16248c2ecf20Sopenharmony_ci} 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci/* latency must be in 0.1us units. */ 16278c2ecf20Sopenharmony_cistatic unsigned int vlv_wm_method2(unsigned int pixel_rate, 16288c2ecf20Sopenharmony_ci unsigned int htotal, 16298c2ecf20Sopenharmony_ci unsigned int width, 16308c2ecf20Sopenharmony_ci unsigned int cpp, 16318c2ecf20Sopenharmony_ci unsigned int latency) 16328c2ecf20Sopenharmony_ci{ 16338c2ecf20Sopenharmony_ci unsigned int ret; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci ret = intel_wm_method2(pixel_rate, htotal, 16368c2ecf20Sopenharmony_ci width, cpp, latency); 16378c2ecf20Sopenharmony_ci ret = DIV_ROUND_UP(ret, 64); 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci return ret; 16408c2ecf20Sopenharmony_ci} 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_cistatic void vlv_setup_wm_latency(struct drm_i915_private *dev_priv) 16438c2ecf20Sopenharmony_ci{ 16448c2ecf20Sopenharmony_ci /* all latencies in usec */ 16458c2ecf20Sopenharmony_ci dev_priv->wm.pri_latency[VLV_WM_LEVEL_PM2] = 3; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci dev_priv->wm.max_level = VLV_WM_LEVEL_PM2; 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci if (IS_CHERRYVIEW(dev_priv)) { 16508c2ecf20Sopenharmony_ci dev_priv->wm.pri_latency[VLV_WM_LEVEL_PM5] = 12; 16518c2ecf20Sopenharmony_ci dev_priv->wm.pri_latency[VLV_WM_LEVEL_DDR_DVFS] = 33; 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci dev_priv->wm.max_level = VLV_WM_LEVEL_DDR_DVFS; 16548c2ecf20Sopenharmony_ci } 16558c2ecf20Sopenharmony_ci} 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_cistatic u16 vlv_compute_wm_level(const struct intel_crtc_state *crtc_state, 16588c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state, 16598c2ecf20Sopenharmony_ci int level) 16608c2ecf20Sopenharmony_ci{ 16618c2ecf20Sopenharmony_ci struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); 16628c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(plane->base.dev); 16638c2ecf20Sopenharmony_ci const struct drm_display_mode *adjusted_mode = 16648c2ecf20Sopenharmony_ci &crtc_state->hw.adjusted_mode; 16658c2ecf20Sopenharmony_ci unsigned int clock, htotal, cpp, width, wm; 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci if (dev_priv->wm.pri_latency[level] == 0) 16688c2ecf20Sopenharmony_ci return USHRT_MAX; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci if (!intel_wm_plane_visible(crtc_state, plane_state)) 16718c2ecf20Sopenharmony_ci return 0; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci cpp = plane_state->hw.fb->format->cpp[0]; 16748c2ecf20Sopenharmony_ci clock = adjusted_mode->crtc_clock; 16758c2ecf20Sopenharmony_ci htotal = adjusted_mode->crtc_htotal; 16768c2ecf20Sopenharmony_ci width = crtc_state->pipe_src_w; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci if (plane->id == PLANE_CURSOR) { 16798c2ecf20Sopenharmony_ci /* 16808c2ecf20Sopenharmony_ci * FIXME the formula gives values that are 16818c2ecf20Sopenharmony_ci * too big for the cursor FIFO, and hence we 16828c2ecf20Sopenharmony_ci * would never be able to use cursors. For 16838c2ecf20Sopenharmony_ci * now just hardcode the watermark. 16848c2ecf20Sopenharmony_ci */ 16858c2ecf20Sopenharmony_ci wm = 63; 16868c2ecf20Sopenharmony_ci } else { 16878c2ecf20Sopenharmony_ci wm = vlv_wm_method2(clock, htotal, width, cpp, 16888c2ecf20Sopenharmony_ci dev_priv->wm.pri_latency[level] * 10); 16898c2ecf20Sopenharmony_ci } 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci return min_t(unsigned int, wm, USHRT_MAX); 16928c2ecf20Sopenharmony_ci} 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_cistatic bool vlv_need_sprite0_fifo_workaround(unsigned int active_planes) 16958c2ecf20Sopenharmony_ci{ 16968c2ecf20Sopenharmony_ci return (active_planes & (BIT(PLANE_SPRITE0) | 16978c2ecf20Sopenharmony_ci BIT(PLANE_SPRITE1))) == BIT(PLANE_SPRITE1); 16988c2ecf20Sopenharmony_ci} 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_cistatic int vlv_compute_fifo(struct intel_crtc_state *crtc_state) 17018c2ecf20Sopenharmony_ci{ 17028c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); 17038c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 17048c2ecf20Sopenharmony_ci const struct g4x_pipe_wm *raw = 17058c2ecf20Sopenharmony_ci &crtc_state->wm.vlv.raw[VLV_WM_LEVEL_PM2]; 17068c2ecf20Sopenharmony_ci struct vlv_fifo_state *fifo_state = &crtc_state->wm.vlv.fifo_state; 17078c2ecf20Sopenharmony_ci unsigned int active_planes = crtc_state->active_planes & ~BIT(PLANE_CURSOR); 17088c2ecf20Sopenharmony_ci int num_active_planes = hweight8(active_planes); 17098c2ecf20Sopenharmony_ci const int fifo_size = 511; 17108c2ecf20Sopenharmony_ci int fifo_extra, fifo_left = fifo_size; 17118c2ecf20Sopenharmony_ci int sprite0_fifo_extra = 0; 17128c2ecf20Sopenharmony_ci unsigned int total_rate; 17138c2ecf20Sopenharmony_ci enum plane_id plane_id; 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci /* 17168c2ecf20Sopenharmony_ci * When enabling sprite0 after sprite1 has already been enabled 17178c2ecf20Sopenharmony_ci * we tend to get an underrun unless sprite0 already has some 17188c2ecf20Sopenharmony_ci * FIFO space allcoated. Hence we always allocate at least one 17198c2ecf20Sopenharmony_ci * cacheline for sprite0 whenever sprite1 is enabled. 17208c2ecf20Sopenharmony_ci * 17218c2ecf20Sopenharmony_ci * All other plane enable sequences appear immune to this problem. 17228c2ecf20Sopenharmony_ci */ 17238c2ecf20Sopenharmony_ci if (vlv_need_sprite0_fifo_workaround(active_planes)) 17248c2ecf20Sopenharmony_ci sprite0_fifo_extra = 1; 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci total_rate = raw->plane[PLANE_PRIMARY] + 17278c2ecf20Sopenharmony_ci raw->plane[PLANE_SPRITE0] + 17288c2ecf20Sopenharmony_ci raw->plane[PLANE_SPRITE1] + 17298c2ecf20Sopenharmony_ci sprite0_fifo_extra; 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci if (total_rate > fifo_size) 17328c2ecf20Sopenharmony_ci return -EINVAL; 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci if (total_rate == 0) 17358c2ecf20Sopenharmony_ci total_rate = 1; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) { 17388c2ecf20Sopenharmony_ci unsigned int rate; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci if ((active_planes & BIT(plane_id)) == 0) { 17418c2ecf20Sopenharmony_ci fifo_state->plane[plane_id] = 0; 17428c2ecf20Sopenharmony_ci continue; 17438c2ecf20Sopenharmony_ci } 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci rate = raw->plane[plane_id]; 17468c2ecf20Sopenharmony_ci fifo_state->plane[plane_id] = fifo_size * rate / total_rate; 17478c2ecf20Sopenharmony_ci fifo_left -= fifo_state->plane[plane_id]; 17488c2ecf20Sopenharmony_ci } 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci fifo_state->plane[PLANE_SPRITE0] += sprite0_fifo_extra; 17518c2ecf20Sopenharmony_ci fifo_left -= sprite0_fifo_extra; 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci fifo_state->plane[PLANE_CURSOR] = 63; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci fifo_extra = DIV_ROUND_UP(fifo_left, num_active_planes ?: 1); 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci /* spread the remainder evenly */ 17588c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) { 17598c2ecf20Sopenharmony_ci int plane_extra; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci if (fifo_left == 0) 17628c2ecf20Sopenharmony_ci break; 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci if ((active_planes & BIT(plane_id)) == 0) 17658c2ecf20Sopenharmony_ci continue; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci plane_extra = min(fifo_extra, fifo_left); 17688c2ecf20Sopenharmony_ci fifo_state->plane[plane_id] += plane_extra; 17698c2ecf20Sopenharmony_ci fifo_left -= plane_extra; 17708c2ecf20Sopenharmony_ci } 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, active_planes != 0 && fifo_left != 0); 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci /* give it all to the first plane if none are active */ 17758c2ecf20Sopenharmony_ci if (active_planes == 0) { 17768c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, fifo_left != fifo_size); 17778c2ecf20Sopenharmony_ci fifo_state->plane[PLANE_PRIMARY] = fifo_left; 17788c2ecf20Sopenharmony_ci } 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci return 0; 17818c2ecf20Sopenharmony_ci} 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci/* mark all levels starting from 'level' as invalid */ 17848c2ecf20Sopenharmony_cistatic void vlv_invalidate_wms(struct intel_crtc *crtc, 17858c2ecf20Sopenharmony_ci struct vlv_wm_state *wm_state, int level) 17868c2ecf20Sopenharmony_ci{ 17878c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci for (; level < intel_wm_num_levels(dev_priv); level++) { 17908c2ecf20Sopenharmony_ci enum plane_id plane_id; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) 17938c2ecf20Sopenharmony_ci wm_state->wm[level].plane[plane_id] = USHRT_MAX; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci wm_state->sr[level].cursor = USHRT_MAX; 17968c2ecf20Sopenharmony_ci wm_state->sr[level].plane = USHRT_MAX; 17978c2ecf20Sopenharmony_ci } 17988c2ecf20Sopenharmony_ci} 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_cistatic u16 vlv_invert_wm_value(u16 wm, u16 fifo_size) 18018c2ecf20Sopenharmony_ci{ 18028c2ecf20Sopenharmony_ci if (wm > fifo_size) 18038c2ecf20Sopenharmony_ci return USHRT_MAX; 18048c2ecf20Sopenharmony_ci else 18058c2ecf20Sopenharmony_ci return fifo_size - wm; 18068c2ecf20Sopenharmony_ci} 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci/* 18098c2ecf20Sopenharmony_ci * Starting from 'level' set all higher 18108c2ecf20Sopenharmony_ci * levels to 'value' in the "raw" watermarks. 18118c2ecf20Sopenharmony_ci */ 18128c2ecf20Sopenharmony_cistatic bool vlv_raw_plane_wm_set(struct intel_crtc_state *crtc_state, 18138c2ecf20Sopenharmony_ci int level, enum plane_id plane_id, u16 value) 18148c2ecf20Sopenharmony_ci{ 18158c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 18168c2ecf20Sopenharmony_ci int num_levels = intel_wm_num_levels(dev_priv); 18178c2ecf20Sopenharmony_ci bool dirty = false; 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci for (; level < num_levels; level++) { 18208c2ecf20Sopenharmony_ci struct g4x_pipe_wm *raw = &crtc_state->wm.vlv.raw[level]; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci dirty |= raw->plane[plane_id] != value; 18238c2ecf20Sopenharmony_ci raw->plane[plane_id] = value; 18248c2ecf20Sopenharmony_ci } 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci return dirty; 18278c2ecf20Sopenharmony_ci} 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_cistatic bool vlv_raw_plane_wm_compute(struct intel_crtc_state *crtc_state, 18308c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state) 18318c2ecf20Sopenharmony_ci{ 18328c2ecf20Sopenharmony_ci struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); 18338c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 18348c2ecf20Sopenharmony_ci enum plane_id plane_id = plane->id; 18358c2ecf20Sopenharmony_ci int num_levels = intel_wm_num_levels(to_i915(plane->base.dev)); 18368c2ecf20Sopenharmony_ci int level; 18378c2ecf20Sopenharmony_ci bool dirty = false; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci if (!intel_wm_plane_visible(crtc_state, plane_state)) { 18408c2ecf20Sopenharmony_ci dirty |= vlv_raw_plane_wm_set(crtc_state, 0, plane_id, 0); 18418c2ecf20Sopenharmony_ci goto out; 18428c2ecf20Sopenharmony_ci } 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci for (level = 0; level < num_levels; level++) { 18458c2ecf20Sopenharmony_ci struct g4x_pipe_wm *raw = &crtc_state->wm.vlv.raw[level]; 18468c2ecf20Sopenharmony_ci int wm = vlv_compute_wm_level(crtc_state, plane_state, level); 18478c2ecf20Sopenharmony_ci int max_wm = plane_id == PLANE_CURSOR ? 63 : 511; 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci if (wm > max_wm) 18508c2ecf20Sopenharmony_ci break; 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci dirty |= raw->plane[plane_id] != wm; 18538c2ecf20Sopenharmony_ci raw->plane[plane_id] = wm; 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci /* mark all higher levels as invalid */ 18578c2ecf20Sopenharmony_ci dirty |= vlv_raw_plane_wm_set(crtc_state, level, plane_id, USHRT_MAX); 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ciout: 18608c2ecf20Sopenharmony_ci if (dirty) 18618c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 18628c2ecf20Sopenharmony_ci "%s watermarks: PM2=%d, PM5=%d, DDR DVFS=%d\n", 18638c2ecf20Sopenharmony_ci plane->base.name, 18648c2ecf20Sopenharmony_ci crtc_state->wm.vlv.raw[VLV_WM_LEVEL_PM2].plane[plane_id], 18658c2ecf20Sopenharmony_ci crtc_state->wm.vlv.raw[VLV_WM_LEVEL_PM5].plane[plane_id], 18668c2ecf20Sopenharmony_ci crtc_state->wm.vlv.raw[VLV_WM_LEVEL_DDR_DVFS].plane[plane_id]); 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci return dirty; 18698c2ecf20Sopenharmony_ci} 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_cistatic bool vlv_raw_plane_wm_is_valid(const struct intel_crtc_state *crtc_state, 18728c2ecf20Sopenharmony_ci enum plane_id plane_id, int level) 18738c2ecf20Sopenharmony_ci{ 18748c2ecf20Sopenharmony_ci const struct g4x_pipe_wm *raw = 18758c2ecf20Sopenharmony_ci &crtc_state->wm.vlv.raw[level]; 18768c2ecf20Sopenharmony_ci const struct vlv_fifo_state *fifo_state = 18778c2ecf20Sopenharmony_ci &crtc_state->wm.vlv.fifo_state; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci return raw->plane[plane_id] <= fifo_state->plane[plane_id]; 18808c2ecf20Sopenharmony_ci} 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_cistatic bool vlv_raw_crtc_wm_is_valid(const struct intel_crtc_state *crtc_state, int level) 18838c2ecf20Sopenharmony_ci{ 18848c2ecf20Sopenharmony_ci return vlv_raw_plane_wm_is_valid(crtc_state, PLANE_PRIMARY, level) && 18858c2ecf20Sopenharmony_ci vlv_raw_plane_wm_is_valid(crtc_state, PLANE_SPRITE0, level) && 18868c2ecf20Sopenharmony_ci vlv_raw_plane_wm_is_valid(crtc_state, PLANE_SPRITE1, level) && 18878c2ecf20Sopenharmony_ci vlv_raw_plane_wm_is_valid(crtc_state, PLANE_CURSOR, level); 18888c2ecf20Sopenharmony_ci} 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_cistatic int vlv_compute_pipe_wm(struct intel_crtc_state *crtc_state) 18918c2ecf20Sopenharmony_ci{ 18928c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); 18938c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 18948c2ecf20Sopenharmony_ci struct intel_atomic_state *state = 18958c2ecf20Sopenharmony_ci to_intel_atomic_state(crtc_state->uapi.state); 18968c2ecf20Sopenharmony_ci struct vlv_wm_state *wm_state = &crtc_state->wm.vlv.optimal; 18978c2ecf20Sopenharmony_ci const struct vlv_fifo_state *fifo_state = 18988c2ecf20Sopenharmony_ci &crtc_state->wm.vlv.fifo_state; 18998c2ecf20Sopenharmony_ci int num_active_planes = hweight8(crtc_state->active_planes & 19008c2ecf20Sopenharmony_ci ~BIT(PLANE_CURSOR)); 19018c2ecf20Sopenharmony_ci bool needs_modeset = drm_atomic_crtc_needs_modeset(&crtc_state->uapi); 19028c2ecf20Sopenharmony_ci const struct intel_plane_state *old_plane_state; 19038c2ecf20Sopenharmony_ci const struct intel_plane_state *new_plane_state; 19048c2ecf20Sopenharmony_ci struct intel_plane *plane; 19058c2ecf20Sopenharmony_ci enum plane_id plane_id; 19068c2ecf20Sopenharmony_ci int level, ret, i; 19078c2ecf20Sopenharmony_ci unsigned int dirty = 0; 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci for_each_oldnew_intel_plane_in_state(state, plane, 19108c2ecf20Sopenharmony_ci old_plane_state, 19118c2ecf20Sopenharmony_ci new_plane_state, i) { 19128c2ecf20Sopenharmony_ci if (new_plane_state->hw.crtc != &crtc->base && 19138c2ecf20Sopenharmony_ci old_plane_state->hw.crtc != &crtc->base) 19148c2ecf20Sopenharmony_ci continue; 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci if (vlv_raw_plane_wm_compute(crtc_state, new_plane_state)) 19178c2ecf20Sopenharmony_ci dirty |= BIT(plane->id); 19188c2ecf20Sopenharmony_ci } 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci /* 19218c2ecf20Sopenharmony_ci * DSPARB registers may have been reset due to the 19228c2ecf20Sopenharmony_ci * power well being turned off. Make sure we restore 19238c2ecf20Sopenharmony_ci * them to a consistent state even if no primary/sprite 19248c2ecf20Sopenharmony_ci * planes are initially active. 19258c2ecf20Sopenharmony_ci */ 19268c2ecf20Sopenharmony_ci if (needs_modeset) 19278c2ecf20Sopenharmony_ci crtc_state->fifo_changed = true; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci if (!dirty) 19308c2ecf20Sopenharmony_ci return 0; 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci /* cursor changes don't warrant a FIFO recompute */ 19338c2ecf20Sopenharmony_ci if (dirty & ~BIT(PLANE_CURSOR)) { 19348c2ecf20Sopenharmony_ci const struct intel_crtc_state *old_crtc_state = 19358c2ecf20Sopenharmony_ci intel_atomic_get_old_crtc_state(state, crtc); 19368c2ecf20Sopenharmony_ci const struct vlv_fifo_state *old_fifo_state = 19378c2ecf20Sopenharmony_ci &old_crtc_state->wm.vlv.fifo_state; 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci ret = vlv_compute_fifo(crtc_state); 19408c2ecf20Sopenharmony_ci if (ret) 19418c2ecf20Sopenharmony_ci return ret; 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci if (needs_modeset || 19448c2ecf20Sopenharmony_ci memcmp(old_fifo_state, fifo_state, 19458c2ecf20Sopenharmony_ci sizeof(*fifo_state)) != 0) 19468c2ecf20Sopenharmony_ci crtc_state->fifo_changed = true; 19478c2ecf20Sopenharmony_ci } 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci /* initially allow all levels */ 19508c2ecf20Sopenharmony_ci wm_state->num_levels = intel_wm_num_levels(dev_priv); 19518c2ecf20Sopenharmony_ci /* 19528c2ecf20Sopenharmony_ci * Note that enabling cxsr with no primary/sprite planes 19538c2ecf20Sopenharmony_ci * enabled can wedge the pipe. Hence we only allow cxsr 19548c2ecf20Sopenharmony_ci * with exactly one enabled primary/sprite plane. 19558c2ecf20Sopenharmony_ci */ 19568c2ecf20Sopenharmony_ci wm_state->cxsr = crtc->pipe != PIPE_C && num_active_planes == 1; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci for (level = 0; level < wm_state->num_levels; level++) { 19598c2ecf20Sopenharmony_ci const struct g4x_pipe_wm *raw = &crtc_state->wm.vlv.raw[level]; 19608c2ecf20Sopenharmony_ci const int sr_fifo_size = INTEL_NUM_PIPES(dev_priv) * 512 - 1; 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci if (!vlv_raw_crtc_wm_is_valid(crtc_state, level)) 19638c2ecf20Sopenharmony_ci break; 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) { 19668c2ecf20Sopenharmony_ci wm_state->wm[level].plane[plane_id] = 19678c2ecf20Sopenharmony_ci vlv_invert_wm_value(raw->plane[plane_id], 19688c2ecf20Sopenharmony_ci fifo_state->plane[plane_id]); 19698c2ecf20Sopenharmony_ci } 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci wm_state->sr[level].plane = 19728c2ecf20Sopenharmony_ci vlv_invert_wm_value(max3(raw->plane[PLANE_PRIMARY], 19738c2ecf20Sopenharmony_ci raw->plane[PLANE_SPRITE0], 19748c2ecf20Sopenharmony_ci raw->plane[PLANE_SPRITE1]), 19758c2ecf20Sopenharmony_ci sr_fifo_size); 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_ci wm_state->sr[level].cursor = 19788c2ecf20Sopenharmony_ci vlv_invert_wm_value(raw->plane[PLANE_CURSOR], 19798c2ecf20Sopenharmony_ci 63); 19808c2ecf20Sopenharmony_ci } 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci if (level == 0) 19838c2ecf20Sopenharmony_ci return -EINVAL; 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci /* limit to only levels we can actually handle */ 19868c2ecf20Sopenharmony_ci wm_state->num_levels = level; 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci /* invalidate the higher levels */ 19898c2ecf20Sopenharmony_ci vlv_invalidate_wms(crtc, wm_state, level); 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci return 0; 19928c2ecf20Sopenharmony_ci} 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci#define VLV_FIFO(plane, value) \ 19958c2ecf20Sopenharmony_ci (((value) << DSPARB_ ## plane ## _SHIFT_VLV) & DSPARB_ ## plane ## _MASK_VLV) 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_cistatic void vlv_atomic_update_fifo(struct intel_atomic_state *state, 19988c2ecf20Sopenharmony_ci struct intel_crtc *crtc) 19998c2ecf20Sopenharmony_ci{ 20008c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 20018c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 20028c2ecf20Sopenharmony_ci const struct intel_crtc_state *crtc_state = 20038c2ecf20Sopenharmony_ci intel_atomic_get_new_crtc_state(state, crtc); 20048c2ecf20Sopenharmony_ci const struct vlv_fifo_state *fifo_state = 20058c2ecf20Sopenharmony_ci &crtc_state->wm.vlv.fifo_state; 20068c2ecf20Sopenharmony_ci int sprite0_start, sprite1_start, fifo_size; 20078c2ecf20Sopenharmony_ci u32 dsparb, dsparb2, dsparb3; 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci if (!crtc_state->fifo_changed) 20108c2ecf20Sopenharmony_ci return; 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci sprite0_start = fifo_state->plane[PLANE_PRIMARY]; 20138c2ecf20Sopenharmony_ci sprite1_start = fifo_state->plane[PLANE_SPRITE0] + sprite0_start; 20148c2ecf20Sopenharmony_ci fifo_size = fifo_state->plane[PLANE_SPRITE1] + sprite1_start; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, fifo_state->plane[PLANE_CURSOR] != 63); 20178c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, fifo_size != 511); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci trace_vlv_fifo_size(crtc, sprite0_start, sprite1_start, fifo_size); 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci /* 20228c2ecf20Sopenharmony_ci * uncore.lock serves a double purpose here. It allows us to 20238c2ecf20Sopenharmony_ci * use the less expensive I915_{READ,WRITE}_FW() functions, and 20248c2ecf20Sopenharmony_ci * it protects the DSPARB registers from getting clobbered by 20258c2ecf20Sopenharmony_ci * parallel updates from multiple pipes. 20268c2ecf20Sopenharmony_ci * 20278c2ecf20Sopenharmony_ci * intel_pipe_update_start() has already disabled interrupts 20288c2ecf20Sopenharmony_ci * for us, so a plain spin_lock() is sufficient here. 20298c2ecf20Sopenharmony_ci */ 20308c2ecf20Sopenharmony_ci spin_lock(&uncore->lock); 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci switch (crtc->pipe) { 20338c2ecf20Sopenharmony_ci case PIPE_A: 20348c2ecf20Sopenharmony_ci dsparb = intel_uncore_read_fw(uncore, DSPARB); 20358c2ecf20Sopenharmony_ci dsparb2 = intel_uncore_read_fw(uncore, DSPARB2); 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci dsparb &= ~(VLV_FIFO(SPRITEA, 0xff) | 20388c2ecf20Sopenharmony_ci VLV_FIFO(SPRITEB, 0xff)); 20398c2ecf20Sopenharmony_ci dsparb |= (VLV_FIFO(SPRITEA, sprite0_start) | 20408c2ecf20Sopenharmony_ci VLV_FIFO(SPRITEB, sprite1_start)); 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci dsparb2 &= ~(VLV_FIFO(SPRITEA_HI, 0x1) | 20438c2ecf20Sopenharmony_ci VLV_FIFO(SPRITEB_HI, 0x1)); 20448c2ecf20Sopenharmony_ci dsparb2 |= (VLV_FIFO(SPRITEA_HI, sprite0_start >> 8) | 20458c2ecf20Sopenharmony_ci VLV_FIFO(SPRITEB_HI, sprite1_start >> 8)); 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci intel_uncore_write_fw(uncore, DSPARB, dsparb); 20488c2ecf20Sopenharmony_ci intel_uncore_write_fw(uncore, DSPARB2, dsparb2); 20498c2ecf20Sopenharmony_ci break; 20508c2ecf20Sopenharmony_ci case PIPE_B: 20518c2ecf20Sopenharmony_ci dsparb = intel_uncore_read_fw(uncore, DSPARB); 20528c2ecf20Sopenharmony_ci dsparb2 = intel_uncore_read_fw(uncore, DSPARB2); 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci dsparb &= ~(VLV_FIFO(SPRITEC, 0xff) | 20558c2ecf20Sopenharmony_ci VLV_FIFO(SPRITED, 0xff)); 20568c2ecf20Sopenharmony_ci dsparb |= (VLV_FIFO(SPRITEC, sprite0_start) | 20578c2ecf20Sopenharmony_ci VLV_FIFO(SPRITED, sprite1_start)); 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ci dsparb2 &= ~(VLV_FIFO(SPRITEC_HI, 0xff) | 20608c2ecf20Sopenharmony_ci VLV_FIFO(SPRITED_HI, 0xff)); 20618c2ecf20Sopenharmony_ci dsparb2 |= (VLV_FIFO(SPRITEC_HI, sprite0_start >> 8) | 20628c2ecf20Sopenharmony_ci VLV_FIFO(SPRITED_HI, sprite1_start >> 8)); 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci intel_uncore_write_fw(uncore, DSPARB, dsparb); 20658c2ecf20Sopenharmony_ci intel_uncore_write_fw(uncore, DSPARB2, dsparb2); 20668c2ecf20Sopenharmony_ci break; 20678c2ecf20Sopenharmony_ci case PIPE_C: 20688c2ecf20Sopenharmony_ci dsparb3 = intel_uncore_read_fw(uncore, DSPARB3); 20698c2ecf20Sopenharmony_ci dsparb2 = intel_uncore_read_fw(uncore, DSPARB2); 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci dsparb3 &= ~(VLV_FIFO(SPRITEE, 0xff) | 20728c2ecf20Sopenharmony_ci VLV_FIFO(SPRITEF, 0xff)); 20738c2ecf20Sopenharmony_ci dsparb3 |= (VLV_FIFO(SPRITEE, sprite0_start) | 20748c2ecf20Sopenharmony_ci VLV_FIFO(SPRITEF, sprite1_start)); 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci dsparb2 &= ~(VLV_FIFO(SPRITEE_HI, 0xff) | 20778c2ecf20Sopenharmony_ci VLV_FIFO(SPRITEF_HI, 0xff)); 20788c2ecf20Sopenharmony_ci dsparb2 |= (VLV_FIFO(SPRITEE_HI, sprite0_start >> 8) | 20798c2ecf20Sopenharmony_ci VLV_FIFO(SPRITEF_HI, sprite1_start >> 8)); 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci intel_uncore_write_fw(uncore, DSPARB3, dsparb3); 20828c2ecf20Sopenharmony_ci intel_uncore_write_fw(uncore, DSPARB2, dsparb2); 20838c2ecf20Sopenharmony_ci break; 20848c2ecf20Sopenharmony_ci default: 20858c2ecf20Sopenharmony_ci break; 20868c2ecf20Sopenharmony_ci } 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci intel_uncore_posting_read_fw(uncore, DSPARB); 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci spin_unlock(&uncore->lock); 20918c2ecf20Sopenharmony_ci} 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci#undef VLV_FIFO 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_cistatic int vlv_compute_intermediate_wm(struct intel_crtc_state *new_crtc_state) 20968c2ecf20Sopenharmony_ci{ 20978c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->uapi.crtc); 20988c2ecf20Sopenharmony_ci struct vlv_wm_state *intermediate = &new_crtc_state->wm.vlv.intermediate; 20998c2ecf20Sopenharmony_ci const struct vlv_wm_state *optimal = &new_crtc_state->wm.vlv.optimal; 21008c2ecf20Sopenharmony_ci struct intel_atomic_state *intel_state = 21018c2ecf20Sopenharmony_ci to_intel_atomic_state(new_crtc_state->uapi.state); 21028c2ecf20Sopenharmony_ci const struct intel_crtc_state *old_crtc_state = 21038c2ecf20Sopenharmony_ci intel_atomic_get_old_crtc_state(intel_state, crtc); 21048c2ecf20Sopenharmony_ci const struct vlv_wm_state *active = &old_crtc_state->wm.vlv.optimal; 21058c2ecf20Sopenharmony_ci int level; 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci if (!new_crtc_state->hw.active || drm_atomic_crtc_needs_modeset(&new_crtc_state->uapi)) { 21088c2ecf20Sopenharmony_ci *intermediate = *optimal; 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci intermediate->cxsr = false; 21118c2ecf20Sopenharmony_ci goto out; 21128c2ecf20Sopenharmony_ci } 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_ci intermediate->num_levels = min(optimal->num_levels, active->num_levels); 21158c2ecf20Sopenharmony_ci intermediate->cxsr = optimal->cxsr && active->cxsr && 21168c2ecf20Sopenharmony_ci !new_crtc_state->disable_cxsr; 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci for (level = 0; level < intermediate->num_levels; level++) { 21198c2ecf20Sopenharmony_ci enum plane_id plane_id; 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) { 21228c2ecf20Sopenharmony_ci intermediate->wm[level].plane[plane_id] = 21238c2ecf20Sopenharmony_ci min(optimal->wm[level].plane[plane_id], 21248c2ecf20Sopenharmony_ci active->wm[level].plane[plane_id]); 21258c2ecf20Sopenharmony_ci } 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci intermediate->sr[level].plane = min(optimal->sr[level].plane, 21288c2ecf20Sopenharmony_ci active->sr[level].plane); 21298c2ecf20Sopenharmony_ci intermediate->sr[level].cursor = min(optimal->sr[level].cursor, 21308c2ecf20Sopenharmony_ci active->sr[level].cursor); 21318c2ecf20Sopenharmony_ci } 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci vlv_invalidate_wms(crtc, intermediate, level); 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ciout: 21368c2ecf20Sopenharmony_ci /* 21378c2ecf20Sopenharmony_ci * If our intermediate WM are identical to the final WM, then we can 21388c2ecf20Sopenharmony_ci * omit the post-vblank programming; only update if it's different. 21398c2ecf20Sopenharmony_ci */ 21408c2ecf20Sopenharmony_ci if (memcmp(intermediate, optimal, sizeof(*intermediate)) != 0) 21418c2ecf20Sopenharmony_ci new_crtc_state->wm.need_postvbl_update = true; 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci return 0; 21448c2ecf20Sopenharmony_ci} 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_cistatic void vlv_merge_wm(struct drm_i915_private *dev_priv, 21478c2ecf20Sopenharmony_ci struct vlv_wm_values *wm) 21488c2ecf20Sopenharmony_ci{ 21498c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 21508c2ecf20Sopenharmony_ci int num_active_pipes = 0; 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci wm->level = dev_priv->wm.max_level; 21538c2ecf20Sopenharmony_ci wm->cxsr = true; 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci for_each_intel_crtc(&dev_priv->drm, crtc) { 21568c2ecf20Sopenharmony_ci const struct vlv_wm_state *wm_state = &crtc->wm.active.vlv; 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci if (!crtc->active) 21598c2ecf20Sopenharmony_ci continue; 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci if (!wm_state->cxsr) 21628c2ecf20Sopenharmony_ci wm->cxsr = false; 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci num_active_pipes++; 21658c2ecf20Sopenharmony_ci wm->level = min_t(int, wm->level, wm_state->num_levels - 1); 21668c2ecf20Sopenharmony_ci } 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci if (num_active_pipes != 1) 21698c2ecf20Sopenharmony_ci wm->cxsr = false; 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci if (num_active_pipes > 1) 21728c2ecf20Sopenharmony_ci wm->level = VLV_WM_LEVEL_PM2; 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci for_each_intel_crtc(&dev_priv->drm, crtc) { 21758c2ecf20Sopenharmony_ci const struct vlv_wm_state *wm_state = &crtc->wm.active.vlv; 21768c2ecf20Sopenharmony_ci enum pipe pipe = crtc->pipe; 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_ci wm->pipe[pipe] = wm_state->wm[wm->level]; 21798c2ecf20Sopenharmony_ci if (crtc->active && wm->cxsr) 21808c2ecf20Sopenharmony_ci wm->sr = wm_state->sr[wm->level]; 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ci wm->ddl[pipe].plane[PLANE_PRIMARY] = DDL_PRECISION_HIGH | 2; 21838c2ecf20Sopenharmony_ci wm->ddl[pipe].plane[PLANE_SPRITE0] = DDL_PRECISION_HIGH | 2; 21848c2ecf20Sopenharmony_ci wm->ddl[pipe].plane[PLANE_SPRITE1] = DDL_PRECISION_HIGH | 2; 21858c2ecf20Sopenharmony_ci wm->ddl[pipe].plane[PLANE_CURSOR] = DDL_PRECISION_HIGH | 2; 21868c2ecf20Sopenharmony_ci } 21878c2ecf20Sopenharmony_ci} 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_cistatic void vlv_program_watermarks(struct drm_i915_private *dev_priv) 21908c2ecf20Sopenharmony_ci{ 21918c2ecf20Sopenharmony_ci struct vlv_wm_values *old_wm = &dev_priv->wm.vlv; 21928c2ecf20Sopenharmony_ci struct vlv_wm_values new_wm = {}; 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci vlv_merge_wm(dev_priv, &new_wm); 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci if (memcmp(old_wm, &new_wm, sizeof(new_wm)) == 0) 21978c2ecf20Sopenharmony_ci return; 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci if (is_disabling(old_wm->level, new_wm.level, VLV_WM_LEVEL_DDR_DVFS)) 22008c2ecf20Sopenharmony_ci chv_set_memory_dvfs(dev_priv, false); 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci if (is_disabling(old_wm->level, new_wm.level, VLV_WM_LEVEL_PM5)) 22038c2ecf20Sopenharmony_ci chv_set_memory_pm5(dev_priv, false); 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci if (is_disabling(old_wm->cxsr, new_wm.cxsr, true)) 22068c2ecf20Sopenharmony_ci _intel_set_memory_cxsr(dev_priv, false); 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci vlv_write_wm_values(dev_priv, &new_wm); 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_ci if (is_enabling(old_wm->cxsr, new_wm.cxsr, true)) 22118c2ecf20Sopenharmony_ci _intel_set_memory_cxsr(dev_priv, true); 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci if (is_enabling(old_wm->level, new_wm.level, VLV_WM_LEVEL_PM5)) 22148c2ecf20Sopenharmony_ci chv_set_memory_pm5(dev_priv, true); 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci if (is_enabling(old_wm->level, new_wm.level, VLV_WM_LEVEL_DDR_DVFS)) 22178c2ecf20Sopenharmony_ci chv_set_memory_dvfs(dev_priv, true); 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_ci *old_wm = new_wm; 22208c2ecf20Sopenharmony_ci} 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_cistatic void vlv_initial_watermarks(struct intel_atomic_state *state, 22238c2ecf20Sopenharmony_ci struct intel_crtc *crtc) 22248c2ecf20Sopenharmony_ci{ 22258c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 22268c2ecf20Sopenharmony_ci const struct intel_crtc_state *crtc_state = 22278c2ecf20Sopenharmony_ci intel_atomic_get_new_crtc_state(state, crtc); 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci mutex_lock(&dev_priv->wm.wm_mutex); 22308c2ecf20Sopenharmony_ci crtc->wm.active.vlv = crtc_state->wm.vlv.intermediate; 22318c2ecf20Sopenharmony_ci vlv_program_watermarks(dev_priv); 22328c2ecf20Sopenharmony_ci mutex_unlock(&dev_priv->wm.wm_mutex); 22338c2ecf20Sopenharmony_ci} 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_cistatic void vlv_optimize_watermarks(struct intel_atomic_state *state, 22368c2ecf20Sopenharmony_ci struct intel_crtc *crtc) 22378c2ecf20Sopenharmony_ci{ 22388c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 22398c2ecf20Sopenharmony_ci const struct intel_crtc_state *crtc_state = 22408c2ecf20Sopenharmony_ci intel_atomic_get_new_crtc_state(state, crtc); 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci if (!crtc_state->wm.need_postvbl_update) 22438c2ecf20Sopenharmony_ci return; 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_ci mutex_lock(&dev_priv->wm.wm_mutex); 22468c2ecf20Sopenharmony_ci crtc->wm.active.vlv = crtc_state->wm.vlv.optimal; 22478c2ecf20Sopenharmony_ci vlv_program_watermarks(dev_priv); 22488c2ecf20Sopenharmony_ci mutex_unlock(&dev_priv->wm.wm_mutex); 22498c2ecf20Sopenharmony_ci} 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_cistatic void i965_update_wm(struct intel_crtc *unused_crtc) 22528c2ecf20Sopenharmony_ci{ 22538c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(unused_crtc->base.dev); 22548c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 22558c2ecf20Sopenharmony_ci int srwm = 1; 22568c2ecf20Sopenharmony_ci int cursor_sr = 16; 22578c2ecf20Sopenharmony_ci bool cxsr_enabled; 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci /* Calc sr entries for one plane configs */ 22608c2ecf20Sopenharmony_ci crtc = single_enabled_crtc(dev_priv); 22618c2ecf20Sopenharmony_ci if (crtc) { 22628c2ecf20Sopenharmony_ci /* self-refresh has much higher latency */ 22638c2ecf20Sopenharmony_ci static const int sr_latency_ns = 12000; 22648c2ecf20Sopenharmony_ci const struct drm_display_mode *adjusted_mode = 22658c2ecf20Sopenharmony_ci &crtc->config->hw.adjusted_mode; 22668c2ecf20Sopenharmony_ci const struct drm_framebuffer *fb = 22678c2ecf20Sopenharmony_ci crtc->base.primary->state->fb; 22688c2ecf20Sopenharmony_ci int clock = adjusted_mode->crtc_clock; 22698c2ecf20Sopenharmony_ci int htotal = adjusted_mode->crtc_htotal; 22708c2ecf20Sopenharmony_ci int hdisplay = crtc->config->pipe_src_w; 22718c2ecf20Sopenharmony_ci int cpp = fb->format->cpp[0]; 22728c2ecf20Sopenharmony_ci int entries; 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_ci entries = intel_wm_method2(clock, htotal, 22758c2ecf20Sopenharmony_ci hdisplay, cpp, sr_latency_ns / 100); 22768c2ecf20Sopenharmony_ci entries = DIV_ROUND_UP(entries, I915_FIFO_LINE_SIZE); 22778c2ecf20Sopenharmony_ci srwm = I965_FIFO_SIZE - entries; 22788c2ecf20Sopenharmony_ci if (srwm < 0) 22798c2ecf20Sopenharmony_ci srwm = 1; 22808c2ecf20Sopenharmony_ci srwm &= 0x1ff; 22818c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 22828c2ecf20Sopenharmony_ci "self-refresh entries: %d, wm: %d\n", 22838c2ecf20Sopenharmony_ci entries, srwm); 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci entries = intel_wm_method2(clock, htotal, 22868c2ecf20Sopenharmony_ci crtc->base.cursor->state->crtc_w, 4, 22878c2ecf20Sopenharmony_ci sr_latency_ns / 100); 22888c2ecf20Sopenharmony_ci entries = DIV_ROUND_UP(entries, 22898c2ecf20Sopenharmony_ci i965_cursor_wm_info.cacheline_size) + 22908c2ecf20Sopenharmony_ci i965_cursor_wm_info.guard_size; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci cursor_sr = i965_cursor_wm_info.fifo_size - entries; 22938c2ecf20Sopenharmony_ci if (cursor_sr > i965_cursor_wm_info.max_wm) 22948c2ecf20Sopenharmony_ci cursor_sr = i965_cursor_wm_info.max_wm; 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 22978c2ecf20Sopenharmony_ci "self-refresh watermark: display plane %d " 22988c2ecf20Sopenharmony_ci "cursor %d\n", srwm, cursor_sr); 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_ci cxsr_enabled = true; 23018c2ecf20Sopenharmony_ci } else { 23028c2ecf20Sopenharmony_ci cxsr_enabled = false; 23038c2ecf20Sopenharmony_ci /* Turn off self refresh if both pipes are enabled */ 23048c2ecf20Sopenharmony_ci intel_set_memory_cxsr(dev_priv, false); 23058c2ecf20Sopenharmony_ci } 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 23088c2ecf20Sopenharmony_ci "Setting FIFO watermarks - A: 8, B: 8, C: 8, SR %d\n", 23098c2ecf20Sopenharmony_ci srwm); 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci /* 965 has limitations... */ 23128c2ecf20Sopenharmony_ci I915_WRITE(DSPFW1, FW_WM(srwm, SR) | 23138c2ecf20Sopenharmony_ci FW_WM(8, CURSORB) | 23148c2ecf20Sopenharmony_ci FW_WM(8, PLANEB) | 23158c2ecf20Sopenharmony_ci FW_WM(8, PLANEA)); 23168c2ecf20Sopenharmony_ci I915_WRITE(DSPFW2, FW_WM(8, CURSORA) | 23178c2ecf20Sopenharmony_ci FW_WM(8, PLANEC_OLD)); 23188c2ecf20Sopenharmony_ci /* update cursor SR watermark */ 23198c2ecf20Sopenharmony_ci I915_WRITE(DSPFW3, FW_WM(cursor_sr, CURSOR_SR)); 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ci if (cxsr_enabled) 23228c2ecf20Sopenharmony_ci intel_set_memory_cxsr(dev_priv, true); 23238c2ecf20Sopenharmony_ci} 23248c2ecf20Sopenharmony_ci 23258c2ecf20Sopenharmony_ci#undef FW_WM 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_cistatic void i9xx_update_wm(struct intel_crtc *unused_crtc) 23288c2ecf20Sopenharmony_ci{ 23298c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(unused_crtc->base.dev); 23308c2ecf20Sopenharmony_ci const struct intel_watermark_params *wm_info; 23318c2ecf20Sopenharmony_ci u32 fwater_lo; 23328c2ecf20Sopenharmony_ci u32 fwater_hi; 23338c2ecf20Sopenharmony_ci int cwm, srwm = 1; 23348c2ecf20Sopenharmony_ci int fifo_size; 23358c2ecf20Sopenharmony_ci int planea_wm, planeb_wm; 23368c2ecf20Sopenharmony_ci struct intel_crtc *crtc, *enabled = NULL; 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci if (IS_I945GM(dev_priv)) 23398c2ecf20Sopenharmony_ci wm_info = &i945_wm_info; 23408c2ecf20Sopenharmony_ci else if (!IS_GEN(dev_priv, 2)) 23418c2ecf20Sopenharmony_ci wm_info = &i915_wm_info; 23428c2ecf20Sopenharmony_ci else 23438c2ecf20Sopenharmony_ci wm_info = &i830_a_wm_info; 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_ci fifo_size = dev_priv->display.get_fifo_size(dev_priv, PLANE_A); 23468c2ecf20Sopenharmony_ci crtc = intel_get_crtc_for_plane(dev_priv, PLANE_A); 23478c2ecf20Sopenharmony_ci if (intel_crtc_active(crtc)) { 23488c2ecf20Sopenharmony_ci const struct drm_display_mode *adjusted_mode = 23498c2ecf20Sopenharmony_ci &crtc->config->hw.adjusted_mode; 23508c2ecf20Sopenharmony_ci const struct drm_framebuffer *fb = 23518c2ecf20Sopenharmony_ci crtc->base.primary->state->fb; 23528c2ecf20Sopenharmony_ci int cpp; 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci if (IS_GEN(dev_priv, 2)) 23558c2ecf20Sopenharmony_ci cpp = 4; 23568c2ecf20Sopenharmony_ci else 23578c2ecf20Sopenharmony_ci cpp = fb->format->cpp[0]; 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock, 23608c2ecf20Sopenharmony_ci wm_info, fifo_size, cpp, 23618c2ecf20Sopenharmony_ci pessimal_latency_ns); 23628c2ecf20Sopenharmony_ci enabled = crtc; 23638c2ecf20Sopenharmony_ci } else { 23648c2ecf20Sopenharmony_ci planea_wm = fifo_size - wm_info->guard_size; 23658c2ecf20Sopenharmony_ci if (planea_wm > (long)wm_info->max_wm) 23668c2ecf20Sopenharmony_ci planea_wm = wm_info->max_wm; 23678c2ecf20Sopenharmony_ci } 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci if (IS_GEN(dev_priv, 2)) 23708c2ecf20Sopenharmony_ci wm_info = &i830_bc_wm_info; 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci fifo_size = dev_priv->display.get_fifo_size(dev_priv, PLANE_B); 23738c2ecf20Sopenharmony_ci crtc = intel_get_crtc_for_plane(dev_priv, PLANE_B); 23748c2ecf20Sopenharmony_ci if (intel_crtc_active(crtc)) { 23758c2ecf20Sopenharmony_ci const struct drm_display_mode *adjusted_mode = 23768c2ecf20Sopenharmony_ci &crtc->config->hw.adjusted_mode; 23778c2ecf20Sopenharmony_ci const struct drm_framebuffer *fb = 23788c2ecf20Sopenharmony_ci crtc->base.primary->state->fb; 23798c2ecf20Sopenharmony_ci int cpp; 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci if (IS_GEN(dev_priv, 2)) 23828c2ecf20Sopenharmony_ci cpp = 4; 23838c2ecf20Sopenharmony_ci else 23848c2ecf20Sopenharmony_ci cpp = fb->format->cpp[0]; 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci planeb_wm = intel_calculate_wm(adjusted_mode->crtc_clock, 23878c2ecf20Sopenharmony_ci wm_info, fifo_size, cpp, 23888c2ecf20Sopenharmony_ci pessimal_latency_ns); 23898c2ecf20Sopenharmony_ci if (enabled == NULL) 23908c2ecf20Sopenharmony_ci enabled = crtc; 23918c2ecf20Sopenharmony_ci else 23928c2ecf20Sopenharmony_ci enabled = NULL; 23938c2ecf20Sopenharmony_ci } else { 23948c2ecf20Sopenharmony_ci planeb_wm = fifo_size - wm_info->guard_size; 23958c2ecf20Sopenharmony_ci if (planeb_wm > (long)wm_info->max_wm) 23968c2ecf20Sopenharmony_ci planeb_wm = wm_info->max_wm; 23978c2ecf20Sopenharmony_ci } 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 24008c2ecf20Sopenharmony_ci "FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm); 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci if (IS_I915GM(dev_priv) && enabled) { 24038c2ecf20Sopenharmony_ci struct drm_i915_gem_object *obj; 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci obj = intel_fb_obj(enabled->base.primary->state->fb); 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_ci /* self-refresh seems busted with untiled */ 24088c2ecf20Sopenharmony_ci if (!i915_gem_object_is_tiled(obj)) 24098c2ecf20Sopenharmony_ci enabled = NULL; 24108c2ecf20Sopenharmony_ci } 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci /* 24138c2ecf20Sopenharmony_ci * Overlay gets an aggressive default since video jitter is bad. 24148c2ecf20Sopenharmony_ci */ 24158c2ecf20Sopenharmony_ci cwm = 2; 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci /* Play safe and disable self-refresh before adjusting watermarks. */ 24188c2ecf20Sopenharmony_ci intel_set_memory_cxsr(dev_priv, false); 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci /* Calc sr entries for one plane configs */ 24218c2ecf20Sopenharmony_ci if (HAS_FW_BLC(dev_priv) && enabled) { 24228c2ecf20Sopenharmony_ci /* self-refresh has much higher latency */ 24238c2ecf20Sopenharmony_ci static const int sr_latency_ns = 6000; 24248c2ecf20Sopenharmony_ci const struct drm_display_mode *adjusted_mode = 24258c2ecf20Sopenharmony_ci &enabled->config->hw.adjusted_mode; 24268c2ecf20Sopenharmony_ci const struct drm_framebuffer *fb = 24278c2ecf20Sopenharmony_ci enabled->base.primary->state->fb; 24288c2ecf20Sopenharmony_ci int clock = adjusted_mode->crtc_clock; 24298c2ecf20Sopenharmony_ci int htotal = adjusted_mode->crtc_htotal; 24308c2ecf20Sopenharmony_ci int hdisplay = enabled->config->pipe_src_w; 24318c2ecf20Sopenharmony_ci int cpp; 24328c2ecf20Sopenharmony_ci int entries; 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_ci if (IS_I915GM(dev_priv) || IS_I945GM(dev_priv)) 24358c2ecf20Sopenharmony_ci cpp = 4; 24368c2ecf20Sopenharmony_ci else 24378c2ecf20Sopenharmony_ci cpp = fb->format->cpp[0]; 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci entries = intel_wm_method2(clock, htotal, hdisplay, cpp, 24408c2ecf20Sopenharmony_ci sr_latency_ns / 100); 24418c2ecf20Sopenharmony_ci entries = DIV_ROUND_UP(entries, wm_info->cacheline_size); 24428c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 24438c2ecf20Sopenharmony_ci "self-refresh entries: %d\n", entries); 24448c2ecf20Sopenharmony_ci srwm = wm_info->fifo_size - entries; 24458c2ecf20Sopenharmony_ci if (srwm < 0) 24468c2ecf20Sopenharmony_ci srwm = 1; 24478c2ecf20Sopenharmony_ci 24488c2ecf20Sopenharmony_ci if (IS_I945G(dev_priv) || IS_I945GM(dev_priv)) 24498c2ecf20Sopenharmony_ci I915_WRITE(FW_BLC_SELF, 24508c2ecf20Sopenharmony_ci FW_BLC_SELF_FIFO_MASK | (srwm & 0xff)); 24518c2ecf20Sopenharmony_ci else 24528c2ecf20Sopenharmony_ci I915_WRITE(FW_BLC_SELF, srwm & 0x3f); 24538c2ecf20Sopenharmony_ci } 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 24568c2ecf20Sopenharmony_ci "Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n", 24578c2ecf20Sopenharmony_ci planea_wm, planeb_wm, cwm, srwm); 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ci fwater_lo = ((planeb_wm & 0x3f) << 16) | (planea_wm & 0x3f); 24608c2ecf20Sopenharmony_ci fwater_hi = (cwm & 0x1f); 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_ci /* Set request length to 8 cachelines per fetch */ 24638c2ecf20Sopenharmony_ci fwater_lo = fwater_lo | (1 << 24) | (1 << 8); 24648c2ecf20Sopenharmony_ci fwater_hi = fwater_hi | (1 << 8); 24658c2ecf20Sopenharmony_ci 24668c2ecf20Sopenharmony_ci I915_WRITE(FW_BLC, fwater_lo); 24678c2ecf20Sopenharmony_ci I915_WRITE(FW_BLC2, fwater_hi); 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ci if (enabled) 24708c2ecf20Sopenharmony_ci intel_set_memory_cxsr(dev_priv, true); 24718c2ecf20Sopenharmony_ci} 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_cistatic void i845_update_wm(struct intel_crtc *unused_crtc) 24748c2ecf20Sopenharmony_ci{ 24758c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(unused_crtc->base.dev); 24768c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 24778c2ecf20Sopenharmony_ci const struct drm_display_mode *adjusted_mode; 24788c2ecf20Sopenharmony_ci u32 fwater_lo; 24798c2ecf20Sopenharmony_ci int planea_wm; 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_ci crtc = single_enabled_crtc(dev_priv); 24828c2ecf20Sopenharmony_ci if (crtc == NULL) 24838c2ecf20Sopenharmony_ci return; 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci adjusted_mode = &crtc->config->hw.adjusted_mode; 24868c2ecf20Sopenharmony_ci planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock, 24878c2ecf20Sopenharmony_ci &i845_wm_info, 24888c2ecf20Sopenharmony_ci dev_priv->display.get_fifo_size(dev_priv, PLANE_A), 24898c2ecf20Sopenharmony_ci 4, pessimal_latency_ns); 24908c2ecf20Sopenharmony_ci fwater_lo = I915_READ(FW_BLC) & ~0xfff; 24918c2ecf20Sopenharmony_ci fwater_lo |= (3<<8) | planea_wm; 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 24948c2ecf20Sopenharmony_ci "Setting FIFO watermarks - A: %d\n", planea_wm); 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_ci I915_WRITE(FW_BLC, fwater_lo); 24978c2ecf20Sopenharmony_ci} 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_ci/* latency must be in 0.1us units. */ 25008c2ecf20Sopenharmony_cistatic unsigned int ilk_wm_method1(unsigned int pixel_rate, 25018c2ecf20Sopenharmony_ci unsigned int cpp, 25028c2ecf20Sopenharmony_ci unsigned int latency) 25038c2ecf20Sopenharmony_ci{ 25048c2ecf20Sopenharmony_ci unsigned int ret; 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci ret = intel_wm_method1(pixel_rate, cpp, latency); 25078c2ecf20Sopenharmony_ci ret = DIV_ROUND_UP(ret, 64) + 2; 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_ci return ret; 25108c2ecf20Sopenharmony_ci} 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci/* latency must be in 0.1us units. */ 25138c2ecf20Sopenharmony_cistatic unsigned int ilk_wm_method2(unsigned int pixel_rate, 25148c2ecf20Sopenharmony_ci unsigned int htotal, 25158c2ecf20Sopenharmony_ci unsigned int width, 25168c2ecf20Sopenharmony_ci unsigned int cpp, 25178c2ecf20Sopenharmony_ci unsigned int latency) 25188c2ecf20Sopenharmony_ci{ 25198c2ecf20Sopenharmony_ci unsigned int ret; 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_ci ret = intel_wm_method2(pixel_rate, htotal, 25228c2ecf20Sopenharmony_ci width, cpp, latency); 25238c2ecf20Sopenharmony_ci ret = DIV_ROUND_UP(ret, 64) + 2; 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_ci return ret; 25268c2ecf20Sopenharmony_ci} 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_cistatic u32 ilk_wm_fbc(u32 pri_val, u32 horiz_pixels, u8 cpp) 25298c2ecf20Sopenharmony_ci{ 25308c2ecf20Sopenharmony_ci /* 25318c2ecf20Sopenharmony_ci * Neither of these should be possible since this function shouldn't be 25328c2ecf20Sopenharmony_ci * called if the CRTC is off or the plane is invisible. But let's be 25338c2ecf20Sopenharmony_ci * extra paranoid to avoid a potential divide-by-zero if we screw up 25348c2ecf20Sopenharmony_ci * elsewhere in the driver. 25358c2ecf20Sopenharmony_ci */ 25368c2ecf20Sopenharmony_ci if (WARN_ON(!cpp)) 25378c2ecf20Sopenharmony_ci return 0; 25388c2ecf20Sopenharmony_ci if (WARN_ON(!horiz_pixels)) 25398c2ecf20Sopenharmony_ci return 0; 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci return DIV_ROUND_UP(pri_val * 64, horiz_pixels * cpp) + 2; 25428c2ecf20Sopenharmony_ci} 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_cistruct ilk_wm_maximums { 25458c2ecf20Sopenharmony_ci u16 pri; 25468c2ecf20Sopenharmony_ci u16 spr; 25478c2ecf20Sopenharmony_ci u16 cur; 25488c2ecf20Sopenharmony_ci u16 fbc; 25498c2ecf20Sopenharmony_ci}; 25508c2ecf20Sopenharmony_ci 25518c2ecf20Sopenharmony_ci/* 25528c2ecf20Sopenharmony_ci * For both WM_PIPE and WM_LP. 25538c2ecf20Sopenharmony_ci * mem_value must be in 0.1us units. 25548c2ecf20Sopenharmony_ci */ 25558c2ecf20Sopenharmony_cistatic u32 ilk_compute_pri_wm(const struct intel_crtc_state *crtc_state, 25568c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state, 25578c2ecf20Sopenharmony_ci u32 mem_value, bool is_lp) 25588c2ecf20Sopenharmony_ci{ 25598c2ecf20Sopenharmony_ci u32 method1, method2; 25608c2ecf20Sopenharmony_ci int cpp; 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci if (mem_value == 0) 25638c2ecf20Sopenharmony_ci return U32_MAX; 25648c2ecf20Sopenharmony_ci 25658c2ecf20Sopenharmony_ci if (!intel_wm_plane_visible(crtc_state, plane_state)) 25668c2ecf20Sopenharmony_ci return 0; 25678c2ecf20Sopenharmony_ci 25688c2ecf20Sopenharmony_ci cpp = plane_state->hw.fb->format->cpp[0]; 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_ci method1 = ilk_wm_method1(crtc_state->pixel_rate, cpp, mem_value); 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci if (!is_lp) 25738c2ecf20Sopenharmony_ci return method1; 25748c2ecf20Sopenharmony_ci 25758c2ecf20Sopenharmony_ci method2 = ilk_wm_method2(crtc_state->pixel_rate, 25768c2ecf20Sopenharmony_ci crtc_state->hw.adjusted_mode.crtc_htotal, 25778c2ecf20Sopenharmony_ci drm_rect_width(&plane_state->uapi.dst), 25788c2ecf20Sopenharmony_ci cpp, mem_value); 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci return min(method1, method2); 25818c2ecf20Sopenharmony_ci} 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_ci/* 25848c2ecf20Sopenharmony_ci * For both WM_PIPE and WM_LP. 25858c2ecf20Sopenharmony_ci * mem_value must be in 0.1us units. 25868c2ecf20Sopenharmony_ci */ 25878c2ecf20Sopenharmony_cistatic u32 ilk_compute_spr_wm(const struct intel_crtc_state *crtc_state, 25888c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state, 25898c2ecf20Sopenharmony_ci u32 mem_value) 25908c2ecf20Sopenharmony_ci{ 25918c2ecf20Sopenharmony_ci u32 method1, method2; 25928c2ecf20Sopenharmony_ci int cpp; 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_ci if (mem_value == 0) 25958c2ecf20Sopenharmony_ci return U32_MAX; 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci if (!intel_wm_plane_visible(crtc_state, plane_state)) 25988c2ecf20Sopenharmony_ci return 0; 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_ci cpp = plane_state->hw.fb->format->cpp[0]; 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_ci method1 = ilk_wm_method1(crtc_state->pixel_rate, cpp, mem_value); 26038c2ecf20Sopenharmony_ci method2 = ilk_wm_method2(crtc_state->pixel_rate, 26048c2ecf20Sopenharmony_ci crtc_state->hw.adjusted_mode.crtc_htotal, 26058c2ecf20Sopenharmony_ci drm_rect_width(&plane_state->uapi.dst), 26068c2ecf20Sopenharmony_ci cpp, mem_value); 26078c2ecf20Sopenharmony_ci return min(method1, method2); 26088c2ecf20Sopenharmony_ci} 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci/* 26118c2ecf20Sopenharmony_ci * For both WM_PIPE and WM_LP. 26128c2ecf20Sopenharmony_ci * mem_value must be in 0.1us units. 26138c2ecf20Sopenharmony_ci */ 26148c2ecf20Sopenharmony_cistatic u32 ilk_compute_cur_wm(const struct intel_crtc_state *crtc_state, 26158c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state, 26168c2ecf20Sopenharmony_ci u32 mem_value) 26178c2ecf20Sopenharmony_ci{ 26188c2ecf20Sopenharmony_ci int cpp; 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci if (mem_value == 0) 26218c2ecf20Sopenharmony_ci return U32_MAX; 26228c2ecf20Sopenharmony_ci 26238c2ecf20Sopenharmony_ci if (!intel_wm_plane_visible(crtc_state, plane_state)) 26248c2ecf20Sopenharmony_ci return 0; 26258c2ecf20Sopenharmony_ci 26268c2ecf20Sopenharmony_ci cpp = plane_state->hw.fb->format->cpp[0]; 26278c2ecf20Sopenharmony_ci 26288c2ecf20Sopenharmony_ci return ilk_wm_method2(crtc_state->pixel_rate, 26298c2ecf20Sopenharmony_ci crtc_state->hw.adjusted_mode.crtc_htotal, 26308c2ecf20Sopenharmony_ci drm_rect_width(&plane_state->uapi.dst), 26318c2ecf20Sopenharmony_ci cpp, mem_value); 26328c2ecf20Sopenharmony_ci} 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_ci/* Only for WM_LP. */ 26358c2ecf20Sopenharmony_cistatic u32 ilk_compute_fbc_wm(const struct intel_crtc_state *crtc_state, 26368c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state, 26378c2ecf20Sopenharmony_ci u32 pri_val) 26388c2ecf20Sopenharmony_ci{ 26398c2ecf20Sopenharmony_ci int cpp; 26408c2ecf20Sopenharmony_ci 26418c2ecf20Sopenharmony_ci if (!intel_wm_plane_visible(crtc_state, plane_state)) 26428c2ecf20Sopenharmony_ci return 0; 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci cpp = plane_state->hw.fb->format->cpp[0]; 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci return ilk_wm_fbc(pri_val, drm_rect_width(&plane_state->uapi.dst), 26478c2ecf20Sopenharmony_ci cpp); 26488c2ecf20Sopenharmony_ci} 26498c2ecf20Sopenharmony_ci 26508c2ecf20Sopenharmony_cistatic unsigned int 26518c2ecf20Sopenharmony_ciilk_display_fifo_size(const struct drm_i915_private *dev_priv) 26528c2ecf20Sopenharmony_ci{ 26538c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 8) 26548c2ecf20Sopenharmony_ci return 3072; 26558c2ecf20Sopenharmony_ci else if (INTEL_GEN(dev_priv) >= 7) 26568c2ecf20Sopenharmony_ci return 768; 26578c2ecf20Sopenharmony_ci else 26588c2ecf20Sopenharmony_ci return 512; 26598c2ecf20Sopenharmony_ci} 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_cistatic unsigned int 26628c2ecf20Sopenharmony_ciilk_plane_wm_reg_max(const struct drm_i915_private *dev_priv, 26638c2ecf20Sopenharmony_ci int level, bool is_sprite) 26648c2ecf20Sopenharmony_ci{ 26658c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 8) 26668c2ecf20Sopenharmony_ci /* BDW primary/sprite plane watermarks */ 26678c2ecf20Sopenharmony_ci return level == 0 ? 255 : 2047; 26688c2ecf20Sopenharmony_ci else if (INTEL_GEN(dev_priv) >= 7) 26698c2ecf20Sopenharmony_ci /* IVB/HSW primary/sprite plane watermarks */ 26708c2ecf20Sopenharmony_ci return level == 0 ? 127 : 1023; 26718c2ecf20Sopenharmony_ci else if (!is_sprite) 26728c2ecf20Sopenharmony_ci /* ILK/SNB primary plane watermarks */ 26738c2ecf20Sopenharmony_ci return level == 0 ? 127 : 511; 26748c2ecf20Sopenharmony_ci else 26758c2ecf20Sopenharmony_ci /* ILK/SNB sprite plane watermarks */ 26768c2ecf20Sopenharmony_ci return level == 0 ? 63 : 255; 26778c2ecf20Sopenharmony_ci} 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_cistatic unsigned int 26808c2ecf20Sopenharmony_ciilk_cursor_wm_reg_max(const struct drm_i915_private *dev_priv, int level) 26818c2ecf20Sopenharmony_ci{ 26828c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 7) 26838c2ecf20Sopenharmony_ci return level == 0 ? 63 : 255; 26848c2ecf20Sopenharmony_ci else 26858c2ecf20Sopenharmony_ci return level == 0 ? 31 : 63; 26868c2ecf20Sopenharmony_ci} 26878c2ecf20Sopenharmony_ci 26888c2ecf20Sopenharmony_cistatic unsigned int ilk_fbc_wm_reg_max(const struct drm_i915_private *dev_priv) 26898c2ecf20Sopenharmony_ci{ 26908c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 8) 26918c2ecf20Sopenharmony_ci return 31; 26928c2ecf20Sopenharmony_ci else 26938c2ecf20Sopenharmony_ci return 15; 26948c2ecf20Sopenharmony_ci} 26958c2ecf20Sopenharmony_ci 26968c2ecf20Sopenharmony_ci/* Calculate the maximum primary/sprite plane watermark */ 26978c2ecf20Sopenharmony_cistatic unsigned int ilk_plane_wm_max(const struct drm_i915_private *dev_priv, 26988c2ecf20Sopenharmony_ci int level, 26998c2ecf20Sopenharmony_ci const struct intel_wm_config *config, 27008c2ecf20Sopenharmony_ci enum intel_ddb_partitioning ddb_partitioning, 27018c2ecf20Sopenharmony_ci bool is_sprite) 27028c2ecf20Sopenharmony_ci{ 27038c2ecf20Sopenharmony_ci unsigned int fifo_size = ilk_display_fifo_size(dev_priv); 27048c2ecf20Sopenharmony_ci 27058c2ecf20Sopenharmony_ci /* if sprites aren't enabled, sprites get nothing */ 27068c2ecf20Sopenharmony_ci if (is_sprite && !config->sprites_enabled) 27078c2ecf20Sopenharmony_ci return 0; 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci /* HSW allows LP1+ watermarks even with multiple pipes */ 27108c2ecf20Sopenharmony_ci if (level == 0 || config->num_pipes_active > 1) { 27118c2ecf20Sopenharmony_ci fifo_size /= INTEL_NUM_PIPES(dev_priv); 27128c2ecf20Sopenharmony_ci 27138c2ecf20Sopenharmony_ci /* 27148c2ecf20Sopenharmony_ci * For some reason the non self refresh 27158c2ecf20Sopenharmony_ci * FIFO size is only half of the self 27168c2ecf20Sopenharmony_ci * refresh FIFO size on ILK/SNB. 27178c2ecf20Sopenharmony_ci */ 27188c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) <= 6) 27198c2ecf20Sopenharmony_ci fifo_size /= 2; 27208c2ecf20Sopenharmony_ci } 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci if (config->sprites_enabled) { 27238c2ecf20Sopenharmony_ci /* level 0 is always calculated with 1:1 split */ 27248c2ecf20Sopenharmony_ci if (level > 0 && ddb_partitioning == INTEL_DDB_PART_5_6) { 27258c2ecf20Sopenharmony_ci if (is_sprite) 27268c2ecf20Sopenharmony_ci fifo_size *= 5; 27278c2ecf20Sopenharmony_ci fifo_size /= 6; 27288c2ecf20Sopenharmony_ci } else { 27298c2ecf20Sopenharmony_ci fifo_size /= 2; 27308c2ecf20Sopenharmony_ci } 27318c2ecf20Sopenharmony_ci } 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci /* clamp to max that the registers can hold */ 27348c2ecf20Sopenharmony_ci return min(fifo_size, ilk_plane_wm_reg_max(dev_priv, level, is_sprite)); 27358c2ecf20Sopenharmony_ci} 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_ci/* Calculate the maximum cursor plane watermark */ 27388c2ecf20Sopenharmony_cistatic unsigned int ilk_cursor_wm_max(const struct drm_i915_private *dev_priv, 27398c2ecf20Sopenharmony_ci int level, 27408c2ecf20Sopenharmony_ci const struct intel_wm_config *config) 27418c2ecf20Sopenharmony_ci{ 27428c2ecf20Sopenharmony_ci /* HSW LP1+ watermarks w/ multiple pipes */ 27438c2ecf20Sopenharmony_ci if (level > 0 && config->num_pipes_active > 1) 27448c2ecf20Sopenharmony_ci return 64; 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci /* otherwise just report max that registers can hold */ 27478c2ecf20Sopenharmony_ci return ilk_cursor_wm_reg_max(dev_priv, level); 27488c2ecf20Sopenharmony_ci} 27498c2ecf20Sopenharmony_ci 27508c2ecf20Sopenharmony_cistatic void ilk_compute_wm_maximums(const struct drm_i915_private *dev_priv, 27518c2ecf20Sopenharmony_ci int level, 27528c2ecf20Sopenharmony_ci const struct intel_wm_config *config, 27538c2ecf20Sopenharmony_ci enum intel_ddb_partitioning ddb_partitioning, 27548c2ecf20Sopenharmony_ci struct ilk_wm_maximums *max) 27558c2ecf20Sopenharmony_ci{ 27568c2ecf20Sopenharmony_ci max->pri = ilk_plane_wm_max(dev_priv, level, config, ddb_partitioning, false); 27578c2ecf20Sopenharmony_ci max->spr = ilk_plane_wm_max(dev_priv, level, config, ddb_partitioning, true); 27588c2ecf20Sopenharmony_ci max->cur = ilk_cursor_wm_max(dev_priv, level, config); 27598c2ecf20Sopenharmony_ci max->fbc = ilk_fbc_wm_reg_max(dev_priv); 27608c2ecf20Sopenharmony_ci} 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_cistatic void ilk_compute_wm_reg_maximums(const struct drm_i915_private *dev_priv, 27638c2ecf20Sopenharmony_ci int level, 27648c2ecf20Sopenharmony_ci struct ilk_wm_maximums *max) 27658c2ecf20Sopenharmony_ci{ 27668c2ecf20Sopenharmony_ci max->pri = ilk_plane_wm_reg_max(dev_priv, level, false); 27678c2ecf20Sopenharmony_ci max->spr = ilk_plane_wm_reg_max(dev_priv, level, true); 27688c2ecf20Sopenharmony_ci max->cur = ilk_cursor_wm_reg_max(dev_priv, level); 27698c2ecf20Sopenharmony_ci max->fbc = ilk_fbc_wm_reg_max(dev_priv); 27708c2ecf20Sopenharmony_ci} 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_cistatic bool ilk_validate_wm_level(int level, 27738c2ecf20Sopenharmony_ci const struct ilk_wm_maximums *max, 27748c2ecf20Sopenharmony_ci struct intel_wm_level *result) 27758c2ecf20Sopenharmony_ci{ 27768c2ecf20Sopenharmony_ci bool ret; 27778c2ecf20Sopenharmony_ci 27788c2ecf20Sopenharmony_ci /* already determined to be invalid? */ 27798c2ecf20Sopenharmony_ci if (!result->enable) 27808c2ecf20Sopenharmony_ci return false; 27818c2ecf20Sopenharmony_ci 27828c2ecf20Sopenharmony_ci result->enable = result->pri_val <= max->pri && 27838c2ecf20Sopenharmony_ci result->spr_val <= max->spr && 27848c2ecf20Sopenharmony_ci result->cur_val <= max->cur; 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ci ret = result->enable; 27878c2ecf20Sopenharmony_ci 27888c2ecf20Sopenharmony_ci /* 27898c2ecf20Sopenharmony_ci * HACK until we can pre-compute everything, 27908c2ecf20Sopenharmony_ci * and thus fail gracefully if LP0 watermarks 27918c2ecf20Sopenharmony_ci * are exceeded... 27928c2ecf20Sopenharmony_ci */ 27938c2ecf20Sopenharmony_ci if (level == 0 && !result->enable) { 27948c2ecf20Sopenharmony_ci if (result->pri_val > max->pri) 27958c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Primary WM%d too large %u (max %u)\n", 27968c2ecf20Sopenharmony_ci level, result->pri_val, max->pri); 27978c2ecf20Sopenharmony_ci if (result->spr_val > max->spr) 27988c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Sprite WM%d too large %u (max %u)\n", 27998c2ecf20Sopenharmony_ci level, result->spr_val, max->spr); 28008c2ecf20Sopenharmony_ci if (result->cur_val > max->cur) 28018c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Cursor WM%d too large %u (max %u)\n", 28028c2ecf20Sopenharmony_ci level, result->cur_val, max->cur); 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_ci result->pri_val = min_t(u32, result->pri_val, max->pri); 28058c2ecf20Sopenharmony_ci result->spr_val = min_t(u32, result->spr_val, max->spr); 28068c2ecf20Sopenharmony_ci result->cur_val = min_t(u32, result->cur_val, max->cur); 28078c2ecf20Sopenharmony_ci result->enable = true; 28088c2ecf20Sopenharmony_ci } 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci return ret; 28118c2ecf20Sopenharmony_ci} 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_cistatic void ilk_compute_wm_level(const struct drm_i915_private *dev_priv, 28148c2ecf20Sopenharmony_ci const struct intel_crtc *crtc, 28158c2ecf20Sopenharmony_ci int level, 28168c2ecf20Sopenharmony_ci struct intel_crtc_state *crtc_state, 28178c2ecf20Sopenharmony_ci const struct intel_plane_state *pristate, 28188c2ecf20Sopenharmony_ci const struct intel_plane_state *sprstate, 28198c2ecf20Sopenharmony_ci const struct intel_plane_state *curstate, 28208c2ecf20Sopenharmony_ci struct intel_wm_level *result) 28218c2ecf20Sopenharmony_ci{ 28228c2ecf20Sopenharmony_ci u16 pri_latency = dev_priv->wm.pri_latency[level]; 28238c2ecf20Sopenharmony_ci u16 spr_latency = dev_priv->wm.spr_latency[level]; 28248c2ecf20Sopenharmony_ci u16 cur_latency = dev_priv->wm.cur_latency[level]; 28258c2ecf20Sopenharmony_ci 28268c2ecf20Sopenharmony_ci /* WM1+ latency values stored in 0.5us units */ 28278c2ecf20Sopenharmony_ci if (level > 0) { 28288c2ecf20Sopenharmony_ci pri_latency *= 5; 28298c2ecf20Sopenharmony_ci spr_latency *= 5; 28308c2ecf20Sopenharmony_ci cur_latency *= 5; 28318c2ecf20Sopenharmony_ci } 28328c2ecf20Sopenharmony_ci 28338c2ecf20Sopenharmony_ci if (pristate) { 28348c2ecf20Sopenharmony_ci result->pri_val = ilk_compute_pri_wm(crtc_state, pristate, 28358c2ecf20Sopenharmony_ci pri_latency, level); 28368c2ecf20Sopenharmony_ci result->fbc_val = ilk_compute_fbc_wm(crtc_state, pristate, result->pri_val); 28378c2ecf20Sopenharmony_ci } 28388c2ecf20Sopenharmony_ci 28398c2ecf20Sopenharmony_ci if (sprstate) 28408c2ecf20Sopenharmony_ci result->spr_val = ilk_compute_spr_wm(crtc_state, sprstate, spr_latency); 28418c2ecf20Sopenharmony_ci 28428c2ecf20Sopenharmony_ci if (curstate) 28438c2ecf20Sopenharmony_ci result->cur_val = ilk_compute_cur_wm(crtc_state, curstate, cur_latency); 28448c2ecf20Sopenharmony_ci 28458c2ecf20Sopenharmony_ci result->enable = true; 28468c2ecf20Sopenharmony_ci} 28478c2ecf20Sopenharmony_ci 28488c2ecf20Sopenharmony_cistatic void intel_read_wm_latency(struct drm_i915_private *dev_priv, 28498c2ecf20Sopenharmony_ci u16 wm[]) 28508c2ecf20Sopenharmony_ci{ 28518c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 28528c2ecf20Sopenharmony_ci 28538c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 9) { 28548c2ecf20Sopenharmony_ci u32 val; 28558c2ecf20Sopenharmony_ci int ret, i; 28568c2ecf20Sopenharmony_ci int level, max_level = ilk_wm_max_level(dev_priv); 28578c2ecf20Sopenharmony_ci 28588c2ecf20Sopenharmony_ci /* read the first set of memory latencies[0:3] */ 28598c2ecf20Sopenharmony_ci val = 0; /* data0 to be programmed to 0 for first set */ 28608c2ecf20Sopenharmony_ci ret = sandybridge_pcode_read(dev_priv, 28618c2ecf20Sopenharmony_ci GEN9_PCODE_READ_MEM_LATENCY, 28628c2ecf20Sopenharmony_ci &val, NULL); 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci if (ret) { 28658c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, 28668c2ecf20Sopenharmony_ci "SKL Mailbox read error = %d\n", ret); 28678c2ecf20Sopenharmony_ci return; 28688c2ecf20Sopenharmony_ci } 28698c2ecf20Sopenharmony_ci 28708c2ecf20Sopenharmony_ci wm[0] = val & GEN9_MEM_LATENCY_LEVEL_MASK; 28718c2ecf20Sopenharmony_ci wm[1] = (val >> GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT) & 28728c2ecf20Sopenharmony_ci GEN9_MEM_LATENCY_LEVEL_MASK; 28738c2ecf20Sopenharmony_ci wm[2] = (val >> GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT) & 28748c2ecf20Sopenharmony_ci GEN9_MEM_LATENCY_LEVEL_MASK; 28758c2ecf20Sopenharmony_ci wm[3] = (val >> GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT) & 28768c2ecf20Sopenharmony_ci GEN9_MEM_LATENCY_LEVEL_MASK; 28778c2ecf20Sopenharmony_ci 28788c2ecf20Sopenharmony_ci /* read the second set of memory latencies[4:7] */ 28798c2ecf20Sopenharmony_ci val = 1; /* data0 to be programmed to 1 for second set */ 28808c2ecf20Sopenharmony_ci ret = sandybridge_pcode_read(dev_priv, 28818c2ecf20Sopenharmony_ci GEN9_PCODE_READ_MEM_LATENCY, 28828c2ecf20Sopenharmony_ci &val, NULL); 28838c2ecf20Sopenharmony_ci if (ret) { 28848c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, 28858c2ecf20Sopenharmony_ci "SKL Mailbox read error = %d\n", ret); 28868c2ecf20Sopenharmony_ci return; 28878c2ecf20Sopenharmony_ci } 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_ci wm[4] = val & GEN9_MEM_LATENCY_LEVEL_MASK; 28908c2ecf20Sopenharmony_ci wm[5] = (val >> GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT) & 28918c2ecf20Sopenharmony_ci GEN9_MEM_LATENCY_LEVEL_MASK; 28928c2ecf20Sopenharmony_ci wm[6] = (val >> GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT) & 28938c2ecf20Sopenharmony_ci GEN9_MEM_LATENCY_LEVEL_MASK; 28948c2ecf20Sopenharmony_ci wm[7] = (val >> GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT) & 28958c2ecf20Sopenharmony_ci GEN9_MEM_LATENCY_LEVEL_MASK; 28968c2ecf20Sopenharmony_ci 28978c2ecf20Sopenharmony_ci /* 28988c2ecf20Sopenharmony_ci * If a level n (n > 1) has a 0us latency, all levels m (m >= n) 28998c2ecf20Sopenharmony_ci * need to be disabled. We make sure to sanitize the values out 29008c2ecf20Sopenharmony_ci * of the punit to satisfy this requirement. 29018c2ecf20Sopenharmony_ci */ 29028c2ecf20Sopenharmony_ci for (level = 1; level <= max_level; level++) { 29038c2ecf20Sopenharmony_ci if (wm[level] == 0) { 29048c2ecf20Sopenharmony_ci for (i = level + 1; i <= max_level; i++) 29058c2ecf20Sopenharmony_ci wm[i] = 0; 29068c2ecf20Sopenharmony_ci break; 29078c2ecf20Sopenharmony_ci } 29088c2ecf20Sopenharmony_ci } 29098c2ecf20Sopenharmony_ci 29108c2ecf20Sopenharmony_ci /* 29118c2ecf20Sopenharmony_ci * WaWmMemoryReadLatency:skl+,glk 29128c2ecf20Sopenharmony_ci * 29138c2ecf20Sopenharmony_ci * punit doesn't take into account the read latency so we need 29148c2ecf20Sopenharmony_ci * to add 2us to the various latency levels we retrieve from the 29158c2ecf20Sopenharmony_ci * punit when level 0 response data us 0us. 29168c2ecf20Sopenharmony_ci */ 29178c2ecf20Sopenharmony_ci if (wm[0] == 0) { 29188c2ecf20Sopenharmony_ci wm[0] += 2; 29198c2ecf20Sopenharmony_ci for (level = 1; level <= max_level; level++) { 29208c2ecf20Sopenharmony_ci if (wm[level] == 0) 29218c2ecf20Sopenharmony_ci break; 29228c2ecf20Sopenharmony_ci wm[level] += 2; 29238c2ecf20Sopenharmony_ci } 29248c2ecf20Sopenharmony_ci } 29258c2ecf20Sopenharmony_ci 29268c2ecf20Sopenharmony_ci /* 29278c2ecf20Sopenharmony_ci * WA Level-0 adjustment for 16GB DIMMs: SKL+ 29288c2ecf20Sopenharmony_ci * If we could not get dimm info enable this WA to prevent from 29298c2ecf20Sopenharmony_ci * any underrun. If not able to get Dimm info assume 16GB dimm 29308c2ecf20Sopenharmony_ci * to avoid any underrun. 29318c2ecf20Sopenharmony_ci */ 29328c2ecf20Sopenharmony_ci if (dev_priv->dram_info.is_16gb_dimm) 29338c2ecf20Sopenharmony_ci wm[0] += 1; 29348c2ecf20Sopenharmony_ci 29358c2ecf20Sopenharmony_ci } else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { 29368c2ecf20Sopenharmony_ci u64 sskpd = intel_uncore_read64(uncore, MCH_SSKPD); 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_ci wm[0] = (sskpd >> 56) & 0xFF; 29398c2ecf20Sopenharmony_ci if (wm[0] == 0) 29408c2ecf20Sopenharmony_ci wm[0] = sskpd & 0xF; 29418c2ecf20Sopenharmony_ci wm[1] = (sskpd >> 4) & 0xFF; 29428c2ecf20Sopenharmony_ci wm[2] = (sskpd >> 12) & 0xFF; 29438c2ecf20Sopenharmony_ci wm[3] = (sskpd >> 20) & 0x1FF; 29448c2ecf20Sopenharmony_ci wm[4] = (sskpd >> 32) & 0x1FF; 29458c2ecf20Sopenharmony_ci } else if (INTEL_GEN(dev_priv) >= 6) { 29468c2ecf20Sopenharmony_ci u32 sskpd = intel_uncore_read(uncore, MCH_SSKPD); 29478c2ecf20Sopenharmony_ci 29488c2ecf20Sopenharmony_ci wm[0] = (sskpd >> SSKPD_WM0_SHIFT) & SSKPD_WM_MASK; 29498c2ecf20Sopenharmony_ci wm[1] = (sskpd >> SSKPD_WM1_SHIFT) & SSKPD_WM_MASK; 29508c2ecf20Sopenharmony_ci wm[2] = (sskpd >> SSKPD_WM2_SHIFT) & SSKPD_WM_MASK; 29518c2ecf20Sopenharmony_ci wm[3] = (sskpd >> SSKPD_WM3_SHIFT) & SSKPD_WM_MASK; 29528c2ecf20Sopenharmony_ci } else if (INTEL_GEN(dev_priv) >= 5) { 29538c2ecf20Sopenharmony_ci u32 mltr = intel_uncore_read(uncore, MLTR_ILK); 29548c2ecf20Sopenharmony_ci 29558c2ecf20Sopenharmony_ci /* ILK primary LP0 latency is 700 ns */ 29568c2ecf20Sopenharmony_ci wm[0] = 7; 29578c2ecf20Sopenharmony_ci wm[1] = (mltr >> MLTR_WM1_SHIFT) & ILK_SRLT_MASK; 29588c2ecf20Sopenharmony_ci wm[2] = (mltr >> MLTR_WM2_SHIFT) & ILK_SRLT_MASK; 29598c2ecf20Sopenharmony_ci } else { 29608c2ecf20Sopenharmony_ci MISSING_CASE(INTEL_DEVID(dev_priv)); 29618c2ecf20Sopenharmony_ci } 29628c2ecf20Sopenharmony_ci} 29638c2ecf20Sopenharmony_ci 29648c2ecf20Sopenharmony_cistatic void intel_fixup_spr_wm_latency(struct drm_i915_private *dev_priv, 29658c2ecf20Sopenharmony_ci u16 wm[5]) 29668c2ecf20Sopenharmony_ci{ 29678c2ecf20Sopenharmony_ci /* ILK sprite LP0 latency is 1300 ns */ 29688c2ecf20Sopenharmony_ci if (IS_GEN(dev_priv, 5)) 29698c2ecf20Sopenharmony_ci wm[0] = 13; 29708c2ecf20Sopenharmony_ci} 29718c2ecf20Sopenharmony_ci 29728c2ecf20Sopenharmony_cistatic void intel_fixup_cur_wm_latency(struct drm_i915_private *dev_priv, 29738c2ecf20Sopenharmony_ci u16 wm[5]) 29748c2ecf20Sopenharmony_ci{ 29758c2ecf20Sopenharmony_ci /* ILK cursor LP0 latency is 1300 ns */ 29768c2ecf20Sopenharmony_ci if (IS_GEN(dev_priv, 5)) 29778c2ecf20Sopenharmony_ci wm[0] = 13; 29788c2ecf20Sopenharmony_ci} 29798c2ecf20Sopenharmony_ci 29808c2ecf20Sopenharmony_ciint ilk_wm_max_level(const struct drm_i915_private *dev_priv) 29818c2ecf20Sopenharmony_ci{ 29828c2ecf20Sopenharmony_ci /* how many WM levels are we expecting */ 29838c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 9) 29848c2ecf20Sopenharmony_ci return 7; 29858c2ecf20Sopenharmony_ci else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) 29868c2ecf20Sopenharmony_ci return 4; 29878c2ecf20Sopenharmony_ci else if (INTEL_GEN(dev_priv) >= 6) 29888c2ecf20Sopenharmony_ci return 3; 29898c2ecf20Sopenharmony_ci else 29908c2ecf20Sopenharmony_ci return 2; 29918c2ecf20Sopenharmony_ci} 29928c2ecf20Sopenharmony_ci 29938c2ecf20Sopenharmony_cistatic void intel_print_wm_latency(struct drm_i915_private *dev_priv, 29948c2ecf20Sopenharmony_ci const char *name, 29958c2ecf20Sopenharmony_ci const u16 wm[]) 29968c2ecf20Sopenharmony_ci{ 29978c2ecf20Sopenharmony_ci int level, max_level = ilk_wm_max_level(dev_priv); 29988c2ecf20Sopenharmony_ci 29998c2ecf20Sopenharmony_ci for (level = 0; level <= max_level; level++) { 30008c2ecf20Sopenharmony_ci unsigned int latency = wm[level]; 30018c2ecf20Sopenharmony_ci 30028c2ecf20Sopenharmony_ci if (latency == 0) { 30038c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 30048c2ecf20Sopenharmony_ci "%s WM%d latency not provided\n", 30058c2ecf20Sopenharmony_ci name, level); 30068c2ecf20Sopenharmony_ci continue; 30078c2ecf20Sopenharmony_ci } 30088c2ecf20Sopenharmony_ci 30098c2ecf20Sopenharmony_ci /* 30108c2ecf20Sopenharmony_ci * - latencies are in us on gen9. 30118c2ecf20Sopenharmony_ci * - before then, WM1+ latency values are in 0.5us units 30128c2ecf20Sopenharmony_ci */ 30138c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 9) 30148c2ecf20Sopenharmony_ci latency *= 10; 30158c2ecf20Sopenharmony_ci else if (level > 0) 30168c2ecf20Sopenharmony_ci latency *= 5; 30178c2ecf20Sopenharmony_ci 30188c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 30198c2ecf20Sopenharmony_ci "%s WM%d latency %u (%u.%u usec)\n", name, level, 30208c2ecf20Sopenharmony_ci wm[level], latency / 10, latency % 10); 30218c2ecf20Sopenharmony_ci } 30228c2ecf20Sopenharmony_ci} 30238c2ecf20Sopenharmony_ci 30248c2ecf20Sopenharmony_cistatic bool ilk_increase_wm_latency(struct drm_i915_private *dev_priv, 30258c2ecf20Sopenharmony_ci u16 wm[5], u16 min) 30268c2ecf20Sopenharmony_ci{ 30278c2ecf20Sopenharmony_ci int level, max_level = ilk_wm_max_level(dev_priv); 30288c2ecf20Sopenharmony_ci 30298c2ecf20Sopenharmony_ci if (wm[0] >= min) 30308c2ecf20Sopenharmony_ci return false; 30318c2ecf20Sopenharmony_ci 30328c2ecf20Sopenharmony_ci wm[0] = max(wm[0], min); 30338c2ecf20Sopenharmony_ci for (level = 1; level <= max_level; level++) 30348c2ecf20Sopenharmony_ci wm[level] = max_t(u16, wm[level], DIV_ROUND_UP(min, 5)); 30358c2ecf20Sopenharmony_ci 30368c2ecf20Sopenharmony_ci return true; 30378c2ecf20Sopenharmony_ci} 30388c2ecf20Sopenharmony_ci 30398c2ecf20Sopenharmony_cistatic void snb_wm_latency_quirk(struct drm_i915_private *dev_priv) 30408c2ecf20Sopenharmony_ci{ 30418c2ecf20Sopenharmony_ci bool changed; 30428c2ecf20Sopenharmony_ci 30438c2ecf20Sopenharmony_ci /* 30448c2ecf20Sopenharmony_ci * The BIOS provided WM memory latency values are often 30458c2ecf20Sopenharmony_ci * inadequate for high resolution displays. Adjust them. 30468c2ecf20Sopenharmony_ci */ 30478c2ecf20Sopenharmony_ci changed = ilk_increase_wm_latency(dev_priv, dev_priv->wm.pri_latency, 12); 30488c2ecf20Sopenharmony_ci changed |= ilk_increase_wm_latency(dev_priv, dev_priv->wm.spr_latency, 12); 30498c2ecf20Sopenharmony_ci changed |= ilk_increase_wm_latency(dev_priv, dev_priv->wm.cur_latency, 12); 30508c2ecf20Sopenharmony_ci 30518c2ecf20Sopenharmony_ci if (!changed) 30528c2ecf20Sopenharmony_ci return; 30538c2ecf20Sopenharmony_ci 30548c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 30558c2ecf20Sopenharmony_ci "WM latency values increased to avoid potential underruns\n"); 30568c2ecf20Sopenharmony_ci intel_print_wm_latency(dev_priv, "Primary", dev_priv->wm.pri_latency); 30578c2ecf20Sopenharmony_ci intel_print_wm_latency(dev_priv, "Sprite", dev_priv->wm.spr_latency); 30588c2ecf20Sopenharmony_ci intel_print_wm_latency(dev_priv, "Cursor", dev_priv->wm.cur_latency); 30598c2ecf20Sopenharmony_ci} 30608c2ecf20Sopenharmony_ci 30618c2ecf20Sopenharmony_cistatic void snb_wm_lp3_irq_quirk(struct drm_i915_private *dev_priv) 30628c2ecf20Sopenharmony_ci{ 30638c2ecf20Sopenharmony_ci /* 30648c2ecf20Sopenharmony_ci * On some SNB machines (Thinkpad X220 Tablet at least) 30658c2ecf20Sopenharmony_ci * LP3 usage can cause vblank interrupts to be lost. 30668c2ecf20Sopenharmony_ci * The DEIIR bit will go high but it looks like the CPU 30678c2ecf20Sopenharmony_ci * never gets interrupted. 30688c2ecf20Sopenharmony_ci * 30698c2ecf20Sopenharmony_ci * It's not clear whether other interrupt source could 30708c2ecf20Sopenharmony_ci * be affected or if this is somehow limited to vblank 30718c2ecf20Sopenharmony_ci * interrupts only. To play it safe we disable LP3 30728c2ecf20Sopenharmony_ci * watermarks entirely. 30738c2ecf20Sopenharmony_ci */ 30748c2ecf20Sopenharmony_ci if (dev_priv->wm.pri_latency[3] == 0 && 30758c2ecf20Sopenharmony_ci dev_priv->wm.spr_latency[3] == 0 && 30768c2ecf20Sopenharmony_ci dev_priv->wm.cur_latency[3] == 0) 30778c2ecf20Sopenharmony_ci return; 30788c2ecf20Sopenharmony_ci 30798c2ecf20Sopenharmony_ci dev_priv->wm.pri_latency[3] = 0; 30808c2ecf20Sopenharmony_ci dev_priv->wm.spr_latency[3] = 0; 30818c2ecf20Sopenharmony_ci dev_priv->wm.cur_latency[3] = 0; 30828c2ecf20Sopenharmony_ci 30838c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 30848c2ecf20Sopenharmony_ci "LP3 watermarks disabled due to potential for lost interrupts\n"); 30858c2ecf20Sopenharmony_ci intel_print_wm_latency(dev_priv, "Primary", dev_priv->wm.pri_latency); 30868c2ecf20Sopenharmony_ci intel_print_wm_latency(dev_priv, "Sprite", dev_priv->wm.spr_latency); 30878c2ecf20Sopenharmony_ci intel_print_wm_latency(dev_priv, "Cursor", dev_priv->wm.cur_latency); 30888c2ecf20Sopenharmony_ci} 30898c2ecf20Sopenharmony_ci 30908c2ecf20Sopenharmony_cistatic void ilk_setup_wm_latency(struct drm_i915_private *dev_priv) 30918c2ecf20Sopenharmony_ci{ 30928c2ecf20Sopenharmony_ci intel_read_wm_latency(dev_priv, dev_priv->wm.pri_latency); 30938c2ecf20Sopenharmony_ci 30948c2ecf20Sopenharmony_ci memcpy(dev_priv->wm.spr_latency, dev_priv->wm.pri_latency, 30958c2ecf20Sopenharmony_ci sizeof(dev_priv->wm.pri_latency)); 30968c2ecf20Sopenharmony_ci memcpy(dev_priv->wm.cur_latency, dev_priv->wm.pri_latency, 30978c2ecf20Sopenharmony_ci sizeof(dev_priv->wm.pri_latency)); 30988c2ecf20Sopenharmony_ci 30998c2ecf20Sopenharmony_ci intel_fixup_spr_wm_latency(dev_priv, dev_priv->wm.spr_latency); 31008c2ecf20Sopenharmony_ci intel_fixup_cur_wm_latency(dev_priv, dev_priv->wm.cur_latency); 31018c2ecf20Sopenharmony_ci 31028c2ecf20Sopenharmony_ci intel_print_wm_latency(dev_priv, "Primary", dev_priv->wm.pri_latency); 31038c2ecf20Sopenharmony_ci intel_print_wm_latency(dev_priv, "Sprite", dev_priv->wm.spr_latency); 31048c2ecf20Sopenharmony_ci intel_print_wm_latency(dev_priv, "Cursor", dev_priv->wm.cur_latency); 31058c2ecf20Sopenharmony_ci 31068c2ecf20Sopenharmony_ci if (IS_GEN(dev_priv, 6)) { 31078c2ecf20Sopenharmony_ci snb_wm_latency_quirk(dev_priv); 31088c2ecf20Sopenharmony_ci snb_wm_lp3_irq_quirk(dev_priv); 31098c2ecf20Sopenharmony_ci } 31108c2ecf20Sopenharmony_ci} 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_cistatic void skl_setup_wm_latency(struct drm_i915_private *dev_priv) 31138c2ecf20Sopenharmony_ci{ 31148c2ecf20Sopenharmony_ci intel_read_wm_latency(dev_priv, dev_priv->wm.skl_latency); 31158c2ecf20Sopenharmony_ci intel_print_wm_latency(dev_priv, "Gen9 Plane", dev_priv->wm.skl_latency); 31168c2ecf20Sopenharmony_ci} 31178c2ecf20Sopenharmony_ci 31188c2ecf20Sopenharmony_cistatic bool ilk_validate_pipe_wm(const struct drm_i915_private *dev_priv, 31198c2ecf20Sopenharmony_ci struct intel_pipe_wm *pipe_wm) 31208c2ecf20Sopenharmony_ci{ 31218c2ecf20Sopenharmony_ci /* LP0 watermark maximums depend on this pipe alone */ 31228c2ecf20Sopenharmony_ci const struct intel_wm_config config = { 31238c2ecf20Sopenharmony_ci .num_pipes_active = 1, 31248c2ecf20Sopenharmony_ci .sprites_enabled = pipe_wm->sprites_enabled, 31258c2ecf20Sopenharmony_ci .sprites_scaled = pipe_wm->sprites_scaled, 31268c2ecf20Sopenharmony_ci }; 31278c2ecf20Sopenharmony_ci struct ilk_wm_maximums max; 31288c2ecf20Sopenharmony_ci 31298c2ecf20Sopenharmony_ci /* LP0 watermarks always use 1/2 DDB partitioning */ 31308c2ecf20Sopenharmony_ci ilk_compute_wm_maximums(dev_priv, 0, &config, INTEL_DDB_PART_1_2, &max); 31318c2ecf20Sopenharmony_ci 31328c2ecf20Sopenharmony_ci /* At least LP0 must be valid */ 31338c2ecf20Sopenharmony_ci if (!ilk_validate_wm_level(0, &max, &pipe_wm->wm[0])) { 31348c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, "LP0 watermark invalid\n"); 31358c2ecf20Sopenharmony_ci return false; 31368c2ecf20Sopenharmony_ci } 31378c2ecf20Sopenharmony_ci 31388c2ecf20Sopenharmony_ci return true; 31398c2ecf20Sopenharmony_ci} 31408c2ecf20Sopenharmony_ci 31418c2ecf20Sopenharmony_ci/* Compute new watermarks for the pipe */ 31428c2ecf20Sopenharmony_cistatic int ilk_compute_pipe_wm(struct intel_crtc_state *crtc_state) 31438c2ecf20Sopenharmony_ci{ 31448c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 31458c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); 31468c2ecf20Sopenharmony_ci struct intel_pipe_wm *pipe_wm; 31478c2ecf20Sopenharmony_ci struct intel_plane *plane; 31488c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state; 31498c2ecf20Sopenharmony_ci const struct intel_plane_state *pristate = NULL; 31508c2ecf20Sopenharmony_ci const struct intel_plane_state *sprstate = NULL; 31518c2ecf20Sopenharmony_ci const struct intel_plane_state *curstate = NULL; 31528c2ecf20Sopenharmony_ci int level, max_level = ilk_wm_max_level(dev_priv), usable_level; 31538c2ecf20Sopenharmony_ci struct ilk_wm_maximums max; 31548c2ecf20Sopenharmony_ci 31558c2ecf20Sopenharmony_ci pipe_wm = &crtc_state->wm.ilk.optimal; 31568c2ecf20Sopenharmony_ci 31578c2ecf20Sopenharmony_ci intel_atomic_crtc_state_for_each_plane_state(plane, plane_state, crtc_state) { 31588c2ecf20Sopenharmony_ci if (plane->base.type == DRM_PLANE_TYPE_PRIMARY) 31598c2ecf20Sopenharmony_ci pristate = plane_state; 31608c2ecf20Sopenharmony_ci else if (plane->base.type == DRM_PLANE_TYPE_OVERLAY) 31618c2ecf20Sopenharmony_ci sprstate = plane_state; 31628c2ecf20Sopenharmony_ci else if (plane->base.type == DRM_PLANE_TYPE_CURSOR) 31638c2ecf20Sopenharmony_ci curstate = plane_state; 31648c2ecf20Sopenharmony_ci } 31658c2ecf20Sopenharmony_ci 31668c2ecf20Sopenharmony_ci pipe_wm->pipe_enabled = crtc_state->hw.active; 31678c2ecf20Sopenharmony_ci if (sprstate) { 31688c2ecf20Sopenharmony_ci pipe_wm->sprites_enabled = sprstate->uapi.visible; 31698c2ecf20Sopenharmony_ci pipe_wm->sprites_scaled = sprstate->uapi.visible && 31708c2ecf20Sopenharmony_ci (drm_rect_width(&sprstate->uapi.dst) != drm_rect_width(&sprstate->uapi.src) >> 16 || 31718c2ecf20Sopenharmony_ci drm_rect_height(&sprstate->uapi.dst) != drm_rect_height(&sprstate->uapi.src) >> 16); 31728c2ecf20Sopenharmony_ci } 31738c2ecf20Sopenharmony_ci 31748c2ecf20Sopenharmony_ci usable_level = max_level; 31758c2ecf20Sopenharmony_ci 31768c2ecf20Sopenharmony_ci /* ILK/SNB: LP2+ watermarks only w/o sprites */ 31778c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) <= 6 && pipe_wm->sprites_enabled) 31788c2ecf20Sopenharmony_ci usable_level = 1; 31798c2ecf20Sopenharmony_ci 31808c2ecf20Sopenharmony_ci /* ILK/SNB/IVB: LP1+ watermarks only w/o scaling */ 31818c2ecf20Sopenharmony_ci if (pipe_wm->sprites_scaled) 31828c2ecf20Sopenharmony_ci usable_level = 0; 31838c2ecf20Sopenharmony_ci 31848c2ecf20Sopenharmony_ci memset(&pipe_wm->wm, 0, sizeof(pipe_wm->wm)); 31858c2ecf20Sopenharmony_ci ilk_compute_wm_level(dev_priv, crtc, 0, crtc_state, 31868c2ecf20Sopenharmony_ci pristate, sprstate, curstate, &pipe_wm->wm[0]); 31878c2ecf20Sopenharmony_ci 31888c2ecf20Sopenharmony_ci if (!ilk_validate_pipe_wm(dev_priv, pipe_wm)) 31898c2ecf20Sopenharmony_ci return -EINVAL; 31908c2ecf20Sopenharmony_ci 31918c2ecf20Sopenharmony_ci ilk_compute_wm_reg_maximums(dev_priv, 1, &max); 31928c2ecf20Sopenharmony_ci 31938c2ecf20Sopenharmony_ci for (level = 1; level <= usable_level; level++) { 31948c2ecf20Sopenharmony_ci struct intel_wm_level *wm = &pipe_wm->wm[level]; 31958c2ecf20Sopenharmony_ci 31968c2ecf20Sopenharmony_ci ilk_compute_wm_level(dev_priv, crtc, level, crtc_state, 31978c2ecf20Sopenharmony_ci pristate, sprstate, curstate, wm); 31988c2ecf20Sopenharmony_ci 31998c2ecf20Sopenharmony_ci /* 32008c2ecf20Sopenharmony_ci * Disable any watermark level that exceeds the 32018c2ecf20Sopenharmony_ci * register maximums since such watermarks are 32028c2ecf20Sopenharmony_ci * always invalid. 32038c2ecf20Sopenharmony_ci */ 32048c2ecf20Sopenharmony_ci if (!ilk_validate_wm_level(level, &max, wm)) { 32058c2ecf20Sopenharmony_ci memset(wm, 0, sizeof(*wm)); 32068c2ecf20Sopenharmony_ci break; 32078c2ecf20Sopenharmony_ci } 32088c2ecf20Sopenharmony_ci } 32098c2ecf20Sopenharmony_ci 32108c2ecf20Sopenharmony_ci return 0; 32118c2ecf20Sopenharmony_ci} 32128c2ecf20Sopenharmony_ci 32138c2ecf20Sopenharmony_ci/* 32148c2ecf20Sopenharmony_ci * Build a set of 'intermediate' watermark values that satisfy both the old 32158c2ecf20Sopenharmony_ci * state and the new state. These can be programmed to the hardware 32168c2ecf20Sopenharmony_ci * immediately. 32178c2ecf20Sopenharmony_ci */ 32188c2ecf20Sopenharmony_cistatic int ilk_compute_intermediate_wm(struct intel_crtc_state *newstate) 32198c2ecf20Sopenharmony_ci{ 32208c2ecf20Sopenharmony_ci struct intel_crtc *intel_crtc = to_intel_crtc(newstate->uapi.crtc); 32218c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev); 32228c2ecf20Sopenharmony_ci struct intel_pipe_wm *a = &newstate->wm.ilk.intermediate; 32238c2ecf20Sopenharmony_ci struct intel_atomic_state *intel_state = 32248c2ecf20Sopenharmony_ci to_intel_atomic_state(newstate->uapi.state); 32258c2ecf20Sopenharmony_ci const struct intel_crtc_state *oldstate = 32268c2ecf20Sopenharmony_ci intel_atomic_get_old_crtc_state(intel_state, intel_crtc); 32278c2ecf20Sopenharmony_ci const struct intel_pipe_wm *b = &oldstate->wm.ilk.optimal; 32288c2ecf20Sopenharmony_ci int level, max_level = ilk_wm_max_level(dev_priv); 32298c2ecf20Sopenharmony_ci 32308c2ecf20Sopenharmony_ci /* 32318c2ecf20Sopenharmony_ci * Start with the final, target watermarks, then combine with the 32328c2ecf20Sopenharmony_ci * currently active watermarks to get values that are safe both before 32338c2ecf20Sopenharmony_ci * and after the vblank. 32348c2ecf20Sopenharmony_ci */ 32358c2ecf20Sopenharmony_ci *a = newstate->wm.ilk.optimal; 32368c2ecf20Sopenharmony_ci if (!newstate->hw.active || drm_atomic_crtc_needs_modeset(&newstate->uapi) || 32378c2ecf20Sopenharmony_ci intel_state->skip_intermediate_wm) 32388c2ecf20Sopenharmony_ci return 0; 32398c2ecf20Sopenharmony_ci 32408c2ecf20Sopenharmony_ci a->pipe_enabled |= b->pipe_enabled; 32418c2ecf20Sopenharmony_ci a->sprites_enabled |= b->sprites_enabled; 32428c2ecf20Sopenharmony_ci a->sprites_scaled |= b->sprites_scaled; 32438c2ecf20Sopenharmony_ci 32448c2ecf20Sopenharmony_ci for (level = 0; level <= max_level; level++) { 32458c2ecf20Sopenharmony_ci struct intel_wm_level *a_wm = &a->wm[level]; 32468c2ecf20Sopenharmony_ci const struct intel_wm_level *b_wm = &b->wm[level]; 32478c2ecf20Sopenharmony_ci 32488c2ecf20Sopenharmony_ci a_wm->enable &= b_wm->enable; 32498c2ecf20Sopenharmony_ci a_wm->pri_val = max(a_wm->pri_val, b_wm->pri_val); 32508c2ecf20Sopenharmony_ci a_wm->spr_val = max(a_wm->spr_val, b_wm->spr_val); 32518c2ecf20Sopenharmony_ci a_wm->cur_val = max(a_wm->cur_val, b_wm->cur_val); 32528c2ecf20Sopenharmony_ci a_wm->fbc_val = max(a_wm->fbc_val, b_wm->fbc_val); 32538c2ecf20Sopenharmony_ci } 32548c2ecf20Sopenharmony_ci 32558c2ecf20Sopenharmony_ci /* 32568c2ecf20Sopenharmony_ci * We need to make sure that these merged watermark values are 32578c2ecf20Sopenharmony_ci * actually a valid configuration themselves. If they're not, 32588c2ecf20Sopenharmony_ci * there's no safe way to transition from the old state to 32598c2ecf20Sopenharmony_ci * the new state, so we need to fail the atomic transaction. 32608c2ecf20Sopenharmony_ci */ 32618c2ecf20Sopenharmony_ci if (!ilk_validate_pipe_wm(dev_priv, a)) 32628c2ecf20Sopenharmony_ci return -EINVAL; 32638c2ecf20Sopenharmony_ci 32648c2ecf20Sopenharmony_ci /* 32658c2ecf20Sopenharmony_ci * If our intermediate WM are identical to the final WM, then we can 32668c2ecf20Sopenharmony_ci * omit the post-vblank programming; only update if it's different. 32678c2ecf20Sopenharmony_ci */ 32688c2ecf20Sopenharmony_ci if (memcmp(a, &newstate->wm.ilk.optimal, sizeof(*a)) != 0) 32698c2ecf20Sopenharmony_ci newstate->wm.need_postvbl_update = true; 32708c2ecf20Sopenharmony_ci 32718c2ecf20Sopenharmony_ci return 0; 32728c2ecf20Sopenharmony_ci} 32738c2ecf20Sopenharmony_ci 32748c2ecf20Sopenharmony_ci/* 32758c2ecf20Sopenharmony_ci * Merge the watermarks from all active pipes for a specific level. 32768c2ecf20Sopenharmony_ci */ 32778c2ecf20Sopenharmony_cistatic void ilk_merge_wm_level(struct drm_i915_private *dev_priv, 32788c2ecf20Sopenharmony_ci int level, 32798c2ecf20Sopenharmony_ci struct intel_wm_level *ret_wm) 32808c2ecf20Sopenharmony_ci{ 32818c2ecf20Sopenharmony_ci const struct intel_crtc *intel_crtc; 32828c2ecf20Sopenharmony_ci 32838c2ecf20Sopenharmony_ci ret_wm->enable = true; 32848c2ecf20Sopenharmony_ci 32858c2ecf20Sopenharmony_ci for_each_intel_crtc(&dev_priv->drm, intel_crtc) { 32868c2ecf20Sopenharmony_ci const struct intel_pipe_wm *active = &intel_crtc->wm.active.ilk; 32878c2ecf20Sopenharmony_ci const struct intel_wm_level *wm = &active->wm[level]; 32888c2ecf20Sopenharmony_ci 32898c2ecf20Sopenharmony_ci if (!active->pipe_enabled) 32908c2ecf20Sopenharmony_ci continue; 32918c2ecf20Sopenharmony_ci 32928c2ecf20Sopenharmony_ci /* 32938c2ecf20Sopenharmony_ci * The watermark values may have been used in the past, 32948c2ecf20Sopenharmony_ci * so we must maintain them in the registers for some 32958c2ecf20Sopenharmony_ci * time even if the level is now disabled. 32968c2ecf20Sopenharmony_ci */ 32978c2ecf20Sopenharmony_ci if (!wm->enable) 32988c2ecf20Sopenharmony_ci ret_wm->enable = false; 32998c2ecf20Sopenharmony_ci 33008c2ecf20Sopenharmony_ci ret_wm->pri_val = max(ret_wm->pri_val, wm->pri_val); 33018c2ecf20Sopenharmony_ci ret_wm->spr_val = max(ret_wm->spr_val, wm->spr_val); 33028c2ecf20Sopenharmony_ci ret_wm->cur_val = max(ret_wm->cur_val, wm->cur_val); 33038c2ecf20Sopenharmony_ci ret_wm->fbc_val = max(ret_wm->fbc_val, wm->fbc_val); 33048c2ecf20Sopenharmony_ci } 33058c2ecf20Sopenharmony_ci} 33068c2ecf20Sopenharmony_ci 33078c2ecf20Sopenharmony_ci/* 33088c2ecf20Sopenharmony_ci * Merge all low power watermarks for all active pipes. 33098c2ecf20Sopenharmony_ci */ 33108c2ecf20Sopenharmony_cistatic void ilk_wm_merge(struct drm_i915_private *dev_priv, 33118c2ecf20Sopenharmony_ci const struct intel_wm_config *config, 33128c2ecf20Sopenharmony_ci const struct ilk_wm_maximums *max, 33138c2ecf20Sopenharmony_ci struct intel_pipe_wm *merged) 33148c2ecf20Sopenharmony_ci{ 33158c2ecf20Sopenharmony_ci int level, max_level = ilk_wm_max_level(dev_priv); 33168c2ecf20Sopenharmony_ci int last_enabled_level = max_level; 33178c2ecf20Sopenharmony_ci 33188c2ecf20Sopenharmony_ci /* ILK/SNB/IVB: LP1+ watermarks only w/ single pipe */ 33198c2ecf20Sopenharmony_ci if ((INTEL_GEN(dev_priv) <= 6 || IS_IVYBRIDGE(dev_priv)) && 33208c2ecf20Sopenharmony_ci config->num_pipes_active > 1) 33218c2ecf20Sopenharmony_ci last_enabled_level = 0; 33228c2ecf20Sopenharmony_ci 33238c2ecf20Sopenharmony_ci /* ILK: FBC WM must be disabled always */ 33248c2ecf20Sopenharmony_ci merged->fbc_wm_enabled = INTEL_GEN(dev_priv) >= 6; 33258c2ecf20Sopenharmony_ci 33268c2ecf20Sopenharmony_ci /* merge each WM1+ level */ 33278c2ecf20Sopenharmony_ci for (level = 1; level <= max_level; level++) { 33288c2ecf20Sopenharmony_ci struct intel_wm_level *wm = &merged->wm[level]; 33298c2ecf20Sopenharmony_ci 33308c2ecf20Sopenharmony_ci ilk_merge_wm_level(dev_priv, level, wm); 33318c2ecf20Sopenharmony_ci 33328c2ecf20Sopenharmony_ci if (level > last_enabled_level) 33338c2ecf20Sopenharmony_ci wm->enable = false; 33348c2ecf20Sopenharmony_ci else if (!ilk_validate_wm_level(level, max, wm)) 33358c2ecf20Sopenharmony_ci /* make sure all following levels get disabled */ 33368c2ecf20Sopenharmony_ci last_enabled_level = level - 1; 33378c2ecf20Sopenharmony_ci 33388c2ecf20Sopenharmony_ci /* 33398c2ecf20Sopenharmony_ci * The spec says it is preferred to disable 33408c2ecf20Sopenharmony_ci * FBC WMs instead of disabling a WM level. 33418c2ecf20Sopenharmony_ci */ 33428c2ecf20Sopenharmony_ci if (wm->fbc_val > max->fbc) { 33438c2ecf20Sopenharmony_ci if (wm->enable) 33448c2ecf20Sopenharmony_ci merged->fbc_wm_enabled = false; 33458c2ecf20Sopenharmony_ci wm->fbc_val = 0; 33468c2ecf20Sopenharmony_ci } 33478c2ecf20Sopenharmony_ci } 33488c2ecf20Sopenharmony_ci 33498c2ecf20Sopenharmony_ci /* ILK: LP2+ must be disabled when FBC WM is disabled but FBC enabled */ 33508c2ecf20Sopenharmony_ci /* 33518c2ecf20Sopenharmony_ci * FIXME this is racy. FBC might get enabled later. 33528c2ecf20Sopenharmony_ci * What we should check here is whether FBC can be 33538c2ecf20Sopenharmony_ci * enabled sometime later. 33548c2ecf20Sopenharmony_ci */ 33558c2ecf20Sopenharmony_ci if (IS_GEN(dev_priv, 5) && !merged->fbc_wm_enabled && 33568c2ecf20Sopenharmony_ci intel_fbc_is_active(dev_priv)) { 33578c2ecf20Sopenharmony_ci for (level = 2; level <= max_level; level++) { 33588c2ecf20Sopenharmony_ci struct intel_wm_level *wm = &merged->wm[level]; 33598c2ecf20Sopenharmony_ci 33608c2ecf20Sopenharmony_ci wm->enable = false; 33618c2ecf20Sopenharmony_ci } 33628c2ecf20Sopenharmony_ci } 33638c2ecf20Sopenharmony_ci} 33648c2ecf20Sopenharmony_ci 33658c2ecf20Sopenharmony_cistatic int ilk_wm_lp_to_level(int wm_lp, const struct intel_pipe_wm *pipe_wm) 33668c2ecf20Sopenharmony_ci{ 33678c2ecf20Sopenharmony_ci /* LP1,LP2,LP3 levels are either 1,2,3 or 1,3,4 */ 33688c2ecf20Sopenharmony_ci return wm_lp + (wm_lp >= 2 && pipe_wm->wm[4].enable); 33698c2ecf20Sopenharmony_ci} 33708c2ecf20Sopenharmony_ci 33718c2ecf20Sopenharmony_ci/* The value we need to program into the WM_LPx latency field */ 33728c2ecf20Sopenharmony_cistatic unsigned int ilk_wm_lp_latency(struct drm_i915_private *dev_priv, 33738c2ecf20Sopenharmony_ci int level) 33748c2ecf20Sopenharmony_ci{ 33758c2ecf20Sopenharmony_ci if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) 33768c2ecf20Sopenharmony_ci return 2 * level; 33778c2ecf20Sopenharmony_ci else 33788c2ecf20Sopenharmony_ci return dev_priv->wm.pri_latency[level]; 33798c2ecf20Sopenharmony_ci} 33808c2ecf20Sopenharmony_ci 33818c2ecf20Sopenharmony_cistatic void ilk_compute_wm_results(struct drm_i915_private *dev_priv, 33828c2ecf20Sopenharmony_ci const struct intel_pipe_wm *merged, 33838c2ecf20Sopenharmony_ci enum intel_ddb_partitioning partitioning, 33848c2ecf20Sopenharmony_ci struct ilk_wm_values *results) 33858c2ecf20Sopenharmony_ci{ 33868c2ecf20Sopenharmony_ci struct intel_crtc *intel_crtc; 33878c2ecf20Sopenharmony_ci int level, wm_lp; 33888c2ecf20Sopenharmony_ci 33898c2ecf20Sopenharmony_ci results->enable_fbc_wm = merged->fbc_wm_enabled; 33908c2ecf20Sopenharmony_ci results->partitioning = partitioning; 33918c2ecf20Sopenharmony_ci 33928c2ecf20Sopenharmony_ci /* LP1+ register values */ 33938c2ecf20Sopenharmony_ci for (wm_lp = 1; wm_lp <= 3; wm_lp++) { 33948c2ecf20Sopenharmony_ci const struct intel_wm_level *r; 33958c2ecf20Sopenharmony_ci 33968c2ecf20Sopenharmony_ci level = ilk_wm_lp_to_level(wm_lp, merged); 33978c2ecf20Sopenharmony_ci 33988c2ecf20Sopenharmony_ci r = &merged->wm[level]; 33998c2ecf20Sopenharmony_ci 34008c2ecf20Sopenharmony_ci /* 34018c2ecf20Sopenharmony_ci * Maintain the watermark values even if the level is 34028c2ecf20Sopenharmony_ci * disabled. Doing otherwise could cause underruns. 34038c2ecf20Sopenharmony_ci */ 34048c2ecf20Sopenharmony_ci results->wm_lp[wm_lp - 1] = 34058c2ecf20Sopenharmony_ci (ilk_wm_lp_latency(dev_priv, level) << WM1_LP_LATENCY_SHIFT) | 34068c2ecf20Sopenharmony_ci (r->pri_val << WM1_LP_SR_SHIFT) | 34078c2ecf20Sopenharmony_ci r->cur_val; 34088c2ecf20Sopenharmony_ci 34098c2ecf20Sopenharmony_ci if (r->enable) 34108c2ecf20Sopenharmony_ci results->wm_lp[wm_lp - 1] |= WM1_LP_SR_EN; 34118c2ecf20Sopenharmony_ci 34128c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 8) 34138c2ecf20Sopenharmony_ci results->wm_lp[wm_lp - 1] |= 34148c2ecf20Sopenharmony_ci r->fbc_val << WM1_LP_FBC_SHIFT_BDW; 34158c2ecf20Sopenharmony_ci else 34168c2ecf20Sopenharmony_ci results->wm_lp[wm_lp - 1] |= 34178c2ecf20Sopenharmony_ci r->fbc_val << WM1_LP_FBC_SHIFT; 34188c2ecf20Sopenharmony_ci 34198c2ecf20Sopenharmony_ci /* 34208c2ecf20Sopenharmony_ci * Always set WM1S_LP_EN when spr_val != 0, even if the 34218c2ecf20Sopenharmony_ci * level is disabled. Doing otherwise could cause underruns. 34228c2ecf20Sopenharmony_ci */ 34238c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) <= 6 && r->spr_val) { 34248c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, wm_lp != 1); 34258c2ecf20Sopenharmony_ci results->wm_lp_spr[wm_lp - 1] = WM1S_LP_EN | r->spr_val; 34268c2ecf20Sopenharmony_ci } else 34278c2ecf20Sopenharmony_ci results->wm_lp_spr[wm_lp - 1] = r->spr_val; 34288c2ecf20Sopenharmony_ci } 34298c2ecf20Sopenharmony_ci 34308c2ecf20Sopenharmony_ci /* LP0 register values */ 34318c2ecf20Sopenharmony_ci for_each_intel_crtc(&dev_priv->drm, intel_crtc) { 34328c2ecf20Sopenharmony_ci enum pipe pipe = intel_crtc->pipe; 34338c2ecf20Sopenharmony_ci const struct intel_pipe_wm *pipe_wm = &intel_crtc->wm.active.ilk; 34348c2ecf20Sopenharmony_ci const struct intel_wm_level *r = &pipe_wm->wm[0]; 34358c2ecf20Sopenharmony_ci 34368c2ecf20Sopenharmony_ci if (drm_WARN_ON(&dev_priv->drm, !r->enable)) 34378c2ecf20Sopenharmony_ci continue; 34388c2ecf20Sopenharmony_ci 34398c2ecf20Sopenharmony_ci results->wm_pipe[pipe] = 34408c2ecf20Sopenharmony_ci (r->pri_val << WM0_PIPE_PLANE_SHIFT) | 34418c2ecf20Sopenharmony_ci (r->spr_val << WM0_PIPE_SPRITE_SHIFT) | 34428c2ecf20Sopenharmony_ci r->cur_val; 34438c2ecf20Sopenharmony_ci } 34448c2ecf20Sopenharmony_ci} 34458c2ecf20Sopenharmony_ci 34468c2ecf20Sopenharmony_ci/* Find the result with the highest level enabled. Check for enable_fbc_wm in 34478c2ecf20Sopenharmony_ci * case both are at the same level. Prefer r1 in case they're the same. */ 34488c2ecf20Sopenharmony_cistatic struct intel_pipe_wm * 34498c2ecf20Sopenharmony_ciilk_find_best_result(struct drm_i915_private *dev_priv, 34508c2ecf20Sopenharmony_ci struct intel_pipe_wm *r1, 34518c2ecf20Sopenharmony_ci struct intel_pipe_wm *r2) 34528c2ecf20Sopenharmony_ci{ 34538c2ecf20Sopenharmony_ci int level, max_level = ilk_wm_max_level(dev_priv); 34548c2ecf20Sopenharmony_ci int level1 = 0, level2 = 0; 34558c2ecf20Sopenharmony_ci 34568c2ecf20Sopenharmony_ci for (level = 1; level <= max_level; level++) { 34578c2ecf20Sopenharmony_ci if (r1->wm[level].enable) 34588c2ecf20Sopenharmony_ci level1 = level; 34598c2ecf20Sopenharmony_ci if (r2->wm[level].enable) 34608c2ecf20Sopenharmony_ci level2 = level; 34618c2ecf20Sopenharmony_ci } 34628c2ecf20Sopenharmony_ci 34638c2ecf20Sopenharmony_ci if (level1 == level2) { 34648c2ecf20Sopenharmony_ci if (r2->fbc_wm_enabled && !r1->fbc_wm_enabled) 34658c2ecf20Sopenharmony_ci return r2; 34668c2ecf20Sopenharmony_ci else 34678c2ecf20Sopenharmony_ci return r1; 34688c2ecf20Sopenharmony_ci } else if (level1 > level2) { 34698c2ecf20Sopenharmony_ci return r1; 34708c2ecf20Sopenharmony_ci } else { 34718c2ecf20Sopenharmony_ci return r2; 34728c2ecf20Sopenharmony_ci } 34738c2ecf20Sopenharmony_ci} 34748c2ecf20Sopenharmony_ci 34758c2ecf20Sopenharmony_ci/* dirty bits used to track which watermarks need changes */ 34768c2ecf20Sopenharmony_ci#define WM_DIRTY_PIPE(pipe) (1 << (pipe)) 34778c2ecf20Sopenharmony_ci#define WM_DIRTY_LP(wm_lp) (1 << (15 + (wm_lp))) 34788c2ecf20Sopenharmony_ci#define WM_DIRTY_LP_ALL (WM_DIRTY_LP(1) | WM_DIRTY_LP(2) | WM_DIRTY_LP(3)) 34798c2ecf20Sopenharmony_ci#define WM_DIRTY_FBC (1 << 24) 34808c2ecf20Sopenharmony_ci#define WM_DIRTY_DDB (1 << 25) 34818c2ecf20Sopenharmony_ci 34828c2ecf20Sopenharmony_cistatic unsigned int ilk_compute_wm_dirty(struct drm_i915_private *dev_priv, 34838c2ecf20Sopenharmony_ci const struct ilk_wm_values *old, 34848c2ecf20Sopenharmony_ci const struct ilk_wm_values *new) 34858c2ecf20Sopenharmony_ci{ 34868c2ecf20Sopenharmony_ci unsigned int dirty = 0; 34878c2ecf20Sopenharmony_ci enum pipe pipe; 34888c2ecf20Sopenharmony_ci int wm_lp; 34898c2ecf20Sopenharmony_ci 34908c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 34918c2ecf20Sopenharmony_ci if (old->wm_pipe[pipe] != new->wm_pipe[pipe]) { 34928c2ecf20Sopenharmony_ci dirty |= WM_DIRTY_PIPE(pipe); 34938c2ecf20Sopenharmony_ci /* Must disable LP1+ watermarks too */ 34948c2ecf20Sopenharmony_ci dirty |= WM_DIRTY_LP_ALL; 34958c2ecf20Sopenharmony_ci } 34968c2ecf20Sopenharmony_ci } 34978c2ecf20Sopenharmony_ci 34988c2ecf20Sopenharmony_ci if (old->enable_fbc_wm != new->enable_fbc_wm) { 34998c2ecf20Sopenharmony_ci dirty |= WM_DIRTY_FBC; 35008c2ecf20Sopenharmony_ci /* Must disable LP1+ watermarks too */ 35018c2ecf20Sopenharmony_ci dirty |= WM_DIRTY_LP_ALL; 35028c2ecf20Sopenharmony_ci } 35038c2ecf20Sopenharmony_ci 35048c2ecf20Sopenharmony_ci if (old->partitioning != new->partitioning) { 35058c2ecf20Sopenharmony_ci dirty |= WM_DIRTY_DDB; 35068c2ecf20Sopenharmony_ci /* Must disable LP1+ watermarks too */ 35078c2ecf20Sopenharmony_ci dirty |= WM_DIRTY_LP_ALL; 35088c2ecf20Sopenharmony_ci } 35098c2ecf20Sopenharmony_ci 35108c2ecf20Sopenharmony_ci /* LP1+ watermarks already deemed dirty, no need to continue */ 35118c2ecf20Sopenharmony_ci if (dirty & WM_DIRTY_LP_ALL) 35128c2ecf20Sopenharmony_ci return dirty; 35138c2ecf20Sopenharmony_ci 35148c2ecf20Sopenharmony_ci /* Find the lowest numbered LP1+ watermark in need of an update... */ 35158c2ecf20Sopenharmony_ci for (wm_lp = 1; wm_lp <= 3; wm_lp++) { 35168c2ecf20Sopenharmony_ci if (old->wm_lp[wm_lp - 1] != new->wm_lp[wm_lp - 1] || 35178c2ecf20Sopenharmony_ci old->wm_lp_spr[wm_lp - 1] != new->wm_lp_spr[wm_lp - 1]) 35188c2ecf20Sopenharmony_ci break; 35198c2ecf20Sopenharmony_ci } 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_ci /* ...and mark it and all higher numbered LP1+ watermarks as dirty */ 35228c2ecf20Sopenharmony_ci for (; wm_lp <= 3; wm_lp++) 35238c2ecf20Sopenharmony_ci dirty |= WM_DIRTY_LP(wm_lp); 35248c2ecf20Sopenharmony_ci 35258c2ecf20Sopenharmony_ci return dirty; 35268c2ecf20Sopenharmony_ci} 35278c2ecf20Sopenharmony_ci 35288c2ecf20Sopenharmony_cistatic bool _ilk_disable_lp_wm(struct drm_i915_private *dev_priv, 35298c2ecf20Sopenharmony_ci unsigned int dirty) 35308c2ecf20Sopenharmony_ci{ 35318c2ecf20Sopenharmony_ci struct ilk_wm_values *previous = &dev_priv->wm.hw; 35328c2ecf20Sopenharmony_ci bool changed = false; 35338c2ecf20Sopenharmony_ci 35348c2ecf20Sopenharmony_ci if (dirty & WM_DIRTY_LP(3) && previous->wm_lp[2] & WM1_LP_SR_EN) { 35358c2ecf20Sopenharmony_ci previous->wm_lp[2] &= ~WM1_LP_SR_EN; 35368c2ecf20Sopenharmony_ci I915_WRITE(WM3_LP_ILK, previous->wm_lp[2]); 35378c2ecf20Sopenharmony_ci changed = true; 35388c2ecf20Sopenharmony_ci } 35398c2ecf20Sopenharmony_ci if (dirty & WM_DIRTY_LP(2) && previous->wm_lp[1] & WM1_LP_SR_EN) { 35408c2ecf20Sopenharmony_ci previous->wm_lp[1] &= ~WM1_LP_SR_EN; 35418c2ecf20Sopenharmony_ci I915_WRITE(WM2_LP_ILK, previous->wm_lp[1]); 35428c2ecf20Sopenharmony_ci changed = true; 35438c2ecf20Sopenharmony_ci } 35448c2ecf20Sopenharmony_ci if (dirty & WM_DIRTY_LP(1) && previous->wm_lp[0] & WM1_LP_SR_EN) { 35458c2ecf20Sopenharmony_ci previous->wm_lp[0] &= ~WM1_LP_SR_EN; 35468c2ecf20Sopenharmony_ci I915_WRITE(WM1_LP_ILK, previous->wm_lp[0]); 35478c2ecf20Sopenharmony_ci changed = true; 35488c2ecf20Sopenharmony_ci } 35498c2ecf20Sopenharmony_ci 35508c2ecf20Sopenharmony_ci /* 35518c2ecf20Sopenharmony_ci * Don't touch WM1S_LP_EN here. 35528c2ecf20Sopenharmony_ci * Doing so could cause underruns. 35538c2ecf20Sopenharmony_ci */ 35548c2ecf20Sopenharmony_ci 35558c2ecf20Sopenharmony_ci return changed; 35568c2ecf20Sopenharmony_ci} 35578c2ecf20Sopenharmony_ci 35588c2ecf20Sopenharmony_ci/* 35598c2ecf20Sopenharmony_ci * The spec says we shouldn't write when we don't need, because every write 35608c2ecf20Sopenharmony_ci * causes WMs to be re-evaluated, expending some power. 35618c2ecf20Sopenharmony_ci */ 35628c2ecf20Sopenharmony_cistatic void ilk_write_wm_values(struct drm_i915_private *dev_priv, 35638c2ecf20Sopenharmony_ci struct ilk_wm_values *results) 35648c2ecf20Sopenharmony_ci{ 35658c2ecf20Sopenharmony_ci struct ilk_wm_values *previous = &dev_priv->wm.hw; 35668c2ecf20Sopenharmony_ci unsigned int dirty; 35678c2ecf20Sopenharmony_ci u32 val; 35688c2ecf20Sopenharmony_ci 35698c2ecf20Sopenharmony_ci dirty = ilk_compute_wm_dirty(dev_priv, previous, results); 35708c2ecf20Sopenharmony_ci if (!dirty) 35718c2ecf20Sopenharmony_ci return; 35728c2ecf20Sopenharmony_ci 35738c2ecf20Sopenharmony_ci _ilk_disable_lp_wm(dev_priv, dirty); 35748c2ecf20Sopenharmony_ci 35758c2ecf20Sopenharmony_ci if (dirty & WM_DIRTY_PIPE(PIPE_A)) 35768c2ecf20Sopenharmony_ci I915_WRITE(WM0_PIPEA_ILK, results->wm_pipe[0]); 35778c2ecf20Sopenharmony_ci if (dirty & WM_DIRTY_PIPE(PIPE_B)) 35788c2ecf20Sopenharmony_ci I915_WRITE(WM0_PIPEB_ILK, results->wm_pipe[1]); 35798c2ecf20Sopenharmony_ci if (dirty & WM_DIRTY_PIPE(PIPE_C)) 35808c2ecf20Sopenharmony_ci I915_WRITE(WM0_PIPEC_IVB, results->wm_pipe[2]); 35818c2ecf20Sopenharmony_ci 35828c2ecf20Sopenharmony_ci if (dirty & WM_DIRTY_DDB) { 35838c2ecf20Sopenharmony_ci if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { 35848c2ecf20Sopenharmony_ci val = I915_READ(WM_MISC); 35858c2ecf20Sopenharmony_ci if (results->partitioning == INTEL_DDB_PART_1_2) 35868c2ecf20Sopenharmony_ci val &= ~WM_MISC_DATA_PARTITION_5_6; 35878c2ecf20Sopenharmony_ci else 35888c2ecf20Sopenharmony_ci val |= WM_MISC_DATA_PARTITION_5_6; 35898c2ecf20Sopenharmony_ci I915_WRITE(WM_MISC, val); 35908c2ecf20Sopenharmony_ci } else { 35918c2ecf20Sopenharmony_ci val = I915_READ(DISP_ARB_CTL2); 35928c2ecf20Sopenharmony_ci if (results->partitioning == INTEL_DDB_PART_1_2) 35938c2ecf20Sopenharmony_ci val &= ~DISP_DATA_PARTITION_5_6; 35948c2ecf20Sopenharmony_ci else 35958c2ecf20Sopenharmony_ci val |= DISP_DATA_PARTITION_5_6; 35968c2ecf20Sopenharmony_ci I915_WRITE(DISP_ARB_CTL2, val); 35978c2ecf20Sopenharmony_ci } 35988c2ecf20Sopenharmony_ci } 35998c2ecf20Sopenharmony_ci 36008c2ecf20Sopenharmony_ci if (dirty & WM_DIRTY_FBC) { 36018c2ecf20Sopenharmony_ci val = I915_READ(DISP_ARB_CTL); 36028c2ecf20Sopenharmony_ci if (results->enable_fbc_wm) 36038c2ecf20Sopenharmony_ci val &= ~DISP_FBC_WM_DIS; 36048c2ecf20Sopenharmony_ci else 36058c2ecf20Sopenharmony_ci val |= DISP_FBC_WM_DIS; 36068c2ecf20Sopenharmony_ci I915_WRITE(DISP_ARB_CTL, val); 36078c2ecf20Sopenharmony_ci } 36088c2ecf20Sopenharmony_ci 36098c2ecf20Sopenharmony_ci if (dirty & WM_DIRTY_LP(1) && 36108c2ecf20Sopenharmony_ci previous->wm_lp_spr[0] != results->wm_lp_spr[0]) 36118c2ecf20Sopenharmony_ci I915_WRITE(WM1S_LP_ILK, results->wm_lp_spr[0]); 36128c2ecf20Sopenharmony_ci 36138c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 7) { 36148c2ecf20Sopenharmony_ci if (dirty & WM_DIRTY_LP(2) && previous->wm_lp_spr[1] != results->wm_lp_spr[1]) 36158c2ecf20Sopenharmony_ci I915_WRITE(WM2S_LP_IVB, results->wm_lp_spr[1]); 36168c2ecf20Sopenharmony_ci if (dirty & WM_DIRTY_LP(3) && previous->wm_lp_spr[2] != results->wm_lp_spr[2]) 36178c2ecf20Sopenharmony_ci I915_WRITE(WM3S_LP_IVB, results->wm_lp_spr[2]); 36188c2ecf20Sopenharmony_ci } 36198c2ecf20Sopenharmony_ci 36208c2ecf20Sopenharmony_ci if (dirty & WM_DIRTY_LP(1) && previous->wm_lp[0] != results->wm_lp[0]) 36218c2ecf20Sopenharmony_ci I915_WRITE(WM1_LP_ILK, results->wm_lp[0]); 36228c2ecf20Sopenharmony_ci if (dirty & WM_DIRTY_LP(2) && previous->wm_lp[1] != results->wm_lp[1]) 36238c2ecf20Sopenharmony_ci I915_WRITE(WM2_LP_ILK, results->wm_lp[1]); 36248c2ecf20Sopenharmony_ci if (dirty & WM_DIRTY_LP(3) && previous->wm_lp[2] != results->wm_lp[2]) 36258c2ecf20Sopenharmony_ci I915_WRITE(WM3_LP_ILK, results->wm_lp[2]); 36268c2ecf20Sopenharmony_ci 36278c2ecf20Sopenharmony_ci dev_priv->wm.hw = *results; 36288c2ecf20Sopenharmony_ci} 36298c2ecf20Sopenharmony_ci 36308c2ecf20Sopenharmony_cibool ilk_disable_lp_wm(struct drm_i915_private *dev_priv) 36318c2ecf20Sopenharmony_ci{ 36328c2ecf20Sopenharmony_ci return _ilk_disable_lp_wm(dev_priv, WM_DIRTY_LP_ALL); 36338c2ecf20Sopenharmony_ci} 36348c2ecf20Sopenharmony_ci 36358c2ecf20Sopenharmony_ciu8 intel_enabled_dbuf_slices_mask(struct drm_i915_private *dev_priv) 36368c2ecf20Sopenharmony_ci{ 36378c2ecf20Sopenharmony_ci int i; 36388c2ecf20Sopenharmony_ci int max_slices = INTEL_INFO(dev_priv)->num_supported_dbuf_slices; 36398c2ecf20Sopenharmony_ci u8 enabled_slices_mask = 0; 36408c2ecf20Sopenharmony_ci 36418c2ecf20Sopenharmony_ci for (i = 0; i < max_slices; i++) { 36428c2ecf20Sopenharmony_ci if (I915_READ(DBUF_CTL_S(i)) & DBUF_POWER_STATE) 36438c2ecf20Sopenharmony_ci enabled_slices_mask |= BIT(i); 36448c2ecf20Sopenharmony_ci } 36458c2ecf20Sopenharmony_ci 36468c2ecf20Sopenharmony_ci return enabled_slices_mask; 36478c2ecf20Sopenharmony_ci} 36488c2ecf20Sopenharmony_ci 36498c2ecf20Sopenharmony_ci/* 36508c2ecf20Sopenharmony_ci * FIXME: We still don't have the proper code detect if we need to apply the WA, 36518c2ecf20Sopenharmony_ci * so assume we'll always need it in order to avoid underruns. 36528c2ecf20Sopenharmony_ci */ 36538c2ecf20Sopenharmony_cistatic bool skl_needs_memory_bw_wa(struct drm_i915_private *dev_priv) 36548c2ecf20Sopenharmony_ci{ 36558c2ecf20Sopenharmony_ci return IS_GEN9_BC(dev_priv) || IS_BROXTON(dev_priv); 36568c2ecf20Sopenharmony_ci} 36578c2ecf20Sopenharmony_ci 36588c2ecf20Sopenharmony_cistatic bool 36598c2ecf20Sopenharmony_ciintel_has_sagv(struct drm_i915_private *dev_priv) 36608c2ecf20Sopenharmony_ci{ 36618c2ecf20Sopenharmony_ci return (IS_GEN9_BC(dev_priv) || INTEL_GEN(dev_priv) >= 10) && 36628c2ecf20Sopenharmony_ci dev_priv->sagv_status != I915_SAGV_NOT_CONTROLLED; 36638c2ecf20Sopenharmony_ci} 36648c2ecf20Sopenharmony_ci 36658c2ecf20Sopenharmony_cistatic void 36668c2ecf20Sopenharmony_ciskl_setup_sagv_block_time(struct drm_i915_private *dev_priv) 36678c2ecf20Sopenharmony_ci{ 36688c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 12) { 36698c2ecf20Sopenharmony_ci u32 val = 0; 36708c2ecf20Sopenharmony_ci int ret; 36718c2ecf20Sopenharmony_ci 36728c2ecf20Sopenharmony_ci ret = sandybridge_pcode_read(dev_priv, 36738c2ecf20Sopenharmony_ci GEN12_PCODE_READ_SAGV_BLOCK_TIME_US, 36748c2ecf20Sopenharmony_ci &val, NULL); 36758c2ecf20Sopenharmony_ci if (!ret) { 36768c2ecf20Sopenharmony_ci dev_priv->sagv_block_time_us = val; 36778c2ecf20Sopenharmony_ci return; 36788c2ecf20Sopenharmony_ci } 36798c2ecf20Sopenharmony_ci 36808c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, "Couldn't read SAGV block time!\n"); 36818c2ecf20Sopenharmony_ci } else if (IS_GEN(dev_priv, 11)) { 36828c2ecf20Sopenharmony_ci dev_priv->sagv_block_time_us = 10; 36838c2ecf20Sopenharmony_ci return; 36848c2ecf20Sopenharmony_ci } else if (IS_GEN(dev_priv, 10)) { 36858c2ecf20Sopenharmony_ci dev_priv->sagv_block_time_us = 20; 36868c2ecf20Sopenharmony_ci return; 36878c2ecf20Sopenharmony_ci } else if (IS_GEN(dev_priv, 9)) { 36888c2ecf20Sopenharmony_ci dev_priv->sagv_block_time_us = 30; 36898c2ecf20Sopenharmony_ci return; 36908c2ecf20Sopenharmony_ci } else { 36918c2ecf20Sopenharmony_ci MISSING_CASE(INTEL_GEN(dev_priv)); 36928c2ecf20Sopenharmony_ci } 36938c2ecf20Sopenharmony_ci 36948c2ecf20Sopenharmony_ci /* Default to an unusable block time */ 36958c2ecf20Sopenharmony_ci dev_priv->sagv_block_time_us = -1; 36968c2ecf20Sopenharmony_ci} 36978c2ecf20Sopenharmony_ci 36988c2ecf20Sopenharmony_ci/* 36998c2ecf20Sopenharmony_ci * SAGV dynamically adjusts the system agent voltage and clock frequencies 37008c2ecf20Sopenharmony_ci * depending on power and performance requirements. The display engine access 37018c2ecf20Sopenharmony_ci * to system memory is blocked during the adjustment time. Because of the 37028c2ecf20Sopenharmony_ci * blocking time, having this enabled can cause full system hangs and/or pipe 37038c2ecf20Sopenharmony_ci * underruns if we don't meet all of the following requirements: 37048c2ecf20Sopenharmony_ci * 37058c2ecf20Sopenharmony_ci * - <= 1 pipe enabled 37068c2ecf20Sopenharmony_ci * - All planes can enable watermarks for latencies >= SAGV engine block time 37078c2ecf20Sopenharmony_ci * - We're not using an interlaced display configuration 37088c2ecf20Sopenharmony_ci */ 37098c2ecf20Sopenharmony_ciint 37108c2ecf20Sopenharmony_ciintel_enable_sagv(struct drm_i915_private *dev_priv) 37118c2ecf20Sopenharmony_ci{ 37128c2ecf20Sopenharmony_ci int ret; 37138c2ecf20Sopenharmony_ci 37148c2ecf20Sopenharmony_ci if (!intel_has_sagv(dev_priv)) 37158c2ecf20Sopenharmony_ci return 0; 37168c2ecf20Sopenharmony_ci 37178c2ecf20Sopenharmony_ci if (dev_priv->sagv_status == I915_SAGV_ENABLED) 37188c2ecf20Sopenharmony_ci return 0; 37198c2ecf20Sopenharmony_ci 37208c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, "Enabling SAGV\n"); 37218c2ecf20Sopenharmony_ci ret = sandybridge_pcode_write(dev_priv, GEN9_PCODE_SAGV_CONTROL, 37228c2ecf20Sopenharmony_ci GEN9_SAGV_ENABLE); 37238c2ecf20Sopenharmony_ci 37248c2ecf20Sopenharmony_ci /* We don't need to wait for SAGV when enabling */ 37258c2ecf20Sopenharmony_ci 37268c2ecf20Sopenharmony_ci /* 37278c2ecf20Sopenharmony_ci * Some skl systems, pre-release machines in particular, 37288c2ecf20Sopenharmony_ci * don't actually have SAGV. 37298c2ecf20Sopenharmony_ci */ 37308c2ecf20Sopenharmony_ci if (IS_SKYLAKE(dev_priv) && ret == -ENXIO) { 37318c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, "No SAGV found on system, ignoring\n"); 37328c2ecf20Sopenharmony_ci dev_priv->sagv_status = I915_SAGV_NOT_CONTROLLED; 37338c2ecf20Sopenharmony_ci return 0; 37348c2ecf20Sopenharmony_ci } else if (ret < 0) { 37358c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, "Failed to enable SAGV\n"); 37368c2ecf20Sopenharmony_ci return ret; 37378c2ecf20Sopenharmony_ci } 37388c2ecf20Sopenharmony_ci 37398c2ecf20Sopenharmony_ci dev_priv->sagv_status = I915_SAGV_ENABLED; 37408c2ecf20Sopenharmony_ci return 0; 37418c2ecf20Sopenharmony_ci} 37428c2ecf20Sopenharmony_ci 37438c2ecf20Sopenharmony_ciint 37448c2ecf20Sopenharmony_ciintel_disable_sagv(struct drm_i915_private *dev_priv) 37458c2ecf20Sopenharmony_ci{ 37468c2ecf20Sopenharmony_ci int ret; 37478c2ecf20Sopenharmony_ci 37488c2ecf20Sopenharmony_ci if (!intel_has_sagv(dev_priv)) 37498c2ecf20Sopenharmony_ci return 0; 37508c2ecf20Sopenharmony_ci 37518c2ecf20Sopenharmony_ci if (dev_priv->sagv_status == I915_SAGV_DISABLED) 37528c2ecf20Sopenharmony_ci return 0; 37538c2ecf20Sopenharmony_ci 37548c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, "Disabling SAGV\n"); 37558c2ecf20Sopenharmony_ci /* bspec says to keep retrying for at least 1 ms */ 37568c2ecf20Sopenharmony_ci ret = skl_pcode_request(dev_priv, GEN9_PCODE_SAGV_CONTROL, 37578c2ecf20Sopenharmony_ci GEN9_SAGV_DISABLE, 37588c2ecf20Sopenharmony_ci GEN9_SAGV_IS_DISABLED, GEN9_SAGV_IS_DISABLED, 37598c2ecf20Sopenharmony_ci 1); 37608c2ecf20Sopenharmony_ci /* 37618c2ecf20Sopenharmony_ci * Some skl systems, pre-release machines in particular, 37628c2ecf20Sopenharmony_ci * don't actually have SAGV. 37638c2ecf20Sopenharmony_ci */ 37648c2ecf20Sopenharmony_ci if (IS_SKYLAKE(dev_priv) && ret == -ENXIO) { 37658c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, "No SAGV found on system, ignoring\n"); 37668c2ecf20Sopenharmony_ci dev_priv->sagv_status = I915_SAGV_NOT_CONTROLLED; 37678c2ecf20Sopenharmony_ci return 0; 37688c2ecf20Sopenharmony_ci } else if (ret < 0) { 37698c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, "Failed to disable SAGV (%d)\n", ret); 37708c2ecf20Sopenharmony_ci return ret; 37718c2ecf20Sopenharmony_ci } 37728c2ecf20Sopenharmony_ci 37738c2ecf20Sopenharmony_ci dev_priv->sagv_status = I915_SAGV_DISABLED; 37748c2ecf20Sopenharmony_ci return 0; 37758c2ecf20Sopenharmony_ci} 37768c2ecf20Sopenharmony_ci 37778c2ecf20Sopenharmony_civoid intel_sagv_pre_plane_update(struct intel_atomic_state *state) 37788c2ecf20Sopenharmony_ci{ 37798c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(state->base.dev); 37808c2ecf20Sopenharmony_ci const struct intel_bw_state *new_bw_state; 37818c2ecf20Sopenharmony_ci const struct intel_bw_state *old_bw_state; 37828c2ecf20Sopenharmony_ci u32 new_mask = 0; 37838c2ecf20Sopenharmony_ci 37848c2ecf20Sopenharmony_ci /* 37858c2ecf20Sopenharmony_ci * Just return if we can't control SAGV or don't have it. 37868c2ecf20Sopenharmony_ci * This is different from situation when we have SAGV but just can't 37878c2ecf20Sopenharmony_ci * afford it due to DBuf limitation - in case if SAGV is completely 37888c2ecf20Sopenharmony_ci * disabled in a BIOS, we are not even allowed to send a PCode request, 37898c2ecf20Sopenharmony_ci * as it will throw an error. So have to check it here. 37908c2ecf20Sopenharmony_ci */ 37918c2ecf20Sopenharmony_ci if (!intel_has_sagv(dev_priv)) 37928c2ecf20Sopenharmony_ci return; 37938c2ecf20Sopenharmony_ci 37948c2ecf20Sopenharmony_ci new_bw_state = intel_atomic_get_new_bw_state(state); 37958c2ecf20Sopenharmony_ci if (!new_bw_state) 37968c2ecf20Sopenharmony_ci return; 37978c2ecf20Sopenharmony_ci 37988c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) < 11 && !intel_can_enable_sagv(dev_priv, new_bw_state)) { 37998c2ecf20Sopenharmony_ci intel_disable_sagv(dev_priv); 38008c2ecf20Sopenharmony_ci return; 38018c2ecf20Sopenharmony_ci } 38028c2ecf20Sopenharmony_ci 38038c2ecf20Sopenharmony_ci old_bw_state = intel_atomic_get_old_bw_state(state); 38048c2ecf20Sopenharmony_ci /* 38058c2ecf20Sopenharmony_ci * Nothing to mask 38068c2ecf20Sopenharmony_ci */ 38078c2ecf20Sopenharmony_ci if (new_bw_state->qgv_points_mask == old_bw_state->qgv_points_mask) 38088c2ecf20Sopenharmony_ci return; 38098c2ecf20Sopenharmony_ci 38108c2ecf20Sopenharmony_ci new_mask = old_bw_state->qgv_points_mask | new_bw_state->qgv_points_mask; 38118c2ecf20Sopenharmony_ci 38128c2ecf20Sopenharmony_ci /* 38138c2ecf20Sopenharmony_ci * If new mask is zero - means there is nothing to mask, 38148c2ecf20Sopenharmony_ci * we can only unmask, which should be done in unmask. 38158c2ecf20Sopenharmony_ci */ 38168c2ecf20Sopenharmony_ci if (!new_mask) 38178c2ecf20Sopenharmony_ci return; 38188c2ecf20Sopenharmony_ci 38198c2ecf20Sopenharmony_ci /* 38208c2ecf20Sopenharmony_ci * Restrict required qgv points before updating the configuration. 38218c2ecf20Sopenharmony_ci * According to BSpec we can't mask and unmask qgv points at the same 38228c2ecf20Sopenharmony_ci * time. Also masking should be done before updating the configuration 38238c2ecf20Sopenharmony_ci * and unmasking afterwards. 38248c2ecf20Sopenharmony_ci */ 38258c2ecf20Sopenharmony_ci icl_pcode_restrict_qgv_points(dev_priv, new_mask); 38268c2ecf20Sopenharmony_ci} 38278c2ecf20Sopenharmony_ci 38288c2ecf20Sopenharmony_civoid intel_sagv_post_plane_update(struct intel_atomic_state *state) 38298c2ecf20Sopenharmony_ci{ 38308c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(state->base.dev); 38318c2ecf20Sopenharmony_ci const struct intel_bw_state *new_bw_state; 38328c2ecf20Sopenharmony_ci const struct intel_bw_state *old_bw_state; 38338c2ecf20Sopenharmony_ci u32 new_mask = 0; 38348c2ecf20Sopenharmony_ci 38358c2ecf20Sopenharmony_ci /* 38368c2ecf20Sopenharmony_ci * Just return if we can't control SAGV or don't have it. 38378c2ecf20Sopenharmony_ci * This is different from situation when we have SAGV but just can't 38388c2ecf20Sopenharmony_ci * afford it due to DBuf limitation - in case if SAGV is completely 38398c2ecf20Sopenharmony_ci * disabled in a BIOS, we are not even allowed to send a PCode request, 38408c2ecf20Sopenharmony_ci * as it will throw an error. So have to check it here. 38418c2ecf20Sopenharmony_ci */ 38428c2ecf20Sopenharmony_ci if (!intel_has_sagv(dev_priv)) 38438c2ecf20Sopenharmony_ci return; 38448c2ecf20Sopenharmony_ci 38458c2ecf20Sopenharmony_ci new_bw_state = intel_atomic_get_new_bw_state(state); 38468c2ecf20Sopenharmony_ci if (!new_bw_state) 38478c2ecf20Sopenharmony_ci return; 38488c2ecf20Sopenharmony_ci 38498c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) < 11 && intel_can_enable_sagv(dev_priv, new_bw_state)) { 38508c2ecf20Sopenharmony_ci intel_enable_sagv(dev_priv); 38518c2ecf20Sopenharmony_ci return; 38528c2ecf20Sopenharmony_ci } 38538c2ecf20Sopenharmony_ci 38548c2ecf20Sopenharmony_ci old_bw_state = intel_atomic_get_old_bw_state(state); 38558c2ecf20Sopenharmony_ci /* 38568c2ecf20Sopenharmony_ci * Nothing to unmask 38578c2ecf20Sopenharmony_ci */ 38588c2ecf20Sopenharmony_ci if (new_bw_state->qgv_points_mask == old_bw_state->qgv_points_mask) 38598c2ecf20Sopenharmony_ci return; 38608c2ecf20Sopenharmony_ci 38618c2ecf20Sopenharmony_ci new_mask = new_bw_state->qgv_points_mask; 38628c2ecf20Sopenharmony_ci 38638c2ecf20Sopenharmony_ci /* 38648c2ecf20Sopenharmony_ci * Allow required qgv points after updating the configuration. 38658c2ecf20Sopenharmony_ci * According to BSpec we can't mask and unmask qgv points at the same 38668c2ecf20Sopenharmony_ci * time. Also masking should be done before updating the configuration 38678c2ecf20Sopenharmony_ci * and unmasking afterwards. 38688c2ecf20Sopenharmony_ci */ 38698c2ecf20Sopenharmony_ci icl_pcode_restrict_qgv_points(dev_priv, new_mask); 38708c2ecf20Sopenharmony_ci} 38718c2ecf20Sopenharmony_ci 38728c2ecf20Sopenharmony_cistatic bool skl_crtc_can_enable_sagv(const struct intel_crtc_state *crtc_state) 38738c2ecf20Sopenharmony_ci{ 38748c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); 38758c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 38768c2ecf20Sopenharmony_ci struct intel_plane *plane; 38778c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state; 38788c2ecf20Sopenharmony_ci int level, latency; 38798c2ecf20Sopenharmony_ci 38808c2ecf20Sopenharmony_ci if (!intel_has_sagv(dev_priv)) 38818c2ecf20Sopenharmony_ci return false; 38828c2ecf20Sopenharmony_ci 38838c2ecf20Sopenharmony_ci if (!crtc_state->hw.active) 38848c2ecf20Sopenharmony_ci return true; 38858c2ecf20Sopenharmony_ci 38868c2ecf20Sopenharmony_ci if (crtc_state->hw.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) 38878c2ecf20Sopenharmony_ci return false; 38888c2ecf20Sopenharmony_ci 38898c2ecf20Sopenharmony_ci intel_atomic_crtc_state_for_each_plane_state(plane, plane_state, crtc_state) { 38908c2ecf20Sopenharmony_ci const struct skl_plane_wm *wm = 38918c2ecf20Sopenharmony_ci &crtc_state->wm.skl.optimal.planes[plane->id]; 38928c2ecf20Sopenharmony_ci 38938c2ecf20Sopenharmony_ci /* Skip this plane if it's not enabled */ 38948c2ecf20Sopenharmony_ci if (!wm->wm[0].plane_en) 38958c2ecf20Sopenharmony_ci continue; 38968c2ecf20Sopenharmony_ci 38978c2ecf20Sopenharmony_ci /* Find the highest enabled wm level for this plane */ 38988c2ecf20Sopenharmony_ci for (level = ilk_wm_max_level(dev_priv); 38998c2ecf20Sopenharmony_ci !wm->wm[level].plane_en; --level) 39008c2ecf20Sopenharmony_ci { } 39018c2ecf20Sopenharmony_ci 39028c2ecf20Sopenharmony_ci latency = dev_priv->wm.skl_latency[level]; 39038c2ecf20Sopenharmony_ci 39048c2ecf20Sopenharmony_ci if (skl_needs_memory_bw_wa(dev_priv) && 39058c2ecf20Sopenharmony_ci plane_state->uapi.fb->modifier == 39068c2ecf20Sopenharmony_ci I915_FORMAT_MOD_X_TILED) 39078c2ecf20Sopenharmony_ci latency += 15; 39088c2ecf20Sopenharmony_ci 39098c2ecf20Sopenharmony_ci /* 39108c2ecf20Sopenharmony_ci * If any of the planes on this pipe don't enable wm levels that 39118c2ecf20Sopenharmony_ci * incur memory latencies higher than sagv_block_time_us we 39128c2ecf20Sopenharmony_ci * can't enable SAGV. 39138c2ecf20Sopenharmony_ci */ 39148c2ecf20Sopenharmony_ci if (latency < dev_priv->sagv_block_time_us) 39158c2ecf20Sopenharmony_ci return false; 39168c2ecf20Sopenharmony_ci } 39178c2ecf20Sopenharmony_ci 39188c2ecf20Sopenharmony_ci return true; 39198c2ecf20Sopenharmony_ci} 39208c2ecf20Sopenharmony_ci 39218c2ecf20Sopenharmony_cistatic bool tgl_crtc_can_enable_sagv(const struct intel_crtc_state *crtc_state) 39228c2ecf20Sopenharmony_ci{ 39238c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); 39248c2ecf20Sopenharmony_ci enum plane_id plane_id; 39258c2ecf20Sopenharmony_ci 39268c2ecf20Sopenharmony_ci if (!crtc_state->hw.active) 39278c2ecf20Sopenharmony_ci return true; 39288c2ecf20Sopenharmony_ci 39298c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) { 39308c2ecf20Sopenharmony_ci const struct skl_ddb_entry *plane_alloc = 39318c2ecf20Sopenharmony_ci &crtc_state->wm.skl.plane_ddb_y[plane_id]; 39328c2ecf20Sopenharmony_ci const struct skl_plane_wm *wm = 39338c2ecf20Sopenharmony_ci &crtc_state->wm.skl.optimal.planes[plane_id]; 39348c2ecf20Sopenharmony_ci 39358c2ecf20Sopenharmony_ci if (skl_ddb_entry_size(plane_alloc) < wm->sagv_wm0.min_ddb_alloc) 39368c2ecf20Sopenharmony_ci return false; 39378c2ecf20Sopenharmony_ci } 39388c2ecf20Sopenharmony_ci 39398c2ecf20Sopenharmony_ci return true; 39408c2ecf20Sopenharmony_ci} 39418c2ecf20Sopenharmony_ci 39428c2ecf20Sopenharmony_cistatic bool intel_crtc_can_enable_sagv(const struct intel_crtc_state *crtc_state) 39438c2ecf20Sopenharmony_ci{ 39448c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); 39458c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 39468c2ecf20Sopenharmony_ci 39478c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 12) 39488c2ecf20Sopenharmony_ci return tgl_crtc_can_enable_sagv(crtc_state); 39498c2ecf20Sopenharmony_ci else 39508c2ecf20Sopenharmony_ci return skl_crtc_can_enable_sagv(crtc_state); 39518c2ecf20Sopenharmony_ci} 39528c2ecf20Sopenharmony_ci 39538c2ecf20Sopenharmony_cibool intel_can_enable_sagv(struct drm_i915_private *dev_priv, 39548c2ecf20Sopenharmony_ci const struct intel_bw_state *bw_state) 39558c2ecf20Sopenharmony_ci{ 39568c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) < 11 && 39578c2ecf20Sopenharmony_ci bw_state->active_pipes && !is_power_of_2(bw_state->active_pipes)) 39588c2ecf20Sopenharmony_ci return false; 39598c2ecf20Sopenharmony_ci 39608c2ecf20Sopenharmony_ci return bw_state->pipe_sagv_reject == 0; 39618c2ecf20Sopenharmony_ci} 39628c2ecf20Sopenharmony_ci 39638c2ecf20Sopenharmony_cistatic int intel_compute_sagv_mask(struct intel_atomic_state *state) 39648c2ecf20Sopenharmony_ci{ 39658c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(state->base.dev); 39668c2ecf20Sopenharmony_ci int ret; 39678c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 39688c2ecf20Sopenharmony_ci struct intel_crtc_state *new_crtc_state; 39698c2ecf20Sopenharmony_ci struct intel_bw_state *new_bw_state = NULL; 39708c2ecf20Sopenharmony_ci const struct intel_bw_state *old_bw_state = NULL; 39718c2ecf20Sopenharmony_ci int i; 39728c2ecf20Sopenharmony_ci 39738c2ecf20Sopenharmony_ci for_each_new_intel_crtc_in_state(state, crtc, 39748c2ecf20Sopenharmony_ci new_crtc_state, i) { 39758c2ecf20Sopenharmony_ci new_bw_state = intel_atomic_get_bw_state(state); 39768c2ecf20Sopenharmony_ci if (IS_ERR(new_bw_state)) 39778c2ecf20Sopenharmony_ci return PTR_ERR(new_bw_state); 39788c2ecf20Sopenharmony_ci 39798c2ecf20Sopenharmony_ci old_bw_state = intel_atomic_get_old_bw_state(state); 39808c2ecf20Sopenharmony_ci 39818c2ecf20Sopenharmony_ci if (intel_crtc_can_enable_sagv(new_crtc_state)) 39828c2ecf20Sopenharmony_ci new_bw_state->pipe_sagv_reject &= ~BIT(crtc->pipe); 39838c2ecf20Sopenharmony_ci else 39848c2ecf20Sopenharmony_ci new_bw_state->pipe_sagv_reject |= BIT(crtc->pipe); 39858c2ecf20Sopenharmony_ci } 39868c2ecf20Sopenharmony_ci 39878c2ecf20Sopenharmony_ci if (!new_bw_state) 39888c2ecf20Sopenharmony_ci return 0; 39898c2ecf20Sopenharmony_ci 39908c2ecf20Sopenharmony_ci new_bw_state->active_pipes = 39918c2ecf20Sopenharmony_ci intel_calc_active_pipes(state, old_bw_state->active_pipes); 39928c2ecf20Sopenharmony_ci 39938c2ecf20Sopenharmony_ci if (new_bw_state->active_pipes != old_bw_state->active_pipes) { 39948c2ecf20Sopenharmony_ci ret = intel_atomic_lock_global_state(&new_bw_state->base); 39958c2ecf20Sopenharmony_ci if (ret) 39968c2ecf20Sopenharmony_ci return ret; 39978c2ecf20Sopenharmony_ci } 39988c2ecf20Sopenharmony_ci 39998c2ecf20Sopenharmony_ci if (intel_can_enable_sagv(dev_priv, new_bw_state) != 40008c2ecf20Sopenharmony_ci intel_can_enable_sagv(dev_priv, old_bw_state)) { 40018c2ecf20Sopenharmony_ci ret = intel_atomic_serialize_global_state(&new_bw_state->base); 40028c2ecf20Sopenharmony_ci if (ret) 40038c2ecf20Sopenharmony_ci return ret; 40048c2ecf20Sopenharmony_ci } else if (new_bw_state->pipe_sagv_reject != old_bw_state->pipe_sagv_reject) { 40058c2ecf20Sopenharmony_ci ret = intel_atomic_lock_global_state(&new_bw_state->base); 40068c2ecf20Sopenharmony_ci if (ret) 40078c2ecf20Sopenharmony_ci return ret; 40088c2ecf20Sopenharmony_ci } 40098c2ecf20Sopenharmony_ci 40108c2ecf20Sopenharmony_ci for_each_new_intel_crtc_in_state(state, crtc, 40118c2ecf20Sopenharmony_ci new_crtc_state, i) { 40128c2ecf20Sopenharmony_ci struct skl_pipe_wm *pipe_wm = &new_crtc_state->wm.skl.optimal; 40138c2ecf20Sopenharmony_ci 40148c2ecf20Sopenharmony_ci /* 40158c2ecf20Sopenharmony_ci * We store use_sagv_wm in the crtc state rather than relying on 40168c2ecf20Sopenharmony_ci * that bw state since we have no convenient way to get at the 40178c2ecf20Sopenharmony_ci * latter from the plane commit hooks (especially in the legacy 40188c2ecf20Sopenharmony_ci * cursor case) 40198c2ecf20Sopenharmony_ci */ 40208c2ecf20Sopenharmony_ci pipe_wm->use_sagv_wm = INTEL_GEN(dev_priv) >= 12 && 40218c2ecf20Sopenharmony_ci intel_can_enable_sagv(dev_priv, new_bw_state); 40228c2ecf20Sopenharmony_ci } 40238c2ecf20Sopenharmony_ci 40248c2ecf20Sopenharmony_ci return 0; 40258c2ecf20Sopenharmony_ci} 40268c2ecf20Sopenharmony_ci 40278c2ecf20Sopenharmony_ci/* 40288c2ecf20Sopenharmony_ci * Calculate initial DBuf slice offset, based on slice size 40298c2ecf20Sopenharmony_ci * and mask(i.e if slice size is 1024 and second slice is enabled 40308c2ecf20Sopenharmony_ci * offset would be 1024) 40318c2ecf20Sopenharmony_ci */ 40328c2ecf20Sopenharmony_cistatic unsigned int 40338c2ecf20Sopenharmony_ciicl_get_first_dbuf_slice_offset(u32 dbuf_slice_mask, 40348c2ecf20Sopenharmony_ci u32 slice_size, 40358c2ecf20Sopenharmony_ci u32 ddb_size) 40368c2ecf20Sopenharmony_ci{ 40378c2ecf20Sopenharmony_ci unsigned int offset = 0; 40388c2ecf20Sopenharmony_ci 40398c2ecf20Sopenharmony_ci if (!dbuf_slice_mask) 40408c2ecf20Sopenharmony_ci return 0; 40418c2ecf20Sopenharmony_ci 40428c2ecf20Sopenharmony_ci offset = (ffs(dbuf_slice_mask) - 1) * slice_size; 40438c2ecf20Sopenharmony_ci 40448c2ecf20Sopenharmony_ci WARN_ON(offset >= ddb_size); 40458c2ecf20Sopenharmony_ci return offset; 40468c2ecf20Sopenharmony_ci} 40478c2ecf20Sopenharmony_ci 40488c2ecf20Sopenharmony_ciu16 intel_get_ddb_size(struct drm_i915_private *dev_priv) 40498c2ecf20Sopenharmony_ci{ 40508c2ecf20Sopenharmony_ci u16 ddb_size = INTEL_INFO(dev_priv)->ddb_size; 40518c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, ddb_size == 0); 40528c2ecf20Sopenharmony_ci 40538c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) < 11) 40548c2ecf20Sopenharmony_ci return ddb_size - 4; /* 4 blocks for bypass path allocation */ 40558c2ecf20Sopenharmony_ci 40568c2ecf20Sopenharmony_ci return ddb_size; 40578c2ecf20Sopenharmony_ci} 40588c2ecf20Sopenharmony_ci 40598c2ecf20Sopenharmony_ciu32 skl_ddb_dbuf_slice_mask(struct drm_i915_private *dev_priv, 40608c2ecf20Sopenharmony_ci const struct skl_ddb_entry *entry) 40618c2ecf20Sopenharmony_ci{ 40628c2ecf20Sopenharmony_ci u32 slice_mask = 0; 40638c2ecf20Sopenharmony_ci u16 ddb_size = intel_get_ddb_size(dev_priv); 40648c2ecf20Sopenharmony_ci u16 num_supported_slices = INTEL_INFO(dev_priv)->num_supported_dbuf_slices; 40658c2ecf20Sopenharmony_ci u16 slice_size = ddb_size / num_supported_slices; 40668c2ecf20Sopenharmony_ci u16 start_slice; 40678c2ecf20Sopenharmony_ci u16 end_slice; 40688c2ecf20Sopenharmony_ci 40698c2ecf20Sopenharmony_ci if (!skl_ddb_entry_size(entry)) 40708c2ecf20Sopenharmony_ci return 0; 40718c2ecf20Sopenharmony_ci 40728c2ecf20Sopenharmony_ci start_slice = entry->start / slice_size; 40738c2ecf20Sopenharmony_ci end_slice = (entry->end - 1) / slice_size; 40748c2ecf20Sopenharmony_ci 40758c2ecf20Sopenharmony_ci /* 40768c2ecf20Sopenharmony_ci * Per plane DDB entry can in a really worst case be on multiple slices 40778c2ecf20Sopenharmony_ci * but single entry is anyway contigious. 40788c2ecf20Sopenharmony_ci */ 40798c2ecf20Sopenharmony_ci while (start_slice <= end_slice) { 40808c2ecf20Sopenharmony_ci slice_mask |= BIT(start_slice); 40818c2ecf20Sopenharmony_ci start_slice++; 40828c2ecf20Sopenharmony_ci } 40838c2ecf20Sopenharmony_ci 40848c2ecf20Sopenharmony_ci return slice_mask; 40858c2ecf20Sopenharmony_ci} 40868c2ecf20Sopenharmony_ci 40878c2ecf20Sopenharmony_cistatic u8 skl_compute_dbuf_slices(const struct intel_crtc_state *crtc_state, 40888c2ecf20Sopenharmony_ci u8 active_pipes); 40898c2ecf20Sopenharmony_ci 40908c2ecf20Sopenharmony_cistatic int 40918c2ecf20Sopenharmony_ciskl_ddb_get_pipe_allocation_limits(struct drm_i915_private *dev_priv, 40928c2ecf20Sopenharmony_ci const struct intel_crtc_state *crtc_state, 40938c2ecf20Sopenharmony_ci const u64 total_data_rate, 40948c2ecf20Sopenharmony_ci struct skl_ddb_entry *alloc, /* out */ 40958c2ecf20Sopenharmony_ci int *num_active /* out */) 40968c2ecf20Sopenharmony_ci{ 40978c2ecf20Sopenharmony_ci struct drm_atomic_state *state = crtc_state->uapi.state; 40988c2ecf20Sopenharmony_ci struct intel_atomic_state *intel_state = to_intel_atomic_state(state); 40998c2ecf20Sopenharmony_ci struct drm_crtc *for_crtc = crtc_state->uapi.crtc; 41008c2ecf20Sopenharmony_ci const struct intel_crtc *crtc; 41018c2ecf20Sopenharmony_ci u32 pipe_width = 0, total_width_in_range = 0, width_before_pipe_in_range = 0; 41028c2ecf20Sopenharmony_ci enum pipe for_pipe = to_intel_crtc(for_crtc)->pipe; 41038c2ecf20Sopenharmony_ci struct intel_dbuf_state *new_dbuf_state = 41048c2ecf20Sopenharmony_ci intel_atomic_get_new_dbuf_state(intel_state); 41058c2ecf20Sopenharmony_ci const struct intel_dbuf_state *old_dbuf_state = 41068c2ecf20Sopenharmony_ci intel_atomic_get_old_dbuf_state(intel_state); 41078c2ecf20Sopenharmony_ci u8 active_pipes = new_dbuf_state->active_pipes; 41088c2ecf20Sopenharmony_ci u16 ddb_size; 41098c2ecf20Sopenharmony_ci u32 ddb_range_size; 41108c2ecf20Sopenharmony_ci u32 i; 41118c2ecf20Sopenharmony_ci u32 dbuf_slice_mask; 41128c2ecf20Sopenharmony_ci u32 offset; 41138c2ecf20Sopenharmony_ci u32 slice_size; 41148c2ecf20Sopenharmony_ci u32 total_slice_mask; 41158c2ecf20Sopenharmony_ci u32 start, end; 41168c2ecf20Sopenharmony_ci int ret; 41178c2ecf20Sopenharmony_ci 41188c2ecf20Sopenharmony_ci *num_active = hweight8(active_pipes); 41198c2ecf20Sopenharmony_ci 41208c2ecf20Sopenharmony_ci if (!crtc_state->hw.active) { 41218c2ecf20Sopenharmony_ci alloc->start = 0; 41228c2ecf20Sopenharmony_ci alloc->end = 0; 41238c2ecf20Sopenharmony_ci return 0; 41248c2ecf20Sopenharmony_ci } 41258c2ecf20Sopenharmony_ci 41268c2ecf20Sopenharmony_ci ddb_size = intel_get_ddb_size(dev_priv); 41278c2ecf20Sopenharmony_ci 41288c2ecf20Sopenharmony_ci slice_size = ddb_size / INTEL_INFO(dev_priv)->num_supported_dbuf_slices; 41298c2ecf20Sopenharmony_ci 41308c2ecf20Sopenharmony_ci /* 41318c2ecf20Sopenharmony_ci * If the state doesn't change the active CRTC's or there is no 41328c2ecf20Sopenharmony_ci * modeset request, then there's no need to recalculate; 41338c2ecf20Sopenharmony_ci * the existing pipe allocation limits should remain unchanged. 41348c2ecf20Sopenharmony_ci * Note that we're safe from racing commits since any racing commit 41358c2ecf20Sopenharmony_ci * that changes the active CRTC list or do modeset would need to 41368c2ecf20Sopenharmony_ci * grab _all_ crtc locks, including the one we currently hold. 41378c2ecf20Sopenharmony_ci */ 41388c2ecf20Sopenharmony_ci if (old_dbuf_state->active_pipes == new_dbuf_state->active_pipes && 41398c2ecf20Sopenharmony_ci !dev_priv->wm.distrust_bios_wm) { 41408c2ecf20Sopenharmony_ci /* 41418c2ecf20Sopenharmony_ci * alloc may be cleared by clear_intel_crtc_state, 41428c2ecf20Sopenharmony_ci * copy from old state to be sure 41438c2ecf20Sopenharmony_ci * 41448c2ecf20Sopenharmony_ci * FIXME get rid of this mess 41458c2ecf20Sopenharmony_ci */ 41468c2ecf20Sopenharmony_ci *alloc = to_intel_crtc_state(for_crtc->state)->wm.skl.ddb; 41478c2ecf20Sopenharmony_ci return 0; 41488c2ecf20Sopenharmony_ci } 41498c2ecf20Sopenharmony_ci 41508c2ecf20Sopenharmony_ci /* 41518c2ecf20Sopenharmony_ci * Get allowed DBuf slices for correspondent pipe and platform. 41528c2ecf20Sopenharmony_ci */ 41538c2ecf20Sopenharmony_ci dbuf_slice_mask = skl_compute_dbuf_slices(crtc_state, active_pipes); 41548c2ecf20Sopenharmony_ci 41558c2ecf20Sopenharmony_ci /* 41568c2ecf20Sopenharmony_ci * Figure out at which DBuf slice we start, i.e if we start at Dbuf S2 41578c2ecf20Sopenharmony_ci * and slice size is 1024, the offset would be 1024 41588c2ecf20Sopenharmony_ci */ 41598c2ecf20Sopenharmony_ci offset = icl_get_first_dbuf_slice_offset(dbuf_slice_mask, 41608c2ecf20Sopenharmony_ci slice_size, ddb_size); 41618c2ecf20Sopenharmony_ci 41628c2ecf20Sopenharmony_ci /* 41638c2ecf20Sopenharmony_ci * Figure out total size of allowed DBuf slices, which is basically 41648c2ecf20Sopenharmony_ci * a number of allowed slices for that pipe multiplied by slice size. 41658c2ecf20Sopenharmony_ci * Inside of this 41668c2ecf20Sopenharmony_ci * range ddb entries are still allocated in proportion to display width. 41678c2ecf20Sopenharmony_ci */ 41688c2ecf20Sopenharmony_ci ddb_range_size = hweight8(dbuf_slice_mask) * slice_size; 41698c2ecf20Sopenharmony_ci 41708c2ecf20Sopenharmony_ci /* 41718c2ecf20Sopenharmony_ci * Watermark/ddb requirement highly depends upon width of the 41728c2ecf20Sopenharmony_ci * framebuffer, So instead of allocating DDB equally among pipes 41738c2ecf20Sopenharmony_ci * distribute DDB based on resolution/width of the display. 41748c2ecf20Sopenharmony_ci */ 41758c2ecf20Sopenharmony_ci total_slice_mask = dbuf_slice_mask; 41768c2ecf20Sopenharmony_ci for_each_new_intel_crtc_in_state(intel_state, crtc, crtc_state, i) { 41778c2ecf20Sopenharmony_ci const struct drm_display_mode *adjusted_mode = 41788c2ecf20Sopenharmony_ci &crtc_state->hw.adjusted_mode; 41798c2ecf20Sopenharmony_ci enum pipe pipe = crtc->pipe; 41808c2ecf20Sopenharmony_ci int hdisplay, vdisplay; 41818c2ecf20Sopenharmony_ci u32 pipe_dbuf_slice_mask; 41828c2ecf20Sopenharmony_ci 41838c2ecf20Sopenharmony_ci if (!crtc_state->hw.active) 41848c2ecf20Sopenharmony_ci continue; 41858c2ecf20Sopenharmony_ci 41868c2ecf20Sopenharmony_ci pipe_dbuf_slice_mask = skl_compute_dbuf_slices(crtc_state, 41878c2ecf20Sopenharmony_ci active_pipes); 41888c2ecf20Sopenharmony_ci 41898c2ecf20Sopenharmony_ci /* 41908c2ecf20Sopenharmony_ci * According to BSpec pipe can share one dbuf slice with another 41918c2ecf20Sopenharmony_ci * pipes or pipe can use multiple dbufs, in both cases we 41928c2ecf20Sopenharmony_ci * account for other pipes only if they have exactly same mask. 41938c2ecf20Sopenharmony_ci * However we need to account how many slices we should enable 41948c2ecf20Sopenharmony_ci * in total. 41958c2ecf20Sopenharmony_ci */ 41968c2ecf20Sopenharmony_ci total_slice_mask |= pipe_dbuf_slice_mask; 41978c2ecf20Sopenharmony_ci 41988c2ecf20Sopenharmony_ci /* 41998c2ecf20Sopenharmony_ci * Do not account pipes using other slice sets 42008c2ecf20Sopenharmony_ci * luckily as of current BSpec slice sets do not partially 42018c2ecf20Sopenharmony_ci * intersect(pipes share either same one slice or same slice set 42028c2ecf20Sopenharmony_ci * i.e no partial intersection), so it is enough to check for 42038c2ecf20Sopenharmony_ci * equality for now. 42048c2ecf20Sopenharmony_ci */ 42058c2ecf20Sopenharmony_ci if (dbuf_slice_mask != pipe_dbuf_slice_mask) 42068c2ecf20Sopenharmony_ci continue; 42078c2ecf20Sopenharmony_ci 42088c2ecf20Sopenharmony_ci drm_mode_get_hv_timing(adjusted_mode, &hdisplay, &vdisplay); 42098c2ecf20Sopenharmony_ci 42108c2ecf20Sopenharmony_ci total_width_in_range += hdisplay; 42118c2ecf20Sopenharmony_ci 42128c2ecf20Sopenharmony_ci if (pipe < for_pipe) 42138c2ecf20Sopenharmony_ci width_before_pipe_in_range += hdisplay; 42148c2ecf20Sopenharmony_ci else if (pipe == for_pipe) 42158c2ecf20Sopenharmony_ci pipe_width = hdisplay; 42168c2ecf20Sopenharmony_ci } 42178c2ecf20Sopenharmony_ci 42188c2ecf20Sopenharmony_ci /* 42198c2ecf20Sopenharmony_ci * FIXME: For now we always enable slice S1 as per 42208c2ecf20Sopenharmony_ci * the Bspec display initialization sequence. 42218c2ecf20Sopenharmony_ci */ 42228c2ecf20Sopenharmony_ci new_dbuf_state->enabled_slices = total_slice_mask | BIT(DBUF_S1); 42238c2ecf20Sopenharmony_ci 42248c2ecf20Sopenharmony_ci if (old_dbuf_state->enabled_slices != new_dbuf_state->enabled_slices) { 42258c2ecf20Sopenharmony_ci ret = intel_atomic_serialize_global_state(&new_dbuf_state->base); 42268c2ecf20Sopenharmony_ci if (ret) 42278c2ecf20Sopenharmony_ci return ret; 42288c2ecf20Sopenharmony_ci } 42298c2ecf20Sopenharmony_ci 42308c2ecf20Sopenharmony_ci start = ddb_range_size * width_before_pipe_in_range / total_width_in_range; 42318c2ecf20Sopenharmony_ci end = ddb_range_size * 42328c2ecf20Sopenharmony_ci (width_before_pipe_in_range + pipe_width) / total_width_in_range; 42338c2ecf20Sopenharmony_ci 42348c2ecf20Sopenharmony_ci alloc->start = offset + start; 42358c2ecf20Sopenharmony_ci alloc->end = offset + end; 42368c2ecf20Sopenharmony_ci 42378c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 42388c2ecf20Sopenharmony_ci "[CRTC:%d:%s] dbuf slices 0x%x, ddb (%d - %d), active pipes 0x%x\n", 42398c2ecf20Sopenharmony_ci for_crtc->base.id, for_crtc->name, 42408c2ecf20Sopenharmony_ci dbuf_slice_mask, alloc->start, alloc->end, active_pipes); 42418c2ecf20Sopenharmony_ci 42428c2ecf20Sopenharmony_ci return 0; 42438c2ecf20Sopenharmony_ci} 42448c2ecf20Sopenharmony_ci 42458c2ecf20Sopenharmony_cistatic int skl_compute_wm_params(const struct intel_crtc_state *crtc_state, 42468c2ecf20Sopenharmony_ci int width, const struct drm_format_info *format, 42478c2ecf20Sopenharmony_ci u64 modifier, unsigned int rotation, 42488c2ecf20Sopenharmony_ci u32 plane_pixel_rate, struct skl_wm_params *wp, 42498c2ecf20Sopenharmony_ci int color_plane); 42508c2ecf20Sopenharmony_cistatic void skl_compute_plane_wm(const struct intel_crtc_state *crtc_state, 42518c2ecf20Sopenharmony_ci int level, 42528c2ecf20Sopenharmony_ci unsigned int latency, 42538c2ecf20Sopenharmony_ci const struct skl_wm_params *wp, 42548c2ecf20Sopenharmony_ci const struct skl_wm_level *result_prev, 42558c2ecf20Sopenharmony_ci struct skl_wm_level *result /* out */); 42568c2ecf20Sopenharmony_ci 42578c2ecf20Sopenharmony_cistatic unsigned int 42588c2ecf20Sopenharmony_ciskl_cursor_allocation(const struct intel_crtc_state *crtc_state, 42598c2ecf20Sopenharmony_ci int num_active) 42608c2ecf20Sopenharmony_ci{ 42618c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 42628c2ecf20Sopenharmony_ci int level, max_level = ilk_wm_max_level(dev_priv); 42638c2ecf20Sopenharmony_ci struct skl_wm_level wm = {}; 42648c2ecf20Sopenharmony_ci int ret, min_ddb_alloc = 0; 42658c2ecf20Sopenharmony_ci struct skl_wm_params wp; 42668c2ecf20Sopenharmony_ci 42678c2ecf20Sopenharmony_ci ret = skl_compute_wm_params(crtc_state, 256, 42688c2ecf20Sopenharmony_ci drm_format_info(DRM_FORMAT_ARGB8888), 42698c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_LINEAR, 42708c2ecf20Sopenharmony_ci DRM_MODE_ROTATE_0, 42718c2ecf20Sopenharmony_ci crtc_state->pixel_rate, &wp, 0); 42728c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, ret); 42738c2ecf20Sopenharmony_ci 42748c2ecf20Sopenharmony_ci for (level = 0; level <= max_level; level++) { 42758c2ecf20Sopenharmony_ci unsigned int latency = dev_priv->wm.skl_latency[level]; 42768c2ecf20Sopenharmony_ci 42778c2ecf20Sopenharmony_ci skl_compute_plane_wm(crtc_state, level, latency, &wp, &wm, &wm); 42788c2ecf20Sopenharmony_ci if (wm.min_ddb_alloc == U16_MAX) 42798c2ecf20Sopenharmony_ci break; 42808c2ecf20Sopenharmony_ci 42818c2ecf20Sopenharmony_ci min_ddb_alloc = wm.min_ddb_alloc; 42828c2ecf20Sopenharmony_ci } 42838c2ecf20Sopenharmony_ci 42848c2ecf20Sopenharmony_ci return max(num_active == 1 ? 32 : 8, min_ddb_alloc); 42858c2ecf20Sopenharmony_ci} 42868c2ecf20Sopenharmony_ci 42878c2ecf20Sopenharmony_cistatic void skl_ddb_entry_init_from_hw(struct drm_i915_private *dev_priv, 42888c2ecf20Sopenharmony_ci struct skl_ddb_entry *entry, u32 reg) 42898c2ecf20Sopenharmony_ci{ 42908c2ecf20Sopenharmony_ci 42918c2ecf20Sopenharmony_ci entry->start = reg & DDB_ENTRY_MASK; 42928c2ecf20Sopenharmony_ci entry->end = (reg >> DDB_ENTRY_END_SHIFT) & DDB_ENTRY_MASK; 42938c2ecf20Sopenharmony_ci 42948c2ecf20Sopenharmony_ci if (entry->end) 42958c2ecf20Sopenharmony_ci entry->end += 1; 42968c2ecf20Sopenharmony_ci} 42978c2ecf20Sopenharmony_ci 42988c2ecf20Sopenharmony_cistatic void 42998c2ecf20Sopenharmony_ciskl_ddb_get_hw_plane_state(struct drm_i915_private *dev_priv, 43008c2ecf20Sopenharmony_ci const enum pipe pipe, 43018c2ecf20Sopenharmony_ci const enum plane_id plane_id, 43028c2ecf20Sopenharmony_ci struct skl_ddb_entry *ddb_y, 43038c2ecf20Sopenharmony_ci struct skl_ddb_entry *ddb_uv) 43048c2ecf20Sopenharmony_ci{ 43058c2ecf20Sopenharmony_ci u32 val, val2; 43068c2ecf20Sopenharmony_ci u32 fourcc = 0; 43078c2ecf20Sopenharmony_ci 43088c2ecf20Sopenharmony_ci /* Cursor doesn't support NV12/planar, so no extra calculation needed */ 43098c2ecf20Sopenharmony_ci if (plane_id == PLANE_CURSOR) { 43108c2ecf20Sopenharmony_ci val = I915_READ(CUR_BUF_CFG(pipe)); 43118c2ecf20Sopenharmony_ci skl_ddb_entry_init_from_hw(dev_priv, ddb_y, val); 43128c2ecf20Sopenharmony_ci return; 43138c2ecf20Sopenharmony_ci } 43148c2ecf20Sopenharmony_ci 43158c2ecf20Sopenharmony_ci val = I915_READ(PLANE_CTL(pipe, plane_id)); 43168c2ecf20Sopenharmony_ci 43178c2ecf20Sopenharmony_ci /* No DDB allocated for disabled planes */ 43188c2ecf20Sopenharmony_ci if (val & PLANE_CTL_ENABLE) 43198c2ecf20Sopenharmony_ci fourcc = skl_format_to_fourcc(val & PLANE_CTL_FORMAT_MASK, 43208c2ecf20Sopenharmony_ci val & PLANE_CTL_ORDER_RGBX, 43218c2ecf20Sopenharmony_ci val & PLANE_CTL_ALPHA_MASK); 43228c2ecf20Sopenharmony_ci 43238c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 11) { 43248c2ecf20Sopenharmony_ci val = I915_READ(PLANE_BUF_CFG(pipe, plane_id)); 43258c2ecf20Sopenharmony_ci skl_ddb_entry_init_from_hw(dev_priv, ddb_y, val); 43268c2ecf20Sopenharmony_ci } else { 43278c2ecf20Sopenharmony_ci val = I915_READ(PLANE_BUF_CFG(pipe, plane_id)); 43288c2ecf20Sopenharmony_ci val2 = I915_READ(PLANE_NV12_BUF_CFG(pipe, plane_id)); 43298c2ecf20Sopenharmony_ci 43308c2ecf20Sopenharmony_ci if (fourcc && 43318c2ecf20Sopenharmony_ci drm_format_info_is_yuv_semiplanar(drm_format_info(fourcc))) 43328c2ecf20Sopenharmony_ci swap(val, val2); 43338c2ecf20Sopenharmony_ci 43348c2ecf20Sopenharmony_ci skl_ddb_entry_init_from_hw(dev_priv, ddb_y, val); 43358c2ecf20Sopenharmony_ci skl_ddb_entry_init_from_hw(dev_priv, ddb_uv, val2); 43368c2ecf20Sopenharmony_ci } 43378c2ecf20Sopenharmony_ci} 43388c2ecf20Sopenharmony_ci 43398c2ecf20Sopenharmony_civoid skl_pipe_ddb_get_hw_state(struct intel_crtc *crtc, 43408c2ecf20Sopenharmony_ci struct skl_ddb_entry *ddb_y, 43418c2ecf20Sopenharmony_ci struct skl_ddb_entry *ddb_uv) 43428c2ecf20Sopenharmony_ci{ 43438c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 43448c2ecf20Sopenharmony_ci enum intel_display_power_domain power_domain; 43458c2ecf20Sopenharmony_ci enum pipe pipe = crtc->pipe; 43468c2ecf20Sopenharmony_ci intel_wakeref_t wakeref; 43478c2ecf20Sopenharmony_ci enum plane_id plane_id; 43488c2ecf20Sopenharmony_ci 43498c2ecf20Sopenharmony_ci power_domain = POWER_DOMAIN_PIPE(pipe); 43508c2ecf20Sopenharmony_ci wakeref = intel_display_power_get_if_enabled(dev_priv, power_domain); 43518c2ecf20Sopenharmony_ci if (!wakeref) 43528c2ecf20Sopenharmony_ci return; 43538c2ecf20Sopenharmony_ci 43548c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) 43558c2ecf20Sopenharmony_ci skl_ddb_get_hw_plane_state(dev_priv, pipe, 43568c2ecf20Sopenharmony_ci plane_id, 43578c2ecf20Sopenharmony_ci &ddb_y[plane_id], 43588c2ecf20Sopenharmony_ci &ddb_uv[plane_id]); 43598c2ecf20Sopenharmony_ci 43608c2ecf20Sopenharmony_ci intel_display_power_put(dev_priv, power_domain, wakeref); 43618c2ecf20Sopenharmony_ci} 43628c2ecf20Sopenharmony_ci 43638c2ecf20Sopenharmony_ci/* 43648c2ecf20Sopenharmony_ci * Determines the downscale amount of a plane for the purposes of watermark calculations. 43658c2ecf20Sopenharmony_ci * The bspec defines downscale amount as: 43668c2ecf20Sopenharmony_ci * 43678c2ecf20Sopenharmony_ci * """ 43688c2ecf20Sopenharmony_ci * Horizontal down scale amount = maximum[1, Horizontal source size / 43698c2ecf20Sopenharmony_ci * Horizontal destination size] 43708c2ecf20Sopenharmony_ci * Vertical down scale amount = maximum[1, Vertical source size / 43718c2ecf20Sopenharmony_ci * Vertical destination size] 43728c2ecf20Sopenharmony_ci * Total down scale amount = Horizontal down scale amount * 43738c2ecf20Sopenharmony_ci * Vertical down scale amount 43748c2ecf20Sopenharmony_ci * """ 43758c2ecf20Sopenharmony_ci * 43768c2ecf20Sopenharmony_ci * Return value is provided in 16.16 fixed point form to retain fractional part. 43778c2ecf20Sopenharmony_ci * Caller should take care of dividing & rounding off the value. 43788c2ecf20Sopenharmony_ci */ 43798c2ecf20Sopenharmony_cistatic uint_fixed_16_16_t 43808c2ecf20Sopenharmony_ciskl_plane_downscale_amount(const struct intel_crtc_state *crtc_state, 43818c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state) 43828c2ecf20Sopenharmony_ci{ 43838c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 43848c2ecf20Sopenharmony_ci u32 src_w, src_h, dst_w, dst_h; 43858c2ecf20Sopenharmony_ci uint_fixed_16_16_t fp_w_ratio, fp_h_ratio; 43868c2ecf20Sopenharmony_ci uint_fixed_16_16_t downscale_h, downscale_w; 43878c2ecf20Sopenharmony_ci 43888c2ecf20Sopenharmony_ci if (drm_WARN_ON(&dev_priv->drm, 43898c2ecf20Sopenharmony_ci !intel_wm_plane_visible(crtc_state, plane_state))) 43908c2ecf20Sopenharmony_ci return u32_to_fixed16(0); 43918c2ecf20Sopenharmony_ci 43928c2ecf20Sopenharmony_ci /* 43938c2ecf20Sopenharmony_ci * Src coordinates are already rotated by 270 degrees for 43948c2ecf20Sopenharmony_ci * the 90/270 degree plane rotation cases (to match the 43958c2ecf20Sopenharmony_ci * GTT mapping), hence no need to account for rotation here. 43968c2ecf20Sopenharmony_ci * 43978c2ecf20Sopenharmony_ci * n.b., src is 16.16 fixed point, dst is whole integer. 43988c2ecf20Sopenharmony_ci */ 43998c2ecf20Sopenharmony_ci src_w = drm_rect_width(&plane_state->uapi.src) >> 16; 44008c2ecf20Sopenharmony_ci src_h = drm_rect_height(&plane_state->uapi.src) >> 16; 44018c2ecf20Sopenharmony_ci dst_w = drm_rect_width(&plane_state->uapi.dst); 44028c2ecf20Sopenharmony_ci dst_h = drm_rect_height(&plane_state->uapi.dst); 44038c2ecf20Sopenharmony_ci 44048c2ecf20Sopenharmony_ci fp_w_ratio = div_fixed16(src_w, dst_w); 44058c2ecf20Sopenharmony_ci fp_h_ratio = div_fixed16(src_h, dst_h); 44068c2ecf20Sopenharmony_ci downscale_w = max_fixed16(fp_w_ratio, u32_to_fixed16(1)); 44078c2ecf20Sopenharmony_ci downscale_h = max_fixed16(fp_h_ratio, u32_to_fixed16(1)); 44088c2ecf20Sopenharmony_ci 44098c2ecf20Sopenharmony_ci return mul_fixed16(downscale_w, downscale_h); 44108c2ecf20Sopenharmony_ci} 44118c2ecf20Sopenharmony_ci 44128c2ecf20Sopenharmony_cistruct dbuf_slice_conf_entry { 44138c2ecf20Sopenharmony_ci u8 active_pipes; 44148c2ecf20Sopenharmony_ci u8 dbuf_mask[I915_MAX_PIPES]; 44158c2ecf20Sopenharmony_ci}; 44168c2ecf20Sopenharmony_ci 44178c2ecf20Sopenharmony_ci/* 44188c2ecf20Sopenharmony_ci * Table taken from Bspec 12716 44198c2ecf20Sopenharmony_ci * Pipes do have some preferred DBuf slice affinity, 44208c2ecf20Sopenharmony_ci * plus there are some hardcoded requirements on how 44218c2ecf20Sopenharmony_ci * those should be distributed for multipipe scenarios. 44228c2ecf20Sopenharmony_ci * For more DBuf slices algorithm can get even more messy 44238c2ecf20Sopenharmony_ci * and less readable, so decided to use a table almost 44248c2ecf20Sopenharmony_ci * as is from BSpec itself - that way it is at least easier 44258c2ecf20Sopenharmony_ci * to compare, change and check. 44268c2ecf20Sopenharmony_ci */ 44278c2ecf20Sopenharmony_cistatic const struct dbuf_slice_conf_entry icl_allowed_dbufs[] = 44288c2ecf20Sopenharmony_ci/* Autogenerated with igt/tools/intel_dbuf_map tool: */ 44298c2ecf20Sopenharmony_ci{ 44308c2ecf20Sopenharmony_ci { 44318c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_A), 44328c2ecf20Sopenharmony_ci .dbuf_mask = { 44338c2ecf20Sopenharmony_ci [PIPE_A] = BIT(DBUF_S1), 44348c2ecf20Sopenharmony_ci }, 44358c2ecf20Sopenharmony_ci }, 44368c2ecf20Sopenharmony_ci { 44378c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_B), 44388c2ecf20Sopenharmony_ci .dbuf_mask = { 44398c2ecf20Sopenharmony_ci [PIPE_B] = BIT(DBUF_S1), 44408c2ecf20Sopenharmony_ci }, 44418c2ecf20Sopenharmony_ci }, 44428c2ecf20Sopenharmony_ci { 44438c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_A) | BIT(PIPE_B), 44448c2ecf20Sopenharmony_ci .dbuf_mask = { 44458c2ecf20Sopenharmony_ci [PIPE_A] = BIT(DBUF_S1), 44468c2ecf20Sopenharmony_ci [PIPE_B] = BIT(DBUF_S2), 44478c2ecf20Sopenharmony_ci }, 44488c2ecf20Sopenharmony_ci }, 44498c2ecf20Sopenharmony_ci { 44508c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_C), 44518c2ecf20Sopenharmony_ci .dbuf_mask = { 44528c2ecf20Sopenharmony_ci [PIPE_C] = BIT(DBUF_S2), 44538c2ecf20Sopenharmony_ci }, 44548c2ecf20Sopenharmony_ci }, 44558c2ecf20Sopenharmony_ci { 44568c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_A) | BIT(PIPE_C), 44578c2ecf20Sopenharmony_ci .dbuf_mask = { 44588c2ecf20Sopenharmony_ci [PIPE_A] = BIT(DBUF_S1), 44598c2ecf20Sopenharmony_ci [PIPE_C] = BIT(DBUF_S2), 44608c2ecf20Sopenharmony_ci }, 44618c2ecf20Sopenharmony_ci }, 44628c2ecf20Sopenharmony_ci { 44638c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_B) | BIT(PIPE_C), 44648c2ecf20Sopenharmony_ci .dbuf_mask = { 44658c2ecf20Sopenharmony_ci [PIPE_B] = BIT(DBUF_S1), 44668c2ecf20Sopenharmony_ci [PIPE_C] = BIT(DBUF_S2), 44678c2ecf20Sopenharmony_ci }, 44688c2ecf20Sopenharmony_ci }, 44698c2ecf20Sopenharmony_ci { 44708c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_A) | BIT(PIPE_B) | BIT(PIPE_C), 44718c2ecf20Sopenharmony_ci .dbuf_mask = { 44728c2ecf20Sopenharmony_ci [PIPE_A] = BIT(DBUF_S1), 44738c2ecf20Sopenharmony_ci [PIPE_B] = BIT(DBUF_S1), 44748c2ecf20Sopenharmony_ci [PIPE_C] = BIT(DBUF_S2), 44758c2ecf20Sopenharmony_ci }, 44768c2ecf20Sopenharmony_ci }, 44778c2ecf20Sopenharmony_ci {} 44788c2ecf20Sopenharmony_ci}; 44798c2ecf20Sopenharmony_ci 44808c2ecf20Sopenharmony_ci/* 44818c2ecf20Sopenharmony_ci * Table taken from Bspec 49255 44828c2ecf20Sopenharmony_ci * Pipes do have some preferred DBuf slice affinity, 44838c2ecf20Sopenharmony_ci * plus there are some hardcoded requirements on how 44848c2ecf20Sopenharmony_ci * those should be distributed for multipipe scenarios. 44858c2ecf20Sopenharmony_ci * For more DBuf slices algorithm can get even more messy 44868c2ecf20Sopenharmony_ci * and less readable, so decided to use a table almost 44878c2ecf20Sopenharmony_ci * as is from BSpec itself - that way it is at least easier 44888c2ecf20Sopenharmony_ci * to compare, change and check. 44898c2ecf20Sopenharmony_ci */ 44908c2ecf20Sopenharmony_cistatic const struct dbuf_slice_conf_entry tgl_allowed_dbufs[] = 44918c2ecf20Sopenharmony_ci/* Autogenerated with igt/tools/intel_dbuf_map tool: */ 44928c2ecf20Sopenharmony_ci{ 44938c2ecf20Sopenharmony_ci { 44948c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_A), 44958c2ecf20Sopenharmony_ci .dbuf_mask = { 44968c2ecf20Sopenharmony_ci [PIPE_A] = BIT(DBUF_S1) | BIT(DBUF_S2), 44978c2ecf20Sopenharmony_ci }, 44988c2ecf20Sopenharmony_ci }, 44998c2ecf20Sopenharmony_ci { 45008c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_B), 45018c2ecf20Sopenharmony_ci .dbuf_mask = { 45028c2ecf20Sopenharmony_ci [PIPE_B] = BIT(DBUF_S1) | BIT(DBUF_S2), 45038c2ecf20Sopenharmony_ci }, 45048c2ecf20Sopenharmony_ci }, 45058c2ecf20Sopenharmony_ci { 45068c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_A) | BIT(PIPE_B), 45078c2ecf20Sopenharmony_ci .dbuf_mask = { 45088c2ecf20Sopenharmony_ci [PIPE_A] = BIT(DBUF_S2), 45098c2ecf20Sopenharmony_ci [PIPE_B] = BIT(DBUF_S1), 45108c2ecf20Sopenharmony_ci }, 45118c2ecf20Sopenharmony_ci }, 45128c2ecf20Sopenharmony_ci { 45138c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_C), 45148c2ecf20Sopenharmony_ci .dbuf_mask = { 45158c2ecf20Sopenharmony_ci [PIPE_C] = BIT(DBUF_S2) | BIT(DBUF_S1), 45168c2ecf20Sopenharmony_ci }, 45178c2ecf20Sopenharmony_ci }, 45188c2ecf20Sopenharmony_ci { 45198c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_A) | BIT(PIPE_C), 45208c2ecf20Sopenharmony_ci .dbuf_mask = { 45218c2ecf20Sopenharmony_ci [PIPE_A] = BIT(DBUF_S1), 45228c2ecf20Sopenharmony_ci [PIPE_C] = BIT(DBUF_S2), 45238c2ecf20Sopenharmony_ci }, 45248c2ecf20Sopenharmony_ci }, 45258c2ecf20Sopenharmony_ci { 45268c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_B) | BIT(PIPE_C), 45278c2ecf20Sopenharmony_ci .dbuf_mask = { 45288c2ecf20Sopenharmony_ci [PIPE_B] = BIT(DBUF_S1), 45298c2ecf20Sopenharmony_ci [PIPE_C] = BIT(DBUF_S2), 45308c2ecf20Sopenharmony_ci }, 45318c2ecf20Sopenharmony_ci }, 45328c2ecf20Sopenharmony_ci { 45338c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_A) | BIT(PIPE_B) | BIT(PIPE_C), 45348c2ecf20Sopenharmony_ci .dbuf_mask = { 45358c2ecf20Sopenharmony_ci [PIPE_A] = BIT(DBUF_S1), 45368c2ecf20Sopenharmony_ci [PIPE_B] = BIT(DBUF_S1), 45378c2ecf20Sopenharmony_ci [PIPE_C] = BIT(DBUF_S2), 45388c2ecf20Sopenharmony_ci }, 45398c2ecf20Sopenharmony_ci }, 45408c2ecf20Sopenharmony_ci { 45418c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_D), 45428c2ecf20Sopenharmony_ci .dbuf_mask = { 45438c2ecf20Sopenharmony_ci [PIPE_D] = BIT(DBUF_S2) | BIT(DBUF_S1), 45448c2ecf20Sopenharmony_ci }, 45458c2ecf20Sopenharmony_ci }, 45468c2ecf20Sopenharmony_ci { 45478c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_A) | BIT(PIPE_D), 45488c2ecf20Sopenharmony_ci .dbuf_mask = { 45498c2ecf20Sopenharmony_ci [PIPE_A] = BIT(DBUF_S1), 45508c2ecf20Sopenharmony_ci [PIPE_D] = BIT(DBUF_S2), 45518c2ecf20Sopenharmony_ci }, 45528c2ecf20Sopenharmony_ci }, 45538c2ecf20Sopenharmony_ci { 45548c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_B) | BIT(PIPE_D), 45558c2ecf20Sopenharmony_ci .dbuf_mask = { 45568c2ecf20Sopenharmony_ci [PIPE_B] = BIT(DBUF_S1), 45578c2ecf20Sopenharmony_ci [PIPE_D] = BIT(DBUF_S2), 45588c2ecf20Sopenharmony_ci }, 45598c2ecf20Sopenharmony_ci }, 45608c2ecf20Sopenharmony_ci { 45618c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_A) | BIT(PIPE_B) | BIT(PIPE_D), 45628c2ecf20Sopenharmony_ci .dbuf_mask = { 45638c2ecf20Sopenharmony_ci [PIPE_A] = BIT(DBUF_S1), 45648c2ecf20Sopenharmony_ci [PIPE_B] = BIT(DBUF_S1), 45658c2ecf20Sopenharmony_ci [PIPE_D] = BIT(DBUF_S2), 45668c2ecf20Sopenharmony_ci }, 45678c2ecf20Sopenharmony_ci }, 45688c2ecf20Sopenharmony_ci { 45698c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_C) | BIT(PIPE_D), 45708c2ecf20Sopenharmony_ci .dbuf_mask = { 45718c2ecf20Sopenharmony_ci [PIPE_C] = BIT(DBUF_S1), 45728c2ecf20Sopenharmony_ci [PIPE_D] = BIT(DBUF_S2), 45738c2ecf20Sopenharmony_ci }, 45748c2ecf20Sopenharmony_ci }, 45758c2ecf20Sopenharmony_ci { 45768c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_A) | BIT(PIPE_C) | BIT(PIPE_D), 45778c2ecf20Sopenharmony_ci .dbuf_mask = { 45788c2ecf20Sopenharmony_ci [PIPE_A] = BIT(DBUF_S1), 45798c2ecf20Sopenharmony_ci [PIPE_C] = BIT(DBUF_S2), 45808c2ecf20Sopenharmony_ci [PIPE_D] = BIT(DBUF_S2), 45818c2ecf20Sopenharmony_ci }, 45828c2ecf20Sopenharmony_ci }, 45838c2ecf20Sopenharmony_ci { 45848c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_B) | BIT(PIPE_C) | BIT(PIPE_D), 45858c2ecf20Sopenharmony_ci .dbuf_mask = { 45868c2ecf20Sopenharmony_ci [PIPE_B] = BIT(DBUF_S1), 45878c2ecf20Sopenharmony_ci [PIPE_C] = BIT(DBUF_S2), 45888c2ecf20Sopenharmony_ci [PIPE_D] = BIT(DBUF_S2), 45898c2ecf20Sopenharmony_ci }, 45908c2ecf20Sopenharmony_ci }, 45918c2ecf20Sopenharmony_ci { 45928c2ecf20Sopenharmony_ci .active_pipes = BIT(PIPE_A) | BIT(PIPE_B) | BIT(PIPE_C) | BIT(PIPE_D), 45938c2ecf20Sopenharmony_ci .dbuf_mask = { 45948c2ecf20Sopenharmony_ci [PIPE_A] = BIT(DBUF_S1), 45958c2ecf20Sopenharmony_ci [PIPE_B] = BIT(DBUF_S1), 45968c2ecf20Sopenharmony_ci [PIPE_C] = BIT(DBUF_S2), 45978c2ecf20Sopenharmony_ci [PIPE_D] = BIT(DBUF_S2), 45988c2ecf20Sopenharmony_ci }, 45998c2ecf20Sopenharmony_ci }, 46008c2ecf20Sopenharmony_ci {} 46018c2ecf20Sopenharmony_ci}; 46028c2ecf20Sopenharmony_ci 46038c2ecf20Sopenharmony_cistatic u8 compute_dbuf_slices(enum pipe pipe, u8 active_pipes, 46048c2ecf20Sopenharmony_ci const struct dbuf_slice_conf_entry *dbuf_slices) 46058c2ecf20Sopenharmony_ci{ 46068c2ecf20Sopenharmony_ci int i; 46078c2ecf20Sopenharmony_ci 46088c2ecf20Sopenharmony_ci for (i = 0; i < dbuf_slices[i].active_pipes; i++) { 46098c2ecf20Sopenharmony_ci if (dbuf_slices[i].active_pipes == active_pipes) 46108c2ecf20Sopenharmony_ci return dbuf_slices[i].dbuf_mask[pipe]; 46118c2ecf20Sopenharmony_ci } 46128c2ecf20Sopenharmony_ci return 0; 46138c2ecf20Sopenharmony_ci} 46148c2ecf20Sopenharmony_ci 46158c2ecf20Sopenharmony_ci/* 46168c2ecf20Sopenharmony_ci * This function finds an entry with same enabled pipe configuration and 46178c2ecf20Sopenharmony_ci * returns correspondent DBuf slice mask as stated in BSpec for particular 46188c2ecf20Sopenharmony_ci * platform. 46198c2ecf20Sopenharmony_ci */ 46208c2ecf20Sopenharmony_cistatic u8 icl_compute_dbuf_slices(enum pipe pipe, u8 active_pipes) 46218c2ecf20Sopenharmony_ci{ 46228c2ecf20Sopenharmony_ci /* 46238c2ecf20Sopenharmony_ci * FIXME: For ICL this is still a bit unclear as prev BSpec revision 46248c2ecf20Sopenharmony_ci * required calculating "pipe ratio" in order to determine 46258c2ecf20Sopenharmony_ci * if one or two slices can be used for single pipe configurations 46268c2ecf20Sopenharmony_ci * as additional constraint to the existing table. 46278c2ecf20Sopenharmony_ci * However based on recent info, it should be not "pipe ratio" 46288c2ecf20Sopenharmony_ci * but rather ratio between pixel_rate and cdclk with additional 46298c2ecf20Sopenharmony_ci * constants, so for now we are using only table until this is 46308c2ecf20Sopenharmony_ci * clarified. Also this is the reason why crtc_state param is 46318c2ecf20Sopenharmony_ci * still here - we will need it once those additional constraints 46328c2ecf20Sopenharmony_ci * pop up. 46338c2ecf20Sopenharmony_ci */ 46348c2ecf20Sopenharmony_ci return compute_dbuf_slices(pipe, active_pipes, icl_allowed_dbufs); 46358c2ecf20Sopenharmony_ci} 46368c2ecf20Sopenharmony_ci 46378c2ecf20Sopenharmony_cistatic u8 tgl_compute_dbuf_slices(enum pipe pipe, u8 active_pipes) 46388c2ecf20Sopenharmony_ci{ 46398c2ecf20Sopenharmony_ci return compute_dbuf_slices(pipe, active_pipes, tgl_allowed_dbufs); 46408c2ecf20Sopenharmony_ci} 46418c2ecf20Sopenharmony_ci 46428c2ecf20Sopenharmony_cistatic u8 skl_compute_dbuf_slices(const struct intel_crtc_state *crtc_state, 46438c2ecf20Sopenharmony_ci u8 active_pipes) 46448c2ecf20Sopenharmony_ci{ 46458c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); 46468c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 46478c2ecf20Sopenharmony_ci enum pipe pipe = crtc->pipe; 46488c2ecf20Sopenharmony_ci 46498c2ecf20Sopenharmony_ci if (IS_GEN(dev_priv, 12)) 46508c2ecf20Sopenharmony_ci return tgl_compute_dbuf_slices(pipe, active_pipes); 46518c2ecf20Sopenharmony_ci else if (IS_GEN(dev_priv, 11)) 46528c2ecf20Sopenharmony_ci return icl_compute_dbuf_slices(pipe, active_pipes); 46538c2ecf20Sopenharmony_ci /* 46548c2ecf20Sopenharmony_ci * For anything else just return one slice yet. 46558c2ecf20Sopenharmony_ci * Should be extended for other platforms. 46568c2ecf20Sopenharmony_ci */ 46578c2ecf20Sopenharmony_ci return active_pipes & BIT(pipe) ? BIT(DBUF_S1) : 0; 46588c2ecf20Sopenharmony_ci} 46598c2ecf20Sopenharmony_ci 46608c2ecf20Sopenharmony_cistatic u64 46618c2ecf20Sopenharmony_ciskl_plane_relative_data_rate(const struct intel_crtc_state *crtc_state, 46628c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state, 46638c2ecf20Sopenharmony_ci int color_plane) 46648c2ecf20Sopenharmony_ci{ 46658c2ecf20Sopenharmony_ci struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); 46668c2ecf20Sopenharmony_ci const struct drm_framebuffer *fb = plane_state->hw.fb; 46678c2ecf20Sopenharmony_ci u32 data_rate; 46688c2ecf20Sopenharmony_ci u32 width = 0, height = 0; 46698c2ecf20Sopenharmony_ci uint_fixed_16_16_t down_scale_amount; 46708c2ecf20Sopenharmony_ci u64 rate; 46718c2ecf20Sopenharmony_ci 46728c2ecf20Sopenharmony_ci if (!plane_state->uapi.visible) 46738c2ecf20Sopenharmony_ci return 0; 46748c2ecf20Sopenharmony_ci 46758c2ecf20Sopenharmony_ci if (plane->id == PLANE_CURSOR) 46768c2ecf20Sopenharmony_ci return 0; 46778c2ecf20Sopenharmony_ci 46788c2ecf20Sopenharmony_ci if (color_plane == 1 && 46798c2ecf20Sopenharmony_ci !intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier)) 46808c2ecf20Sopenharmony_ci return 0; 46818c2ecf20Sopenharmony_ci 46828c2ecf20Sopenharmony_ci /* 46838c2ecf20Sopenharmony_ci * Src coordinates are already rotated by 270 degrees for 46848c2ecf20Sopenharmony_ci * the 90/270 degree plane rotation cases (to match the 46858c2ecf20Sopenharmony_ci * GTT mapping), hence no need to account for rotation here. 46868c2ecf20Sopenharmony_ci */ 46878c2ecf20Sopenharmony_ci width = drm_rect_width(&plane_state->uapi.src) >> 16; 46888c2ecf20Sopenharmony_ci height = drm_rect_height(&plane_state->uapi.src) >> 16; 46898c2ecf20Sopenharmony_ci 46908c2ecf20Sopenharmony_ci /* UV plane does 1/2 pixel sub-sampling */ 46918c2ecf20Sopenharmony_ci if (color_plane == 1) { 46928c2ecf20Sopenharmony_ci width /= 2; 46938c2ecf20Sopenharmony_ci height /= 2; 46948c2ecf20Sopenharmony_ci } 46958c2ecf20Sopenharmony_ci 46968c2ecf20Sopenharmony_ci data_rate = width * height; 46978c2ecf20Sopenharmony_ci 46988c2ecf20Sopenharmony_ci down_scale_amount = skl_plane_downscale_amount(crtc_state, plane_state); 46998c2ecf20Sopenharmony_ci 47008c2ecf20Sopenharmony_ci rate = mul_round_up_u32_fixed16(data_rate, down_scale_amount); 47018c2ecf20Sopenharmony_ci 47028c2ecf20Sopenharmony_ci rate *= fb->format->cpp[color_plane]; 47038c2ecf20Sopenharmony_ci return rate; 47048c2ecf20Sopenharmony_ci} 47058c2ecf20Sopenharmony_ci 47068c2ecf20Sopenharmony_cistatic u64 47078c2ecf20Sopenharmony_ciskl_get_total_relative_data_rate(struct intel_crtc_state *crtc_state, 47088c2ecf20Sopenharmony_ci u64 *plane_data_rate, 47098c2ecf20Sopenharmony_ci u64 *uv_plane_data_rate) 47108c2ecf20Sopenharmony_ci{ 47118c2ecf20Sopenharmony_ci struct intel_plane *plane; 47128c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state; 47138c2ecf20Sopenharmony_ci u64 total_data_rate = 0; 47148c2ecf20Sopenharmony_ci 47158c2ecf20Sopenharmony_ci /* Calculate and cache data rate for each plane */ 47168c2ecf20Sopenharmony_ci intel_atomic_crtc_state_for_each_plane_state(plane, plane_state, crtc_state) { 47178c2ecf20Sopenharmony_ci enum plane_id plane_id = plane->id; 47188c2ecf20Sopenharmony_ci u64 rate; 47198c2ecf20Sopenharmony_ci 47208c2ecf20Sopenharmony_ci /* packed/y */ 47218c2ecf20Sopenharmony_ci rate = skl_plane_relative_data_rate(crtc_state, plane_state, 0); 47228c2ecf20Sopenharmony_ci plane_data_rate[plane_id] = rate; 47238c2ecf20Sopenharmony_ci total_data_rate += rate; 47248c2ecf20Sopenharmony_ci 47258c2ecf20Sopenharmony_ci /* uv-plane */ 47268c2ecf20Sopenharmony_ci rate = skl_plane_relative_data_rate(crtc_state, plane_state, 1); 47278c2ecf20Sopenharmony_ci uv_plane_data_rate[plane_id] = rate; 47288c2ecf20Sopenharmony_ci total_data_rate += rate; 47298c2ecf20Sopenharmony_ci } 47308c2ecf20Sopenharmony_ci 47318c2ecf20Sopenharmony_ci return total_data_rate; 47328c2ecf20Sopenharmony_ci} 47338c2ecf20Sopenharmony_ci 47348c2ecf20Sopenharmony_cistatic u64 47358c2ecf20Sopenharmony_ciicl_get_total_relative_data_rate(struct intel_crtc_state *crtc_state, 47368c2ecf20Sopenharmony_ci u64 *plane_data_rate) 47378c2ecf20Sopenharmony_ci{ 47388c2ecf20Sopenharmony_ci struct intel_plane *plane; 47398c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state; 47408c2ecf20Sopenharmony_ci u64 total_data_rate = 0; 47418c2ecf20Sopenharmony_ci 47428c2ecf20Sopenharmony_ci /* Calculate and cache data rate for each plane */ 47438c2ecf20Sopenharmony_ci intel_atomic_crtc_state_for_each_plane_state(plane, plane_state, crtc_state) { 47448c2ecf20Sopenharmony_ci enum plane_id plane_id = plane->id; 47458c2ecf20Sopenharmony_ci u64 rate; 47468c2ecf20Sopenharmony_ci 47478c2ecf20Sopenharmony_ci if (!plane_state->planar_linked_plane) { 47488c2ecf20Sopenharmony_ci rate = skl_plane_relative_data_rate(crtc_state, plane_state, 0); 47498c2ecf20Sopenharmony_ci plane_data_rate[plane_id] = rate; 47508c2ecf20Sopenharmony_ci total_data_rate += rate; 47518c2ecf20Sopenharmony_ci } else { 47528c2ecf20Sopenharmony_ci enum plane_id y_plane_id; 47538c2ecf20Sopenharmony_ci 47548c2ecf20Sopenharmony_ci /* 47558c2ecf20Sopenharmony_ci * The slave plane might not iterate in 47568c2ecf20Sopenharmony_ci * intel_atomic_crtc_state_for_each_plane_state(), 47578c2ecf20Sopenharmony_ci * and needs the master plane state which may be 47588c2ecf20Sopenharmony_ci * NULL if we try get_new_plane_state(), so we 47598c2ecf20Sopenharmony_ci * always calculate from the master. 47608c2ecf20Sopenharmony_ci */ 47618c2ecf20Sopenharmony_ci if (plane_state->planar_slave) 47628c2ecf20Sopenharmony_ci continue; 47638c2ecf20Sopenharmony_ci 47648c2ecf20Sopenharmony_ci /* Y plane rate is calculated on the slave */ 47658c2ecf20Sopenharmony_ci rate = skl_plane_relative_data_rate(crtc_state, plane_state, 0); 47668c2ecf20Sopenharmony_ci y_plane_id = plane_state->planar_linked_plane->id; 47678c2ecf20Sopenharmony_ci plane_data_rate[y_plane_id] = rate; 47688c2ecf20Sopenharmony_ci total_data_rate += rate; 47698c2ecf20Sopenharmony_ci 47708c2ecf20Sopenharmony_ci rate = skl_plane_relative_data_rate(crtc_state, plane_state, 1); 47718c2ecf20Sopenharmony_ci plane_data_rate[plane_id] = rate; 47728c2ecf20Sopenharmony_ci total_data_rate += rate; 47738c2ecf20Sopenharmony_ci } 47748c2ecf20Sopenharmony_ci } 47758c2ecf20Sopenharmony_ci 47768c2ecf20Sopenharmony_ci return total_data_rate; 47778c2ecf20Sopenharmony_ci} 47788c2ecf20Sopenharmony_ci 47798c2ecf20Sopenharmony_cistatic const struct skl_wm_level * 47808c2ecf20Sopenharmony_ciskl_plane_wm_level(const struct intel_crtc_state *crtc_state, 47818c2ecf20Sopenharmony_ci enum plane_id plane_id, 47828c2ecf20Sopenharmony_ci int level) 47838c2ecf20Sopenharmony_ci{ 47848c2ecf20Sopenharmony_ci const struct skl_pipe_wm *pipe_wm = &crtc_state->wm.skl.optimal; 47858c2ecf20Sopenharmony_ci const struct skl_plane_wm *wm = &pipe_wm->planes[plane_id]; 47868c2ecf20Sopenharmony_ci 47878c2ecf20Sopenharmony_ci if (level == 0 && pipe_wm->use_sagv_wm) 47888c2ecf20Sopenharmony_ci return &wm->sagv_wm0; 47898c2ecf20Sopenharmony_ci 47908c2ecf20Sopenharmony_ci return &wm->wm[level]; 47918c2ecf20Sopenharmony_ci} 47928c2ecf20Sopenharmony_ci 47938c2ecf20Sopenharmony_cistatic int 47948c2ecf20Sopenharmony_ciskl_allocate_pipe_ddb(struct intel_crtc_state *crtc_state) 47958c2ecf20Sopenharmony_ci{ 47968c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); 47978c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 47988c2ecf20Sopenharmony_ci struct skl_ddb_entry *alloc = &crtc_state->wm.skl.ddb; 47998c2ecf20Sopenharmony_ci u16 alloc_size, start = 0; 48008c2ecf20Sopenharmony_ci u16 total[I915_MAX_PLANES] = {}; 48018c2ecf20Sopenharmony_ci u16 uv_total[I915_MAX_PLANES] = {}; 48028c2ecf20Sopenharmony_ci u64 total_data_rate; 48038c2ecf20Sopenharmony_ci enum plane_id plane_id; 48048c2ecf20Sopenharmony_ci int num_active; 48058c2ecf20Sopenharmony_ci u64 plane_data_rate[I915_MAX_PLANES] = {}; 48068c2ecf20Sopenharmony_ci u64 uv_plane_data_rate[I915_MAX_PLANES] = {}; 48078c2ecf20Sopenharmony_ci u32 blocks; 48088c2ecf20Sopenharmony_ci int level; 48098c2ecf20Sopenharmony_ci int ret; 48108c2ecf20Sopenharmony_ci 48118c2ecf20Sopenharmony_ci /* Clear the partitioning for disabled planes. */ 48128c2ecf20Sopenharmony_ci memset(crtc_state->wm.skl.plane_ddb_y, 0, sizeof(crtc_state->wm.skl.plane_ddb_y)); 48138c2ecf20Sopenharmony_ci memset(crtc_state->wm.skl.plane_ddb_uv, 0, sizeof(crtc_state->wm.skl.plane_ddb_uv)); 48148c2ecf20Sopenharmony_ci 48158c2ecf20Sopenharmony_ci if (!crtc_state->hw.active) { 48168c2ecf20Sopenharmony_ci struct intel_atomic_state *state = 48178c2ecf20Sopenharmony_ci to_intel_atomic_state(crtc_state->uapi.state); 48188c2ecf20Sopenharmony_ci struct intel_dbuf_state *new_dbuf_state = 48198c2ecf20Sopenharmony_ci intel_atomic_get_new_dbuf_state(state); 48208c2ecf20Sopenharmony_ci const struct intel_dbuf_state *old_dbuf_state = 48218c2ecf20Sopenharmony_ci intel_atomic_get_old_dbuf_state(state); 48228c2ecf20Sopenharmony_ci 48238c2ecf20Sopenharmony_ci /* 48248c2ecf20Sopenharmony_ci * FIXME hack to make sure we compute this sensibly when 48258c2ecf20Sopenharmony_ci * turning off all the pipes. Otherwise we leave it at 48268c2ecf20Sopenharmony_ci * whatever we had previously, and then runtime PM will 48278c2ecf20Sopenharmony_ci * mess it up by turning off all but S1. Remove this 48288c2ecf20Sopenharmony_ci * once the dbuf state computation flow becomes sane. 48298c2ecf20Sopenharmony_ci */ 48308c2ecf20Sopenharmony_ci if (new_dbuf_state->active_pipes == 0) { 48318c2ecf20Sopenharmony_ci new_dbuf_state->enabled_slices = BIT(DBUF_S1); 48328c2ecf20Sopenharmony_ci 48338c2ecf20Sopenharmony_ci if (old_dbuf_state->enabled_slices != new_dbuf_state->enabled_slices) { 48348c2ecf20Sopenharmony_ci ret = intel_atomic_serialize_global_state(&new_dbuf_state->base); 48358c2ecf20Sopenharmony_ci if (ret) 48368c2ecf20Sopenharmony_ci return ret; 48378c2ecf20Sopenharmony_ci } 48388c2ecf20Sopenharmony_ci } 48398c2ecf20Sopenharmony_ci 48408c2ecf20Sopenharmony_ci alloc->start = alloc->end = 0; 48418c2ecf20Sopenharmony_ci return 0; 48428c2ecf20Sopenharmony_ci } 48438c2ecf20Sopenharmony_ci 48448c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 11) 48458c2ecf20Sopenharmony_ci total_data_rate = 48468c2ecf20Sopenharmony_ci icl_get_total_relative_data_rate(crtc_state, 48478c2ecf20Sopenharmony_ci plane_data_rate); 48488c2ecf20Sopenharmony_ci else 48498c2ecf20Sopenharmony_ci total_data_rate = 48508c2ecf20Sopenharmony_ci skl_get_total_relative_data_rate(crtc_state, 48518c2ecf20Sopenharmony_ci plane_data_rate, 48528c2ecf20Sopenharmony_ci uv_plane_data_rate); 48538c2ecf20Sopenharmony_ci 48548c2ecf20Sopenharmony_ci ret = skl_ddb_get_pipe_allocation_limits(dev_priv, crtc_state, 48558c2ecf20Sopenharmony_ci total_data_rate, 48568c2ecf20Sopenharmony_ci alloc, &num_active); 48578c2ecf20Sopenharmony_ci if (ret) 48588c2ecf20Sopenharmony_ci return ret; 48598c2ecf20Sopenharmony_ci 48608c2ecf20Sopenharmony_ci alloc_size = skl_ddb_entry_size(alloc); 48618c2ecf20Sopenharmony_ci if (alloc_size == 0) 48628c2ecf20Sopenharmony_ci return 0; 48638c2ecf20Sopenharmony_ci 48648c2ecf20Sopenharmony_ci /* Allocate fixed number of blocks for cursor. */ 48658c2ecf20Sopenharmony_ci total[PLANE_CURSOR] = skl_cursor_allocation(crtc_state, num_active); 48668c2ecf20Sopenharmony_ci alloc_size -= total[PLANE_CURSOR]; 48678c2ecf20Sopenharmony_ci crtc_state->wm.skl.plane_ddb_y[PLANE_CURSOR].start = 48688c2ecf20Sopenharmony_ci alloc->end - total[PLANE_CURSOR]; 48698c2ecf20Sopenharmony_ci crtc_state->wm.skl.plane_ddb_y[PLANE_CURSOR].end = alloc->end; 48708c2ecf20Sopenharmony_ci 48718c2ecf20Sopenharmony_ci if (total_data_rate == 0) 48728c2ecf20Sopenharmony_ci return 0; 48738c2ecf20Sopenharmony_ci 48748c2ecf20Sopenharmony_ci /* 48758c2ecf20Sopenharmony_ci * Find the highest watermark level for which we can satisfy the block 48768c2ecf20Sopenharmony_ci * requirement of active planes. 48778c2ecf20Sopenharmony_ci */ 48788c2ecf20Sopenharmony_ci for (level = ilk_wm_max_level(dev_priv); level >= 0; level--) { 48798c2ecf20Sopenharmony_ci blocks = 0; 48808c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) { 48818c2ecf20Sopenharmony_ci const struct skl_plane_wm *wm = 48828c2ecf20Sopenharmony_ci &crtc_state->wm.skl.optimal.planes[plane_id]; 48838c2ecf20Sopenharmony_ci 48848c2ecf20Sopenharmony_ci if (plane_id == PLANE_CURSOR) { 48858c2ecf20Sopenharmony_ci if (wm->wm[level].min_ddb_alloc > total[PLANE_CURSOR]) { 48868c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, 48878c2ecf20Sopenharmony_ci wm->wm[level].min_ddb_alloc != U16_MAX); 48888c2ecf20Sopenharmony_ci blocks = U32_MAX; 48898c2ecf20Sopenharmony_ci break; 48908c2ecf20Sopenharmony_ci } 48918c2ecf20Sopenharmony_ci continue; 48928c2ecf20Sopenharmony_ci } 48938c2ecf20Sopenharmony_ci 48948c2ecf20Sopenharmony_ci blocks += wm->wm[level].min_ddb_alloc; 48958c2ecf20Sopenharmony_ci blocks += wm->uv_wm[level].min_ddb_alloc; 48968c2ecf20Sopenharmony_ci } 48978c2ecf20Sopenharmony_ci 48988c2ecf20Sopenharmony_ci if (blocks <= alloc_size) { 48998c2ecf20Sopenharmony_ci alloc_size -= blocks; 49008c2ecf20Sopenharmony_ci break; 49018c2ecf20Sopenharmony_ci } 49028c2ecf20Sopenharmony_ci } 49038c2ecf20Sopenharmony_ci 49048c2ecf20Sopenharmony_ci if (level < 0) { 49058c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 49068c2ecf20Sopenharmony_ci "Requested display configuration exceeds system DDB limitations"); 49078c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, "minimum required %d/%d\n", 49088c2ecf20Sopenharmony_ci blocks, alloc_size); 49098c2ecf20Sopenharmony_ci return -EINVAL; 49108c2ecf20Sopenharmony_ci } 49118c2ecf20Sopenharmony_ci 49128c2ecf20Sopenharmony_ci /* 49138c2ecf20Sopenharmony_ci * Grant each plane the blocks it requires at the highest achievable 49148c2ecf20Sopenharmony_ci * watermark level, plus an extra share of the leftover blocks 49158c2ecf20Sopenharmony_ci * proportional to its relative data rate. 49168c2ecf20Sopenharmony_ci */ 49178c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) { 49188c2ecf20Sopenharmony_ci const struct skl_plane_wm *wm = 49198c2ecf20Sopenharmony_ci &crtc_state->wm.skl.optimal.planes[plane_id]; 49208c2ecf20Sopenharmony_ci u64 rate; 49218c2ecf20Sopenharmony_ci u16 extra; 49228c2ecf20Sopenharmony_ci 49238c2ecf20Sopenharmony_ci if (plane_id == PLANE_CURSOR) 49248c2ecf20Sopenharmony_ci continue; 49258c2ecf20Sopenharmony_ci 49268c2ecf20Sopenharmony_ci /* 49278c2ecf20Sopenharmony_ci * We've accounted for all active planes; remaining planes are 49288c2ecf20Sopenharmony_ci * all disabled. 49298c2ecf20Sopenharmony_ci */ 49308c2ecf20Sopenharmony_ci if (total_data_rate == 0) 49318c2ecf20Sopenharmony_ci break; 49328c2ecf20Sopenharmony_ci 49338c2ecf20Sopenharmony_ci rate = plane_data_rate[plane_id]; 49348c2ecf20Sopenharmony_ci extra = min_t(u16, alloc_size, 49358c2ecf20Sopenharmony_ci DIV64_U64_ROUND_UP(alloc_size * rate, 49368c2ecf20Sopenharmony_ci total_data_rate)); 49378c2ecf20Sopenharmony_ci total[plane_id] = wm->wm[level].min_ddb_alloc + extra; 49388c2ecf20Sopenharmony_ci alloc_size -= extra; 49398c2ecf20Sopenharmony_ci total_data_rate -= rate; 49408c2ecf20Sopenharmony_ci 49418c2ecf20Sopenharmony_ci if (total_data_rate == 0) 49428c2ecf20Sopenharmony_ci break; 49438c2ecf20Sopenharmony_ci 49448c2ecf20Sopenharmony_ci rate = uv_plane_data_rate[plane_id]; 49458c2ecf20Sopenharmony_ci extra = min_t(u16, alloc_size, 49468c2ecf20Sopenharmony_ci DIV64_U64_ROUND_UP(alloc_size * rate, 49478c2ecf20Sopenharmony_ci total_data_rate)); 49488c2ecf20Sopenharmony_ci uv_total[plane_id] = wm->uv_wm[level].min_ddb_alloc + extra; 49498c2ecf20Sopenharmony_ci alloc_size -= extra; 49508c2ecf20Sopenharmony_ci total_data_rate -= rate; 49518c2ecf20Sopenharmony_ci } 49528c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, alloc_size != 0 || total_data_rate != 0); 49538c2ecf20Sopenharmony_ci 49548c2ecf20Sopenharmony_ci /* Set the actual DDB start/end points for each plane */ 49558c2ecf20Sopenharmony_ci start = alloc->start; 49568c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) { 49578c2ecf20Sopenharmony_ci struct skl_ddb_entry *plane_alloc = 49588c2ecf20Sopenharmony_ci &crtc_state->wm.skl.plane_ddb_y[plane_id]; 49598c2ecf20Sopenharmony_ci struct skl_ddb_entry *uv_plane_alloc = 49608c2ecf20Sopenharmony_ci &crtc_state->wm.skl.plane_ddb_uv[plane_id]; 49618c2ecf20Sopenharmony_ci 49628c2ecf20Sopenharmony_ci if (plane_id == PLANE_CURSOR) 49638c2ecf20Sopenharmony_ci continue; 49648c2ecf20Sopenharmony_ci 49658c2ecf20Sopenharmony_ci /* Gen11+ uses a separate plane for UV watermarks */ 49668c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, 49678c2ecf20Sopenharmony_ci INTEL_GEN(dev_priv) >= 11 && uv_total[plane_id]); 49688c2ecf20Sopenharmony_ci 49698c2ecf20Sopenharmony_ci /* Leave disabled planes at (0,0) */ 49708c2ecf20Sopenharmony_ci if (total[plane_id]) { 49718c2ecf20Sopenharmony_ci plane_alloc->start = start; 49728c2ecf20Sopenharmony_ci start += total[plane_id]; 49738c2ecf20Sopenharmony_ci plane_alloc->end = start; 49748c2ecf20Sopenharmony_ci } 49758c2ecf20Sopenharmony_ci 49768c2ecf20Sopenharmony_ci if (uv_total[plane_id]) { 49778c2ecf20Sopenharmony_ci uv_plane_alloc->start = start; 49788c2ecf20Sopenharmony_ci start += uv_total[plane_id]; 49798c2ecf20Sopenharmony_ci uv_plane_alloc->end = start; 49808c2ecf20Sopenharmony_ci } 49818c2ecf20Sopenharmony_ci } 49828c2ecf20Sopenharmony_ci 49838c2ecf20Sopenharmony_ci /* 49848c2ecf20Sopenharmony_ci * When we calculated watermark values we didn't know how high 49858c2ecf20Sopenharmony_ci * of a level we'd actually be able to hit, so we just marked 49868c2ecf20Sopenharmony_ci * all levels as "enabled." Go back now and disable the ones 49878c2ecf20Sopenharmony_ci * that aren't actually possible. 49888c2ecf20Sopenharmony_ci */ 49898c2ecf20Sopenharmony_ci for (level++; level <= ilk_wm_max_level(dev_priv); level++) { 49908c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) { 49918c2ecf20Sopenharmony_ci struct skl_plane_wm *wm = 49928c2ecf20Sopenharmony_ci &crtc_state->wm.skl.optimal.planes[plane_id]; 49938c2ecf20Sopenharmony_ci 49948c2ecf20Sopenharmony_ci /* 49958c2ecf20Sopenharmony_ci * We only disable the watermarks for each plane if 49968c2ecf20Sopenharmony_ci * they exceed the ddb allocation of said plane. This 49978c2ecf20Sopenharmony_ci * is done so that we don't end up touching cursor 49988c2ecf20Sopenharmony_ci * watermarks needlessly when some other plane reduces 49998c2ecf20Sopenharmony_ci * our max possible watermark level. 50008c2ecf20Sopenharmony_ci * 50018c2ecf20Sopenharmony_ci * Bspec has this to say about the PLANE_WM enable bit: 50028c2ecf20Sopenharmony_ci * "All the watermarks at this level for all enabled 50038c2ecf20Sopenharmony_ci * planes must be enabled before the level will be used." 50048c2ecf20Sopenharmony_ci * So this is actually safe to do. 50058c2ecf20Sopenharmony_ci */ 50068c2ecf20Sopenharmony_ci if (wm->wm[level].min_ddb_alloc > total[plane_id] || 50078c2ecf20Sopenharmony_ci wm->uv_wm[level].min_ddb_alloc > uv_total[plane_id]) 50088c2ecf20Sopenharmony_ci memset(&wm->wm[level], 0, sizeof(wm->wm[level])); 50098c2ecf20Sopenharmony_ci 50108c2ecf20Sopenharmony_ci /* 50118c2ecf20Sopenharmony_ci * Wa_1408961008:icl, ehl 50128c2ecf20Sopenharmony_ci * Underruns with WM1+ disabled 50138c2ecf20Sopenharmony_ci */ 50148c2ecf20Sopenharmony_ci if (IS_GEN(dev_priv, 11) && 50158c2ecf20Sopenharmony_ci level == 1 && wm->wm[0].plane_en) { 50168c2ecf20Sopenharmony_ci wm->wm[level].plane_res_b = wm->wm[0].plane_res_b; 50178c2ecf20Sopenharmony_ci wm->wm[level].plane_res_l = wm->wm[0].plane_res_l; 50188c2ecf20Sopenharmony_ci wm->wm[level].ignore_lines = wm->wm[0].ignore_lines; 50198c2ecf20Sopenharmony_ci } 50208c2ecf20Sopenharmony_ci } 50218c2ecf20Sopenharmony_ci } 50228c2ecf20Sopenharmony_ci 50238c2ecf20Sopenharmony_ci /* 50248c2ecf20Sopenharmony_ci * Go back and disable the transition watermark if it turns out we 50258c2ecf20Sopenharmony_ci * don't have enough DDB blocks for it. 50268c2ecf20Sopenharmony_ci */ 50278c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) { 50288c2ecf20Sopenharmony_ci struct skl_plane_wm *wm = 50298c2ecf20Sopenharmony_ci &crtc_state->wm.skl.optimal.planes[plane_id]; 50308c2ecf20Sopenharmony_ci 50318c2ecf20Sopenharmony_ci if (wm->trans_wm.plane_res_b >= total[plane_id]) 50328c2ecf20Sopenharmony_ci memset(&wm->trans_wm, 0, sizeof(wm->trans_wm)); 50338c2ecf20Sopenharmony_ci } 50348c2ecf20Sopenharmony_ci 50358c2ecf20Sopenharmony_ci return 0; 50368c2ecf20Sopenharmony_ci} 50378c2ecf20Sopenharmony_ci 50388c2ecf20Sopenharmony_ci/* 50398c2ecf20Sopenharmony_ci * The max latency should be 257 (max the punit can code is 255 and we add 2us 50408c2ecf20Sopenharmony_ci * for the read latency) and cpp should always be <= 8, so that 50418c2ecf20Sopenharmony_ci * should allow pixel_rate up to ~2 GHz which seems sufficient since max 50428c2ecf20Sopenharmony_ci * 2xcdclk is 1350 MHz and the pixel rate should never exceed that. 50438c2ecf20Sopenharmony_ci*/ 50448c2ecf20Sopenharmony_cistatic uint_fixed_16_16_t 50458c2ecf20Sopenharmony_ciskl_wm_method1(const struct drm_i915_private *dev_priv, u32 pixel_rate, 50468c2ecf20Sopenharmony_ci u8 cpp, u32 latency, u32 dbuf_block_size) 50478c2ecf20Sopenharmony_ci{ 50488c2ecf20Sopenharmony_ci u32 wm_intermediate_val; 50498c2ecf20Sopenharmony_ci uint_fixed_16_16_t ret; 50508c2ecf20Sopenharmony_ci 50518c2ecf20Sopenharmony_ci if (latency == 0) 50528c2ecf20Sopenharmony_ci return FP_16_16_MAX; 50538c2ecf20Sopenharmony_ci 50548c2ecf20Sopenharmony_ci wm_intermediate_val = latency * pixel_rate * cpp; 50558c2ecf20Sopenharmony_ci ret = div_fixed16(wm_intermediate_val, 1000 * dbuf_block_size); 50568c2ecf20Sopenharmony_ci 50578c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv)) 50588c2ecf20Sopenharmony_ci ret = add_fixed16_u32(ret, 1); 50598c2ecf20Sopenharmony_ci 50608c2ecf20Sopenharmony_ci return ret; 50618c2ecf20Sopenharmony_ci} 50628c2ecf20Sopenharmony_ci 50638c2ecf20Sopenharmony_cistatic uint_fixed_16_16_t 50648c2ecf20Sopenharmony_ciskl_wm_method2(u32 pixel_rate, u32 pipe_htotal, u32 latency, 50658c2ecf20Sopenharmony_ci uint_fixed_16_16_t plane_blocks_per_line) 50668c2ecf20Sopenharmony_ci{ 50678c2ecf20Sopenharmony_ci u32 wm_intermediate_val; 50688c2ecf20Sopenharmony_ci uint_fixed_16_16_t ret; 50698c2ecf20Sopenharmony_ci 50708c2ecf20Sopenharmony_ci if (latency == 0) 50718c2ecf20Sopenharmony_ci return FP_16_16_MAX; 50728c2ecf20Sopenharmony_ci 50738c2ecf20Sopenharmony_ci wm_intermediate_val = latency * pixel_rate; 50748c2ecf20Sopenharmony_ci wm_intermediate_val = DIV_ROUND_UP(wm_intermediate_val, 50758c2ecf20Sopenharmony_ci pipe_htotal * 1000); 50768c2ecf20Sopenharmony_ci ret = mul_u32_fixed16(wm_intermediate_val, plane_blocks_per_line); 50778c2ecf20Sopenharmony_ci return ret; 50788c2ecf20Sopenharmony_ci} 50798c2ecf20Sopenharmony_ci 50808c2ecf20Sopenharmony_cistatic uint_fixed_16_16_t 50818c2ecf20Sopenharmony_ciintel_get_linetime_us(const struct intel_crtc_state *crtc_state) 50828c2ecf20Sopenharmony_ci{ 50838c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 50848c2ecf20Sopenharmony_ci u32 pixel_rate; 50858c2ecf20Sopenharmony_ci u32 crtc_htotal; 50868c2ecf20Sopenharmony_ci uint_fixed_16_16_t linetime_us; 50878c2ecf20Sopenharmony_ci 50888c2ecf20Sopenharmony_ci if (!crtc_state->hw.active) 50898c2ecf20Sopenharmony_ci return u32_to_fixed16(0); 50908c2ecf20Sopenharmony_ci 50918c2ecf20Sopenharmony_ci pixel_rate = crtc_state->pixel_rate; 50928c2ecf20Sopenharmony_ci 50938c2ecf20Sopenharmony_ci if (drm_WARN_ON(&dev_priv->drm, pixel_rate == 0)) 50948c2ecf20Sopenharmony_ci return u32_to_fixed16(0); 50958c2ecf20Sopenharmony_ci 50968c2ecf20Sopenharmony_ci crtc_htotal = crtc_state->hw.adjusted_mode.crtc_htotal; 50978c2ecf20Sopenharmony_ci linetime_us = div_fixed16(crtc_htotal * 1000, pixel_rate); 50988c2ecf20Sopenharmony_ci 50998c2ecf20Sopenharmony_ci return linetime_us; 51008c2ecf20Sopenharmony_ci} 51018c2ecf20Sopenharmony_ci 51028c2ecf20Sopenharmony_cistatic u32 51038c2ecf20Sopenharmony_ciskl_adjusted_plane_pixel_rate(const struct intel_crtc_state *crtc_state, 51048c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state) 51058c2ecf20Sopenharmony_ci{ 51068c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 51078c2ecf20Sopenharmony_ci u64 adjusted_pixel_rate; 51088c2ecf20Sopenharmony_ci uint_fixed_16_16_t downscale_amount; 51098c2ecf20Sopenharmony_ci 51108c2ecf20Sopenharmony_ci /* Shouldn't reach here on disabled planes... */ 51118c2ecf20Sopenharmony_ci if (drm_WARN_ON(&dev_priv->drm, 51128c2ecf20Sopenharmony_ci !intel_wm_plane_visible(crtc_state, plane_state))) 51138c2ecf20Sopenharmony_ci return 0; 51148c2ecf20Sopenharmony_ci 51158c2ecf20Sopenharmony_ci /* 51168c2ecf20Sopenharmony_ci * Adjusted plane pixel rate is just the pipe's adjusted pixel rate 51178c2ecf20Sopenharmony_ci * with additional adjustments for plane-specific scaling. 51188c2ecf20Sopenharmony_ci */ 51198c2ecf20Sopenharmony_ci adjusted_pixel_rate = crtc_state->pixel_rate; 51208c2ecf20Sopenharmony_ci downscale_amount = skl_plane_downscale_amount(crtc_state, plane_state); 51218c2ecf20Sopenharmony_ci 51228c2ecf20Sopenharmony_ci return mul_round_up_u32_fixed16(adjusted_pixel_rate, 51238c2ecf20Sopenharmony_ci downscale_amount); 51248c2ecf20Sopenharmony_ci} 51258c2ecf20Sopenharmony_ci 51268c2ecf20Sopenharmony_cistatic int 51278c2ecf20Sopenharmony_ciskl_compute_wm_params(const struct intel_crtc_state *crtc_state, 51288c2ecf20Sopenharmony_ci int width, const struct drm_format_info *format, 51298c2ecf20Sopenharmony_ci u64 modifier, unsigned int rotation, 51308c2ecf20Sopenharmony_ci u32 plane_pixel_rate, struct skl_wm_params *wp, 51318c2ecf20Sopenharmony_ci int color_plane) 51328c2ecf20Sopenharmony_ci{ 51338c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); 51348c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 51358c2ecf20Sopenharmony_ci u32 interm_pbpl; 51368c2ecf20Sopenharmony_ci 51378c2ecf20Sopenharmony_ci /* only planar format has two planes */ 51388c2ecf20Sopenharmony_ci if (color_plane == 1 && 51398c2ecf20Sopenharmony_ci !intel_format_info_is_yuv_semiplanar(format, modifier)) { 51408c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 51418c2ecf20Sopenharmony_ci "Non planar format have single plane\n"); 51428c2ecf20Sopenharmony_ci return -EINVAL; 51438c2ecf20Sopenharmony_ci } 51448c2ecf20Sopenharmony_ci 51458c2ecf20Sopenharmony_ci wp->y_tiled = modifier == I915_FORMAT_MOD_Y_TILED || 51468c2ecf20Sopenharmony_ci modifier == I915_FORMAT_MOD_Yf_TILED || 51478c2ecf20Sopenharmony_ci modifier == I915_FORMAT_MOD_Y_TILED_CCS || 51488c2ecf20Sopenharmony_ci modifier == I915_FORMAT_MOD_Yf_TILED_CCS || 51498c2ecf20Sopenharmony_ci modifier == I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS || 51508c2ecf20Sopenharmony_ci modifier == I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS; 51518c2ecf20Sopenharmony_ci wp->x_tiled = modifier == I915_FORMAT_MOD_X_TILED; 51528c2ecf20Sopenharmony_ci wp->rc_surface = modifier == I915_FORMAT_MOD_Y_TILED_CCS || 51538c2ecf20Sopenharmony_ci modifier == I915_FORMAT_MOD_Yf_TILED_CCS || 51548c2ecf20Sopenharmony_ci modifier == I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS || 51558c2ecf20Sopenharmony_ci modifier == I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS; 51568c2ecf20Sopenharmony_ci wp->is_planar = intel_format_info_is_yuv_semiplanar(format, modifier); 51578c2ecf20Sopenharmony_ci 51588c2ecf20Sopenharmony_ci wp->width = width; 51598c2ecf20Sopenharmony_ci if (color_plane == 1 && wp->is_planar) 51608c2ecf20Sopenharmony_ci wp->width /= 2; 51618c2ecf20Sopenharmony_ci 51628c2ecf20Sopenharmony_ci wp->cpp = format->cpp[color_plane]; 51638c2ecf20Sopenharmony_ci wp->plane_pixel_rate = plane_pixel_rate; 51648c2ecf20Sopenharmony_ci 51658c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 11 && 51668c2ecf20Sopenharmony_ci modifier == I915_FORMAT_MOD_Yf_TILED && wp->cpp == 1) 51678c2ecf20Sopenharmony_ci wp->dbuf_block_size = 256; 51688c2ecf20Sopenharmony_ci else 51698c2ecf20Sopenharmony_ci wp->dbuf_block_size = 512; 51708c2ecf20Sopenharmony_ci 51718c2ecf20Sopenharmony_ci if (drm_rotation_90_or_270(rotation)) { 51728c2ecf20Sopenharmony_ci switch (wp->cpp) { 51738c2ecf20Sopenharmony_ci case 1: 51748c2ecf20Sopenharmony_ci wp->y_min_scanlines = 16; 51758c2ecf20Sopenharmony_ci break; 51768c2ecf20Sopenharmony_ci case 2: 51778c2ecf20Sopenharmony_ci wp->y_min_scanlines = 8; 51788c2ecf20Sopenharmony_ci break; 51798c2ecf20Sopenharmony_ci case 4: 51808c2ecf20Sopenharmony_ci wp->y_min_scanlines = 4; 51818c2ecf20Sopenharmony_ci break; 51828c2ecf20Sopenharmony_ci default: 51838c2ecf20Sopenharmony_ci MISSING_CASE(wp->cpp); 51848c2ecf20Sopenharmony_ci return -EINVAL; 51858c2ecf20Sopenharmony_ci } 51868c2ecf20Sopenharmony_ci } else { 51878c2ecf20Sopenharmony_ci wp->y_min_scanlines = 4; 51888c2ecf20Sopenharmony_ci } 51898c2ecf20Sopenharmony_ci 51908c2ecf20Sopenharmony_ci if (skl_needs_memory_bw_wa(dev_priv)) 51918c2ecf20Sopenharmony_ci wp->y_min_scanlines *= 2; 51928c2ecf20Sopenharmony_ci 51938c2ecf20Sopenharmony_ci wp->plane_bytes_per_line = wp->width * wp->cpp; 51948c2ecf20Sopenharmony_ci if (wp->y_tiled) { 51958c2ecf20Sopenharmony_ci interm_pbpl = DIV_ROUND_UP(wp->plane_bytes_per_line * 51968c2ecf20Sopenharmony_ci wp->y_min_scanlines, 51978c2ecf20Sopenharmony_ci wp->dbuf_block_size); 51988c2ecf20Sopenharmony_ci 51998c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv)) 52008c2ecf20Sopenharmony_ci interm_pbpl++; 52018c2ecf20Sopenharmony_ci 52028c2ecf20Sopenharmony_ci wp->plane_blocks_per_line = div_fixed16(interm_pbpl, 52038c2ecf20Sopenharmony_ci wp->y_min_scanlines); 52048c2ecf20Sopenharmony_ci } else { 52058c2ecf20Sopenharmony_ci interm_pbpl = DIV_ROUND_UP(wp->plane_bytes_per_line, 52068c2ecf20Sopenharmony_ci wp->dbuf_block_size); 52078c2ecf20Sopenharmony_ci 52088c2ecf20Sopenharmony_ci if (!wp->x_tiled || 52098c2ecf20Sopenharmony_ci INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv)) 52108c2ecf20Sopenharmony_ci interm_pbpl++; 52118c2ecf20Sopenharmony_ci 52128c2ecf20Sopenharmony_ci wp->plane_blocks_per_line = u32_to_fixed16(interm_pbpl); 52138c2ecf20Sopenharmony_ci } 52148c2ecf20Sopenharmony_ci 52158c2ecf20Sopenharmony_ci wp->y_tile_minimum = mul_u32_fixed16(wp->y_min_scanlines, 52168c2ecf20Sopenharmony_ci wp->plane_blocks_per_line); 52178c2ecf20Sopenharmony_ci 52188c2ecf20Sopenharmony_ci wp->linetime_us = fixed16_to_u32_round_up( 52198c2ecf20Sopenharmony_ci intel_get_linetime_us(crtc_state)); 52208c2ecf20Sopenharmony_ci 52218c2ecf20Sopenharmony_ci return 0; 52228c2ecf20Sopenharmony_ci} 52238c2ecf20Sopenharmony_ci 52248c2ecf20Sopenharmony_cistatic int 52258c2ecf20Sopenharmony_ciskl_compute_plane_wm_params(const struct intel_crtc_state *crtc_state, 52268c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state, 52278c2ecf20Sopenharmony_ci struct skl_wm_params *wp, int color_plane) 52288c2ecf20Sopenharmony_ci{ 52298c2ecf20Sopenharmony_ci const struct drm_framebuffer *fb = plane_state->hw.fb; 52308c2ecf20Sopenharmony_ci int width; 52318c2ecf20Sopenharmony_ci 52328c2ecf20Sopenharmony_ci /* 52338c2ecf20Sopenharmony_ci * Src coordinates are already rotated by 270 degrees for 52348c2ecf20Sopenharmony_ci * the 90/270 degree plane rotation cases (to match the 52358c2ecf20Sopenharmony_ci * GTT mapping), hence no need to account for rotation here. 52368c2ecf20Sopenharmony_ci */ 52378c2ecf20Sopenharmony_ci width = drm_rect_width(&plane_state->uapi.src) >> 16; 52388c2ecf20Sopenharmony_ci 52398c2ecf20Sopenharmony_ci return skl_compute_wm_params(crtc_state, width, 52408c2ecf20Sopenharmony_ci fb->format, fb->modifier, 52418c2ecf20Sopenharmony_ci plane_state->hw.rotation, 52428c2ecf20Sopenharmony_ci skl_adjusted_plane_pixel_rate(crtc_state, plane_state), 52438c2ecf20Sopenharmony_ci wp, color_plane); 52448c2ecf20Sopenharmony_ci} 52458c2ecf20Sopenharmony_ci 52468c2ecf20Sopenharmony_cistatic bool skl_wm_has_lines(struct drm_i915_private *dev_priv, int level) 52478c2ecf20Sopenharmony_ci{ 52488c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv)) 52498c2ecf20Sopenharmony_ci return true; 52508c2ecf20Sopenharmony_ci 52518c2ecf20Sopenharmony_ci /* The number of lines are ignored for the level 0 watermark. */ 52528c2ecf20Sopenharmony_ci return level > 0; 52538c2ecf20Sopenharmony_ci} 52548c2ecf20Sopenharmony_ci 52558c2ecf20Sopenharmony_cistatic void skl_compute_plane_wm(const struct intel_crtc_state *crtc_state, 52568c2ecf20Sopenharmony_ci int level, 52578c2ecf20Sopenharmony_ci unsigned int latency, 52588c2ecf20Sopenharmony_ci const struct skl_wm_params *wp, 52598c2ecf20Sopenharmony_ci const struct skl_wm_level *result_prev, 52608c2ecf20Sopenharmony_ci struct skl_wm_level *result /* out */) 52618c2ecf20Sopenharmony_ci{ 52628c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 52638c2ecf20Sopenharmony_ci uint_fixed_16_16_t method1, method2; 52648c2ecf20Sopenharmony_ci uint_fixed_16_16_t selected_result; 52658c2ecf20Sopenharmony_ci u32 res_blocks, res_lines, min_ddb_alloc = 0; 52668c2ecf20Sopenharmony_ci 52678c2ecf20Sopenharmony_ci if (latency == 0) { 52688c2ecf20Sopenharmony_ci /* reject it */ 52698c2ecf20Sopenharmony_ci result->min_ddb_alloc = U16_MAX; 52708c2ecf20Sopenharmony_ci return; 52718c2ecf20Sopenharmony_ci } 52728c2ecf20Sopenharmony_ci 52738c2ecf20Sopenharmony_ci /* 52748c2ecf20Sopenharmony_ci * WaIncreaseLatencyIPCEnabled: kbl,cfl 52758c2ecf20Sopenharmony_ci * Display WA #1141: kbl,cfl 52768c2ecf20Sopenharmony_ci */ 52778c2ecf20Sopenharmony_ci if ((IS_KABYLAKE(dev_priv) || 52788c2ecf20Sopenharmony_ci IS_COFFEELAKE(dev_priv) || 52798c2ecf20Sopenharmony_ci IS_COMETLAKE(dev_priv)) && 52808c2ecf20Sopenharmony_ci dev_priv->ipc_enabled) 52818c2ecf20Sopenharmony_ci latency += 4; 52828c2ecf20Sopenharmony_ci 52838c2ecf20Sopenharmony_ci if (skl_needs_memory_bw_wa(dev_priv) && wp->x_tiled) 52848c2ecf20Sopenharmony_ci latency += 15; 52858c2ecf20Sopenharmony_ci 52868c2ecf20Sopenharmony_ci method1 = skl_wm_method1(dev_priv, wp->plane_pixel_rate, 52878c2ecf20Sopenharmony_ci wp->cpp, latency, wp->dbuf_block_size); 52888c2ecf20Sopenharmony_ci method2 = skl_wm_method2(wp->plane_pixel_rate, 52898c2ecf20Sopenharmony_ci crtc_state->hw.adjusted_mode.crtc_htotal, 52908c2ecf20Sopenharmony_ci latency, 52918c2ecf20Sopenharmony_ci wp->plane_blocks_per_line); 52928c2ecf20Sopenharmony_ci 52938c2ecf20Sopenharmony_ci if (wp->y_tiled) { 52948c2ecf20Sopenharmony_ci selected_result = max_fixed16(method2, wp->y_tile_minimum); 52958c2ecf20Sopenharmony_ci } else { 52968c2ecf20Sopenharmony_ci if ((wp->cpp * crtc_state->hw.adjusted_mode.crtc_htotal / 52978c2ecf20Sopenharmony_ci wp->dbuf_block_size < 1) && 52988c2ecf20Sopenharmony_ci (wp->plane_bytes_per_line / wp->dbuf_block_size < 1)) { 52998c2ecf20Sopenharmony_ci selected_result = method2; 53008c2ecf20Sopenharmony_ci } else if (latency >= wp->linetime_us) { 53018c2ecf20Sopenharmony_ci if (IS_GEN(dev_priv, 9) && 53028c2ecf20Sopenharmony_ci !IS_GEMINILAKE(dev_priv)) 53038c2ecf20Sopenharmony_ci selected_result = min_fixed16(method1, method2); 53048c2ecf20Sopenharmony_ci else 53058c2ecf20Sopenharmony_ci selected_result = method2; 53068c2ecf20Sopenharmony_ci } else { 53078c2ecf20Sopenharmony_ci selected_result = method1; 53088c2ecf20Sopenharmony_ci } 53098c2ecf20Sopenharmony_ci } 53108c2ecf20Sopenharmony_ci 53118c2ecf20Sopenharmony_ci res_blocks = fixed16_to_u32_round_up(selected_result) + 1; 53128c2ecf20Sopenharmony_ci res_lines = div_round_up_fixed16(selected_result, 53138c2ecf20Sopenharmony_ci wp->plane_blocks_per_line); 53148c2ecf20Sopenharmony_ci 53158c2ecf20Sopenharmony_ci if (IS_GEN9_BC(dev_priv) || IS_BROXTON(dev_priv)) { 53168c2ecf20Sopenharmony_ci /* Display WA #1125: skl,bxt,kbl */ 53178c2ecf20Sopenharmony_ci if (level == 0 && wp->rc_surface) 53188c2ecf20Sopenharmony_ci res_blocks += 53198c2ecf20Sopenharmony_ci fixed16_to_u32_round_up(wp->y_tile_minimum); 53208c2ecf20Sopenharmony_ci 53218c2ecf20Sopenharmony_ci /* Display WA #1126: skl,bxt,kbl */ 53228c2ecf20Sopenharmony_ci if (level >= 1 && level <= 7) { 53238c2ecf20Sopenharmony_ci if (wp->y_tiled) { 53248c2ecf20Sopenharmony_ci res_blocks += 53258c2ecf20Sopenharmony_ci fixed16_to_u32_round_up(wp->y_tile_minimum); 53268c2ecf20Sopenharmony_ci res_lines += wp->y_min_scanlines; 53278c2ecf20Sopenharmony_ci } else { 53288c2ecf20Sopenharmony_ci res_blocks++; 53298c2ecf20Sopenharmony_ci } 53308c2ecf20Sopenharmony_ci 53318c2ecf20Sopenharmony_ci /* 53328c2ecf20Sopenharmony_ci * Make sure result blocks for higher latency levels are 53338c2ecf20Sopenharmony_ci * atleast as high as level below the current level. 53348c2ecf20Sopenharmony_ci * Assumption in DDB algorithm optimization for special 53358c2ecf20Sopenharmony_ci * cases. Also covers Display WA #1125 for RC. 53368c2ecf20Sopenharmony_ci */ 53378c2ecf20Sopenharmony_ci if (result_prev->plane_res_b > res_blocks) 53388c2ecf20Sopenharmony_ci res_blocks = result_prev->plane_res_b; 53398c2ecf20Sopenharmony_ci } 53408c2ecf20Sopenharmony_ci } 53418c2ecf20Sopenharmony_ci 53428c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 11) { 53438c2ecf20Sopenharmony_ci if (wp->y_tiled) { 53448c2ecf20Sopenharmony_ci int extra_lines; 53458c2ecf20Sopenharmony_ci 53468c2ecf20Sopenharmony_ci if (res_lines % wp->y_min_scanlines == 0) 53478c2ecf20Sopenharmony_ci extra_lines = wp->y_min_scanlines; 53488c2ecf20Sopenharmony_ci else 53498c2ecf20Sopenharmony_ci extra_lines = wp->y_min_scanlines * 2 - 53508c2ecf20Sopenharmony_ci res_lines % wp->y_min_scanlines; 53518c2ecf20Sopenharmony_ci 53528c2ecf20Sopenharmony_ci min_ddb_alloc = mul_round_up_u32_fixed16(res_lines + extra_lines, 53538c2ecf20Sopenharmony_ci wp->plane_blocks_per_line); 53548c2ecf20Sopenharmony_ci } else { 53558c2ecf20Sopenharmony_ci min_ddb_alloc = res_blocks + 53568c2ecf20Sopenharmony_ci DIV_ROUND_UP(res_blocks, 10); 53578c2ecf20Sopenharmony_ci } 53588c2ecf20Sopenharmony_ci } 53598c2ecf20Sopenharmony_ci 53608c2ecf20Sopenharmony_ci if (!skl_wm_has_lines(dev_priv, level)) 53618c2ecf20Sopenharmony_ci res_lines = 0; 53628c2ecf20Sopenharmony_ci 53638c2ecf20Sopenharmony_ci if (res_lines > 31) { 53648c2ecf20Sopenharmony_ci /* reject it */ 53658c2ecf20Sopenharmony_ci result->min_ddb_alloc = U16_MAX; 53668c2ecf20Sopenharmony_ci return; 53678c2ecf20Sopenharmony_ci } 53688c2ecf20Sopenharmony_ci 53698c2ecf20Sopenharmony_ci /* 53708c2ecf20Sopenharmony_ci * If res_lines is valid, assume we can use this watermark level 53718c2ecf20Sopenharmony_ci * for now. We'll come back and disable it after we calculate the 53728c2ecf20Sopenharmony_ci * DDB allocation if it turns out we don't actually have enough 53738c2ecf20Sopenharmony_ci * blocks to satisfy it. 53748c2ecf20Sopenharmony_ci */ 53758c2ecf20Sopenharmony_ci result->plane_res_b = res_blocks; 53768c2ecf20Sopenharmony_ci result->plane_res_l = res_lines; 53778c2ecf20Sopenharmony_ci /* Bspec says: value >= plane ddb allocation -> invalid, hence the +1 here */ 53788c2ecf20Sopenharmony_ci result->min_ddb_alloc = max(min_ddb_alloc, res_blocks) + 1; 53798c2ecf20Sopenharmony_ci result->plane_en = true; 53808c2ecf20Sopenharmony_ci} 53818c2ecf20Sopenharmony_ci 53828c2ecf20Sopenharmony_cistatic void 53838c2ecf20Sopenharmony_ciskl_compute_wm_levels(const struct intel_crtc_state *crtc_state, 53848c2ecf20Sopenharmony_ci const struct skl_wm_params *wm_params, 53858c2ecf20Sopenharmony_ci struct skl_wm_level *levels) 53868c2ecf20Sopenharmony_ci{ 53878c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 53888c2ecf20Sopenharmony_ci int level, max_level = ilk_wm_max_level(dev_priv); 53898c2ecf20Sopenharmony_ci struct skl_wm_level *result_prev = &levels[0]; 53908c2ecf20Sopenharmony_ci 53918c2ecf20Sopenharmony_ci for (level = 0; level <= max_level; level++) { 53928c2ecf20Sopenharmony_ci struct skl_wm_level *result = &levels[level]; 53938c2ecf20Sopenharmony_ci unsigned int latency = dev_priv->wm.skl_latency[level]; 53948c2ecf20Sopenharmony_ci 53958c2ecf20Sopenharmony_ci skl_compute_plane_wm(crtc_state, level, latency, 53968c2ecf20Sopenharmony_ci wm_params, result_prev, result); 53978c2ecf20Sopenharmony_ci 53988c2ecf20Sopenharmony_ci result_prev = result; 53998c2ecf20Sopenharmony_ci } 54008c2ecf20Sopenharmony_ci} 54018c2ecf20Sopenharmony_ci 54028c2ecf20Sopenharmony_cistatic void tgl_compute_sagv_wm(const struct intel_crtc_state *crtc_state, 54038c2ecf20Sopenharmony_ci const struct skl_wm_params *wm_params, 54048c2ecf20Sopenharmony_ci struct skl_plane_wm *plane_wm) 54058c2ecf20Sopenharmony_ci{ 54068c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 54078c2ecf20Sopenharmony_ci struct skl_wm_level *sagv_wm = &plane_wm->sagv_wm0; 54088c2ecf20Sopenharmony_ci struct skl_wm_level *levels = plane_wm->wm; 54098c2ecf20Sopenharmony_ci unsigned int latency = dev_priv->wm.skl_latency[0] + dev_priv->sagv_block_time_us; 54108c2ecf20Sopenharmony_ci 54118c2ecf20Sopenharmony_ci skl_compute_plane_wm(crtc_state, 0, latency, 54128c2ecf20Sopenharmony_ci wm_params, &levels[0], 54138c2ecf20Sopenharmony_ci sagv_wm); 54148c2ecf20Sopenharmony_ci} 54158c2ecf20Sopenharmony_ci 54168c2ecf20Sopenharmony_cistatic void skl_compute_transition_wm(const struct intel_crtc_state *crtc_state, 54178c2ecf20Sopenharmony_ci const struct skl_wm_params *wp, 54188c2ecf20Sopenharmony_ci struct skl_plane_wm *wm) 54198c2ecf20Sopenharmony_ci{ 54208c2ecf20Sopenharmony_ci struct drm_device *dev = crtc_state->uapi.crtc->dev; 54218c2ecf20Sopenharmony_ci const struct drm_i915_private *dev_priv = to_i915(dev); 54228c2ecf20Sopenharmony_ci u16 trans_min, trans_amount, trans_y_tile_min; 54238c2ecf20Sopenharmony_ci u16 wm0_sel_res_b, trans_offset_b, res_blocks; 54248c2ecf20Sopenharmony_ci 54258c2ecf20Sopenharmony_ci /* Transition WM don't make any sense if ipc is disabled */ 54268c2ecf20Sopenharmony_ci if (!dev_priv->ipc_enabled) 54278c2ecf20Sopenharmony_ci return; 54288c2ecf20Sopenharmony_ci 54298c2ecf20Sopenharmony_ci /* 54308c2ecf20Sopenharmony_ci * WaDisableTWM:skl,kbl,cfl,bxt 54318c2ecf20Sopenharmony_ci * Transition WM are not recommended by HW team for GEN9 54328c2ecf20Sopenharmony_ci */ 54338c2ecf20Sopenharmony_ci if (IS_GEN9_BC(dev_priv) || IS_BROXTON(dev_priv)) 54348c2ecf20Sopenharmony_ci return; 54358c2ecf20Sopenharmony_ci 54368c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 11) 54378c2ecf20Sopenharmony_ci trans_min = 4; 54388c2ecf20Sopenharmony_ci else 54398c2ecf20Sopenharmony_ci trans_min = 14; 54408c2ecf20Sopenharmony_ci 54418c2ecf20Sopenharmony_ci /* Display WA #1140: glk,cnl */ 54428c2ecf20Sopenharmony_ci if (IS_CANNONLAKE(dev_priv) || IS_GEMINILAKE(dev_priv)) 54438c2ecf20Sopenharmony_ci trans_amount = 0; 54448c2ecf20Sopenharmony_ci else 54458c2ecf20Sopenharmony_ci trans_amount = 10; /* This is configurable amount */ 54468c2ecf20Sopenharmony_ci 54478c2ecf20Sopenharmony_ci trans_offset_b = trans_min + trans_amount; 54488c2ecf20Sopenharmony_ci 54498c2ecf20Sopenharmony_ci /* 54508c2ecf20Sopenharmony_ci * The spec asks for Selected Result Blocks for wm0 (the real value), 54518c2ecf20Sopenharmony_ci * not Result Blocks (the integer value). Pay attention to the capital 54528c2ecf20Sopenharmony_ci * letters. The value wm_l0->plane_res_b is actually Result Blocks, but 54538c2ecf20Sopenharmony_ci * since Result Blocks is the ceiling of Selected Result Blocks plus 1, 54548c2ecf20Sopenharmony_ci * and since we later will have to get the ceiling of the sum in the 54558c2ecf20Sopenharmony_ci * transition watermarks calculation, we can just pretend Selected 54568c2ecf20Sopenharmony_ci * Result Blocks is Result Blocks minus 1 and it should work for the 54578c2ecf20Sopenharmony_ci * current platforms. 54588c2ecf20Sopenharmony_ci */ 54598c2ecf20Sopenharmony_ci wm0_sel_res_b = wm->wm[0].plane_res_b - 1; 54608c2ecf20Sopenharmony_ci 54618c2ecf20Sopenharmony_ci if (wp->y_tiled) { 54628c2ecf20Sopenharmony_ci trans_y_tile_min = 54638c2ecf20Sopenharmony_ci (u16)mul_round_up_u32_fixed16(2, wp->y_tile_minimum); 54648c2ecf20Sopenharmony_ci res_blocks = max(wm0_sel_res_b, trans_y_tile_min) + 54658c2ecf20Sopenharmony_ci trans_offset_b; 54668c2ecf20Sopenharmony_ci } else { 54678c2ecf20Sopenharmony_ci res_blocks = wm0_sel_res_b + trans_offset_b; 54688c2ecf20Sopenharmony_ci } 54698c2ecf20Sopenharmony_ci 54708c2ecf20Sopenharmony_ci /* 54718c2ecf20Sopenharmony_ci * Just assume we can enable the transition watermark. After 54728c2ecf20Sopenharmony_ci * computing the DDB we'll come back and disable it if that 54738c2ecf20Sopenharmony_ci * assumption turns out to be false. 54748c2ecf20Sopenharmony_ci */ 54758c2ecf20Sopenharmony_ci wm->trans_wm.plane_res_b = res_blocks + 1; 54768c2ecf20Sopenharmony_ci wm->trans_wm.plane_en = true; 54778c2ecf20Sopenharmony_ci} 54788c2ecf20Sopenharmony_ci 54798c2ecf20Sopenharmony_cistatic int skl_build_plane_wm_single(struct intel_crtc_state *crtc_state, 54808c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state, 54818c2ecf20Sopenharmony_ci enum plane_id plane_id, int color_plane) 54828c2ecf20Sopenharmony_ci{ 54838c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); 54848c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 54858c2ecf20Sopenharmony_ci struct skl_plane_wm *wm = &crtc_state->wm.skl.optimal.planes[plane_id]; 54868c2ecf20Sopenharmony_ci struct skl_wm_params wm_params; 54878c2ecf20Sopenharmony_ci int ret; 54888c2ecf20Sopenharmony_ci 54898c2ecf20Sopenharmony_ci ret = skl_compute_plane_wm_params(crtc_state, plane_state, 54908c2ecf20Sopenharmony_ci &wm_params, color_plane); 54918c2ecf20Sopenharmony_ci if (ret) 54928c2ecf20Sopenharmony_ci return ret; 54938c2ecf20Sopenharmony_ci 54948c2ecf20Sopenharmony_ci skl_compute_wm_levels(crtc_state, &wm_params, wm->wm); 54958c2ecf20Sopenharmony_ci 54968c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 12) 54978c2ecf20Sopenharmony_ci tgl_compute_sagv_wm(crtc_state, &wm_params, wm); 54988c2ecf20Sopenharmony_ci 54998c2ecf20Sopenharmony_ci skl_compute_transition_wm(crtc_state, &wm_params, wm); 55008c2ecf20Sopenharmony_ci 55018c2ecf20Sopenharmony_ci return 0; 55028c2ecf20Sopenharmony_ci} 55038c2ecf20Sopenharmony_ci 55048c2ecf20Sopenharmony_cistatic int skl_build_plane_wm_uv(struct intel_crtc_state *crtc_state, 55058c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state, 55068c2ecf20Sopenharmony_ci enum plane_id plane_id) 55078c2ecf20Sopenharmony_ci{ 55088c2ecf20Sopenharmony_ci struct skl_plane_wm *wm = &crtc_state->wm.skl.optimal.planes[plane_id]; 55098c2ecf20Sopenharmony_ci struct skl_wm_params wm_params; 55108c2ecf20Sopenharmony_ci int ret; 55118c2ecf20Sopenharmony_ci 55128c2ecf20Sopenharmony_ci wm->is_planar = true; 55138c2ecf20Sopenharmony_ci 55148c2ecf20Sopenharmony_ci /* uv plane watermarks must also be validated for NV12/Planar */ 55158c2ecf20Sopenharmony_ci ret = skl_compute_plane_wm_params(crtc_state, plane_state, 55168c2ecf20Sopenharmony_ci &wm_params, 1); 55178c2ecf20Sopenharmony_ci if (ret) 55188c2ecf20Sopenharmony_ci return ret; 55198c2ecf20Sopenharmony_ci 55208c2ecf20Sopenharmony_ci skl_compute_wm_levels(crtc_state, &wm_params, wm->uv_wm); 55218c2ecf20Sopenharmony_ci 55228c2ecf20Sopenharmony_ci return 0; 55238c2ecf20Sopenharmony_ci} 55248c2ecf20Sopenharmony_ci 55258c2ecf20Sopenharmony_cistatic int skl_build_plane_wm(struct intel_crtc_state *crtc_state, 55268c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state) 55278c2ecf20Sopenharmony_ci{ 55288c2ecf20Sopenharmony_ci struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); 55298c2ecf20Sopenharmony_ci const struct drm_framebuffer *fb = plane_state->hw.fb; 55308c2ecf20Sopenharmony_ci enum plane_id plane_id = plane->id; 55318c2ecf20Sopenharmony_ci int ret; 55328c2ecf20Sopenharmony_ci 55338c2ecf20Sopenharmony_ci if (!intel_wm_plane_visible(crtc_state, plane_state)) 55348c2ecf20Sopenharmony_ci return 0; 55358c2ecf20Sopenharmony_ci 55368c2ecf20Sopenharmony_ci ret = skl_build_plane_wm_single(crtc_state, plane_state, 55378c2ecf20Sopenharmony_ci plane_id, 0); 55388c2ecf20Sopenharmony_ci if (ret) 55398c2ecf20Sopenharmony_ci return ret; 55408c2ecf20Sopenharmony_ci 55418c2ecf20Sopenharmony_ci if (fb->format->is_yuv && fb->format->num_planes > 1) { 55428c2ecf20Sopenharmony_ci ret = skl_build_plane_wm_uv(crtc_state, plane_state, 55438c2ecf20Sopenharmony_ci plane_id); 55448c2ecf20Sopenharmony_ci if (ret) 55458c2ecf20Sopenharmony_ci return ret; 55468c2ecf20Sopenharmony_ci } 55478c2ecf20Sopenharmony_ci 55488c2ecf20Sopenharmony_ci return 0; 55498c2ecf20Sopenharmony_ci} 55508c2ecf20Sopenharmony_ci 55518c2ecf20Sopenharmony_cistatic int icl_build_plane_wm(struct intel_crtc_state *crtc_state, 55528c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state) 55538c2ecf20Sopenharmony_ci{ 55548c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 55558c2ecf20Sopenharmony_ci enum plane_id plane_id = to_intel_plane(plane_state->uapi.plane)->id; 55568c2ecf20Sopenharmony_ci int ret; 55578c2ecf20Sopenharmony_ci 55588c2ecf20Sopenharmony_ci /* Watermarks calculated in master */ 55598c2ecf20Sopenharmony_ci if (plane_state->planar_slave) 55608c2ecf20Sopenharmony_ci return 0; 55618c2ecf20Sopenharmony_ci 55628c2ecf20Sopenharmony_ci if (plane_state->planar_linked_plane) { 55638c2ecf20Sopenharmony_ci const struct drm_framebuffer *fb = plane_state->hw.fb; 55648c2ecf20Sopenharmony_ci enum plane_id y_plane_id = plane_state->planar_linked_plane->id; 55658c2ecf20Sopenharmony_ci 55668c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, 55678c2ecf20Sopenharmony_ci !intel_wm_plane_visible(crtc_state, plane_state)); 55688c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, !fb->format->is_yuv || 55698c2ecf20Sopenharmony_ci fb->format->num_planes == 1); 55708c2ecf20Sopenharmony_ci 55718c2ecf20Sopenharmony_ci ret = skl_build_plane_wm_single(crtc_state, plane_state, 55728c2ecf20Sopenharmony_ci y_plane_id, 0); 55738c2ecf20Sopenharmony_ci if (ret) 55748c2ecf20Sopenharmony_ci return ret; 55758c2ecf20Sopenharmony_ci 55768c2ecf20Sopenharmony_ci ret = skl_build_plane_wm_single(crtc_state, plane_state, 55778c2ecf20Sopenharmony_ci plane_id, 1); 55788c2ecf20Sopenharmony_ci if (ret) 55798c2ecf20Sopenharmony_ci return ret; 55808c2ecf20Sopenharmony_ci } else if (intel_wm_plane_visible(crtc_state, plane_state)) { 55818c2ecf20Sopenharmony_ci ret = skl_build_plane_wm_single(crtc_state, plane_state, 55828c2ecf20Sopenharmony_ci plane_id, 0); 55838c2ecf20Sopenharmony_ci if (ret) 55848c2ecf20Sopenharmony_ci return ret; 55858c2ecf20Sopenharmony_ci } 55868c2ecf20Sopenharmony_ci 55878c2ecf20Sopenharmony_ci return 0; 55888c2ecf20Sopenharmony_ci} 55898c2ecf20Sopenharmony_ci 55908c2ecf20Sopenharmony_cistatic int skl_build_pipe_wm(struct intel_crtc_state *crtc_state) 55918c2ecf20Sopenharmony_ci{ 55928c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); 55938c2ecf20Sopenharmony_ci struct skl_pipe_wm *pipe_wm = &crtc_state->wm.skl.optimal; 55948c2ecf20Sopenharmony_ci struct intel_plane *plane; 55958c2ecf20Sopenharmony_ci const struct intel_plane_state *plane_state; 55968c2ecf20Sopenharmony_ci int ret; 55978c2ecf20Sopenharmony_ci 55988c2ecf20Sopenharmony_ci /* 55998c2ecf20Sopenharmony_ci * We'll only calculate watermarks for planes that are actually 56008c2ecf20Sopenharmony_ci * enabled, so make sure all other planes are set as disabled. 56018c2ecf20Sopenharmony_ci */ 56028c2ecf20Sopenharmony_ci memset(pipe_wm->planes, 0, sizeof(pipe_wm->planes)); 56038c2ecf20Sopenharmony_ci 56048c2ecf20Sopenharmony_ci intel_atomic_crtc_state_for_each_plane_state(plane, plane_state, 56058c2ecf20Sopenharmony_ci crtc_state) { 56068c2ecf20Sopenharmony_ci 56078c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 11) 56088c2ecf20Sopenharmony_ci ret = icl_build_plane_wm(crtc_state, plane_state); 56098c2ecf20Sopenharmony_ci else 56108c2ecf20Sopenharmony_ci ret = skl_build_plane_wm(crtc_state, plane_state); 56118c2ecf20Sopenharmony_ci if (ret) 56128c2ecf20Sopenharmony_ci return ret; 56138c2ecf20Sopenharmony_ci } 56148c2ecf20Sopenharmony_ci 56158c2ecf20Sopenharmony_ci return 0; 56168c2ecf20Sopenharmony_ci} 56178c2ecf20Sopenharmony_ci 56188c2ecf20Sopenharmony_cistatic void skl_ddb_entry_write(struct drm_i915_private *dev_priv, 56198c2ecf20Sopenharmony_ci i915_reg_t reg, 56208c2ecf20Sopenharmony_ci const struct skl_ddb_entry *entry) 56218c2ecf20Sopenharmony_ci{ 56228c2ecf20Sopenharmony_ci if (entry->end) 56238c2ecf20Sopenharmony_ci intel_de_write_fw(dev_priv, reg, 56248c2ecf20Sopenharmony_ci (entry->end - 1) << 16 | entry->start); 56258c2ecf20Sopenharmony_ci else 56268c2ecf20Sopenharmony_ci intel_de_write_fw(dev_priv, reg, 0); 56278c2ecf20Sopenharmony_ci} 56288c2ecf20Sopenharmony_ci 56298c2ecf20Sopenharmony_cistatic void skl_write_wm_level(struct drm_i915_private *dev_priv, 56308c2ecf20Sopenharmony_ci i915_reg_t reg, 56318c2ecf20Sopenharmony_ci const struct skl_wm_level *level) 56328c2ecf20Sopenharmony_ci{ 56338c2ecf20Sopenharmony_ci u32 val = 0; 56348c2ecf20Sopenharmony_ci 56358c2ecf20Sopenharmony_ci if (level->plane_en) 56368c2ecf20Sopenharmony_ci val |= PLANE_WM_EN; 56378c2ecf20Sopenharmony_ci if (level->ignore_lines) 56388c2ecf20Sopenharmony_ci val |= PLANE_WM_IGNORE_LINES; 56398c2ecf20Sopenharmony_ci val |= level->plane_res_b; 56408c2ecf20Sopenharmony_ci val |= level->plane_res_l << PLANE_WM_LINES_SHIFT; 56418c2ecf20Sopenharmony_ci 56428c2ecf20Sopenharmony_ci intel_de_write_fw(dev_priv, reg, val); 56438c2ecf20Sopenharmony_ci} 56448c2ecf20Sopenharmony_ci 56458c2ecf20Sopenharmony_civoid skl_write_plane_wm(struct intel_plane *plane, 56468c2ecf20Sopenharmony_ci const struct intel_crtc_state *crtc_state) 56478c2ecf20Sopenharmony_ci{ 56488c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(plane->base.dev); 56498c2ecf20Sopenharmony_ci int level, max_level = ilk_wm_max_level(dev_priv); 56508c2ecf20Sopenharmony_ci enum plane_id plane_id = plane->id; 56518c2ecf20Sopenharmony_ci enum pipe pipe = plane->pipe; 56528c2ecf20Sopenharmony_ci const struct skl_plane_wm *wm = 56538c2ecf20Sopenharmony_ci &crtc_state->wm.skl.optimal.planes[plane_id]; 56548c2ecf20Sopenharmony_ci const struct skl_ddb_entry *ddb_y = 56558c2ecf20Sopenharmony_ci &crtc_state->wm.skl.plane_ddb_y[plane_id]; 56568c2ecf20Sopenharmony_ci const struct skl_ddb_entry *ddb_uv = 56578c2ecf20Sopenharmony_ci &crtc_state->wm.skl.plane_ddb_uv[plane_id]; 56588c2ecf20Sopenharmony_ci 56598c2ecf20Sopenharmony_ci for (level = 0; level <= max_level; level++) { 56608c2ecf20Sopenharmony_ci const struct skl_wm_level *wm_level; 56618c2ecf20Sopenharmony_ci 56628c2ecf20Sopenharmony_ci wm_level = skl_plane_wm_level(crtc_state, plane_id, level); 56638c2ecf20Sopenharmony_ci 56648c2ecf20Sopenharmony_ci skl_write_wm_level(dev_priv, PLANE_WM(pipe, plane_id, level), 56658c2ecf20Sopenharmony_ci wm_level); 56668c2ecf20Sopenharmony_ci } 56678c2ecf20Sopenharmony_ci skl_write_wm_level(dev_priv, PLANE_WM_TRANS(pipe, plane_id), 56688c2ecf20Sopenharmony_ci &wm->trans_wm); 56698c2ecf20Sopenharmony_ci 56708c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 11) { 56718c2ecf20Sopenharmony_ci skl_ddb_entry_write(dev_priv, 56728c2ecf20Sopenharmony_ci PLANE_BUF_CFG(pipe, plane_id), ddb_y); 56738c2ecf20Sopenharmony_ci return; 56748c2ecf20Sopenharmony_ci } 56758c2ecf20Sopenharmony_ci 56768c2ecf20Sopenharmony_ci if (wm->is_planar) 56778c2ecf20Sopenharmony_ci swap(ddb_y, ddb_uv); 56788c2ecf20Sopenharmony_ci 56798c2ecf20Sopenharmony_ci skl_ddb_entry_write(dev_priv, 56808c2ecf20Sopenharmony_ci PLANE_BUF_CFG(pipe, plane_id), ddb_y); 56818c2ecf20Sopenharmony_ci skl_ddb_entry_write(dev_priv, 56828c2ecf20Sopenharmony_ci PLANE_NV12_BUF_CFG(pipe, plane_id), ddb_uv); 56838c2ecf20Sopenharmony_ci} 56848c2ecf20Sopenharmony_ci 56858c2ecf20Sopenharmony_civoid skl_write_cursor_wm(struct intel_plane *plane, 56868c2ecf20Sopenharmony_ci const struct intel_crtc_state *crtc_state) 56878c2ecf20Sopenharmony_ci{ 56888c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(plane->base.dev); 56898c2ecf20Sopenharmony_ci int level, max_level = ilk_wm_max_level(dev_priv); 56908c2ecf20Sopenharmony_ci enum plane_id plane_id = plane->id; 56918c2ecf20Sopenharmony_ci enum pipe pipe = plane->pipe; 56928c2ecf20Sopenharmony_ci const struct skl_plane_wm *wm = 56938c2ecf20Sopenharmony_ci &crtc_state->wm.skl.optimal.planes[plane_id]; 56948c2ecf20Sopenharmony_ci const struct skl_ddb_entry *ddb = 56958c2ecf20Sopenharmony_ci &crtc_state->wm.skl.plane_ddb_y[plane_id]; 56968c2ecf20Sopenharmony_ci 56978c2ecf20Sopenharmony_ci for (level = 0; level <= max_level; level++) { 56988c2ecf20Sopenharmony_ci const struct skl_wm_level *wm_level; 56998c2ecf20Sopenharmony_ci 57008c2ecf20Sopenharmony_ci wm_level = skl_plane_wm_level(crtc_state, plane_id, level); 57018c2ecf20Sopenharmony_ci 57028c2ecf20Sopenharmony_ci skl_write_wm_level(dev_priv, CUR_WM(pipe, level), 57038c2ecf20Sopenharmony_ci wm_level); 57048c2ecf20Sopenharmony_ci } 57058c2ecf20Sopenharmony_ci skl_write_wm_level(dev_priv, CUR_WM_TRANS(pipe), &wm->trans_wm); 57068c2ecf20Sopenharmony_ci 57078c2ecf20Sopenharmony_ci skl_ddb_entry_write(dev_priv, CUR_BUF_CFG(pipe), ddb); 57088c2ecf20Sopenharmony_ci} 57098c2ecf20Sopenharmony_ci 57108c2ecf20Sopenharmony_cibool skl_wm_level_equals(const struct skl_wm_level *l1, 57118c2ecf20Sopenharmony_ci const struct skl_wm_level *l2) 57128c2ecf20Sopenharmony_ci{ 57138c2ecf20Sopenharmony_ci return l1->plane_en == l2->plane_en && 57148c2ecf20Sopenharmony_ci l1->ignore_lines == l2->ignore_lines && 57158c2ecf20Sopenharmony_ci l1->plane_res_l == l2->plane_res_l && 57168c2ecf20Sopenharmony_ci l1->plane_res_b == l2->plane_res_b; 57178c2ecf20Sopenharmony_ci} 57188c2ecf20Sopenharmony_ci 57198c2ecf20Sopenharmony_cistatic bool skl_plane_wm_equals(struct drm_i915_private *dev_priv, 57208c2ecf20Sopenharmony_ci const struct skl_plane_wm *wm1, 57218c2ecf20Sopenharmony_ci const struct skl_plane_wm *wm2) 57228c2ecf20Sopenharmony_ci{ 57238c2ecf20Sopenharmony_ci int level, max_level = ilk_wm_max_level(dev_priv); 57248c2ecf20Sopenharmony_ci 57258c2ecf20Sopenharmony_ci for (level = 0; level <= max_level; level++) { 57268c2ecf20Sopenharmony_ci /* 57278c2ecf20Sopenharmony_ci * We don't check uv_wm as the hardware doesn't actually 57288c2ecf20Sopenharmony_ci * use it. It only gets used for calculating the required 57298c2ecf20Sopenharmony_ci * ddb allocation. 57308c2ecf20Sopenharmony_ci */ 57318c2ecf20Sopenharmony_ci if (!skl_wm_level_equals(&wm1->wm[level], &wm2->wm[level])) 57328c2ecf20Sopenharmony_ci return false; 57338c2ecf20Sopenharmony_ci } 57348c2ecf20Sopenharmony_ci 57358c2ecf20Sopenharmony_ci return skl_wm_level_equals(&wm1->trans_wm, &wm2->trans_wm); 57368c2ecf20Sopenharmony_ci} 57378c2ecf20Sopenharmony_ci 57388c2ecf20Sopenharmony_cistatic bool skl_ddb_entries_overlap(const struct skl_ddb_entry *a, 57398c2ecf20Sopenharmony_ci const struct skl_ddb_entry *b) 57408c2ecf20Sopenharmony_ci{ 57418c2ecf20Sopenharmony_ci return a->start < b->end && b->start < a->end; 57428c2ecf20Sopenharmony_ci} 57438c2ecf20Sopenharmony_ci 57448c2ecf20Sopenharmony_cibool skl_ddb_allocation_overlaps(const struct skl_ddb_entry *ddb, 57458c2ecf20Sopenharmony_ci const struct skl_ddb_entry *entries, 57468c2ecf20Sopenharmony_ci int num_entries, int ignore_idx) 57478c2ecf20Sopenharmony_ci{ 57488c2ecf20Sopenharmony_ci int i; 57498c2ecf20Sopenharmony_ci 57508c2ecf20Sopenharmony_ci for (i = 0; i < num_entries; i++) { 57518c2ecf20Sopenharmony_ci if (i != ignore_idx && 57528c2ecf20Sopenharmony_ci skl_ddb_entries_overlap(ddb, &entries[i])) 57538c2ecf20Sopenharmony_ci return true; 57548c2ecf20Sopenharmony_ci } 57558c2ecf20Sopenharmony_ci 57568c2ecf20Sopenharmony_ci return false; 57578c2ecf20Sopenharmony_ci} 57588c2ecf20Sopenharmony_ci 57598c2ecf20Sopenharmony_cistatic int 57608c2ecf20Sopenharmony_ciskl_ddb_add_affected_planes(const struct intel_crtc_state *old_crtc_state, 57618c2ecf20Sopenharmony_ci struct intel_crtc_state *new_crtc_state) 57628c2ecf20Sopenharmony_ci{ 57638c2ecf20Sopenharmony_ci struct intel_atomic_state *state = to_intel_atomic_state(new_crtc_state->uapi.state); 57648c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->uapi.crtc); 57658c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 57668c2ecf20Sopenharmony_ci struct intel_plane *plane; 57678c2ecf20Sopenharmony_ci 57688c2ecf20Sopenharmony_ci for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) { 57698c2ecf20Sopenharmony_ci struct intel_plane_state *plane_state; 57708c2ecf20Sopenharmony_ci enum plane_id plane_id = plane->id; 57718c2ecf20Sopenharmony_ci 57728c2ecf20Sopenharmony_ci if (skl_ddb_entry_equal(&old_crtc_state->wm.skl.plane_ddb_y[plane_id], 57738c2ecf20Sopenharmony_ci &new_crtc_state->wm.skl.plane_ddb_y[plane_id]) && 57748c2ecf20Sopenharmony_ci skl_ddb_entry_equal(&old_crtc_state->wm.skl.plane_ddb_uv[plane_id], 57758c2ecf20Sopenharmony_ci &new_crtc_state->wm.skl.plane_ddb_uv[plane_id])) 57768c2ecf20Sopenharmony_ci continue; 57778c2ecf20Sopenharmony_ci 57788c2ecf20Sopenharmony_ci plane_state = intel_atomic_get_plane_state(state, plane); 57798c2ecf20Sopenharmony_ci if (IS_ERR(plane_state)) 57808c2ecf20Sopenharmony_ci return PTR_ERR(plane_state); 57818c2ecf20Sopenharmony_ci 57828c2ecf20Sopenharmony_ci new_crtc_state->update_planes |= BIT(plane_id); 57838c2ecf20Sopenharmony_ci } 57848c2ecf20Sopenharmony_ci 57858c2ecf20Sopenharmony_ci return 0; 57868c2ecf20Sopenharmony_ci} 57878c2ecf20Sopenharmony_ci 57888c2ecf20Sopenharmony_cistatic int 57898c2ecf20Sopenharmony_ciskl_compute_ddb(struct intel_atomic_state *state) 57908c2ecf20Sopenharmony_ci{ 57918c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(state->base.dev); 57928c2ecf20Sopenharmony_ci const struct intel_dbuf_state *old_dbuf_state; 57938c2ecf20Sopenharmony_ci const struct intel_dbuf_state *new_dbuf_state; 57948c2ecf20Sopenharmony_ci const struct intel_crtc_state *old_crtc_state; 57958c2ecf20Sopenharmony_ci struct intel_crtc_state *new_crtc_state; 57968c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 57978c2ecf20Sopenharmony_ci int ret, i; 57988c2ecf20Sopenharmony_ci 57998c2ecf20Sopenharmony_ci for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, 58008c2ecf20Sopenharmony_ci new_crtc_state, i) { 58018c2ecf20Sopenharmony_ci ret = skl_allocate_pipe_ddb(new_crtc_state); 58028c2ecf20Sopenharmony_ci if (ret) 58038c2ecf20Sopenharmony_ci return ret; 58048c2ecf20Sopenharmony_ci 58058c2ecf20Sopenharmony_ci ret = skl_ddb_add_affected_planes(old_crtc_state, 58068c2ecf20Sopenharmony_ci new_crtc_state); 58078c2ecf20Sopenharmony_ci if (ret) 58088c2ecf20Sopenharmony_ci return ret; 58098c2ecf20Sopenharmony_ci } 58108c2ecf20Sopenharmony_ci 58118c2ecf20Sopenharmony_ci old_dbuf_state = intel_atomic_get_old_dbuf_state(state); 58128c2ecf20Sopenharmony_ci new_dbuf_state = intel_atomic_get_new_dbuf_state(state); 58138c2ecf20Sopenharmony_ci 58148c2ecf20Sopenharmony_ci if (new_dbuf_state && 58158c2ecf20Sopenharmony_ci new_dbuf_state->enabled_slices != old_dbuf_state->enabled_slices) 58168c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 58178c2ecf20Sopenharmony_ci "Enabled dbuf slices 0x%x -> 0x%x (out of %d dbuf slices)\n", 58188c2ecf20Sopenharmony_ci old_dbuf_state->enabled_slices, 58198c2ecf20Sopenharmony_ci new_dbuf_state->enabled_slices, 58208c2ecf20Sopenharmony_ci INTEL_INFO(dev_priv)->num_supported_dbuf_slices); 58218c2ecf20Sopenharmony_ci 58228c2ecf20Sopenharmony_ci return 0; 58238c2ecf20Sopenharmony_ci} 58248c2ecf20Sopenharmony_ci 58258c2ecf20Sopenharmony_cistatic char enast(bool enable) 58268c2ecf20Sopenharmony_ci{ 58278c2ecf20Sopenharmony_ci return enable ? '*' : ' '; 58288c2ecf20Sopenharmony_ci} 58298c2ecf20Sopenharmony_ci 58308c2ecf20Sopenharmony_cistatic void 58318c2ecf20Sopenharmony_ciskl_print_wm_changes(struct intel_atomic_state *state) 58328c2ecf20Sopenharmony_ci{ 58338c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(state->base.dev); 58348c2ecf20Sopenharmony_ci const struct intel_crtc_state *old_crtc_state; 58358c2ecf20Sopenharmony_ci const struct intel_crtc_state *new_crtc_state; 58368c2ecf20Sopenharmony_ci struct intel_plane *plane; 58378c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 58388c2ecf20Sopenharmony_ci int i; 58398c2ecf20Sopenharmony_ci 58408c2ecf20Sopenharmony_ci if (!drm_debug_enabled(DRM_UT_KMS)) 58418c2ecf20Sopenharmony_ci return; 58428c2ecf20Sopenharmony_ci 58438c2ecf20Sopenharmony_ci for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, 58448c2ecf20Sopenharmony_ci new_crtc_state, i) { 58458c2ecf20Sopenharmony_ci const struct skl_pipe_wm *old_pipe_wm, *new_pipe_wm; 58468c2ecf20Sopenharmony_ci 58478c2ecf20Sopenharmony_ci old_pipe_wm = &old_crtc_state->wm.skl.optimal; 58488c2ecf20Sopenharmony_ci new_pipe_wm = &new_crtc_state->wm.skl.optimal; 58498c2ecf20Sopenharmony_ci 58508c2ecf20Sopenharmony_ci for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) { 58518c2ecf20Sopenharmony_ci enum plane_id plane_id = plane->id; 58528c2ecf20Sopenharmony_ci const struct skl_ddb_entry *old, *new; 58538c2ecf20Sopenharmony_ci 58548c2ecf20Sopenharmony_ci old = &old_crtc_state->wm.skl.plane_ddb_y[plane_id]; 58558c2ecf20Sopenharmony_ci new = &new_crtc_state->wm.skl.plane_ddb_y[plane_id]; 58568c2ecf20Sopenharmony_ci 58578c2ecf20Sopenharmony_ci if (skl_ddb_entry_equal(old, new)) 58588c2ecf20Sopenharmony_ci continue; 58598c2ecf20Sopenharmony_ci 58608c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 58618c2ecf20Sopenharmony_ci "[PLANE:%d:%s] ddb (%4d - %4d) -> (%4d - %4d), size %4d -> %4d\n", 58628c2ecf20Sopenharmony_ci plane->base.base.id, plane->base.name, 58638c2ecf20Sopenharmony_ci old->start, old->end, new->start, new->end, 58648c2ecf20Sopenharmony_ci skl_ddb_entry_size(old), skl_ddb_entry_size(new)); 58658c2ecf20Sopenharmony_ci } 58668c2ecf20Sopenharmony_ci 58678c2ecf20Sopenharmony_ci for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) { 58688c2ecf20Sopenharmony_ci enum plane_id plane_id = plane->id; 58698c2ecf20Sopenharmony_ci const struct skl_plane_wm *old_wm, *new_wm; 58708c2ecf20Sopenharmony_ci 58718c2ecf20Sopenharmony_ci old_wm = &old_pipe_wm->planes[plane_id]; 58728c2ecf20Sopenharmony_ci new_wm = &new_pipe_wm->planes[plane_id]; 58738c2ecf20Sopenharmony_ci 58748c2ecf20Sopenharmony_ci if (skl_plane_wm_equals(dev_priv, old_wm, new_wm)) 58758c2ecf20Sopenharmony_ci continue; 58768c2ecf20Sopenharmony_ci 58778c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 58788c2ecf20Sopenharmony_ci "[PLANE:%d:%s] level %cwm0,%cwm1,%cwm2,%cwm3,%cwm4,%cwm5,%cwm6,%cwm7,%ctwm,%cswm" 58798c2ecf20Sopenharmony_ci " -> %cwm0,%cwm1,%cwm2,%cwm3,%cwm4,%cwm5,%cwm6,%cwm7,%ctwm,%cswm\n", 58808c2ecf20Sopenharmony_ci plane->base.base.id, plane->base.name, 58818c2ecf20Sopenharmony_ci enast(old_wm->wm[0].plane_en), enast(old_wm->wm[1].plane_en), 58828c2ecf20Sopenharmony_ci enast(old_wm->wm[2].plane_en), enast(old_wm->wm[3].plane_en), 58838c2ecf20Sopenharmony_ci enast(old_wm->wm[4].plane_en), enast(old_wm->wm[5].plane_en), 58848c2ecf20Sopenharmony_ci enast(old_wm->wm[6].plane_en), enast(old_wm->wm[7].plane_en), 58858c2ecf20Sopenharmony_ci enast(old_wm->trans_wm.plane_en), 58868c2ecf20Sopenharmony_ci enast(old_wm->sagv_wm0.plane_en), 58878c2ecf20Sopenharmony_ci enast(new_wm->wm[0].plane_en), enast(new_wm->wm[1].plane_en), 58888c2ecf20Sopenharmony_ci enast(new_wm->wm[2].plane_en), enast(new_wm->wm[3].plane_en), 58898c2ecf20Sopenharmony_ci enast(new_wm->wm[4].plane_en), enast(new_wm->wm[5].plane_en), 58908c2ecf20Sopenharmony_ci enast(new_wm->wm[6].plane_en), enast(new_wm->wm[7].plane_en), 58918c2ecf20Sopenharmony_ci enast(new_wm->trans_wm.plane_en), 58928c2ecf20Sopenharmony_ci enast(new_wm->sagv_wm0.plane_en)); 58938c2ecf20Sopenharmony_ci 58948c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 58958c2ecf20Sopenharmony_ci "[PLANE:%d:%s] lines %c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d" 58968c2ecf20Sopenharmony_ci " -> %c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d,%c%3d\n", 58978c2ecf20Sopenharmony_ci plane->base.base.id, plane->base.name, 58988c2ecf20Sopenharmony_ci enast(old_wm->wm[0].ignore_lines), old_wm->wm[0].plane_res_l, 58998c2ecf20Sopenharmony_ci enast(old_wm->wm[1].ignore_lines), old_wm->wm[1].plane_res_l, 59008c2ecf20Sopenharmony_ci enast(old_wm->wm[2].ignore_lines), old_wm->wm[2].plane_res_l, 59018c2ecf20Sopenharmony_ci enast(old_wm->wm[3].ignore_lines), old_wm->wm[3].plane_res_l, 59028c2ecf20Sopenharmony_ci enast(old_wm->wm[4].ignore_lines), old_wm->wm[4].plane_res_l, 59038c2ecf20Sopenharmony_ci enast(old_wm->wm[5].ignore_lines), old_wm->wm[5].plane_res_l, 59048c2ecf20Sopenharmony_ci enast(old_wm->wm[6].ignore_lines), old_wm->wm[6].plane_res_l, 59058c2ecf20Sopenharmony_ci enast(old_wm->wm[7].ignore_lines), old_wm->wm[7].plane_res_l, 59068c2ecf20Sopenharmony_ci enast(old_wm->trans_wm.ignore_lines), old_wm->trans_wm.plane_res_l, 59078c2ecf20Sopenharmony_ci enast(old_wm->sagv_wm0.ignore_lines), old_wm->sagv_wm0.plane_res_l, 59088c2ecf20Sopenharmony_ci 59098c2ecf20Sopenharmony_ci enast(new_wm->wm[0].ignore_lines), new_wm->wm[0].plane_res_l, 59108c2ecf20Sopenharmony_ci enast(new_wm->wm[1].ignore_lines), new_wm->wm[1].plane_res_l, 59118c2ecf20Sopenharmony_ci enast(new_wm->wm[2].ignore_lines), new_wm->wm[2].plane_res_l, 59128c2ecf20Sopenharmony_ci enast(new_wm->wm[3].ignore_lines), new_wm->wm[3].plane_res_l, 59138c2ecf20Sopenharmony_ci enast(new_wm->wm[4].ignore_lines), new_wm->wm[4].plane_res_l, 59148c2ecf20Sopenharmony_ci enast(new_wm->wm[5].ignore_lines), new_wm->wm[5].plane_res_l, 59158c2ecf20Sopenharmony_ci enast(new_wm->wm[6].ignore_lines), new_wm->wm[6].plane_res_l, 59168c2ecf20Sopenharmony_ci enast(new_wm->wm[7].ignore_lines), new_wm->wm[7].plane_res_l, 59178c2ecf20Sopenharmony_ci enast(new_wm->trans_wm.ignore_lines), new_wm->trans_wm.plane_res_l, 59188c2ecf20Sopenharmony_ci enast(new_wm->sagv_wm0.ignore_lines), new_wm->sagv_wm0.plane_res_l); 59198c2ecf20Sopenharmony_ci 59208c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 59218c2ecf20Sopenharmony_ci "[PLANE:%d:%s] blocks %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d" 59228c2ecf20Sopenharmony_ci " -> %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d\n", 59238c2ecf20Sopenharmony_ci plane->base.base.id, plane->base.name, 59248c2ecf20Sopenharmony_ci old_wm->wm[0].plane_res_b, old_wm->wm[1].plane_res_b, 59258c2ecf20Sopenharmony_ci old_wm->wm[2].plane_res_b, old_wm->wm[3].plane_res_b, 59268c2ecf20Sopenharmony_ci old_wm->wm[4].plane_res_b, old_wm->wm[5].plane_res_b, 59278c2ecf20Sopenharmony_ci old_wm->wm[6].plane_res_b, old_wm->wm[7].plane_res_b, 59288c2ecf20Sopenharmony_ci old_wm->trans_wm.plane_res_b, 59298c2ecf20Sopenharmony_ci old_wm->sagv_wm0.plane_res_b, 59308c2ecf20Sopenharmony_ci new_wm->wm[0].plane_res_b, new_wm->wm[1].plane_res_b, 59318c2ecf20Sopenharmony_ci new_wm->wm[2].plane_res_b, new_wm->wm[3].plane_res_b, 59328c2ecf20Sopenharmony_ci new_wm->wm[4].plane_res_b, new_wm->wm[5].plane_res_b, 59338c2ecf20Sopenharmony_ci new_wm->wm[6].plane_res_b, new_wm->wm[7].plane_res_b, 59348c2ecf20Sopenharmony_ci new_wm->trans_wm.plane_res_b, 59358c2ecf20Sopenharmony_ci new_wm->sagv_wm0.plane_res_b); 59368c2ecf20Sopenharmony_ci 59378c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 59388c2ecf20Sopenharmony_ci "[PLANE:%d:%s] min_ddb %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d" 59398c2ecf20Sopenharmony_ci " -> %4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d,%4d\n", 59408c2ecf20Sopenharmony_ci plane->base.base.id, plane->base.name, 59418c2ecf20Sopenharmony_ci old_wm->wm[0].min_ddb_alloc, old_wm->wm[1].min_ddb_alloc, 59428c2ecf20Sopenharmony_ci old_wm->wm[2].min_ddb_alloc, old_wm->wm[3].min_ddb_alloc, 59438c2ecf20Sopenharmony_ci old_wm->wm[4].min_ddb_alloc, old_wm->wm[5].min_ddb_alloc, 59448c2ecf20Sopenharmony_ci old_wm->wm[6].min_ddb_alloc, old_wm->wm[7].min_ddb_alloc, 59458c2ecf20Sopenharmony_ci old_wm->trans_wm.min_ddb_alloc, 59468c2ecf20Sopenharmony_ci old_wm->sagv_wm0.min_ddb_alloc, 59478c2ecf20Sopenharmony_ci new_wm->wm[0].min_ddb_alloc, new_wm->wm[1].min_ddb_alloc, 59488c2ecf20Sopenharmony_ci new_wm->wm[2].min_ddb_alloc, new_wm->wm[3].min_ddb_alloc, 59498c2ecf20Sopenharmony_ci new_wm->wm[4].min_ddb_alloc, new_wm->wm[5].min_ddb_alloc, 59508c2ecf20Sopenharmony_ci new_wm->wm[6].min_ddb_alloc, new_wm->wm[7].min_ddb_alloc, 59518c2ecf20Sopenharmony_ci new_wm->trans_wm.min_ddb_alloc, 59528c2ecf20Sopenharmony_ci new_wm->sagv_wm0.min_ddb_alloc); 59538c2ecf20Sopenharmony_ci } 59548c2ecf20Sopenharmony_ci } 59558c2ecf20Sopenharmony_ci} 59568c2ecf20Sopenharmony_ci 59578c2ecf20Sopenharmony_cistatic int intel_add_affected_pipes(struct intel_atomic_state *state, 59588c2ecf20Sopenharmony_ci u8 pipe_mask) 59598c2ecf20Sopenharmony_ci{ 59608c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(state->base.dev); 59618c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 59628c2ecf20Sopenharmony_ci 59638c2ecf20Sopenharmony_ci for_each_intel_crtc(&dev_priv->drm, crtc) { 59648c2ecf20Sopenharmony_ci struct intel_crtc_state *crtc_state; 59658c2ecf20Sopenharmony_ci 59668c2ecf20Sopenharmony_ci if ((pipe_mask & BIT(crtc->pipe)) == 0) 59678c2ecf20Sopenharmony_ci continue; 59688c2ecf20Sopenharmony_ci 59698c2ecf20Sopenharmony_ci crtc_state = intel_atomic_get_crtc_state(&state->base, crtc); 59708c2ecf20Sopenharmony_ci if (IS_ERR(crtc_state)) 59718c2ecf20Sopenharmony_ci return PTR_ERR(crtc_state); 59728c2ecf20Sopenharmony_ci } 59738c2ecf20Sopenharmony_ci 59748c2ecf20Sopenharmony_ci return 0; 59758c2ecf20Sopenharmony_ci} 59768c2ecf20Sopenharmony_ci 59778c2ecf20Sopenharmony_cistatic int 59788c2ecf20Sopenharmony_ciskl_ddb_add_affected_pipes(struct intel_atomic_state *state) 59798c2ecf20Sopenharmony_ci{ 59808c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(state->base.dev); 59818c2ecf20Sopenharmony_ci struct intel_crtc_state *crtc_state; 59828c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 59838c2ecf20Sopenharmony_ci int i, ret; 59848c2ecf20Sopenharmony_ci 59858c2ecf20Sopenharmony_ci if (dev_priv->wm.distrust_bios_wm) { 59868c2ecf20Sopenharmony_ci /* 59878c2ecf20Sopenharmony_ci * skl_ddb_get_pipe_allocation_limits() currently requires 59888c2ecf20Sopenharmony_ci * all active pipes to be included in the state so that 59898c2ecf20Sopenharmony_ci * it can redistribute the dbuf among them, and it really 59908c2ecf20Sopenharmony_ci * wants to recompute things when distrust_bios_wm is set 59918c2ecf20Sopenharmony_ci * so we add all the pipes to the state. 59928c2ecf20Sopenharmony_ci */ 59938c2ecf20Sopenharmony_ci ret = intel_add_affected_pipes(state, ~0); 59948c2ecf20Sopenharmony_ci if (ret) 59958c2ecf20Sopenharmony_ci return ret; 59968c2ecf20Sopenharmony_ci } 59978c2ecf20Sopenharmony_ci 59988c2ecf20Sopenharmony_ci for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) { 59998c2ecf20Sopenharmony_ci struct intel_dbuf_state *new_dbuf_state; 60008c2ecf20Sopenharmony_ci const struct intel_dbuf_state *old_dbuf_state; 60018c2ecf20Sopenharmony_ci 60028c2ecf20Sopenharmony_ci new_dbuf_state = intel_atomic_get_dbuf_state(state); 60038c2ecf20Sopenharmony_ci if (IS_ERR(new_dbuf_state)) 60048c2ecf20Sopenharmony_ci return PTR_ERR(new_dbuf_state); 60058c2ecf20Sopenharmony_ci 60068c2ecf20Sopenharmony_ci old_dbuf_state = intel_atomic_get_old_dbuf_state(state); 60078c2ecf20Sopenharmony_ci 60088c2ecf20Sopenharmony_ci new_dbuf_state->active_pipes = 60098c2ecf20Sopenharmony_ci intel_calc_active_pipes(state, old_dbuf_state->active_pipes); 60108c2ecf20Sopenharmony_ci 60118c2ecf20Sopenharmony_ci if (old_dbuf_state->active_pipes == new_dbuf_state->active_pipes) 60128c2ecf20Sopenharmony_ci break; 60138c2ecf20Sopenharmony_ci 60148c2ecf20Sopenharmony_ci ret = intel_atomic_lock_global_state(&new_dbuf_state->base); 60158c2ecf20Sopenharmony_ci if (ret) 60168c2ecf20Sopenharmony_ci return ret; 60178c2ecf20Sopenharmony_ci 60188c2ecf20Sopenharmony_ci /* 60198c2ecf20Sopenharmony_ci * skl_ddb_get_pipe_allocation_limits() currently requires 60208c2ecf20Sopenharmony_ci * all active pipes to be included in the state so that 60218c2ecf20Sopenharmony_ci * it can redistribute the dbuf among them. 60228c2ecf20Sopenharmony_ci */ 60238c2ecf20Sopenharmony_ci ret = intel_add_affected_pipes(state, 60248c2ecf20Sopenharmony_ci new_dbuf_state->active_pipes); 60258c2ecf20Sopenharmony_ci if (ret) 60268c2ecf20Sopenharmony_ci return ret; 60278c2ecf20Sopenharmony_ci 60288c2ecf20Sopenharmony_ci break; 60298c2ecf20Sopenharmony_ci } 60308c2ecf20Sopenharmony_ci 60318c2ecf20Sopenharmony_ci return 0; 60328c2ecf20Sopenharmony_ci} 60338c2ecf20Sopenharmony_ci 60348c2ecf20Sopenharmony_ci/* 60358c2ecf20Sopenharmony_ci * To make sure the cursor watermark registers are always consistent 60368c2ecf20Sopenharmony_ci * with our computed state the following scenario needs special 60378c2ecf20Sopenharmony_ci * treatment: 60388c2ecf20Sopenharmony_ci * 60398c2ecf20Sopenharmony_ci * 1. enable cursor 60408c2ecf20Sopenharmony_ci * 2. move cursor entirely offscreen 60418c2ecf20Sopenharmony_ci * 3. disable cursor 60428c2ecf20Sopenharmony_ci * 60438c2ecf20Sopenharmony_ci * Step 2. does call .disable_plane() but does not zero the watermarks 60448c2ecf20Sopenharmony_ci * (since we consider an offscreen cursor still active for the purposes 60458c2ecf20Sopenharmony_ci * of watermarks). Step 3. would not normally call .disable_plane() 60468c2ecf20Sopenharmony_ci * because the actual plane visibility isn't changing, and we don't 60478c2ecf20Sopenharmony_ci * deallocate the cursor ddb until the pipe gets disabled. So we must 60488c2ecf20Sopenharmony_ci * force step 3. to call .disable_plane() to update the watermark 60498c2ecf20Sopenharmony_ci * registers properly. 60508c2ecf20Sopenharmony_ci * 60518c2ecf20Sopenharmony_ci * Other planes do not suffer from this issues as their watermarks are 60528c2ecf20Sopenharmony_ci * calculated based on the actual plane visibility. The only time this 60538c2ecf20Sopenharmony_ci * can trigger for the other planes is during the initial readout as the 60548c2ecf20Sopenharmony_ci * default value of the watermarks registers is not zero. 60558c2ecf20Sopenharmony_ci */ 60568c2ecf20Sopenharmony_cistatic int skl_wm_add_affected_planes(struct intel_atomic_state *state, 60578c2ecf20Sopenharmony_ci struct intel_crtc *crtc) 60588c2ecf20Sopenharmony_ci{ 60598c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 60608c2ecf20Sopenharmony_ci const struct intel_crtc_state *old_crtc_state = 60618c2ecf20Sopenharmony_ci intel_atomic_get_old_crtc_state(state, crtc); 60628c2ecf20Sopenharmony_ci struct intel_crtc_state *new_crtc_state = 60638c2ecf20Sopenharmony_ci intel_atomic_get_new_crtc_state(state, crtc); 60648c2ecf20Sopenharmony_ci struct intel_plane *plane; 60658c2ecf20Sopenharmony_ci 60668c2ecf20Sopenharmony_ci for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) { 60678c2ecf20Sopenharmony_ci struct intel_plane_state *plane_state; 60688c2ecf20Sopenharmony_ci enum plane_id plane_id = plane->id; 60698c2ecf20Sopenharmony_ci 60708c2ecf20Sopenharmony_ci /* 60718c2ecf20Sopenharmony_ci * Force a full wm update for every plane on modeset. 60728c2ecf20Sopenharmony_ci * Required because the reset value of the wm registers 60738c2ecf20Sopenharmony_ci * is non-zero, whereas we want all disabled planes to 60748c2ecf20Sopenharmony_ci * have zero watermarks. So if we turn off the relevant 60758c2ecf20Sopenharmony_ci * power well the hardware state will go out of sync 60768c2ecf20Sopenharmony_ci * with the software state. 60778c2ecf20Sopenharmony_ci */ 60788c2ecf20Sopenharmony_ci if (!drm_atomic_crtc_needs_modeset(&new_crtc_state->uapi) && 60798c2ecf20Sopenharmony_ci skl_plane_wm_equals(dev_priv, 60808c2ecf20Sopenharmony_ci &old_crtc_state->wm.skl.optimal.planes[plane_id], 60818c2ecf20Sopenharmony_ci &new_crtc_state->wm.skl.optimal.planes[plane_id])) 60828c2ecf20Sopenharmony_ci continue; 60838c2ecf20Sopenharmony_ci 60848c2ecf20Sopenharmony_ci plane_state = intel_atomic_get_plane_state(state, plane); 60858c2ecf20Sopenharmony_ci if (IS_ERR(plane_state)) 60868c2ecf20Sopenharmony_ci return PTR_ERR(plane_state); 60878c2ecf20Sopenharmony_ci 60888c2ecf20Sopenharmony_ci new_crtc_state->update_planes |= BIT(plane_id); 60898c2ecf20Sopenharmony_ci } 60908c2ecf20Sopenharmony_ci 60918c2ecf20Sopenharmony_ci return 0; 60928c2ecf20Sopenharmony_ci} 60938c2ecf20Sopenharmony_ci 60948c2ecf20Sopenharmony_cistatic int 60958c2ecf20Sopenharmony_ciskl_compute_wm(struct intel_atomic_state *state) 60968c2ecf20Sopenharmony_ci{ 60978c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 60988c2ecf20Sopenharmony_ci struct intel_crtc_state *new_crtc_state; 60998c2ecf20Sopenharmony_ci struct intel_crtc_state *old_crtc_state; 61008c2ecf20Sopenharmony_ci int ret, i; 61018c2ecf20Sopenharmony_ci 61028c2ecf20Sopenharmony_ci ret = skl_ddb_add_affected_pipes(state); 61038c2ecf20Sopenharmony_ci if (ret) 61048c2ecf20Sopenharmony_ci return ret; 61058c2ecf20Sopenharmony_ci 61068c2ecf20Sopenharmony_ci /* 61078c2ecf20Sopenharmony_ci * Calculate WM's for all pipes that are part of this transaction. 61088c2ecf20Sopenharmony_ci * Note that skl_ddb_add_affected_pipes may have added more CRTC's that 61098c2ecf20Sopenharmony_ci * weren't otherwise being modified if pipe allocations had to change. 61108c2ecf20Sopenharmony_ci */ 61118c2ecf20Sopenharmony_ci for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, 61128c2ecf20Sopenharmony_ci new_crtc_state, i) { 61138c2ecf20Sopenharmony_ci ret = skl_build_pipe_wm(new_crtc_state); 61148c2ecf20Sopenharmony_ci if (ret) 61158c2ecf20Sopenharmony_ci return ret; 61168c2ecf20Sopenharmony_ci } 61178c2ecf20Sopenharmony_ci 61188c2ecf20Sopenharmony_ci ret = skl_compute_ddb(state); 61198c2ecf20Sopenharmony_ci if (ret) 61208c2ecf20Sopenharmony_ci return ret; 61218c2ecf20Sopenharmony_ci 61228c2ecf20Sopenharmony_ci ret = intel_compute_sagv_mask(state); 61238c2ecf20Sopenharmony_ci if (ret) 61248c2ecf20Sopenharmony_ci return ret; 61258c2ecf20Sopenharmony_ci 61268c2ecf20Sopenharmony_ci /* 61278c2ecf20Sopenharmony_ci * skl_compute_ddb() will have adjusted the final watermarks 61288c2ecf20Sopenharmony_ci * based on how much ddb is available. Now we can actually 61298c2ecf20Sopenharmony_ci * check if the final watermarks changed. 61308c2ecf20Sopenharmony_ci */ 61318c2ecf20Sopenharmony_ci for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, 61328c2ecf20Sopenharmony_ci new_crtc_state, i) { 61338c2ecf20Sopenharmony_ci ret = skl_wm_add_affected_planes(state, crtc); 61348c2ecf20Sopenharmony_ci if (ret) 61358c2ecf20Sopenharmony_ci return ret; 61368c2ecf20Sopenharmony_ci } 61378c2ecf20Sopenharmony_ci 61388c2ecf20Sopenharmony_ci skl_print_wm_changes(state); 61398c2ecf20Sopenharmony_ci 61408c2ecf20Sopenharmony_ci return 0; 61418c2ecf20Sopenharmony_ci} 61428c2ecf20Sopenharmony_ci 61438c2ecf20Sopenharmony_cistatic void ilk_compute_wm_config(struct drm_i915_private *dev_priv, 61448c2ecf20Sopenharmony_ci struct intel_wm_config *config) 61458c2ecf20Sopenharmony_ci{ 61468c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 61478c2ecf20Sopenharmony_ci 61488c2ecf20Sopenharmony_ci /* Compute the currently _active_ config */ 61498c2ecf20Sopenharmony_ci for_each_intel_crtc(&dev_priv->drm, crtc) { 61508c2ecf20Sopenharmony_ci const struct intel_pipe_wm *wm = &crtc->wm.active.ilk; 61518c2ecf20Sopenharmony_ci 61528c2ecf20Sopenharmony_ci if (!wm->pipe_enabled) 61538c2ecf20Sopenharmony_ci continue; 61548c2ecf20Sopenharmony_ci 61558c2ecf20Sopenharmony_ci config->sprites_enabled |= wm->sprites_enabled; 61568c2ecf20Sopenharmony_ci config->sprites_scaled |= wm->sprites_scaled; 61578c2ecf20Sopenharmony_ci config->num_pipes_active++; 61588c2ecf20Sopenharmony_ci } 61598c2ecf20Sopenharmony_ci} 61608c2ecf20Sopenharmony_ci 61618c2ecf20Sopenharmony_cistatic void ilk_program_watermarks(struct drm_i915_private *dev_priv) 61628c2ecf20Sopenharmony_ci{ 61638c2ecf20Sopenharmony_ci struct intel_pipe_wm lp_wm_1_2 = {}, lp_wm_5_6 = {}, *best_lp_wm; 61648c2ecf20Sopenharmony_ci struct ilk_wm_maximums max; 61658c2ecf20Sopenharmony_ci struct intel_wm_config config = {}; 61668c2ecf20Sopenharmony_ci struct ilk_wm_values results = {}; 61678c2ecf20Sopenharmony_ci enum intel_ddb_partitioning partitioning; 61688c2ecf20Sopenharmony_ci 61698c2ecf20Sopenharmony_ci ilk_compute_wm_config(dev_priv, &config); 61708c2ecf20Sopenharmony_ci 61718c2ecf20Sopenharmony_ci ilk_compute_wm_maximums(dev_priv, 1, &config, INTEL_DDB_PART_1_2, &max); 61728c2ecf20Sopenharmony_ci ilk_wm_merge(dev_priv, &config, &max, &lp_wm_1_2); 61738c2ecf20Sopenharmony_ci 61748c2ecf20Sopenharmony_ci /* 5/6 split only in single pipe config on IVB+ */ 61758c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 7 && 61768c2ecf20Sopenharmony_ci config.num_pipes_active == 1 && config.sprites_enabled) { 61778c2ecf20Sopenharmony_ci ilk_compute_wm_maximums(dev_priv, 1, &config, INTEL_DDB_PART_5_6, &max); 61788c2ecf20Sopenharmony_ci ilk_wm_merge(dev_priv, &config, &max, &lp_wm_5_6); 61798c2ecf20Sopenharmony_ci 61808c2ecf20Sopenharmony_ci best_lp_wm = ilk_find_best_result(dev_priv, &lp_wm_1_2, &lp_wm_5_6); 61818c2ecf20Sopenharmony_ci } else { 61828c2ecf20Sopenharmony_ci best_lp_wm = &lp_wm_1_2; 61838c2ecf20Sopenharmony_ci } 61848c2ecf20Sopenharmony_ci 61858c2ecf20Sopenharmony_ci partitioning = (best_lp_wm == &lp_wm_1_2) ? 61868c2ecf20Sopenharmony_ci INTEL_DDB_PART_1_2 : INTEL_DDB_PART_5_6; 61878c2ecf20Sopenharmony_ci 61888c2ecf20Sopenharmony_ci ilk_compute_wm_results(dev_priv, best_lp_wm, partitioning, &results); 61898c2ecf20Sopenharmony_ci 61908c2ecf20Sopenharmony_ci ilk_write_wm_values(dev_priv, &results); 61918c2ecf20Sopenharmony_ci} 61928c2ecf20Sopenharmony_ci 61938c2ecf20Sopenharmony_cistatic void ilk_initial_watermarks(struct intel_atomic_state *state, 61948c2ecf20Sopenharmony_ci struct intel_crtc *crtc) 61958c2ecf20Sopenharmony_ci{ 61968c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 61978c2ecf20Sopenharmony_ci const struct intel_crtc_state *crtc_state = 61988c2ecf20Sopenharmony_ci intel_atomic_get_new_crtc_state(state, crtc); 61998c2ecf20Sopenharmony_ci 62008c2ecf20Sopenharmony_ci mutex_lock(&dev_priv->wm.wm_mutex); 62018c2ecf20Sopenharmony_ci crtc->wm.active.ilk = crtc_state->wm.ilk.intermediate; 62028c2ecf20Sopenharmony_ci ilk_program_watermarks(dev_priv); 62038c2ecf20Sopenharmony_ci mutex_unlock(&dev_priv->wm.wm_mutex); 62048c2ecf20Sopenharmony_ci} 62058c2ecf20Sopenharmony_ci 62068c2ecf20Sopenharmony_cistatic void ilk_optimize_watermarks(struct intel_atomic_state *state, 62078c2ecf20Sopenharmony_ci struct intel_crtc *crtc) 62088c2ecf20Sopenharmony_ci{ 62098c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 62108c2ecf20Sopenharmony_ci const struct intel_crtc_state *crtc_state = 62118c2ecf20Sopenharmony_ci intel_atomic_get_new_crtc_state(state, crtc); 62128c2ecf20Sopenharmony_ci 62138c2ecf20Sopenharmony_ci if (!crtc_state->wm.need_postvbl_update) 62148c2ecf20Sopenharmony_ci return; 62158c2ecf20Sopenharmony_ci 62168c2ecf20Sopenharmony_ci mutex_lock(&dev_priv->wm.wm_mutex); 62178c2ecf20Sopenharmony_ci crtc->wm.active.ilk = crtc_state->wm.ilk.optimal; 62188c2ecf20Sopenharmony_ci ilk_program_watermarks(dev_priv); 62198c2ecf20Sopenharmony_ci mutex_unlock(&dev_priv->wm.wm_mutex); 62208c2ecf20Sopenharmony_ci} 62218c2ecf20Sopenharmony_ci 62228c2ecf20Sopenharmony_cistatic void skl_wm_level_from_reg_val(u32 val, struct skl_wm_level *level) 62238c2ecf20Sopenharmony_ci{ 62248c2ecf20Sopenharmony_ci level->plane_en = val & PLANE_WM_EN; 62258c2ecf20Sopenharmony_ci level->ignore_lines = val & PLANE_WM_IGNORE_LINES; 62268c2ecf20Sopenharmony_ci level->plane_res_b = val & PLANE_WM_BLOCKS_MASK; 62278c2ecf20Sopenharmony_ci level->plane_res_l = (val >> PLANE_WM_LINES_SHIFT) & 62288c2ecf20Sopenharmony_ci PLANE_WM_LINES_MASK; 62298c2ecf20Sopenharmony_ci} 62308c2ecf20Sopenharmony_ci 62318c2ecf20Sopenharmony_civoid skl_pipe_wm_get_hw_state(struct intel_crtc *crtc, 62328c2ecf20Sopenharmony_ci struct skl_pipe_wm *out) 62338c2ecf20Sopenharmony_ci{ 62348c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 62358c2ecf20Sopenharmony_ci enum pipe pipe = crtc->pipe; 62368c2ecf20Sopenharmony_ci int level, max_level; 62378c2ecf20Sopenharmony_ci enum plane_id plane_id; 62388c2ecf20Sopenharmony_ci u32 val; 62398c2ecf20Sopenharmony_ci 62408c2ecf20Sopenharmony_ci max_level = ilk_wm_max_level(dev_priv); 62418c2ecf20Sopenharmony_ci 62428c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) { 62438c2ecf20Sopenharmony_ci struct skl_plane_wm *wm = &out->planes[plane_id]; 62448c2ecf20Sopenharmony_ci 62458c2ecf20Sopenharmony_ci for (level = 0; level <= max_level; level++) { 62468c2ecf20Sopenharmony_ci if (plane_id != PLANE_CURSOR) 62478c2ecf20Sopenharmony_ci val = I915_READ(PLANE_WM(pipe, plane_id, level)); 62488c2ecf20Sopenharmony_ci else 62498c2ecf20Sopenharmony_ci val = I915_READ(CUR_WM(pipe, level)); 62508c2ecf20Sopenharmony_ci 62518c2ecf20Sopenharmony_ci skl_wm_level_from_reg_val(val, &wm->wm[level]); 62528c2ecf20Sopenharmony_ci } 62538c2ecf20Sopenharmony_ci 62548c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 12) 62558c2ecf20Sopenharmony_ci wm->sagv_wm0 = wm->wm[0]; 62568c2ecf20Sopenharmony_ci 62578c2ecf20Sopenharmony_ci if (plane_id != PLANE_CURSOR) 62588c2ecf20Sopenharmony_ci val = I915_READ(PLANE_WM_TRANS(pipe, plane_id)); 62598c2ecf20Sopenharmony_ci else 62608c2ecf20Sopenharmony_ci val = I915_READ(CUR_WM_TRANS(pipe)); 62618c2ecf20Sopenharmony_ci 62628c2ecf20Sopenharmony_ci skl_wm_level_from_reg_val(val, &wm->trans_wm); 62638c2ecf20Sopenharmony_ci } 62648c2ecf20Sopenharmony_ci 62658c2ecf20Sopenharmony_ci if (!crtc->active) 62668c2ecf20Sopenharmony_ci return; 62678c2ecf20Sopenharmony_ci} 62688c2ecf20Sopenharmony_ci 62698c2ecf20Sopenharmony_civoid skl_wm_get_hw_state(struct drm_i915_private *dev_priv) 62708c2ecf20Sopenharmony_ci{ 62718c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 62728c2ecf20Sopenharmony_ci struct intel_crtc_state *crtc_state; 62738c2ecf20Sopenharmony_ci 62748c2ecf20Sopenharmony_ci for_each_intel_crtc(&dev_priv->drm, crtc) { 62758c2ecf20Sopenharmony_ci crtc_state = to_intel_crtc_state(crtc->base.state); 62768c2ecf20Sopenharmony_ci 62778c2ecf20Sopenharmony_ci skl_pipe_wm_get_hw_state(crtc, &crtc_state->wm.skl.optimal); 62788c2ecf20Sopenharmony_ci } 62798c2ecf20Sopenharmony_ci 62808c2ecf20Sopenharmony_ci if (dev_priv->active_pipes) { 62818c2ecf20Sopenharmony_ci /* Fully recompute DDB on first atomic commit */ 62828c2ecf20Sopenharmony_ci dev_priv->wm.distrust_bios_wm = true; 62838c2ecf20Sopenharmony_ci } 62848c2ecf20Sopenharmony_ci} 62858c2ecf20Sopenharmony_ci 62868c2ecf20Sopenharmony_cistatic void ilk_pipe_wm_get_hw_state(struct intel_crtc *crtc) 62878c2ecf20Sopenharmony_ci{ 62888c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->base.dev; 62898c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(dev); 62908c2ecf20Sopenharmony_ci struct ilk_wm_values *hw = &dev_priv->wm.hw; 62918c2ecf20Sopenharmony_ci struct intel_crtc_state *crtc_state = to_intel_crtc_state(crtc->base.state); 62928c2ecf20Sopenharmony_ci struct intel_pipe_wm *active = &crtc_state->wm.ilk.optimal; 62938c2ecf20Sopenharmony_ci enum pipe pipe = crtc->pipe; 62948c2ecf20Sopenharmony_ci static const i915_reg_t wm0_pipe_reg[] = { 62958c2ecf20Sopenharmony_ci [PIPE_A] = WM0_PIPEA_ILK, 62968c2ecf20Sopenharmony_ci [PIPE_B] = WM0_PIPEB_ILK, 62978c2ecf20Sopenharmony_ci [PIPE_C] = WM0_PIPEC_IVB, 62988c2ecf20Sopenharmony_ci }; 62998c2ecf20Sopenharmony_ci 63008c2ecf20Sopenharmony_ci hw->wm_pipe[pipe] = I915_READ(wm0_pipe_reg[pipe]); 63018c2ecf20Sopenharmony_ci 63028c2ecf20Sopenharmony_ci memset(active, 0, sizeof(*active)); 63038c2ecf20Sopenharmony_ci 63048c2ecf20Sopenharmony_ci active->pipe_enabled = crtc->active; 63058c2ecf20Sopenharmony_ci 63068c2ecf20Sopenharmony_ci if (active->pipe_enabled) { 63078c2ecf20Sopenharmony_ci u32 tmp = hw->wm_pipe[pipe]; 63088c2ecf20Sopenharmony_ci 63098c2ecf20Sopenharmony_ci /* 63108c2ecf20Sopenharmony_ci * For active pipes LP0 watermark is marked as 63118c2ecf20Sopenharmony_ci * enabled, and LP1+ watermaks as disabled since 63128c2ecf20Sopenharmony_ci * we can't really reverse compute them in case 63138c2ecf20Sopenharmony_ci * multiple pipes are active. 63148c2ecf20Sopenharmony_ci */ 63158c2ecf20Sopenharmony_ci active->wm[0].enable = true; 63168c2ecf20Sopenharmony_ci active->wm[0].pri_val = (tmp & WM0_PIPE_PLANE_MASK) >> WM0_PIPE_PLANE_SHIFT; 63178c2ecf20Sopenharmony_ci active->wm[0].spr_val = (tmp & WM0_PIPE_SPRITE_MASK) >> WM0_PIPE_SPRITE_SHIFT; 63188c2ecf20Sopenharmony_ci active->wm[0].cur_val = tmp & WM0_PIPE_CURSOR_MASK; 63198c2ecf20Sopenharmony_ci } else { 63208c2ecf20Sopenharmony_ci int level, max_level = ilk_wm_max_level(dev_priv); 63218c2ecf20Sopenharmony_ci 63228c2ecf20Sopenharmony_ci /* 63238c2ecf20Sopenharmony_ci * For inactive pipes, all watermark levels 63248c2ecf20Sopenharmony_ci * should be marked as enabled but zeroed, 63258c2ecf20Sopenharmony_ci * which is what we'd compute them to. 63268c2ecf20Sopenharmony_ci */ 63278c2ecf20Sopenharmony_ci for (level = 0; level <= max_level; level++) 63288c2ecf20Sopenharmony_ci active->wm[level].enable = true; 63298c2ecf20Sopenharmony_ci } 63308c2ecf20Sopenharmony_ci 63318c2ecf20Sopenharmony_ci crtc->wm.active.ilk = *active; 63328c2ecf20Sopenharmony_ci} 63338c2ecf20Sopenharmony_ci 63348c2ecf20Sopenharmony_ci#define _FW_WM(value, plane) \ 63358c2ecf20Sopenharmony_ci (((value) & DSPFW_ ## plane ## _MASK) >> DSPFW_ ## plane ## _SHIFT) 63368c2ecf20Sopenharmony_ci#define _FW_WM_VLV(value, plane) \ 63378c2ecf20Sopenharmony_ci (((value) & DSPFW_ ## plane ## _MASK_VLV) >> DSPFW_ ## plane ## _SHIFT) 63388c2ecf20Sopenharmony_ci 63398c2ecf20Sopenharmony_cistatic void g4x_read_wm_values(struct drm_i915_private *dev_priv, 63408c2ecf20Sopenharmony_ci struct g4x_wm_values *wm) 63418c2ecf20Sopenharmony_ci{ 63428c2ecf20Sopenharmony_ci u32 tmp; 63438c2ecf20Sopenharmony_ci 63448c2ecf20Sopenharmony_ci tmp = I915_READ(DSPFW1); 63458c2ecf20Sopenharmony_ci wm->sr.plane = _FW_WM(tmp, SR); 63468c2ecf20Sopenharmony_ci wm->pipe[PIPE_B].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORB); 63478c2ecf20Sopenharmony_ci wm->pipe[PIPE_B].plane[PLANE_PRIMARY] = _FW_WM(tmp, PLANEB); 63488c2ecf20Sopenharmony_ci wm->pipe[PIPE_A].plane[PLANE_PRIMARY] = _FW_WM(tmp, PLANEA); 63498c2ecf20Sopenharmony_ci 63508c2ecf20Sopenharmony_ci tmp = I915_READ(DSPFW2); 63518c2ecf20Sopenharmony_ci wm->fbc_en = tmp & DSPFW_FBC_SR_EN; 63528c2ecf20Sopenharmony_ci wm->sr.fbc = _FW_WM(tmp, FBC_SR); 63538c2ecf20Sopenharmony_ci wm->hpll.fbc = _FW_WM(tmp, FBC_HPLL_SR); 63548c2ecf20Sopenharmony_ci wm->pipe[PIPE_B].plane[PLANE_SPRITE0] = _FW_WM(tmp, SPRITEB); 63558c2ecf20Sopenharmony_ci wm->pipe[PIPE_A].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORA); 63568c2ecf20Sopenharmony_ci wm->pipe[PIPE_A].plane[PLANE_SPRITE0] = _FW_WM(tmp, SPRITEA); 63578c2ecf20Sopenharmony_ci 63588c2ecf20Sopenharmony_ci tmp = I915_READ(DSPFW3); 63598c2ecf20Sopenharmony_ci wm->hpll_en = tmp & DSPFW_HPLL_SR_EN; 63608c2ecf20Sopenharmony_ci wm->sr.cursor = _FW_WM(tmp, CURSOR_SR); 63618c2ecf20Sopenharmony_ci wm->hpll.cursor = _FW_WM(tmp, HPLL_CURSOR); 63628c2ecf20Sopenharmony_ci wm->hpll.plane = _FW_WM(tmp, HPLL_SR); 63638c2ecf20Sopenharmony_ci} 63648c2ecf20Sopenharmony_ci 63658c2ecf20Sopenharmony_cistatic void vlv_read_wm_values(struct drm_i915_private *dev_priv, 63668c2ecf20Sopenharmony_ci struct vlv_wm_values *wm) 63678c2ecf20Sopenharmony_ci{ 63688c2ecf20Sopenharmony_ci enum pipe pipe; 63698c2ecf20Sopenharmony_ci u32 tmp; 63708c2ecf20Sopenharmony_ci 63718c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 63728c2ecf20Sopenharmony_ci tmp = I915_READ(VLV_DDL(pipe)); 63738c2ecf20Sopenharmony_ci 63748c2ecf20Sopenharmony_ci wm->ddl[pipe].plane[PLANE_PRIMARY] = 63758c2ecf20Sopenharmony_ci (tmp >> DDL_PLANE_SHIFT) & (DDL_PRECISION_HIGH | DRAIN_LATENCY_MASK); 63768c2ecf20Sopenharmony_ci wm->ddl[pipe].plane[PLANE_CURSOR] = 63778c2ecf20Sopenharmony_ci (tmp >> DDL_CURSOR_SHIFT) & (DDL_PRECISION_HIGH | DRAIN_LATENCY_MASK); 63788c2ecf20Sopenharmony_ci wm->ddl[pipe].plane[PLANE_SPRITE0] = 63798c2ecf20Sopenharmony_ci (tmp >> DDL_SPRITE_SHIFT(0)) & (DDL_PRECISION_HIGH | DRAIN_LATENCY_MASK); 63808c2ecf20Sopenharmony_ci wm->ddl[pipe].plane[PLANE_SPRITE1] = 63818c2ecf20Sopenharmony_ci (tmp >> DDL_SPRITE_SHIFT(1)) & (DDL_PRECISION_HIGH | DRAIN_LATENCY_MASK); 63828c2ecf20Sopenharmony_ci } 63838c2ecf20Sopenharmony_ci 63848c2ecf20Sopenharmony_ci tmp = I915_READ(DSPFW1); 63858c2ecf20Sopenharmony_ci wm->sr.plane = _FW_WM(tmp, SR); 63868c2ecf20Sopenharmony_ci wm->pipe[PIPE_B].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORB); 63878c2ecf20Sopenharmony_ci wm->pipe[PIPE_B].plane[PLANE_PRIMARY] = _FW_WM_VLV(tmp, PLANEB); 63888c2ecf20Sopenharmony_ci wm->pipe[PIPE_A].plane[PLANE_PRIMARY] = _FW_WM_VLV(tmp, PLANEA); 63898c2ecf20Sopenharmony_ci 63908c2ecf20Sopenharmony_ci tmp = I915_READ(DSPFW2); 63918c2ecf20Sopenharmony_ci wm->pipe[PIPE_A].plane[PLANE_SPRITE1] = _FW_WM_VLV(tmp, SPRITEB); 63928c2ecf20Sopenharmony_ci wm->pipe[PIPE_A].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORA); 63938c2ecf20Sopenharmony_ci wm->pipe[PIPE_A].plane[PLANE_SPRITE0] = _FW_WM_VLV(tmp, SPRITEA); 63948c2ecf20Sopenharmony_ci 63958c2ecf20Sopenharmony_ci tmp = I915_READ(DSPFW3); 63968c2ecf20Sopenharmony_ci wm->sr.cursor = _FW_WM(tmp, CURSOR_SR); 63978c2ecf20Sopenharmony_ci 63988c2ecf20Sopenharmony_ci if (IS_CHERRYVIEW(dev_priv)) { 63998c2ecf20Sopenharmony_ci tmp = I915_READ(DSPFW7_CHV); 64008c2ecf20Sopenharmony_ci wm->pipe[PIPE_B].plane[PLANE_SPRITE1] = _FW_WM_VLV(tmp, SPRITED); 64018c2ecf20Sopenharmony_ci wm->pipe[PIPE_B].plane[PLANE_SPRITE0] = _FW_WM_VLV(tmp, SPRITEC); 64028c2ecf20Sopenharmony_ci 64038c2ecf20Sopenharmony_ci tmp = I915_READ(DSPFW8_CHV); 64048c2ecf20Sopenharmony_ci wm->pipe[PIPE_C].plane[PLANE_SPRITE1] = _FW_WM_VLV(tmp, SPRITEF); 64058c2ecf20Sopenharmony_ci wm->pipe[PIPE_C].plane[PLANE_SPRITE0] = _FW_WM_VLV(tmp, SPRITEE); 64068c2ecf20Sopenharmony_ci 64078c2ecf20Sopenharmony_ci tmp = I915_READ(DSPFW9_CHV); 64088c2ecf20Sopenharmony_ci wm->pipe[PIPE_C].plane[PLANE_PRIMARY] = _FW_WM_VLV(tmp, PLANEC); 64098c2ecf20Sopenharmony_ci wm->pipe[PIPE_C].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORC); 64108c2ecf20Sopenharmony_ci 64118c2ecf20Sopenharmony_ci tmp = I915_READ(DSPHOWM); 64128c2ecf20Sopenharmony_ci wm->sr.plane |= _FW_WM(tmp, SR_HI) << 9; 64138c2ecf20Sopenharmony_ci wm->pipe[PIPE_C].plane[PLANE_SPRITE1] |= _FW_WM(tmp, SPRITEF_HI) << 8; 64148c2ecf20Sopenharmony_ci wm->pipe[PIPE_C].plane[PLANE_SPRITE0] |= _FW_WM(tmp, SPRITEE_HI) << 8; 64158c2ecf20Sopenharmony_ci wm->pipe[PIPE_C].plane[PLANE_PRIMARY] |= _FW_WM(tmp, PLANEC_HI) << 8; 64168c2ecf20Sopenharmony_ci wm->pipe[PIPE_B].plane[PLANE_SPRITE1] |= _FW_WM(tmp, SPRITED_HI) << 8; 64178c2ecf20Sopenharmony_ci wm->pipe[PIPE_B].plane[PLANE_SPRITE0] |= _FW_WM(tmp, SPRITEC_HI) << 8; 64188c2ecf20Sopenharmony_ci wm->pipe[PIPE_B].plane[PLANE_PRIMARY] |= _FW_WM(tmp, PLANEB_HI) << 8; 64198c2ecf20Sopenharmony_ci wm->pipe[PIPE_A].plane[PLANE_SPRITE1] |= _FW_WM(tmp, SPRITEB_HI) << 8; 64208c2ecf20Sopenharmony_ci wm->pipe[PIPE_A].plane[PLANE_SPRITE0] |= _FW_WM(tmp, SPRITEA_HI) << 8; 64218c2ecf20Sopenharmony_ci wm->pipe[PIPE_A].plane[PLANE_PRIMARY] |= _FW_WM(tmp, PLANEA_HI) << 8; 64228c2ecf20Sopenharmony_ci } else { 64238c2ecf20Sopenharmony_ci tmp = I915_READ(DSPFW7); 64248c2ecf20Sopenharmony_ci wm->pipe[PIPE_B].plane[PLANE_SPRITE1] = _FW_WM_VLV(tmp, SPRITED); 64258c2ecf20Sopenharmony_ci wm->pipe[PIPE_B].plane[PLANE_SPRITE0] = _FW_WM_VLV(tmp, SPRITEC); 64268c2ecf20Sopenharmony_ci 64278c2ecf20Sopenharmony_ci tmp = I915_READ(DSPHOWM); 64288c2ecf20Sopenharmony_ci wm->sr.plane |= _FW_WM(tmp, SR_HI) << 9; 64298c2ecf20Sopenharmony_ci wm->pipe[PIPE_B].plane[PLANE_SPRITE1] |= _FW_WM(tmp, SPRITED_HI) << 8; 64308c2ecf20Sopenharmony_ci wm->pipe[PIPE_B].plane[PLANE_SPRITE0] |= _FW_WM(tmp, SPRITEC_HI) << 8; 64318c2ecf20Sopenharmony_ci wm->pipe[PIPE_B].plane[PLANE_PRIMARY] |= _FW_WM(tmp, PLANEB_HI) << 8; 64328c2ecf20Sopenharmony_ci wm->pipe[PIPE_A].plane[PLANE_SPRITE1] |= _FW_WM(tmp, SPRITEB_HI) << 8; 64338c2ecf20Sopenharmony_ci wm->pipe[PIPE_A].plane[PLANE_SPRITE0] |= _FW_WM(tmp, SPRITEA_HI) << 8; 64348c2ecf20Sopenharmony_ci wm->pipe[PIPE_A].plane[PLANE_PRIMARY] |= _FW_WM(tmp, PLANEA_HI) << 8; 64358c2ecf20Sopenharmony_ci } 64368c2ecf20Sopenharmony_ci} 64378c2ecf20Sopenharmony_ci 64388c2ecf20Sopenharmony_ci#undef _FW_WM 64398c2ecf20Sopenharmony_ci#undef _FW_WM_VLV 64408c2ecf20Sopenharmony_ci 64418c2ecf20Sopenharmony_civoid g4x_wm_get_hw_state(struct drm_i915_private *dev_priv) 64428c2ecf20Sopenharmony_ci{ 64438c2ecf20Sopenharmony_ci struct g4x_wm_values *wm = &dev_priv->wm.g4x; 64448c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 64458c2ecf20Sopenharmony_ci 64468c2ecf20Sopenharmony_ci g4x_read_wm_values(dev_priv, wm); 64478c2ecf20Sopenharmony_ci 64488c2ecf20Sopenharmony_ci wm->cxsr = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN; 64498c2ecf20Sopenharmony_ci 64508c2ecf20Sopenharmony_ci for_each_intel_crtc(&dev_priv->drm, crtc) { 64518c2ecf20Sopenharmony_ci struct intel_crtc_state *crtc_state = 64528c2ecf20Sopenharmony_ci to_intel_crtc_state(crtc->base.state); 64538c2ecf20Sopenharmony_ci struct g4x_wm_state *active = &crtc->wm.active.g4x; 64548c2ecf20Sopenharmony_ci struct g4x_pipe_wm *raw; 64558c2ecf20Sopenharmony_ci enum pipe pipe = crtc->pipe; 64568c2ecf20Sopenharmony_ci enum plane_id plane_id; 64578c2ecf20Sopenharmony_ci int level, max_level; 64588c2ecf20Sopenharmony_ci 64598c2ecf20Sopenharmony_ci active->cxsr = wm->cxsr; 64608c2ecf20Sopenharmony_ci active->hpll_en = wm->hpll_en; 64618c2ecf20Sopenharmony_ci active->fbc_en = wm->fbc_en; 64628c2ecf20Sopenharmony_ci 64638c2ecf20Sopenharmony_ci active->sr = wm->sr; 64648c2ecf20Sopenharmony_ci active->hpll = wm->hpll; 64658c2ecf20Sopenharmony_ci 64668c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) { 64678c2ecf20Sopenharmony_ci active->wm.plane[plane_id] = 64688c2ecf20Sopenharmony_ci wm->pipe[pipe].plane[plane_id]; 64698c2ecf20Sopenharmony_ci } 64708c2ecf20Sopenharmony_ci 64718c2ecf20Sopenharmony_ci if (wm->cxsr && wm->hpll_en) 64728c2ecf20Sopenharmony_ci max_level = G4X_WM_LEVEL_HPLL; 64738c2ecf20Sopenharmony_ci else if (wm->cxsr) 64748c2ecf20Sopenharmony_ci max_level = G4X_WM_LEVEL_SR; 64758c2ecf20Sopenharmony_ci else 64768c2ecf20Sopenharmony_ci max_level = G4X_WM_LEVEL_NORMAL; 64778c2ecf20Sopenharmony_ci 64788c2ecf20Sopenharmony_ci level = G4X_WM_LEVEL_NORMAL; 64798c2ecf20Sopenharmony_ci raw = &crtc_state->wm.g4x.raw[level]; 64808c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) 64818c2ecf20Sopenharmony_ci raw->plane[plane_id] = active->wm.plane[plane_id]; 64828c2ecf20Sopenharmony_ci 64838c2ecf20Sopenharmony_ci if (++level > max_level) 64848c2ecf20Sopenharmony_ci goto out; 64858c2ecf20Sopenharmony_ci 64868c2ecf20Sopenharmony_ci raw = &crtc_state->wm.g4x.raw[level]; 64878c2ecf20Sopenharmony_ci raw->plane[PLANE_PRIMARY] = active->sr.plane; 64888c2ecf20Sopenharmony_ci raw->plane[PLANE_CURSOR] = active->sr.cursor; 64898c2ecf20Sopenharmony_ci raw->plane[PLANE_SPRITE0] = 0; 64908c2ecf20Sopenharmony_ci raw->fbc = active->sr.fbc; 64918c2ecf20Sopenharmony_ci 64928c2ecf20Sopenharmony_ci if (++level > max_level) 64938c2ecf20Sopenharmony_ci goto out; 64948c2ecf20Sopenharmony_ci 64958c2ecf20Sopenharmony_ci raw = &crtc_state->wm.g4x.raw[level]; 64968c2ecf20Sopenharmony_ci raw->plane[PLANE_PRIMARY] = active->hpll.plane; 64978c2ecf20Sopenharmony_ci raw->plane[PLANE_CURSOR] = active->hpll.cursor; 64988c2ecf20Sopenharmony_ci raw->plane[PLANE_SPRITE0] = 0; 64998c2ecf20Sopenharmony_ci raw->fbc = active->hpll.fbc; 65008c2ecf20Sopenharmony_ci 65018c2ecf20Sopenharmony_ci out: 65028c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) 65038c2ecf20Sopenharmony_ci g4x_raw_plane_wm_set(crtc_state, level, 65048c2ecf20Sopenharmony_ci plane_id, USHRT_MAX); 65058c2ecf20Sopenharmony_ci g4x_raw_fbc_wm_set(crtc_state, level, USHRT_MAX); 65068c2ecf20Sopenharmony_ci 65078c2ecf20Sopenharmony_ci crtc_state->wm.g4x.optimal = *active; 65088c2ecf20Sopenharmony_ci crtc_state->wm.g4x.intermediate = *active; 65098c2ecf20Sopenharmony_ci 65108c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 65118c2ecf20Sopenharmony_ci "Initial watermarks: pipe %c, plane=%d, cursor=%d, sprite=%d\n", 65128c2ecf20Sopenharmony_ci pipe_name(pipe), 65138c2ecf20Sopenharmony_ci wm->pipe[pipe].plane[PLANE_PRIMARY], 65148c2ecf20Sopenharmony_ci wm->pipe[pipe].plane[PLANE_CURSOR], 65158c2ecf20Sopenharmony_ci wm->pipe[pipe].plane[PLANE_SPRITE0]); 65168c2ecf20Sopenharmony_ci } 65178c2ecf20Sopenharmony_ci 65188c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 65198c2ecf20Sopenharmony_ci "Initial SR watermarks: plane=%d, cursor=%d fbc=%d\n", 65208c2ecf20Sopenharmony_ci wm->sr.plane, wm->sr.cursor, wm->sr.fbc); 65218c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 65228c2ecf20Sopenharmony_ci "Initial HPLL watermarks: plane=%d, SR cursor=%d fbc=%d\n", 65238c2ecf20Sopenharmony_ci wm->hpll.plane, wm->hpll.cursor, wm->hpll.fbc); 65248c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, "Initial SR=%s HPLL=%s FBC=%s\n", 65258c2ecf20Sopenharmony_ci yesno(wm->cxsr), yesno(wm->hpll_en), yesno(wm->fbc_en)); 65268c2ecf20Sopenharmony_ci} 65278c2ecf20Sopenharmony_ci 65288c2ecf20Sopenharmony_civoid g4x_wm_sanitize(struct drm_i915_private *dev_priv) 65298c2ecf20Sopenharmony_ci{ 65308c2ecf20Sopenharmony_ci struct intel_plane *plane; 65318c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 65328c2ecf20Sopenharmony_ci 65338c2ecf20Sopenharmony_ci mutex_lock(&dev_priv->wm.wm_mutex); 65348c2ecf20Sopenharmony_ci 65358c2ecf20Sopenharmony_ci for_each_intel_plane(&dev_priv->drm, plane) { 65368c2ecf20Sopenharmony_ci struct intel_crtc *crtc = 65378c2ecf20Sopenharmony_ci intel_get_crtc_for_pipe(dev_priv, plane->pipe); 65388c2ecf20Sopenharmony_ci struct intel_crtc_state *crtc_state = 65398c2ecf20Sopenharmony_ci to_intel_crtc_state(crtc->base.state); 65408c2ecf20Sopenharmony_ci struct intel_plane_state *plane_state = 65418c2ecf20Sopenharmony_ci to_intel_plane_state(plane->base.state); 65428c2ecf20Sopenharmony_ci struct g4x_wm_state *wm_state = &crtc_state->wm.g4x.optimal; 65438c2ecf20Sopenharmony_ci enum plane_id plane_id = plane->id; 65448c2ecf20Sopenharmony_ci int level; 65458c2ecf20Sopenharmony_ci 65468c2ecf20Sopenharmony_ci if (plane_state->uapi.visible) 65478c2ecf20Sopenharmony_ci continue; 65488c2ecf20Sopenharmony_ci 65498c2ecf20Sopenharmony_ci for (level = 0; level < 3; level++) { 65508c2ecf20Sopenharmony_ci struct g4x_pipe_wm *raw = 65518c2ecf20Sopenharmony_ci &crtc_state->wm.g4x.raw[level]; 65528c2ecf20Sopenharmony_ci 65538c2ecf20Sopenharmony_ci raw->plane[plane_id] = 0; 65548c2ecf20Sopenharmony_ci wm_state->wm.plane[plane_id] = 0; 65558c2ecf20Sopenharmony_ci } 65568c2ecf20Sopenharmony_ci 65578c2ecf20Sopenharmony_ci if (plane_id == PLANE_PRIMARY) { 65588c2ecf20Sopenharmony_ci for (level = 0; level < 3; level++) { 65598c2ecf20Sopenharmony_ci struct g4x_pipe_wm *raw = 65608c2ecf20Sopenharmony_ci &crtc_state->wm.g4x.raw[level]; 65618c2ecf20Sopenharmony_ci raw->fbc = 0; 65628c2ecf20Sopenharmony_ci } 65638c2ecf20Sopenharmony_ci 65648c2ecf20Sopenharmony_ci wm_state->sr.fbc = 0; 65658c2ecf20Sopenharmony_ci wm_state->hpll.fbc = 0; 65668c2ecf20Sopenharmony_ci wm_state->fbc_en = false; 65678c2ecf20Sopenharmony_ci } 65688c2ecf20Sopenharmony_ci } 65698c2ecf20Sopenharmony_ci 65708c2ecf20Sopenharmony_ci for_each_intel_crtc(&dev_priv->drm, crtc) { 65718c2ecf20Sopenharmony_ci struct intel_crtc_state *crtc_state = 65728c2ecf20Sopenharmony_ci to_intel_crtc_state(crtc->base.state); 65738c2ecf20Sopenharmony_ci 65748c2ecf20Sopenharmony_ci crtc_state->wm.g4x.intermediate = 65758c2ecf20Sopenharmony_ci crtc_state->wm.g4x.optimal; 65768c2ecf20Sopenharmony_ci crtc->wm.active.g4x = crtc_state->wm.g4x.optimal; 65778c2ecf20Sopenharmony_ci } 65788c2ecf20Sopenharmony_ci 65798c2ecf20Sopenharmony_ci g4x_program_watermarks(dev_priv); 65808c2ecf20Sopenharmony_ci 65818c2ecf20Sopenharmony_ci mutex_unlock(&dev_priv->wm.wm_mutex); 65828c2ecf20Sopenharmony_ci} 65838c2ecf20Sopenharmony_ci 65848c2ecf20Sopenharmony_civoid vlv_wm_get_hw_state(struct drm_i915_private *dev_priv) 65858c2ecf20Sopenharmony_ci{ 65868c2ecf20Sopenharmony_ci struct vlv_wm_values *wm = &dev_priv->wm.vlv; 65878c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 65888c2ecf20Sopenharmony_ci u32 val; 65898c2ecf20Sopenharmony_ci 65908c2ecf20Sopenharmony_ci vlv_read_wm_values(dev_priv, wm); 65918c2ecf20Sopenharmony_ci 65928c2ecf20Sopenharmony_ci wm->cxsr = I915_READ(FW_BLC_SELF_VLV) & FW_CSPWRDWNEN; 65938c2ecf20Sopenharmony_ci wm->level = VLV_WM_LEVEL_PM2; 65948c2ecf20Sopenharmony_ci 65958c2ecf20Sopenharmony_ci if (IS_CHERRYVIEW(dev_priv)) { 65968c2ecf20Sopenharmony_ci vlv_punit_get(dev_priv); 65978c2ecf20Sopenharmony_ci 65988c2ecf20Sopenharmony_ci val = vlv_punit_read(dev_priv, PUNIT_REG_DSPSSPM); 65998c2ecf20Sopenharmony_ci if (val & DSP_MAXFIFO_PM5_ENABLE) 66008c2ecf20Sopenharmony_ci wm->level = VLV_WM_LEVEL_PM5; 66018c2ecf20Sopenharmony_ci 66028c2ecf20Sopenharmony_ci /* 66038c2ecf20Sopenharmony_ci * If DDR DVFS is disabled in the BIOS, Punit 66048c2ecf20Sopenharmony_ci * will never ack the request. So if that happens 66058c2ecf20Sopenharmony_ci * assume we don't have to enable/disable DDR DVFS 66068c2ecf20Sopenharmony_ci * dynamically. To test that just set the REQ_ACK 66078c2ecf20Sopenharmony_ci * bit to poke the Punit, but don't change the 66088c2ecf20Sopenharmony_ci * HIGH/LOW bits so that we don't actually change 66098c2ecf20Sopenharmony_ci * the current state. 66108c2ecf20Sopenharmony_ci */ 66118c2ecf20Sopenharmony_ci val = vlv_punit_read(dev_priv, PUNIT_REG_DDR_SETUP2); 66128c2ecf20Sopenharmony_ci val |= FORCE_DDR_FREQ_REQ_ACK; 66138c2ecf20Sopenharmony_ci vlv_punit_write(dev_priv, PUNIT_REG_DDR_SETUP2, val); 66148c2ecf20Sopenharmony_ci 66158c2ecf20Sopenharmony_ci if (wait_for((vlv_punit_read(dev_priv, PUNIT_REG_DDR_SETUP2) & 66168c2ecf20Sopenharmony_ci FORCE_DDR_FREQ_REQ_ACK) == 0, 3)) { 66178c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 66188c2ecf20Sopenharmony_ci "Punit not acking DDR DVFS request, " 66198c2ecf20Sopenharmony_ci "assuming DDR DVFS is disabled\n"); 66208c2ecf20Sopenharmony_ci dev_priv->wm.max_level = VLV_WM_LEVEL_PM5; 66218c2ecf20Sopenharmony_ci } else { 66228c2ecf20Sopenharmony_ci val = vlv_punit_read(dev_priv, PUNIT_REG_DDR_SETUP2); 66238c2ecf20Sopenharmony_ci if ((val & FORCE_DDR_HIGH_FREQ) == 0) 66248c2ecf20Sopenharmony_ci wm->level = VLV_WM_LEVEL_DDR_DVFS; 66258c2ecf20Sopenharmony_ci } 66268c2ecf20Sopenharmony_ci 66278c2ecf20Sopenharmony_ci vlv_punit_put(dev_priv); 66288c2ecf20Sopenharmony_ci } 66298c2ecf20Sopenharmony_ci 66308c2ecf20Sopenharmony_ci for_each_intel_crtc(&dev_priv->drm, crtc) { 66318c2ecf20Sopenharmony_ci struct intel_crtc_state *crtc_state = 66328c2ecf20Sopenharmony_ci to_intel_crtc_state(crtc->base.state); 66338c2ecf20Sopenharmony_ci struct vlv_wm_state *active = &crtc->wm.active.vlv; 66348c2ecf20Sopenharmony_ci const struct vlv_fifo_state *fifo_state = 66358c2ecf20Sopenharmony_ci &crtc_state->wm.vlv.fifo_state; 66368c2ecf20Sopenharmony_ci enum pipe pipe = crtc->pipe; 66378c2ecf20Sopenharmony_ci enum plane_id plane_id; 66388c2ecf20Sopenharmony_ci int level; 66398c2ecf20Sopenharmony_ci 66408c2ecf20Sopenharmony_ci vlv_get_fifo_size(crtc_state); 66418c2ecf20Sopenharmony_ci 66428c2ecf20Sopenharmony_ci active->num_levels = wm->level + 1; 66438c2ecf20Sopenharmony_ci active->cxsr = wm->cxsr; 66448c2ecf20Sopenharmony_ci 66458c2ecf20Sopenharmony_ci for (level = 0; level < active->num_levels; level++) { 66468c2ecf20Sopenharmony_ci struct g4x_pipe_wm *raw = 66478c2ecf20Sopenharmony_ci &crtc_state->wm.vlv.raw[level]; 66488c2ecf20Sopenharmony_ci 66498c2ecf20Sopenharmony_ci active->sr[level].plane = wm->sr.plane; 66508c2ecf20Sopenharmony_ci active->sr[level].cursor = wm->sr.cursor; 66518c2ecf20Sopenharmony_ci 66528c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) { 66538c2ecf20Sopenharmony_ci active->wm[level].plane[plane_id] = 66548c2ecf20Sopenharmony_ci wm->pipe[pipe].plane[plane_id]; 66558c2ecf20Sopenharmony_ci 66568c2ecf20Sopenharmony_ci raw->plane[plane_id] = 66578c2ecf20Sopenharmony_ci vlv_invert_wm_value(active->wm[level].plane[plane_id], 66588c2ecf20Sopenharmony_ci fifo_state->plane[plane_id]); 66598c2ecf20Sopenharmony_ci } 66608c2ecf20Sopenharmony_ci } 66618c2ecf20Sopenharmony_ci 66628c2ecf20Sopenharmony_ci for_each_plane_id_on_crtc(crtc, plane_id) 66638c2ecf20Sopenharmony_ci vlv_raw_plane_wm_set(crtc_state, level, 66648c2ecf20Sopenharmony_ci plane_id, USHRT_MAX); 66658c2ecf20Sopenharmony_ci vlv_invalidate_wms(crtc, active, level); 66668c2ecf20Sopenharmony_ci 66678c2ecf20Sopenharmony_ci crtc_state->wm.vlv.optimal = *active; 66688c2ecf20Sopenharmony_ci crtc_state->wm.vlv.intermediate = *active; 66698c2ecf20Sopenharmony_ci 66708c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 66718c2ecf20Sopenharmony_ci "Initial watermarks: pipe %c, plane=%d, cursor=%d, sprite0=%d, sprite1=%d\n", 66728c2ecf20Sopenharmony_ci pipe_name(pipe), 66738c2ecf20Sopenharmony_ci wm->pipe[pipe].plane[PLANE_PRIMARY], 66748c2ecf20Sopenharmony_ci wm->pipe[pipe].plane[PLANE_CURSOR], 66758c2ecf20Sopenharmony_ci wm->pipe[pipe].plane[PLANE_SPRITE0], 66768c2ecf20Sopenharmony_ci wm->pipe[pipe].plane[PLANE_SPRITE1]); 66778c2ecf20Sopenharmony_ci } 66788c2ecf20Sopenharmony_ci 66798c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 66808c2ecf20Sopenharmony_ci "Initial watermarks: SR plane=%d, SR cursor=%d level=%d cxsr=%d\n", 66818c2ecf20Sopenharmony_ci wm->sr.plane, wm->sr.cursor, wm->level, wm->cxsr); 66828c2ecf20Sopenharmony_ci} 66838c2ecf20Sopenharmony_ci 66848c2ecf20Sopenharmony_civoid vlv_wm_sanitize(struct drm_i915_private *dev_priv) 66858c2ecf20Sopenharmony_ci{ 66868c2ecf20Sopenharmony_ci struct intel_plane *plane; 66878c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 66888c2ecf20Sopenharmony_ci 66898c2ecf20Sopenharmony_ci mutex_lock(&dev_priv->wm.wm_mutex); 66908c2ecf20Sopenharmony_ci 66918c2ecf20Sopenharmony_ci for_each_intel_plane(&dev_priv->drm, plane) { 66928c2ecf20Sopenharmony_ci struct intel_crtc *crtc = 66938c2ecf20Sopenharmony_ci intel_get_crtc_for_pipe(dev_priv, plane->pipe); 66948c2ecf20Sopenharmony_ci struct intel_crtc_state *crtc_state = 66958c2ecf20Sopenharmony_ci to_intel_crtc_state(crtc->base.state); 66968c2ecf20Sopenharmony_ci struct intel_plane_state *plane_state = 66978c2ecf20Sopenharmony_ci to_intel_plane_state(plane->base.state); 66988c2ecf20Sopenharmony_ci struct vlv_wm_state *wm_state = &crtc_state->wm.vlv.optimal; 66998c2ecf20Sopenharmony_ci const struct vlv_fifo_state *fifo_state = 67008c2ecf20Sopenharmony_ci &crtc_state->wm.vlv.fifo_state; 67018c2ecf20Sopenharmony_ci enum plane_id plane_id = plane->id; 67028c2ecf20Sopenharmony_ci int level; 67038c2ecf20Sopenharmony_ci 67048c2ecf20Sopenharmony_ci if (plane_state->uapi.visible) 67058c2ecf20Sopenharmony_ci continue; 67068c2ecf20Sopenharmony_ci 67078c2ecf20Sopenharmony_ci for (level = 0; level < wm_state->num_levels; level++) { 67088c2ecf20Sopenharmony_ci struct g4x_pipe_wm *raw = 67098c2ecf20Sopenharmony_ci &crtc_state->wm.vlv.raw[level]; 67108c2ecf20Sopenharmony_ci 67118c2ecf20Sopenharmony_ci raw->plane[plane_id] = 0; 67128c2ecf20Sopenharmony_ci 67138c2ecf20Sopenharmony_ci wm_state->wm[level].plane[plane_id] = 67148c2ecf20Sopenharmony_ci vlv_invert_wm_value(raw->plane[plane_id], 67158c2ecf20Sopenharmony_ci fifo_state->plane[plane_id]); 67168c2ecf20Sopenharmony_ci } 67178c2ecf20Sopenharmony_ci } 67188c2ecf20Sopenharmony_ci 67198c2ecf20Sopenharmony_ci for_each_intel_crtc(&dev_priv->drm, crtc) { 67208c2ecf20Sopenharmony_ci struct intel_crtc_state *crtc_state = 67218c2ecf20Sopenharmony_ci to_intel_crtc_state(crtc->base.state); 67228c2ecf20Sopenharmony_ci 67238c2ecf20Sopenharmony_ci crtc_state->wm.vlv.intermediate = 67248c2ecf20Sopenharmony_ci crtc_state->wm.vlv.optimal; 67258c2ecf20Sopenharmony_ci crtc->wm.active.vlv = crtc_state->wm.vlv.optimal; 67268c2ecf20Sopenharmony_ci } 67278c2ecf20Sopenharmony_ci 67288c2ecf20Sopenharmony_ci vlv_program_watermarks(dev_priv); 67298c2ecf20Sopenharmony_ci 67308c2ecf20Sopenharmony_ci mutex_unlock(&dev_priv->wm.wm_mutex); 67318c2ecf20Sopenharmony_ci} 67328c2ecf20Sopenharmony_ci 67338c2ecf20Sopenharmony_ci/* 67348c2ecf20Sopenharmony_ci * FIXME should probably kill this and improve 67358c2ecf20Sopenharmony_ci * the real watermark readout/sanitation instead 67368c2ecf20Sopenharmony_ci */ 67378c2ecf20Sopenharmony_cistatic void ilk_init_lp_watermarks(struct drm_i915_private *dev_priv) 67388c2ecf20Sopenharmony_ci{ 67398c2ecf20Sopenharmony_ci I915_WRITE(WM3_LP_ILK, I915_READ(WM3_LP_ILK) & ~WM1_LP_SR_EN); 67408c2ecf20Sopenharmony_ci I915_WRITE(WM2_LP_ILK, I915_READ(WM2_LP_ILK) & ~WM1_LP_SR_EN); 67418c2ecf20Sopenharmony_ci I915_WRITE(WM1_LP_ILK, I915_READ(WM1_LP_ILK) & ~WM1_LP_SR_EN); 67428c2ecf20Sopenharmony_ci 67438c2ecf20Sopenharmony_ci /* 67448c2ecf20Sopenharmony_ci * Don't touch WM1S_LP_EN here. 67458c2ecf20Sopenharmony_ci * Doing so could cause underruns. 67468c2ecf20Sopenharmony_ci */ 67478c2ecf20Sopenharmony_ci} 67488c2ecf20Sopenharmony_ci 67498c2ecf20Sopenharmony_civoid ilk_wm_get_hw_state(struct drm_i915_private *dev_priv) 67508c2ecf20Sopenharmony_ci{ 67518c2ecf20Sopenharmony_ci struct ilk_wm_values *hw = &dev_priv->wm.hw; 67528c2ecf20Sopenharmony_ci struct intel_crtc *crtc; 67538c2ecf20Sopenharmony_ci 67548c2ecf20Sopenharmony_ci ilk_init_lp_watermarks(dev_priv); 67558c2ecf20Sopenharmony_ci 67568c2ecf20Sopenharmony_ci for_each_intel_crtc(&dev_priv->drm, crtc) 67578c2ecf20Sopenharmony_ci ilk_pipe_wm_get_hw_state(crtc); 67588c2ecf20Sopenharmony_ci 67598c2ecf20Sopenharmony_ci hw->wm_lp[0] = I915_READ(WM1_LP_ILK); 67608c2ecf20Sopenharmony_ci hw->wm_lp[1] = I915_READ(WM2_LP_ILK); 67618c2ecf20Sopenharmony_ci hw->wm_lp[2] = I915_READ(WM3_LP_ILK); 67628c2ecf20Sopenharmony_ci 67638c2ecf20Sopenharmony_ci hw->wm_lp_spr[0] = I915_READ(WM1S_LP_ILK); 67648c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 7) { 67658c2ecf20Sopenharmony_ci hw->wm_lp_spr[1] = I915_READ(WM2S_LP_IVB); 67668c2ecf20Sopenharmony_ci hw->wm_lp_spr[2] = I915_READ(WM3S_LP_IVB); 67678c2ecf20Sopenharmony_ci } 67688c2ecf20Sopenharmony_ci 67698c2ecf20Sopenharmony_ci if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) 67708c2ecf20Sopenharmony_ci hw->partitioning = (I915_READ(WM_MISC) & WM_MISC_DATA_PARTITION_5_6) ? 67718c2ecf20Sopenharmony_ci INTEL_DDB_PART_5_6 : INTEL_DDB_PART_1_2; 67728c2ecf20Sopenharmony_ci else if (IS_IVYBRIDGE(dev_priv)) 67738c2ecf20Sopenharmony_ci hw->partitioning = (I915_READ(DISP_ARB_CTL2) & DISP_DATA_PARTITION_5_6) ? 67748c2ecf20Sopenharmony_ci INTEL_DDB_PART_5_6 : INTEL_DDB_PART_1_2; 67758c2ecf20Sopenharmony_ci 67768c2ecf20Sopenharmony_ci hw->enable_fbc_wm = 67778c2ecf20Sopenharmony_ci !(I915_READ(DISP_ARB_CTL) & DISP_FBC_WM_DIS); 67788c2ecf20Sopenharmony_ci} 67798c2ecf20Sopenharmony_ci 67808c2ecf20Sopenharmony_ci/** 67818c2ecf20Sopenharmony_ci * intel_update_watermarks - update FIFO watermark values based on current modes 67828c2ecf20Sopenharmony_ci * @crtc: the #intel_crtc on which to compute the WM 67838c2ecf20Sopenharmony_ci * 67848c2ecf20Sopenharmony_ci * Calculate watermark values for the various WM regs based on current mode 67858c2ecf20Sopenharmony_ci * and plane configuration. 67868c2ecf20Sopenharmony_ci * 67878c2ecf20Sopenharmony_ci * There are several cases to deal with here: 67888c2ecf20Sopenharmony_ci * - normal (i.e. non-self-refresh) 67898c2ecf20Sopenharmony_ci * - self-refresh (SR) mode 67908c2ecf20Sopenharmony_ci * - lines are large relative to FIFO size (buffer can hold up to 2) 67918c2ecf20Sopenharmony_ci * - lines are small relative to FIFO size (buffer can hold more than 2 67928c2ecf20Sopenharmony_ci * lines), so need to account for TLB latency 67938c2ecf20Sopenharmony_ci * 67948c2ecf20Sopenharmony_ci * The normal calculation is: 67958c2ecf20Sopenharmony_ci * watermark = dotclock * bytes per pixel * latency 67968c2ecf20Sopenharmony_ci * where latency is platform & configuration dependent (we assume pessimal 67978c2ecf20Sopenharmony_ci * values here). 67988c2ecf20Sopenharmony_ci * 67998c2ecf20Sopenharmony_ci * The SR calculation is: 68008c2ecf20Sopenharmony_ci * watermark = (trunc(latency/line time)+1) * surface width * 68018c2ecf20Sopenharmony_ci * bytes per pixel 68028c2ecf20Sopenharmony_ci * where 68038c2ecf20Sopenharmony_ci * line time = htotal / dotclock 68048c2ecf20Sopenharmony_ci * surface width = hdisplay for normal plane and 64 for cursor 68058c2ecf20Sopenharmony_ci * and latency is assumed to be high, as above. 68068c2ecf20Sopenharmony_ci * 68078c2ecf20Sopenharmony_ci * The final value programmed to the register should always be rounded up, 68088c2ecf20Sopenharmony_ci * and include an extra 2 entries to account for clock crossings. 68098c2ecf20Sopenharmony_ci * 68108c2ecf20Sopenharmony_ci * We don't use the sprite, so we can ignore that. And on Crestline we have 68118c2ecf20Sopenharmony_ci * to set the non-SR watermarks to 8. 68128c2ecf20Sopenharmony_ci */ 68138c2ecf20Sopenharmony_civoid intel_update_watermarks(struct intel_crtc *crtc) 68148c2ecf20Sopenharmony_ci{ 68158c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 68168c2ecf20Sopenharmony_ci 68178c2ecf20Sopenharmony_ci if (dev_priv->display.update_wm) 68188c2ecf20Sopenharmony_ci dev_priv->display.update_wm(crtc); 68198c2ecf20Sopenharmony_ci} 68208c2ecf20Sopenharmony_ci 68218c2ecf20Sopenharmony_civoid intel_enable_ipc(struct drm_i915_private *dev_priv) 68228c2ecf20Sopenharmony_ci{ 68238c2ecf20Sopenharmony_ci u32 val; 68248c2ecf20Sopenharmony_ci 68258c2ecf20Sopenharmony_ci if (!HAS_IPC(dev_priv)) 68268c2ecf20Sopenharmony_ci return; 68278c2ecf20Sopenharmony_ci 68288c2ecf20Sopenharmony_ci val = I915_READ(DISP_ARB_CTL2); 68298c2ecf20Sopenharmony_ci 68308c2ecf20Sopenharmony_ci if (dev_priv->ipc_enabled) 68318c2ecf20Sopenharmony_ci val |= DISP_IPC_ENABLE; 68328c2ecf20Sopenharmony_ci else 68338c2ecf20Sopenharmony_ci val &= ~DISP_IPC_ENABLE; 68348c2ecf20Sopenharmony_ci 68358c2ecf20Sopenharmony_ci I915_WRITE(DISP_ARB_CTL2, val); 68368c2ecf20Sopenharmony_ci} 68378c2ecf20Sopenharmony_ci 68388c2ecf20Sopenharmony_cistatic bool intel_can_enable_ipc(struct drm_i915_private *dev_priv) 68398c2ecf20Sopenharmony_ci{ 68408c2ecf20Sopenharmony_ci /* Display WA #0477 WaDisableIPC: skl */ 68418c2ecf20Sopenharmony_ci if (IS_SKYLAKE(dev_priv)) 68428c2ecf20Sopenharmony_ci return false; 68438c2ecf20Sopenharmony_ci 68448c2ecf20Sopenharmony_ci /* Display WA #1141: SKL:all KBL:all CFL */ 68458c2ecf20Sopenharmony_ci if (IS_KABYLAKE(dev_priv) || 68468c2ecf20Sopenharmony_ci IS_COFFEELAKE(dev_priv) || 68478c2ecf20Sopenharmony_ci IS_COMETLAKE(dev_priv)) 68488c2ecf20Sopenharmony_ci return dev_priv->dram_info.symmetric_memory; 68498c2ecf20Sopenharmony_ci 68508c2ecf20Sopenharmony_ci return true; 68518c2ecf20Sopenharmony_ci} 68528c2ecf20Sopenharmony_ci 68538c2ecf20Sopenharmony_civoid intel_init_ipc(struct drm_i915_private *dev_priv) 68548c2ecf20Sopenharmony_ci{ 68558c2ecf20Sopenharmony_ci if (!HAS_IPC(dev_priv)) 68568c2ecf20Sopenharmony_ci return; 68578c2ecf20Sopenharmony_ci 68588c2ecf20Sopenharmony_ci dev_priv->ipc_enabled = intel_can_enable_ipc(dev_priv); 68598c2ecf20Sopenharmony_ci 68608c2ecf20Sopenharmony_ci intel_enable_ipc(dev_priv); 68618c2ecf20Sopenharmony_ci} 68628c2ecf20Sopenharmony_ci 68638c2ecf20Sopenharmony_cistatic void ibx_init_clock_gating(struct drm_i915_private *dev_priv) 68648c2ecf20Sopenharmony_ci{ 68658c2ecf20Sopenharmony_ci /* 68668c2ecf20Sopenharmony_ci * On Ibex Peak and Cougar Point, we need to disable clock 68678c2ecf20Sopenharmony_ci * gating for the panel power sequencer or it will fail to 68688c2ecf20Sopenharmony_ci * start up when no ports are active. 68698c2ecf20Sopenharmony_ci */ 68708c2ecf20Sopenharmony_ci I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE); 68718c2ecf20Sopenharmony_ci} 68728c2ecf20Sopenharmony_ci 68738c2ecf20Sopenharmony_cistatic void g4x_disable_trickle_feed(struct drm_i915_private *dev_priv) 68748c2ecf20Sopenharmony_ci{ 68758c2ecf20Sopenharmony_ci enum pipe pipe; 68768c2ecf20Sopenharmony_ci 68778c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 68788c2ecf20Sopenharmony_ci I915_WRITE(DSPCNTR(pipe), 68798c2ecf20Sopenharmony_ci I915_READ(DSPCNTR(pipe)) | 68808c2ecf20Sopenharmony_ci DISPPLANE_TRICKLE_FEED_DISABLE); 68818c2ecf20Sopenharmony_ci 68828c2ecf20Sopenharmony_ci I915_WRITE(DSPSURF(pipe), I915_READ(DSPSURF(pipe))); 68838c2ecf20Sopenharmony_ci POSTING_READ(DSPSURF(pipe)); 68848c2ecf20Sopenharmony_ci } 68858c2ecf20Sopenharmony_ci} 68868c2ecf20Sopenharmony_ci 68878c2ecf20Sopenharmony_cistatic void ilk_init_clock_gating(struct drm_i915_private *dev_priv) 68888c2ecf20Sopenharmony_ci{ 68898c2ecf20Sopenharmony_ci u32 dspclk_gate = ILK_VRHUNIT_CLOCK_GATE_DISABLE; 68908c2ecf20Sopenharmony_ci 68918c2ecf20Sopenharmony_ci /* 68928c2ecf20Sopenharmony_ci * Required for FBC 68938c2ecf20Sopenharmony_ci * WaFbcDisableDpfcClockGating:ilk 68948c2ecf20Sopenharmony_ci */ 68958c2ecf20Sopenharmony_ci dspclk_gate |= ILK_DPFCRUNIT_CLOCK_GATE_DISABLE | 68968c2ecf20Sopenharmony_ci ILK_DPFCUNIT_CLOCK_GATE_DISABLE | 68978c2ecf20Sopenharmony_ci ILK_DPFDUNIT_CLOCK_GATE_ENABLE; 68988c2ecf20Sopenharmony_ci 68998c2ecf20Sopenharmony_ci I915_WRITE(PCH_3DCGDIS0, 69008c2ecf20Sopenharmony_ci MARIUNIT_CLOCK_GATE_DISABLE | 69018c2ecf20Sopenharmony_ci SVSMUNIT_CLOCK_GATE_DISABLE); 69028c2ecf20Sopenharmony_ci I915_WRITE(PCH_3DCGDIS1, 69038c2ecf20Sopenharmony_ci VFMUNIT_CLOCK_GATE_DISABLE); 69048c2ecf20Sopenharmony_ci 69058c2ecf20Sopenharmony_ci /* 69068c2ecf20Sopenharmony_ci * According to the spec the following bits should be set in 69078c2ecf20Sopenharmony_ci * order to enable memory self-refresh 69088c2ecf20Sopenharmony_ci * The bit 22/21 of 0x42004 69098c2ecf20Sopenharmony_ci * The bit 5 of 0x42020 69108c2ecf20Sopenharmony_ci * The bit 15 of 0x45000 69118c2ecf20Sopenharmony_ci */ 69128c2ecf20Sopenharmony_ci I915_WRITE(ILK_DISPLAY_CHICKEN2, 69138c2ecf20Sopenharmony_ci (I915_READ(ILK_DISPLAY_CHICKEN2) | 69148c2ecf20Sopenharmony_ci ILK_DPARB_GATE | ILK_VSDPFD_FULL)); 69158c2ecf20Sopenharmony_ci dspclk_gate |= ILK_DPARBUNIT_CLOCK_GATE_ENABLE; 69168c2ecf20Sopenharmony_ci I915_WRITE(DISP_ARB_CTL, 69178c2ecf20Sopenharmony_ci (I915_READ(DISP_ARB_CTL) | 69188c2ecf20Sopenharmony_ci DISP_FBC_WM_DIS)); 69198c2ecf20Sopenharmony_ci 69208c2ecf20Sopenharmony_ci /* 69218c2ecf20Sopenharmony_ci * Based on the document from hardware guys the following bits 69228c2ecf20Sopenharmony_ci * should be set unconditionally in order to enable FBC. 69238c2ecf20Sopenharmony_ci * The bit 22 of 0x42000 69248c2ecf20Sopenharmony_ci * The bit 22 of 0x42004 69258c2ecf20Sopenharmony_ci * The bit 7,8,9 of 0x42020. 69268c2ecf20Sopenharmony_ci */ 69278c2ecf20Sopenharmony_ci if (IS_IRONLAKE_M(dev_priv)) { 69288c2ecf20Sopenharmony_ci /* WaFbcAsynchFlipDisableFbcQueue:ilk */ 69298c2ecf20Sopenharmony_ci I915_WRITE(ILK_DISPLAY_CHICKEN1, 69308c2ecf20Sopenharmony_ci I915_READ(ILK_DISPLAY_CHICKEN1) | 69318c2ecf20Sopenharmony_ci ILK_FBCQ_DIS); 69328c2ecf20Sopenharmony_ci I915_WRITE(ILK_DISPLAY_CHICKEN2, 69338c2ecf20Sopenharmony_ci I915_READ(ILK_DISPLAY_CHICKEN2) | 69348c2ecf20Sopenharmony_ci ILK_DPARB_GATE); 69358c2ecf20Sopenharmony_ci } 69368c2ecf20Sopenharmony_ci 69378c2ecf20Sopenharmony_ci I915_WRITE(ILK_DSPCLK_GATE_D, dspclk_gate); 69388c2ecf20Sopenharmony_ci 69398c2ecf20Sopenharmony_ci I915_WRITE(ILK_DISPLAY_CHICKEN2, 69408c2ecf20Sopenharmony_ci I915_READ(ILK_DISPLAY_CHICKEN2) | 69418c2ecf20Sopenharmony_ci ILK_ELPIN_409_SELECT); 69428c2ecf20Sopenharmony_ci 69438c2ecf20Sopenharmony_ci g4x_disable_trickle_feed(dev_priv); 69448c2ecf20Sopenharmony_ci 69458c2ecf20Sopenharmony_ci ibx_init_clock_gating(dev_priv); 69468c2ecf20Sopenharmony_ci} 69478c2ecf20Sopenharmony_ci 69488c2ecf20Sopenharmony_cistatic void cpt_init_clock_gating(struct drm_i915_private *dev_priv) 69498c2ecf20Sopenharmony_ci{ 69508c2ecf20Sopenharmony_ci enum pipe pipe; 69518c2ecf20Sopenharmony_ci u32 val; 69528c2ecf20Sopenharmony_ci 69538c2ecf20Sopenharmony_ci /* 69548c2ecf20Sopenharmony_ci * On Ibex Peak and Cougar Point, we need to disable clock 69558c2ecf20Sopenharmony_ci * gating for the panel power sequencer or it will fail to 69568c2ecf20Sopenharmony_ci * start up when no ports are active. 69578c2ecf20Sopenharmony_ci */ 69588c2ecf20Sopenharmony_ci I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE | 69598c2ecf20Sopenharmony_ci PCH_DPLUNIT_CLOCK_GATE_DISABLE | 69608c2ecf20Sopenharmony_ci PCH_CPUNIT_CLOCK_GATE_DISABLE); 69618c2ecf20Sopenharmony_ci I915_WRITE(SOUTH_CHICKEN2, I915_READ(SOUTH_CHICKEN2) | 69628c2ecf20Sopenharmony_ci DPLS_EDP_PPS_FIX_DIS); 69638c2ecf20Sopenharmony_ci /* The below fixes the weird display corruption, a few pixels shifted 69648c2ecf20Sopenharmony_ci * downward, on (only) LVDS of some HP laptops with IVY. 69658c2ecf20Sopenharmony_ci */ 69668c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 69678c2ecf20Sopenharmony_ci val = I915_READ(TRANS_CHICKEN2(pipe)); 69688c2ecf20Sopenharmony_ci val |= TRANS_CHICKEN2_TIMING_OVERRIDE; 69698c2ecf20Sopenharmony_ci val &= ~TRANS_CHICKEN2_FDI_POLARITY_REVERSED; 69708c2ecf20Sopenharmony_ci if (dev_priv->vbt.fdi_rx_polarity_inverted) 69718c2ecf20Sopenharmony_ci val |= TRANS_CHICKEN2_FDI_POLARITY_REVERSED; 69728c2ecf20Sopenharmony_ci val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_COUNTER; 69738c2ecf20Sopenharmony_ci val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_MODESWITCH; 69748c2ecf20Sopenharmony_ci I915_WRITE(TRANS_CHICKEN2(pipe), val); 69758c2ecf20Sopenharmony_ci } 69768c2ecf20Sopenharmony_ci /* WADP0ClockGatingDisable */ 69778c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 69788c2ecf20Sopenharmony_ci I915_WRITE(TRANS_CHICKEN1(pipe), 69798c2ecf20Sopenharmony_ci TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); 69808c2ecf20Sopenharmony_ci } 69818c2ecf20Sopenharmony_ci} 69828c2ecf20Sopenharmony_ci 69838c2ecf20Sopenharmony_cistatic void gen6_check_mch_setup(struct drm_i915_private *dev_priv) 69848c2ecf20Sopenharmony_ci{ 69858c2ecf20Sopenharmony_ci u32 tmp; 69868c2ecf20Sopenharmony_ci 69878c2ecf20Sopenharmony_ci tmp = I915_READ(MCH_SSKPD); 69888c2ecf20Sopenharmony_ci if ((tmp & MCH_SSKPD_WM0_MASK) != MCH_SSKPD_WM0_VAL) 69898c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 69908c2ecf20Sopenharmony_ci "Wrong MCH_SSKPD value: 0x%08x This can cause underruns.\n", 69918c2ecf20Sopenharmony_ci tmp); 69928c2ecf20Sopenharmony_ci} 69938c2ecf20Sopenharmony_ci 69948c2ecf20Sopenharmony_cistatic void gen6_init_clock_gating(struct drm_i915_private *dev_priv) 69958c2ecf20Sopenharmony_ci{ 69968c2ecf20Sopenharmony_ci u32 dspclk_gate = ILK_VRHUNIT_CLOCK_GATE_DISABLE; 69978c2ecf20Sopenharmony_ci 69988c2ecf20Sopenharmony_ci I915_WRITE(ILK_DSPCLK_GATE_D, dspclk_gate); 69998c2ecf20Sopenharmony_ci 70008c2ecf20Sopenharmony_ci I915_WRITE(ILK_DISPLAY_CHICKEN2, 70018c2ecf20Sopenharmony_ci I915_READ(ILK_DISPLAY_CHICKEN2) | 70028c2ecf20Sopenharmony_ci ILK_ELPIN_409_SELECT); 70038c2ecf20Sopenharmony_ci 70048c2ecf20Sopenharmony_ci I915_WRITE(GEN6_UCGCTL1, 70058c2ecf20Sopenharmony_ci I915_READ(GEN6_UCGCTL1) | 70068c2ecf20Sopenharmony_ci GEN6_BLBUNIT_CLOCK_GATE_DISABLE | 70078c2ecf20Sopenharmony_ci GEN6_CSUNIT_CLOCK_GATE_DISABLE); 70088c2ecf20Sopenharmony_ci 70098c2ecf20Sopenharmony_ci /* According to the BSpec vol1g, bit 12 (RCPBUNIT) clock 70108c2ecf20Sopenharmony_ci * gating disable must be set. Failure to set it results in 70118c2ecf20Sopenharmony_ci * flickering pixels due to Z write ordering failures after 70128c2ecf20Sopenharmony_ci * some amount of runtime in the Mesa "fire" demo, and Unigine 70138c2ecf20Sopenharmony_ci * Sanctuary and Tropics, and apparently anything else with 70148c2ecf20Sopenharmony_ci * alpha test or pixel discard. 70158c2ecf20Sopenharmony_ci * 70168c2ecf20Sopenharmony_ci * According to the spec, bit 11 (RCCUNIT) must also be set, 70178c2ecf20Sopenharmony_ci * but we didn't debug actual testcases to find it out. 70188c2ecf20Sopenharmony_ci * 70198c2ecf20Sopenharmony_ci * WaDisableRCCUnitClockGating:snb 70208c2ecf20Sopenharmony_ci * WaDisableRCPBUnitClockGating:snb 70218c2ecf20Sopenharmony_ci */ 70228c2ecf20Sopenharmony_ci I915_WRITE(GEN6_UCGCTL2, 70238c2ecf20Sopenharmony_ci GEN6_RCPBUNIT_CLOCK_GATE_DISABLE | 70248c2ecf20Sopenharmony_ci GEN6_RCCUNIT_CLOCK_GATE_DISABLE); 70258c2ecf20Sopenharmony_ci 70268c2ecf20Sopenharmony_ci /* 70278c2ecf20Sopenharmony_ci * According to the spec the following bits should be 70288c2ecf20Sopenharmony_ci * set in order to enable memory self-refresh and fbc: 70298c2ecf20Sopenharmony_ci * The bit21 and bit22 of 0x42000 70308c2ecf20Sopenharmony_ci * The bit21 and bit22 of 0x42004 70318c2ecf20Sopenharmony_ci * The bit5 and bit7 of 0x42020 70328c2ecf20Sopenharmony_ci * The bit14 of 0x70180 70338c2ecf20Sopenharmony_ci * The bit14 of 0x71180 70348c2ecf20Sopenharmony_ci * 70358c2ecf20Sopenharmony_ci * WaFbcAsynchFlipDisableFbcQueue:snb 70368c2ecf20Sopenharmony_ci */ 70378c2ecf20Sopenharmony_ci I915_WRITE(ILK_DISPLAY_CHICKEN1, 70388c2ecf20Sopenharmony_ci I915_READ(ILK_DISPLAY_CHICKEN1) | 70398c2ecf20Sopenharmony_ci ILK_FBCQ_DIS | ILK_PABSTRETCH_DIS); 70408c2ecf20Sopenharmony_ci I915_WRITE(ILK_DISPLAY_CHICKEN2, 70418c2ecf20Sopenharmony_ci I915_READ(ILK_DISPLAY_CHICKEN2) | 70428c2ecf20Sopenharmony_ci ILK_DPARB_GATE | ILK_VSDPFD_FULL); 70438c2ecf20Sopenharmony_ci I915_WRITE(ILK_DSPCLK_GATE_D, 70448c2ecf20Sopenharmony_ci I915_READ(ILK_DSPCLK_GATE_D) | 70458c2ecf20Sopenharmony_ci ILK_DPARBUNIT_CLOCK_GATE_ENABLE | 70468c2ecf20Sopenharmony_ci ILK_DPFDUNIT_CLOCK_GATE_ENABLE); 70478c2ecf20Sopenharmony_ci 70488c2ecf20Sopenharmony_ci g4x_disable_trickle_feed(dev_priv); 70498c2ecf20Sopenharmony_ci 70508c2ecf20Sopenharmony_ci cpt_init_clock_gating(dev_priv); 70518c2ecf20Sopenharmony_ci 70528c2ecf20Sopenharmony_ci gen6_check_mch_setup(dev_priv); 70538c2ecf20Sopenharmony_ci} 70548c2ecf20Sopenharmony_ci 70558c2ecf20Sopenharmony_cistatic void lpt_init_clock_gating(struct drm_i915_private *dev_priv) 70568c2ecf20Sopenharmony_ci{ 70578c2ecf20Sopenharmony_ci /* 70588c2ecf20Sopenharmony_ci * TODO: this bit should only be enabled when really needed, then 70598c2ecf20Sopenharmony_ci * disabled when not needed anymore in order to save power. 70608c2ecf20Sopenharmony_ci */ 70618c2ecf20Sopenharmony_ci if (HAS_PCH_LPT_LP(dev_priv)) 70628c2ecf20Sopenharmony_ci I915_WRITE(SOUTH_DSPCLK_GATE_D, 70638c2ecf20Sopenharmony_ci I915_READ(SOUTH_DSPCLK_GATE_D) | 70648c2ecf20Sopenharmony_ci PCH_LP_PARTITION_LEVEL_DISABLE); 70658c2ecf20Sopenharmony_ci 70668c2ecf20Sopenharmony_ci /* WADPOClockGatingDisable:hsw */ 70678c2ecf20Sopenharmony_ci I915_WRITE(TRANS_CHICKEN1(PIPE_A), 70688c2ecf20Sopenharmony_ci I915_READ(TRANS_CHICKEN1(PIPE_A)) | 70698c2ecf20Sopenharmony_ci TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); 70708c2ecf20Sopenharmony_ci} 70718c2ecf20Sopenharmony_ci 70728c2ecf20Sopenharmony_cistatic void lpt_suspend_hw(struct drm_i915_private *dev_priv) 70738c2ecf20Sopenharmony_ci{ 70748c2ecf20Sopenharmony_ci if (HAS_PCH_LPT_LP(dev_priv)) { 70758c2ecf20Sopenharmony_ci u32 val = I915_READ(SOUTH_DSPCLK_GATE_D); 70768c2ecf20Sopenharmony_ci 70778c2ecf20Sopenharmony_ci val &= ~PCH_LP_PARTITION_LEVEL_DISABLE; 70788c2ecf20Sopenharmony_ci I915_WRITE(SOUTH_DSPCLK_GATE_D, val); 70798c2ecf20Sopenharmony_ci } 70808c2ecf20Sopenharmony_ci} 70818c2ecf20Sopenharmony_ci 70828c2ecf20Sopenharmony_cistatic void gen8_set_l3sqc_credits(struct drm_i915_private *dev_priv, 70838c2ecf20Sopenharmony_ci int general_prio_credits, 70848c2ecf20Sopenharmony_ci int high_prio_credits) 70858c2ecf20Sopenharmony_ci{ 70868c2ecf20Sopenharmony_ci u32 misccpctl; 70878c2ecf20Sopenharmony_ci u32 val; 70888c2ecf20Sopenharmony_ci 70898c2ecf20Sopenharmony_ci /* WaTempDisableDOPClkGating:bdw */ 70908c2ecf20Sopenharmony_ci misccpctl = I915_READ(GEN7_MISCCPCTL); 70918c2ecf20Sopenharmony_ci I915_WRITE(GEN7_MISCCPCTL, misccpctl & ~GEN7_DOP_CLOCK_GATE_ENABLE); 70928c2ecf20Sopenharmony_ci 70938c2ecf20Sopenharmony_ci val = I915_READ(GEN8_L3SQCREG1); 70948c2ecf20Sopenharmony_ci val &= ~L3_PRIO_CREDITS_MASK; 70958c2ecf20Sopenharmony_ci val |= L3_GENERAL_PRIO_CREDITS(general_prio_credits); 70968c2ecf20Sopenharmony_ci val |= L3_HIGH_PRIO_CREDITS(high_prio_credits); 70978c2ecf20Sopenharmony_ci I915_WRITE(GEN8_L3SQCREG1, val); 70988c2ecf20Sopenharmony_ci 70998c2ecf20Sopenharmony_ci /* 71008c2ecf20Sopenharmony_ci * Wait at least 100 clocks before re-enabling clock gating. 71018c2ecf20Sopenharmony_ci * See the definition of L3SQCREG1 in BSpec. 71028c2ecf20Sopenharmony_ci */ 71038c2ecf20Sopenharmony_ci POSTING_READ(GEN8_L3SQCREG1); 71048c2ecf20Sopenharmony_ci udelay(1); 71058c2ecf20Sopenharmony_ci I915_WRITE(GEN7_MISCCPCTL, misccpctl); 71068c2ecf20Sopenharmony_ci} 71078c2ecf20Sopenharmony_ci 71088c2ecf20Sopenharmony_cistatic void icl_init_clock_gating(struct drm_i915_private *dev_priv) 71098c2ecf20Sopenharmony_ci{ 71108c2ecf20Sopenharmony_ci /* Wa_1409120013:icl,ehl */ 71118c2ecf20Sopenharmony_ci I915_WRITE(ILK_DPFC_CHICKEN, 71128c2ecf20Sopenharmony_ci ILK_DPFC_CHICKEN_COMP_DUMMY_PIXEL); 71138c2ecf20Sopenharmony_ci 71148c2ecf20Sopenharmony_ci /* This is not an Wa. Enable to reduce Sampler power */ 71158c2ecf20Sopenharmony_ci I915_WRITE(GEN10_DFR_RATIO_EN_AND_CHICKEN, 71168c2ecf20Sopenharmony_ci I915_READ(GEN10_DFR_RATIO_EN_AND_CHICKEN) & ~DFR_DISABLE); 71178c2ecf20Sopenharmony_ci 71188c2ecf20Sopenharmony_ci /*Wa_14010594013:icl, ehl */ 71198c2ecf20Sopenharmony_ci intel_uncore_rmw(&dev_priv->uncore, GEN8_CHICKEN_DCPR_1, 71208c2ecf20Sopenharmony_ci 0, CNL_DELAY_PMRSP); 71218c2ecf20Sopenharmony_ci} 71228c2ecf20Sopenharmony_ci 71238c2ecf20Sopenharmony_cistatic void tgl_init_clock_gating(struct drm_i915_private *dev_priv) 71248c2ecf20Sopenharmony_ci{ 71258c2ecf20Sopenharmony_ci /* Wa_1409120013:tgl */ 71268c2ecf20Sopenharmony_ci I915_WRITE(ILK_DPFC_CHICKEN, 71278c2ecf20Sopenharmony_ci ILK_DPFC_CHICKEN_COMP_DUMMY_PIXEL); 71288c2ecf20Sopenharmony_ci 71298c2ecf20Sopenharmony_ci /* Wa_1409825376:tgl (pre-prod)*/ 71308c2ecf20Sopenharmony_ci if (IS_TGL_DISP_REVID(dev_priv, TGL_REVID_A0, TGL_REVID_B1)) 71318c2ecf20Sopenharmony_ci I915_WRITE(GEN9_CLKGATE_DIS_3, I915_READ(GEN9_CLKGATE_DIS_3) | 71328c2ecf20Sopenharmony_ci TGL_VRH_GATING_DIS); 71338c2ecf20Sopenharmony_ci 71348c2ecf20Sopenharmony_ci /* Wa_14011059788:tgl */ 71358c2ecf20Sopenharmony_ci intel_uncore_rmw(&dev_priv->uncore, GEN10_DFR_RATIO_EN_AND_CHICKEN, 71368c2ecf20Sopenharmony_ci 0, DFR_DISABLE); 71378c2ecf20Sopenharmony_ci} 71388c2ecf20Sopenharmony_ci 71398c2ecf20Sopenharmony_cistatic void cnp_init_clock_gating(struct drm_i915_private *dev_priv) 71408c2ecf20Sopenharmony_ci{ 71418c2ecf20Sopenharmony_ci if (!HAS_PCH_CNP(dev_priv)) 71428c2ecf20Sopenharmony_ci return; 71438c2ecf20Sopenharmony_ci 71448c2ecf20Sopenharmony_ci /* Display WA #1181 WaSouthDisplayDisablePWMCGEGating: cnp */ 71458c2ecf20Sopenharmony_ci I915_WRITE(SOUTH_DSPCLK_GATE_D, I915_READ(SOUTH_DSPCLK_GATE_D) | 71468c2ecf20Sopenharmony_ci CNP_PWM_CGE_GATING_DISABLE); 71478c2ecf20Sopenharmony_ci} 71488c2ecf20Sopenharmony_ci 71498c2ecf20Sopenharmony_cistatic void cnl_init_clock_gating(struct drm_i915_private *dev_priv) 71508c2ecf20Sopenharmony_ci{ 71518c2ecf20Sopenharmony_ci u32 val; 71528c2ecf20Sopenharmony_ci cnp_init_clock_gating(dev_priv); 71538c2ecf20Sopenharmony_ci 71548c2ecf20Sopenharmony_ci /* This is not an Wa. Enable for better image quality */ 71558c2ecf20Sopenharmony_ci I915_WRITE(_3D_CHICKEN3, 71568c2ecf20Sopenharmony_ci _MASKED_BIT_ENABLE(_3D_CHICKEN3_AA_LINE_QUALITY_FIX_ENABLE)); 71578c2ecf20Sopenharmony_ci 71588c2ecf20Sopenharmony_ci /* WaEnableChickenDCPR:cnl */ 71598c2ecf20Sopenharmony_ci I915_WRITE(GEN8_CHICKEN_DCPR_1, 71608c2ecf20Sopenharmony_ci I915_READ(GEN8_CHICKEN_DCPR_1) | MASK_WAKEMEM); 71618c2ecf20Sopenharmony_ci 71628c2ecf20Sopenharmony_ci /* 71638c2ecf20Sopenharmony_ci * WaFbcWakeMemOn:cnl 71648c2ecf20Sopenharmony_ci * Display WA #0859: cnl 71658c2ecf20Sopenharmony_ci */ 71668c2ecf20Sopenharmony_ci I915_WRITE(DISP_ARB_CTL, I915_READ(DISP_ARB_CTL) | 71678c2ecf20Sopenharmony_ci DISP_FBC_MEMORY_WAKE); 71688c2ecf20Sopenharmony_ci 71698c2ecf20Sopenharmony_ci val = I915_READ(SLICE_UNIT_LEVEL_CLKGATE); 71708c2ecf20Sopenharmony_ci /* ReadHitWriteOnlyDisable:cnl */ 71718c2ecf20Sopenharmony_ci val |= RCCUNIT_CLKGATE_DIS; 71728c2ecf20Sopenharmony_ci I915_WRITE(SLICE_UNIT_LEVEL_CLKGATE, val); 71738c2ecf20Sopenharmony_ci 71748c2ecf20Sopenharmony_ci /* Wa_2201832410:cnl */ 71758c2ecf20Sopenharmony_ci val = I915_READ(SUBSLICE_UNIT_LEVEL_CLKGATE); 71768c2ecf20Sopenharmony_ci val |= GWUNIT_CLKGATE_DIS; 71778c2ecf20Sopenharmony_ci I915_WRITE(SUBSLICE_UNIT_LEVEL_CLKGATE, val); 71788c2ecf20Sopenharmony_ci 71798c2ecf20Sopenharmony_ci /* WaDisableVFclkgate:cnl */ 71808c2ecf20Sopenharmony_ci /* WaVFUnitClockGatingDisable:cnl */ 71818c2ecf20Sopenharmony_ci val = I915_READ(UNSLICE_UNIT_LEVEL_CLKGATE); 71828c2ecf20Sopenharmony_ci val |= VFUNIT_CLKGATE_DIS; 71838c2ecf20Sopenharmony_ci I915_WRITE(UNSLICE_UNIT_LEVEL_CLKGATE, val); 71848c2ecf20Sopenharmony_ci} 71858c2ecf20Sopenharmony_ci 71868c2ecf20Sopenharmony_cistatic void cfl_init_clock_gating(struct drm_i915_private *dev_priv) 71878c2ecf20Sopenharmony_ci{ 71888c2ecf20Sopenharmony_ci cnp_init_clock_gating(dev_priv); 71898c2ecf20Sopenharmony_ci gen9_init_clock_gating(dev_priv); 71908c2ecf20Sopenharmony_ci 71918c2ecf20Sopenharmony_ci /* 71928c2ecf20Sopenharmony_ci * WaFbcTurnOffFbcWatermark:cfl 71938c2ecf20Sopenharmony_ci * Display WA #0562: cfl 71948c2ecf20Sopenharmony_ci */ 71958c2ecf20Sopenharmony_ci I915_WRITE(DISP_ARB_CTL, I915_READ(DISP_ARB_CTL) | 71968c2ecf20Sopenharmony_ci DISP_FBC_WM_DIS); 71978c2ecf20Sopenharmony_ci 71988c2ecf20Sopenharmony_ci /* 71998c2ecf20Sopenharmony_ci * WaFbcNukeOnHostModify:cfl 72008c2ecf20Sopenharmony_ci * Display WA #0873: cfl 72018c2ecf20Sopenharmony_ci */ 72028c2ecf20Sopenharmony_ci I915_WRITE(ILK_DPFC_CHICKEN, I915_READ(ILK_DPFC_CHICKEN) | 72038c2ecf20Sopenharmony_ci ILK_DPFC_NUKE_ON_ANY_MODIFICATION); 72048c2ecf20Sopenharmony_ci} 72058c2ecf20Sopenharmony_ci 72068c2ecf20Sopenharmony_cistatic void kbl_init_clock_gating(struct drm_i915_private *dev_priv) 72078c2ecf20Sopenharmony_ci{ 72088c2ecf20Sopenharmony_ci gen9_init_clock_gating(dev_priv); 72098c2ecf20Sopenharmony_ci 72108c2ecf20Sopenharmony_ci /* WaDisableSDEUnitClockGating:kbl */ 72118c2ecf20Sopenharmony_ci if (IS_KBL_GT_REVID(dev_priv, 0, KBL_REVID_B0)) 72128c2ecf20Sopenharmony_ci I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | 72138c2ecf20Sopenharmony_ci GEN8_SDEUNIT_CLOCK_GATE_DISABLE); 72148c2ecf20Sopenharmony_ci 72158c2ecf20Sopenharmony_ci /* WaDisableGamClockGating:kbl */ 72168c2ecf20Sopenharmony_ci if (IS_KBL_GT_REVID(dev_priv, 0, KBL_REVID_B0)) 72178c2ecf20Sopenharmony_ci I915_WRITE(GEN6_UCGCTL1, I915_READ(GEN6_UCGCTL1) | 72188c2ecf20Sopenharmony_ci GEN6_GAMUNIT_CLOCK_GATE_DISABLE); 72198c2ecf20Sopenharmony_ci 72208c2ecf20Sopenharmony_ci /* 72218c2ecf20Sopenharmony_ci * WaFbcTurnOffFbcWatermark:kbl 72228c2ecf20Sopenharmony_ci * Display WA #0562: kbl 72238c2ecf20Sopenharmony_ci */ 72248c2ecf20Sopenharmony_ci I915_WRITE(DISP_ARB_CTL, I915_READ(DISP_ARB_CTL) | 72258c2ecf20Sopenharmony_ci DISP_FBC_WM_DIS); 72268c2ecf20Sopenharmony_ci 72278c2ecf20Sopenharmony_ci /* 72288c2ecf20Sopenharmony_ci * WaFbcNukeOnHostModify:kbl 72298c2ecf20Sopenharmony_ci * Display WA #0873: kbl 72308c2ecf20Sopenharmony_ci */ 72318c2ecf20Sopenharmony_ci I915_WRITE(ILK_DPFC_CHICKEN, I915_READ(ILK_DPFC_CHICKEN) | 72328c2ecf20Sopenharmony_ci ILK_DPFC_NUKE_ON_ANY_MODIFICATION); 72338c2ecf20Sopenharmony_ci} 72348c2ecf20Sopenharmony_ci 72358c2ecf20Sopenharmony_cistatic void skl_init_clock_gating(struct drm_i915_private *dev_priv) 72368c2ecf20Sopenharmony_ci{ 72378c2ecf20Sopenharmony_ci gen9_init_clock_gating(dev_priv); 72388c2ecf20Sopenharmony_ci 72398c2ecf20Sopenharmony_ci /* WaDisableDopClockGating:skl */ 72408c2ecf20Sopenharmony_ci I915_WRITE(GEN7_MISCCPCTL, I915_READ(GEN7_MISCCPCTL) & 72418c2ecf20Sopenharmony_ci ~GEN7_DOP_CLOCK_GATE_ENABLE); 72428c2ecf20Sopenharmony_ci 72438c2ecf20Sopenharmony_ci /* WAC6entrylatency:skl */ 72448c2ecf20Sopenharmony_ci I915_WRITE(FBC_LLC_READ_CTRL, I915_READ(FBC_LLC_READ_CTRL) | 72458c2ecf20Sopenharmony_ci FBC_LLC_FULLY_OPEN); 72468c2ecf20Sopenharmony_ci 72478c2ecf20Sopenharmony_ci /* 72488c2ecf20Sopenharmony_ci * WaFbcTurnOffFbcWatermark:skl 72498c2ecf20Sopenharmony_ci * Display WA #0562: skl 72508c2ecf20Sopenharmony_ci */ 72518c2ecf20Sopenharmony_ci I915_WRITE(DISP_ARB_CTL, I915_READ(DISP_ARB_CTL) | 72528c2ecf20Sopenharmony_ci DISP_FBC_WM_DIS); 72538c2ecf20Sopenharmony_ci 72548c2ecf20Sopenharmony_ci /* 72558c2ecf20Sopenharmony_ci * WaFbcNukeOnHostModify:skl 72568c2ecf20Sopenharmony_ci * Display WA #0873: skl 72578c2ecf20Sopenharmony_ci */ 72588c2ecf20Sopenharmony_ci I915_WRITE(ILK_DPFC_CHICKEN, I915_READ(ILK_DPFC_CHICKEN) | 72598c2ecf20Sopenharmony_ci ILK_DPFC_NUKE_ON_ANY_MODIFICATION); 72608c2ecf20Sopenharmony_ci 72618c2ecf20Sopenharmony_ci /* 72628c2ecf20Sopenharmony_ci * WaFbcHighMemBwCorruptionAvoidance:skl 72638c2ecf20Sopenharmony_ci * Display WA #0883: skl 72648c2ecf20Sopenharmony_ci */ 72658c2ecf20Sopenharmony_ci I915_WRITE(ILK_DPFC_CHICKEN, I915_READ(ILK_DPFC_CHICKEN) | 72668c2ecf20Sopenharmony_ci ILK_DPFC_DISABLE_DUMMY0); 72678c2ecf20Sopenharmony_ci} 72688c2ecf20Sopenharmony_ci 72698c2ecf20Sopenharmony_cistatic void bdw_init_clock_gating(struct drm_i915_private *dev_priv) 72708c2ecf20Sopenharmony_ci{ 72718c2ecf20Sopenharmony_ci enum pipe pipe; 72728c2ecf20Sopenharmony_ci 72738c2ecf20Sopenharmony_ci /* WaFbcAsynchFlipDisableFbcQueue:hsw,bdw */ 72748c2ecf20Sopenharmony_ci I915_WRITE(CHICKEN_PIPESL_1(PIPE_A), 72758c2ecf20Sopenharmony_ci I915_READ(CHICKEN_PIPESL_1(PIPE_A)) | 72768c2ecf20Sopenharmony_ci HSW_FBCQ_DIS); 72778c2ecf20Sopenharmony_ci 72788c2ecf20Sopenharmony_ci /* WaSwitchSolVfFArbitrationPriority:bdw */ 72798c2ecf20Sopenharmony_ci I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL); 72808c2ecf20Sopenharmony_ci 72818c2ecf20Sopenharmony_ci /* WaPsrDPAMaskVBlankInSRD:bdw */ 72828c2ecf20Sopenharmony_ci I915_WRITE(CHICKEN_PAR1_1, 72838c2ecf20Sopenharmony_ci I915_READ(CHICKEN_PAR1_1) | DPA_MASK_VBLANK_SRD); 72848c2ecf20Sopenharmony_ci 72858c2ecf20Sopenharmony_ci /* WaPsrDPRSUnmaskVBlankInSRD:bdw */ 72868c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 72878c2ecf20Sopenharmony_ci I915_WRITE(CHICKEN_PIPESL_1(pipe), 72888c2ecf20Sopenharmony_ci I915_READ(CHICKEN_PIPESL_1(pipe)) | 72898c2ecf20Sopenharmony_ci BDW_DPRS_MASK_VBLANK_SRD); 72908c2ecf20Sopenharmony_ci } 72918c2ecf20Sopenharmony_ci 72928c2ecf20Sopenharmony_ci /* WaVSRefCountFullforceMissDisable:bdw */ 72938c2ecf20Sopenharmony_ci /* WaDSRefCountFullforceMissDisable:bdw */ 72948c2ecf20Sopenharmony_ci I915_WRITE(GEN7_FF_THREAD_MODE, 72958c2ecf20Sopenharmony_ci I915_READ(GEN7_FF_THREAD_MODE) & 72968c2ecf20Sopenharmony_ci ~(GEN8_FF_DS_REF_CNT_FFME | GEN7_FF_VS_REF_CNT_FFME)); 72978c2ecf20Sopenharmony_ci 72988c2ecf20Sopenharmony_ci I915_WRITE(GEN6_RC_SLEEP_PSMI_CONTROL, 72998c2ecf20Sopenharmony_ci _MASKED_BIT_ENABLE(GEN8_RC_SEMA_IDLE_MSG_DISABLE)); 73008c2ecf20Sopenharmony_ci 73018c2ecf20Sopenharmony_ci /* WaDisableSDEUnitClockGating:bdw */ 73028c2ecf20Sopenharmony_ci I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | 73038c2ecf20Sopenharmony_ci GEN8_SDEUNIT_CLOCK_GATE_DISABLE); 73048c2ecf20Sopenharmony_ci 73058c2ecf20Sopenharmony_ci /* WaProgramL3SqcReg1Default:bdw */ 73068c2ecf20Sopenharmony_ci gen8_set_l3sqc_credits(dev_priv, 30, 2); 73078c2ecf20Sopenharmony_ci 73088c2ecf20Sopenharmony_ci /* WaKVMNotificationOnConfigChange:bdw */ 73098c2ecf20Sopenharmony_ci I915_WRITE(CHICKEN_PAR2_1, I915_READ(CHICKEN_PAR2_1) 73108c2ecf20Sopenharmony_ci | KVM_CONFIG_CHANGE_NOTIFICATION_SELECT); 73118c2ecf20Sopenharmony_ci 73128c2ecf20Sopenharmony_ci lpt_init_clock_gating(dev_priv); 73138c2ecf20Sopenharmony_ci 73148c2ecf20Sopenharmony_ci /* WaDisableDopClockGating:bdw 73158c2ecf20Sopenharmony_ci * 73168c2ecf20Sopenharmony_ci * Also see the CHICKEN2 write in bdw_init_workarounds() to disable DOP 73178c2ecf20Sopenharmony_ci * clock gating. 73188c2ecf20Sopenharmony_ci */ 73198c2ecf20Sopenharmony_ci I915_WRITE(GEN6_UCGCTL1, 73208c2ecf20Sopenharmony_ci I915_READ(GEN6_UCGCTL1) | GEN6_EU_TCUNIT_CLOCK_GATE_DISABLE); 73218c2ecf20Sopenharmony_ci} 73228c2ecf20Sopenharmony_ci 73238c2ecf20Sopenharmony_cistatic void hsw_init_clock_gating(struct drm_i915_private *dev_priv) 73248c2ecf20Sopenharmony_ci{ 73258c2ecf20Sopenharmony_ci /* WaFbcAsynchFlipDisableFbcQueue:hsw,bdw */ 73268c2ecf20Sopenharmony_ci I915_WRITE(CHICKEN_PIPESL_1(PIPE_A), 73278c2ecf20Sopenharmony_ci I915_READ(CHICKEN_PIPESL_1(PIPE_A)) | 73288c2ecf20Sopenharmony_ci HSW_FBCQ_DIS); 73298c2ecf20Sopenharmony_ci 73308c2ecf20Sopenharmony_ci /* This is required by WaCatErrorRejectionIssue:hsw */ 73318c2ecf20Sopenharmony_ci I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, 73328c2ecf20Sopenharmony_ci I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | 73338c2ecf20Sopenharmony_ci GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); 73348c2ecf20Sopenharmony_ci 73358c2ecf20Sopenharmony_ci /* WaSwitchSolVfFArbitrationPriority:hsw */ 73368c2ecf20Sopenharmony_ci I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL); 73378c2ecf20Sopenharmony_ci 73388c2ecf20Sopenharmony_ci lpt_init_clock_gating(dev_priv); 73398c2ecf20Sopenharmony_ci} 73408c2ecf20Sopenharmony_ci 73418c2ecf20Sopenharmony_cistatic void ivb_init_clock_gating(struct drm_i915_private *dev_priv) 73428c2ecf20Sopenharmony_ci{ 73438c2ecf20Sopenharmony_ci u32 snpcr; 73448c2ecf20Sopenharmony_ci 73458c2ecf20Sopenharmony_ci I915_WRITE(ILK_DSPCLK_GATE_D, ILK_VRHUNIT_CLOCK_GATE_DISABLE); 73468c2ecf20Sopenharmony_ci 73478c2ecf20Sopenharmony_ci /* WaFbcAsynchFlipDisableFbcQueue:ivb */ 73488c2ecf20Sopenharmony_ci I915_WRITE(ILK_DISPLAY_CHICKEN1, 73498c2ecf20Sopenharmony_ci I915_READ(ILK_DISPLAY_CHICKEN1) | 73508c2ecf20Sopenharmony_ci ILK_FBCQ_DIS); 73518c2ecf20Sopenharmony_ci 73528c2ecf20Sopenharmony_ci /* WaDisableBackToBackFlipFix:ivb */ 73538c2ecf20Sopenharmony_ci I915_WRITE(IVB_CHICKEN3, 73548c2ecf20Sopenharmony_ci CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE | 73558c2ecf20Sopenharmony_ci CHICKEN3_DGMG_DONE_FIX_DISABLE); 73568c2ecf20Sopenharmony_ci 73578c2ecf20Sopenharmony_ci if (IS_IVB_GT1(dev_priv)) 73588c2ecf20Sopenharmony_ci I915_WRITE(GEN7_ROW_CHICKEN2, 73598c2ecf20Sopenharmony_ci _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); 73608c2ecf20Sopenharmony_ci else { 73618c2ecf20Sopenharmony_ci /* must write both registers */ 73628c2ecf20Sopenharmony_ci I915_WRITE(GEN7_ROW_CHICKEN2, 73638c2ecf20Sopenharmony_ci _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); 73648c2ecf20Sopenharmony_ci I915_WRITE(GEN7_ROW_CHICKEN2_GT2, 73658c2ecf20Sopenharmony_ci _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); 73668c2ecf20Sopenharmony_ci } 73678c2ecf20Sopenharmony_ci 73688c2ecf20Sopenharmony_ci /* 73698c2ecf20Sopenharmony_ci * According to the spec, bit 13 (RCZUNIT) must be set on IVB. 73708c2ecf20Sopenharmony_ci * This implements the WaDisableRCZUnitClockGating:ivb workaround. 73718c2ecf20Sopenharmony_ci */ 73728c2ecf20Sopenharmony_ci I915_WRITE(GEN6_UCGCTL2, 73738c2ecf20Sopenharmony_ci GEN6_RCZUNIT_CLOCK_GATE_DISABLE); 73748c2ecf20Sopenharmony_ci 73758c2ecf20Sopenharmony_ci /* This is required by WaCatErrorRejectionIssue:ivb */ 73768c2ecf20Sopenharmony_ci I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, 73778c2ecf20Sopenharmony_ci I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | 73788c2ecf20Sopenharmony_ci GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); 73798c2ecf20Sopenharmony_ci 73808c2ecf20Sopenharmony_ci g4x_disable_trickle_feed(dev_priv); 73818c2ecf20Sopenharmony_ci 73828c2ecf20Sopenharmony_ci snpcr = I915_READ(GEN6_MBCUNIT_SNPCR); 73838c2ecf20Sopenharmony_ci snpcr &= ~GEN6_MBC_SNPCR_MASK; 73848c2ecf20Sopenharmony_ci snpcr |= GEN6_MBC_SNPCR_MED; 73858c2ecf20Sopenharmony_ci I915_WRITE(GEN6_MBCUNIT_SNPCR, snpcr); 73868c2ecf20Sopenharmony_ci 73878c2ecf20Sopenharmony_ci if (!HAS_PCH_NOP(dev_priv)) 73888c2ecf20Sopenharmony_ci cpt_init_clock_gating(dev_priv); 73898c2ecf20Sopenharmony_ci 73908c2ecf20Sopenharmony_ci gen6_check_mch_setup(dev_priv); 73918c2ecf20Sopenharmony_ci} 73928c2ecf20Sopenharmony_ci 73938c2ecf20Sopenharmony_cistatic void vlv_init_clock_gating(struct drm_i915_private *dev_priv) 73948c2ecf20Sopenharmony_ci{ 73958c2ecf20Sopenharmony_ci /* WaDisableBackToBackFlipFix:vlv */ 73968c2ecf20Sopenharmony_ci I915_WRITE(IVB_CHICKEN3, 73978c2ecf20Sopenharmony_ci CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE | 73988c2ecf20Sopenharmony_ci CHICKEN3_DGMG_DONE_FIX_DISABLE); 73998c2ecf20Sopenharmony_ci 74008c2ecf20Sopenharmony_ci /* WaDisableDopClockGating:vlv */ 74018c2ecf20Sopenharmony_ci I915_WRITE(GEN7_ROW_CHICKEN2, 74028c2ecf20Sopenharmony_ci _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); 74038c2ecf20Sopenharmony_ci 74048c2ecf20Sopenharmony_ci /* This is required by WaCatErrorRejectionIssue:vlv */ 74058c2ecf20Sopenharmony_ci I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, 74068c2ecf20Sopenharmony_ci I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | 74078c2ecf20Sopenharmony_ci GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); 74088c2ecf20Sopenharmony_ci 74098c2ecf20Sopenharmony_ci /* 74108c2ecf20Sopenharmony_ci * According to the spec, bit 13 (RCZUNIT) must be set on IVB. 74118c2ecf20Sopenharmony_ci * This implements the WaDisableRCZUnitClockGating:vlv workaround. 74128c2ecf20Sopenharmony_ci */ 74138c2ecf20Sopenharmony_ci I915_WRITE(GEN6_UCGCTL2, 74148c2ecf20Sopenharmony_ci GEN6_RCZUNIT_CLOCK_GATE_DISABLE); 74158c2ecf20Sopenharmony_ci 74168c2ecf20Sopenharmony_ci /* WaDisableL3Bank2xClockGate:vlv 74178c2ecf20Sopenharmony_ci * Disabling L3 clock gating- MMIO 940c[25] = 1 74188c2ecf20Sopenharmony_ci * Set bit 25, to disable L3_BANK_2x_CLK_GATING */ 74198c2ecf20Sopenharmony_ci I915_WRITE(GEN7_UCGCTL4, 74208c2ecf20Sopenharmony_ci I915_READ(GEN7_UCGCTL4) | GEN7_L3BANK2X_CLOCK_GATE_DISABLE); 74218c2ecf20Sopenharmony_ci 74228c2ecf20Sopenharmony_ci /* 74238c2ecf20Sopenharmony_ci * WaDisableVLVClockGating_VBIIssue:vlv 74248c2ecf20Sopenharmony_ci * Disable clock gating on th GCFG unit to prevent a delay 74258c2ecf20Sopenharmony_ci * in the reporting of vblank events. 74268c2ecf20Sopenharmony_ci */ 74278c2ecf20Sopenharmony_ci I915_WRITE(VLV_GUNIT_CLOCK_GATE, GCFG_DIS); 74288c2ecf20Sopenharmony_ci} 74298c2ecf20Sopenharmony_ci 74308c2ecf20Sopenharmony_cistatic void chv_init_clock_gating(struct drm_i915_private *dev_priv) 74318c2ecf20Sopenharmony_ci{ 74328c2ecf20Sopenharmony_ci /* WaVSRefCountFullforceMissDisable:chv */ 74338c2ecf20Sopenharmony_ci /* WaDSRefCountFullforceMissDisable:chv */ 74348c2ecf20Sopenharmony_ci I915_WRITE(GEN7_FF_THREAD_MODE, 74358c2ecf20Sopenharmony_ci I915_READ(GEN7_FF_THREAD_MODE) & 74368c2ecf20Sopenharmony_ci ~(GEN8_FF_DS_REF_CNT_FFME | GEN7_FF_VS_REF_CNT_FFME)); 74378c2ecf20Sopenharmony_ci 74388c2ecf20Sopenharmony_ci /* WaDisableSemaphoreAndSyncFlipWait:chv */ 74398c2ecf20Sopenharmony_ci I915_WRITE(GEN6_RC_SLEEP_PSMI_CONTROL, 74408c2ecf20Sopenharmony_ci _MASKED_BIT_ENABLE(GEN8_RC_SEMA_IDLE_MSG_DISABLE)); 74418c2ecf20Sopenharmony_ci 74428c2ecf20Sopenharmony_ci /* WaDisableCSUnitClockGating:chv */ 74438c2ecf20Sopenharmony_ci I915_WRITE(GEN6_UCGCTL1, I915_READ(GEN6_UCGCTL1) | 74448c2ecf20Sopenharmony_ci GEN6_CSUNIT_CLOCK_GATE_DISABLE); 74458c2ecf20Sopenharmony_ci 74468c2ecf20Sopenharmony_ci /* WaDisableSDEUnitClockGating:chv */ 74478c2ecf20Sopenharmony_ci I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | 74488c2ecf20Sopenharmony_ci GEN8_SDEUNIT_CLOCK_GATE_DISABLE); 74498c2ecf20Sopenharmony_ci 74508c2ecf20Sopenharmony_ci /* 74518c2ecf20Sopenharmony_ci * WaProgramL3SqcReg1Default:chv 74528c2ecf20Sopenharmony_ci * See gfxspecs/Related Documents/Performance Guide/ 74538c2ecf20Sopenharmony_ci * LSQC Setting Recommendations. 74548c2ecf20Sopenharmony_ci */ 74558c2ecf20Sopenharmony_ci gen8_set_l3sqc_credits(dev_priv, 38, 2); 74568c2ecf20Sopenharmony_ci} 74578c2ecf20Sopenharmony_ci 74588c2ecf20Sopenharmony_cistatic void g4x_init_clock_gating(struct drm_i915_private *dev_priv) 74598c2ecf20Sopenharmony_ci{ 74608c2ecf20Sopenharmony_ci u32 dspclk_gate; 74618c2ecf20Sopenharmony_ci 74628c2ecf20Sopenharmony_ci I915_WRITE(RENCLK_GATE_D1, 0); 74638c2ecf20Sopenharmony_ci I915_WRITE(RENCLK_GATE_D2, VF_UNIT_CLOCK_GATE_DISABLE | 74648c2ecf20Sopenharmony_ci GS_UNIT_CLOCK_GATE_DISABLE | 74658c2ecf20Sopenharmony_ci CL_UNIT_CLOCK_GATE_DISABLE); 74668c2ecf20Sopenharmony_ci I915_WRITE(RAMCLK_GATE_D, 0); 74678c2ecf20Sopenharmony_ci dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE | 74688c2ecf20Sopenharmony_ci OVRUNIT_CLOCK_GATE_DISABLE | 74698c2ecf20Sopenharmony_ci OVCUNIT_CLOCK_GATE_DISABLE; 74708c2ecf20Sopenharmony_ci if (IS_GM45(dev_priv)) 74718c2ecf20Sopenharmony_ci dspclk_gate |= DSSUNIT_CLOCK_GATE_DISABLE; 74728c2ecf20Sopenharmony_ci I915_WRITE(DSPCLK_GATE_D, dspclk_gate); 74738c2ecf20Sopenharmony_ci 74748c2ecf20Sopenharmony_ci g4x_disable_trickle_feed(dev_priv); 74758c2ecf20Sopenharmony_ci} 74768c2ecf20Sopenharmony_ci 74778c2ecf20Sopenharmony_cistatic void i965gm_init_clock_gating(struct drm_i915_private *dev_priv) 74788c2ecf20Sopenharmony_ci{ 74798c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 74808c2ecf20Sopenharmony_ci 74818c2ecf20Sopenharmony_ci intel_uncore_write(uncore, RENCLK_GATE_D1, I965_RCC_CLOCK_GATE_DISABLE); 74828c2ecf20Sopenharmony_ci intel_uncore_write(uncore, RENCLK_GATE_D2, 0); 74838c2ecf20Sopenharmony_ci intel_uncore_write(uncore, DSPCLK_GATE_D, 0); 74848c2ecf20Sopenharmony_ci intel_uncore_write(uncore, RAMCLK_GATE_D, 0); 74858c2ecf20Sopenharmony_ci intel_uncore_write16(uncore, DEUC, 0); 74868c2ecf20Sopenharmony_ci intel_uncore_write(uncore, 74878c2ecf20Sopenharmony_ci MI_ARB_STATE, 74888c2ecf20Sopenharmony_ci _MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE)); 74898c2ecf20Sopenharmony_ci} 74908c2ecf20Sopenharmony_ci 74918c2ecf20Sopenharmony_cistatic void i965g_init_clock_gating(struct drm_i915_private *dev_priv) 74928c2ecf20Sopenharmony_ci{ 74938c2ecf20Sopenharmony_ci I915_WRITE(RENCLK_GATE_D1, I965_RCZ_CLOCK_GATE_DISABLE | 74948c2ecf20Sopenharmony_ci I965_RCC_CLOCK_GATE_DISABLE | 74958c2ecf20Sopenharmony_ci I965_RCPB_CLOCK_GATE_DISABLE | 74968c2ecf20Sopenharmony_ci I965_ISC_CLOCK_GATE_DISABLE | 74978c2ecf20Sopenharmony_ci I965_FBC_CLOCK_GATE_DISABLE); 74988c2ecf20Sopenharmony_ci I915_WRITE(RENCLK_GATE_D2, 0); 74998c2ecf20Sopenharmony_ci I915_WRITE(MI_ARB_STATE, 75008c2ecf20Sopenharmony_ci _MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE)); 75018c2ecf20Sopenharmony_ci} 75028c2ecf20Sopenharmony_ci 75038c2ecf20Sopenharmony_cistatic void gen3_init_clock_gating(struct drm_i915_private *dev_priv) 75048c2ecf20Sopenharmony_ci{ 75058c2ecf20Sopenharmony_ci u32 dstate = I915_READ(D_STATE); 75068c2ecf20Sopenharmony_ci 75078c2ecf20Sopenharmony_ci dstate |= DSTATE_PLL_D3_OFF | DSTATE_GFX_CLOCK_GATING | 75088c2ecf20Sopenharmony_ci DSTATE_DOT_CLOCK_GATING; 75098c2ecf20Sopenharmony_ci I915_WRITE(D_STATE, dstate); 75108c2ecf20Sopenharmony_ci 75118c2ecf20Sopenharmony_ci if (IS_PINEVIEW(dev_priv)) 75128c2ecf20Sopenharmony_ci I915_WRITE(ECOSKPD, _MASKED_BIT_ENABLE(ECO_GATING_CX_ONLY)); 75138c2ecf20Sopenharmony_ci 75148c2ecf20Sopenharmony_ci /* IIR "flip pending" means done if this bit is set */ 75158c2ecf20Sopenharmony_ci I915_WRITE(ECOSKPD, _MASKED_BIT_DISABLE(ECO_FLIP_DONE)); 75168c2ecf20Sopenharmony_ci 75178c2ecf20Sopenharmony_ci /* interrupts should cause a wake up from C3 */ 75188c2ecf20Sopenharmony_ci I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_AGPBUSY_INT_EN)); 75198c2ecf20Sopenharmony_ci 75208c2ecf20Sopenharmony_ci /* On GEN3 we really need to make sure the ARB C3 LP bit is set */ 75218c2ecf20Sopenharmony_ci I915_WRITE(MI_ARB_STATE, _MASKED_BIT_ENABLE(MI_ARB_C3_LP_WRITE_ENABLE)); 75228c2ecf20Sopenharmony_ci 75238c2ecf20Sopenharmony_ci I915_WRITE(MI_ARB_STATE, 75248c2ecf20Sopenharmony_ci _MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE)); 75258c2ecf20Sopenharmony_ci} 75268c2ecf20Sopenharmony_ci 75278c2ecf20Sopenharmony_cistatic void i85x_init_clock_gating(struct drm_i915_private *dev_priv) 75288c2ecf20Sopenharmony_ci{ 75298c2ecf20Sopenharmony_ci I915_WRITE(RENCLK_GATE_D1, SV_CLOCK_GATE_DISABLE); 75308c2ecf20Sopenharmony_ci 75318c2ecf20Sopenharmony_ci /* interrupts should cause a wake up from C3 */ 75328c2ecf20Sopenharmony_ci I915_WRITE(MI_STATE, _MASKED_BIT_ENABLE(MI_AGPBUSY_INT_EN) | 75338c2ecf20Sopenharmony_ci _MASKED_BIT_DISABLE(MI_AGPBUSY_830_MODE)); 75348c2ecf20Sopenharmony_ci 75358c2ecf20Sopenharmony_ci I915_WRITE(MEM_MODE, 75368c2ecf20Sopenharmony_ci _MASKED_BIT_ENABLE(MEM_DISPLAY_TRICKLE_FEED_DISABLE)); 75378c2ecf20Sopenharmony_ci 75388c2ecf20Sopenharmony_ci /* 75398c2ecf20Sopenharmony_ci * Have FBC ignore 3D activity since we use software 75408c2ecf20Sopenharmony_ci * render tracking, and otherwise a pure 3D workload 75418c2ecf20Sopenharmony_ci * (even if it just renders a single frame and then does 75428c2ecf20Sopenharmony_ci * abosultely nothing) would not allow FBC to recompress 75438c2ecf20Sopenharmony_ci * until a 2D blit occurs. 75448c2ecf20Sopenharmony_ci */ 75458c2ecf20Sopenharmony_ci I915_WRITE(SCPD0, 75468c2ecf20Sopenharmony_ci _MASKED_BIT_ENABLE(SCPD_FBC_IGNORE_3D)); 75478c2ecf20Sopenharmony_ci} 75488c2ecf20Sopenharmony_ci 75498c2ecf20Sopenharmony_cistatic void i830_init_clock_gating(struct drm_i915_private *dev_priv) 75508c2ecf20Sopenharmony_ci{ 75518c2ecf20Sopenharmony_ci I915_WRITE(MEM_MODE, 75528c2ecf20Sopenharmony_ci _MASKED_BIT_ENABLE(MEM_DISPLAY_A_TRICKLE_FEED_DISABLE) | 75538c2ecf20Sopenharmony_ci _MASKED_BIT_ENABLE(MEM_DISPLAY_B_TRICKLE_FEED_DISABLE)); 75548c2ecf20Sopenharmony_ci} 75558c2ecf20Sopenharmony_ci 75568c2ecf20Sopenharmony_civoid intel_init_clock_gating(struct drm_i915_private *dev_priv) 75578c2ecf20Sopenharmony_ci{ 75588c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating(dev_priv); 75598c2ecf20Sopenharmony_ci} 75608c2ecf20Sopenharmony_ci 75618c2ecf20Sopenharmony_civoid intel_suspend_hw(struct drm_i915_private *dev_priv) 75628c2ecf20Sopenharmony_ci{ 75638c2ecf20Sopenharmony_ci if (HAS_PCH_LPT(dev_priv)) 75648c2ecf20Sopenharmony_ci lpt_suspend_hw(dev_priv); 75658c2ecf20Sopenharmony_ci} 75668c2ecf20Sopenharmony_ci 75678c2ecf20Sopenharmony_cistatic void nop_init_clock_gating(struct drm_i915_private *dev_priv) 75688c2ecf20Sopenharmony_ci{ 75698c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 75708c2ecf20Sopenharmony_ci "No clock gating settings or workarounds applied.\n"); 75718c2ecf20Sopenharmony_ci} 75728c2ecf20Sopenharmony_ci 75738c2ecf20Sopenharmony_ci/** 75748c2ecf20Sopenharmony_ci * intel_init_clock_gating_hooks - setup the clock gating hooks 75758c2ecf20Sopenharmony_ci * @dev_priv: device private 75768c2ecf20Sopenharmony_ci * 75778c2ecf20Sopenharmony_ci * Setup the hooks that configure which clocks of a given platform can be 75788c2ecf20Sopenharmony_ci * gated and also apply various GT and display specific workarounds for these 75798c2ecf20Sopenharmony_ci * platforms. Note that some GT specific workarounds are applied separately 75808c2ecf20Sopenharmony_ci * when GPU contexts or batchbuffers start their execution. 75818c2ecf20Sopenharmony_ci */ 75828c2ecf20Sopenharmony_civoid intel_init_clock_gating_hooks(struct drm_i915_private *dev_priv) 75838c2ecf20Sopenharmony_ci{ 75848c2ecf20Sopenharmony_ci if (IS_GEN(dev_priv, 12)) 75858c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = tgl_init_clock_gating; 75868c2ecf20Sopenharmony_ci else if (IS_GEN(dev_priv, 11)) 75878c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = icl_init_clock_gating; 75888c2ecf20Sopenharmony_ci else if (IS_CANNONLAKE(dev_priv)) 75898c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = cnl_init_clock_gating; 75908c2ecf20Sopenharmony_ci else if (IS_COFFEELAKE(dev_priv) || IS_COMETLAKE(dev_priv)) 75918c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = cfl_init_clock_gating; 75928c2ecf20Sopenharmony_ci else if (IS_SKYLAKE(dev_priv)) 75938c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = skl_init_clock_gating; 75948c2ecf20Sopenharmony_ci else if (IS_KABYLAKE(dev_priv)) 75958c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = kbl_init_clock_gating; 75968c2ecf20Sopenharmony_ci else if (IS_BROXTON(dev_priv)) 75978c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = bxt_init_clock_gating; 75988c2ecf20Sopenharmony_ci else if (IS_GEMINILAKE(dev_priv)) 75998c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = glk_init_clock_gating; 76008c2ecf20Sopenharmony_ci else if (IS_BROADWELL(dev_priv)) 76018c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = bdw_init_clock_gating; 76028c2ecf20Sopenharmony_ci else if (IS_CHERRYVIEW(dev_priv)) 76038c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = chv_init_clock_gating; 76048c2ecf20Sopenharmony_ci else if (IS_HASWELL(dev_priv)) 76058c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = hsw_init_clock_gating; 76068c2ecf20Sopenharmony_ci else if (IS_IVYBRIDGE(dev_priv)) 76078c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = ivb_init_clock_gating; 76088c2ecf20Sopenharmony_ci else if (IS_VALLEYVIEW(dev_priv)) 76098c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = vlv_init_clock_gating; 76108c2ecf20Sopenharmony_ci else if (IS_GEN(dev_priv, 6)) 76118c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = gen6_init_clock_gating; 76128c2ecf20Sopenharmony_ci else if (IS_GEN(dev_priv, 5)) 76138c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = ilk_init_clock_gating; 76148c2ecf20Sopenharmony_ci else if (IS_G4X(dev_priv)) 76158c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = g4x_init_clock_gating; 76168c2ecf20Sopenharmony_ci else if (IS_I965GM(dev_priv)) 76178c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = i965gm_init_clock_gating; 76188c2ecf20Sopenharmony_ci else if (IS_I965G(dev_priv)) 76198c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = i965g_init_clock_gating; 76208c2ecf20Sopenharmony_ci else if (IS_GEN(dev_priv, 3)) 76218c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = gen3_init_clock_gating; 76228c2ecf20Sopenharmony_ci else if (IS_I85X(dev_priv) || IS_I865G(dev_priv)) 76238c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = i85x_init_clock_gating; 76248c2ecf20Sopenharmony_ci else if (IS_GEN(dev_priv, 2)) 76258c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = i830_init_clock_gating; 76268c2ecf20Sopenharmony_ci else { 76278c2ecf20Sopenharmony_ci MISSING_CASE(INTEL_DEVID(dev_priv)); 76288c2ecf20Sopenharmony_ci dev_priv->display.init_clock_gating = nop_init_clock_gating; 76298c2ecf20Sopenharmony_ci } 76308c2ecf20Sopenharmony_ci} 76318c2ecf20Sopenharmony_ci 76328c2ecf20Sopenharmony_ci/* Set up chip specific power management-related functions */ 76338c2ecf20Sopenharmony_civoid intel_init_pm(struct drm_i915_private *dev_priv) 76348c2ecf20Sopenharmony_ci{ 76358c2ecf20Sopenharmony_ci /* For cxsr */ 76368c2ecf20Sopenharmony_ci if (IS_PINEVIEW(dev_priv)) 76378c2ecf20Sopenharmony_ci pnv_get_mem_freq(dev_priv); 76388c2ecf20Sopenharmony_ci else if (IS_GEN(dev_priv, 5)) 76398c2ecf20Sopenharmony_ci ilk_get_mem_freq(dev_priv); 76408c2ecf20Sopenharmony_ci 76418c2ecf20Sopenharmony_ci if (intel_has_sagv(dev_priv)) 76428c2ecf20Sopenharmony_ci skl_setup_sagv_block_time(dev_priv); 76438c2ecf20Sopenharmony_ci 76448c2ecf20Sopenharmony_ci /* For FIFO watermark updates */ 76458c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 9) { 76468c2ecf20Sopenharmony_ci skl_setup_wm_latency(dev_priv); 76478c2ecf20Sopenharmony_ci dev_priv->display.compute_global_watermarks = skl_compute_wm; 76488c2ecf20Sopenharmony_ci } else if (HAS_PCH_SPLIT(dev_priv)) { 76498c2ecf20Sopenharmony_ci ilk_setup_wm_latency(dev_priv); 76508c2ecf20Sopenharmony_ci 76518c2ecf20Sopenharmony_ci if ((IS_GEN(dev_priv, 5) && dev_priv->wm.pri_latency[1] && 76528c2ecf20Sopenharmony_ci dev_priv->wm.spr_latency[1] && dev_priv->wm.cur_latency[1]) || 76538c2ecf20Sopenharmony_ci (!IS_GEN(dev_priv, 5) && dev_priv->wm.pri_latency[0] && 76548c2ecf20Sopenharmony_ci dev_priv->wm.spr_latency[0] && dev_priv->wm.cur_latency[0])) { 76558c2ecf20Sopenharmony_ci dev_priv->display.compute_pipe_wm = ilk_compute_pipe_wm; 76568c2ecf20Sopenharmony_ci dev_priv->display.compute_intermediate_wm = 76578c2ecf20Sopenharmony_ci ilk_compute_intermediate_wm; 76588c2ecf20Sopenharmony_ci dev_priv->display.initial_watermarks = 76598c2ecf20Sopenharmony_ci ilk_initial_watermarks; 76608c2ecf20Sopenharmony_ci dev_priv->display.optimize_watermarks = 76618c2ecf20Sopenharmony_ci ilk_optimize_watermarks; 76628c2ecf20Sopenharmony_ci } else { 76638c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 76648c2ecf20Sopenharmony_ci "Failed to read display plane latency. " 76658c2ecf20Sopenharmony_ci "Disable CxSR\n"); 76668c2ecf20Sopenharmony_ci } 76678c2ecf20Sopenharmony_ci } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { 76688c2ecf20Sopenharmony_ci vlv_setup_wm_latency(dev_priv); 76698c2ecf20Sopenharmony_ci dev_priv->display.compute_pipe_wm = vlv_compute_pipe_wm; 76708c2ecf20Sopenharmony_ci dev_priv->display.compute_intermediate_wm = vlv_compute_intermediate_wm; 76718c2ecf20Sopenharmony_ci dev_priv->display.initial_watermarks = vlv_initial_watermarks; 76728c2ecf20Sopenharmony_ci dev_priv->display.optimize_watermarks = vlv_optimize_watermarks; 76738c2ecf20Sopenharmony_ci dev_priv->display.atomic_update_watermarks = vlv_atomic_update_fifo; 76748c2ecf20Sopenharmony_ci } else if (IS_G4X(dev_priv)) { 76758c2ecf20Sopenharmony_ci g4x_setup_wm_latency(dev_priv); 76768c2ecf20Sopenharmony_ci dev_priv->display.compute_pipe_wm = g4x_compute_pipe_wm; 76778c2ecf20Sopenharmony_ci dev_priv->display.compute_intermediate_wm = g4x_compute_intermediate_wm; 76788c2ecf20Sopenharmony_ci dev_priv->display.initial_watermarks = g4x_initial_watermarks; 76798c2ecf20Sopenharmony_ci dev_priv->display.optimize_watermarks = g4x_optimize_watermarks; 76808c2ecf20Sopenharmony_ci } else if (IS_PINEVIEW(dev_priv)) { 76818c2ecf20Sopenharmony_ci if (!intel_get_cxsr_latency(!IS_MOBILE(dev_priv), 76828c2ecf20Sopenharmony_ci dev_priv->is_ddr3, 76838c2ecf20Sopenharmony_ci dev_priv->fsb_freq, 76848c2ecf20Sopenharmony_ci dev_priv->mem_freq)) { 76858c2ecf20Sopenharmony_ci drm_info(&dev_priv->drm, 76868c2ecf20Sopenharmony_ci "failed to find known CxSR latency " 76878c2ecf20Sopenharmony_ci "(found ddr%s fsb freq %d, mem freq %d), " 76888c2ecf20Sopenharmony_ci "disabling CxSR\n", 76898c2ecf20Sopenharmony_ci (dev_priv->is_ddr3 == 1) ? "3" : "2", 76908c2ecf20Sopenharmony_ci dev_priv->fsb_freq, dev_priv->mem_freq); 76918c2ecf20Sopenharmony_ci /* Disable CxSR and never update its watermark again */ 76928c2ecf20Sopenharmony_ci intel_set_memory_cxsr(dev_priv, false); 76938c2ecf20Sopenharmony_ci dev_priv->display.update_wm = NULL; 76948c2ecf20Sopenharmony_ci } else 76958c2ecf20Sopenharmony_ci dev_priv->display.update_wm = pnv_update_wm; 76968c2ecf20Sopenharmony_ci } else if (IS_GEN(dev_priv, 4)) { 76978c2ecf20Sopenharmony_ci dev_priv->display.update_wm = i965_update_wm; 76988c2ecf20Sopenharmony_ci } else if (IS_GEN(dev_priv, 3)) { 76998c2ecf20Sopenharmony_ci dev_priv->display.update_wm = i9xx_update_wm; 77008c2ecf20Sopenharmony_ci dev_priv->display.get_fifo_size = i9xx_get_fifo_size; 77018c2ecf20Sopenharmony_ci } else if (IS_GEN(dev_priv, 2)) { 77028c2ecf20Sopenharmony_ci if (INTEL_NUM_PIPES(dev_priv) == 1) { 77038c2ecf20Sopenharmony_ci dev_priv->display.update_wm = i845_update_wm; 77048c2ecf20Sopenharmony_ci dev_priv->display.get_fifo_size = i845_get_fifo_size; 77058c2ecf20Sopenharmony_ci } else { 77068c2ecf20Sopenharmony_ci dev_priv->display.update_wm = i9xx_update_wm; 77078c2ecf20Sopenharmony_ci dev_priv->display.get_fifo_size = i830_get_fifo_size; 77088c2ecf20Sopenharmony_ci } 77098c2ecf20Sopenharmony_ci } else { 77108c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, 77118c2ecf20Sopenharmony_ci "unexpected fall-through in %s\n", __func__); 77128c2ecf20Sopenharmony_ci } 77138c2ecf20Sopenharmony_ci} 77148c2ecf20Sopenharmony_ci 77158c2ecf20Sopenharmony_civoid intel_pm_setup(struct drm_i915_private *dev_priv) 77168c2ecf20Sopenharmony_ci{ 77178c2ecf20Sopenharmony_ci dev_priv->runtime_pm.suspended = false; 77188c2ecf20Sopenharmony_ci atomic_set(&dev_priv->runtime_pm.wakeref_count, 0); 77198c2ecf20Sopenharmony_ci} 77208c2ecf20Sopenharmony_ci 77218c2ecf20Sopenharmony_cistatic struct intel_global_state *intel_dbuf_duplicate_state(struct intel_global_obj *obj) 77228c2ecf20Sopenharmony_ci{ 77238c2ecf20Sopenharmony_ci struct intel_dbuf_state *dbuf_state; 77248c2ecf20Sopenharmony_ci 77258c2ecf20Sopenharmony_ci dbuf_state = kmemdup(obj->state, sizeof(*dbuf_state), GFP_KERNEL); 77268c2ecf20Sopenharmony_ci if (!dbuf_state) 77278c2ecf20Sopenharmony_ci return NULL; 77288c2ecf20Sopenharmony_ci 77298c2ecf20Sopenharmony_ci return &dbuf_state->base; 77308c2ecf20Sopenharmony_ci} 77318c2ecf20Sopenharmony_ci 77328c2ecf20Sopenharmony_cistatic void intel_dbuf_destroy_state(struct intel_global_obj *obj, 77338c2ecf20Sopenharmony_ci struct intel_global_state *state) 77348c2ecf20Sopenharmony_ci{ 77358c2ecf20Sopenharmony_ci kfree(state); 77368c2ecf20Sopenharmony_ci} 77378c2ecf20Sopenharmony_ci 77388c2ecf20Sopenharmony_cistatic const struct intel_global_state_funcs intel_dbuf_funcs = { 77398c2ecf20Sopenharmony_ci .atomic_duplicate_state = intel_dbuf_duplicate_state, 77408c2ecf20Sopenharmony_ci .atomic_destroy_state = intel_dbuf_destroy_state, 77418c2ecf20Sopenharmony_ci}; 77428c2ecf20Sopenharmony_ci 77438c2ecf20Sopenharmony_cistruct intel_dbuf_state * 77448c2ecf20Sopenharmony_ciintel_atomic_get_dbuf_state(struct intel_atomic_state *state) 77458c2ecf20Sopenharmony_ci{ 77468c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(state->base.dev); 77478c2ecf20Sopenharmony_ci struct intel_global_state *dbuf_state; 77488c2ecf20Sopenharmony_ci 77498c2ecf20Sopenharmony_ci dbuf_state = intel_atomic_get_global_obj_state(state, &dev_priv->dbuf.obj); 77508c2ecf20Sopenharmony_ci if (IS_ERR(dbuf_state)) 77518c2ecf20Sopenharmony_ci return ERR_CAST(dbuf_state); 77528c2ecf20Sopenharmony_ci 77538c2ecf20Sopenharmony_ci return to_intel_dbuf_state(dbuf_state); 77548c2ecf20Sopenharmony_ci} 77558c2ecf20Sopenharmony_ci 77568c2ecf20Sopenharmony_ciint intel_dbuf_init(struct drm_i915_private *dev_priv) 77578c2ecf20Sopenharmony_ci{ 77588c2ecf20Sopenharmony_ci struct intel_dbuf_state *dbuf_state; 77598c2ecf20Sopenharmony_ci 77608c2ecf20Sopenharmony_ci dbuf_state = kzalloc(sizeof(*dbuf_state), GFP_KERNEL); 77618c2ecf20Sopenharmony_ci if (!dbuf_state) 77628c2ecf20Sopenharmony_ci return -ENOMEM; 77638c2ecf20Sopenharmony_ci 77648c2ecf20Sopenharmony_ci intel_atomic_global_obj_init(dev_priv, &dev_priv->dbuf.obj, 77658c2ecf20Sopenharmony_ci &dbuf_state->base, &intel_dbuf_funcs); 77668c2ecf20Sopenharmony_ci 77678c2ecf20Sopenharmony_ci return 0; 77688c2ecf20Sopenharmony_ci} 77698c2ecf20Sopenharmony_ci 77708c2ecf20Sopenharmony_civoid intel_dbuf_pre_plane_update(struct intel_atomic_state *state) 77718c2ecf20Sopenharmony_ci{ 77728c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(state->base.dev); 77738c2ecf20Sopenharmony_ci const struct intel_dbuf_state *new_dbuf_state = 77748c2ecf20Sopenharmony_ci intel_atomic_get_new_dbuf_state(state); 77758c2ecf20Sopenharmony_ci const struct intel_dbuf_state *old_dbuf_state = 77768c2ecf20Sopenharmony_ci intel_atomic_get_old_dbuf_state(state); 77778c2ecf20Sopenharmony_ci 77788c2ecf20Sopenharmony_ci if (!new_dbuf_state || 77798c2ecf20Sopenharmony_ci new_dbuf_state->enabled_slices == old_dbuf_state->enabled_slices) 77808c2ecf20Sopenharmony_ci return; 77818c2ecf20Sopenharmony_ci 77828c2ecf20Sopenharmony_ci WARN_ON(!new_dbuf_state->base.changed); 77838c2ecf20Sopenharmony_ci 77848c2ecf20Sopenharmony_ci gen9_dbuf_slices_update(dev_priv, 77858c2ecf20Sopenharmony_ci old_dbuf_state->enabled_slices | 77868c2ecf20Sopenharmony_ci new_dbuf_state->enabled_slices); 77878c2ecf20Sopenharmony_ci} 77888c2ecf20Sopenharmony_ci 77898c2ecf20Sopenharmony_civoid intel_dbuf_post_plane_update(struct intel_atomic_state *state) 77908c2ecf20Sopenharmony_ci{ 77918c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(state->base.dev); 77928c2ecf20Sopenharmony_ci const struct intel_dbuf_state *new_dbuf_state = 77938c2ecf20Sopenharmony_ci intel_atomic_get_new_dbuf_state(state); 77948c2ecf20Sopenharmony_ci const struct intel_dbuf_state *old_dbuf_state = 77958c2ecf20Sopenharmony_ci intel_atomic_get_old_dbuf_state(state); 77968c2ecf20Sopenharmony_ci 77978c2ecf20Sopenharmony_ci if (!new_dbuf_state || 77988c2ecf20Sopenharmony_ci new_dbuf_state->enabled_slices == old_dbuf_state->enabled_slices) 77998c2ecf20Sopenharmony_ci return; 78008c2ecf20Sopenharmony_ci 78018c2ecf20Sopenharmony_ci WARN_ON(!new_dbuf_state->base.changed); 78028c2ecf20Sopenharmony_ci 78038c2ecf20Sopenharmony_ci gen9_dbuf_slices_update(dev_priv, 78048c2ecf20Sopenharmony_ci new_dbuf_state->enabled_slices); 78058c2ecf20Sopenharmony_ci} 7806