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 #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 */ 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