1// SPDX-License-Identifier: MIT
2/*
3 * Copyright © 2023 Intel Corporation
4 */
5
6#include <linux/pci.h>
7#include <linux/pnp.h>
8
9#include <drm/drm_managed.h>
10#include <drm/i915_drm.h>
11
12#include "i915_drv.h"
13#include "intel_gmch.h"
14#include "intel_pci_config.h"
15
16static void intel_gmch_bridge_release(struct drm_device *dev, void *bridge)
17{
18	pci_dev_put(bridge);
19}
20
21int intel_gmch_bridge_setup(struct drm_i915_private *i915)
22{
23	int domain = pci_domain_nr(to_pci_dev(i915->drm.dev)->bus);
24
25	i915->gmch.pdev = pci_get_domain_bus_and_slot(domain, 0, PCI_DEVFN(0, 0));
26	if (!i915->gmch.pdev) {
27		drm_err(&i915->drm, "bridge device not found\n");
28		return -EIO;
29	}
30
31	return drmm_add_action_or_reset(&i915->drm, intel_gmch_bridge_release,
32					i915->gmch.pdev);
33}
34
35/* Allocate space for the MCH regs if needed, return nonzero on error */
36static int
37intel_alloc_mchbar_resource(struct drm_i915_private *i915)
38{
39	int reg = GRAPHICS_VER(i915) >= 4 ? MCHBAR_I965 : MCHBAR_I915;
40	u32 temp_lo, temp_hi = 0;
41	u64 mchbar_addr;
42	int ret;
43
44	if (GRAPHICS_VER(i915) >= 4)
45		pci_read_config_dword(i915->gmch.pdev, reg + 4, &temp_hi);
46	pci_read_config_dword(i915->gmch.pdev, reg, &temp_lo);
47	mchbar_addr = ((u64)temp_hi << 32) | temp_lo;
48
49	/* If ACPI doesn't have it, assume we need to allocate it ourselves */
50	if (IS_ENABLED(CONFIG_PNP) && mchbar_addr &&
51	    pnp_range_reserved(mchbar_addr, mchbar_addr + MCHBAR_SIZE))
52		return 0;
53
54	/* Get some space for it */
55	i915->gmch.mch_res.name = "i915 MCHBAR";
56	i915->gmch.mch_res.flags = IORESOURCE_MEM;
57	ret = pci_bus_alloc_resource(i915->gmch.pdev->bus,
58				     &i915->gmch.mch_res,
59				     MCHBAR_SIZE, MCHBAR_SIZE,
60				     PCIBIOS_MIN_MEM,
61				     0, pcibios_align_resource,
62				     i915->gmch.pdev);
63	if (ret) {
64		drm_dbg(&i915->drm, "failed bus alloc: %d\n", ret);
65		i915->gmch.mch_res.start = 0;
66		return ret;
67	}
68
69	if (GRAPHICS_VER(i915) >= 4)
70		pci_write_config_dword(i915->gmch.pdev, reg + 4,
71				       upper_32_bits(i915->gmch.mch_res.start));
72
73	pci_write_config_dword(i915->gmch.pdev, reg,
74			       lower_32_bits(i915->gmch.mch_res.start));
75	return 0;
76}
77
78/* Setup MCHBAR if possible, return true if we should disable it again */
79void intel_gmch_bar_setup(struct drm_i915_private *i915)
80{
81	int mchbar_reg = GRAPHICS_VER(i915) >= 4 ? MCHBAR_I965 : MCHBAR_I915;
82	u32 temp;
83	bool enabled;
84
85	if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915))
86		return;
87
88	i915->gmch.mchbar_need_disable = false;
89
90	if (IS_I915G(i915) || IS_I915GM(i915)) {
91		pci_read_config_dword(i915->gmch.pdev, DEVEN, &temp);
92		enabled = !!(temp & DEVEN_MCHBAR_EN);
93	} else {
94		pci_read_config_dword(i915->gmch.pdev, mchbar_reg, &temp);
95		enabled = temp & 1;
96	}
97
98	/* If it's already enabled, don't have to do anything */
99	if (enabled)
100		return;
101
102	if (intel_alloc_mchbar_resource(i915))
103		return;
104
105	i915->gmch.mchbar_need_disable = true;
106
107	/* Space is allocated or reserved, so enable it. */
108	if (IS_I915G(i915) || IS_I915GM(i915)) {
109		pci_write_config_dword(i915->gmch.pdev, DEVEN,
110				       temp | DEVEN_MCHBAR_EN);
111	} else {
112		pci_read_config_dword(i915->gmch.pdev, mchbar_reg, &temp);
113		pci_write_config_dword(i915->gmch.pdev, mchbar_reg, temp | 1);
114	}
115}
116
117void intel_gmch_bar_teardown(struct drm_i915_private *i915)
118{
119	int mchbar_reg = GRAPHICS_VER(i915) >= 4 ? MCHBAR_I965 : MCHBAR_I915;
120
121	if (i915->gmch.mchbar_need_disable) {
122		if (IS_I915G(i915) || IS_I915GM(i915)) {
123			u32 deven_val;
124
125			pci_read_config_dword(i915->gmch.pdev, DEVEN,
126					      &deven_val);
127			deven_val &= ~DEVEN_MCHBAR_EN;
128			pci_write_config_dword(i915->gmch.pdev, DEVEN,
129					       deven_val);
130		} else {
131			u32 mchbar_val;
132
133			pci_read_config_dword(i915->gmch.pdev, mchbar_reg,
134					      &mchbar_val);
135			mchbar_val &= ~1;
136			pci_write_config_dword(i915->gmch.pdev, mchbar_reg,
137					       mchbar_val);
138		}
139	}
140
141	if (i915->gmch.mch_res.start)
142		release_resource(&i915->gmch.mch_res);
143}
144
145int intel_gmch_vga_set_state(struct drm_i915_private *i915, bool enable_decode)
146{
147	unsigned int reg = DISPLAY_VER(i915) >= 6 ? SNB_GMCH_CTRL : INTEL_GMCH_CTRL;
148	u16 gmch_ctrl;
149
150	if (pci_read_config_word(i915->gmch.pdev, reg, &gmch_ctrl)) {
151		drm_err(&i915->drm, "failed to read control word\n");
152		return -EIO;
153	}
154
155	if (!!(gmch_ctrl & INTEL_GMCH_VGA_DISABLE) == !enable_decode)
156		return 0;
157
158	if (enable_decode)
159		gmch_ctrl &= ~INTEL_GMCH_VGA_DISABLE;
160	else
161		gmch_ctrl |= INTEL_GMCH_VGA_DISABLE;
162
163	if (pci_write_config_word(i915->gmch.pdev, reg, gmch_ctrl)) {
164		drm_err(&i915->drm, "failed to write control word\n");
165		return -EIO;
166	}
167
168	return 0;
169}
170