aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/msm
diff options
context:
space:
mode:
authorRob Clark <robdclark@gmail.com>2013-11-16 12:56:06 -0500
committerRob Clark <robdclark@gmail.com>2014-01-09 14:38:58 -0500
commit871d812aa43e6350a4edf41bf7cb0879675255f1 (patch)
treea1700a8c806367a0f85b199f620aed2441277b8b /drivers/gpu/drm/msm
parentbf2b33afb9ea1d9609767c70562610a686bdfbd7 (diff)
drm/msm: add support for non-IOMMU systems
Add a VRAM carveout that is used for systems which do not have an IOMMU. The VRAM carveout uses CMA. The arch code must setup a CMA pool for the device (preferrably in highmem.. a 256m-512m VRAM pool in lowmem is not cool). The user can configure the VRAM pool size using msm.vram module param. Technically, the abstraction of IOMMU behind msm_mmu is not strictly needed, but it simplifies the GEM code a bit, and will be useful later when I add support for a2xx devices with GPUMMU, so I decided to keep this part. It appears to be possible to configure the GPU to restrict access to addresses within the VRAM pool, but this is not done yet. So for now the GPU will refuse to load if there is no sort of mmu. Once address based limits are supported and tested to confirm that we aren't giving the GPU access to arbitrary memory, this restriction can be lifted Signed-off-by: Rob Clark <robdclark@gmail.com>
Diffstat (limited to 'drivers/gpu/drm/msm')
-rw-r--r--drivers/gpu/drm/msm/Makefile1
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_gpu.c15
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.c13
-rw-r--r--drivers/gpu/drm/msm/mdp4/mdp4_kms.c29
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c87
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h21
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c170
-rw-r--r--drivers/gpu/drm/msm/msm_gem.h5
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c19
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.h2
-rw-r--r--drivers/gpu/drm/msm/msm_iommu.c148
-rw-r--r--drivers/gpu/drm/msm/msm_mmu.h47
12 files changed, 410 insertions, 147 deletions
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index e5fa12b0d21e..ca62457e0431 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -24,6 +24,7 @@ msm-y := \
24 msm_gem_prime.o \ 24 msm_gem_prime.o \
25 msm_gem_submit.o \ 25 msm_gem_submit.o \
26 msm_gpu.o \ 26 msm_gpu.o \
27 msm_iommu.o \
27 msm_ringbuffer.o 28 msm_ringbuffer.o
28 29
29msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o 30msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
index d9e72a60080c..16fe15d4f571 100644
--- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
@@ -426,7 +426,20 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
426 if (ret) 426 if (ret)
427 goto fail; 427 goto fail;
428 428
429 return &a3xx_gpu->base.base; 429 if (!gpu->mmu) {
430 /* TODO we think it is possible to configure the GPU to
431 * restrict access to VRAM carveout. But the required
432 * registers are unknown. For now just bail out and
433 * limp along with just modesetting. If it turns out
434 * to not be possible to restrict access, then we must
435 * implement a cmdstream validator.
436 */
437 dev_err(dev->dev, "No memory protection without IOMMU\n");
438 ret = -ENXIO;
439 goto fail;
440 }
441
442 return gpu;
430 443
431fail: 444fail:
432 if (a3xx_gpu) 445 if (a3xx_gpu)
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index d7bc51b385bf..3f1c7b27e33e 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -17,6 +17,7 @@
17 17
18#include "adreno_gpu.h" 18#include "adreno_gpu.h"
19#include "msm_gem.h" 19#include "msm_gem.h"
20#include "msm_mmu.h"
20 21
21struct adreno_info { 22struct adreno_info {
22 struct adreno_rev rev; 23 struct adreno_rev rev;
@@ -291,6 +292,7 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
291 struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs, 292 struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs,
292 struct adreno_rev rev) 293 struct adreno_rev rev)
293{ 294{
295 struct msm_mmu *mmu;
294 int i, ret; 296 int i, ret;
295 297
296 /* identify gpu: */ 298 /* identify gpu: */
@@ -338,10 +340,13 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
338 if (ret) 340 if (ret)
339 return ret; 341 return ret;
340 342
341 ret = msm_iommu_attach(drm, gpu->base.iommu, 343 mmu = gpu->base.mmu;
342 iommu_ports, ARRAY_SIZE(iommu_ports)); 344 if (mmu) {
343 if (ret) 345 ret = mmu->funcs->attach(mmu, iommu_ports,
344 return ret; 346 ARRAY_SIZE(iommu_ports));
347 if (ret)
348 return ret;
349 }
345 350
346 gpu->memptrs_bo = msm_gem_new(drm, sizeof(*gpu->memptrs), 351 gpu->memptrs_bo = msm_gem_new(drm, sizeof(*gpu->memptrs),
347 MSM_BO_UNCACHED); 352 MSM_BO_UNCACHED);
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp4/mdp4_kms.c
index 8972ac35a43d..bab8cbc6d798 100644
--- a/drivers/gpu/drm/msm/mdp4/mdp4_kms.c
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_kms.c
@@ -17,6 +17,7 @@
17 17
18 18
19#include "msm_drv.h" 19#include "msm_drv.h"
20#include "msm_mmu.h"
20#include "mdp4_kms.h" 21#include "mdp4_kms.h"
21 22
22static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev); 23static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev);
@@ -260,6 +261,7 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)
260 struct mdp4_platform_config *config = mdp4_get_config(pdev); 261 struct mdp4_platform_config *config = mdp4_get_config(pdev);
261 struct mdp4_kms *mdp4_kms; 262 struct mdp4_kms *mdp4_kms;
262 struct msm_kms *kms = NULL; 263 struct msm_kms *kms = NULL;
264 struct msm_mmu *mmu;
263 int ret; 265 int ret;
264 266
265 mdp4_kms = kzalloc(sizeof(*mdp4_kms), GFP_KERNEL); 267 mdp4_kms = kzalloc(sizeof(*mdp4_kms), GFP_KERNEL);
@@ -322,12 +324,6 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)
322 clk_set_rate(mdp4_kms->clk, config->max_clk); 324 clk_set_rate(mdp4_kms->clk, config->max_clk);
323 clk_set_rate(mdp4_kms->lut_clk, config->max_clk); 325 clk_set_rate(mdp4_kms->lut_clk, config->max_clk);
324 326
325 if (!config->iommu) {
326 dev_err(dev->dev, "no iommu\n");
327 ret = -ENXIO;
328 goto fail;
329 }
330
331 /* make sure things are off before attaching iommu (bootloader could 327 /* make sure things are off before attaching iommu (bootloader could
332 * have left things on, in which case we'll start getting faults if 328 * have left things on, in which case we'll start getting faults if
333 * we don't disable): 329 * we don't disable):
@@ -337,12 +333,23 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)
337 mdp4_write(mdp4_kms, REG_MDP4_DSI_ENABLE, 0); 333 mdp4_write(mdp4_kms, REG_MDP4_DSI_ENABLE, 0);
338 mdelay(16); 334 mdelay(16);
339 335
340 ret = msm_iommu_attach(dev, config->iommu, 336 if (config->iommu) {
341 iommu_ports, ARRAY_SIZE(iommu_ports)); 337 mmu = msm_iommu_new(dev, config->iommu);
342 if (ret) 338 if (IS_ERR(mmu)) {
343 goto fail; 339 ret = PTR_ERR(mmu);
340 goto fail;
341 }
342 ret = mmu->funcs->attach(mmu, iommu_ports,
343 ARRAY_SIZE(iommu_ports));
344 if (ret)
345 goto fail;
346 } else {
347 dev_info(dev->dev, "no iommu, fallback to phys "
348 "contig buffers for scanout\n");
349 mmu = NULL;
350 }
344 351
345 mdp4_kms->id = msm_register_iommu(dev, config->iommu); 352 mdp4_kms->id = msm_register_mmu(dev, mmu);
346 if (mdp4_kms->id < 0) { 353 if (mdp4_kms->id < 0) {
347 ret = mdp4_kms->id; 354 ret = mdp4_kms->id;
348 dev_err(dev->dev, "failed to register mdp4 iommu: %d\n", ret); 355 dev_err(dev->dev, "failed to register mdp4 iommu: %d\n", ret);
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 28b57eb6f9a1..a7f0c65ca655 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -30,50 +30,19 @@ static const struct drm_mode_config_funcs mode_config_funcs = {
30 .output_poll_changed = msm_fb_output_poll_changed, 30 .output_poll_changed = msm_fb_output_poll_changed,
31}; 31};
32 32
33static int msm_fault_handler(struct iommu_domain *iommu, struct device *dev, 33int msm_register_mmu(struct drm_device *dev, struct msm_mmu *mmu)
34 unsigned long iova, int flags, void *arg)
35{
36 DBG("*** fault: iova=%08lx, flags=%d", iova, flags);
37 return 0;
38}
39
40int msm_register_iommu(struct drm_device *dev, struct iommu_domain *iommu)
41{ 34{
42 struct msm_drm_private *priv = dev->dev_private; 35 struct msm_drm_private *priv = dev->dev_private;
43 int idx = priv->num_iommus++; 36 int idx = priv->num_mmus++;
44 37
45 if (WARN_ON(idx >= ARRAY_SIZE(priv->iommus))) 38 if (WARN_ON(idx >= ARRAY_SIZE(priv->mmus)))
46 return -EINVAL; 39 return -EINVAL;
47 40
48 priv->iommus[idx] = iommu; 41 priv->mmus[idx] = mmu;
49
50 iommu_set_fault_handler(iommu, msm_fault_handler, dev);
51
52 /* need to iommu_attach_device() somewhere?? on resume?? */
53 42
54 return idx; 43 return idx;
55} 44}
56 45
57int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu,
58 const char **names, int cnt)
59{
60 int i, ret;
61
62 for (i = 0; i < cnt; i++) {
63 /* TODO maybe some day msm iommu won't require this hack: */
64 struct device *msm_iommu_get_ctx(const char *ctx_name);
65 struct device *ctx = msm_iommu_get_ctx(names[i]);
66 if (!ctx)
67 continue;
68 ret = iommu_attach_device(iommu, ctx);
69 if (ret) {
70 dev_warn(dev->dev, "could not attach iommu to %s", names[i]);
71 return ret;
72 }
73 }
74 return 0;
75}
76
77#ifdef CONFIG_DRM_MSM_REGISTER_LOGGING 46#ifdef CONFIG_DRM_MSM_REGISTER_LOGGING
78static bool reglog = false; 47static bool reglog = false;
79MODULE_PARM_DESC(reglog, "Enable register read/write logging"); 48MODULE_PARM_DESC(reglog, "Enable register read/write logging");
@@ -82,6 +51,10 @@ module_param(reglog, bool, 0600);
82#define reglog 0 51#define reglog 0
83#endif 52#endif
84 53
54static char *vram;
55MODULE_PARM_DESC(vram, "Configure VRAM size (for devices without IOMMU/GPUMMU");
56module_param(vram, charp, 0);
57
85void __iomem *msm_ioremap(struct platform_device *pdev, const char *name, 58void __iomem *msm_ioremap(struct platform_device *pdev, const char *name,
86 const char *dbgname) 59 const char *dbgname)
87{ 60{
@@ -161,6 +134,14 @@ static int msm_unload(struct drm_device *dev)
161 mutex_unlock(&dev->struct_mutex); 134 mutex_unlock(&dev->struct_mutex);
162 } 135 }
163 136
137 if (priv->vram.paddr) {
138 DEFINE_DMA_ATTRS(attrs);
139 dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
140 drm_mm_takedown(&priv->vram.mm);
141 dma_free_attrs(dev->dev, priv->vram.size, NULL,
142 priv->vram.paddr, &attrs);
143 }
144
164 dev->dev_private = NULL; 145 dev->dev_private = NULL;
165 146
166 kfree(priv); 147 kfree(priv);
@@ -191,6 +172,41 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
191 172
192 drm_mode_config_init(dev); 173 drm_mode_config_init(dev);
193 174
175 /* if we have no IOMMU, then we need to use carveout allocator.
176 * Grab the entire CMA chunk carved out in early startup in
177 * mach-msm:
178 */
179 if (!iommu_present(&platform_bus_type)) {
180 DEFINE_DMA_ATTRS(attrs);
181 unsigned long size;
182 void *p;
183
184 DBG("using %s VRAM carveout", vram);
185 size = memparse(vram, NULL);
186 priv->vram.size = size;
187
188 drm_mm_init(&priv->vram.mm, 0, (size >> PAGE_SHIFT) - 1);
189
190 dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
191 dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
192
193 /* note that for no-kernel-mapping, the vaddr returned
194 * is bogus, but non-null if allocation succeeded:
195 */
196 p = dma_alloc_attrs(dev->dev, size,
197 &priv->vram.paddr, 0, &attrs);
198 if (!p) {
199 dev_err(dev->dev, "failed to allocate VRAM\n");
200 priv->vram.paddr = 0;
201 ret = -ENOMEM;
202 goto fail;
203 }
204
205 dev_info(dev->dev, "VRAM: %08x->%08x\n",
206 (uint32_t)priv->vram.paddr,
207 (uint32_t)(priv->vram.paddr + size));
208 }
209
194 kms = mdp4_kms_init(dev); 210 kms = mdp4_kms_init(dev);
195 if (IS_ERR(kms)) { 211 if (IS_ERR(kms)) {
196 /* 212 /*
@@ -778,6 +794,7 @@ static const struct dev_pm_ops msm_pm_ops = {
778 794
779static int msm_pdev_probe(struct platform_device *pdev) 795static int msm_pdev_probe(struct platform_device *pdev)
780{ 796{
797 pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
781 return drm_platform_init(&msm_driver, pdev); 798 return drm_platform_init(&msm_driver, pdev);
782} 799}
783 800
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index a4c52cfe7fca..1d22d87a49fb 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -53,6 +53,7 @@ static inline struct device *msm_iommu_get_ctx(const char *ctx_name)
53 53
54struct msm_kms; 54struct msm_kms;
55struct msm_gpu; 55struct msm_gpu;
56struct msm_mmu;
56 57
57#define NUM_DOMAINS 2 /* one for KMS, then one per gpu core (?) */ 58#define NUM_DOMAINS 2 /* one for KMS, then one per gpu core (?) */
58 59
@@ -85,9 +86,9 @@ struct msm_drm_private {
85 /* callbacks deferred until bo is inactive: */ 86 /* callbacks deferred until bo is inactive: */
86 struct list_head fence_cbs; 87 struct list_head fence_cbs;
87 88
88 /* registered IOMMU domains: */ 89 /* registered MMUs: */
89 unsigned int num_iommus; 90 unsigned int num_mmus;
90 struct iommu_domain *iommus[NUM_DOMAINS]; 91 struct msm_mmu *mmus[NUM_DOMAINS];
91 92
92 unsigned int num_planes; 93 unsigned int num_planes;
93 struct drm_plane *planes[8]; 94 struct drm_plane *planes[8];
@@ -103,6 +104,16 @@ struct msm_drm_private {
103 104
104 unsigned int num_connectors; 105 unsigned int num_connectors;
105 struct drm_connector *connectors[8]; 106 struct drm_connector *connectors[8];
107
108 /* VRAM carveout, used when no IOMMU: */
109 struct {
110 unsigned long size;
111 dma_addr_t paddr;
112 /* NOTE: mm managed at the page level, size is in # of pages
113 * and position mm_node->start is in # of pages:
114 */
115 struct drm_mm mm;
116 } vram;
106}; 117};
107 118
108struct msm_format { 119struct msm_format {
@@ -153,9 +164,7 @@ struct msm_kms {
153 164
154struct msm_kms *mdp4_kms_init(struct drm_device *dev); 165struct msm_kms *mdp4_kms_init(struct drm_device *dev);
155 166
156int msm_register_iommu(struct drm_device *dev, struct iommu_domain *iommu); 167int msm_register_mmu(struct drm_device *dev, struct msm_mmu *mmu);
157int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu,
158 const char **names, int cnt);
159 168
160int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence, 169int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
161 struct timespec *timeout); 170 struct timespec *timeout);
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index e587d251c590..d8d60c969ac7 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -22,7 +22,45 @@
22#include "msm_drv.h" 22#include "msm_drv.h"
23#include "msm_gem.h" 23#include "msm_gem.h"
24#include "msm_gpu.h" 24#include "msm_gpu.h"
25#include "msm_mmu.h"
25 26
27static dma_addr_t physaddr(struct drm_gem_object *obj)
28{
29 struct msm_gem_object *msm_obj = to_msm_bo(obj);
30 struct msm_drm_private *priv = obj->dev->dev_private;
31 return (((dma_addr_t)msm_obj->vram_node->start) << PAGE_SHIFT) +
32 priv->vram.paddr;
33}
34
35/* allocate pages from VRAM carveout, used when no IOMMU: */
36static struct page **get_pages_vram(struct drm_gem_object *obj,
37 int npages)
38{
39 struct msm_gem_object *msm_obj = to_msm_bo(obj);
40 struct msm_drm_private *priv = obj->dev->dev_private;
41 dma_addr_t paddr;
42 struct page **p;
43 int ret, i;
44
45 p = drm_malloc_ab(npages, sizeof(struct page *));
46 if (!p)
47 return ERR_PTR(-ENOMEM);
48
49 ret = drm_mm_insert_node(&priv->vram.mm, msm_obj->vram_node,
50 npages, 0, DRM_MM_SEARCH_DEFAULT);
51 if (ret) {
52 drm_free_large(p);
53 return ERR_PTR(ret);
54 }
55
56 paddr = physaddr(obj);
57 for (i = 0; i < npages; i++) {
58 p[i] = phys_to_page(paddr);
59 paddr += PAGE_SIZE;
60 }
61
62 return p;
63}
26 64
27/* called with dev->struct_mutex held */ 65/* called with dev->struct_mutex held */
28static struct page **get_pages(struct drm_gem_object *obj) 66static struct page **get_pages(struct drm_gem_object *obj)
@@ -31,9 +69,14 @@ static struct page **get_pages(struct drm_gem_object *obj)
31 69
32 if (!msm_obj->pages) { 70 if (!msm_obj->pages) {
33 struct drm_device *dev = obj->dev; 71 struct drm_device *dev = obj->dev;
34 struct page **p = drm_gem_get_pages(obj, 0); 72 struct page **p;
35 int npages = obj->size >> PAGE_SHIFT; 73 int npages = obj->size >> PAGE_SHIFT;
36 74
75 if (iommu_present(&platform_bus_type))
76 p = drm_gem_get_pages(obj, 0);
77 else
78 p = get_pages_vram(obj, npages);
79
37 if (IS_ERR(p)) { 80 if (IS_ERR(p)) {
38 dev_err(dev->dev, "could not get pages: %ld\n", 81 dev_err(dev->dev, "could not get pages: %ld\n",
39 PTR_ERR(p)); 82 PTR_ERR(p));
@@ -73,7 +116,11 @@ static void put_pages(struct drm_gem_object *obj)
73 sg_free_table(msm_obj->sgt); 116 sg_free_table(msm_obj->sgt);
74 kfree(msm_obj->sgt); 117 kfree(msm_obj->sgt);
75 118
76 drm_gem_put_pages(obj, msm_obj->pages, true, false); 119 if (iommu_present(&platform_bus_type))
120 drm_gem_put_pages(obj, msm_obj->pages, true, false);
121 else
122 drm_mm_remove_node(msm_obj->vram_node);
123
77 msm_obj->pages = NULL; 124 msm_obj->pages = NULL;
78 } 125 }
79} 126}
@@ -138,7 +185,6 @@ int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
138int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 185int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
139{ 186{
140 struct drm_gem_object *obj = vma->vm_private_data; 187 struct drm_gem_object *obj = vma->vm_private_data;
141 struct msm_gem_object *msm_obj = to_msm_bo(obj);
142 struct drm_device *dev = obj->dev; 188 struct drm_device *dev = obj->dev;
143 struct page **pages; 189 struct page **pages;
144 unsigned long pfn; 190 unsigned long pfn;
@@ -163,7 +209,7 @@ int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
163 pgoff = ((unsigned long)vmf->virtual_address - 209 pgoff = ((unsigned long)vmf->virtual_address -
164 vma->vm_start) >> PAGE_SHIFT; 210 vma->vm_start) >> PAGE_SHIFT;
165 211
166 pfn = page_to_pfn(msm_obj->pages[pgoff]); 212 pfn = page_to_pfn(pages[pgoff]);
167 213
168 VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address, 214 VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address,
169 pfn, pfn << PAGE_SHIFT); 215 pfn, pfn << PAGE_SHIFT);
@@ -219,67 +265,6 @@ uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj)
219 return offset; 265 return offset;
220} 266}
221 267
222/* helpers for dealing w/ iommu: */
223static int map_range(struct iommu_domain *domain, unsigned int iova,
224 struct sg_table *sgt, unsigned int len, int prot)
225{
226 struct scatterlist *sg;
227 unsigned int da = iova;
228 unsigned int i, j;
229 int ret;
230
231 if (!domain || !sgt)
232 return -EINVAL;
233
234 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
235 u32 pa = sg_phys(sg) - sg->offset;
236 size_t bytes = sg->length + sg->offset;
237
238 VERB("map[%d]: %08x %08x(%x)", i, iova, pa, bytes);
239
240 ret = iommu_map(domain, da, pa, bytes, prot);
241 if (ret)
242 goto fail;
243
244 da += bytes;
245 }
246
247 return 0;
248
249fail:
250 da = iova;
251
252 for_each_sg(sgt->sgl, sg, i, j) {
253 size_t bytes = sg->length + sg->offset;
254 iommu_unmap(domain, da, bytes);
255 da += bytes;
256 }
257 return ret;
258}
259
260static void unmap_range(struct iommu_domain *domain, unsigned int iova,
261 struct sg_table *sgt, unsigned int len)
262{
263 struct scatterlist *sg;
264 unsigned int da = iova;
265 int i;
266
267 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
268 size_t bytes = sg->length + sg->offset;
269 size_t unmapped;
270
271 unmapped = iommu_unmap(domain, da, bytes);
272 if (unmapped < bytes)
273 break;
274
275 VERB("unmap[%d]: %08x(%x)", i, iova, bytes);
276
277 BUG_ON(!IS_ALIGNED(bytes, PAGE_SIZE));
278
279 da += bytes;
280 }
281}
282
283/* should be called under struct_mutex.. although it can be called 268/* should be called under struct_mutex.. although it can be called
284 * from atomic context without struct_mutex to acquire an extra 269 * from atomic context without struct_mutex to acquire an extra
285 * iova ref if you know one is already held. 270 * iova ref if you know one is already held.
@@ -295,15 +280,20 @@ int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id,
295 280
296 if (!msm_obj->domain[id].iova) { 281 if (!msm_obj->domain[id].iova) {
297 struct msm_drm_private *priv = obj->dev->dev_private; 282 struct msm_drm_private *priv = obj->dev->dev_private;
298 uint32_t offset = (uint32_t)mmap_offset(obj); 283 struct msm_mmu *mmu = priv->mmus[id];
299 struct page **pages; 284 struct page **pages = get_pages(obj);
300 pages = get_pages(obj); 285
301 if (IS_ERR(pages)) 286 if (IS_ERR(pages))
302 return PTR_ERR(pages); 287 return PTR_ERR(pages);
303 // XXX ideally we would not map buffers writable when not needed... 288
304 ret = map_range(priv->iommus[id], offset, msm_obj->sgt, 289 if (iommu_present(&platform_bus_type)) {
305 obj->size, IOMMU_READ | IOMMU_WRITE); 290 uint32_t offset = (uint32_t)mmap_offset(obj);
306 msm_obj->domain[id].iova = offset; 291 ret = mmu->funcs->map(mmu, offset, msm_obj->sgt,
292 obj->size, IOMMU_READ | IOMMU_WRITE);
293 msm_obj->domain[id].iova = offset;
294 } else {
295 msm_obj->domain[id].iova = physaddr(obj);
296 }
307 } 297 }
308 298
309 if (!ret) 299 if (!ret)
@@ -514,6 +504,7 @@ void msm_gem_describe_objects(struct list_head *list, struct seq_file *m)
514void msm_gem_free_object(struct drm_gem_object *obj) 504void msm_gem_free_object(struct drm_gem_object *obj)
515{ 505{
516 struct drm_device *dev = obj->dev; 506 struct drm_device *dev = obj->dev;
507 struct msm_drm_private *priv = obj->dev->dev_private;
517 struct msm_gem_object *msm_obj = to_msm_bo(obj); 508 struct msm_gem_object *msm_obj = to_msm_bo(obj);
518 int id; 509 int id;
519 510
@@ -525,11 +516,10 @@ void msm_gem_free_object(struct drm_gem_object *obj)
525 list_del(&msm_obj->mm_list); 516 list_del(&msm_obj->mm_list);
526 517
527 for (id = 0; id < ARRAY_SIZE(msm_obj->domain); id++) { 518 for (id = 0; id < ARRAY_SIZE(msm_obj->domain); id++) {
528 if (msm_obj->domain[id].iova) { 519 struct msm_mmu *mmu = priv->mmus[id];
529 struct msm_drm_private *priv = obj->dev->dev_private; 520 if (mmu && msm_obj->domain[id].iova) {
530 uint32_t offset = (uint32_t)mmap_offset(obj); 521 uint32_t offset = (uint32_t)mmap_offset(obj);
531 unmap_range(priv->iommus[id], offset, 522 mmu->funcs->unmap(mmu, offset, msm_obj->sgt, obj->size);
532 msm_obj->sgt, obj->size);
533 } 523 }
534 } 524 }
535 525
@@ -591,6 +581,7 @@ static int msm_gem_new_impl(struct drm_device *dev,
591{ 581{
592 struct msm_drm_private *priv = dev->dev_private; 582 struct msm_drm_private *priv = dev->dev_private;
593 struct msm_gem_object *msm_obj; 583 struct msm_gem_object *msm_obj;
584 unsigned sz;
594 585
595 switch (flags & MSM_BO_CACHE_MASK) { 586 switch (flags & MSM_BO_CACHE_MASK) {
596 case MSM_BO_UNCACHED: 587 case MSM_BO_UNCACHED:
@@ -603,10 +594,17 @@ static int msm_gem_new_impl(struct drm_device *dev,
603 return -EINVAL; 594 return -EINVAL;
604 } 595 }
605 596
606 msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL); 597 sz = sizeof(*msm_obj);
598 if (!iommu_present(&platform_bus_type))
599 sz += sizeof(struct drm_mm_node);
600
601 msm_obj = kzalloc(sz, GFP_KERNEL);
607 if (!msm_obj) 602 if (!msm_obj)
608 return -ENOMEM; 603 return -ENOMEM;
609 604
605 if (!iommu_present(&platform_bus_type))
606 msm_obj->vram_node = (void *)&msm_obj[1];
607
610 msm_obj->flags = flags; 608 msm_obj->flags = flags;
611 609
612 msm_obj->resv = &msm_obj->_resv; 610 msm_obj->resv = &msm_obj->_resv;
@@ -623,7 +621,7 @@ static int msm_gem_new_impl(struct drm_device *dev,
623struct drm_gem_object *msm_gem_new(struct drm_device *dev, 621struct drm_gem_object *msm_gem_new(struct drm_device *dev,
624 uint32_t size, uint32_t flags) 622 uint32_t size, uint32_t flags)
625{ 623{
626 struct drm_gem_object *obj; 624 struct drm_gem_object *obj = NULL;
627 int ret; 625 int ret;
628 626
629 WARN_ON(!mutex_is_locked(&dev->struct_mutex)); 627 WARN_ON(!mutex_is_locked(&dev->struct_mutex));
@@ -634,9 +632,13 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev,
634 if (ret) 632 if (ret)
635 goto fail; 633 goto fail;
636 634
637 ret = drm_gem_object_init(dev, obj, size); 635 if (iommu_present(&platform_bus_type)) {
638 if (ret) 636 ret = drm_gem_object_init(dev, obj, size);
639 goto fail; 637 if (ret)
638 goto fail;
639 } else {
640 drm_gem_private_object_init(dev, obj, size);
641 }
640 642
641 return obj; 643 return obj;
642 644
@@ -654,6 +656,12 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev,
654 struct drm_gem_object *obj; 656 struct drm_gem_object *obj;
655 int ret, npages; 657 int ret, npages;
656 658
659 /* if we don't have IOMMU, don't bother pretending we can import: */
660 if (!iommu_present(&platform_bus_type)) {
661 dev_err(dev->dev, "cannot import without IOMMU\n");
662 return ERR_PTR(-EINVAL);
663 }
664
657 size = PAGE_ALIGN(size); 665 size = PAGE_ALIGN(size);
658 666
659 ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj); 667 ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj);
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index f4f23a578d9d..3246bb46c4f2 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -57,6 +57,11 @@ struct msm_gem_object {
57 /* normally (resv == &_resv) except for imported bo's */ 57 /* normally (resv == &_resv) except for imported bo's */
58 struct reservation_object *resv; 58 struct reservation_object *resv;
59 struct reservation_object _resv; 59 struct reservation_object _resv;
60
61 /* For physically contiguous buffers. Used when we don't have
62 * an IOMMU.
63 */
64 struct drm_mm_node *vram_node;
60}; 65};
61#define to_msm_bo(x) container_of(x, struct msm_gem_object, base) 66#define to_msm_bo(x) container_of(x, struct msm_gem_object, base)
62 67
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 71f105f0d897..4ebce8be489d 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -17,6 +17,7 @@
17 17
18#include "msm_gpu.h" 18#include "msm_gpu.h"
19#include "msm_gem.h" 19#include "msm_gem.h"
20#include "msm_mmu.h"
20 21
21 22
22/* 23/*
@@ -353,6 +354,7 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
353 struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs, 354 struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs,
354 const char *name, const char *ioname, const char *irqname, int ringsz) 355 const char *name, const char *ioname, const char *irqname, int ringsz)
355{ 356{
357 struct iommu_domain *iommu;
356 int i, ret; 358 int i, ret;
357 359
358 gpu->dev = drm; 360 gpu->dev = drm;
@@ -418,13 +420,14 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
418 * and have separate page tables per context. For now, to keep things 420 * and have separate page tables per context. For now, to keep things
419 * simple and to get something working, just use a single address space: 421 * simple and to get something working, just use a single address space:
420 */ 422 */
421 gpu->iommu = iommu_domain_alloc(&platform_bus_type); 423 iommu = iommu_domain_alloc(&platform_bus_type);
422 if (!gpu->iommu) { 424 if (iommu) {
423 dev_err(drm->dev, "failed to allocate IOMMU\n"); 425 dev_info(drm->dev, "%s: using IOMMU\n", name);
424 ret = -ENOMEM; 426 gpu->mmu = msm_iommu_new(drm, iommu);
425 goto fail; 427 } else {
428 dev_info(drm->dev, "%s: no IOMMU, fallback to VRAM carveout!\n", name);
426 } 429 }
427 gpu->id = msm_register_iommu(drm, gpu->iommu); 430 gpu->id = msm_register_mmu(drm, gpu->mmu);
428 431
429 /* Create ringbuffer: */ 432 /* Create ringbuffer: */
430 gpu->rb = msm_ringbuffer_new(gpu, ringsz); 433 gpu->rb = msm_ringbuffer_new(gpu, ringsz);
@@ -464,6 +467,6 @@ void msm_gpu_cleanup(struct msm_gpu *gpu)
464 msm_ringbuffer_destroy(gpu->rb); 467 msm_ringbuffer_destroy(gpu->rb);
465 } 468 }
466 469
467 if (gpu->iommu) 470 if (gpu->mmu)
468 iommu_domain_free(gpu->iommu); 471 gpu->mmu->funcs->destroy(gpu->mmu);
469} 472}
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index 08d08420cbcc..458db8c64c28 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -78,7 +78,7 @@ struct msm_gpu {
78 void __iomem *mmio; 78 void __iomem *mmio;
79 int irq; 79 int irq;
80 80
81 struct iommu_domain *iommu; 81 struct msm_mmu *mmu;
82 int id; 82 int id;
83 83
84 /* Power Control: */ 84 /* Power Control: */
diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c
new file mode 100644
index 000000000000..014a3fd04f62
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_iommu.c
@@ -0,0 +1,148 @@
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);
31 return 0;
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]);
43 if (!ctx)
44 continue;
45 ret = iommu_attach_device(iommu->domain, ctx);
46 if (ret) {
47 dev_warn(dev->dev, "could not attach iommu to %s", names[i]);
48 return ret;
49 }
50 }
51
52 return 0;
53}
54
55static int msm_iommu_map(struct msm_mmu *mmu, uint32_t iova,
56 struct sg_table *sgt, unsigned len, int prot)
57{
58 struct msm_iommu *iommu = to_msm_iommu(mmu);
59 struct iommu_domain *domain = iommu->domain;
60 struct scatterlist *sg;
61 unsigned int da = iova;
62 unsigned int i, j;
63 int ret;
64
65 if (!domain || !sgt)
66 return -EINVAL;
67
68 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
69 u32 pa = sg_phys(sg) - sg->offset;
70 size_t bytes = sg->length + sg->offset;
71
72 VERB("map[%d]: %08x %08x(%x)", i, iova, pa, bytes);
73
74 ret = iommu_map(domain, da, pa, bytes, prot);
75 if (ret)
76 goto fail;
77
78 da += bytes;
79 }
80
81 return 0;
82
83fail:
84 da = iova;
85
86 for_each_sg(sgt->sgl, sg, i, j) {
87 size_t bytes = sg->length + sg->offset;
88 iommu_unmap(domain, da, bytes);
89 da += bytes;
90 }
91 return ret;
92}
93
94static int msm_iommu_unmap(struct msm_mmu *mmu, uint32_t iova,
95 struct sg_table *sgt, unsigned len)
96{
97 struct msm_iommu *iommu = to_msm_iommu(mmu);
98 struct iommu_domain *domain = iommu->domain;
99 struct scatterlist *sg;
100 unsigned int da = iova;
101 int i;
102
103 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
104 size_t bytes = sg->length + sg->offset;
105 size_t unmapped;
106
107 unmapped = iommu_unmap(domain, da, bytes);
108 if (unmapped < bytes)
109 return unmapped;
110
111 VERB("unmap[%d]: %08x(%x)", i, iova, bytes);
112
113 BUG_ON(!IS_ALIGNED(bytes, PAGE_SIZE));
114
115 da += bytes;
116 }
117
118 return 0;
119}
120
121static void msm_iommu_destroy(struct msm_mmu *mmu)
122{
123 struct msm_iommu *iommu = to_msm_iommu(mmu);
124 iommu_domain_free(iommu->domain);
125 kfree(iommu);
126}
127
128static const struct msm_mmu_funcs funcs = {
129 .attach = msm_iommu_attach,
130 .map = msm_iommu_map,
131 .unmap = msm_iommu_unmap,
132 .destroy = msm_iommu_destroy,
133};
134
135struct msm_mmu *msm_iommu_new(struct drm_device *dev, struct iommu_domain *domain)
136{
137 struct msm_iommu *iommu;
138
139 iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
140 if (!iommu)
141 return ERR_PTR(-ENOMEM);
142
143 iommu->domain = domain;
144 msm_mmu_init(&iommu->base, dev, &funcs);
145 iommu_set_fault_handler(domain, msm_fault_handler, dev);
146
147 return &iommu->base;
148}
diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h
new file mode 100644
index 000000000000..030324482b4a
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_mmu.h
@@ -0,0 +1,47 @@
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#ifndef __MSM_MMU_H__
19#define __MSM_MMU_H__
20
21#include <linux/iommu.h>
22
23struct msm_mmu_funcs {
24 int (*attach)(struct msm_mmu *mmu, const char **names, int cnt);
25 int (*map)(struct msm_mmu *mmu, uint32_t iova, struct sg_table *sgt,
26 unsigned len, int prot);
27 int (*unmap)(struct msm_mmu *mmu, uint32_t iova, struct sg_table *sgt,
28 unsigned len);
29 void (*destroy)(struct msm_mmu *mmu);
30};
31
32struct msm_mmu {
33 const struct msm_mmu_funcs *funcs;
34 struct drm_device *dev;
35};
36
37static inline void msm_mmu_init(struct msm_mmu *mmu, struct drm_device *dev,
38 const struct msm_mmu_funcs *funcs)
39{
40 mmu->dev = dev;
41 mmu->funcs = funcs;
42}
43
44struct msm_mmu *msm_iommu_new(struct drm_device *dev, struct iommu_domain *domain);
45struct msm_mmu *msm_gpummu_new(struct drm_device *dev, struct msm_gpu *gpu);
46
47#endif /* __MSM_MMU_H__ */