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