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 int i; 40 41 /* 42 * This hack allows us to use drm.video.lvds1="<video-mode>" 43 * in loader.conf, where linux would use video=LVDS-1:<video-mode>. 44 * e.g. drm.video.lvds1=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 * (This corresponds to the video= Linux kernel command-line 49 * option) 50 */ 51 memset(str, 0, sizeof(str)); 52 ksnprintf(buf, sizeof(buf), "drm.video.%s", connector_name); 53 i = 0; 54 while (i < strlen(buf)) { 55 buf[i] = tolower(buf[i]); 56 if (buf[i] == '-') { 57 memmove(&buf[i], &buf[i+1], strlen(buf)-i); 58 } else { 59 i++; 60 } 61 } 62 kprintf("looking up kenv for \"%s\"\n", buf); 63 if (kgetenv_string(buf, str, sizeof(str)-1)) { 64 kprintf("found kenv %s=%s\n", buf, str); 65 *option = kstrdup(str, M_DRM); 66 return (0); 67 } else { 68 kprintf("didn't find value for kenv %s\n", buf); 69 return (1); 70 } 71 } 72 73 /* 74 * Implement simplified version of kvasnrprintf() for drm needs using 75 * M_DRM and kvsnprintf(). Since it is unclear what string size is 76 * optimal thus use of an actual length. 77 */ 78 char *drm_vasprintf(int flags, const char *format, __va_list ap) 79 { 80 char *str; 81 size_t size; 82 __va_list aq; 83 84 __va_copy(aq, ap); 85 size = kvsnprintf(NULL, 0, format, aq); 86 __va_end(aq); 87 88 str = kmalloc(size+1, M_DRM, flags); 89 if (str == NULL) 90 return NULL; 91 92 kvsnprintf(str, size+1, format, ap); 93 94 return str; 95 } 96 97 /* mimic ksnrprintf(), return pointer to char* and match drm api */ 98 char *drm_asprintf(int flags, const char *format, ...) 99 { 100 char *str; 101 __va_list ap; 102 103 __va_start(ap, format); 104 str = drm_vasprintf(flags, format, ap); 105 __va_end(ap); 106 107 return str; 108 } 109 110 /* 111 * XXX pci glue logic helpers 112 * Should be done in drm_pci_init(), pending drm update. 113 * Assumes static runtime data. 114 * Only for usage in *_driver_[un]load() 115 */ 116 117 static void drm_fill_pdev(struct device *dev, struct pci_dev *pdev) 118 { 119 pdev->dev = dev; 120 pdev->vendor = pci_get_vendor(dev); 121 pdev->device = pci_get_device(dev); 122 pdev->subsystem_vendor = pci_get_subvendor(dev); 123 pdev->subsystem_device = pci_get_subdevice(dev); 124 } 125 126 void drm_init_pdev(struct device *dev, struct pci_dev **pdev) 127 { 128 BUG_ON(*pdev != NULL); 129 130 *pdev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL); 131 drm_fill_pdev(dev, *pdev); 132 133 (*pdev)->bus = kzalloc(sizeof(struct pci_bus), GFP_KERNEL); 134 (*pdev)->bus->self = kzalloc(sizeof(struct pci_dev), GFP_KERNEL); 135 136 drm_fill_pdev(device_get_parent(dev), (*pdev)->bus->self); 137 (*pdev)->bus->number = pci_get_bus(dev); 138 } 139 140 void drm_fini_pdev(struct pci_dev **pdev) 141 { 142 kfree((*pdev)->bus->self); 143 kfree((*pdev)->bus); 144 145 kfree(*pdev); 146 } 147 148 /* Allocation of PCI memory resources (framebuffer, registers, etc.) for 149 * drm_get_resource_*. Note that they are not RF_ACTIVE, so there's no virtual 150 * address for accessing them. Cleaned up at unload. 151 */ 152 static int drm_alloc_resource(struct drm_device *dev, int resource) 153 { 154 struct resource *res; 155 int rid; 156 157 DRM_LOCK_ASSERT(dev); 158 159 if (resource >= DRM_MAX_PCI_RESOURCE) { 160 DRM_ERROR("Resource %d too large\n", resource); 161 return 1; 162 } 163 164 if (dev->pcir[resource] != NULL) { 165 return 0; 166 } 167 168 DRM_UNLOCK(dev); 169 rid = PCIR_BAR(resource); 170 res = bus_alloc_resource_any(dev->dev, SYS_RES_MEMORY, &rid, 171 RF_SHAREABLE); 172 DRM_LOCK(dev); 173 if (res == NULL) { 174 DRM_ERROR("Couldn't find resource 0x%x\n", resource); 175 return 1; 176 } 177 178 if (dev->pcir[resource] == NULL) { 179 dev->pcirid[resource] = rid; 180 dev->pcir[resource] = res; 181 } 182 183 return 0; 184 } 185 186 unsigned long drm_get_resource_start(struct drm_device *dev, 187 unsigned int resource) 188 { 189 if (drm_alloc_resource(dev, resource) != 0) 190 return 0; 191 192 return rman_get_start(dev->pcir[resource]); 193 } 194 195 unsigned long drm_get_resource_len(struct drm_device *dev, 196 unsigned int resource) 197 { 198 if (drm_alloc_resource(dev, resource) != 0) 199 return 0; 200 201 return rman_get_size(dev->pcir[resource]); 202 } 203