aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915/display/intel_modeset_setup.c
diff options
context:
space:
mode:
authorLibravatar Linus Torvalds <torvalds@linux-foundation.org>2023-02-21 18:24:12 -0800
committerLibravatar Linus Torvalds <torvalds@linux-foundation.org>2023-02-21 18:24:12 -0800
commit5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch)
treecc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/gpu/drm/i915/display/intel_modeset_setup.c
downloadlinux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz
linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski: "Core: - Add dedicated kmem_cache for typical/small skb->head, avoid having to access struct page at kfree time, and improve memory use. - Introduce sysctl to set default RPS configuration for new netdevs. - Define Netlink protocol specification format which can be used to describe messages used by each family and auto-generate parsers. Add tools for generating kernel data structures and uAPI headers. - Expose all net/core sysctls inside netns. - Remove 4s sleep in netpoll if carrier is instantly detected on boot. - Add configurable limit of MDB entries per port, and port-vlan. - Continue populating drop reasons throughout the stack. - Retire a handful of legacy Qdiscs and classifiers. Protocols: - Support IPv4 big TCP (TSO frames larger than 64kB). - Add IP_LOCAL_PORT_RANGE socket option, to control local port range on socket by socket basis. - Track and report in procfs number of MPTCP sockets used. - Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path manager. - IPv6: don't check net.ipv6.route.max_size and rely on garbage collection to free memory (similarly to IPv4). - Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986). - ICMP: add per-rate limit counters. - Add support for user scanning requests in ieee802154. - Remove static WEP support. - Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate reporting. - WiFi 7 EHT channel puncturing support (client & AP). BPF: - Add a rbtree data structure following the "next-gen data structure" precedent set by recently added linked list, that is, by using kfunc + kptr instead of adding a new BPF map type. - Expose XDP hints via kfuncs with initial support for RX hash and timestamp metadata. - Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to better support decap on GRE tunnel devices not operating in collect metadata. - Improve x86 JIT's codegen for PROBE_MEM runtime error checks. - Remove the need for trace_printk_lock for bpf_trace_printk and bpf_trace_vprintk helpers. - Extend libbpf's bpf_tracing.h support for tracing arguments of kprobes/uprobes and syscall as a special case. - Significantly reduce the search time for module symbols by livepatch and BPF. - Enable cpumasks to be used as kptrs, which is useful for tracing programs tracking which tasks end up running on which CPUs in different time intervals. - Add support for BPF trampoline on s390x and riscv64. - Add capability to export the XDP features supported by the NIC. - Add __bpf_kfunc tag for marking kernel functions as kfuncs. - Add cgroup.memory=nobpf kernel parameter option to disable BPF memory accounting for container environments. Netfilter: - Remove the CLUSTERIP target. It has been marked as obsolete for years, and we still have WARN splats wrt races of the out-of-band /proc interface installed by this target. - Add 'destroy' commands to nf_tables. They are identical to the existing 'delete' commands, but do not return an error if the referenced object (set, chain, rule...) did not exist. Driver API: - Improve cpumask_local_spread() locality to help NICs set the right IRQ affinity on AMD platforms. - Separate C22 and C45 MDIO bus transactions more clearly. - Introduce new DCB table to control DSCP rewrite on egress. - Support configuration of Physical Layer Collision Avoidance (PLCA) Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of shared medium Ethernet. - Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing preemption of low priority frames by high priority frames. - Add support for controlling MACSec offload using netlink SET. - Rework devlink instance refcounts to allow registration and de-registration under the instance lock. Split the code into multiple files, drop some of the unnecessarily granular locks and factor out common parts of netlink operation handling. - Add TX frame aggregation parameters (for USB drivers). - Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning messages with notifications for debug. - Allow offloading of UDP NEW connections via act_ct. - Add support for per action HW stats in TC. - Support hardware miss to TC action (continue processing in SW from a specific point in the action chain). - Warn if old Wireless Extension user space interface is used with modern cfg80211/mac80211 drivers. Do not support Wireless Extensions for Wi-Fi 7 devices at all. Everyone should switch to using nl80211 interface instead. - Improve the CAN bit timing configuration. Use extack to return error messages directly to user space, update the SJW handling, including the definition of a new default value that will benefit CAN-FD controllers, by increasing their oscillator tolerance. New hardware / drivers: - Ethernet: - nVidia BlueField-3 support (control traffic driver) - Ethernet support for imx93 SoCs - Motorcomm yt8531 gigabit Ethernet PHY - onsemi NCN26000 10BASE-T1S PHY (with support for PLCA) - Microchip LAN8841 PHY (incl. cable diagnostics and PTP) - Amlogic gxl MDIO mux - WiFi: - RealTek RTL8188EU (rtl8xxxu) - Qualcomm Wi-Fi 7 devices (ath12k) - CAN: - Renesas R-Car V4H Drivers: - Bluetooth: - Set Per Platform Antenna Gain (PPAG) for Intel controllers. - Ethernet NICs: - Intel (1G, igc): - support TSN / Qbv / packet scheduling features of i226 model - Intel (100G, ice): - use GNSS subsystem instead of TTY - multi-buffer XDP support - extend support for GPIO pins to E823 devices - nVidia/Mellanox: - update the shared buffer configuration on PFC commands - implement PTP adjphase function for HW offset control - TC support for Geneve and GRE with VF tunnel offload - more efficient crypto key management method - multi-port eswitch support - Netronome/Corigine: - add DCB IEEE support - support IPsec offloading for NFP3800 - Freescale/NXP (enetc): - support XDP_REDIRECT for XDP non-linear buffers - improve reconfig, avoid link flap and waiting for idle - support MAC Merge layer - Other NICs: - sfc/ef100: add basic devlink support for ef100 - ionic: rx_push mode operation (writing descriptors via MMIO) - bnxt: use the auxiliary bus abstraction for RDMA - r8169: disable ASPM and reset bus in case of tx timeout - cpsw: support QSGMII mode for J721e CPSW9G - cpts: support pulse-per-second output - ngbe: add an mdio bus driver - usbnet: optimize usbnet_bh() by avoiding unnecessary queuing - r8152: handle devices with FW with NCM support - amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation - virtio-net: support multi buffer XDP - virtio/vsock: replace virtio_vsock_pkt with sk_buff - tsnep: XDP support - Ethernet high-speed switches: - nVidia/Mellanox (mlxsw): - add support for latency TLV (in FW control messages) - Microchip (sparx5): - separate explicit and implicit traffic forwarding rules, make the implicit rules always active - add support for egress DSCP rewrite - IS0 VCAP support (Ingress Classification) - IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS etc.) - ES2 VCAP support (Egress Access Control) - support for Per-Stream Filtering and Policing (802.1Q, 8.6.5.1) - Ethernet embedded switches: - Marvell (mv88e6xxx): - add MAB (port auth) offload support - enable PTP receive for mv88e6390 - NXP (ocelot): - support MAC Merge layer - support for the the vsc7512 internal copper phys - Microchip: - lan9303: convert to PHYLINK - lan966x: support TC flower filter statistics - lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x - lan937x: support Credit Based Shaper configuration - ksz9477: support Energy Efficient Ethernet - other: - qca8k: convert to regmap read/write API, use bulk operations - rswitch: Improve TX timestamp accuracy - Intel WiFi (iwlwifi): - EHT (Wi-Fi 7) rate reporting - STEP equalizer support: transfer some STEP (connection to radio on platforms with integrated wifi) related parameters from the BIOS to the firmware. - Qualcomm 802.11ax WiFi (ath11k): - IPQ5018 support - Fine Timing Measurement (FTM) responder role support - channel 177 support - MediaTek WiFi (mt76): - per-PHY LED support - mt7996: EHT (Wi-Fi 7) support - Wireless Ethernet Dispatch (WED) reset support - switch to using page pool allocator - RealTek WiFi (rtw89): - support new version of Bluetooth co-existance - Mobile: - rmnet: support TX aggregation" * tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits) page_pool: add a comment explaining the fragment counter usage net: ethtool: fix __ethtool_dev_mm_supported() implementation ethtool: pse-pd: Fix double word in comments xsk: add linux/vmalloc.h to xsk.c sefltests: netdevsim: wait for devlink instance after netns removal selftest: fib_tests: Always cleanup before exit net/mlx5e: Align IPsec ASO result memory to be as required by hardware net/mlx5e: TC, Set CT miss to the specific ct action instance net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG net/mlx5: Refactor tc miss handling to a single function net/mlx5: Kconfig: Make tc offload depend on tc skb extension net/sched: flower: Support hardware miss to tc action net/sched: flower: Move filter handle initialization earlier net/sched: cls_api: Support hardware miss to tc action net/sched: Rename user cookie and act cookie sfc: fix builds without CONFIG_RTC_LIB sfc: clean up some inconsistent indentings net/mlx4_en: Introduce flexible array to silence overflow warning net: lan966x: Fix possible deadlock inside PTP net/ulp: Remove redundant ->clone() test in inet_clone_ulp(). ...
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_modeset_setup.c')
-rw-r--r--drivers/gpu/drm/i915/display/intel_modeset_setup.c750
1 files changed, 750 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_modeset_setup.c b/drivers/gpu/drm/i915/display/intel_modeset_setup.c
new file mode 100644
index 000000000..96395bfbd
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_modeset_setup.c
@@ -0,0 +1,750 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2022 Intel Corporation
+ *
+ * Read out the current hardware modeset state, and sanitize it to the current
+ * state.
+ */
+
+#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_atomic_state_helper.h>
+
+#include "i915_drv.h"
+#include "i915_reg.h"
+#include "intel_atomic.h"
+#include "intel_bw.h"
+#include "intel_color.h"
+#include "intel_crtc.h"
+#include "intel_crtc_state_dump.h"
+#include "intel_ddi.h"
+#include "intel_de.h"
+#include "intel_display.h"
+#include "intel_display_power.h"
+#include "intel_display_types.h"
+#include "intel_modeset_setup.h"
+#include "intel_pch_display.h"
+#include "intel_pm.h"
+#include "skl_watermark.h"
+
+static void intel_crtc_disable_noatomic(struct intel_crtc *crtc,
+ struct drm_modeset_acquire_ctx *ctx)
+{
+ struct intel_encoder *encoder;
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+ struct intel_bw_state *bw_state =
+ to_intel_bw_state(i915->display.bw.obj.state);
+ struct intel_cdclk_state *cdclk_state =
+ to_intel_cdclk_state(i915->display.cdclk.obj.state);
+ struct intel_dbuf_state *dbuf_state =
+ to_intel_dbuf_state(i915->display.dbuf.obj.state);
+ struct intel_crtc_state *crtc_state =
+ to_intel_crtc_state(crtc->base.state);
+ struct intel_plane *plane;
+ struct drm_atomic_state *state;
+ struct intel_crtc_state *temp_crtc_state;
+ enum pipe pipe = crtc->pipe;
+ int ret;
+
+ if (!crtc_state->hw.active)
+ return;
+
+ for_each_intel_plane_on_crtc(&i915->drm, crtc, plane) {
+ const struct intel_plane_state *plane_state =
+ to_intel_plane_state(plane->base.state);
+
+ if (plane_state->uapi.visible)
+ intel_plane_disable_noatomic(crtc, plane);
+ }
+
+ state = drm_atomic_state_alloc(&i915->drm);
+ if (!state) {
+ drm_dbg_kms(&i915->drm,
+ "failed to disable [CRTC:%d:%s], out of memory",
+ crtc->base.base.id, crtc->base.name);
+ return;
+ }
+
+ state->acquire_ctx = ctx;
+
+ /* Everything's already locked, -EDEADLK can't happen. */
+ temp_crtc_state = intel_atomic_get_crtc_state(state, crtc);
+ ret = drm_atomic_add_affected_connectors(state, &crtc->base);
+
+ drm_WARN_ON(&i915->drm, IS_ERR(temp_crtc_state) || ret);
+
+ i915->display.funcs.display->crtc_disable(to_intel_atomic_state(state), crtc);
+
+ drm_atomic_state_put(state);
+
+ drm_dbg_kms(&i915->drm,
+ "[CRTC:%d:%s] hw state adjusted, was enabled, now disabled\n",
+ crtc->base.base.id, crtc->base.name);
+
+ crtc->active = false;
+ crtc->base.enabled = false;
+
+ drm_WARN_ON(&i915->drm,
+ drm_atomic_set_mode_for_crtc(&crtc_state->uapi, NULL) < 0);
+ crtc_state->uapi.active = false;
+ crtc_state->uapi.connector_mask = 0;
+ crtc_state->uapi.encoder_mask = 0;
+ intel_crtc_free_hw_state(crtc_state);
+ memset(&crtc_state->hw, 0, sizeof(crtc_state->hw));
+
+ for_each_encoder_on_crtc(&i915->drm, &crtc->base, encoder)
+ encoder->base.crtc = NULL;
+
+ intel_fbc_disable(crtc);
+ intel_update_watermarks(i915);
+ intel_disable_shared_dpll(crtc_state);
+
+ intel_display_power_put_all_in_set(i915, &crtc->enabled_power_domains);
+
+ cdclk_state->min_cdclk[pipe] = 0;
+ cdclk_state->min_voltage_level[pipe] = 0;
+ cdclk_state->active_pipes &= ~BIT(pipe);
+
+ dbuf_state->active_pipes &= ~BIT(pipe);
+
+ bw_state->data_rate[pipe] = 0;
+ bw_state->num_active_planes[pipe] = 0;
+}
+
+static void intel_modeset_update_connector_atomic_state(struct drm_i915_private *i915)
+{
+ struct intel_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(&i915->drm, &conn_iter);
+ for_each_intel_connector_iter(connector, &conn_iter) {
+ struct drm_connector_state *conn_state = connector->base.state;
+ struct intel_encoder *encoder =
+ to_intel_encoder(connector->base.encoder);
+
+ if (conn_state->crtc)
+ drm_connector_put(&connector->base);
+
+ if (encoder) {
+ struct intel_crtc *crtc =
+ to_intel_crtc(encoder->base.crtc);
+ const struct intel_crtc_state *crtc_state =
+ to_intel_crtc_state(crtc->base.state);
+
+ conn_state->best_encoder = &encoder->base;
+ conn_state->crtc = &crtc->base;
+ conn_state->max_bpc = (crtc_state->pipe_bpp ?: 24) / 3;
+
+ drm_connector_get(&connector->base);
+ } else {
+ conn_state->best_encoder = NULL;
+ conn_state->crtc = NULL;
+ }
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
+static void intel_crtc_copy_hw_to_uapi_state(struct intel_crtc_state *crtc_state)
+{
+ if (intel_crtc_is_bigjoiner_slave(crtc_state))
+ return;
+
+ crtc_state->uapi.enable = crtc_state->hw.enable;
+ crtc_state->uapi.active = crtc_state->hw.active;
+ drm_WARN_ON(crtc_state->uapi.crtc->dev,
+ drm_atomic_set_mode_for_crtc(&crtc_state->uapi, &crtc_state->hw.mode) < 0);
+
+ crtc_state->uapi.adjusted_mode = crtc_state->hw.adjusted_mode;
+ crtc_state->uapi.scaling_filter = crtc_state->hw.scaling_filter;
+
+ /* assume 1:1 mapping */
+ drm_property_replace_blob(&crtc_state->hw.degamma_lut,
+ crtc_state->pre_csc_lut);
+ drm_property_replace_blob(&crtc_state->hw.gamma_lut,
+ crtc_state->post_csc_lut);
+
+ drm_property_replace_blob(&crtc_state->uapi.degamma_lut,
+ crtc_state->hw.degamma_lut);
+ drm_property_replace_blob(&crtc_state->uapi.gamma_lut,
+ crtc_state->hw.gamma_lut);
+ drm_property_replace_blob(&crtc_state->uapi.ctm,
+ crtc_state->hw.ctm);
+}
+
+static void
+intel_sanitize_plane_mapping(struct drm_i915_private *i915)
+{
+ struct intel_crtc *crtc;
+
+ if (DISPLAY_VER(i915) >= 4)
+ return;
+
+ for_each_intel_crtc(&i915->drm, crtc) {
+ struct intel_plane *plane =
+ to_intel_plane(crtc->base.primary);
+ struct intel_crtc *plane_crtc;
+ enum pipe pipe;
+
+ if (!plane->get_hw_state(plane, &pipe))
+ continue;
+
+ if (pipe == crtc->pipe)
+ continue;
+
+ drm_dbg_kms(&i915->drm,
+ "[PLANE:%d:%s] attached to the wrong pipe, disabling plane\n",
+ plane->base.base.id, plane->base.name);
+
+ plane_crtc = intel_crtc_for_pipe(i915, pipe);
+ intel_plane_disable_noatomic(plane_crtc, plane);
+ }
+}
+
+static bool intel_crtc_has_encoders(struct intel_crtc *crtc)
+{
+ struct drm_device *dev = crtc->base.dev;
+ struct intel_encoder *encoder;
+
+ for_each_encoder_on_crtc(dev, &crtc->base, encoder)
+ return true;
+
+ return false;
+}
+
+static struct intel_connector *intel_encoder_find_connector(struct intel_encoder *encoder)
+{
+ struct drm_i915_private *i915 = to_i915(encoder->base.dev);
+ struct drm_connector_list_iter conn_iter;
+ struct intel_connector *connector;
+ struct intel_connector *found_connector = NULL;
+
+ drm_connector_list_iter_begin(&i915->drm, &conn_iter);
+ for_each_intel_connector_iter(connector, &conn_iter) {
+ if (&encoder->base == connector->base.encoder) {
+ found_connector = connector;
+ break;
+ }
+ }
+ drm_connector_list_iter_end(&conn_iter);
+
+ return found_connector;
+}
+
+static void intel_sanitize_fifo_underrun_reporting(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+
+ if (!crtc_state->hw.active && !HAS_GMCH(i915))
+ return;
+
+ /*
+ * We start out with underrun reporting disabled to avoid races.
+ * For correct bookkeeping mark this on active crtcs.
+ *
+ * Also on gmch platforms we dont have any hardware bits to
+ * disable the underrun reporting. Which means we need to start
+ * out with underrun reporting disabled also on inactive pipes,
+ * since otherwise we'll complain about the garbage we read when
+ * e.g. coming up after runtime pm.
+ *
+ * No protection against concurrent access is required - at
+ * worst a fifo underrun happens which also sets this to false.
+ */
+ crtc->cpu_fifo_underrun_disabled = true;
+
+ /*
+ * We track the PCH trancoder underrun reporting state
+ * within the crtc. With crtc for pipe A housing the underrun
+ * reporting state for PCH transcoder A, crtc for pipe B housing
+ * it for PCH transcoder B, etc. LPT-H has only PCH transcoder A,
+ * and marking underrun reporting as disabled for the non-existing
+ * PCH transcoders B and C would prevent enabling the south
+ * error interrupt (see cpt_can_enable_serr_int()).
+ */
+ if (intel_has_pch_trancoder(i915, crtc->pipe))
+ crtc->pch_fifo_underrun_disabled = true;
+}
+
+static void intel_sanitize_crtc(struct intel_crtc *crtc,
+ struct drm_modeset_acquire_ctx *ctx)
+{
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+ struct intel_crtc_state *crtc_state = to_intel_crtc_state(crtc->base.state);
+
+ if (crtc_state->hw.active) {
+ struct intel_plane *plane;
+
+ /* Disable everything but the primary plane */
+ for_each_intel_plane_on_crtc(&i915->drm, crtc, plane) {
+ const struct intel_plane_state *plane_state =
+ to_intel_plane_state(plane->base.state);
+
+ if (plane_state->uapi.visible &&
+ plane->base.type != DRM_PLANE_TYPE_PRIMARY)
+ intel_plane_disable_noatomic(crtc, plane);
+ }
+
+ /* Disable any background color/etc. set by the BIOS */
+ intel_color_commit_noarm(crtc_state);
+ intel_color_commit_arm(crtc_state);
+ }
+
+ /*
+ * Adjust the state of the output pipe according to whether we have
+ * active connectors/encoders.
+ */
+ if (crtc_state->hw.active && !intel_crtc_has_encoders(crtc) &&
+ !intel_crtc_is_bigjoiner_slave(crtc_state))
+ intel_crtc_disable_noatomic(crtc, ctx);
+}
+
+static bool has_bogus_dpll_config(const struct intel_crtc_state *crtc_state)
+{
+ struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
+
+ /*
+ * Some SNB BIOSen (eg. ASUS K53SV) are known to misprogram
+ * the hardware when a high res displays plugged in. DPLL P
+ * divider is zero, and the pipe timings are bonkers. We'll
+ * try to disable everything in that case.
+ *
+ * FIXME would be nice to be able to sanitize this state
+ * without several WARNs, but for now let's take the easy
+ * road.
+ */
+ return IS_SANDYBRIDGE(i915) &&
+ crtc_state->hw.active &&
+ crtc_state->shared_dpll &&
+ crtc_state->port_clock == 0;
+}
+
+static void intel_sanitize_encoder(struct intel_encoder *encoder)
+{
+ struct drm_i915_private *i915 = to_i915(encoder->base.dev);
+ struct intel_connector *connector;
+ struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
+ struct intel_crtc_state *crtc_state = crtc ?
+ to_intel_crtc_state(crtc->base.state) : NULL;
+
+ /*
+ * We need to check both for a crtc link (meaning that the encoder is
+ * active and trying to read from a pipe) and the pipe itself being
+ * active.
+ */
+ bool has_active_crtc = crtc_state &&
+ crtc_state->hw.active;
+
+ if (crtc_state && has_bogus_dpll_config(crtc_state)) {
+ drm_dbg_kms(&i915->drm,
+ "BIOS has misprogrammed the hardware. Disabling pipe %c\n",
+ pipe_name(crtc->pipe));
+ has_active_crtc = false;
+ }
+
+ connector = intel_encoder_find_connector(encoder);
+ if (connector && !has_active_crtc) {
+ drm_dbg_kms(&i915->drm,
+ "[ENCODER:%d:%s] has active connectors but no active pipe!\n",
+ encoder->base.base.id,
+ encoder->base.name);
+
+ /*
+ * Connector is active, but has no active pipe. This is fallout
+ * from our resume register restoring. Disable the encoder
+ * manually again.
+ */
+ if (crtc_state) {
+ struct drm_encoder *best_encoder;
+
+ drm_dbg_kms(&i915->drm,
+ "[ENCODER:%d:%s] manually disabled\n",
+ encoder->base.base.id,
+ encoder->base.name);
+
+ /* avoid oopsing in case the hooks consult best_encoder */
+ best_encoder = connector->base.state->best_encoder;
+ connector->base.state->best_encoder = &encoder->base;
+
+ /* FIXME NULL atomic state passed! */
+ if (encoder->disable)
+ encoder->disable(NULL, encoder, crtc_state,
+ connector->base.state);
+ if (encoder->post_disable)
+ encoder->post_disable(NULL, encoder, crtc_state,
+ connector->base.state);
+
+ connector->base.state->best_encoder = best_encoder;
+ }
+ encoder->base.crtc = NULL;
+
+ /*
+ * Inconsistent output/port/pipe state happens presumably due to
+ * a bug in one of the get_hw_state functions. Or someplace else
+ * in our code, like the register restore mess on resume. Clamp
+ * things to off as a safer default.
+ */
+ connector->base.dpms = DRM_MODE_DPMS_OFF;
+ connector->base.encoder = NULL;
+ }
+
+ /* notify opregion of the sanitized encoder state */
+ intel_opregion_notify_encoder(encoder, connector && has_active_crtc);
+
+ if (HAS_DDI(i915))
+ intel_ddi_sanitize_encoder_pll_mapping(encoder);
+}
+
+/* FIXME read out full plane state for all planes */
+static void readout_plane_state(struct drm_i915_private *i915)
+{
+ struct intel_plane *plane;
+ struct intel_crtc *crtc;
+
+ for_each_intel_plane(&i915->drm, plane) {
+ struct intel_plane_state *plane_state =
+ to_intel_plane_state(plane->base.state);
+ struct intel_crtc_state *crtc_state;
+ enum pipe pipe = PIPE_A;
+ bool visible;
+
+ visible = plane->get_hw_state(plane, &pipe);
+
+ crtc = intel_crtc_for_pipe(i915, pipe);
+ crtc_state = to_intel_crtc_state(crtc->base.state);
+
+ intel_set_plane_visible(crtc_state, plane_state, visible);
+
+ drm_dbg_kms(&i915->drm,
+ "[PLANE:%d:%s] hw state readout: %s, pipe %c\n",
+ plane->base.base.id, plane->base.name,
+ str_enabled_disabled(visible), pipe_name(pipe));
+ }
+
+ for_each_intel_crtc(&i915->drm, crtc) {
+ struct intel_crtc_state *crtc_state =
+ to_intel_crtc_state(crtc->base.state);
+
+ intel_plane_fixup_bitmasks(crtc_state);
+ }
+}
+
+static void intel_modeset_readout_hw_state(struct drm_i915_private *i915)
+{
+ struct intel_cdclk_state *cdclk_state =
+ to_intel_cdclk_state(i915->display.cdclk.obj.state);
+ struct intel_dbuf_state *dbuf_state =
+ to_intel_dbuf_state(i915->display.dbuf.obj.state);
+ enum pipe pipe;
+ struct intel_crtc *crtc;
+ struct intel_encoder *encoder;
+ struct intel_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+ u8 active_pipes = 0;
+
+ for_each_intel_crtc(&i915->drm, crtc) {
+ struct intel_crtc_state *crtc_state =
+ to_intel_crtc_state(crtc->base.state);
+
+ __drm_atomic_helper_crtc_destroy_state(&crtc_state->uapi);
+ intel_crtc_free_hw_state(crtc_state);
+ intel_crtc_state_reset(crtc_state, crtc);
+
+ intel_crtc_get_pipe_config(crtc_state);
+
+ crtc_state->hw.enable = crtc_state->hw.active;
+
+ crtc->base.enabled = crtc_state->hw.enable;
+ crtc->active = crtc_state->hw.active;
+
+ if (crtc_state->hw.active)
+ active_pipes |= BIT(crtc->pipe);
+
+ drm_dbg_kms(&i915->drm,
+ "[CRTC:%d:%s] hw state readout: %s\n",
+ crtc->base.base.id, crtc->base.name,
+ str_enabled_disabled(crtc_state->hw.active));
+ }
+
+ cdclk_state->active_pipes = active_pipes;
+ dbuf_state->active_pipes = active_pipes;
+
+ readout_plane_state(i915);
+
+ for_each_intel_encoder(&i915->drm, encoder) {
+ struct intel_crtc_state *crtc_state = NULL;
+
+ pipe = 0;
+
+ if (encoder->get_hw_state(encoder, &pipe)) {
+ crtc = intel_crtc_for_pipe(i915, pipe);
+ crtc_state = to_intel_crtc_state(crtc->base.state);
+
+ encoder->base.crtc = &crtc->base;
+ intel_encoder_get_config(encoder, crtc_state);
+
+ /* read out to slave crtc as well for bigjoiner */
+ if (crtc_state->bigjoiner_pipes) {
+ struct intel_crtc *slave_crtc;
+
+ /* encoder should read be linked to bigjoiner master */
+ WARN_ON(intel_crtc_is_bigjoiner_slave(crtc_state));
+
+ for_each_intel_crtc_in_pipe_mask(&i915->drm, slave_crtc,
+ intel_crtc_bigjoiner_slave_pipes(crtc_state)) {
+ struct intel_crtc_state *slave_crtc_state;
+
+ slave_crtc_state = to_intel_crtc_state(slave_crtc->base.state);
+ intel_encoder_get_config(encoder, slave_crtc_state);
+ }
+ }
+ } else {
+ encoder->base.crtc = NULL;
+ }
+
+ if (encoder->sync_state)
+ encoder->sync_state(encoder, crtc_state);
+
+ drm_dbg_kms(&i915->drm,
+ "[ENCODER:%d:%s] hw state readout: %s, pipe %c\n",
+ encoder->base.base.id, encoder->base.name,
+ str_enabled_disabled(encoder->base.crtc),
+ pipe_name(pipe));
+ }
+
+ intel_dpll_readout_hw_state(i915);
+
+ drm_connector_list_iter_begin(&i915->drm, &conn_iter);
+ for_each_intel_connector_iter(connector, &conn_iter) {
+ if (connector->get_hw_state(connector)) {
+ struct intel_crtc_state *crtc_state;
+ struct intel_crtc *crtc;
+
+ connector->base.dpms = DRM_MODE_DPMS_ON;
+
+ encoder = intel_attached_encoder(connector);
+ connector->base.encoder = &encoder->base;
+
+ crtc = to_intel_crtc(encoder->base.crtc);
+ crtc_state = crtc ? to_intel_crtc_state(crtc->base.state) : NULL;
+
+ if (crtc_state && crtc_state->hw.active) {
+ /*
+ * This has to be done during hardware readout
+ * because anything calling .crtc_disable may
+ * rely on the connector_mask being accurate.
+ */
+ crtc_state->uapi.connector_mask |=
+ drm_connector_mask(&connector->base);
+ crtc_state->uapi.encoder_mask |=
+ drm_encoder_mask(&encoder->base);
+ }
+ } else {
+ connector->base.dpms = DRM_MODE_DPMS_OFF;
+ connector->base.encoder = NULL;
+ }
+ drm_dbg_kms(&i915->drm,
+ "[CONNECTOR:%d:%s] hw state readout: %s\n",
+ connector->base.base.id, connector->base.name,
+ str_enabled_disabled(connector->base.encoder));
+ }
+ drm_connector_list_iter_end(&conn_iter);
+
+ for_each_intel_crtc(&i915->drm, crtc) {
+ struct intel_bw_state *bw_state =
+ to_intel_bw_state(i915->display.bw.obj.state);
+ struct intel_crtc_state *crtc_state =
+ to_intel_crtc_state(crtc->base.state);
+ struct intel_plane *plane;
+ int min_cdclk = 0;
+
+ if (crtc_state->hw.active) {
+ /*
+ * The initial mode needs to be set in order to keep
+ * the atomic core happy. It wants a valid mode if the
+ * crtc's enabled, so we do the above call.
+ *
+ * But we don't set all the derived state fully, hence
+ * set a flag to indicate that a full recalculation is
+ * needed on the next commit.
+ */
+ crtc_state->inherited = true;
+
+ intel_crtc_update_active_timings(crtc_state);
+
+ intel_crtc_copy_hw_to_uapi_state(crtc_state);
+ }
+
+ for_each_intel_plane_on_crtc(&i915->drm, crtc, plane) {
+ const struct intel_plane_state *plane_state =
+ to_intel_plane_state(plane->base.state);
+
+ /*
+ * FIXME don't have the fb yet, so can't
+ * use intel_plane_data_rate() :(
+ */
+ if (plane_state->uapi.visible)
+ crtc_state->data_rate[plane->id] =
+ 4 * crtc_state->pixel_rate;
+ /*
+ * FIXME don't have the fb yet, so can't
+ * use plane->min_cdclk() :(
+ */
+ if (plane_state->uapi.visible && plane->min_cdclk) {
+ if (crtc_state->double_wide || DISPLAY_VER(i915) >= 10)
+ crtc_state->min_cdclk[plane->id] =
+ DIV_ROUND_UP(crtc_state->pixel_rate, 2);
+ else
+ crtc_state->min_cdclk[plane->id] =
+ crtc_state->pixel_rate;
+ }
+ drm_dbg_kms(&i915->drm,
+ "[PLANE:%d:%s] min_cdclk %d kHz\n",
+ plane->base.base.id, plane->base.name,
+ crtc_state->min_cdclk[plane->id]);
+ }
+
+ if (crtc_state->hw.active) {
+ min_cdclk = intel_crtc_compute_min_cdclk(crtc_state);
+ if (drm_WARN_ON(&i915->drm, min_cdclk < 0))
+ min_cdclk = 0;
+ }
+
+ cdclk_state->min_cdclk[crtc->pipe] = min_cdclk;
+ cdclk_state->min_voltage_level[crtc->pipe] =
+ crtc_state->min_voltage_level;
+
+ intel_bw_crtc_update(bw_state, crtc_state);
+ }
+}
+
+static void
+get_encoder_power_domains(struct drm_i915_private *i915)
+{
+ struct intel_encoder *encoder;
+
+ for_each_intel_encoder(&i915->drm, encoder) {
+ struct intel_crtc_state *crtc_state;
+
+ if (!encoder->get_power_domains)
+ continue;
+
+ /*
+ * MST-primary and inactive encoders don't have a crtc state
+ * and neither of these require any power domain references.
+ */
+ if (!encoder->base.crtc)
+ continue;
+
+ crtc_state = to_intel_crtc_state(encoder->base.crtc->state);
+ encoder->get_power_domains(encoder, crtc_state);
+ }
+}
+
+static void intel_early_display_was(struct drm_i915_private *i915)
+{
+ /*
+ * Display WA #1185 WaDisableDARBFClkGating:glk,icl,ehl,tgl
+ * Also known as Wa_14010480278.
+ */
+ if (IS_DISPLAY_VER(i915, 10, 12))
+ intel_de_write(i915, GEN9_CLKGATE_DIS_0,
+ intel_de_read(i915, GEN9_CLKGATE_DIS_0) | DARBF_GATING_DIS);
+
+ if (IS_HASWELL(i915)) {
+ /*
+ * WaRsPkgCStateDisplayPMReq:hsw
+ * System hang if this isn't done before disabling all planes!
+ */
+ intel_de_write(i915, CHICKEN_PAR1_1,
+ intel_de_read(i915, CHICKEN_PAR1_1) | FORCE_ARB_IDLE_PLANES);
+ }
+
+ if (IS_KABYLAKE(i915) || IS_COFFEELAKE(i915) || IS_COMETLAKE(i915)) {
+ /* Display WA #1142:kbl,cfl,cml */
+ intel_de_rmw(i915, CHICKEN_PAR1_1,
+ KBL_ARB_FILL_SPARE_22, KBL_ARB_FILL_SPARE_22);
+ intel_de_rmw(i915, CHICKEN_MISC_2,
+ KBL_ARB_FILL_SPARE_13 | KBL_ARB_FILL_SPARE_14,
+ KBL_ARB_FILL_SPARE_14);
+ }
+}
+
+void intel_modeset_setup_hw_state(struct drm_i915_private *i915,
+ struct drm_modeset_acquire_ctx *ctx)
+{
+ struct intel_encoder *encoder;
+ struct intel_crtc *crtc;
+ intel_wakeref_t wakeref;
+
+ wakeref = intel_display_power_get(i915, POWER_DOMAIN_INIT);
+
+ intel_early_display_was(i915);
+ intel_modeset_readout_hw_state(i915);
+
+ /* HW state is read out, now we need to sanitize this mess. */
+ get_encoder_power_domains(i915);
+
+ intel_pch_sanitize(i915);
+
+ /*
+ * intel_sanitize_plane_mapping() may need to do vblank
+ * waits, so we need vblank interrupts restored beforehand.
+ */
+ for_each_intel_crtc(&i915->drm, crtc) {
+ struct intel_crtc_state *crtc_state =
+ to_intel_crtc_state(crtc->base.state);
+
+ intel_sanitize_fifo_underrun_reporting(crtc_state);
+
+ drm_crtc_vblank_reset(&crtc->base);
+
+ if (crtc_state->hw.active)
+ intel_crtc_vblank_on(crtc_state);
+ }
+
+ intel_fbc_sanitize(i915);
+
+ intel_sanitize_plane_mapping(i915);
+
+ for_each_intel_encoder(&i915->drm, encoder)
+ intel_sanitize_encoder(encoder);
+
+ for_each_intel_crtc(&i915->drm, crtc) {
+ struct intel_crtc_state *crtc_state =
+ to_intel_crtc_state(crtc->base.state);
+
+ intel_sanitize_crtc(crtc, ctx);
+ intel_crtc_state_dump(crtc_state, NULL, "setup_hw_state");
+ }
+
+ intel_modeset_update_connector_atomic_state(i915);
+
+ intel_dpll_sanitize_state(i915);
+
+ if (IS_G4X(i915)) {
+ g4x_wm_get_hw_state(i915);
+ g4x_wm_sanitize(i915);
+ } else if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915)) {
+ vlv_wm_get_hw_state(i915);
+ vlv_wm_sanitize(i915);
+ } else if (DISPLAY_VER(i915) >= 9) {
+ skl_wm_get_hw_state(i915);
+ skl_wm_sanitize(i915);
+ } else if (HAS_PCH_SPLIT(i915)) {
+ ilk_wm_get_hw_state(i915);
+ }
+
+ for_each_intel_crtc(&i915->drm, crtc) {
+ struct intel_crtc_state *crtc_state =
+ to_intel_crtc_state(crtc->base.state);
+ struct intel_power_domain_mask put_domains;
+
+ intel_modeset_get_crtc_power_domains(crtc_state, &put_domains);
+ if (drm_WARN_ON(&i915->drm, !bitmap_empty(put_domains.bits, POWER_DOMAIN_NUM)))
+ intel_modeset_put_crtc_power_domains(crtc, &put_domains);
+ }
+
+ intel_display_power_put(i915, POWER_DOMAIN_INIT, wakeref);
+
+ intel_power_domains_sanitize_state(i915);
+}