summaryrefslogtreecommitdiff
path: root/drivers/video/tegra/nvmap/nvmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/tegra/nvmap/nvmap.c')
-rw-r--r--drivers/video/tegra/nvmap/nvmap.c50
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;
+}