diff options
Diffstat (limited to 'drivers/video/tegra/nvmap/nvmap.c')
-rw-r--r-- | drivers/video/tegra/nvmap/nvmap.c | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/drivers/video/tegra/nvmap/nvmap.c b/drivers/video/tegra/nvmap/nvmap.c index 00b9a5adb49f..e8d795006082 100644 --- a/drivers/video/tegra/nvmap/nvmap.c +++ b/drivers/video/tegra/nvmap/nvmap.c @@ -730,3 +730,53 @@ void nvmap_free(struct nvmap_client *client, struct nvmap_handle_ref *r) { nvmap_free_handle_id(client, nvmap_ref_to_id(r)); } + +/* + * create a mapping to the user's buffer and write it + * (uses similar logic from nvmap_reloc_pin_array to map the cmdbuf) + */ +int nvmap_patch_wait(struct nvmap_client *client, + struct nvmap_handle *patch, + u32 patch_offset, u32 patch_value) +{ + unsigned long phys; + unsigned int pfn, last_pfn = 0; + void *addr; + pte_t **pte; + + if (patch_offset >= patch->size) { + nvmap_warn(client, "read/write outside of handle\n"); + return -EFAULT; + } + + pte = nvmap_alloc_pte(client->dev, &addr); + if (IS_ERR(pte)) + return PTR_ERR(pte); + + /* derive physaddr of cmdbuf WAIT to patch */ + if (patch->heap_pgalloc) { + unsigned int page = patch_offset >> PAGE_SHIFT; + phys = page_to_phys(patch->pgalloc.pages[page]); + phys += (patch_offset & ~PAGE_MASK); + } else { + phys = patch->carveout->base + patch_offset; + } + + pfn = __phys_to_pfn(phys); + + /* write PTE, so addr points to cmdbuf PFN */ + if (pfn != last_pfn) { + pgprot_t prot = nvmap_pgprot(patch, pgprot_kernel); + unsigned long kaddr = (unsigned long)addr; + set_pte_at(&init_mm, kaddr, *pte, pfn_pte(pfn, prot)); + flush_tlb_kernel_page(kaddr); + last_pfn = pfn; + } + + /* write patch_value to addr + page offset */ + __raw_writel(patch_value, addr + (phys & ~PAGE_MASK)); + + nvmap_free_pte(client->dev, pte); + wmb(); + return 0; +} |