aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/gma500/gtt.c
diff options
context:
space:
mode:
authorAlan Cox <alan@linux.intel.com>2011-11-03 14:21:09 -0400
committerDave Airlie <airlied@redhat.com>2011-11-16 06:23:38 -0500
commit8c8f1c958ab5e948e954ebd97e328f23d347293b (patch)
treed2539a02b51207edc29d437b6ccf34a881cbd02e /drivers/gpu/drm/gma500/gtt.c
parente32681d66dd33a7792a3f1a1e3ea0eb0c415f895 (diff)
gma500: introduce the GTT and MMU handling logic
This fits alongside the GEM support to manage our resources on the card itself. It's not actually clear we need to configure the MMU at all. Further research is needed before removing it entirely. For now we suck it in (slightly abused) from the old semi-free driver. Signed-off-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/gma500/gtt.c')
-rw-r--r--drivers/gpu/drm/gma500/gtt.c500
1 files changed, 500 insertions, 0 deletions
diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c
new file mode 100644
index 000000000000..461ead251bbd
--- /dev/null
+++ b/drivers/gpu/drm/gma500/gtt.c
@@ -0,0 +1,500 @@
1/*
2 * Copyright (c) 2007, Intel Corporation.
3 * All Rights Reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Authors: Thomas Hellstrom <thomas-at-tungstengraphics.com>
19 * Alan Cox <alan@linux.intel.com>
20 */
21
22#include <drm/drmP.h>
23#include "psb_drv.h"
24
25
26/*
27 * GTT resource allocator - manage page mappings in GTT space
28 */
29
30/**
31 * psb_gtt_mask_pte - generate GTT pte entry
32 * @pfn: page number to encode
33 * @type: type of memory in the GTT
34 *
35 * Set the GTT entry for the appropriate memory type.
36 */
37static inline uint32_t psb_gtt_mask_pte(uint32_t pfn, int type)
38{
39 uint32_t mask = PSB_PTE_VALID;
40
41 if (type & PSB_MMU_CACHED_MEMORY)
42 mask |= PSB_PTE_CACHED;
43 if (type & PSB_MMU_RO_MEMORY)
44 mask |= PSB_PTE_RO;
45 if (type & PSB_MMU_WO_MEMORY)
46 mask |= PSB_PTE_WO;
47
48 return (pfn << PAGE_SHIFT) | mask;
49}
50
51/**
52 * psb_gtt_entry - find the GTT entries for a gtt_range
53 * @dev: our DRM device
54 * @r: our GTT range
55 *
56 * Given a gtt_range object return the GTT offset of the page table
57 * entries for this gtt_range
58 */
59u32 *psb_gtt_entry(struct drm_device *dev, struct gtt_range *r)
60{
61 struct drm_psb_private *dev_priv = dev->dev_private;
62 unsigned long offset;
63
64 offset = r->resource.start - dev_priv->gtt_mem->start;
65
66 return dev_priv->gtt_map + (offset >> PAGE_SHIFT);
67}
68
69/**
70 * psb_gtt_insert - put an object into the GTT
71 * @dev: our DRM device
72 * @r: our GTT range
73 *
74 * Take our preallocated GTT range and insert the GEM object into
75 * the GTT.
76 *
77 * FIXME: gtt lock ?
78 */
79static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r)
80{
81 u32 *gtt_slot, pte;
82 struct page **pages;
83 int i;
84
85 if (r->pages == NULL) {
86 WARN_ON(1);
87 return -EINVAL;
88 }
89
90 WARN_ON(r->stolen); /* refcount these maybe ? */
91
92 gtt_slot = psb_gtt_entry(dev, r);
93 pages = r->pages;
94
95 /* Make sure changes are visible to the GPU */
96 set_pages_array_uc(pages, r->npage);
97
98 /* Write our page entries into the GTT itself */
99 for (i = 0; i < r->npage; i++) {
100 pte = psb_gtt_mask_pte(page_to_pfn(*pages++), 0/*type*/);
101 iowrite32(pte, gtt_slot++);
102 }
103 /* Make sure all the entries are set before we return */
104 ioread32(gtt_slot - 1);
105 return 0;
106}
107
108/**
109 * psb_gtt_remove - remove an object from the GTT
110 * @dev: our DRM device
111 * @r: our GTT range
112 *
113 * Remove a preallocated GTT range from the GTT. Overwrite all the
114 * page table entries with the dummy page
115 */
116
117static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r)
118{
119 struct drm_psb_private *dev_priv = dev->dev_private;
120 u32 *gtt_slot, pte;
121 int i;
122
123 WARN_ON(r->stolen);
124
125 gtt_slot = psb_gtt_entry(dev, r);
126 pte = psb_gtt_mask_pte(page_to_pfn(dev_priv->scratch_page), 0);
127
128 for (i = 0; i < r->npage; i++)
129 iowrite32(pte, gtt_slot++);
130 ioread32(gtt_slot - 1);
131 set_pages_array_wb(r->pages, r->npage);
132}
133
134/**
135 * psb_gtt_attach_pages - attach and pin GEM pages
136 * @gt: the gtt range
137 *
138 * Pin and build an in kernel list of the pages that back our GEM object.
139 * While we hold this the pages cannot be swapped out
140 */
141static int psb_gtt_attach_pages(struct gtt_range *gt)
142{
143 struct inode *inode;
144 struct address_space *mapping;
145 int i;
146 struct page *p;
147 int pages = gt->gem.size / PAGE_SIZE;
148
149 WARN_ON(gt->pages);
150
151 /* This is the shared memory object that backs the GEM resource */
152 inode = gt->gem.filp->f_path.dentry->d_inode;
153 mapping = inode->i_mapping;
154
155 gt->pages = kmalloc(pages * sizeof(struct page *), GFP_KERNEL);
156 if (gt->pages == NULL)
157 return -ENOMEM;
158 gt->npage = pages;
159
160 for (i = 0; i < pages; i++) {
161 /* FIXME: review flags later */
162 p = read_cache_page_gfp(mapping, i,
163 __GFP_COLD | GFP_KERNEL);
164 if (IS_ERR(p))
165 goto err;
166 gt->pages[i] = p;
167 }
168 return 0;
169
170err:
171 while (i--)
172 page_cache_release(gt->pages[i]);
173 kfree(gt->pages);
174 gt->pages = NULL;
175 return PTR_ERR(p);
176}
177
178/**
179 * psb_gtt_detach_pages - attach and pin GEM pages
180 * @gt: the gtt range
181 *
182 * Undo the effect of psb_gtt_attach_pages. At this point the pages
183 * must have been removed from the GTT as they could now be paged out
184 * and move bus address.
185 */
186static void psb_gtt_detach_pages(struct gtt_range *gt)
187{
188 int i;
189 for (i = 0; i < gt->npage; i++) {
190 /* FIXME: do we need to force dirty */
191 set_page_dirty(gt->pages[i]);
192 page_cache_release(gt->pages[i]);
193 }
194 kfree(gt->pages);
195 gt->pages = NULL;
196}
197
198/**
199 * psb_gtt_pin - pin pages into the GTT
200 * @gt: range to pin
201 *
202 * Pin a set of pages into the GTT. The pins are refcounted so that
203 * multiple pins need multiple unpins to undo.
204 *
205 * Non GEM backed objects treat this as a no-op as they are always GTT
206 * backed objects.
207 */
208int psb_gtt_pin(struct gtt_range *gt)
209{
210 int ret = 0;
211 struct drm_device *dev = gt->gem.dev;
212 struct drm_psb_private *dev_priv = dev->dev_private;
213
214 mutex_lock(&dev_priv->gtt_mutex);
215
216 if (gt->in_gart == 0 && gt->stolen == 0) {
217 ret = psb_gtt_attach_pages(gt);
218 if (ret < 0)
219 goto out;
220 ret = psb_gtt_insert(dev, gt);
221 if (ret < 0) {
222 psb_gtt_detach_pages(gt);
223 goto out;
224 }
225 }
226 gt->in_gart++;
227out:
228 mutex_unlock(&dev_priv->gtt_mutex);
229 return ret;
230}
231
232/**
233 * psb_gtt_unpin - Drop a GTT pin requirement
234 * @gt: range to pin
235 *
236 * Undoes the effect of psb_gtt_pin. On the last drop the GEM object
237 * will be removed from the GTT which will also drop the page references
238 * and allow the VM to clean up or page stuff.
239 *
240 * Non GEM backed objects treat this as a no-op as they are always GTT
241 * backed objects.
242 */
243void psb_gtt_unpin(struct gtt_range *gt)
244{
245 struct drm_device *dev = gt->gem.dev;
246 struct drm_psb_private *dev_priv = dev->dev_private;
247
248 mutex_lock(&dev_priv->gtt_mutex);
249
250 WARN_ON(!gt->in_gart);
251
252 gt->in_gart--;
253 if (gt->in_gart == 0 && gt->stolen == 0) {
254 psb_gtt_remove(dev, gt);
255 psb_gtt_detach_pages(gt);
256 }
257 mutex_unlock(&dev_priv->gtt_mutex);
258}
259
260/*
261 * GTT resource allocator - allocate and manage GTT address space
262 */
263
264/**
265 * psb_gtt_alloc_range - allocate GTT address space
266 * @dev: Our DRM device
267 * @len: length (bytes) of address space required
268 * @name: resource name
269 * @backed: resource should be backed by stolen pages
270 *
271 * Ask the kernel core to find us a suitable range of addresses
272 * to use for a GTT mapping.
273 *
274 * Returns a gtt_range structure describing the object, or NULL on
275 * error. On successful return the resource is both allocated and marked
276 * as in use.
277 */
278struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len,
279 const char *name, int backed)
280{
281 struct drm_psb_private *dev_priv = dev->dev_private;
282 struct gtt_range *gt;
283 struct resource *r = dev_priv->gtt_mem;
284 int ret;
285 unsigned long start, end;
286
287 if (backed) {
288 /* The start of the GTT is the stolen pages */
289 start = r->start;
290 end = r->start + dev_priv->gtt.stolen_size - 1;
291 } else {
292 /* The rest we will use for GEM backed objects */
293 start = r->start + dev_priv->gtt.stolen_size;
294 end = r->end;
295 }
296
297 gt = kzalloc(sizeof(struct gtt_range), GFP_KERNEL);
298 if (gt == NULL)
299 return NULL;
300 gt->resource.name = name;
301 gt->stolen = backed;
302 gt->in_gart = backed;
303 /* Ensure this is set for non GEM objects */
304 gt->gem.dev = dev;
305 ret = allocate_resource(dev_priv->gtt_mem, &gt->resource,
306 len, start, end, PAGE_SIZE, NULL, NULL);
307 if (ret == 0) {
308 gt->offset = gt->resource.start - r->start;
309 return gt;
310 }
311 kfree(gt);
312 return NULL;
313}
314
315/**
316 * psb_gtt_free_range - release GTT address space
317 * @dev: our DRM device
318 * @gt: a mapping created with psb_gtt_alloc_range
319 *
320 * Release a resource that was allocated with psb_gtt_alloc_range. If the
321 * object has been pinned by mmap users we clean this up here currently.
322 */
323void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt)
324{
325 /* Undo the mmap pin if we are destroying the object */
326 if (gt->mmapping) {
327 psb_gtt_unpin(gt);
328 gt->mmapping = 0;
329 }
330 WARN_ON(gt->in_gart && !gt->stolen);
331 release_resource(&gt->resource);
332 kfree(gt);
333}
334
335void psb_gtt_alloc(struct drm_device *dev)
336{
337 struct drm_psb_private *dev_priv = dev->dev_private;
338 init_rwsem(&dev_priv->gtt.sem);
339}
340
341void psb_gtt_takedown(struct drm_device *dev)
342{
343 struct drm_psb_private *dev_priv = dev->dev_private;
344
345 if (dev_priv->gtt_map) {
346 iounmap(dev_priv->gtt_map);
347 dev_priv->gtt_map = NULL;
348 }
349 if (dev_priv->gtt_initialized) {
350 pci_write_config_word(dev->pdev, PSB_GMCH_CTRL,
351 dev_priv->gmch_ctrl);
352 PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL);
353 (void) PSB_RVDC32(PSB_PGETBL_CTL);
354 }
355 if (dev_priv->vram_addr)
356 iounmap(dev_priv->gtt_map);
357}
358
359int psb_gtt_init(struct drm_device *dev, int resume)
360{
361 struct drm_psb_private *dev_priv = dev->dev_private;
362 unsigned gtt_pages;
363 unsigned long stolen_size, vram_stolen_size;
364 unsigned i, num_pages;
365 unsigned pfn_base;
366 uint32_t vram_pages;
367 uint32_t dvmt_mode = 0;
368 struct psb_gtt *pg;
369
370 int ret = 0;
371 uint32_t pte;
372
373 mutex_init(&dev_priv->gtt_mutex);
374
375 psb_gtt_alloc(dev);
376 pg = &dev_priv->gtt;
377
378 /* Enable the GTT */
379 pci_read_config_word(dev->pdev, PSB_GMCH_CTRL, &dev_priv->gmch_ctrl);
380 pci_write_config_word(dev->pdev, PSB_GMCH_CTRL,
381 dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED);
382
383 dev_priv->pge_ctl = PSB_RVDC32(PSB_PGETBL_CTL);
384 PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL);
385 (void) PSB_RVDC32(PSB_PGETBL_CTL);
386
387 /* The root resource we allocate address space from */
388 dev_priv->gtt_initialized = 1;
389
390 pg->gtt_phys_start = dev_priv->pge_ctl & PAGE_MASK;
391
392 /*
393 * FIXME: video mmu has hw bug to access 0x0D0000000,
394 * then make gatt start at 0x0e000,0000
395 */
396 pg->mmu_gatt_start = 0xE0000000;
397
398 pg->gtt_start = pci_resource_start(dev->pdev, PSB_GTT_RESOURCE);
399 gtt_pages = pci_resource_len(dev->pdev, PSB_GTT_RESOURCE)
400 >> PAGE_SHIFT;
401 /* CDV workaround */
402 if (pg->gtt_start == 0 || gtt_pages == 0) {
403 dev_err(dev->dev, "GTT PCI BAR not initialized.\n");
404 gtt_pages = 64;
405 pg->gtt_start = dev_priv->pge_ctl;
406 }
407
408 pg->gatt_start = pci_resource_start(dev->pdev, PSB_GATT_RESOURCE);
409 pg->gatt_pages = pci_resource_len(dev->pdev, PSB_GATT_RESOURCE)
410 >> PAGE_SHIFT;
411 dev_priv->gtt_mem = &dev->pdev->resource[PSB_GATT_RESOURCE];
412
413 if (pg->gatt_pages == 0 || pg->gatt_start == 0) {
414 static struct resource fudge; /* Preferably peppermint */
415
416 /* This can occur on CDV SDV systems. Fudge it in this case.
417 We really don't care what imaginary space is being allocated
418 at this point */
419 dev_err(dev->dev, "GATT PCI BAR not initialized.\n");
420 pg->gatt_start = 0x40000000;
421 pg->gatt_pages = (128 * 1024 * 1024) >> PAGE_SHIFT;
422 fudge.start = 0x40000000;
423 fudge.end = 0x40000000 + 128 * 1024 * 1024 - 1;
424 fudge.name = "fudge";
425 fudge.flags = IORESOURCE_MEM;
426 dev_priv->gtt_mem = &fudge;
427 }
428
429 pci_read_config_dword(dev->pdev, PSB_BSM, &dev_priv->stolen_base);
430 vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base
431 - PAGE_SIZE;
432
433 stolen_size = vram_stolen_size;
434
435 printk(KERN_INFO "Stolen memory information\n");
436 printk(KERN_INFO " base in RAM: 0x%x\n", dev_priv->stolen_base);
437 printk(KERN_INFO " size: %luK, calculated by (GTT RAM base) - (Stolen base), seems wrong\n",
438 vram_stolen_size/1024);
439 dvmt_mode = (dev_priv->gmch_ctrl >> 4) & 0x7;
440 printk(KERN_INFO " the correct size should be: %dM(dvmt mode=%d)\n",
441 (dvmt_mode == 1) ? 1 : (2 << (dvmt_mode - 1)), dvmt_mode);
442
443 if (resume && (gtt_pages != pg->gtt_pages) &&
444 (stolen_size != pg->stolen_size)) {
445 dev_err(dev->dev, "GTT resume error.\n");
446 ret = -EINVAL;
447 goto out_err;
448 }
449
450 pg->gtt_pages = gtt_pages;
451 pg->stolen_size = stolen_size;
452 dev_priv->vram_stolen_size = vram_stolen_size;
453
454 /*
455 * Map the GTT and the stolen memory area
456 */
457 dev_priv->gtt_map = ioremap_nocache(pg->gtt_phys_start,
458 gtt_pages << PAGE_SHIFT);
459 if (!dev_priv->gtt_map) {
460 dev_err(dev->dev, "Failure to map gtt.\n");
461 ret = -ENOMEM;
462 goto out_err;
463 }
464
465 dev_priv->vram_addr = ioremap_wc(dev_priv->stolen_base, stolen_size);
466 if (!dev_priv->vram_addr) {
467 dev_err(dev->dev, "Failure to map stolen base.\n");
468 ret = -ENOMEM;
469 goto out_err;
470 }
471
472 /*
473 * Insert vram stolen pages into the GTT
474 */
475
476 pfn_base = dev_priv->stolen_base >> PAGE_SHIFT;
477 vram_pages = num_pages = vram_stolen_size >> PAGE_SHIFT;
478 printk(KERN_INFO"Set up %d stolen pages starting at 0x%08x, GTT offset %dK\n",
479 num_pages, pfn_base << PAGE_SHIFT, 0);
480 for (i = 0; i < num_pages; ++i) {
481 pte = psb_gtt_mask_pte(pfn_base + i, 0);
482 iowrite32(pte, dev_priv->gtt_map + i);
483 }
484
485 /*
486 * Init rest of GTT to the scratch page to avoid accidents or scribbles
487 */
488
489 pfn_base = page_to_pfn(dev_priv->scratch_page);
490 pte = psb_gtt_mask_pte(pfn_base, 0);
491 for (; i < gtt_pages; ++i)
492 iowrite32(pte, dev_priv->gtt_map + i);
493
494 (void) ioread32(dev_priv->gtt_map + i - 1);
495 return 0;
496
497out_err:
498 psb_gtt_takedown(dev);
499 return ret;
500}