| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include <linux/shmem_fs.h> |
| |
| #include <asm/set_memory.h> |
| |
| #include "blitter.h" |
| #include "psb_drv.h" |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static inline uint32_t psb_gtt_mask_pte(uint32_t pfn, int type) |
| { |
| <------>uint32_t mask = PSB_PTE_VALID; |
| |
| <------> |
| <------> a high mapping page into the gtt */ |
| <------>BUG_ON(pfn & ~(0xFFFFFFFF >> PAGE_SHIFT)); |
| |
| <------>if (type & PSB_MMU_CACHED_MEMORY) |
| <------><------>mask |= PSB_PTE_CACHED; |
| <------>if (type & PSB_MMU_RO_MEMORY) |
| <------><------>mask |= PSB_PTE_RO; |
| <------>if (type & PSB_MMU_WO_MEMORY) |
| <------><------>mask |= PSB_PTE_WO; |
| |
| <------>return (pfn << PAGE_SHIFT) | mask; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static u32 __iomem *psb_gtt_entry(struct drm_device *dev, struct gtt_range *r) |
| { |
| <------>struct drm_psb_private *dev_priv = dev->dev_private; |
| <------>unsigned long offset; |
| |
| <------>offset = r->resource.start - dev_priv->gtt_mem->start; |
| |
| <------>return dev_priv->gtt_map + (offset >> PAGE_SHIFT); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r, |
| <------><------><------> int resume) |
| { |
| <------>u32 __iomem *gtt_slot; |
| <------>u32 pte; |
| <------>struct page **pages; |
| <------>int i; |
| |
| <------>if (r->pages == NULL) { |
| <------><------>WARN_ON(1); |
| <------><------>return -EINVAL; |
| <------>} |
| |
| <------>WARN_ON(r->stolen); |
| |
| <------>gtt_slot = psb_gtt_entry(dev, r); |
| <------>pages = r->pages; |
| |
| <------>if (!resume) { |
| <------><------> |
| <------><------>set_pages_array_wc(pages, r->npage); |
| <------>} |
| |
| <------> |
| <------>for (i = r->roll; i < r->npage; i++) { |
| <------><------>pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), |
| <------><------><------><------> PSB_MMU_CACHED_MEMORY); |
| <------><------>iowrite32(pte, gtt_slot++); |
| <------>} |
| <------>for (i = 0; i < r->roll; i++) { |
| <------><------>pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), |
| <------><------><------><------> PSB_MMU_CACHED_MEMORY); |
| <------><------>iowrite32(pte, gtt_slot++); |
| <------>} |
| <------> |
| <------>ioread32(gtt_slot - 1); |
| |
| <------>return 0; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r) |
| { |
| <------>struct drm_psb_private *dev_priv = dev->dev_private; |
| <------>u32 __iomem *gtt_slot; |
| <------>u32 pte; |
| <------>int i; |
| |
| <------>WARN_ON(r->stolen); |
| |
| <------>gtt_slot = psb_gtt_entry(dev, r); |
| <------>pte = psb_gtt_mask_pte(page_to_pfn(dev_priv->scratch_page), |
| <------><------><------> PSB_MMU_CACHED_MEMORY); |
| |
| <------>for (i = 0; i < r->npage; i++) |
| <------><------>iowrite32(pte, gtt_slot++); |
| <------>ioread32(gtt_slot - 1); |
| <------>set_pages_array_wb(r->pages, r->npage); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void psb_gtt_roll(struct drm_device *dev, struct gtt_range *r, int roll) |
| { |
| <------>u32 __iomem *gtt_slot; |
| <------>u32 pte; |
| <------>int i; |
| |
| <------>if (roll >= r->npage) { |
| <------><------>WARN_ON(1); |
| <------><------>return; |
| <------>} |
| |
| <------>r->roll = roll; |
| |
| <------> |
| <------> the right position when it gets pinned */ |
| <------>if (!r->stolen && !r->in_gart) |
| <------><------>return; |
| |
| <------>gtt_slot = psb_gtt_entry(dev, r); |
| |
| <------>for (i = r->roll; i < r->npage; i++) { |
| <------><------>pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), |
| <------><------><------><------> PSB_MMU_CACHED_MEMORY); |
| <------><------>iowrite32(pte, gtt_slot++); |
| <------>} |
| <------>for (i = 0; i < r->roll; i++) { |
| <------><------>pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), |
| <------><------><------><------> PSB_MMU_CACHED_MEMORY); |
| <------><------>iowrite32(pte, gtt_slot++); |
| <------>} |
| <------>ioread32(gtt_slot - 1); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int psb_gtt_attach_pages(struct gtt_range *gt) |
| { |
| <------>struct page **pages; |
| |
| <------>WARN_ON(gt->pages); |
| |
| <------>pages = drm_gem_get_pages(>->gem); |
| <------>if (IS_ERR(pages)) |
| <------><------>return PTR_ERR(pages); |
| |
| <------>gt->npage = gt->gem.size / PAGE_SIZE; |
| <------>gt->pages = pages; |
| |
| <------>return 0; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void psb_gtt_detach_pages(struct gtt_range *gt) |
| { |
| <------>drm_gem_put_pages(>->gem, gt->pages, true, false); |
| <------>gt->pages = NULL; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int psb_gtt_pin(struct gtt_range *gt) |
| { |
| <------>int ret = 0; |
| <------>struct drm_device *dev = gt->gem.dev; |
| <------>struct drm_psb_private *dev_priv = dev->dev_private; |
| <------>u32 gpu_base = dev_priv->gtt.gatt_start; |
| |
| <------>mutex_lock(&dev_priv->gtt_mutex); |
| |
| <------>if (gt->in_gart == 0 && gt->stolen == 0) { |
| <------><------>ret = psb_gtt_attach_pages(gt); |
| <------><------>if (ret < 0) |
| <------><------><------>goto out; |
| <------><------>ret = psb_gtt_insert(dev, gt, 0); |
| <------><------>if (ret < 0) { |
| <------><------><------>psb_gtt_detach_pages(gt); |
| <------><------><------>goto out; |
| <------><------>} |
| <------><------>psb_mmu_insert_pages(psb_mmu_get_default_pd(dev_priv->mmu), |
| <------><------><------><------> gt->pages, (gpu_base + gt->offset), |
| <------><------><------><------> gt->npage, 0, 0, PSB_MMU_CACHED_MEMORY); |
| <------>} |
| <------>gt->in_gart++; |
| out: |
| <------>mutex_unlock(&dev_priv->gtt_mutex); |
| <------>return ret; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void psb_gtt_unpin(struct gtt_range *gt) |
| { |
| <------>struct drm_device *dev = gt->gem.dev; |
| <------>struct drm_psb_private *dev_priv = dev->dev_private; |
| <------>u32 gpu_base = dev_priv->gtt.gatt_start; |
| <------>int ret; |
| |
| <------> |
| <------>mutex_lock(&dev_priv->gtt_mutex); |
| |
| <------> |
| <------>ret = gma_blt_wait_idle(dev_priv); |
| <------>if (ret) { |
| <------><------>DRM_ERROR("Failed to idle the blitter, unpin failed!"); |
| <------><------>goto out; |
| <------>} |
| |
| <------>WARN_ON(!gt->in_gart); |
| |
| <------>gt->in_gart--; |
| <------>if (gt->in_gart == 0 && gt->stolen == 0) { |
| <------><------>psb_mmu_remove_pages(psb_mmu_get_default_pd(dev_priv->mmu), |
| <------><------><------><------> (gpu_base + gt->offset), gt->npage, 0, 0); |
| <------><------>psb_gtt_remove(dev, gt); |
| <------><------>psb_gtt_detach_pages(gt); |
| <------>} |
| |
| out: |
| <------>mutex_unlock(&dev_priv->gtt_mutex); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len, |
| <------><------><------><------> const char *name, int backed, u32 align) |
| { |
| <------>struct drm_psb_private *dev_priv = dev->dev_private; |
| <------>struct gtt_range *gt; |
| <------>struct resource *r = dev_priv->gtt_mem; |
| <------>int ret; |
| <------>unsigned long start, end; |
| |
| <------>if (backed) { |
| <------><------> |
| <------><------>start = r->start; |
| <------><------>end = r->start + dev_priv->gtt.stolen_size - 1; |
| <------>} else { |
| <------><------> |
| <------><------>start = r->start + dev_priv->gtt.stolen_size; |
| <------><------>end = r->end; |
| <------>} |
| |
| <------>gt = kzalloc(sizeof(struct gtt_range), GFP_KERNEL); |
| <------>if (gt == NULL) |
| <------><------>return NULL; |
| <------>gt->resource.name = name; |
| <------>gt->stolen = backed; |
| <------>gt->in_gart = backed; |
| <------>gt->roll = 0; |
| <------> |
| <------>gt->gem.dev = dev; |
| <------>ret = allocate_resource(dev_priv->gtt_mem, >->resource, |
| <------><------><------><------>len, start, end, align, NULL, NULL); |
| <------>if (ret == 0) { |
| <------><------>gt->offset = gt->resource.start - r->start; |
| <------><------>return gt; |
| <------>} |
| <------>kfree(gt); |
| <------>return NULL; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt) |
| { |
| <------> |
| <------>if (gt->mmapping) { |
| <------><------>psb_gtt_unpin(gt); |
| <------><------>gt->mmapping = 0; |
| <------>} |
| <------>WARN_ON(gt->in_gart && !gt->stolen); |
| <------>release_resource(>->resource); |
| <------>kfree(gt); |
| } |
| |
| static void psb_gtt_alloc(struct drm_device *dev) |
| { |
| <------>struct drm_psb_private *dev_priv = dev->dev_private; |
| <------>init_rwsem(&dev_priv->gtt.sem); |
| } |
| |
| void psb_gtt_takedown(struct drm_device *dev) |
| { |
| <------>struct drm_psb_private *dev_priv = dev->dev_private; |
| |
| <------>if (dev_priv->gtt_map) { |
| <------><------>iounmap(dev_priv->gtt_map); |
| <------><------>dev_priv->gtt_map = NULL; |
| <------>} |
| <------>if (dev_priv->gtt_initialized) { |
| <------><------>pci_write_config_word(dev->pdev, PSB_GMCH_CTRL, |
| <------><------><------><------> dev_priv->gmch_ctrl); |
| <------><------>PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL); |
| <------><------>(void) PSB_RVDC32(PSB_PGETBL_CTL); |
| <------>} |
| <------>if (dev_priv->vram_addr) |
| <------><------>iounmap(dev_priv->gtt_map); |
| } |
| |
| int psb_gtt_init(struct drm_device *dev, int resume) |
| { |
| <------>struct drm_psb_private *dev_priv = dev->dev_private; |
| <------>unsigned gtt_pages; |
| <------>unsigned long stolen_size, vram_stolen_size; |
| <------>unsigned i, num_pages; |
| <------>unsigned pfn_base; |
| <------>struct psb_gtt *pg; |
| |
| <------>int ret = 0; |
| <------>uint32_t pte; |
| |
| <------>if (!resume) { |
| <------><------>mutex_init(&dev_priv->gtt_mutex); |
| <------><------>mutex_init(&dev_priv->mmap_mutex); |
| <------><------>psb_gtt_alloc(dev); |
| <------>} |
| |
| <------>pg = &dev_priv->gtt; |
| |
| <------> |
| <------>pci_read_config_word(dev->pdev, PSB_GMCH_CTRL, &dev_priv->gmch_ctrl); |
| <------>pci_write_config_word(dev->pdev, PSB_GMCH_CTRL, |
| <------><------><------> dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED); |
| |
| <------>dev_priv->pge_ctl = PSB_RVDC32(PSB_PGETBL_CTL); |
| <------>PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL); |
| <------>(void) PSB_RVDC32(PSB_PGETBL_CTL); |
| |
| <------> |
| <------>dev_priv->gtt_initialized = 1; |
| |
| <------>pg->gtt_phys_start = dev_priv->pge_ctl & PAGE_MASK; |
| |
| <------> |
| <------> * The video mmu has a hw bug when accessing 0x0D0000000. |
| <------> * Make gatt start at 0x0e000,0000. This doesn't actually |
| <------> * matter for us but may do if the video acceleration ever |
| <------> * gets opened up. |
| <------> */ |
| <------>pg->mmu_gatt_start = 0xE0000000; |
| |
| <------>pg->gtt_start = pci_resource_start(dev->pdev, PSB_GTT_RESOURCE); |
| <------>gtt_pages = pci_resource_len(dev->pdev, PSB_GTT_RESOURCE) |
| <------><------><------><------><------><------><------><------>>> PAGE_SHIFT; |
| <------> |
| <------>if (pg->gtt_start == 0 || gtt_pages == 0) { |
| <------><------>dev_dbg(dev->dev, "GTT PCI BAR not initialized.\n"); |
| <------><------>gtt_pages = 64; |
| <------><------>pg->gtt_start = dev_priv->pge_ctl; |
| <------>} |
| |
| <------>pg->gatt_start = pci_resource_start(dev->pdev, PSB_GATT_RESOURCE); |
| <------>pg->gatt_pages = pci_resource_len(dev->pdev, PSB_GATT_RESOURCE) |
| <------><------><------><------><------><------><------><------>>> PAGE_SHIFT; |
| <------>dev_priv->gtt_mem = &dev->pdev->resource[PSB_GATT_RESOURCE]; |
| |
| <------>if (pg->gatt_pages == 0 || pg->gatt_start == 0) { |
| <------><------>static struct resource fudge; |
| <------><------> |
| <------><------> We really don't care what imaginary space is being allocated |
| <------><------> at this point */ |
| <------><------>dev_dbg(dev->dev, "GATT PCI BAR not initialized.\n"); |
| <------><------>pg->gatt_start = 0x40000000; |
| <------><------>pg->gatt_pages = (128 * 1024 * 1024) >> PAGE_SHIFT; |
| <------><------> |
| <------><------> a view from the GPU into memory and not vice versa. As such |
| <------><------> this is really allocating space that is not the same as the |
| <------><------> CPU address space on CDV */ |
| <------><------>fudge.start = 0x40000000; |
| <------><------>fudge.end = 0x40000000 + 128 * 1024 * 1024 - 1; |
| <------><------>fudge.name = "fudge"; |
| <------><------>fudge.flags = IORESOURCE_MEM; |
| <------><------>dev_priv->gtt_mem = &fudge; |
| <------>} |
| |
| <------>pci_read_config_dword(dev->pdev, PSB_BSM, &dev_priv->stolen_base); |
| <------>vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base |
| <------><------><------><------><------><------><------><------>- PAGE_SIZE; |
| |
| <------>stolen_size = vram_stolen_size; |
| |
| <------>dev_dbg(dev->dev, "Stolen memory base 0x%x, size %luK\n", |
| <------><------><------>dev_priv->stolen_base, vram_stolen_size / 1024); |
| |
| <------>if (resume && (gtt_pages != pg->gtt_pages) && |
| <------> (stolen_size != pg->stolen_size)) { |
| <------><------>dev_err(dev->dev, "GTT resume error.\n"); |
| <------><------>ret = -EINVAL; |
| <------><------>goto out_err; |
| <------>} |
| |
| <------>pg->gtt_pages = gtt_pages; |
| <------>pg->stolen_size = stolen_size; |
| <------>dev_priv->vram_stolen_size = vram_stolen_size; |
| |
| <------> |
| <------> * Map the GTT and the stolen memory area |
| <------> */ |
| <------>if (!resume) |
| <------><------>dev_priv->gtt_map = ioremap(pg->gtt_phys_start, |
| <------><------><------><------><------><------>gtt_pages << PAGE_SHIFT); |
| <------>if (!dev_priv->gtt_map) { |
| <------><------>dev_err(dev->dev, "Failure to map gtt.\n"); |
| <------><------>ret = -ENOMEM; |
| <------><------>goto out_err; |
| <------>} |
| |
| <------>if (!resume) |
| <------><------>dev_priv->vram_addr = ioremap_wc(dev_priv->stolen_base, |
| <------><------><------><------><------><------> stolen_size); |
| |
| <------>if (!dev_priv->vram_addr) { |
| <------><------>dev_err(dev->dev, "Failure to map stolen base.\n"); |
| <------><------>ret = -ENOMEM; |
| <------><------>goto out_err; |
| <------>} |
| |
| <------> |
| <------> * Insert vram stolen pages into the GTT |
| <------> */ |
| |
| <------>pfn_base = dev_priv->stolen_base >> PAGE_SHIFT; |
| <------>num_pages = vram_stolen_size >> PAGE_SHIFT; |
| <------>dev_dbg(dev->dev, "Set up %d stolen pages starting at 0x%08x, GTT offset %dK\n", |
| <------><------>num_pages, pfn_base << PAGE_SHIFT, 0); |
| <------>for (i = 0; i < num_pages; ++i) { |
| <------><------>pte = psb_gtt_mask_pte(pfn_base + i, PSB_MMU_CACHED_MEMORY); |
| <------><------>iowrite32(pte, dev_priv->gtt_map + i); |
| <------>} |
| |
| <------> |
| <------> * Init rest of GTT to the scratch page to avoid accidents or scribbles |
| <------> */ |
| |
| <------>pfn_base = page_to_pfn(dev_priv->scratch_page); |
| <------>pte = psb_gtt_mask_pte(pfn_base, PSB_MMU_CACHED_MEMORY); |
| <------>for (; i < gtt_pages; ++i) |
| <------><------>iowrite32(pte, dev_priv->gtt_map + i); |
| |
| <------>(void) ioread32(dev_priv->gtt_map + i - 1); |
| <------>return 0; |
| |
| out_err: |
| <------>psb_gtt_takedown(dev); |
| <------>return ret; |
| } |
| |
| int psb_gtt_restore(struct drm_device *dev) |
| { |
| <------>struct drm_psb_private *dev_priv = dev->dev_private; |
| <------>struct resource *r = dev_priv->gtt_mem->child; |
| <------>struct gtt_range *range; |
| <------>unsigned int restored = 0, total = 0, size = 0; |
| |
| <------> |
| <------>mutex_lock(&dev_priv->gtt_mutex); |
| <------>psb_gtt_init(dev, 1); |
| |
| <------>while (r != NULL) { |
| <------><------>range = container_of(r, struct gtt_range, resource); |
| <------><------>if (range->pages) { |
| <------><------><------>psb_gtt_insert(dev, range, 1); |
| <------><------><------>size += range->resource.end - range->resource.start; |
| <------><------><------>restored++; |
| <------><------>} |
| <------><------>r = r->sibling; |
| <------><------>total++; |
| <------>} |
| <------>mutex_unlock(&dev_priv->gtt_mutex); |
| <------>DRM_DEBUG_DRIVER("Restored %u of %u gtt ranges (%u KB)", restored, |
| <------><------><------> total, (size / 1024)); |
| |
| <------>return 0; |
| } |
| |