xref: /openbmc/linux/drivers/gpu/drm/drm_scatter.c (revision 45b2fda3d82d686cc014e4c485332f85e4bd64de)
1*45b2fda3SQian Cai /*
2c0e09200SDave Airlie  * \file drm_scatter.c
3c0e09200SDave Airlie  * IOCTLs to manage scatter/gather memory
4c0e09200SDave Airlie  *
5c0e09200SDave Airlie  * \author Gareth Hughes <gareth@valinux.com>
6c0e09200SDave Airlie  */
7c0e09200SDave Airlie 
8c0e09200SDave Airlie /*
9c0e09200SDave Airlie  * Created: Mon Dec 18 23:20:54 2000 by gareth@valinux.com
10c0e09200SDave Airlie  *
11c0e09200SDave Airlie  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
12c0e09200SDave Airlie  * All Rights Reserved.
13c0e09200SDave Airlie  *
14c0e09200SDave Airlie  * Permission is hereby granted, free of charge, to any person obtaining a
15c0e09200SDave Airlie  * copy of this software and associated documentation files (the "Software"),
16c0e09200SDave Airlie  * to deal in the Software without restriction, including without limitation
17c0e09200SDave Airlie  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18c0e09200SDave Airlie  * and/or sell copies of the Software, and to permit persons to whom the
19c0e09200SDave Airlie  * Software is furnished to do so, subject to the following conditions:
20c0e09200SDave Airlie  *
21c0e09200SDave Airlie  * The above copyright notice and this permission notice (including the next
22c0e09200SDave Airlie  * paragraph) shall be included in all copies or substantial portions of the
23c0e09200SDave Airlie  * Software.
24c0e09200SDave Airlie  *
25c0e09200SDave Airlie  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26c0e09200SDave Airlie  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27c0e09200SDave Airlie  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
28c0e09200SDave Airlie  * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
29c0e09200SDave Airlie  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
30c0e09200SDave Airlie  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31c0e09200SDave Airlie  * DEALINGS IN THE SOFTWARE.
32c0e09200SDave Airlie  */
33c0e09200SDave Airlie 
340500c04eSSam Ravnborg #include <linux/mm.h>
355a0e3ad6STejun Heo #include <linux/slab.h>
360500c04eSSam Ravnborg #include <linux/vmalloc.h>
370500c04eSSam Ravnborg 
380500c04eSSam Ravnborg #include <drm/drm.h>
390500c04eSSam Ravnborg #include <drm/drm_drv.h>
400500c04eSSam Ravnborg #include <drm/drm_print.h>
410500c04eSSam Ravnborg 
429ec4e2ffSDaniel Vetter #include "drm_legacy.h"
43c0e09200SDave Airlie 
44c0e09200SDave Airlie #define DEBUG_SCATTER 0
45c0e09200SDave Airlie 
46c0e09200SDave Airlie static inline void *drm_vmalloc_dma(unsigned long size)
47c0e09200SDave Airlie {
48c0e09200SDave Airlie #if defined(__powerpc__) && defined(CONFIG_NOT_COHERENT_CACHE)
4937035e74SBenjamin Herrenschmidt 	return __vmalloc(size, GFP_KERNEL, pgprot_noncached_wc(PAGE_KERNEL));
50c0e09200SDave Airlie #else
51c0e09200SDave Airlie 	return vmalloc_32(size);
52c0e09200SDave Airlie #endif
53c0e09200SDave Airlie }
54c0e09200SDave Airlie 
553d914e83SDaniel Vetter static void drm_sg_cleanup(struct drm_sg_mem * entry)
56c0e09200SDave Airlie {
57c0e09200SDave Airlie 	struct page *page;
58c0e09200SDave Airlie 	int i;
59c0e09200SDave Airlie 
60c0e09200SDave Airlie 	for (i = 0; i < entry->pages; i++) {
61c0e09200SDave Airlie 		page = entry->pagelist[i];
62c0e09200SDave Airlie 		if (page)
63c0e09200SDave Airlie 			ClearPageReserved(page);
64c0e09200SDave Airlie 	}
65c0e09200SDave Airlie 
66c0e09200SDave Airlie 	vfree(entry->virtual);
67c0e09200SDave Airlie 
689a298b2aSEric Anholt 	kfree(entry->busaddr);
699a298b2aSEric Anholt 	kfree(entry->pagelist);
709a298b2aSEric Anholt 	kfree(entry);
71c0e09200SDave Airlie }
72c0e09200SDave Airlie 
733d914e83SDaniel Vetter void drm_legacy_sg_cleanup(struct drm_device *dev)
743d914e83SDaniel Vetter {
753d914e83SDaniel Vetter 	if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg &&
76fa538645SDaniel Vetter 	    drm_core_check_feature(dev, DRIVER_LEGACY)) {
773d914e83SDaniel Vetter 		drm_sg_cleanup(dev->sg);
783d914e83SDaniel Vetter 		dev->sg = NULL;
793d914e83SDaniel Vetter 	}
803d914e83SDaniel Vetter }
81c0e09200SDave Airlie #ifdef _LP64
82c0e09200SDave Airlie # define ScatterHandle(x) (unsigned int)((x >> 32) + (x & ((1L << 32) - 1)))
83c0e09200SDave Airlie #else
84c0e09200SDave Airlie # define ScatterHandle(x) (unsigned int)(x)
85c0e09200SDave Airlie #endif
86c0e09200SDave Airlie 
879ec4e2ffSDaniel Vetter int drm_legacy_sg_alloc(struct drm_device *dev, void *data,
881d8d29cfSDaniel Vetter 			struct drm_file *file_priv)
89c0e09200SDave Airlie {
901d8d29cfSDaniel Vetter 	struct drm_scatter_gather *request = data;
91c0e09200SDave Airlie 	struct drm_sg_mem *entry;
92c0e09200SDave Airlie 	unsigned long pages, i, j;
93c0e09200SDave Airlie 
94c0e09200SDave Airlie 	DRM_DEBUG("\n");
95c0e09200SDave Airlie 
96fa538645SDaniel Vetter 	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
9769fdf420SChris Wilson 		return -EOPNOTSUPP;
988e194bbfSDaniel Vetter 
99c0e09200SDave Airlie 	if (!drm_core_check_feature(dev, DRIVER_SG))
10069fdf420SChris Wilson 		return -EOPNOTSUPP;
101c0e09200SDave Airlie 
102c0e09200SDave Airlie 	if (dev->sg)
103c0e09200SDave Airlie 		return -EINVAL;
104c0e09200SDave Airlie 
105f35119d6SRakib Mullick 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
106c0e09200SDave Airlie 	if (!entry)
107c0e09200SDave Airlie 		return -ENOMEM;
108c0e09200SDave Airlie 
109c0e09200SDave Airlie 	pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
110c0e09200SDave Airlie 	DRM_DEBUG("size=%ld pages=%ld\n", request->size, pages);
111c0e09200SDave Airlie 
112c0e09200SDave Airlie 	entry->pages = pages;
113f35119d6SRakib Mullick 	entry->pagelist = kcalloc(pages, sizeof(*entry->pagelist), GFP_KERNEL);
114c0e09200SDave Airlie 	if (!entry->pagelist) {
1159a298b2aSEric Anholt 		kfree(entry);
116c0e09200SDave Airlie 		return -ENOMEM;
117c0e09200SDave Airlie 	}
118c0e09200SDave Airlie 
119f35119d6SRakib Mullick 	entry->busaddr = kcalloc(pages, sizeof(*entry->busaddr), GFP_KERNEL);
120c0e09200SDave Airlie 	if (!entry->busaddr) {
1219a298b2aSEric Anholt 		kfree(entry->pagelist);
1229a298b2aSEric Anholt 		kfree(entry);
123c0e09200SDave Airlie 		return -ENOMEM;
124c0e09200SDave Airlie 	}
125c0e09200SDave Airlie 
126c0e09200SDave Airlie 	entry->virtual = drm_vmalloc_dma(pages << PAGE_SHIFT);
127c0e09200SDave Airlie 	if (!entry->virtual) {
1289a298b2aSEric Anholt 		kfree(entry->busaddr);
1299a298b2aSEric Anholt 		kfree(entry->pagelist);
1309a298b2aSEric Anholt 		kfree(entry);
131c0e09200SDave Airlie 		return -ENOMEM;
132c0e09200SDave Airlie 	}
133c0e09200SDave Airlie 
134c0e09200SDave Airlie 	/* This also forces the mapping of COW pages, so our page list
135c0e09200SDave Airlie 	 * will be valid.  Please don't remove it...
136c0e09200SDave Airlie 	 */
137c0e09200SDave Airlie 	memset(entry->virtual, 0, pages << PAGE_SHIFT);
138c0e09200SDave Airlie 
139c0e09200SDave Airlie 	entry->handle = ScatterHandle((unsigned long)entry->virtual);
140c0e09200SDave Airlie 
141c0e09200SDave Airlie 	DRM_DEBUG("handle  = %08lx\n", entry->handle);
142c0e09200SDave Airlie 	DRM_DEBUG("virtual = %p\n", entry->virtual);
143c0e09200SDave Airlie 
144c0e09200SDave Airlie 	for (i = (unsigned long)entry->virtual, j = 0; j < pages;
145c0e09200SDave Airlie 	     i += PAGE_SIZE, j++) {
146c0e09200SDave Airlie 		entry->pagelist[j] = vmalloc_to_page((void *)i);
147c0e09200SDave Airlie 		if (!entry->pagelist[j])
148c0e09200SDave Airlie 			goto failed;
149c0e09200SDave Airlie 		SetPageReserved(entry->pagelist[j]);
150c0e09200SDave Airlie 	}
151c0e09200SDave Airlie 
152c0e09200SDave Airlie 	request->handle = entry->handle;
153c0e09200SDave Airlie 
154c0e09200SDave Airlie 	dev->sg = entry;
155c0e09200SDave Airlie 
156c0e09200SDave Airlie #if DEBUG_SCATTER
157c0e09200SDave Airlie 	/* Verify that each page points to its virtual address, and vice
158c0e09200SDave Airlie 	 * versa.
159c0e09200SDave Airlie 	 */
160c0e09200SDave Airlie 	{
161c0e09200SDave Airlie 		int error = 0;
162c0e09200SDave Airlie 
163c0e09200SDave Airlie 		for (i = 0; i < pages; i++) {
164c0e09200SDave Airlie 			unsigned long *tmp;
165c0e09200SDave Airlie 
166c0e09200SDave Airlie 			tmp = page_address(entry->pagelist[i]);
167c0e09200SDave Airlie 			for (j = 0;
168c0e09200SDave Airlie 			     j < PAGE_SIZE / sizeof(unsigned long);
169c0e09200SDave Airlie 			     j++, tmp++) {
170c0e09200SDave Airlie 				*tmp = 0xcafebabe;
171c0e09200SDave Airlie 			}
172c0e09200SDave Airlie 			tmp = (unsigned long *)((u8 *) entry->virtual +
173c0e09200SDave Airlie 						(PAGE_SIZE * i));
174c0e09200SDave Airlie 			for (j = 0;
175c0e09200SDave Airlie 			     j < PAGE_SIZE / sizeof(unsigned long);
176c0e09200SDave Airlie 			     j++, tmp++) {
177c0e09200SDave Airlie 				if (*tmp != 0xcafebabe && error == 0) {
178c0e09200SDave Airlie 					error = 1;
179c0e09200SDave Airlie 					DRM_ERROR("Scatter allocation error, "
180c0e09200SDave Airlie 						  "pagelist does not match "
181c0e09200SDave Airlie 						  "virtual mapping\n");
182c0e09200SDave Airlie 				}
183c0e09200SDave Airlie 			}
184c0e09200SDave Airlie 			tmp = page_address(entry->pagelist[i]);
185c0e09200SDave Airlie 			for (j = 0;
186c0e09200SDave Airlie 			     j < PAGE_SIZE / sizeof(unsigned long);
187c0e09200SDave Airlie 			     j++, tmp++) {
188c0e09200SDave Airlie 				*tmp = 0;
189c0e09200SDave Airlie 			}
190c0e09200SDave Airlie 		}
191c0e09200SDave Airlie 		if (error == 0)
192c0e09200SDave Airlie 			DRM_ERROR("Scatter allocation matches pagelist\n");
193c0e09200SDave Airlie 	}
194c0e09200SDave Airlie #endif
195c0e09200SDave Airlie 
196c0e09200SDave Airlie 	return 0;
197c0e09200SDave Airlie 
198c0e09200SDave Airlie       failed:
199c0e09200SDave Airlie 	drm_sg_cleanup(entry);
200c0e09200SDave Airlie 	return -ENOMEM;
201c0e09200SDave Airlie }
202c0e09200SDave Airlie 
2039ec4e2ffSDaniel Vetter int drm_legacy_sg_free(struct drm_device *dev, void *data,
204c0e09200SDave Airlie 		       struct drm_file *file_priv)
205c0e09200SDave Airlie {
206c0e09200SDave Airlie 	struct drm_scatter_gather *request = data;
207c0e09200SDave Airlie 	struct drm_sg_mem *entry;
208c0e09200SDave Airlie 
209fa538645SDaniel Vetter 	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
21069fdf420SChris Wilson 		return -EOPNOTSUPP;
2118e194bbfSDaniel Vetter 
212c0e09200SDave Airlie 	if (!drm_core_check_feature(dev, DRIVER_SG))
21369fdf420SChris Wilson 		return -EOPNOTSUPP;
214c0e09200SDave Airlie 
215c0e09200SDave Airlie 	entry = dev->sg;
216c0e09200SDave Airlie 	dev->sg = NULL;
217c0e09200SDave Airlie 
218c0e09200SDave Airlie 	if (!entry || entry->handle != request->handle)
219c0e09200SDave Airlie 		return -EINVAL;
220c0e09200SDave Airlie 
221c0e09200SDave Airlie 	DRM_DEBUG("virtual  = %p\n", entry->virtual);
222c0e09200SDave Airlie 
223c0e09200SDave Airlie 	drm_sg_cleanup(entry);
224c0e09200SDave Airlie 
225c0e09200SDave Airlie 	return 0;
226c0e09200SDave Airlie }
227