drm: fix a bug in ttm_bo_add_to_lru()
[dragonfly.git] / sys / dev / drm / drm_dragonfly.c
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
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  */
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 */
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
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
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
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
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 /* Allocation of PCI memory resources (framebuffer, registers, etc.) for
172  * drm_get_resource_*.  Note that they are not RF_ACTIVE, so there's no virtual
173  * address for accessing them.  Cleaned up at unload.
174  */
175 static int drm_alloc_resource(struct drm_device *dev, int resource)
176 {
177         struct resource *res;
178         int rid;
179
180         KKASSERT(lockstatus(&dev->struct_mutex, curthread) != 0);
181
182         if (resource >= DRM_MAX_PCI_RESOURCE) {
183                 DRM_ERROR("Resource %d too large\n", resource);
184                 return 1;
185         }
186
187         if (dev->pcir[resource] != NULL) {
188                 return 0;
189         }
190
191         DRM_UNLOCK(dev);
192         rid = PCIR_BAR(resource);
193         res = bus_alloc_resource_any(dev->dev->bsddev, SYS_RES_MEMORY, &rid,
194             RF_SHAREABLE);
195         DRM_LOCK(dev);
196         if (res == NULL) {
197                 DRM_ERROR("Couldn't find resource 0x%x\n", resource);
198                 return 1;
199         }
200
201         if (dev->pcir[resource] == NULL) {
202                 dev->pcirid[resource] = rid;
203                 dev->pcir[resource] = res;
204         }
205
206         return 0;
207 }
208
209 unsigned long drm_get_resource_start(struct drm_device *dev,
210                                      unsigned int resource)
211 {
212         if (drm_alloc_resource(dev, resource) != 0)
213                 return 0;
214
215         return rman_get_start(dev->pcir[resource]);
216 }
217
218 unsigned long drm_get_resource_len(struct drm_device *dev,
219                                    unsigned int resource)
220 {
221         if (drm_alloc_resource(dev, resource) != 0)
222                 return 0;
223
224         return rman_get_size(dev->pcir[resource]);
225 }
226
227 /* Former drm_release() in the legacy DragonFly BSD drm codebase */
228 int drm_device_detach(device_t kdev)
229 {
230         struct drm_softc *softc = device_get_softc(kdev);
231         struct drm_device *dev = softc->drm_driver_data;
232
233         drm_sysctl_cleanup(dev);
234         if (dev->devnode != NULL)
235                 destroy_dev(dev->devnode);
236
237 #ifdef __DragonFly__
238         /* Clean up PCI resources allocated by drm_bufs.c.  We're not really
239          * worried about resource consumption while the DRM is inactive (between
240          * lastclose and firstopen or unload) because these aren't actually
241          * taking up KVA, just keeping the PCI resource allocated.
242          */
243         for (int i = 0; i < DRM_MAX_PCI_RESOURCE; i++) {
244                 if (dev->pcir[i] == NULL)
245                         continue;
246                 bus_release_resource(dev->dev->bsddev, SYS_RES_MEMORY,
247                     dev->pcirid[i], dev->pcir[i]);
248                 dev->pcir[i] = NULL;
249         }
250
251         if (dev->agp) {
252                 kfree(dev->agp);
253                 dev->agp = NULL;
254         }
255
256         if (dev->driver->unload != NULL) {
257                 DRM_LOCK(dev);
258                 dev->driver->unload(dev);
259                 DRM_UNLOCK(dev);
260         }
261
262         if (pci_disable_busmaster(dev->dev->bsddev))
263                 DRM_ERROR("Request to disable bus-master failed.\n");
264
265         lockuninit(&dev->vbl_lock);
266         lockuninit(&dev->event_lock);
267         lockuninit(&dev->struct_mutex);
268 #endif
269
270         return 0;
271 }