Merge remote-tracking branch 'lsk/linux-linaro-lsk-v4.4-android' into linux-linaro...
[firefly-linux-kernel-4.4.55.git] / drivers / video / adf / adf_memblock.c
1 /*
2  * Copyright (C) 2013 Google, Inc.
3  *
4  * This software is licensed under the terms of the GNU General Public
5  * License version 2, as published by the Free Software Foundation, and
6  * may be copied, distributed, and modified under those terms.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  */
14
15 #include <linux/dma-buf.h>
16 #include <linux/highmem.h>
17 #include <linux/memblock.h>
18 #include <linux/slab.h>
19
20 struct adf_memblock_pdata {
21         phys_addr_t base;
22 };
23
24 static struct sg_table *adf_memblock_map(struct dma_buf_attachment *attach,
25                 enum dma_data_direction direction)
26 {
27         struct adf_memblock_pdata *pdata = attach->dmabuf->priv;
28         unsigned long pfn = PFN_DOWN(pdata->base);
29         struct page *page = pfn_to_page(pfn);
30         struct sg_table *table;
31         int nents, ret;
32
33         table = kzalloc(sizeof(*table), GFP_KERNEL);
34         if (!table)
35                 return ERR_PTR(-ENOMEM);
36
37         ret = sg_alloc_table(table, 1, GFP_KERNEL);
38         if (ret < 0)
39                 goto err_alloc;
40
41         sg_set_page(table->sgl, page, attach->dmabuf->size, 0);
42
43         nents = dma_map_sg(attach->dev, table->sgl, 1, direction);
44         if (!nents) {
45                 ret = -EINVAL;
46                 goto err_map;
47         }
48
49         return table;
50
51 err_map:
52         sg_free_table(table);
53 err_alloc:
54         kfree(table);
55         return ERR_PTR(ret);
56 }
57
58 static void adf_memblock_unmap(struct dma_buf_attachment *attach,
59                 struct sg_table *table, enum dma_data_direction direction)
60 {
61         dma_unmap_sg(attach->dev, table->sgl, 1, direction);
62         sg_free_table(table);
63 }
64
65 static void __init_memblock adf_memblock_release(struct dma_buf *buf)
66 {
67         struct adf_memblock_pdata *pdata = buf->priv;
68         int err = memblock_free(pdata->base, buf->size);
69
70         if (err < 0)
71                 pr_warn("%s: freeing memblock failed: %d\n", __func__, err);
72         kfree(pdata);
73 }
74
75 static void *adf_memblock_do_kmap(struct dma_buf *buf, unsigned long pgoffset,
76                 bool atomic)
77 {
78         struct adf_memblock_pdata *pdata = buf->priv;
79         unsigned long pfn = PFN_DOWN(pdata->base) + pgoffset;
80         struct page *page = pfn_to_page(pfn);
81
82         if (atomic)
83                 return kmap_atomic(page);
84         else
85                 return kmap(page);
86 }
87
88 static void *adf_memblock_kmap_atomic(struct dma_buf *buf,
89                 unsigned long pgoffset)
90 {
91         return adf_memblock_do_kmap(buf, pgoffset, true);
92 }
93
94 static void adf_memblock_kunmap_atomic(struct dma_buf *buf,
95                 unsigned long pgoffset, void *vaddr)
96 {
97         kunmap_atomic(vaddr);
98 }
99
100 static void *adf_memblock_kmap(struct dma_buf *buf, unsigned long pgoffset)
101 {
102         return adf_memblock_do_kmap(buf, pgoffset, false);
103 }
104
105 static void adf_memblock_kunmap(struct dma_buf *buf, unsigned long pgoffset,
106                 void *vaddr)
107 {
108         kunmap(vaddr);
109 }
110
111 static int adf_memblock_mmap(struct dma_buf *buf, struct vm_area_struct *vma)
112 {
113         struct adf_memblock_pdata *pdata = buf->priv;
114
115         return remap_pfn_range(vma, vma->vm_start, PFN_DOWN(pdata->base),
116                         vma->vm_end - vma->vm_start, vma->vm_page_prot);
117 }
118
119 struct dma_buf_ops adf_memblock_ops = {
120         .map_dma_buf = adf_memblock_map,
121         .unmap_dma_buf = adf_memblock_unmap,
122         .release = adf_memblock_release,
123         .kmap_atomic = adf_memblock_kmap_atomic,
124         .kunmap_atomic = adf_memblock_kunmap_atomic,
125         .kmap = adf_memblock_kmap,
126         .kunmap = adf_memblock_kunmap,
127         .mmap = adf_memblock_mmap,
128 };
129
130 /**
131  * adf_memblock_export - export a memblock reserved area as a dma-buf
132  *
133  * @base: base physical address
134  * @size: memblock size
135  * @flags: mode flags for the dma-buf's file
136  *
137  * @base and @size must be page-aligned.
138  *
139  * Returns a dma-buf on success or ERR_PTR(-errno) on failure.
140  */
141 struct dma_buf *adf_memblock_export(phys_addr_t base, size_t size, int flags)
142 {
143         struct adf_memblock_pdata *pdata;
144         struct dma_buf *buf;
145         DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
146
147         if (PAGE_ALIGN(base) != base || PAGE_ALIGN(size) != size)
148                 return ERR_PTR(-EINVAL);
149
150         pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
151         if (!pdata)
152                 return ERR_PTR(-ENOMEM);
153
154         pdata->base = base;
155         exp_info.ops = &adf_memblock_ops;
156         exp_info.size = size;
157         exp_info.flags = flags;
158         exp_info.priv = pdata;
159
160         buf = dma_buf_export(&exp_info);
161         if (IS_ERR(buf))
162                 kfree(pdata);
163
164         return buf;
165 }
166 EXPORT_SYMBOL(adf_memblock_export);