1 /*
2 * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
3 */
4
5 /*
6 * Copyright (c) 2012 Intel Corporation. All rights reserved.
7 */
8
9 /**
10 * \file drm_scatter.c
11 * IOCTLs to manage scatter/gather memory
12 *
13 * \author Gareth Hughes <gareth@valinux.com>
14 */
15
16 /*
17 * Created: Mon Dec 18 23:20:54 2000 by gareth@valinux.com
18 *
19 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
20 * All Rights Reserved.
21 *
22 * Permission is hereby granted, free of charge, to any person obtaining a
23 * copy of this software and associated documentation files (the "Software"),
24 * to deal in the Software without restriction, including without limitation
25 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
26 * and/or sell copies of the Software, and to permit persons to whom the
27 * Software is furnished to do so, subject to the following conditions:
28 *
29 * The above copyright notice and this permission notice (including the next
30 * paragraph) shall be included in all copies or substantial portions of the
31 * Software.
32 *
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
36 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
37 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
38 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
39 * DEALINGS IN THE SOFTWARE.
40 */
41
42 #include "drmP.h"
43 #include "drm_io32.h"
44
45 #define DEBUG_SCATTER 0
46
drm_sg_cleanup(struct drm_sg_mem * entry)47 void drm_sg_cleanup(struct drm_sg_mem *entry)
48 {
49 int pages = entry->pages;
50
51 if (entry->busaddr) {
52 kmem_free(entry->busaddr, sizeof (*entry->busaddr) * pages);
53 entry->busaddr = NULL;
54 }
55
56 ASSERT(entry->umem_cookie == NULL);
57
58 if (entry->dmah_sg) {
59 drm_pci_free(entry->dmah_sg);
60 entry->dmah_sg = NULL;
61 }
62
63 if (entry->dmah_gart) {
64 drm_pci_free(entry->dmah_gart);
65 entry->dmah_gart = NULL;
66 }
67
68 kfree(entry, sizeof (struct drm_sg_mem));
69 }
70
71 #ifdef _LP64
72 #define ScatterHandle(x) (unsigned int)((x >> 32) + (x & ((1L << 32) - 1)))
73 #else
74 #define ScatterHandle(x) (unsigned int)(x)
75 #endif
76
drm_sg_alloc(struct drm_device * dev,struct drm_scatter_gather * request)77 int drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather * request)
78 {
79 struct drm_sg_mem *entry;
80 unsigned long pages;
81 drm_dma_handle_t *dmah;
82
83 DRM_DEBUG("\n");
84
85 if (!drm_core_check_feature(dev, DRIVER_SG))
86 return -EINVAL;
87
88 if (dev->sg)
89 return -EINVAL;
90
91 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
92 if (!entry)
93 return -ENOMEM;
94
95 (void) memset(entry, 0, sizeof(*entry));
96 pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
97 DRM_DEBUG("size=%ld pages=%ld\n", request->size, pages);
98
99 entry->pages = (int)pages;
100 dmah = drm_pci_alloc(dev, ptob(pages), 4096, 0xfffffffful, pages);
101 if (dmah == NULL)
102 goto err_exit;
103 entry->busaddr = (void *)kmem_zalloc(sizeof (*entry->busaddr) *
104 pages, KM_SLEEP);
105
106 entry->handle = ScatterHandle((unsigned long)dmah->vaddr);
107 entry->virtual = (void *)dmah->vaddr;
108 request->handle = entry->handle;
109 entry->dmah_sg = dmah;
110
111 dev->sg = entry;
112
113 return 0;
114
115 err_exit:
116 drm_sg_cleanup(entry);
117 return -ENOMEM;
118 }
119
120 /* LINTED */
drm_sg_alloc_ioctl(DRM_IOCTL_ARGS)121 int drm_sg_alloc_ioctl(DRM_IOCTL_ARGS)
122 {
123 struct drm_scatter_gather *request = data;
124
125 return drm_sg_alloc(dev, request);
126
127 }
128
129 /* LINTED */
drm_sg_free(DRM_IOCTL_ARGS)130 int drm_sg_free(DRM_IOCTL_ARGS)
131 {
132 struct drm_scatter_gather *request = data;
133 struct drm_sg_mem *entry;
134
135 if (!drm_core_check_feature(dev, DRIVER_SG))
136 return -EINVAL;
137
138 entry = dev->sg;
139 dev->sg = NULL;
140
141 if (!entry || entry->handle != request->handle)
142 return -EINVAL;
143
144 DRM_DEBUG("virtual = %p\n", entry->virtual);
145
146 drm_sg_cleanup(entry);
147
148 return 0;
149 }
150