xref: /gfx-drm/usr/src/uts/common/io/drm/drm_pci.c (revision 47dc10d7)
1 /*
2  * Copyright (c) 2006, 2013, 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_pci.h
11  * \brief PCI consistent, DMA-accessible memory functions.
12  *
13  * \author Eric Anholt <anholt@FreeBSD.org>
14  */
15 
16 /*-
17  * Copyright 2003 Eric Anholt.
18  * All Rights Reserved.
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining a
21  * copy of this software and associated documentation files (the "Software"),
22  * to deal in the Software without restriction, including without limitation
23  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
24  * and/or sell copies of the Software, and to permit persons to whom the
25  * Software is furnished to do so, subject to the following conditions:
26  *
27  * The above copyright notice and this permission notice (including the next
28  * paragraph) shall be included in all copies or substantial portions of the
29  * Software.
30  *
31  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
34  * AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
35  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
36  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37  */
38 
39 /**********************************************************************/
40 /** \name PCI memory */
41 /*@{*/
42 
43 #include "drmP.h"
44 #include <vm/seg_kmem.h>
45 
46 typedef struct drm_pci_resource {
47 	uint_t regnum;
48 	unsigned long offset;
49 	unsigned long size;
50 } drm_pci_resource_t;
51 
52 void
drm_core_ioremap(struct drm_local_map * map,struct drm_device * dev)53 drm_core_ioremap(struct drm_local_map *map, struct drm_device *dev)
54 {
55 	if ((map->type == _DRM_AGP) && dev->agp) {
56 		/*
57 		 * During AGP mapping initialization, we map AGP aperture
58 		 * into kernel space. So, when we access the memory which
59 		 * managed by agp gart in kernel space, we have to go
60 		 * through two-level address translation: kernel virtual
61 		 * address --> aperture address --> physical address. For
62 		 * improving this, here in opensourced code, agp_remap()
63 		 * gets invoking to dispose the mapping between agp aperture
64 		 * and kernel space, and directly map the actual physical
65 		 * memory which is allocated to agp gart to kernel space.
66 		 * After that, access to physical memory managed by agp gart
67 		 * hardware in kernel space doesn't go through agp hardware,
68 		 * it will be: kernel virtual ---> physical address.
69 		 * Obviously, it is more efficient. But in Solaris operating
70 		 * system, the ioctl AGPIOC_ALLOCATE of agpgart driver does
71 		 * not return physical address. We are unable to create the
72 		 * direct mapping between kernel space and agp memory. So,
73 		 * we remove the calling to agp_remap().
74 		 */
75 		DRM_DEBUG("drm_core_ioremap: skipping agp_remap\n");
76 	} else {
77 		(void) drm_ioremap(dev, map);
78 
79 	}
80 }
81 
82 void
drm_core_ioremapfree(struct drm_local_map * map,struct drm_device * dev)83 drm_core_ioremapfree(struct drm_local_map *map, struct drm_device *dev)
84 {
85 	_NOTE(ARGUNUSED(dev))
86 
87 	if (map->type != _DRM_AGP) {
88 		if (map->handle && map->size)
89 			drm_ioremapfree(map);
90 	} else {
91 		/*
92 		 * Refer to the comments in drm_core_ioremap() where we removed
93 		 * the calling to agp_remap(), correspondingly, we remove the
94 		 * calling to agp_remap_free(dev, map);
95 		 */
96 		DRM_DEBUG("drm_core_ioremap: skipping agp_remap_free\n");
97 	}
98 }
99 
100 /*
101  * pci_alloc_consistent()
102  */
103 static ddi_dma_attr_t	hw_dma_attr = {
104 		DMA_ATTR_V0,		/* version */
105 		0,			/* addr_lo */
106 		0xffffffff,	/* addr_hi */
107 		0xffffffff,	/* count_max */
108 		4096, 			/* alignment */
109 		0xfff,			/* burstsize */
110 		1,			/* minxfer */
111 		0xffffffff,		/* maxxfer */
112 		0xffffffff,		/* seg */
113 		1,			/* sgllen */
114 		4,			/* granular */
115 		DDI_DMA_FLAGERR,	/* flags */
116 };
117 
118 static ddi_device_acc_attr_t hw_acc_attr = {
119 	DDI_DEVICE_ATTR_V0,
120 	DDI_NEVERSWAP_ACC,
121 	DDI_STRICTORDER_ACC,
122 	DDI_FLAGERR_ACC
123 };
124 
125 void *
drm_pci_alloc(struct drm_device * dev,size_t size,size_t align,dma_addr_t maxaddr,int segments)126 drm_pci_alloc(struct drm_device *dev, size_t size,
127     size_t align, dma_addr_t maxaddr, int segments)
128 {
129 	struct drm_dma_handle *dmah;
130 	uint_t count;
131 
132 	/* allocate continous physical memory for hw status page */
133 	hw_dma_attr.dma_attr_align = (!align) ? 1 : align;
134 
135 	hw_dma_attr.dma_attr_addr_hi = maxaddr;
136 	hw_dma_attr.dma_attr_sgllen = segments;
137 
138 	dmah = kmem_zalloc(sizeof(struct drm_dma_handle), KM_SLEEP);
139 
140 	if (ddi_dma_alloc_handle(dev->devinfo, &hw_dma_attr,
141 	    DDI_DMA_SLEEP, NULL, &dmah->dma_hdl) != DDI_SUCCESS) {
142 		DRM_ERROR("ddi_dma_alloc_handle() failed");
143 		goto err3;
144 	}
145 
146 	if (ddi_dma_mem_alloc(dmah->dma_hdl, size, &hw_acc_attr,
147 	    DDI_DMA_CONSISTENT | IOMEM_DATA_UNCACHED,
148 	    DDI_DMA_SLEEP, NULL, (caddr_t *)&dmah->vaddr,
149 	    &dmah->real_sz, &dmah->acc_hdl) != DDI_SUCCESS) {
150 		DRM_ERROR("ddi_dma_mem_alloc() failed\n");
151 		goto err2;
152 	}
153 
154 	if (ddi_dma_addr_bind_handle(dmah->dma_hdl, NULL,
155 	    (caddr_t)dmah->vaddr, dmah->real_sz,
156 	    DDI_DMA_RDWR|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
157 	    NULL, &dmah->cookie, &count) != DDI_DMA_MAPPED) {
158 		DRM_ERROR("ddi_dma_addr_bind_handle() failed");
159 		goto err1;
160 	}
161 
162 	if (count > segments) {
163 		(void) ddi_dma_unbind_handle(dmah->dma_hdl);
164 		goto err1;
165 	}
166 
167 	dmah->cookie_num = count;
168 	if (count == 1)
169 		dmah->paddr = dmah->cookie.dmac_address;
170 
171 	return (dmah);
172 
173 err1:
174 	ddi_dma_mem_free(&dmah->acc_hdl);
175 err2:
176 	ddi_dma_free_handle(&dmah->dma_hdl);
177 err3:
178 	kmem_free(dmah, sizeof (*dmah));
179 	return (NULL);
180 }
181 
182 void
drm_pci_free(drm_dma_handle_t * dmah)183 drm_pci_free(drm_dma_handle_t *dmah)
184 {
185 	ASSERT(dmah != NULL);
186 
187 	(void) ddi_dma_unbind_handle(dmah->dma_hdl);
188 	ddi_dma_mem_free(&dmah->acc_hdl);
189 	ddi_dma_free_handle(&dmah->dma_hdl);
190 	kmem_free(dmah, sizeof (drm_dma_handle_t));
191 }
192 
193 int
do_get_pci_res(struct drm_device * dev,drm_pci_resource_t * resp)194 do_get_pci_res(struct drm_device *dev, drm_pci_resource_t *resp)
195 {
196 	int length;
197 	pci_regspec_t	*regs;
198 
199 	if (ddi_getlongprop(
200 	    DDI_DEV_T_ANY, dev->devinfo, DDI_PROP_DONTPASS,
201 	    "assigned-addresses", (caddr_t)&regs, &length) !=
202 	    DDI_PROP_SUCCESS) {
203 		DRM_ERROR("do_get_pci_res: ddi_getlongprop failed!\n");
204 		return (EFAULT);
205 	}
206 	resp->offset =
207 	    (unsigned long)regs[resp->regnum].pci_phys_low;
208 	resp->size =
209 	    (unsigned long)regs[resp->regnum].pci_size_low;
210 	kmem_free(regs, (size_t)length);
211 
212 	return (0);
213 }
214 
215 unsigned long
drm_get_resource_start(struct drm_device * dev,unsigned int regnum)216 drm_get_resource_start(struct drm_device *dev, unsigned int regnum)
217 {
218 	drm_pci_resource_t res;
219 	int ret;
220 
221 	res.regnum = regnum;
222 
223 	ret = do_get_pci_res(dev, &res);
224 
225 	if (ret != 0) {
226 		DRM_ERROR("drm_get_resource_start: ioctl failed");
227 		return (0);
228 	}
229 
230 	return (res.offset);
231 
232 }
233 
234 unsigned long
drm_get_resource_len(struct drm_device * softstate,unsigned int regnum)235 drm_get_resource_len(struct drm_device *softstate, unsigned int regnum)
236 {
237 	drm_pci_resource_t res;
238 	int ret;
239 
240 	res.regnum = regnum;
241 
242 	ret = do_get_pci_res(softstate, &res);
243 
244 	if (ret != 0) {
245 		DRM_ERROR("drm_get_resource_len: ioctl failed");
246 		return (0);
247 	}
248 
249 	return (res.size);
250 }
251 
252