1 /* 2 * Copyright (c) 2015 Imre Vadász <imre@vdsz.com> 3 * Copyright (c) 2015 Rimvydas Jasinskas 4 * 5 * DRM Dragonfly-specific helper functions 6 * 7 * Permission to use, copy, modify, distribute, and sell this software and its 8 * documentation for any purpose is hereby granted without fee, provided that 9 * the above copyright notice appear in all copies and that both that copyright 10 * notice and this permission notice appear in supporting documentation, and 11 * that the name of the copyright holders not be used in advertising or 12 * publicity pertaining to distribution of the software without specific, 13 * written prior permission. The copyright holders make no representations 14 * about the suitability of this software for any purpose. It is provided "as 15 * is" without express or implied warranty. 16 * 17 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 19 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 20 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 22 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 23 * OF THIS SOFTWARE. 24 */ 25 26 #include <sys/libkern.h> 27 #include <sys/ctype.h> 28 #include <drm/drmP.h> 29 30 /* 31 * An implementation of fb_get_options() 32 * This can be used to set the video mode used for the syscons fb console, 33 * a la "video=..." in linux. 34 */ 35 int 36 fb_get_options(const char *connector_name, char **option) 37 { 38 char buf[128], str[1024]; 39 40 /* 41 * Where on linux one would use the command line option 42 * video=LVDS-1:<video-mode>, the corresponding tunable is 43 * drm.video.LVDS-1=<video-mode>. 44 * e.g. drm.video.LVDS-1=1024x768 sets the LVDS-1 connector to 45 * a 1024x768 video mode in the syscons framebuffer console. 46 * See https://wiki.archlinux.org/index.php/Kernel_mode_setting 47 * for an explanation of the video mode command line option. 48 */ 49 memset(str, 0, sizeof(str)); 50 ksnprintf(buf, sizeof(buf), "drm.video.%s", connector_name); 51 if (kgetenv_string(buf, str, sizeof(str)-1)) { 52 kprintf("found kenv %s=%s\n", buf, str); 53 *option = kstrdup(str, M_DRM); 54 return (0); 55 } else { 56 kprintf("tunable %s is not set\n", buf); 57 return (1); 58 } 59 } 60 61 /* 62 * Implement simplified version of kvasnprintf() for drm needs using 63 * M_DRM and kvsnprintf(). Since it is unclear what string size is 64 * optimal thus use of an actual length. 65 */ 66 char *drm_vasprintf(int flags, const char *format, __va_list ap) 67 { 68 char *str; 69 size_t size; 70 __va_list aq; 71 72 __va_copy(aq, ap); 73 size = kvsnprintf(NULL, 0, format, aq); 74 __va_end(aq); 75 76 str = kmalloc(size+1, M_DRM, flags); 77 if (str == NULL) 78 return NULL; 79 80 kvsnprintf(str, size+1, format, ap); 81 82 return str; 83 } 84 85 /* mimic ksnprintf(), return pointer to char* and match drm api */ 86 char *drm_asprintf(int flags, const char *format, ...) 87 { 88 char *str; 89 __va_list ap; 90 91 __va_start(ap, format); 92 str = drm_vasprintf(flags, format, ap); 93 __va_end(ap); 94 95 return str; 96 } 97 98 /* 99 * XXX pci glue logic helpers 100 * Should be done in drm_pci_init(), pending drm update. 101 * Assumes static runtime data. 102 * Only for usage in *_driver_[un]load() 103 */ 104 105 static void drm_fill_pdev(device_t dev, struct pci_dev *pdev) 106 { 107 int msi_enable = 1; 108 u_int irq_flags; 109 110 pdev->dev.bsddev = dev; 111 pdev->vendor = pci_get_vendor(dev); 112 pdev->device = pci_get_device(dev); 113 pdev->subsystem_vendor = pci_get_subvendor(dev); 114 pdev->subsystem_device = pci_get_subdevice(dev); 115 116 pdev->revision = pci_get_revid(dev) & 0xff; 117 118 pdev->_irq_type = pci_alloc_1intr(dev, msi_enable, 119 &pdev->_irqrid, &irq_flags); 120 121 pdev->_irqr = bus_alloc_resource_any(dev, SYS_RES_IRQ, 122 &pdev->_irqrid, irq_flags); 123 if (!pdev->_irqr) 124 return; 125 126 pdev->irq = rman_get_start(pdev->_irqr); 127 } 128 129 void drm_init_pdev(device_t dev, struct pci_dev **pdev) 130 { 131 BUG_ON(*pdev != NULL); 132 133 *pdev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL); 134 drm_fill_pdev(dev, *pdev); 135 136 (*pdev)->bus = kzalloc(sizeof(struct pci_bus), GFP_KERNEL); 137 (*pdev)->bus->self = kzalloc(sizeof(struct pci_dev), GFP_KERNEL); 138 139 drm_fill_pdev(device_get_parent(dev), (*pdev)->bus->self); 140 (*pdev)->bus->number = pci_get_bus(dev); 141 } 142 143 void drm_fini_pdev(struct pci_dev **pdev) 144 { 145 kfree((*pdev)->bus->self); 146 kfree((*pdev)->bus); 147 148 kfree(*pdev); 149 } 150 151 void drm_print_pdev(struct pci_dev *pdev) 152 { 153 if (pdev == NULL) { 154 DRM_ERROR("pdev is null!\n"); 155 return; 156 } 157 158 DRM_INFO("pdev: vendor=0x%04x device=0x%04x rev=0x%02x\n", 159 pdev->vendor, pdev->device, pdev->revision); 160 DRM_INFO(" svendor=0x%04x sdevice=0x%04x irq=%u\n", 161 pdev->subsystem_vendor, pdev->subsystem_device, pdev->irq); 162 } 163 164 /* Allocation of PCI memory resources (framebuffer, registers, etc.) for 165 * drm_get_resource_*. Note that they are not RF_ACTIVE, so there's no virtual 166 * address for accessing them. Cleaned up at unload. 167 */ 168 static int drm_alloc_resource(struct drm_device *dev, int resource) 169 { 170 struct resource *res; 171 int rid; 172 173 DRM_LOCK_ASSERT(dev); 174 175 if (resource >= DRM_MAX_PCI_RESOURCE) { 176 DRM_ERROR("Resource %d too large\n", resource); 177 return 1; 178 } 179 180 if (dev->pcir[resource] != NULL) { 181 return 0; 182 } 183 184 DRM_UNLOCK(dev); 185 rid = PCIR_BAR(resource); 186 res = bus_alloc_resource_any(dev->dev->bsddev, SYS_RES_MEMORY, &rid, 187 RF_SHAREABLE); 188 DRM_LOCK(dev); 189 if (res == NULL) { 190 DRM_ERROR("Couldn't find resource 0x%x\n", resource); 191 return 1; 192 } 193 194 if (dev->pcir[resource] == NULL) { 195 dev->pcirid[resource] = rid; 196 dev->pcir[resource] = res; 197 } 198 199 return 0; 200 } 201 202 unsigned long drm_get_resource_start(struct drm_device *dev, 203 unsigned int resource) 204 { 205 if (drm_alloc_resource(dev, resource) != 0) 206 return 0; 207 208 return rman_get_start(dev->pcir[resource]); 209 } 210 211 unsigned long drm_get_resource_len(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_size(dev->pcir[resource]); 218 } 219