xref: /dragonfly/sys/dev/drm/i915/i915_gem_stolen.c (revision 3f2dd94a)
1a2fdbec6SFrançois Tigeot /*
2a2fdbec6SFrançois Tigeot  * Copyright © 2008-2012 Intel Corporation
3a2fdbec6SFrançois Tigeot  *
4a2fdbec6SFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
5a2fdbec6SFrançois Tigeot  * copy of this software and associated documentation files (the "Software"),
6a2fdbec6SFrançois Tigeot  * to deal in the Software without restriction, including without limitation
7a2fdbec6SFrançois Tigeot  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8a2fdbec6SFrançois Tigeot  * and/or sell copies of the Software, and to permit persons to whom the
9a2fdbec6SFrançois Tigeot  * Software is furnished to do so, subject to the following conditions:
10a2fdbec6SFrançois Tigeot  *
11a2fdbec6SFrançois Tigeot  * The above copyright notice and this permission notice (including the next
12a2fdbec6SFrançois Tigeot  * paragraph) shall be included in all copies or substantial portions of the
13a2fdbec6SFrançois Tigeot  * Software.
14a2fdbec6SFrançois Tigeot  *
15a2fdbec6SFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16a2fdbec6SFrançois Tigeot  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17a2fdbec6SFrançois Tigeot  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18a2fdbec6SFrançois Tigeot  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19a2fdbec6SFrançois Tigeot  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20a2fdbec6SFrançois Tigeot  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21a2fdbec6SFrançois Tigeot  * IN THE SOFTWARE.
22a2fdbec6SFrançois Tigeot  *
23a2fdbec6SFrançois Tigeot  * Authors:
24a2fdbec6SFrançois Tigeot  *    Eric Anholt <eric@anholt.net>
25a2fdbec6SFrançois Tigeot  *    Chris Wilson <chris@chris-wilson.co.uk>
26a2fdbec6SFrançois Tigeot  *
27a2fdbec6SFrançois Tigeot  */
28a2fdbec6SFrançois Tigeot 
29a2fdbec6SFrançois Tigeot #include <drm/drmP.h>
30a2fdbec6SFrançois Tigeot #include <drm/i915_drm.h>
31a2fdbec6SFrançois Tigeot #include "i915_drv.h"
32a2fdbec6SFrançois Tigeot 
33352ff8bdSFrançois Tigeot #define KB(x) ((x) * 1024)
34352ff8bdSFrançois Tigeot #define MB(x) (KB(x) * 1024)
35352ff8bdSFrançois Tigeot 
36a2fdbec6SFrançois Tigeot /*
37a2fdbec6SFrançois Tigeot  * The BIOS typically reserves some of the system's memory for the exclusive
38a2fdbec6SFrançois Tigeot  * use of the integrated graphics. This memory is no longer available for
39a2fdbec6SFrançois Tigeot  * use by the OS and so the user finds that his system has less memory
40a2fdbec6SFrançois Tigeot  * available than he put in. We refer to this memory as stolen.
41a2fdbec6SFrançois Tigeot  *
42a2fdbec6SFrançois Tigeot  * The BIOS will allocate its framebuffer from the stolen memory. Our
43a2fdbec6SFrançois Tigeot  * goal is try to reuse that object for our own fbcon which must always
44a2fdbec6SFrançois Tigeot  * be available for panics. Anything else we can reuse the stolen memory
45a2fdbec6SFrançois Tigeot  * for is a boon.
46a2fdbec6SFrançois Tigeot  */
47a2fdbec6SFrançois Tigeot 
i915_gem_stolen_insert_node_in_range(struct drm_i915_private * dev_priv,struct drm_mm_node * node,u64 size,unsigned alignment,u64 start,u64 end)48352ff8bdSFrançois Tigeot int i915_gem_stolen_insert_node_in_range(struct drm_i915_private *dev_priv,
49a05eeebfSFrançois Tigeot 					 struct drm_mm_node *node, u64 size,
50352ff8bdSFrançois Tigeot 					 unsigned alignment, u64 start, u64 end)
51a05eeebfSFrançois Tigeot {
52a05eeebfSFrançois Tigeot 	int ret;
53a05eeebfSFrançois Tigeot 
54a05eeebfSFrançois Tigeot 	if (!drm_mm_initialized(&dev_priv->mm.stolen))
55a05eeebfSFrançois Tigeot 		return -ENODEV;
56a05eeebfSFrançois Tigeot 
57a05eeebfSFrançois Tigeot 	mutex_lock(&dev_priv->mm.stolen_lock);
58a85cb24fSFrançois Tigeot 	ret = drm_mm_insert_node_in_range(&dev_priv->mm.stolen, node,
59a85cb24fSFrançois Tigeot 					  size, alignment, 0,
60a85cb24fSFrançois Tigeot 					  start, end, DRM_MM_INSERT_BEST);
61a05eeebfSFrançois Tigeot 	mutex_unlock(&dev_priv->mm.stolen_lock);
62a05eeebfSFrançois Tigeot 
63a05eeebfSFrançois Tigeot 	return ret;
64a05eeebfSFrançois Tigeot }
65a05eeebfSFrançois Tigeot 
i915_gem_stolen_insert_node(struct drm_i915_private * dev_priv,struct drm_mm_node * node,u64 size,unsigned alignment)66352ff8bdSFrançois Tigeot int i915_gem_stolen_insert_node(struct drm_i915_private *dev_priv,
67352ff8bdSFrançois Tigeot 				struct drm_mm_node *node, u64 size,
68352ff8bdSFrançois Tigeot 				unsigned alignment)
69352ff8bdSFrançois Tigeot {
70352ff8bdSFrançois Tigeot 	return i915_gem_stolen_insert_node_in_range(dev_priv, node, size,
71a85cb24fSFrançois Tigeot 						    alignment, 0, U64_MAX);
72352ff8bdSFrançois Tigeot }
73352ff8bdSFrançois Tigeot 
i915_gem_stolen_remove_node(struct drm_i915_private * dev_priv,struct drm_mm_node * node)74a05eeebfSFrançois Tigeot void i915_gem_stolen_remove_node(struct drm_i915_private *dev_priv,
75a05eeebfSFrançois Tigeot 				 struct drm_mm_node *node)
76a05eeebfSFrançois Tigeot {
77a05eeebfSFrançois Tigeot 	mutex_lock(&dev_priv->mm.stolen_lock);
78a05eeebfSFrançois Tigeot 	drm_mm_remove_node(node);
79a05eeebfSFrançois Tigeot 	mutex_unlock(&dev_priv->mm.stolen_lock);
80a05eeebfSFrançois Tigeot }
81a05eeebfSFrançois Tigeot 
825a822b41SFrançois Tigeot #ifdef __DragonFly__
835a822b41SFrançois Tigeot static
devm_request_mem_region(struct device * dev,resource_size_t start,resource_size_t n,const char * name)845a822b41SFrançois Tigeot struct resource * devm_request_mem_region(struct device *dev,
855a822b41SFrançois Tigeot     resource_size_t start, resource_size_t n, const char *name)
865a822b41SFrançois Tigeot {
875a822b41SFrançois Tigeot 	static struct rman stolen_rman;
885a822b41SFrançois Tigeot 	struct resource *res;
895a822b41SFrançois Tigeot 
905a822b41SFrançois Tigeot 	stolen_rman.rm_start = start;
915a822b41SFrançois Tigeot 	stolen_rman.rm_end = start + n;
925a822b41SFrançois Tigeot 	stolen_rman.rm_type = RMAN_ARRAY;
935a822b41SFrançois Tigeot 	stolen_rman.rm_descr = name;
945a822b41SFrançois Tigeot 	if (rman_init(&stolen_rman, -1))
955a822b41SFrançois Tigeot 		return NULL;
965a822b41SFrançois Tigeot 
975a822b41SFrançois Tigeot 	if (rman_manage_region(&stolen_rman, stolen_rman.rm_start, stolen_rman.rm_end))
985a822b41SFrançois Tigeot 		return NULL;
995a822b41SFrançois Tigeot 
1005a822b41SFrançois Tigeot 	res = kmalloc(sizeof(*res), M_DRM, GFP_KERNEL);
1015a822b41SFrançois Tigeot 	return res;
1025a822b41SFrançois Tigeot }
1035a822b41SFrançois Tigeot #endif	/* __DragonFly__ */
1045a822b41SFrançois Tigeot 
i915_stolen_to_dma(struct drm_i915_private * dev_priv)105a85cb24fSFrançois Tigeot static dma_addr_t i915_stolen_to_dma(struct drm_i915_private *dev_priv)
106a2fdbec6SFrançois Tigeot {
1071e12ee3bSFrançois Tigeot 	struct pci_dev *pdev = dev_priv->drm.pdev;
1088621f407SFrançois Tigeot 	struct i915_ggtt *ggtt = &dev_priv->ggtt;
1095a822b41SFrançois Tigeot 	struct resource *r;
110a85cb24fSFrançois Tigeot 	dma_addr_t base;
111a2fdbec6SFrançois Tigeot 
1129edbd4a0SFrançois Tigeot 	/* Almost universally we can find the Graphics Base of Stolen Memory
1138621f407SFrançois Tigeot 	 * at register BSM (0x5c) in the igfx configuration space. On a few
1148621f407SFrançois Tigeot 	 * (desktop) machines this is also mirrored in the bridge device at
1158621f407SFrançois Tigeot 	 * different locations, or in the MCHBAR.
116a2fdbec6SFrançois Tigeot 	 *
117352ff8bdSFrançois Tigeot 	 * On 865 we just check the TOUD register.
118352ff8bdSFrançois Tigeot 	 *
119352ff8bdSFrançois Tigeot 	 * On 830/845/85x the stolen memory base isn't available in any
120352ff8bdSFrançois Tigeot 	 * register. We need to calculate it as TOM-TSEG_SIZE-stolen_size.
121352ff8bdSFrançois Tigeot 	 *
122a2fdbec6SFrançois Tigeot 	 */
123a2fdbec6SFrançois Tigeot 	base = 0;
1244be47400SFrançois Tigeot 	if (INTEL_GEN(dev_priv) >= 3) {
1258621f407SFrançois Tigeot 		u32 bsm;
1268621f407SFrançois Tigeot 
1271e12ee3bSFrançois Tigeot 		pci_read_config_dword(pdev, INTEL_BSM, &bsm);
1288621f407SFrançois Tigeot 
1291487f786SFrançois Tigeot 		base = bsm & INTEL_BSM_MASK;
1301e12ee3bSFrançois Tigeot 	} else if (IS_I865G(dev_priv)) {
131bf017597SFrançois Tigeot 		u32 tseg_size = 0;
132352ff8bdSFrançois Tigeot 		u16 toud = 0;
133bf017597SFrançois Tigeot 		u8 tmp;
134352ff8bdSFrançois Tigeot 
1351e12ee3bSFrançois Tigeot 		pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0),
136bf017597SFrançois Tigeot 					 I845_ESMRAMC, &tmp);
137bf017597SFrançois Tigeot 
138bf017597SFrançois Tigeot 		if (tmp & TSEG_ENABLE) {
139bf017597SFrançois Tigeot 			switch (tmp & I845_TSEG_SIZE_MASK) {
140bf017597SFrançois Tigeot 			case I845_TSEG_SIZE_512K:
141bf017597SFrançois Tigeot 				tseg_size = KB(512);
142bf017597SFrançois Tigeot 				break;
143bf017597SFrançois Tigeot 			case I845_TSEG_SIZE_1M:
144bf017597SFrançois Tigeot 				tseg_size = MB(1);
145bf017597SFrançois Tigeot 				break;
146bf017597SFrançois Tigeot 			}
147bf017597SFrançois Tigeot 		}
148bf017597SFrançois Tigeot 
1491e12ee3bSFrançois Tigeot 		pci_bus_read_config_word(pdev->bus, PCI_DEVFN(0, 0),
150352ff8bdSFrançois Tigeot 					 I865_TOUD, &toud);
151352ff8bdSFrançois Tigeot 
152bf017597SFrançois Tigeot 		base = (toud << 16) + tseg_size;
1534be47400SFrançois Tigeot 	} else if (IS_I85X(dev_priv)) {
154352ff8bdSFrançois Tigeot 		u32 tseg_size = 0;
155352ff8bdSFrançois Tigeot 		u32 tom;
156352ff8bdSFrançois Tigeot 		u8 tmp;
157352ff8bdSFrançois Tigeot 
1581e12ee3bSFrançois Tigeot 		pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0),
159352ff8bdSFrançois Tigeot 					 I85X_ESMRAMC, &tmp);
160352ff8bdSFrançois Tigeot 
161352ff8bdSFrançois Tigeot 		if (tmp & TSEG_ENABLE)
162352ff8bdSFrançois Tigeot 			tseg_size = MB(1);
163352ff8bdSFrançois Tigeot 
1641e12ee3bSFrançois Tigeot 		pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 1),
165352ff8bdSFrançois Tigeot 					 I85X_DRB3, &tmp);
166352ff8bdSFrançois Tigeot 		tom = tmp * MB(32);
167352ff8bdSFrançois Tigeot 
1688621f407SFrançois Tigeot 		base = tom - tseg_size - ggtt->stolen_size;
169a85cb24fSFrançois Tigeot 	} else if (IS_I845G(dev_priv)) {
170352ff8bdSFrançois Tigeot 		u32 tseg_size = 0;
171352ff8bdSFrançois Tigeot 		u32 tom;
172352ff8bdSFrançois Tigeot 		u8 tmp;
173352ff8bdSFrançois Tigeot 
1741e12ee3bSFrançois Tigeot 		pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0),
175352ff8bdSFrançois Tigeot 					 I845_ESMRAMC, &tmp);
176352ff8bdSFrançois Tigeot 
177352ff8bdSFrançois Tigeot 		if (tmp & TSEG_ENABLE) {
178352ff8bdSFrançois Tigeot 			switch (tmp & I845_TSEG_SIZE_MASK) {
179352ff8bdSFrançois Tigeot 			case I845_TSEG_SIZE_512K:
180352ff8bdSFrançois Tigeot 				tseg_size = KB(512);
181352ff8bdSFrançois Tigeot 				break;
182352ff8bdSFrançois Tigeot 			case I845_TSEG_SIZE_1M:
183352ff8bdSFrançois Tigeot 				tseg_size = MB(1);
184352ff8bdSFrançois Tigeot 				break;
185352ff8bdSFrançois Tigeot 			}
186352ff8bdSFrançois Tigeot 		}
187352ff8bdSFrançois Tigeot 
1881e12ee3bSFrançois Tigeot 		pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0),
189352ff8bdSFrançois Tigeot 					 I830_DRB3, &tmp);
190352ff8bdSFrançois Tigeot 		tom = tmp * MB(32);
191352ff8bdSFrançois Tigeot 
1928621f407SFrançois Tigeot 		base = tom - tseg_size - ggtt->stolen_size;
1931e12ee3bSFrançois Tigeot 	} else if (IS_I830(dev_priv)) {
194352ff8bdSFrançois Tigeot 		u32 tseg_size = 0;
195352ff8bdSFrançois Tigeot 		u32 tom;
196352ff8bdSFrançois Tigeot 		u8 tmp;
197352ff8bdSFrançois Tigeot 
1981e12ee3bSFrançois Tigeot 		pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0),
199352ff8bdSFrançois Tigeot 					 I830_ESMRAMC, &tmp);
200352ff8bdSFrançois Tigeot 
201352ff8bdSFrançois Tigeot 		if (tmp & TSEG_ENABLE) {
202352ff8bdSFrançois Tigeot 			if (tmp & I830_TSEG_SIZE_1M)
203352ff8bdSFrançois Tigeot 				tseg_size = MB(1);
204352ff8bdSFrançois Tigeot 			else
205352ff8bdSFrançois Tigeot 				tseg_size = KB(512);
206352ff8bdSFrançois Tigeot 		}
207352ff8bdSFrançois Tigeot 
2081e12ee3bSFrançois Tigeot 		pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0),
209352ff8bdSFrançois Tigeot 					 I830_DRB3, &tmp);
210352ff8bdSFrançois Tigeot 		tom = tmp * MB(32);
211352ff8bdSFrançois Tigeot 
2128621f407SFrançois Tigeot 		base = tom - tseg_size - ggtt->stolen_size;
213a2fdbec6SFrançois Tigeot 	}
214a2fdbec6SFrançois Tigeot 
215a85cb24fSFrançois Tigeot 	if (base == 0 || add_overflows(base, ggtt->stolen_size))
2169edbd4a0SFrançois Tigeot 		return 0;
2179edbd4a0SFrançois Tigeot 
218ba55f2f5SFrançois Tigeot 	/* make sure we don't clobber the GTT if it's within stolen memory */
219a85cb24fSFrançois Tigeot 	if (INTEL_GEN(dev_priv) <= 4 &&
220a85cb24fSFrançois Tigeot 	    !IS_G33(dev_priv) && !IS_PINEVIEW(dev_priv) && !IS_G4X(dev_priv)) {
221ba55f2f5SFrançois Tigeot 		struct {
222a85cb24fSFrançois Tigeot 			dma_addr_t start, end;
223ba55f2f5SFrançois Tigeot 		} stolen[2] = {
2248621f407SFrançois Tigeot 			{ .start = base, .end = base + ggtt->stolen_size, },
2258621f407SFrançois Tigeot 			{ .start = base, .end = base + ggtt->stolen_size, },
226ba55f2f5SFrançois Tigeot 		};
2278621f407SFrançois Tigeot 		u64 ggtt_start, ggtt_end;
228ba55f2f5SFrançois Tigeot 
2298621f407SFrançois Tigeot 		ggtt_start = I915_READ(PGTBL_CTL);
2301e12ee3bSFrançois Tigeot 		if (IS_GEN4(dev_priv))
2318621f407SFrançois Tigeot 			ggtt_start = (ggtt_start & PGTBL_ADDRESS_LO_MASK) |
2328621f407SFrançois Tigeot 				     (ggtt_start & PGTBL_ADDRESS_HI_MASK) << 28;
233ba55f2f5SFrançois Tigeot 		else
2348621f407SFrançois Tigeot 			ggtt_start &= PGTBL_ADDRESS_LO_MASK;
2358621f407SFrançois Tigeot 		ggtt_end = ggtt_start + ggtt_total_entries(ggtt) * 4;
236ba55f2f5SFrançois Tigeot 
2378621f407SFrançois Tigeot 		if (ggtt_start >= stolen[0].start && ggtt_start < stolen[0].end)
2388621f407SFrançois Tigeot 			stolen[0].end = ggtt_start;
2398621f407SFrançois Tigeot 		if (ggtt_end > stolen[1].start && ggtt_end <= stolen[1].end)
2408621f407SFrançois Tigeot 			stolen[1].start = ggtt_end;
241ba55f2f5SFrançois Tigeot 
242ba55f2f5SFrançois Tigeot 		/* pick the larger of the two chunks */
243ba55f2f5SFrançois Tigeot 		if (stolen[0].end - stolen[0].start >
244ba55f2f5SFrançois Tigeot 		    stolen[1].end - stolen[1].start) {
245ba55f2f5SFrançois Tigeot 			base = stolen[0].start;
2468621f407SFrançois Tigeot 			ggtt->stolen_size = stolen[0].end - stolen[0].start;
247ba55f2f5SFrançois Tigeot 		} else {
248ba55f2f5SFrançois Tigeot 			base = stolen[1].start;
2498621f407SFrançois Tigeot 			ggtt->stolen_size = stolen[1].end - stolen[1].start;
250ba55f2f5SFrançois Tigeot 		}
251ba55f2f5SFrançois Tigeot 
252ba55f2f5SFrançois Tigeot 		if (stolen[0].start != stolen[1].start ||
253ba55f2f5SFrançois Tigeot 		    stolen[0].end != stolen[1].end) {
254a85cb24fSFrançois Tigeot 			dma_addr_t end = base + ggtt->stolen_size - 1;
255a85cb24fSFrançois Tigeot 
256ba55f2f5SFrançois Tigeot 			DRM_DEBUG_KMS("GTT within stolen memory at 0x%llx-0x%llx\n",
2578621f407SFrançois Tigeot 				      (unsigned long long)ggtt_start,
2588621f407SFrançois Tigeot 				      (unsigned long long)ggtt_end - 1);
259a85cb24fSFrançois Tigeot 			DRM_DEBUG_KMS("Stolen memory adjusted to %pad - %pad\n",
260a85cb24fSFrançois Tigeot 				      &base, &end);
261ba55f2f5SFrançois Tigeot 		}
262ba55f2f5SFrançois Tigeot 	}
263ba55f2f5SFrançois Tigeot 
264ba55f2f5SFrançois Tigeot 
2659edbd4a0SFrançois Tigeot 	/* Verify that nothing else uses this physical address. Stolen
2669edbd4a0SFrançois Tigeot 	 * memory should be reserved by the BIOS and hidden from the
2679edbd4a0SFrançois Tigeot 	 * kernel. So if the region is already marked as busy, something
2689edbd4a0SFrançois Tigeot 	 * is seriously wrong.
2699edbd4a0SFrançois Tigeot 	 */
2704be47400SFrançois Tigeot 	r = devm_request_mem_region(dev_priv->drm.dev, base, ggtt->stolen_size,
2719edbd4a0SFrançois Tigeot 				    "Graphics Stolen Memory");
2729edbd4a0SFrançois Tigeot 	if (r == NULL) {
2739edbd4a0SFrançois Tigeot 		/*
2749edbd4a0SFrançois Tigeot 		 * One more attempt but this time requesting region from
2759edbd4a0SFrançois Tigeot 		 * base + 1, as we have seen that this resolves the region
2769edbd4a0SFrançois Tigeot 		 * conflict with the PCI Bus.
2779edbd4a0SFrançois Tigeot 		 * This is a BIOS w/a: Some BIOS wrap stolen in the root
2789edbd4a0SFrançois Tigeot 		 * PCI bus, but have an off-by-one error. Hence retry the
2799edbd4a0SFrançois Tigeot 		 * reservation starting from 1 instead of 0.
280*3f2dd94aSFrançois Tigeot 		 * There's also BIOS with off-by-one on the other end.
2819edbd4a0SFrançois Tigeot 		 */
2824be47400SFrançois Tigeot 		r = devm_request_mem_region(dev_priv->drm.dev, base + 1,
283*3f2dd94aSFrançois Tigeot 					    ggtt->stolen_size - 2,
2849edbd4a0SFrançois Tigeot 					    "Graphics Stolen Memory");
2852c9916cdSFrançois Tigeot 		/*
2862c9916cdSFrançois Tigeot 		 * GEN3 firmware likes to smash pci bridges into the stolen
2872c9916cdSFrançois Tigeot 		 * range. Apparently this works.
2882c9916cdSFrançois Tigeot 		 */
2891e12ee3bSFrançois Tigeot 		if (r == NULL && !IS_GEN3(dev_priv)) {
290a85cb24fSFrançois Tigeot 			dma_addr_t end = base + ggtt->stolen_size;
291a85cb24fSFrançois Tigeot 
292a85cb24fSFrançois Tigeot 			DRM_ERROR("conflict detected with stolen region: [%pad - %pad]\n",
293a85cb24fSFrançois Tigeot 				  &base, &end);
2949edbd4a0SFrançois Tigeot 			base = 0;
2959edbd4a0SFrançois Tigeot 		}
2969edbd4a0SFrançois Tigeot 	}
2979edbd4a0SFrançois Tigeot 
298a2fdbec6SFrançois Tigeot 	return base;
299a2fdbec6SFrançois Tigeot }
300a2fdbec6SFrançois Tigeot 
i915_gem_cleanup_stolen(struct drm_device * dev)301a2fdbec6SFrançois Tigeot void i915_gem_cleanup_stolen(struct drm_device *dev)
302a2fdbec6SFrançois Tigeot {
303303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
304a2fdbec6SFrançois Tigeot 
3055d0b1887SFrançois Tigeot 	if (!drm_mm_initialized(&dev_priv->mm.stolen))
3065d0b1887SFrançois Tigeot 		return;
3075d0b1887SFrançois Tigeot 
308a2fdbec6SFrançois Tigeot 	drm_mm_takedown(&dev_priv->mm.stolen);
309a2fdbec6SFrançois Tigeot }
310a2fdbec6SFrançois Tigeot 
g4x_get_stolen_reserved(struct drm_i915_private * dev_priv,dma_addr_t * base,u32 * size)311352ff8bdSFrançois Tigeot static void g4x_get_stolen_reserved(struct drm_i915_private *dev_priv,
312a85cb24fSFrançois Tigeot 				    dma_addr_t *base, u32 *size)
313352ff8bdSFrançois Tigeot {
3148621f407SFrançois Tigeot 	struct i915_ggtt *ggtt = &dev_priv->ggtt;
315352ff8bdSFrançois Tigeot 	uint32_t reg_val = I915_READ(IS_GM45(dev_priv) ?
316352ff8bdSFrançois Tigeot 				     CTG_STOLEN_RESERVED :
317352ff8bdSFrançois Tigeot 				     ELK_STOLEN_RESERVED);
318a85cb24fSFrançois Tigeot 	dma_addr_t stolen_top = dev_priv->mm.stolen_base + ggtt->stolen_size;
319352ff8bdSFrançois Tigeot 
320352ff8bdSFrançois Tigeot 	*base = (reg_val & G4X_STOLEN_RESERVED_ADDR2_MASK) << 16;
321352ff8bdSFrançois Tigeot 
322352ff8bdSFrançois Tigeot 	WARN_ON((reg_val & G4X_STOLEN_RESERVED_ADDR1_MASK) < *base);
323352ff8bdSFrançois Tigeot 
324352ff8bdSFrançois Tigeot 	/* On these platforms, the register doesn't have a size field, so the
325352ff8bdSFrançois Tigeot 	 * size is the distance between the base and the top of the stolen
326352ff8bdSFrançois Tigeot 	 * memory. We also have the genuine case where base is zero and there's
327352ff8bdSFrançois Tigeot 	 * nothing reserved. */
328352ff8bdSFrançois Tigeot 	if (*base == 0)
329352ff8bdSFrançois Tigeot 		*size = 0;
330352ff8bdSFrançois Tigeot 	else
331352ff8bdSFrançois Tigeot 		*size = stolen_top - *base;
332352ff8bdSFrançois Tigeot }
333352ff8bdSFrançois Tigeot 
gen6_get_stolen_reserved(struct drm_i915_private * dev_priv,dma_addr_t * base,u32 * size)334a05eeebfSFrançois Tigeot static void gen6_get_stolen_reserved(struct drm_i915_private *dev_priv,
335a85cb24fSFrançois Tigeot 				     dma_addr_t *base, u32 *size)
336a05eeebfSFrançois Tigeot {
337a05eeebfSFrançois Tigeot 	uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED);
338a05eeebfSFrançois Tigeot 
339a05eeebfSFrançois Tigeot 	*base = reg_val & GEN6_STOLEN_RESERVED_ADDR_MASK;
340a05eeebfSFrançois Tigeot 
341a05eeebfSFrançois Tigeot 	switch (reg_val & GEN6_STOLEN_RESERVED_SIZE_MASK) {
342a05eeebfSFrançois Tigeot 	case GEN6_STOLEN_RESERVED_1M:
343a05eeebfSFrançois Tigeot 		*size = 1024 * 1024;
344a05eeebfSFrançois Tigeot 		break;
345a05eeebfSFrançois Tigeot 	case GEN6_STOLEN_RESERVED_512K:
346a05eeebfSFrançois Tigeot 		*size = 512 * 1024;
347a05eeebfSFrançois Tigeot 		break;
348a05eeebfSFrançois Tigeot 	case GEN6_STOLEN_RESERVED_256K:
349a05eeebfSFrançois Tigeot 		*size = 256 * 1024;
350a05eeebfSFrançois Tigeot 		break;
351a05eeebfSFrançois Tigeot 	case GEN6_STOLEN_RESERVED_128K:
352a05eeebfSFrançois Tigeot 		*size = 128 * 1024;
353a05eeebfSFrançois Tigeot 		break;
354a05eeebfSFrançois Tigeot 	default:
355a05eeebfSFrançois Tigeot 		*size = 1024 * 1024;
356a05eeebfSFrançois Tigeot 		MISSING_CASE(reg_val & GEN6_STOLEN_RESERVED_SIZE_MASK);
357a05eeebfSFrançois Tigeot 	}
358a05eeebfSFrançois Tigeot }
359a05eeebfSFrançois Tigeot 
gen7_get_stolen_reserved(struct drm_i915_private * dev_priv,dma_addr_t * base,u32 * size)360a05eeebfSFrançois Tigeot static void gen7_get_stolen_reserved(struct drm_i915_private *dev_priv,
361a85cb24fSFrançois Tigeot 				     dma_addr_t *base, u32 *size)
362a05eeebfSFrançois Tigeot {
363a05eeebfSFrançois Tigeot 	uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED);
364a05eeebfSFrançois Tigeot 
365a05eeebfSFrançois Tigeot 	*base = reg_val & GEN7_STOLEN_RESERVED_ADDR_MASK;
366a05eeebfSFrançois Tigeot 
367a05eeebfSFrançois Tigeot 	switch (reg_val & GEN7_STOLEN_RESERVED_SIZE_MASK) {
368a05eeebfSFrançois Tigeot 	case GEN7_STOLEN_RESERVED_1M:
369a05eeebfSFrançois Tigeot 		*size = 1024 * 1024;
370a05eeebfSFrançois Tigeot 		break;
371a05eeebfSFrançois Tigeot 	case GEN7_STOLEN_RESERVED_256K:
372a05eeebfSFrançois Tigeot 		*size = 256 * 1024;
373a05eeebfSFrançois Tigeot 		break;
374a05eeebfSFrançois Tigeot 	default:
375a05eeebfSFrançois Tigeot 		*size = 1024 * 1024;
376a05eeebfSFrançois Tigeot 		MISSING_CASE(reg_val & GEN7_STOLEN_RESERVED_SIZE_MASK);
377a05eeebfSFrançois Tigeot 	}
378a05eeebfSFrançois Tigeot }
379a05eeebfSFrançois Tigeot 
chv_get_stolen_reserved(struct drm_i915_private * dev_priv,dma_addr_t * base,u32 * size)380a85cb24fSFrançois Tigeot static void chv_get_stolen_reserved(struct drm_i915_private *dev_priv,
381a85cb24fSFrançois Tigeot 				    dma_addr_t *base, u32 *size)
382a05eeebfSFrançois Tigeot {
383a05eeebfSFrançois Tigeot 	uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED);
384a05eeebfSFrançois Tigeot 
385a05eeebfSFrançois Tigeot 	*base = reg_val & GEN6_STOLEN_RESERVED_ADDR_MASK;
386a05eeebfSFrançois Tigeot 
387a05eeebfSFrançois Tigeot 	switch (reg_val & GEN8_STOLEN_RESERVED_SIZE_MASK) {
388a05eeebfSFrançois Tigeot 	case GEN8_STOLEN_RESERVED_1M:
389a05eeebfSFrançois Tigeot 		*size = 1024 * 1024;
390a05eeebfSFrançois Tigeot 		break;
391a05eeebfSFrançois Tigeot 	case GEN8_STOLEN_RESERVED_2M:
392a05eeebfSFrançois Tigeot 		*size = 2 * 1024 * 1024;
393a05eeebfSFrançois Tigeot 		break;
394a05eeebfSFrançois Tigeot 	case GEN8_STOLEN_RESERVED_4M:
395a05eeebfSFrançois Tigeot 		*size = 4 * 1024 * 1024;
396a05eeebfSFrançois Tigeot 		break;
397a05eeebfSFrançois Tigeot 	case GEN8_STOLEN_RESERVED_8M:
398a05eeebfSFrançois Tigeot 		*size = 8 * 1024 * 1024;
399a05eeebfSFrançois Tigeot 		break;
400a05eeebfSFrançois Tigeot 	default:
401a05eeebfSFrançois Tigeot 		*size = 8 * 1024 * 1024;
402a05eeebfSFrançois Tigeot 		MISSING_CASE(reg_val & GEN8_STOLEN_RESERVED_SIZE_MASK);
403a05eeebfSFrançois Tigeot 	}
404a05eeebfSFrançois Tigeot }
405a05eeebfSFrançois Tigeot 
bdw_get_stolen_reserved(struct drm_i915_private * dev_priv,dma_addr_t * base,u32 * size)406a05eeebfSFrançois Tigeot static void bdw_get_stolen_reserved(struct drm_i915_private *dev_priv,
407a85cb24fSFrançois Tigeot 				    dma_addr_t *base, u32 *size)
408a05eeebfSFrançois Tigeot {
4098621f407SFrançois Tigeot 	struct i915_ggtt *ggtt = &dev_priv->ggtt;
410a05eeebfSFrançois Tigeot 	uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED);
411a85cb24fSFrançois Tigeot 	dma_addr_t stolen_top;
412a05eeebfSFrançois Tigeot 
4138621f407SFrançois Tigeot 	stolen_top = dev_priv->mm.stolen_base + ggtt->stolen_size;
414a05eeebfSFrançois Tigeot 
415a05eeebfSFrançois Tigeot 	*base = reg_val & GEN6_STOLEN_RESERVED_ADDR_MASK;
416a05eeebfSFrançois Tigeot 
417a05eeebfSFrançois Tigeot 	/* On these platforms, the register doesn't have a size field, so the
418a05eeebfSFrançois Tigeot 	 * size is the distance between the base and the top of the stolen
419a05eeebfSFrançois Tigeot 	 * memory. We also have the genuine case where base is zero and there's
420a05eeebfSFrançois Tigeot 	 * nothing reserved. */
421a05eeebfSFrançois Tigeot 	if (*base == 0)
422a05eeebfSFrançois Tigeot 		*size = 0;
423a05eeebfSFrançois Tigeot 	else
424a05eeebfSFrançois Tigeot 		*size = stolen_top - *base;
425a05eeebfSFrançois Tigeot }
426a05eeebfSFrançois Tigeot 
i915_gem_init_stolen(struct drm_i915_private * dev_priv)4274be47400SFrançois Tigeot int i915_gem_init_stolen(struct drm_i915_private *dev_priv)
428a2fdbec6SFrançois Tigeot {
4298621f407SFrançois Tigeot 	struct i915_ggtt *ggtt = &dev_priv->ggtt;
430a85cb24fSFrançois Tigeot 	dma_addr_t reserved_base, stolen_top;
431a85cb24fSFrançois Tigeot 	u32 reserved_total, reserved_size;
432a85cb24fSFrançois Tigeot 	u32 stolen_usable_start;
433a05eeebfSFrançois Tigeot 
434a05eeebfSFrançois Tigeot 	lockinit(&dev_priv->mm.stolen_lock, "i915msl", 0, LK_CANRECURSE);
435a2fdbec6SFrançois Tigeot 
4364be47400SFrançois Tigeot 	if (intel_vgpu_active(dev_priv)) {
4374be47400SFrançois Tigeot 		DRM_INFO("iGVT-g active, disabling use of stolen memory\n");
4384be47400SFrançois Tigeot 		return 0;
4394be47400SFrançois Tigeot 	}
4404be47400SFrançois Tigeot 
441*3f2dd94aSFrançois Tigeot 	if (intel_vtd_active() && INTEL_GEN(dev_priv) < 8) {
4429edbd4a0SFrançois Tigeot 		DRM_INFO("DMAR active, disabling use of stolen memory\n");
4439edbd4a0SFrançois Tigeot 		return 0;
4449edbd4a0SFrançois Tigeot 	}
4459edbd4a0SFrançois Tigeot 
4468621f407SFrançois Tigeot 	if (ggtt->stolen_size == 0)
4479edbd4a0SFrançois Tigeot 		return 0;
4489edbd4a0SFrançois Tigeot 
449a85cb24fSFrançois Tigeot 	dev_priv->mm.stolen_base = i915_stolen_to_dma(dev_priv);
450a2fdbec6SFrançois Tigeot 	if (dev_priv->mm.stolen_base == 0)
451a2fdbec6SFrançois Tigeot 		return 0;
452a2fdbec6SFrançois Tigeot 
4538621f407SFrançois Tigeot 	stolen_top = dev_priv->mm.stolen_base + ggtt->stolen_size;
454a85cb24fSFrançois Tigeot 	reserved_base = 0;
455a85cb24fSFrançois Tigeot 	reserved_size = 0;
456a2fdbec6SFrançois Tigeot 
457a05eeebfSFrançois Tigeot 	switch (INTEL_INFO(dev_priv)->gen) {
458a05eeebfSFrançois Tigeot 	case 2:
459a05eeebfSFrançois Tigeot 	case 3:
460352ff8bdSFrançois Tigeot 		break;
461a05eeebfSFrançois Tigeot 	case 4:
4621e12ee3bSFrançois Tigeot 		if (IS_G4X(dev_priv))
463a85cb24fSFrançois Tigeot 			g4x_get_stolen_reserved(dev_priv,
464a85cb24fSFrançois Tigeot 						&reserved_base, &reserved_size);
465352ff8bdSFrançois Tigeot 		break;
466a05eeebfSFrançois Tigeot 	case 5:
467a05eeebfSFrançois Tigeot 		/* Assume the gen6 maximum for the older platforms. */
468a05eeebfSFrançois Tigeot 		reserved_size = 1024 * 1024;
469a05eeebfSFrançois Tigeot 		reserved_base = stolen_top - reserved_size;
470a05eeebfSFrançois Tigeot 		break;
471a05eeebfSFrançois Tigeot 	case 6:
472a85cb24fSFrançois Tigeot 		gen6_get_stolen_reserved(dev_priv,
473a85cb24fSFrançois Tigeot 					 &reserved_base, &reserved_size);
474a05eeebfSFrançois Tigeot 		break;
475a05eeebfSFrançois Tigeot 	case 7:
476a85cb24fSFrançois Tigeot 		gen7_get_stolen_reserved(dev_priv,
477a85cb24fSFrançois Tigeot 					 &reserved_base, &reserved_size);
478a05eeebfSFrançois Tigeot 		break;
479a05eeebfSFrançois Tigeot 	default:
480a85cb24fSFrançois Tigeot 		if (IS_LP(dev_priv))
481a85cb24fSFrançois Tigeot 			chv_get_stolen_reserved(dev_priv,
482a85cb24fSFrançois Tigeot 						&reserved_base, &reserved_size);
483a05eeebfSFrançois Tigeot 		else
484a85cb24fSFrançois Tigeot 			bdw_get_stolen_reserved(dev_priv,
485a85cb24fSFrançois Tigeot 						&reserved_base, &reserved_size);
486a05eeebfSFrançois Tigeot 		break;
4871b13d190SFrançois Tigeot 	}
4885d0b1887SFrançois Tigeot 
489a05eeebfSFrançois Tigeot 	/* It is possible for the reserved base to be zero, but the register
490a05eeebfSFrançois Tigeot 	 * field for size doesn't have a zero option. */
491a05eeebfSFrançois Tigeot 	if (reserved_base == 0) {
492a05eeebfSFrançois Tigeot 		reserved_size = 0;
493a05eeebfSFrançois Tigeot 		reserved_base = stolen_top;
494a05eeebfSFrançois Tigeot 	}
495a05eeebfSFrançois Tigeot 
496a05eeebfSFrançois Tigeot 	if (reserved_base < dev_priv->mm.stolen_base ||
497a05eeebfSFrançois Tigeot 	    reserved_base + reserved_size > stolen_top) {
498a85cb24fSFrançois Tigeot 		dma_addr_t reserved_top = reserved_base + reserved_size;
499a85cb24fSFrançois Tigeot 		DRM_DEBUG_KMS("Stolen reserved area [%pad - %pad] outside stolen memory [%pad - %pad]\n",
500a85cb24fSFrançois Tigeot 			      &reserved_base, &reserved_top,
501a85cb24fSFrançois Tigeot 			      &dev_priv->mm.stolen_base, &stolen_top);
5029edbd4a0SFrançois Tigeot 		return 0;
503a05eeebfSFrançois Tigeot 	}
504a05eeebfSFrançois Tigeot 
5058621f407SFrançois Tigeot 	ggtt->stolen_reserved_base = reserved_base;
5068621f407SFrançois Tigeot 	ggtt->stolen_reserved_size = reserved_size;
507c0e85e96SFrançois Tigeot 
508a05eeebfSFrançois Tigeot 	/* It is possible for the reserved area to end before the end of stolen
509a05eeebfSFrançois Tigeot 	 * memory, so just consider the start. */
510a05eeebfSFrançois Tigeot 	reserved_total = stolen_top - reserved_base;
511a05eeebfSFrançois Tigeot 
512a85cb24fSFrançois Tigeot 	DRM_DEBUG_KMS("Memory reserved for graphics device: %uK, usable: %uK\n",
5138621f407SFrançois Tigeot 		      ggtt->stolen_size >> 10,
5148621f407SFrançois Tigeot 		      (ggtt->stolen_size - reserved_total) >> 10);
5159edbd4a0SFrançois Tigeot 
516a85cb24fSFrançois Tigeot 	stolen_usable_start = 0;
517a85cb24fSFrançois Tigeot 	/* WaSkipStolenMemoryFirstPage:bdw+ */
518a85cb24fSFrançois Tigeot 	if (INTEL_GEN(dev_priv) >= 8)
519a85cb24fSFrançois Tigeot 		stolen_usable_start = 4096;
520352ff8bdSFrançois Tigeot 
521a85cb24fSFrançois Tigeot 	ggtt->stolen_usable_size =
522a85cb24fSFrançois Tigeot 		ggtt->stolen_size - reserved_total - stolen_usable_start;
523a85cb24fSFrançois Tigeot 
524a85cb24fSFrançois Tigeot 	/* Basic memrange allocator for stolen space. */
525a85cb24fSFrançois Tigeot 	drm_mm_init(&dev_priv->mm.stolen, stolen_usable_start,
526a85cb24fSFrançois Tigeot 		    ggtt->stolen_usable_size);
527a2fdbec6SFrançois Tigeot 
528a2fdbec6SFrançois Tigeot 	return 0;
529a2fdbec6SFrançois Tigeot }
5305d0b1887SFrançois Tigeot 
5319edbd4a0SFrançois Tigeot static struct sg_table *
i915_pages_create_for_stolen(struct drm_device * dev,u32 offset,u32 size)5329edbd4a0SFrançois Tigeot i915_pages_create_for_stolen(struct drm_device *dev,
5339edbd4a0SFrançois Tigeot 			     u32 offset, u32 size)
5349edbd4a0SFrançois Tigeot {
5358621f407SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
5369edbd4a0SFrançois Tigeot 	struct sg_table *st;
5379edbd4a0SFrançois Tigeot 	struct scatterlist *sg;
5389edbd4a0SFrançois Tigeot 
539a85cb24fSFrançois Tigeot 	GEM_BUG_ON(range_overflows(offset, size, dev_priv->ggtt.stolen_size));
5409edbd4a0SFrançois Tigeot 
5419edbd4a0SFrançois Tigeot 	/* We hide that we have no struct page backing our stolen object
5429edbd4a0SFrançois Tigeot 	 * by wrapping the contiguous physical allocation with a fake
5439edbd4a0SFrançois Tigeot 	 * dma mapping in a single scatterlist.
5449edbd4a0SFrançois Tigeot 	 */
5459edbd4a0SFrançois Tigeot 
5461e12ee3bSFrançois Tigeot 	st = kmalloc(sizeof(*st), M_DRM, GFP_KERNEL);
5479edbd4a0SFrançois Tigeot 	if (st == NULL)
5484be47400SFrançois Tigeot 		return ERR_PTR(-ENOMEM);
5499edbd4a0SFrançois Tigeot 
5509edbd4a0SFrançois Tigeot 	if (sg_alloc_table(st, 1, GFP_KERNEL)) {
5519edbd4a0SFrançois Tigeot 		kfree(st);
5524be47400SFrançois Tigeot 		return ERR_PTR(-ENOMEM);
5539edbd4a0SFrançois Tigeot 	}
5549edbd4a0SFrançois Tigeot 
5559edbd4a0SFrançois Tigeot 	sg = st->sgl;
5569edbd4a0SFrançois Tigeot 	sg->offset = 0;
5579edbd4a0SFrançois Tigeot 	sg->length = size;
5589edbd4a0SFrançois Tigeot 
5599edbd4a0SFrançois Tigeot 	sg_dma_address(sg) = (dma_addr_t)dev_priv->mm.stolen_base + offset;
5609edbd4a0SFrançois Tigeot 	sg_dma_len(sg) = size;
5619edbd4a0SFrançois Tigeot 
5629edbd4a0SFrançois Tigeot 	return st;
5639edbd4a0SFrançois Tigeot }
5649edbd4a0SFrançois Tigeot 
i915_gem_object_get_pages_stolen(struct drm_i915_gem_object * obj)565*3f2dd94aSFrançois Tigeot static int i915_gem_object_get_pages_stolen(struct drm_i915_gem_object *obj)
5665d0b1887SFrançois Tigeot {
567*3f2dd94aSFrançois Tigeot 	struct sg_table *pages =
568*3f2dd94aSFrançois Tigeot 		i915_pages_create_for_stolen(obj->base.dev,
5694be47400SFrançois Tigeot 					     obj->stolen->start,
5704be47400SFrançois Tigeot 					     obj->stolen->size);
571*3f2dd94aSFrançois Tigeot 	if (IS_ERR(pages))
572*3f2dd94aSFrançois Tigeot 		return PTR_ERR(pages);
573*3f2dd94aSFrançois Tigeot 
574*3f2dd94aSFrançois Tigeot 	__i915_gem_object_set_pages(obj, pages, obj->stolen->size);
575*3f2dd94aSFrançois Tigeot 
576*3f2dd94aSFrançois Tigeot 	return 0;
5775d0b1887SFrançois Tigeot }
5785d0b1887SFrançois Tigeot 
i915_gem_object_put_pages_stolen(struct drm_i915_gem_object * obj,struct sg_table * pages)5794be47400SFrançois Tigeot static void i915_gem_object_put_pages_stolen(struct drm_i915_gem_object *obj,
5804be47400SFrançois Tigeot 					     struct sg_table *pages)
5815d0b1887SFrançois Tigeot {
5824be47400SFrançois Tigeot 	/* Should only be called from i915_gem_object_release_stolen() */
5834be47400SFrançois Tigeot 	sg_free_table(pages);
5844be47400SFrançois Tigeot 	kfree(pages);
5855d0b1887SFrançois Tigeot }
5865d0b1887SFrançois Tigeot 
58724edb884SFrançois Tigeot static void
i915_gem_object_release_stolen(struct drm_i915_gem_object * obj)58824edb884SFrançois Tigeot i915_gem_object_release_stolen(struct drm_i915_gem_object *obj)
58924edb884SFrançois Tigeot {
590303bf270SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
5914be47400SFrançois Tigeot 	struct drm_mm_node *stolen = fetch_and_zero(&obj->stolen);
592a05eeebfSFrançois Tigeot 
5934be47400SFrançois Tigeot 	GEM_BUG_ON(!stolen);
5944be47400SFrançois Tigeot 
5954be47400SFrançois Tigeot 	__i915_gem_object_unpin_pages(obj);
5964be47400SFrançois Tigeot 
5974be47400SFrançois Tigeot 	i915_gem_stolen_remove_node(dev_priv, stolen);
5984be47400SFrançois Tigeot 	kfree(stolen);
59924edb884SFrançois Tigeot }
6004be47400SFrançois Tigeot 
6015d0b1887SFrançois Tigeot static const struct drm_i915_gem_object_ops i915_gem_object_stolen_ops = {
6025d0b1887SFrançois Tigeot 	.get_pages = i915_gem_object_get_pages_stolen,
6035d0b1887SFrançois Tigeot 	.put_pages = i915_gem_object_put_pages_stolen,
60424edb884SFrançois Tigeot 	.release = i915_gem_object_release_stolen,
6055d0b1887SFrançois Tigeot };
6065d0b1887SFrançois Tigeot 
6075d0b1887SFrançois Tigeot static struct drm_i915_gem_object *
_i915_gem_object_create_stolen(struct drm_i915_private * dev_priv,struct drm_mm_node * stolen)608a85cb24fSFrançois Tigeot _i915_gem_object_create_stolen(struct drm_i915_private *dev_priv,
6095d0b1887SFrançois Tigeot 			       struct drm_mm_node *stolen)
6105d0b1887SFrançois Tigeot {
6115d0b1887SFrançois Tigeot 	struct drm_i915_gem_object *obj;
612*3f2dd94aSFrançois Tigeot 	unsigned int cache_level;
6135d0b1887SFrançois Tigeot 
614a85cb24fSFrançois Tigeot 	obj = i915_gem_object_alloc(dev_priv);
6155d0b1887SFrançois Tigeot 	if (obj == NULL)
6165d0b1887SFrançois Tigeot 		return NULL;
6175d0b1887SFrançois Tigeot 
618a85cb24fSFrançois Tigeot 	drm_gem_private_object_init(&dev_priv->drm, &obj->base, stolen->size);
6195d0b1887SFrançois Tigeot 	i915_gem_object_init(obj, &i915_gem_object_stolen_ops);
6205d0b1887SFrançois Tigeot 
6215d0b1887SFrançois Tigeot 	obj->stolen = stolen;
6229edbd4a0SFrançois Tigeot 	obj->base.read_domains = I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT;
623*3f2dd94aSFrançois Tigeot 	cache_level = HAS_LLC(dev_priv) ? I915_CACHE_LLC : I915_CACHE_NONE;
624*3f2dd94aSFrançois Tigeot 	i915_gem_object_set_cache_coherency(obj, cache_level);
6254be47400SFrançois Tigeot 
6264be47400SFrançois Tigeot 	if (i915_gem_object_pin_pages(obj))
6274be47400SFrançois Tigeot 		goto cleanup;
6285d0b1887SFrançois Tigeot 
6295d0b1887SFrançois Tigeot 	return obj;
6305d0b1887SFrançois Tigeot 
6315d0b1887SFrançois Tigeot cleanup:
6325d0b1887SFrançois Tigeot 	i915_gem_object_free(obj);
6335d0b1887SFrançois Tigeot 	return NULL;
6345d0b1887SFrançois Tigeot }
6355d0b1887SFrançois Tigeot 
6365d0b1887SFrançois Tigeot struct drm_i915_gem_object *
i915_gem_object_create_stolen(struct drm_i915_private * dev_priv,u32 size)637a85cb24fSFrançois Tigeot i915_gem_object_create_stolen(struct drm_i915_private *dev_priv, u32 size)
6385d0b1887SFrançois Tigeot {
6395d0b1887SFrançois Tigeot 	struct drm_i915_gem_object *obj;
6405d0b1887SFrançois Tigeot 	struct drm_mm_node *stolen;
6419edbd4a0SFrançois Tigeot 	int ret;
6425d0b1887SFrançois Tigeot 
6435d0b1887SFrançois Tigeot 	if (!drm_mm_initialized(&dev_priv->mm.stolen))
6445d0b1887SFrançois Tigeot 		return NULL;
6455d0b1887SFrançois Tigeot 
6465d0b1887SFrançois Tigeot 	if (size == 0)
6475d0b1887SFrançois Tigeot 		return NULL;
6485d0b1887SFrançois Tigeot 
6499edbd4a0SFrançois Tigeot 	stolen = kzalloc(sizeof(*stolen), GFP_KERNEL);
6509edbd4a0SFrançois Tigeot 	if (!stolen)
6515d0b1887SFrançois Tigeot 		return NULL;
6525d0b1887SFrançois Tigeot 
653a05eeebfSFrançois Tigeot 	ret = i915_gem_stolen_insert_node(dev_priv, stolen, size, 4096);
6549edbd4a0SFrançois Tigeot 	if (ret) {
6559edbd4a0SFrançois Tigeot 		kfree(stolen);
6569edbd4a0SFrançois Tigeot 		return NULL;
6579edbd4a0SFrançois Tigeot 	}
6589edbd4a0SFrançois Tigeot 
659a85cb24fSFrançois Tigeot 	obj = _i915_gem_object_create_stolen(dev_priv, stolen);
6605d0b1887SFrançois Tigeot 	if (obj)
6615d0b1887SFrançois Tigeot 		return obj;
6625d0b1887SFrançois Tigeot 
663a05eeebfSFrançois Tigeot 	i915_gem_stolen_remove_node(dev_priv, stolen);
6649edbd4a0SFrançois Tigeot 	kfree(stolen);
6655d0b1887SFrançois Tigeot 	return NULL;
6665d0b1887SFrançois Tigeot }
6675d0b1887SFrançois Tigeot 
6685d0b1887SFrançois Tigeot struct drm_i915_gem_object *
i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private * dev_priv,u32 stolen_offset,u32 gtt_offset,u32 size)669a85cb24fSFrançois Tigeot i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv,
6705d0b1887SFrançois Tigeot 					       u32 stolen_offset,
6715d0b1887SFrançois Tigeot 					       u32 gtt_offset,
6725d0b1887SFrançois Tigeot 					       u32 size)
6735d0b1887SFrançois Tigeot {
6748621f407SFrançois Tigeot 	struct i915_ggtt *ggtt = &dev_priv->ggtt;
6755d0b1887SFrançois Tigeot 	struct drm_i915_gem_object *obj;
6765d0b1887SFrançois Tigeot 	struct drm_mm_node *stolen;
6779edbd4a0SFrançois Tigeot 	struct i915_vma *vma;
6789edbd4a0SFrançois Tigeot 	int ret;
6795d0b1887SFrançois Tigeot 
6805d0b1887SFrançois Tigeot 	if (!drm_mm_initialized(&dev_priv->mm.stolen))
6815d0b1887SFrançois Tigeot 		return NULL;
6825d0b1887SFrançois Tigeot 
683a85cb24fSFrançois Tigeot 	lockdep_assert_held(&dev_priv->drm.struct_mutex);
684c0e85e96SFrançois Tigeot 
6855d0b1887SFrançois Tigeot 	DRM_DEBUG_KMS("creating preallocated stolen object: stolen_offset=%x, gtt_offset=%x, size=%x\n",
6865d0b1887SFrançois Tigeot 			stolen_offset, gtt_offset, size);
6875d0b1887SFrançois Tigeot 
6885d0b1887SFrançois Tigeot 	/* KISS and expect everything to be page-aligned */
689a85cb24fSFrançois Tigeot 	if (WARN_ON(size == 0) ||
690a85cb24fSFrançois Tigeot 	    WARN_ON(!IS_ALIGNED(size, I915_GTT_PAGE_SIZE)) ||
691a85cb24fSFrançois Tigeot 	    WARN_ON(!IS_ALIGNED(stolen_offset, I915_GTT_MIN_ALIGNMENT)))
6925d0b1887SFrançois Tigeot 		return NULL;
6935d0b1887SFrançois Tigeot 
6949edbd4a0SFrançois Tigeot 	stolen = kzalloc(sizeof(*stolen), GFP_KERNEL);
6959edbd4a0SFrançois Tigeot 	if (!stolen)
6969edbd4a0SFrançois Tigeot 		return NULL;
6979edbd4a0SFrançois Tigeot 
6989edbd4a0SFrançois Tigeot 	stolen->start = stolen_offset;
6999edbd4a0SFrançois Tigeot 	stolen->size = size;
700a05eeebfSFrançois Tigeot 	mutex_lock(&dev_priv->mm.stolen_lock);
7019edbd4a0SFrançois Tigeot 	ret = drm_mm_reserve_node(&dev_priv->mm.stolen, stolen);
702a05eeebfSFrançois Tigeot 	mutex_unlock(&dev_priv->mm.stolen_lock);
7039edbd4a0SFrançois Tigeot 	if (ret) {
7045d0b1887SFrançois Tigeot 		DRM_DEBUG_KMS("failed to allocate stolen space\n");
7059edbd4a0SFrançois Tigeot 		kfree(stolen);
7065d0b1887SFrançois Tigeot 		return NULL;
7075d0b1887SFrançois Tigeot 	}
7085d0b1887SFrançois Tigeot 
709a85cb24fSFrançois Tigeot 	obj = _i915_gem_object_create_stolen(dev_priv, stolen);
7105d0b1887SFrançois Tigeot 	if (obj == NULL) {
7115d0b1887SFrançois Tigeot 		DRM_DEBUG_KMS("failed to allocate stolen object\n");
712a05eeebfSFrançois Tigeot 		i915_gem_stolen_remove_node(dev_priv, stolen);
7139edbd4a0SFrançois Tigeot 		kfree(stolen);
7145d0b1887SFrançois Tigeot 		return NULL;
7155d0b1887SFrançois Tigeot 	}
7165d0b1887SFrançois Tigeot 
7175d0b1887SFrançois Tigeot 	/* Some objects just need physical mem from stolen space */
7189edbd4a0SFrançois Tigeot 	if (gtt_offset == I915_GTT_OFFSET_NONE)
7195d0b1887SFrançois Tigeot 		return obj;
7205d0b1887SFrançois Tigeot 
7214be47400SFrançois Tigeot 	ret = i915_gem_object_pin_pages(obj);
7224be47400SFrançois Tigeot 	if (ret)
7234be47400SFrançois Tigeot 		goto err;
7244be47400SFrançois Tigeot 
725a85cb24fSFrançois Tigeot 	vma = i915_vma_instance(obj, &ggtt->base, NULL);
7269edbd4a0SFrançois Tigeot 	if (IS_ERR(vma)) {
7279edbd4a0SFrançois Tigeot 		ret = PTR_ERR(vma);
7284be47400SFrançois Tigeot 		goto err_pages;
7299edbd4a0SFrançois Tigeot 	}
7309edbd4a0SFrançois Tigeot 
7315d0b1887SFrançois Tigeot 	/* To simplify the initialisation sequence between KMS and GTT,
7325d0b1887SFrançois Tigeot 	 * we allow construction of the stolen object prior to
7335d0b1887SFrançois Tigeot 	 * setting up the GTT space. The actual reservation will occur
7345d0b1887SFrançois Tigeot 	 * later.
7355d0b1887SFrançois Tigeot 	 */
736a85cb24fSFrançois Tigeot 	ret = i915_gem_gtt_reserve(&ggtt->base, &vma->node,
737a85cb24fSFrançois Tigeot 				   size, gtt_offset, obj->cache_level,
738a85cb24fSFrançois Tigeot 				   0);
7399edbd4a0SFrançois Tigeot 	if (ret) {
7405d0b1887SFrançois Tigeot 		DRM_DEBUG_KMS("failed to allocate stolen GTT space\n");
7414be47400SFrançois Tigeot 		goto err_pages;
7429edbd4a0SFrançois Tigeot 	}
7435d0b1887SFrançois Tigeot 
744a85cb24fSFrançois Tigeot 	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
745a85cb24fSFrançois Tigeot 
7464be47400SFrançois Tigeot 	vma->pages = obj->mm.pages;
74771f41f3eSFrançois Tigeot 	vma->flags |= I915_VMA_GLOBAL_BIND;
748352ff8bdSFrançois Tigeot 	__i915_vma_set_map_and_fenceable(vma);
74971f41f3eSFrançois Tigeot 	list_move_tail(&vma->vm_link, &ggtt->base.inactive_list);
750*3f2dd94aSFrançois Tigeot 
751*3f2dd94aSFrançois Tigeot 	lockmgr(&dev_priv->mm.obj_lock, LK_EXCLUSIVE);
752*3f2dd94aSFrançois Tigeot 	list_move_tail(&obj->mm.link, &dev_priv->mm.bound_list);
75371f41f3eSFrançois Tigeot 	obj->bind_count++;
754*3f2dd94aSFrançois Tigeot 	lockmgr(&dev_priv->mm.obj_lock, LK_RELEASE);
7555d0b1887SFrançois Tigeot 
7565d0b1887SFrançois Tigeot 	return obj;
7579edbd4a0SFrançois Tigeot 
7584be47400SFrançois Tigeot err_pages:
7594be47400SFrançois Tigeot 	i915_gem_object_unpin_pages(obj);
760352ff8bdSFrançois Tigeot err:
76187df8fc6SFrançois Tigeot 	i915_gem_object_put(obj);
7625d0b1887SFrançois Tigeot 	return NULL;
7639edbd4a0SFrançois Tigeot }
764