1 /*
2 * Copyright (c) 2015 Imre Vadász <imre@vdsz.com>
3 * Copyright (c) 2015 Rimvydas Jasinskas
4 * Copyright (c) 2018 François Tigeot <ftigeot@wolfpond.org>
5 *
6 * DRM Dragonfly-specific helper functions
7 *
8 * Permission to use, copy, modify, distribute, and sell this software and its
9 * documentation for any purpose is hereby granted without fee, provided that
10 * the above copyright notice appear in all copies and that both that copyright
11 * notice and this permission notice appear in supporting documentation, and
12 * that the name of the copyright holders not be used in advertising or
13 * publicity pertaining to distribution of the software without specific,
14 * written prior permission. The copyright holders make no representations
15 * about the suitability of this software for any purpose. It is provided "as
16 * is" without express or implied warranty.
17 *
18 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
19 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
20 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
21 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
22 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
23 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24 * OF THIS SOFTWARE.
25 */
26
27 #include <sys/libkern.h>
28 #include <sys/ctype.h>
29 #include <drm/drmP.h>
30
31 /*
32 * An implementation of fb_get_options()
33 * This can be used to set the video mode used for the syscons fb console,
34 * a la "video=..." in linux.
35 */
36 int
fb_get_options(const char * connector_name,char ** option)37 fb_get_options(const char *connector_name, char **option)
38 {
39 char buf[128], str[1024];
40
41 /*
42 * Where on linux one would use the command line option
43 * video=LVDS-1:<video-mode>, the corresponding tunable is
44 * drm.video.LVDS-1=<video-mode>.
45 * e.g. drm.video.LVDS-1=1024x768 sets the LVDS-1 connector to
46 * a 1024x768 video mode in the syscons framebuffer console.
47 * See https://wiki.archlinux.org/index.php/Kernel_mode_setting
48 * for an explanation of the video mode command line option.
49 */
50 memset(str, 0, sizeof(str));
51 ksnprintf(buf, sizeof(buf), "drm.video.%s", connector_name);
52 if (kgetenv_string(buf, str, sizeof(str)-1)) {
53 kprintf("found kenv %s=%s\n", buf, str);
54 *option = kstrdup(str, M_DRM);
55 return (0);
56 } else {
57 kprintf("tunable %s is not set\n", buf);
58 return (1);
59 }
60 }
61
62 /*
63 * Implement simplified version of kvasnprintf() for drm needs using
64 * M_DRM and kvsnprintf(). Since it is unclear what string size is
65 * optimal thus use of an actual length.
66 */
kvasprintf(int flags,const char * format,va_list ap)67 char *kvasprintf(int flags, const char *format, va_list ap)
68 {
69 char *str;
70 size_t size;
71 va_list aq;
72
73 va_copy(aq, ap);
74 size = kvsnprintf(NULL, 0, format, aq);
75 va_end(aq);
76
77 str = kmalloc(size+1, M_DRM, flags);
78 if (str == NULL)
79 return NULL;
80
81 kvsnprintf(str, size+1, format, ap);
82
83 return str;
84 }
85
86 /* mimic ksnprintf(), return pointer to char* and match drm api */
kasprintf(int flags,const char * format,...)87 char *kasprintf(int flags, const char *format, ...)
88 {
89 char *str;
90 va_list ap;
91
92 va_start(ap, format);
93 str = kvasprintf(flags, format, ap);
94 va_end(ap);
95
96 return str;
97 }
98
99 /*
100 * XXX pci glue logic helpers
101 * Should be done in drm_pci_init(), pending drm update.
102 * Assumes static runtime data.
103 * Only for usage in *_driver_[un]load()
104 */
105
drm_fill_pdev(device_t dev,struct pci_dev * pdev)106 static void drm_fill_pdev(device_t dev, struct pci_dev *pdev)
107 {
108 int msi_enable = 1;
109 u_int irq_flags;
110 int slot, func;
111
112 pdev->dev.bsddev = dev;
113 pdev->devfn = PCI_DEVFN(pci_get_slot(dev), pci_get_function(dev));
114 pdev->vendor = pci_get_vendor(dev);
115 pdev->device = pci_get_device(dev);
116 pdev->subsystem_vendor = pci_get_subvendor(dev);
117 pdev->subsystem_device = pci_get_subdevice(dev);
118
119 pdev->revision = pci_get_revid(dev) & 0xff;
120
121 pdev->_irq_type = pci_alloc_1intr(dev, msi_enable,
122 &pdev->_irqrid, &irq_flags);
123
124 pdev->_irqr = bus_alloc_resource_any(dev, SYS_RES_IRQ,
125 &pdev->_irqrid, irq_flags);
126 if (!pdev->_irqr)
127 return;
128
129 pdev->irq = (int)rman_get_start(pdev->_irqr);
130
131 slot = pci_get_slot(dev);
132 func = pci_get_function(dev);
133 pdev->devfn = PCI_DEVFN(slot, func);
134 }
135
drm_init_pdev(device_t dev,struct pci_dev ** pdev)136 void drm_init_pdev(device_t dev, struct pci_dev **pdev)
137 {
138 BUG_ON(*pdev != NULL);
139
140 *pdev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
141 drm_fill_pdev(dev, *pdev);
142
143 (*pdev)->bus = kzalloc(sizeof(struct pci_bus), GFP_KERNEL);
144 (*pdev)->bus->self = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
145
146 drm_fill_pdev(device_get_parent(dev), (*pdev)->bus->self);
147 (*pdev)->bus->number = pci_get_bus(dev);
148 }
149
drm_fini_pdev(struct pci_dev ** pdev)150 void drm_fini_pdev(struct pci_dev **pdev)
151 {
152 kfree((*pdev)->bus->self);
153 kfree((*pdev)->bus);
154
155 kfree(*pdev);
156 }
157
drm_print_pdev(struct pci_dev * pdev)158 void drm_print_pdev(struct pci_dev *pdev)
159 {
160 if (pdev == NULL) {
161 DRM_ERROR("pdev is null!\n");
162 return;
163 }
164
165 DRM_INFO("pdev: vendor=0x%04x device=0x%04x rev=0x%02x\n",
166 pdev->vendor, pdev->device, pdev->revision);
167 DRM_INFO(" svendor=0x%04x sdevice=0x%04x irq=%u\n",
168 pdev->subsystem_vendor, pdev->subsystem_device, pdev->irq);
169 }
170
171 #if 0
172 /* Allocation of PCI memory resources (framebuffer, registers, etc.) for
173 * drm_get_resource_*. Note that they are not RF_ACTIVE, so there's no virtual
174 * address for accessing them. Cleaned up at unload.
175 */
176 static int drm_alloc_resource(struct drm_device *dev, int resource)
177 {
178 struct resource *res;
179 int rid;
180
181 KKASSERT(lockstatus(&dev->struct_mutex, curthread) != 0);
182
183 if (resource >= DRM_MAX_PCI_RESOURCE) {
184 DRM_ERROR("Resource %d too large\n", resource);
185 return 1;
186 }
187
188 if (dev->pcir[resource] != NULL) {
189 return 0;
190 }
191
192 DRM_UNLOCK(dev);
193 rid = PCIR_BAR(resource);
194 res = bus_alloc_resource_any(dev->dev->bsddev, SYS_RES_MEMORY, &rid,
195 RF_SHAREABLE);
196 DRM_LOCK(dev);
197 if (res == NULL) {
198 DRM_ERROR("Couldn't find resource 0x%x\n", resource);
199 DRM_UNLOCK(dev);
200 return 1;
201 }
202
203 if (dev->pcir[resource] == NULL) {
204 dev->pcirid[resource] = rid;
205 dev->pcir[resource] = res;
206 }
207
208 return 0;
209 }
210
211 unsigned long drm_get_resource_start(struct drm_device *dev,
212 unsigned int resource)
213 {
214 if (drm_alloc_resource(dev, resource) != 0)
215 return 0;
216
217 return rman_get_start(dev->pcir[resource]);
218 }
219
220 unsigned long drm_get_resource_len(struct drm_device *dev,
221 unsigned int resource)
222 {
223 if (drm_alloc_resource(dev, resource) != 0)
224 return 0;
225
226 return rman_get_size(dev->pcir[resource]);
227 }
228 #endif
229
230 /* Former drm_release() in the legacy DragonFly BSD drm codebase */
drm_device_detach(device_t kdev)231 int drm_device_detach(device_t kdev)
232 {
233 struct drm_softc *softc = device_get_softc(kdev);
234 struct drm_device *dev = softc->drm_driver_data;
235
236 drm_sysctl_cleanup(dev);
237
238 #ifdef __DragonFly__
239 #if 0
240 /* Clean up PCI resources allocated by drm_bufs.c. We're not really
241 * worried about resource consumption while the DRM is inactive (between
242 * lastclose and firstopen or unload) because these aren't actually
243 * taking up KVA, just keeping the PCI resource allocated.
244 */
245 for (int i = 0; i < DRM_MAX_PCI_RESOURCE; i++) {
246 if (dev->pcir[i] == NULL)
247 continue;
248 bus_release_resource(dev->dev->bsddev, SYS_RES_MEMORY,
249 dev->pcirid[i], dev->pcir[i]);
250 dev->pcir[i] = NULL;
251 }
252 #endif
253
254 if (dev->agp) {
255 kfree(dev->agp);
256 dev->agp = NULL;
257 }
258
259 if (dev->driver->unload != NULL) {
260 DRM_LOCK(dev);
261 dev->driver->unload(dev);
262 DRM_UNLOCK(dev);
263 }
264
265 if (pci_disable_busmaster(dev->dev->bsddev))
266 DRM_ERROR("Request to disable bus-master failed.\n");
267
268 lockuninit(&dev->vbl_lock);
269 lockuninit(&dev->event_lock);
270 lockuninit(&dev->struct_mutex);
271 #endif
272
273 return 0;
274 }
275