xref: /dragonfly/sys/dev/drm/drm_pci.c (revision a72de5ad)
1 /*
2  * Copyright 2003 José Fonseca.
3  * Copyright 2003 Leif Delgass.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
20  * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <linux/export.h>
26 #include <drm/drmP.h>
27 #include "drm_internal.h"
28 #include "drm_legacy.h"
29 
30 /**********************************************************************/
31 /** \name PCI memory */
32 /*@{*/
33 
34 static void
35 drm_pci_busdma_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
36 {
37 	drm_dma_handle_t *dmah = arg;
38 
39 	if (error != 0)
40 		return;
41 
42 	KASSERT(nsegs == 1, ("drm_pci_busdma_callback: bad dma segment count"));
43 	dmah->busaddr = segs[0].ds_addr;
44 }
45 
46 /**
47  * \brief Allocate a PCI consistent memory block, for DMA.
48  */
49 drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t align)
50 {
51 	drm_dma_handle_t *dmah;
52 	int ret;
53 
54 	/* pci_alloc_consistent only guarantees alignment to the smallest
55 	 * PAGE_SIZE order which is greater than or equal to the requested size.
56 	 * Return NULL here for now to make sure nobody tries for larger alignment
57 	 */
58 	if (align > size)
59 		return NULL;
60 
61 	/* Need power-of-two alignment, so fail the allocation if it isn't. */
62 	if ((align & (align - 1)) != 0) {
63 		DRM_ERROR("drm_pci_alloc with non-power-of-two alignment %d\n",
64 		    (int)align);
65 		return NULL;
66 	}
67 
68 	dmah = kmalloc(sizeof(drm_dma_handle_t), M_DRM, M_WAITOK | M_NULLOK);
69 	if (!dmah)
70 		return NULL;
71 
72 	dmah->size = size;
73 
74 	ret = bus_dma_tag_create(NULL, align, 0, /* tag, align, boundary */
75 	    ~0, BUS_SPACE_MAXADDR, /* lowaddr, highaddr */
76 	    NULL, NULL, /* filtfunc, filtfuncargs */
77 	    size, 1, size, /* maxsize, nsegs, maxsegsize */
78 	    0,		/* flags */
79 	    &dmah->tag);
80 	if (ret != 0) {
81 		kfree(dmah);
82 		return NULL;
83 	}
84 
85 	ret = bus_dmamem_alloc(dmah->tag, &dmah->vaddr,
86 	    BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_NOCACHE, &dmah->map);
87 	if (ret != 0) {
88 		bus_dma_tag_destroy(dmah->tag);
89 		kfree(dmah);
90 		return NULL;
91 	}
92 
93 	ret = bus_dmamap_load(dmah->tag, dmah->map, dmah->vaddr, size,
94 	    drm_pci_busdma_callback, dmah, BUS_DMA_NOWAIT);
95 	if (ret != 0) {
96 		bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map);
97 		bus_dma_tag_destroy(dmah->tag);
98 		kfree(dmah);
99 		return NULL;
100 	}
101 
102 	memset(dmah->vaddr, 0, size);
103 
104 	return dmah;
105 }
106 
107 /*
108  * Free a PCI consistent memory block without freeing its descriptor.
109  *
110  * This function is for internal use in the Linux-specific DRM core code.
111  */
112 void __drm_legacy_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah)
113 {
114 	if (dmah == NULL)
115 		return;
116 
117 	bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map);
118 	bus_dma_tag_destroy(dmah->tag);
119 }
120 
121 /**
122  * drm_pci_free - Free a PCI consistent memory block
123  * @dev: DRM device
124  * @dmah: handle to memory block
125  */
126 void drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah)
127 {
128 	__drm_legacy_pci_free(dev, dmah);
129 	kfree(dmah);
130 }
131 
132 int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask)
133 {
134 	device_t root;
135 	int pos;
136 	u32 lnkcap = 0, lnkcap2 = 0;
137 
138 	*mask = 0;
139 
140 	root = device_get_parent(dev->dev->bsddev);
141 
142 	/* we've been informed via and serverworks don't make the cut */
143 	if (pci_get_vendor(root) == PCI_VENDOR_ID_VIA ||
144 	    pci_get_vendor(root) == PCI_VENDOR_ID_SERVERWORKS)
145 		return -EINVAL;
146 
147 	pos = 0;
148 	pci_find_extcap(root, PCIY_EXPRESS, &pos);
149 	if (!pos)
150 		return -EINVAL;
151 
152 	lnkcap = pci_read_config(root, pos + PCIER_LINKCAP, 4);
153 	lnkcap2 = pci_read_config(root, pos + PCIER_LINK_CAP2, 4);
154 
155 	lnkcap &= PCIEM_LNKCAP_SPEED_MASK;
156 	lnkcap2 &= 0xfe;
157 
158 #define	PCI_EXP_LNKCAP_SLS_2_5GB	PCIEM_LNKCAP_SPEED_2_5
159 #define	PCI_EXP_LNKCAP_SLS_5_0GB	PCIEM_LNKCAP_SPEED_5
160 #define	PCI_EXP_LNKCAP2_SLS_2_5GB 0x02	/* Supported Link Speed 2.5GT/s */
161 #define	PCI_EXP_LNKCAP2_SLS_5_0GB 0x04	/* Supported Link Speed 5.0GT/s */
162 #define	PCI_EXP_LNKCAP2_SLS_8_0GB 0x08	/* Supported Link Speed 8.0GT/s */
163 
164 	if (lnkcap2) {	/* PCIe r3.0-compliant */
165 		if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB)
166 			*mask |= DRM_PCIE_SPEED_25;
167 		if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB)
168 			*mask |= DRM_PCIE_SPEED_50;
169 		if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB)
170 			*mask |= DRM_PCIE_SPEED_80;
171 	} else {	/* pre-r3.0 */
172 		if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB)
173 			*mask |= DRM_PCIE_SPEED_25;
174 		if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB)
175 			*mask |= DRM_PCIE_SPEED_50;
176 	}
177 
178 	DRM_INFO("probing gen 2 caps for device %x:%x = %x/%x\n", pci_get_vendor(root), pci_get_device(root), lnkcap, lnkcap2);
179 	return 0;
180 }
181 EXPORT_SYMBOL(drm_pcie_get_speed_cap_mask);
182 
183 #if 0
184 int drm_pcie_get_max_link_width(struct drm_device *dev, u32 *mlw)
185 {
186 	struct pci_dev *root;
187 	u32 lnkcap;
188 
189 	*mlw = 0;
190 	if (!dev->pdev)
191 		return -EINVAL;
192 
193 	root = dev->pdev->bus->self;
194 
195 	pcie_capability_read_dword(root, PCI_EXP_LNKCAP, &lnkcap);
196 
197 	*mlw = (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4;
198 
199 	DRM_INFO("probing mlw for device %x:%x = %x\n", root->vendor, root->device, lnkcap);
200 	return 0;
201 }
202 EXPORT_SYMBOL(drm_pcie_get_max_link_width);
203 #endif
204 
205 /**
206  * Get interrupt from bus id.
207  *
208  * \param inode device inode.
209  * \param file_priv DRM file private.
210  * \param cmd command.
211  * \param arg user argument, pointing to a drm_irq_busid structure.
212  * \return zero on success or a negative number on failure.
213  *
214  * Finds the PCI device with the specified bus id and gets its IRQ number.
215  * This IOCTL is deprecated, and will now return EINVAL for any busid not equal
216  * to that of the device that this DRM instance attached to.
217  */
218 int drm_irq_by_busid(struct drm_device *dev, void *data,
219 		     struct drm_file *file_priv)
220 {
221 	struct drm_irq_busid *irq = data;
222 
223 	if ((irq->busnum >> 8) != dev->pci_domain ||
224 	    (irq->busnum & 0xff) != dev->pci_bus ||
225 	    irq->devnum != dev->pci_slot ||
226 	    irq->funcnum != dev->pci_func)
227 		return -EINVAL;
228 
229 	irq->irq = dev->irq;
230 
231 	DRM_DEBUG("%d:%d:%d => IRQ %d\n",
232 	    irq->busnum, irq->devnum, irq->funcnum, irq->irq);
233 
234 	return 0;
235 }
236