drm_linux.c revision 1.8
1/* $OpenBSD: drm_linux.c,v 1.8 2016/02/05 15:51:10 kettenis Exp $ */ 2/* 3 * Copyright (c) 2013 Jonathan Gray <jsg@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <dev/pci/drm/drmP.h> 19#include <dev/pci/ppbreg.h> 20 21struct timespec 22ns_to_timespec(const int64_t nsec) 23{ 24 struct timespec ts; 25 int32_t rem; 26 27 if (nsec == 0) { 28 ts.tv_sec = 0; 29 ts.tv_nsec = 0; 30 return (ts); 31 } 32 33 ts.tv_sec = nsec / NSEC_PER_SEC; 34 rem = nsec % NSEC_PER_SEC; 35 if (rem < 0) { 36 ts.tv_sec--; 37 rem += NSEC_PER_SEC; 38 } 39 ts.tv_nsec = rem; 40 return (ts); 41} 42 43int64_t 44timeval_to_ns(const struct timeval *tv) 45{ 46 return ((int64_t)tv->tv_sec * NSEC_PER_SEC) + 47 tv->tv_usec * NSEC_PER_USEC; 48} 49 50struct timeval 51ns_to_timeval(const int64_t nsec) 52{ 53 struct timeval tv; 54 int32_t rem; 55 56 if (nsec == 0) { 57 tv.tv_sec = 0; 58 tv.tv_usec = 0; 59 return (tv); 60 } 61 62 tv.tv_sec = nsec / NSEC_PER_SEC; 63 rem = nsec % NSEC_PER_SEC; 64 if (rem < 0) { 65 tv.tv_sec--; 66 rem += NSEC_PER_SEC; 67 } 68 tv.tv_usec = rem / 1000; 69 return (tv); 70} 71 72extern char *hw_vendor, *hw_prod; 73 74static bool 75dmi_found(const struct dmi_system_id *dsi) 76{ 77 int i, slot; 78 79 for (i = 0; i < nitems(dsi->matches); i++) { 80 slot = dsi->matches[i].slot; 81 switch (slot) { 82 case DMI_NONE: 83 break; 84 case DMI_SYS_VENDOR: 85 case DMI_BOARD_VENDOR: 86 if (hw_vendor != NULL && 87 !strcmp(hw_vendor, dsi->matches[i].substr)) 88 break; 89 else 90 return false; 91 case DMI_PRODUCT_NAME: 92 case DMI_BOARD_NAME: 93 if (hw_prod != NULL && 94 !strcmp(hw_prod, dsi->matches[i].substr)) 95 break; 96 else 97 return false; 98 default: 99 return false; 100 } 101 } 102 103 return true; 104} 105 106int 107dmi_check_system(const struct dmi_system_id *sysid) 108{ 109 const struct dmi_system_id *dsi; 110 int num = 0; 111 112 for (dsi = sysid; dsi->matches[0].slot != 0 ; dsi++) { 113 if (dmi_found(dsi)) { 114 num++; 115 if (dsi->callback && dsi->callback(dsi)) 116 break; 117 } 118 } 119 return (num); 120} 121 122struct vm_page * 123alloc_pages(unsigned int gfp_mask, unsigned int order) 124{ 125 int flags = (gfp_mask & M_NOWAIT) ? UVM_PLA_NOWAIT : UVM_PLA_WAITOK; 126 struct pglist mlist; 127 128 if (gfp_mask & M_CANFAIL) 129 flags |= UVM_PLA_FAILOK; 130 131 TAILQ_INIT(&mlist); 132 if (uvm_pglistalloc(PAGE_SIZE << order, 0, -1, PAGE_SIZE, 0, 133 &mlist, 1, flags)) 134 return NULL; 135 return TAILQ_FIRST(&mlist); 136} 137 138void 139__free_pages(struct vm_page *page, unsigned int order) 140{ 141 struct pglist mlist; 142 int i; 143 144 TAILQ_INIT(&mlist); 145 for (i = 0; i < (1 << order); i++) 146 TAILQ_INSERT_TAIL(&mlist, &page[i], pageq); 147 uvm_pglistfree(&mlist); 148} 149 150void * 151kmap(struct vm_page *pg) 152{ 153 vaddr_t va; 154 155#if defined (__HAVE_PMAP_DIRECT) 156 va = pmap_map_direct(pg); 157#else 158 va = uvm_km_valloc_wait(phys_map, PAGE_SIZE); 159 pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg), PROT_READ | PROT_WRITE); 160 pmap_update(pmap_kernel()); 161#endif 162 return (void *)va; 163} 164 165void 166kunmap(void *addr) 167{ 168 vaddr_t va = (vaddr_t)addr; 169 170#if defined (__HAVE_PMAP_DIRECT) 171 pmap_unmap_direct(va); 172#else 173 pmap_kremove(va, PAGE_SIZE); 174 pmap_update(pmap_kernel()); 175 uvm_km_free_wakeup(phys_map, va, PAGE_SIZE); 176#endif 177} 178 179void * 180vmap(struct vm_page **pages, unsigned int npages, unsigned long flags, 181 pgprot_t prot) 182{ 183 vaddr_t va; 184 paddr_t pa; 185 int i; 186 187 va = uvm_km_valloc(kernel_map, PAGE_SIZE * npages); 188 if (va == 0) 189 return NULL; 190 for (i = 0; i < npages; i++) { 191 pa = VM_PAGE_TO_PHYS(pages[i]) | prot; 192 pmap_enter(pmap_kernel(), va + (i * PAGE_SIZE), pa, 193 PROT_READ | PROT_WRITE, 194 PROT_READ | PROT_WRITE | PMAP_WIRED); 195 pmap_update(pmap_kernel()); 196 } 197 198 return (void *)va; 199} 200 201void 202vunmap(void *addr, size_t size) 203{ 204 vaddr_t va = (vaddr_t)addr; 205 206 pmap_remove(pmap_kernel(), va, va + size); 207 pmap_update(pmap_kernel()); 208 uvm_km_free(kernel_map, va, size); 209} 210 211#if defined(__amd64__) || defined(__i386__) 212 213/* 214 * This is a minimal implementation of the Linux vga_get/vga_put 215 * interface. In all likelyhood, it will only work for inteldrm(4) as 216 * it assumes that if there is another active VGA device in the 217 * system, it is sitting behind a PCI bridge. 218 */ 219 220extern int pci_enumerate_bus(struct pci_softc *, 221 int (*)(struct pci_attach_args *), struct pci_attach_args *); 222 223pcitag_t vga_bridge_tag; 224int vga_bridge_disabled; 225 226int 227vga_disable_bridge(struct pci_attach_args *pa) 228{ 229 pcireg_t bhlc, bc; 230 231 if (pa->pa_domain != 0) 232 return 0; 233 234 bhlc = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG); 235 if (PCI_HDRTYPE_TYPE(bhlc) != 1) 236 return 0; 237 238 bc = pci_conf_read(pa->pa_pc, pa->pa_tag, PPB_REG_BRIDGECONTROL); 239 if ((bc & PPB_BC_VGA_ENABLE) == 0) 240 return 0; 241 bc &= ~PPB_BC_VGA_ENABLE; 242 pci_conf_write(pa->pa_pc, pa->pa_tag, PPB_REG_BRIDGECONTROL, bc); 243 244 vga_bridge_tag = pa->pa_tag; 245 vga_bridge_disabled = 1; 246 247 return 1; 248} 249 250void 251vga_get_uninterruptible(struct pci_dev *pdev, int rsrc) 252{ 253 KASSERT(pdev->pci->sc_bridgetag == NULL); 254 pci_enumerate_bus(pdev->pci, vga_disable_bridge, NULL); 255} 256 257void 258vga_put(struct pci_dev *pdev, int rsrc) 259{ 260 pcireg_t bc; 261 262 if (!vga_bridge_disabled) 263 return; 264 265 bc = pci_conf_read(pdev->pc, vga_bridge_tag, PPB_REG_BRIDGECONTROL); 266 bc |= PPB_BC_VGA_ENABLE; 267 pci_conf_write(pdev->pc, vga_bridge_tag, PPB_REG_BRIDGECONTROL, bc); 268 269 vga_bridge_disabled = 0; 270} 271 272#endif 273 274/* 275 * ACPI types and interfaces. 276 */ 277 278#if defined(__amd64__) || defined(__i386__) 279#include "acpi.h" 280#endif 281 282#if NACPI > 0 283 284#include <dev/acpi/acpireg.h> 285#include <dev/acpi/acpivar.h> 286 287acpi_status 288acpi_get_table_with_size(const char *sig, int instance, 289 struct acpi_table_header **hdr, acpi_size *size) 290{ 291 struct acpi_softc *sc = acpi_softc; 292 struct acpi_q *entry; 293 294 KASSERT(instance == 1); 295 296 SIMPLEQ_FOREACH(entry, &sc->sc_tables, q_next) { 297 if (memcmp(entry->q_table, sig, strlen(sig)) == 0) { 298 *hdr = entry->q_table; 299 *size = (*hdr)->length; 300 return 0; 301 } 302 } 303 304 return AE_NOT_FOUND; 305} 306 307#endif 308