From 915c2f85a0db015be62ac559f09a4983f67a87ff Mon Sep 17 00:00:00 2001 From: Jeevan Shriram Date: Mon, 28 Apr 2014 18:04:07 -0700 Subject: [PATCH] mdss: fb: Add backward compatibility to fb memory In the current implementation, contiguous memory allocation during bootup is removed and related changes were made in frame buffer driver which is not backward compatible. With this change, add backward compatibility to map to physically contiguous memory when available which is allocated at bootup. Change-Id: Ib1a595f2426dda8d3e1c5e50c71c35d3f45854b6 Signed-off-by: Jeevan Shriram --- drivers/video/fbdev/msm/mdss_fb.c | 163 +++++++++++++++++++++++++++--- drivers/video/fbdev/msm/mdss_fb.h | 1 - 2 files changed, 151 insertions(+), 13 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index fe2dd93da88c..396a007ba8cd 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -92,6 +92,8 @@ static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg); static int mdss_fb_fbmem_ion_mmap(struct fb_info *info, struct vm_area_struct *vma); +static int mdss_fb_alloc_fb_ion_memory(struct msm_fb_data_type *mfd, + size_t size); static void mdss_fb_release_fences(struct msm_fb_data_type *mfd); static int __mdss_fb_sync_buf_done_callback(struct notifier_block *p, unsigned long val, void *data); @@ -1123,11 +1125,11 @@ void mdss_fb_free_fb_ion_memory(struct msm_fb_data_type *mfd) } ion_free(mfd->fb_ion_client, mfd->fb_ion_handle); + mfd->fb_ion_handle = NULL; } -int mdss_fb_alloc_fb_ion_memory(struct msm_fb_data_type *mfd) +int mdss_fb_alloc_fb_ion_memory(struct msm_fb_data_type *mfd, size_t fb_size) { - size_t size; unsigned long buf_size; int rc; void *vaddr; @@ -1145,10 +1147,8 @@ int mdss_fb_alloc_fb_ion_memory(struct msm_fb_data_type *mfd) } } - size = mfd->fbi->fix.line_length * mfd->fbi->var.yres * MDSS_FB_NUM; - pr_debug("size for mmap = %d", (int) size); - - mfd->fb_ion_handle = ion_alloc(mfd->fb_ion_client, size, SZ_4K, + pr_debug("size for mmap = %zu", fb_size); + mfd->fb_ion_handle = ion_alloc(mfd->fb_ion_client, fb_size, SZ_4K, ION_HEAP(ION_SYSTEM_HEAP_ID), 0); if (IS_ERR_OR_NULL(mfd->fb_ion_handle)) { pr_err("unable to alloc fbmem from ion - %ld\n", @@ -1181,11 +1181,11 @@ int mdss_fb_alloc_fb_ion_memory(struct msm_fb_data_type *mfd) goto fb_mmap_failed; } - pr_debug("alloc 0x%zuB vaddr = %p (%pa iova) for fb%d\n", size, vaddr, - &mfd->iova, mfd->index); + pr_debug("alloc 0x%zuB vaddr = %p (%pa iova) for fb%d\n", fb_size, + vaddr, &mfd->iova, mfd->index); mfd->fbi->screen_base = (char *) vaddr; - mfd->fbi->fix.smem_len = size; + mfd->fbi->fix.smem_len = fb_size; return rc; @@ -1232,7 +1232,7 @@ static int mdss_fb_fbmem_ion_mmap(struct fb_info *info, } if (!mfd->fbi->screen_base) { - rc = mdss_fb_alloc_fb_ion_memory(mfd); + rc = mdss_fb_alloc_fb_ion_memory(mfd, fb_size); if (rc < 0) { pr_err("fb mmap failed!!!!"); return rc; @@ -1292,6 +1292,69 @@ static int mdss_fb_fbmem_ion_mmap(struct fb_info *info, return rc; } +/* + * mdss_fb_physical_mmap() - Custom fb mmap() function for MSM driver. + * + * @info - Framebuffer info. + * @vma - VM area which is part of the process virtual memory. + * + * This framebuffer mmap function differs from standard mmap() function as + * map to framebuffer memory from the CMA memory which is allocated during + * bootup. + * + * Return: virtual address is returned through vma + */ +static int mdss_fb_physical_mmap(struct fb_info *info, + struct vm_area_struct *vma) +{ + /* Get frame buffer memory range. */ + unsigned long start = info->fix.smem_start; + u32 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); + unsigned long off = vma->vm_pgoff << PAGE_SHIFT; + + if (!start) { + pr_warn("No framebuffer memory is allocated\n"); + return -ENOMEM; + } + + /* Set VM flags. */ + start &= PAGE_MASK; + if ((vma->vm_end <= vma->vm_start) || + (off >= len) || + ((vma->vm_end - vma->vm_start) > (len - off))) + return -EINVAL; + off += start; + if (off < start) + return -EINVAL; + vma->vm_pgoff = off >> PAGE_SHIFT; + /* This is an IO map - tell maydump to skip this VMA */ + vma->vm_flags |= VM_IO; + + /* Remap the frame buffer I/O range */ + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} + +static int mdss_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + int rc = 0; + + if (!info->fix.smem_start && !mfd->fb_ion_handle) + rc = mdss_fb_fbmem_ion_mmap(info, vma); + else + rc = mdss_fb_physical_mmap(info, vma); + + if (rc < 0) + pr_err("fb mmap failed with rc = %d", rc); + + return rc; +} + static struct fb_ops mdss_fb_ops = { .owner = THIS_MODULE, .fb_open = mdss_fb_open, @@ -1304,14 +1367,89 @@ static struct fb_ops mdss_fb_ops = { #ifdef CONFIG_COMPAT .fb_compat_ioctl = mdss_fb_compat_ioctl, #endif - .fb_mmap = mdss_fb_fbmem_ion_mmap, + .fb_mmap = mdss_fb_mmap, }; +static int mdss_fb_alloc_fbmem_iommu(struct msm_fb_data_type *mfd, int dom) +{ + void *virt = NULL; + phys_addr_t phys = 0; + size_t size = 0; + struct platform_device *pdev = mfd->pdev; + int rc = 0; + struct device_node *fbmem_pnode = NULL; + + if (!pdev || !pdev->dev.of_node) { + pr_err("Invalid device node\n"); + return -ENODEV; + } + + fbmem_pnode = of_parse_phandle(pdev->dev.of_node, + "linux,contiguous-region", 0); + if (!fbmem_pnode) { + pr_debug("fbmem is not reserved for %s\n", pdev->name); + mfd->fbi->screen_base = NULL; + mfd->fbi->fix.smem_start = 0; + mfd->fbi->fix.smem_len = 0; + return 0; + } else { + const u32 *addr; + u64 len; + + addr = of_get_address(fbmem_pnode, 0, &len, NULL); + if (!addr) { + pr_err("fbmem size is not specified\n"); + of_node_put(fbmem_pnode); + return -EINVAL; + } + size = (size_t)len; + of_node_put(fbmem_pnode); + } + + pr_debug("%s frame buffer reserve_size=0x%zx\n", __func__, size); + + if (size < PAGE_ALIGN(mfd->fbi->fix.line_length * + mfd->fbi->var.yres_virtual)) + pr_warn("reserve size is smaller than framebuffer size\n"); + + virt = dma_alloc_coherent(&pdev->dev, size, &phys, GFP_KERNEL); + if (!virt) { + pr_err("unable to alloc fbmem size=%zx\n", size); + return -ENOMEM; + } + + if (MDSS_LPAE_CHECK(phys)) { + pr_warn("fb mem phys %pa > 4GB is not supported.\n", &phys); + dma_free_coherent(&pdev->dev, size, &virt, GFP_KERNEL); + return -ERANGE; + } + + rc = msm_iommu_map_contig_buffer(phys, dom, 0, size, SZ_4K, 0, + &mfd->iova); + if (rc) + pr_warn("Cannot map fb_mem %pa to IOMMU. rc=%d\n", &phys, rc); + + pr_debug("alloc 0x%zxB @ (%pa phys) (0x%p virt) (%pa iova) for fb%d\n", + size, &phys, virt, &mfd->iova, mfd->index); + + mfd->fbi->screen_base = virt; + mfd->fbi->fix.smem_start = phys; + mfd->fbi->fix.smem_len = size; + + return 0; +} + static int mdss_fb_alloc_fbmem(struct msm_fb_data_type *mfd) { if (mfd->mdp.fb_mem_alloc_fnc) { return mfd->mdp.fb_mem_alloc_fnc(mfd); + } else if (mfd->mdp.fb_mem_get_iommu_domain) { + int dom = mfd->mdp.fb_mem_get_iommu_domain(); + if (dom >= 0) + return mdss_fb_alloc_fbmem_iommu(mfd, dom); + else + return -ENOMEM; } else { pr_err("no fb memory allocator function defined\n"); return -ENOMEM; @@ -1814,7 +1952,8 @@ static int mdss_fb_release_all(struct fb_info *info, bool release_all) mfd->index, task->comm, pid); } - mdss_fb_free_fb_ion_memory(mfd); + if (mfd->fb_ion_handle) + mdss_fb_free_fb_ion_memory(mfd); ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info, mfd->op_enable); diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h index 1570af4292fa..f09cfc31a065 100644 --- a/drivers/video/fbdev/msm/mdss_fb.h +++ b/drivers/video/fbdev/msm/mdss_fb.h @@ -269,7 +269,6 @@ void mdss_fb_signal_timeline(struct msm_sync_pt_data *sync_pt_data); struct sync_fence *mdss_fb_sync_get_fence(struct sw_sync_timeline *timeline, const char *fence_name, int val); int mdss_fb_register_mdp_instance(struct msm_mdp_interface *mdp); -int mdss_fb_alloc_fb_ion_memory(struct msm_fb_data_type *mfd); int mdss_fb_dcm(struct msm_fb_data_type *mfd, int req_state); int mdss_fb_suspres_panel(struct device *dev, void *data); int mdss_fb_do_ioctl(struct fb_info *info, unsigned int cmd,