[go: nahoru, domu]

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
20struct adf_memblock_pdata {
21	phys_addr_t base;
22};
23
24static 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
51err_map:
52	sg_free_table(table);
53err_alloc:
54	kfree(table);
55	return ERR_PTR(ret);
56}
57
58static 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
65static 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
75static 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
88static 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
94static void adf_memblock_kunmap_atomic(struct dma_buf *buf,
95		unsigned long pgoffset, void *vaddr)
96{
97	kunmap_atomic(vaddr);
98}
99
100static void *adf_memblock_kmap(struct dma_buf *buf, unsigned long pgoffset)
101{
102	return adf_memblock_do_kmap(buf, pgoffset, false);
103}
104
105static void adf_memblock_kunmap(struct dma_buf *buf, unsigned long pgoffset,
106		void *vaddr)
107{
108	kunmap(vaddr);
109}
110
111static 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
119struct 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 */
141struct 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
146	if (PAGE_ALIGN(base) != base || PAGE_ALIGN(size) != size)
147		return ERR_PTR(-EINVAL);
148
149	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
150	if (!pdata)
151		return ERR_PTR(-ENOMEM);
152
153	pdata->base = base;
154	buf = dma_buf_export(pdata, &adf_memblock_ops, size, flags, NULL);
155	if (IS_ERR(buf))
156		kfree(pdata);
157
158	return buf;
159}
160EXPORT_SYMBOL(adf_memblock_export);
161