]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/gpu/drm/msm/msm_iommu.c
Merge remote-tracking branches 'asoc/topic/adau1977', 'asoc/topic/ak4642', 'asoc...
[mirror_ubuntu-artful-kernel.git] / drivers / gpu / drm / msm / msm_iommu.c
CommitLineData
871d812a
RC
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "msm_drv.h"
19#include "msm_mmu.h"
20
21struct msm_iommu {
22 struct msm_mmu base;
23 struct iommu_domain *domain;
24};
25#define to_msm_iommu(x) container_of(x, struct msm_iommu, base)
26
27static int msm_fault_handler(struct iommu_domain *iommu, struct device *dev,
28 unsigned long iova, int flags, void *arg)
29{
30 DBG("*** fault: iova=%08lx, flags=%d", iova, flags);
87e956e9 31 return -ENOSYS;
871d812a
RC
32}
33
34static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, int cnt)
35{
36 struct drm_device *dev = mmu->dev;
37 struct msm_iommu *iommu = to_msm_iommu(mmu);
38 int i, ret;
39
40 for (i = 0; i < cnt; i++) {
41 struct device *msm_iommu_get_ctx(const char *ctx_name);
42 struct device *ctx = msm_iommu_get_ctx(names[i]);
87e956e9
SV
43 if (IS_ERR_OR_NULL(ctx)) {
44 dev_warn(dev->dev, "couldn't get %s context", names[i]);
871d812a 45 continue;
87e956e9 46 }
871d812a
RC
47 ret = iommu_attach_device(iommu->domain, ctx);
48 if (ret) {
49 dev_warn(dev->dev, "could not attach iommu to %s", names[i]);
50 return ret;
51 }
52 }
53
54 return 0;
55}
56
87e956e9
SV
57static void msm_iommu_detach(struct msm_mmu *mmu, const char **names, int cnt)
58{
59 struct msm_iommu *iommu = to_msm_iommu(mmu);
60 int i;
61
62 for (i = 0; i < cnt; i++) {
63 struct device *msm_iommu_get_ctx(const char *ctx_name);
64 struct device *ctx = msm_iommu_get_ctx(names[i]);
65 if (IS_ERR_OR_NULL(ctx))
66 continue;
67 iommu_detach_device(iommu->domain, ctx);
68 }
69}
70
871d812a
RC
71static int msm_iommu_map(struct msm_mmu *mmu, uint32_t iova,
72 struct sg_table *sgt, unsigned len, int prot)
73{
74 struct msm_iommu *iommu = to_msm_iommu(mmu);
75 struct iommu_domain *domain = iommu->domain;
76 struct scatterlist *sg;
77 unsigned int da = iova;
78 unsigned int i, j;
79 int ret;
80
81 if (!domain || !sgt)
82 return -EINVAL;
83
84 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
85 u32 pa = sg_phys(sg) - sg->offset;
86 size_t bytes = sg->length + sg->offset;
87
88 VERB("map[%d]: %08x %08x(%x)", i, iova, pa, bytes);
89
90 ret = iommu_map(domain, da, pa, bytes, prot);
91 if (ret)
92 goto fail;
93
94 da += bytes;
95 }
96
97 return 0;
98
99fail:
100 da = iova;
101
102 for_each_sg(sgt->sgl, sg, i, j) {
103 size_t bytes = sg->length + sg->offset;
104 iommu_unmap(domain, da, bytes);
105 da += bytes;
106 }
107 return ret;
108}
109
110static int msm_iommu_unmap(struct msm_mmu *mmu, uint32_t iova,
111 struct sg_table *sgt, unsigned len)
112{
113 struct msm_iommu *iommu = to_msm_iommu(mmu);
114 struct iommu_domain *domain = iommu->domain;
115 struct scatterlist *sg;
116 unsigned int da = iova;
117 int i;
118
119 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
120 size_t bytes = sg->length + sg->offset;
121 size_t unmapped;
122
123 unmapped = iommu_unmap(domain, da, bytes);
124 if (unmapped < bytes)
125 return unmapped;
126
127 VERB("unmap[%d]: %08x(%x)", i, iova, bytes);
128
cf3198c2 129 BUG_ON(!PAGE_ALIGNED(bytes));
871d812a
RC
130
131 da += bytes;
132 }
133
134 return 0;
135}
136
137static void msm_iommu_destroy(struct msm_mmu *mmu)
138{
139 struct msm_iommu *iommu = to_msm_iommu(mmu);
140 iommu_domain_free(iommu->domain);
141 kfree(iommu);
142}
143
144static const struct msm_mmu_funcs funcs = {
145 .attach = msm_iommu_attach,
87e956e9 146 .detach = msm_iommu_detach,
871d812a
RC
147 .map = msm_iommu_map,
148 .unmap = msm_iommu_unmap,
149 .destroy = msm_iommu_destroy,
150};
151
152struct msm_mmu *msm_iommu_new(struct drm_device *dev, struct iommu_domain *domain)
153{
154 struct msm_iommu *iommu;
155
156 iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
157 if (!iommu)
158 return ERR_PTR(-ENOMEM);
159
160 iommu->domain = domain;
161 msm_mmu_init(&iommu->base, dev, &funcs);
162 iommu_set_fault_handler(domain, msm_fault_handler, dev);
163
164 return &iommu->base;
165}